Tool-call routing beats one-shot image descriptions for text-only LLM agents
Most vision plugins for text-only LLM agents work by converting an image to a text description and feeding that description to the reasoning model. The pixel data is gone before the agent ever sees it. dsh-vision-router takes a different approach: it keeps DeepSeek as the reasoning brain and routes image turns to a separate vision model, which the agent interrogates through tool calls rather than receiving a one-shot summary.
The architecture is explicit about the division of labor. DeepSeek never touches the raw image; instead it calls tools like vision_ground, vision_crop, vision_pixel_diff, and vision_describe across as many steps as the task requires. The agent can locate a UI element with vision_ground, zoom in with vision_crop, rebuild something, screenshot it with vision_html_screenshot, and then verify the result with vision_pixel_diff — which returns a diff ratio, a red heatmap, and a worst-region ranking. The README shows a concrete example of this loop closing at a 2.54% final pixel difference (32,939 out of 1,296,000 pixels differing at a threshold of 16 per channel).
The free-by-default claim is real and specific. The built-in anonymous fallback runs through OVHcloud, capped at 2 requests per minute per IP per model, across five independent model buckets — Qwen3.5-397B, Qwen2.5-VL-72B, Qwen3.6-27B, Mistral-Small-3.2-24B, and Qwen3.5-9B — yielding roughly 10 RPM in theory without any account or key. A free OVH access key lifts that same endpoint to 400 requests per minute per project per model.
Fourteen tools ship in the default configuration, all registered at session start rather than mounted on demand. The README is candid about why: progressive tool exposure can invalidate long-context KV and prefix caches mid-conversation. Progressive mode exists as an explicit opt-in (progressiveTools: true), but the default keeps the schema stable. The full pipeline — downscaling, grounding, cropping, pixel diff, palette extraction, OCR, SVG vectorization, foreground cutout, HTML screenshot — runs on sharp, potrace, tesseract, and system Chrome. No Python anywhere.
The plugin also handles the messy operational details that similar tools skip. Failures are classified by type: region blocks, ToS filtering, quota exhaustion, rate limits, context overflow, network failures. A 429 advances immediately to the next backend and opens a Retry-After-aware circuit-breaker cooldown rather than sleeping inside the request. Vision answers are cached by attachment content hash, so later text turns can reference earlier images without re-spending vision calls.
The README is unusually transparent about design lineage, naming six prior DSH community projects and explaining specifically what each one does differently — description bridges, transcription bridges, two-model bridges — and where this plugin diverges. That kind of attribution is rare and makes the ecosystem easier to navigate.
The one thing to watch: enabling vision requires an explicit 👁 Vision toggle in the composer. Pasting an image does not activate it automatically. That is a deliberate choice, not an oversight, but it will catch users who expect automatic detection.
A pixel-faithful vision layer for text-only DeepSeek agents, built around tool calls rather than one-shot image descriptions, with a genuinely keyless free fallback.