返回目录
文件与数据 插件

dsh-vision-bridge

YuLee-314/dsh-vision-bridge

DeepSeek Harness 视觉补全:孪生路由解锁原生图片体验,本地 Ollama 请求层看图,零云端依赖。Vision twin + local agentic vision tools for DeepSeek Harness.

Stars
0
Forks
0
Issues
0
更新
1 天前

PROJECT TOPICS

项目标签

INSTALL REFERENCE

安装参考

未验证
dsh plugin --profile web add github:YuLee-314/dsh-vision-bridge

该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。

PROJECT README

README

dsh-vision-bridge

A DeepSeek Harness plugin that lets text-only models receive and understand images. The vision work is done by a local model on your machine.

Plugin Version License Node Platform

English · 简体中文


What is this?

DeepSeek Harness (dsh) is an open-source AI coding environment built entirely from plugins. Its chat models — deepseek-v4-flash and deepseek-v4-pro — are text-only: their API rejects image data. As a result, in Harness you cannot paste a screenshot into a session that uses them, attach an image to a message, or use the built-in read_image tool.

This plugin fixes that. It works in three layers:

  1. A second model route that accepts images. The same DeepSeek models are registered again as a "twin" provider (deepseek-vision). Because the twin declares image support, the normal image features work: pasting produces a thumbnail and an image block, and read_image is allowed. Before each request is sent to the DeepSeek API, the plugin converts every image in the conversation into a text description produced by a local vision model. The API only ever receives text; the model answers as if it had seen the image.
  2. Nine inspection tools. describe_image, extract_text, structured_scan, query_region, detect_elements, locate_object, compare_images, read_clipboard, and check_health let the model look at an image at different levels of detail — from a general description down to per-element coordinates — and let you do the same through chat.
  3. Paste routing. When you paste an image, a small browser component asks the server whether the current model can handle images. If yes (twin route), the paste stays a normal image. If no (official text-only route), the image is saved to a private local file and the path is inserted as text, which the inspection tools can then read.

The vision model (Ollama + qwen2.5vl) runs on your machine. No image bytes are ever sent to DeepSeek's API or to any cloud vision service.

If you only use the official route, the plugin still helps: pasted images become local paths and the inspection tools work on them. If you only want the tools, you can ignore the twin route entirely.

Table of Contents


The Problem

DeepSeek's flagship chat models (deepseek-v4-flash, deepseek-v4-pro) are text-only: their API endpoints cannot receive image bytes, and their model metadata declares inputModalities: ['text']. DeepSeek Harness builds on that metadata in three hard places:

Gate Location Effect
Composer admission api-proxy prompt handler Pasting an image is rejected: "the current model does not support images" — the image part never enters the conversation
read_image tool dsh-tool-fs Refuses to read an image into context unless the active route declares image input
Adapter serializer llm-deepseek Core image blocks are rejected (UNSUPPORTED_CONTENT) — images physically cannot reach the provider

Workarounds in the wild either fork presets (fragile), require cloud vision keys (Gemini etc.), or force you to switch to a weaker vision model for the whole session — losing DeepSeek's coding ability. None of them deliver what users actually want: DeepSeek, plus native image UX, plus the ability to see.

The Solution

One self-contained plugin, three cooperating layers:

  1. Vision twin route (deepseek-vision) — the same DeepSeek models re-registered with inputModalities: ['text', 'image']. Every native gate opens: paste admission, thumbnails, durable image blocks, read_image. Under the hood the twin intercepts image blocks at the request layer, runs the local vision bridge, and forwards only text to DeepSeek's API.
  2. Agentic vision tools — nine native tools (describe_image, structured_scan, query_region, ...) powered by a local Ollama vision model, with structured output, validation-with-retry, and content-addressed caching.
  3. Paste router — a browser half that asks the host for a verdict based on real model metadata: vision-capable routes keep the native photo flow; text-only routes fall back to a local path so the bridge tools can take over.

Features

  • Native image experience for text-only models — paste a screenshot, get a thumbnail, image block, and a DeepSeek that actually saw it, without a single image byte ever reaching the API.
  • No cloud keys required — the vision engine is local Ollama (qwen2.5vl); the twin reuses your existing DEEPSEEK_API_KEY credential with the official route's own resolution logic.
  • Request-layer transparency — no prompt hacks, no preset forks, no dynamic injection that can race; the interception happens in the adapter, exactly once, per request.
  • Structured output with coordinates — element bounding boxes ([0,1000] normalized), region cropping, two-stage localization, image comparison, clipboard reads, schema-validated output with automatic retry on malformed responses.
  • Repeated images are cached — repeated images hit the content-hash cache: zero extra inference, stable prefix-cache behavior.
  • Self-contained & distributable — a single 29 KB tarball with no machine-specific paths; installs on any Harness via dsh plugin --profile web add.
  • Coexists with the official route — the official provider stays untouched as the fallback; the paste verdict decides per session, from live metadata, which flow runs.

