1HelloMan1/dsh-vision-fallback ↗★ 2
dsh-vision-fallback
DSH 静默视觉增强:主模型照常选择,图片自动交给固定视觉模型后以隐藏上下文返回主模型。
安装
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:1HelloMan1/dsh-vision-fallback说明文档
阅读完整 README ↗Configuration
Two ways, both live (no restart needed after saving):
Web settings page
Open Settings → 视觉增强 / Vision Enhancement in the DSH web UI. It exposes only:
| Field | Default | Meaning |
|---|---|---|
| Enabled | true | Master switch |
| Vision model | mimo-v2.5 | OpenAI-compatible model |
| Base URL | https://opencode.ai/zen/go/v1 | The plugin appends /chat/completions |
| Credential ref | OPENCODE_GO_API_KEY | Key resolved from the DSH credential store (never written to env files) |
| Max tokens / Timeout / Max bytes | 1536 / 60000 / 15MB | Vision request limits |
| Recent context | includeRecentContext: true, contextMessages: 6, contextMaxChars: 6000 | How much recent chat to attach for the vision model |
| Prompt | (Chinese detailed-analysis prompt) | Analysis instruction; the user's question is appended automatically |
| Tag result | true | Prepend 【视觉观察:】 to the observation |
settings.yaml
vision-fallback:
enabled: true
model: mimo-v2.5
baseURL: https://opencode.ai/zen/go/v1
apiKeyRef: OPENCODE_GO_API_KEY
maxTokens: 1536
timeoutMs: 60000
maxBytes: 15728640
includeRecentContext: true
contextMessages: 6
contextMaxChars: 6000
prompt: "请分析这张图片..."
tagResult: true
The API key is resolved through the DSH credentials system (~/.dsh/.credentials.yaml), with process.env[apiKeyRef] as a fallback — it is never materialized into shell environment files by the plugin.
Usage records (usage.jsonl)
With recordUsage enabled, every real vision call (success or failure) appends one JSON line to
/vision-fallback/usage.jsonl (override via usageLogPath in the settings page), consumed by usage-dashboard.
Fields:
| Field | Meaning |
|---|---|
ts | Call start time (epoch ms) |
durationMs | Response latency of this call (ms) |
kind | Always "vision" |
status | "ok" success / "error" failure |
model | Vision model name |
inputTokens / outputTokens | Input / output tokens |
cacheReadTokens | Cached input tokens served |
error | Error message (failure entries only) |
imageName / mediaType / imageBytes | Image filename / media type / byte size |
imageIndex / imageTotal | This image's position / total images in the request |
Reusing a remembered observation (observations.json) does not append a line — this file counts real external vision calls only.
Observation cache semantics
Observations are keyed by the image's occurrence in a session (session id + message id):
- The same image at the same message position processed again (restart recovery, replay, compaction) → reused, no re-recognition;
- The same image at a new position in the session (new turn, main model asking to "look again") → re-recognized with fresh context;
- Observations are not shared across sessions.
The cache is capped at 256 entries (LRU eviction); failed results are never cached.
Cache location: when usageLogPath is set, observations.json is written beside that log; otherwise it is stored at
/vision-fallback/observations.json. This lets compaction, event replay, and service restarts reuse successful observations.
Compaction, tool results, and restarts
When the main model can already see images, the bridge does not call the vision model. If a later text-only compaction model encounters the original image event, the plugin first reuses the main model's projected observation and the persistent cache, then handles only images that truly have no observation. This prevents every compaction from re-describing the entire image history while still allowing the same image at a new message position to be re-read with fresh context.