ysr666/dsh-vision-router
ysr666/dsh-vision-router为纯文本 Agent 提供视觉能力:内置免 Key 视觉链 + 像素级视觉工具(看图问答、定位、裁剪、像素对比、取色、OCR、矢量化、抠图、截图);粘贴图片即可用。
DeepSeek 视觉桥接:注册原生 LLM provider,聊天内粘贴的图片自动由视觉模型(pi-ai/llama.cpp 上的 Qwen3-VL)转成文字描述后交给纯文本 DeepSeek 作答——图片准入、路由与会话压缩全部走 harness 原生机制,带 LRU 描述缓存与 503 重试。
English | 中文
End-of-life as of 2026-10-09. Development has stopped, the npm package is marked deprecated, and no further issues or pull requests will be handled.
DSH's DeepSeek adapter now handles images natively: its default catalog advertises deepseek-flash (DeepSeek-V41-Flash) with inputModalities: ["text", "image"] and resolves request images through the provider Files API — see the DeepSeek image-understanding guide. The workaround this plugin exists for (routing images around MODEL_DOES_NOT_SUPPORT_IMAGES on a text-only DeepSeek) is no longer needed on the official route.
Migrate away:
dsh plugin --profile web remove dsh-llm-vision-bridge
then select deepseek-flash on the official route (deepseek-official) and paste images directly in the composer.
This bridge remains useful only if you specifically need (a) a local/private vision model whose bytes never leave your network, (b) image input for a non-DeepSeek text-only model, or (c) an older DSH release that predates native vision. In those cases, fork it — the code is MIT.
DSH STORE listing: unlisted (not installable from the store). No dsh.compatibility.dshReleases matrix will be maintained, so the entry stays unlisted by design — see AI-Scarlett/DSH-Store#574.
Last released version: 0.1.2. Existing installs keep working; you may keep, disable, or uninstall them.
The documentation below is kept as a historical record of how the bridge worked.
Let text-only LLMs (DeepSeek) "see" images in the dsh web GUI: paste an image into the chat and the plugin automatically routes it to a vision model (Qwen3-VL via your existing pi-ai / llama.cpp route), then feeds the resulting text description to DeepSeek, which continues the conversation as if it were a native multimodal model.
deepseek-vision on the DSH LlmAdapter seam. Image admission, request routing, and session compaction all run through harness-native mechanisms; no UI changes, no front-end interception.deepseek-official).[图片 N 描述] text block before the request reaches DeepSeek.placeholder (insert a failure note and continue) or error (fail the turn).The chat composer natively supports image attachments: images enter the model request as {type:"image", attachment} content blocks. The DeepSeek chat-completions adapter rejects image blocks with UNSUPPORTED_CONTENT, so a text-only model cannot process them directly.
This plugin's bridge provider (deepseek-vision) declares inputModalities: ["text", "image"], which satisfies the host's image-admission check (MODEL_DOES_NOT_SUPPORT_IMAGES is otherwise thrown before the message ever reaches the agent). Inside its stream():
yield* ctx.llm.stream({ ...options, provider: fallbackProvider }) — passthrough, zero cost.ctx.llm.stream() against the configured vision provider (e.g. pi-ai's llama route; image bytes are read automatically by the attachment service), then replace the image block with a [图片 N 描述]\n<description> text block and forward the rewritten messages to the fallback provider.Session compaction reuses the provider of the most recent request, so image-bearing history is also bridged automatically. The optional autoRoute setting (default off) additionally rewrites deepseek-official agent requests to this provider, but it cannot bypass the host's image-admission check — it is only a fallback. To actually send images, set the main model to deepseek-vision.
# From GitHub (plain JS, no build step, no allowBuilds needed)
dsh plugin --profile web add github:Einskyle/dsh-llm-vision-bridge
# Or from the npm registry
dsh plugin --profile web add dsh-llm-vision-bridge
# Restart the web service
pnpm dsh web
Manual install without pnpm (equivalent):
%USERPROFILE%\.dsh\profiles\web\node_modules\dsh-llm-vision-bridge\%USERPROFILE%\.dsh\profiles\web\package.json:
"dsh-llm-vision-bridge": "file:<absolute path>" to dependencies"dsh-llm-vision-bridge" to dsh.profile.bundlesdeepseek-v4-flash / deepseek-v4-pro.agent-default-model.provider: deepseek-vision. This is required: the host's image-admission check reads the session-selected model's inputModalities, and only the bridge model advertises image.deepseek-official any time for pure text (image uploads are then rejected by admission, as expected).| Field | Default | Description |
|---|---|---|
enabled | true | Master switch; when off the bridge provider degrades to pure passthrough |
autoRoute | false | Additionally rewrite deepseek-official agent requests to the bridge provider (cannot bypass image admission; fallback only) |
fallbackProvider | deepseek-official | The text-only provider that actually generates the reply |
visionProvider | llama | Vision provider route (pi-ai) |
visionModel | /models/qwen3-vl-4b-thinking/Qwen3-VL-4B-Thinking-Q4_K_M.gguf | Vision model id |
visionPrompt | (built-in Chinese prompt) | System prompt for the vision model |
visionMaxTokens | 2048 | Vision output cap (keep ≥1024; thinking consumes tokens) |
visionRetries | 3 | Max retries for retryable errors (503/429/timeout) |
visionRetryDelayMs | 30000 | Retry delay |
onVisionFailure | placeholder | Final failure policy: placeholder = insert a failure note and continue; error = fail the turn |
The vision call goes through the pi-ai adapter (ctx.llm.stream against visionProvider/visionModel), so any OpenAI-compatible vision endpoint works — a local llama.cpp gateway is only the default, not a requirement.
| Type | Example | API key | Notes |
|---|---|---|---|
| Local llama.cpp gateway (current default) | Qwen3-VL-4B via http://<desktop-ip>:18081/v1 | No | Free, private, LAN-only; image bytes never leave your network |
| Cloud OpenAI-compatible APIs | qwen-vl-max (DashScope), glm-4v-plus (Zhipu), gpt-4o (OpenAI), OpenRouter/ SiliconFlow, … | Yes | Stronger models; images are sent to the cloud provider |
Example — add a DashScope route to settings.yaml (or Settings → Models → llm-pi-ai) and point the bridge at it:
llm-pi-ai:
providers:
dashscope:
displayName: DashScope
apiKeyEnv: DASHSCOPE_API_KEY
api: openai-completions
baseURL: https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
models:
- id: qwen-vl-max
name: Qwen-VL-Max
input: [ text, image ]
llm-vision-bridge:
visionProvider: dashscope
visionModel: qwen-vl-max
Settings changes apply without a restart. Constraints: the endpoint must be OpenAI-compatible and accept image input; the vision provider must not be the bridge provider itself (deepseek-vision, recursion guard); cloud routes need a stored credential (apiKeyEnv → Settings → Models), otherwise pi-ai reports MISSING_CREDENTIAL.
llama route: baseURL pointing at the desktop llama.cpp http://<desktop-ip>:18081/v1, model declaring input: [text, image]).apiKeyEnv but the credential is not set, pi-ai reports MISSING_CREDENTIAL: store any placeholder value on the Settings page (local llama.cpp does not validate the key), or remove that apiKeyEnv.visionRetries.| Symptom | Fix |
|---|---|
| 「DeepSeek(视觉桥接)」 missing in Settings | Plugin not loaded; check the web service startup log and confirm the bundle is in the profile |
attachment-error / MODEL_DOES_NOT_SUPPORT_IMAGES on send | Session model is not the bridge model: set agent-default-model.provider: deepseek-vision, or select 「DeepSeek(视觉桥接)」 for the session |
VISION_UNAVAILABLE | Vision model unreachable: check the llama provider baseURL, the desktop is powered on, and LLAMA_API_KEY is present |
UNSUPPORTED_CONTENT after sending | Request did not go through the bridge provider: confirm the main model is deepseek-vision, not deepseek-official |
| Slow vision replies | Qwen3-VL cold load of 10–40s is normal; on frequent 503, wait for other desktop GPU jobs |
MIT