Architecture

┌──────────────────────────── Browser · client.js ────────────────────────────┐
│ paste / drop image                                                          │
│   → capture-phase listener (before the composer's own)                      │
│   → GET /vision-bridge/paste?model=<selector label>   (host verdict)        │
│        ├─ takeover:true   (confirmed text-only route)                       │
│        │    → POST bytes → host saves private temp file → path text         │
│        │      inserted into the composer; bridge tools take over            │
│        └─ takeover:false  (image-capable route)                             │
│             → native paste: image part + thumbnail, untouched               │
└─────────────────────────────────────┬───────────────────────────────────────┘
                                      ▼
┌──────────────────────── Host plugin · lib/index.js ────────────────────────┐
│  · POST /vision-bridge/paste → magic-byte sniff → 0600 temp file → {path}  │
│  · 9 tools registered into ctx.tools (native catalog, no mcp__ prefix)     │
│  · registerAdapter('deepseek-vision', VisionDeepSeekAdapter)               │
└──────────────┬───────────────────────────────────────────┬─────────────────┘
               ▼                                           ▼
     ┌────────────────────┐                  ┌──────────────────────────────┐
     │ vision-core         │                  │ VisionDeepSeekAdapter (twin) │
     │ · prepare/validate  │                  │ · listModels/resolveModel:  │
     │ · queue (serial)    │                  │   image-capable metadata     │
     │ · LRU cache         │                  │ · stream(): ImageBlock ──►   │
     │ · 9 tool handlers   │                  │   bridge analysis text ──►   │
     │                     │                  │   DeepSeek API (text-only)  │
     └──────────┬──────────┘                  └───────────────┬──────────────┘
                ▼                                              ▼
        Ollama · qwen2.5vl                        DeepSeek API · same endpoint,
        localhost · private · free                same credential as official

lib/core/ is the host-agnostic vision core shared with the author's MCP vision bridge (scripts/sync-core.mjs re-syncs it — one source of truth, two delivery forms).

Interaction Logic

1. Paste routing (per paste, decided by live model metadata)

Session model Verdict What happens when you paste
DeepSeek-V4-Flash/Pro (视觉桥) (twin) takeover:false Native photo: thumbnail + image block; the twin analyses it at the request layer
DeepSeek-V4-Flash (official) takeover:true Paste bytes → private temp file → path text; the model calls bridge tools on the path
Any future vision-capable route takeover:false Native paste preserved automatically — the verdict is evidence-based, never a name regex

The client caches verdicts per selector label (60 s TTL) and refreshes on focus, so the first paste of a session is already correct. Unknown metadata never hijacks a paste — the native path is the safe default.

2. Request-layer interception (vision twin only)

user pastes image ──► durable image block in session history
        │
        ▼
next model request ──► VisionDeepSeekAdapter.stream()
        │
        ▼
sanitize(): for every image block ──► attachments.readImage(bytes)
        │                                 │
        │                                 ▼
        │               write content-addressed file
        │               (~/.dsh/vision-bridge/images/<sha1>.png)
        │                                 │
        │                                 ▼
        │               vision-core.describe_image(path)   ← cache hit ⇒ zero inference
        │                                 │
        │                                 ▼
        │               text: [图片(视觉桥分析)] … + local path
        │
        ▼
text-only request ──► DeepSeek API (identical endpoint/credential as official route)
        │
        ▼
DeepSeek answers WITH the vision analysis; it may also call query_region / extract_text
on the embedded path for deeper, coordinate-accurate inspection.

3. Tool chain (evidence workflow)

structured_scan ──► element list with bboxes (heading/table/chart/button/…)
        │
        ├──► query_region(bbox) ──► the region is REALLY cropped and analyzed alone
        ├──► extract_text(with_coordinates) ──► OCR blocks with normalized coordinates
        └──► locate_object(desc) ──► coarse locate → crop ×1.3 → fine locate → full-image bbox

Installation

# 1. Prerequisites: Ollama running with a vision model (e.g. qwen2.5vl:7b),
#    and DEEPSEEK_API_KEY stored (for the twin route).

# 2. Install the plugin (any form works)
dsh plugin --profile web add .\dsh-external-dsh-vision-bridge-2.0.0.tgz   # tarball
#   dsh plugin --profile web add <directory>                             # checkout
#   dsh plugin --profile web add @yulee-314/dsh-vision-bridge            # npm

# 3. Restart dsh web (bundle layers load at boot)

Usage

  1. In any session, open the model selector and pick:
    • DeepSeek (视觉桥) → DeepSeek-V4-Flash (视觉桥) — recommended: coding + native image UX, vision supplied by the bridge.
    • Official DeepSeek route — paste falls back to path text; bridge tools remain available.
  2. Paste or drop an image. A thumbnail appears (twin) or a path is inserted (official).
  3. Ask normally. DeepSeek answers from the bridge analysis; use query_region / extract_text for coordinate-level detail.

To make the twin the default for every new session:

# ~/.dsh/settings.yaml
agent-default-model:
  provider: deepseek-vision
  model: deepseek-v4-flash-vision
  reasoningEffort: max

Configuration

Variable Default Purpose
OLLAMA_BASE_URL http://127.0.0.1:11434/v1 Vision engine endpoint (OpenAI-compatible)
OLLAMA_VISION_MODEL qwen2.5vl:7b-q3_K_M Vision model used by the bridge tools
OLLAMA_API_KEY ollama Compatible key for the engine
VISION_MAX_TOKENS / VISION_TEMPERATURE / VISION_MAX_RETRIES / VISION_CONCURRENCY 8192 / 0.1 / 1 / 1 Inference parameters
DEEPSEEK_API_KEY credentials service Twin-route key — same source as the official route
DEEPSEEK_BASE_URL public API Twin-route endpoint — same resolution as the official route

Tool rows are disabled by setting pasteToPath: false on the bundle row if the paste router is undesired on a specific deployment.

Vision Tools

Tool What it does
describe_image Full-image understanding in Chinese; accepts paths and URLs
extract_text OCR — plain text, or block-level JSON with normalized coordinates
structured_scan Element detection (heading/text/table/image/chart/formula/button/list) with bboxes + confidence, schema-validated
query_region Region-focused query — the region is really cropped before inference
detect_elements Bbox-only localization for selected element types
locate_object Two-stage localization: coarse full-image → ×1.3 crop → fine → full-image bbox
compare_images Before/after visual regression: side-by-side composite, structured diff JSON
read_clipboard Windows clipboard image → exported PNG path
check_health Ollama reachability, model presence, config summary, cache size — zero inference

Project Structure

dsh-vision-bridge/
├── package.json              # dsh.bundle + dsh.client manifests, self-contained deps
├── cordis.patch.yml          # loader row (bundle layer)
├── client.js                 # browser half: paste interception + verdict protocol
├── README.md / README.zh-CN.md
├── scripts/sync-core.mjs     # re-sync lib/core from the MCP vision bridge
└── lib/
    ├── index.js              # host plugin: tools, paste route, adapter registration
    ├── deepseek-vision.mjs   # vision twin route (extends the official DeepSeek adapter)
    └── core/                 # host-agnostic vision core (vendored, self-contained)
        ├── vision-core.mjs   #   tools + prompts + retry/validation orchestration
        ├── image.js          #   preprocessing, crop, data URLs
        ├── validate.js       #   schema validation + retry hints
        ├── cache.js          #   content-hash LRU
        ├── queue.js          #   serial inference queue
        ├── errors.js         #   error taxonomy (ollama_down / model_not_found / …)
        ├── grounding.js      #   two-stage localization math
        ├── compare.js        #   side-by-side comparison composition
        └── clipboard.js      #   Windows clipboard reader

Security & Privacy

  • Images never leave your machine. The vision engine is localhost Ollama; the twin route sends only the bridge's text analysis to DeepSeek's API.
  • Pasted bytes are magic-byte checked (PNG/JPEG/WebP/GIF), size-capped (25 MB), and stored 0600 in fresh unpredictable temp directories.
  • The twin reuses the official route's credential resolution — no second key, no plaintext config.
  • Paste hijacking is strictly evidence-based: without a positive text-only confirmation the native path stays untouched.

Requirements

Component Requirement
DeepSeek Harness web profile, rc.5+ (tested on 0.1.0-rc.5)
Node.js ≥ 22.19
Ollama running, with a vision model (tested: qwen2.5vl:7b)
API key DEEPSEEK_API_KEY for the twin route (same as official)

Troubleshooting

Symptom Cause / fix
Paste still inserts a path text The session is on the official route — select the (视觉桥) variant, or check the latest verdict entry in ~/.dsh/vision-bridge-activity.jsonl for the real selector label
Tools report [ollama_down] ollama serve not running, or the model missing (ollama pull qwen2.5vl:7b)
Twin route fails with MISSING_CREDENTIAL Store DEEPSEEK_API_KEY on the Web Models page or export it in the environment
read_image refuses on the twin Only possible if the twin's metadata is not loaded — restart dsh after installing
Plugin changes not active Bundle layers load at boot; restart dsh web after dsh plugin operations

License

MIT. The vision core originates from the author's MCP vision bridge project.

CLASSIFICATION EVIDENCE

分类依据

项目类型插件
功能分类文件与数据
规则置信度

系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: vision。