Work around this vLLM error without restarting the model server:
At most 4 image(s) may be provided in one prompt. (parameter=image)
Pi sends images from retained conversation history, not just the latest turn. This extension keeps the newest four images in each outgoing request and replaces older image blocks with text markers. Both user attachments and tool-result screenshots are covered. Saved session history, image files, model settings, and the server are left unchanged.
This is especially useful for computer-use and app-development agents that capture screenshots repeatedly. A screenshot taken one at a time can still accumulate with earlier screenshots in the next model request. Without a retention policy, image history can consume the context needed for code, instructions, logs, and tool results.
-
Download
pi-spark-image-history.tsfrom this gist using Raw, saving it as~/Downloads/pi-spark-image-history.ts(not an HTML page). -
Copy it into Pi's extension directory:
mkdir -p ~/.pi/agent/extensions cp -i ~/Downloads/pi-spark-image-history.ts \ ~/.pi/agent/extensions/pi-spark-image-history.ts
-
In your existing Pi session, run:
/reload /spark-images -
Confirm the command reports active, then continue the conversation.
New Pi processes load the extension automatically. No npm package or runtime dependencies are needed. Updating Pi alone does not install this workaround. As with any Pi extension, review the source before installing it: extensions run with your user's permissions.
The extension deliberately applies only to this profile:
| Field | Value |
|---|---|
| Model ID | DeepSeek-v4.1-Flash-EXL3 |
| API | openai-completions |
| Provider ID | gx10-dspark or gx10-vllm |
If your provider has another ID, adjust the allowlist in isSparkModel() in
the extension and run /reload. Do not rename a working model profile just
to match the extension. The Droid/Gemini alias and other providers are not
affected. MAX_IMAGES = 4 must match the server's allowed image count.
The four-image cap is model-specific, not a global limit on all Pi models.
The extension checks the exact model ID DeepSeek-v4.1-Flash-EXL3, the API,
and the provider allowlist before changing an outgoing request. Other model
IDs are untouched, even under the same provider.
In ~/.pi/agent/models.json, check the matching entry in your existing
provider's models array. It must declare image input:
{
"id": "DeepSeek-v4.1-Flash-EXL3",
"api": "openai-completions",
"input": ["text", "image"]
}This is a partial model entry, not a replacement for your settings file. Preserve your existing provider ID, endpoint, credentials, context window, output limit, reasoning settings, and other models. The API may also be inherited from the provider instead of repeated inside this entry.
Also check images.blockImages in ~/.pi/agent/settings.json and the current
project's .pi/settings.json: it must not be true for image requests.
images.autoResize controls image dimensions, not image count.
Pi's input field enables vision; the extension's MAX_IMAGES = 4 enforces
the four-image request budget. Do not add an invented maxImages field to
models.json. The model server must already accept image input and four images.
If you changed the model entry, open bare /model and reselect it to reload
the model configuration. Use /reload for extension changes, then
/spark-images to confirm the filter is active. Already-correct profiles
need no settings changes.
These are approximate, upper-end estimates for this DeepSeek V4.1 Flash deployment, not universal costs for every Pi model. The model's image-token budget is 1,024 per image; its scheduler conservatively allows 1,025 tokens including a margin. Smaller processed images can use fewer tokens. JPEG/PNG file size does not directly determine token cost.
| Images retained in a request | Approximate image-token budget | Preprocessed image tensors, one resident copy |
|---|---|---|
| 1 | 1,025 | ~10 MiB |
| 4 | 4,100 | ~40 MiB |
| 16 | 16,400 | ~160 MiB |
| 100 | 102,500 | ~1 GiB |
With a 240,000-token context, 100 large images can use about 43% of the context. After reserving 32,768 tokens for output, that leaves roughly 105,000 tokens for text, code, system instructions, and tool history. At 1,000 large images, the image budget alone is approximately one million tokens: far beyond this context window. Rows above four illustrate the cost of unbounded history; they are not tested serving limits for this deployment.
The RAM column estimates the BF16 patch tensors produced by the installed processor: up to roughly 9,000 patches x 3 channels x 14 x 14 pixels x 2 bytes, or about 10 MiB per large image per copy. It is not the full request peak:
- A decoded RGB image alone is about 5.9 MiB at 1920x1080, or 11.4 MiB at 2000x2000; image libraries may use additional storage.
- Encoded/base64 request bodies, processor-cache copies, transport buffers, and temporary vision/prefill activations add overhead. API and engine processor caches can duplicate data. See vLLM processor-cache documentation.
- For reference, this deployment's reserved 2.5 GiB KV pool per Spark has a reported capacity of 729,200 tokens. 102,500 image tokens represent roughly 0.35 GiB of KV-pool-equivalent capacity per Spark. This is an average capacity estimate, not a measurement of marginal allocation: that pool is already reserved and does not allocate another 0.35 GiB just for the request.
- Chunked scheduling does not run all 100 full-size images through the vision encoder simultaneously. Do not multiply a single-image activation peak by 100 to predict GPU memory. Conversely, CPU-side images and preprocessing data may accumulate before those chunks execute.
Several GiB of request/preprocessing overhead is plausible for 100 large images, but the total peak has not been measured. Merely raising an image limit does not consume those tokens or load 100 copies of the vision tower; the actual request contents drive the cost.
Four images provide a bounded recent visual history while leaving most of the context for the coding task. This also avoids the server's four-image validation error. It does not guarantee faster prefill: replacing old image blocks can invalidate part of a cached prefix, as noted below.
Anemll macOS Agent v0.2.1 — Multi-Client Skill Installer
is a companion for Pi and other coding agents to control or assist with
computer use, especially macOS app development, debugging, and UI testing.
The local menu-bar host offers Accessibility inspection, mouse/keyboard
actions, screenshots, OCR, and REST/MCP interfaces. The release adds skill
and MCP setup for Pi, Droid, Claude, Codex, and Grok, including automatic
pi-mcp-adapter installation when Pi is available.
An app-development loop often looks like: inspect the app, perform an action, capture the result, fix the code, rebuild, and capture again. Each capture can become another image in the coding agent's retained conversation. Even though the agent captured only one new screenshot, its next request may resend many older screenshots. A long-running loop can therefore exhaust context or hit the backend's image-count limit.
Recommended capture policy:
- Prefer compact Accessibility/OCR text when it answers the question; use screenshots for visual layout, rendering bugs, and custom-drawn interfaces.
- Capture the relevant window or region and resize appropriately instead of repeatedly sending an entire high-resolution desktop.
- Keep the latest four images in outgoing Pi requests, preserving recent visual evidence without resending the entire screenshot history.
- Record important older observations as text and re-read/reattach an old screenshot when a later comparison needs it. The filter does not generate those summaries automatically.
The macOS agent supplies the computer-use tools; this extension supplies the Pi-side image-retention policy. It does not change Droid or other clients, which need their own equivalent policy where necessary. See the v0.2.1 usage documentation for setup and the semantic-first workflow.
/spark-imagesreports whether the filter is active and the last request's image count. This command does not send an inference request.- A footer status such as
images 4/7means four of seven images were sent in the most recently checked request. Custom footers may hide this status. - An interactive warning appears once per agent run when images are omitted.
- Older images stay in the saved transcript, but their pixels are not sent to the model. Re-read or reattach an older image to inspect it again. The text marker is not an image summary.
- This bounds outgoing image history, not on-disk session size or Pi's memory for stored history. Text/tool history also continues growing and still needs normal compaction or a new session during long-running work.
- More than four newly supplied images are also reduced to the newest four. Submit image comparisons in groups of at most four. Headless requests are filtered too, but there is no interactive warning in headless mode.
- Replacing an older image changes the prompt prefix and may reduce KV-prefix cache reuse, causing additional prefill. This is not a throughput fix.
- A later extension hook that adds images can exceed the limit again. The
filter covers Chat Completions
image_urlblocks, not every provider format. - The extension does not log image bytes, prompt text, URLs, or credentials.
Verified with Pi 0.85.1 and 0.86.1 extension loaders. The 16 offline tests cover 0, 1, 4, 5, 7, 100, and 1,000 images, tool-call preservation, immutable inputs, model scoping, and UI failures. A saved-session check using Pi 0.86.1's actual serializer reduced five active-context images to the newest four, without altering the original payload or session file. No inference request was sent by that check; it is not a vision-quality benchmark.
The extension file's SHA-256:
43461497d1df9208d728d3d3b5c123c292bc7802af88b5271acfd62a4ebeee94
Check a downloaded copy with shasum -a 256 pi-spark-image-history.ts.
Move pi-spark-image-history.ts outside ~/.pi/agent/extensions/, then run
/reload. Renaming it to another .ts file in the same folder does not
disable auto-discovery.
References: Pi extensions and hot reload, Pi model configuration, Pi image settings.