dsh-tool-vision 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -3
- package/README.zh.md +43 -1
- package/client.js +11 -5
- package/cordis.patch.yml +8 -8
- package/index.js +22 -2
- package/lib/vision-tools.js +2481 -0
- package/package.json +68 -62
- package/vendor/dsh-settings-expose.js +75 -75
package/README.md
CHANGED
|
@@ -169,8 +169,53 @@ Example endpoints (`baseURL`):
|
|
|
169
169
|
> once more** and the section appears. A dsh update overwrites the patch;
|
|
170
170
|
> the next plugin start re-applies it.
|
|
171
171
|
> - Settings changes hot-apply (no restart needed).
|
|
172
|
-
> - Tested against DSH `0.1.0-rc.6`.
|
|
173
|
-
|
|
172
|
+
> - Tested against DSH `0.1.0-rc.6`, `0.1.0-rc.7`, and `0.1.0-rc.8`.
|
|
173
|
+
|
|
174
|
+
## Pixel-level vision tools (v0.6.0, ported from dsh-vision-router)
|
|
175
|
+
|
|
176
|
+
14 `vision_*` tools driven by the **same** configured endpoint as
|
|
177
|
+
`inspect_image` (baseURL/apiKey/model) — no provider chain, no local models,
|
|
178
|
+
no extra settings:
|
|
179
|
+
|
|
180
|
+
| Tool | Purpose |
|
|
181
|
+
|---|---|
|
|
182
|
+
| `vision_describe` | Image Q&A / multi-image comparison (optional structured JSON) |
|
|
183
|
+
| `vision_ground` | Locate a target and return its ORIGINAL-pixel bounding box |
|
|
184
|
+
| `vision_detect` | Enumerate elements (buttons, inputs, icons…) with numbered boxes |
|
|
185
|
+
| `vision_crop` | Crop a pixel region to a PNG artifact |
|
|
186
|
+
| `vision_pixel_diff` | Per-pixel comparison: ratio, worst regions, heatmap, report |
|
|
187
|
+
| `vision_colors` | Dominant-color quantization for palette matching |
|
|
188
|
+
| `vision_ocr` | Verbatim text transcription (letters only — not scene analysis) |
|
|
189
|
+
| `vision_long_screenshot_ocr` | Chunked long-screenshot transcription into Markdown |
|
|
190
|
+
| `vision_trace` | Potrace vectorization into colored SVG (worker-thread, safe) |
|
|
191
|
+
| `vision_extract_foreground` | Solid-background removal → transparent PNG |
|
|
192
|
+
| `vision_html_screenshot` | Headless render of a local .html (network blocked) |
|
|
193
|
+
| `vision_screenshot` | Desktop capture (privacy-gated: enable `desktopScreenshot` in settings; Win: PowerShell / macOS: screencapture / Linux: import/scrot) |
|
|
194
|
+
| `vision_present` | Publish a generated image to the user via the host attachment store |
|
|
195
|
+
| `vision_materialize` | Copy an attachment/local image into the workspace as a real path |
|
|
196
|
+
|
|
197
|
+
Quality & safety details:
|
|
198
|
+
|
|
199
|
+
- **Content-hash cache** keyed by endpoint+model+image+question (no stale
|
|
200
|
+
answers across model switches, failures are never cached).
|
|
201
|
+
- **Uniform 4MP downscale** before every model call; oversized inputs are
|
|
202
|
+
rejected with a clear error (stat pre-check, 20MB cap on both file and
|
|
203
|
+
attachment paths).
|
|
204
|
+
- **Rate-limit / 5xx auto-retry** with Retry-After-aware backoff; endpoint
|
|
205
|
+
**content-safety rejections** are surfaced as `VISION_CONTENT_FILTERED`
|
|
206
|
+
instead of a generic backend error.
|
|
207
|
+
- **Long-OCR bounds**: 120s total budget, 40-chunk cap, cancellation checks,
|
|
208
|
+
stop-on-first-backend-failure.
|
|
209
|
+
- **Path containment** for relative inputs; artifacts land in
|
|
210
|
+
`<workspace>/.dsh-tool-vision/`.
|
|
211
|
+
|
|
212
|
+
Requires `sharp` / `potrace` / `puppeteer-core` (declared as optional
|
|
213
|
+
dependencies: a failed platform install never blocks the plugin; missing ones
|
|
214
|
+
degrade lazily with an install hint and never break other tools).
|
|
215
|
+
|
|
216
|
+
`vision_screenshot` is privacy-sensitive and therefore **not registered by
|
|
217
|
+
default** — set `desktopScreenshot: true` in the tool-vision settings to
|
|
218
|
+
enable desktop capture.
|
|
174
219
|
|
|
175
220
|
## Limitations
|
|
176
221
|
|
|
@@ -183,4 +228,6 @@ Example endpoints (`baseURL`):
|
|
|
183
228
|
|
|
184
229
|
## License
|
|
185
230
|
|
|
186
|
-
MIT
|
|
231
|
+
MIT — bridge preview & integration: xing666173. Pixel vision tools ported
|
|
232
|
+
from [dsh-vision-router](https://github.com/ysr666/dsh-vision-router)
|
|
233
|
+
(© ysr666, MIT) with gratitude.
|
package/README.zh.md
CHANGED
|
@@ -113,6 +113,46 @@ DeepSeek 自家模型是纯文本的,而且 harness 的每次模型请求都**
|
|
|
113
113
|
- **Moonshot(Kimi)**:`https://api.moonshot.cn/v1` —— `moonshot-v1-8k-vision-preview`
|
|
114
114
|
- **Ollama 本地**:`http://localhost:11434/v1` —— `llama3.2-vision`(无需密钥)
|
|
115
115
|
|
|
116
|
+
## 像素级视觉工具(v0.6.0,移植自 dsh-vision-router)
|
|
117
|
+
|
|
118
|
+
14 个 `vision_*` 工具由**同一个** `inspect_image` 配置的端点驱动
|
|
119
|
+
(baseURL/apiKey/model)——无 provider 链、无本地模型、零新增配置:
|
|
120
|
+
|
|
121
|
+
| 工具 | 用途 |
|
|
122
|
+
|---|---|
|
|
123
|
+
| `vision_describe` | 看图问答 / 多图对比(可选结构化 JSON) |
|
|
124
|
+
| `vision_ground` | 定位目标,返回原图像素坐标框 |
|
|
125
|
+
| `vision_detect` | 枚举元素(按钮/输入框/图标…),带编号框 |
|
|
126
|
+
| `vision_crop` | 按像素区域裁剪出 PNG 产物 |
|
|
127
|
+
| `vision_pixel_diff` | 逐像素对比:差异比例、最差区域、热图、报告 |
|
|
128
|
+
| `vision_colors` | 主色量化,还原 UI 调色板 |
|
|
129
|
+
| `vision_ocr` | 逐字转写文字(只读字,不做场景识别) |
|
|
130
|
+
| `vision_long_screenshot_ocr` | 长截图分块转写为 Markdown |
|
|
131
|
+
| `vision_trace` | potrace 矢量化输出彩色 SVG(worker 线程,安全) |
|
|
132
|
+
| `vision_extract_foreground` | 纯色背景抠图 → 透明 PNG |
|
|
133
|
+
| `vision_html_screenshot` | 本地 HTML 无头渲染截图(禁网) |
|
|
134
|
+
| `vision_screenshot` | 桌面截屏(隐私门控:需在设置中开启 `desktopScreenshot`;Win: PowerShell / macOS: screencapture / Linux: import/scrot) |
|
|
135
|
+
| `vision_present` | 通过宿主附件库把生成的图片正式展示给用户 |
|
|
136
|
+
| `vision_materialize` | 把附件/本地图片落盘为工作区真实路径 |
|
|
137
|
+
|
|
138
|
+
质量与安全细节:
|
|
139
|
+
|
|
140
|
+
- **内容哈希缓存**按 端点+模型+图片+问题 取键(切模型不吃旧答案,失败结果
|
|
141
|
+
不入缓存);
|
|
142
|
+
- **统一 4MP 降采样**后再调用模型;超大输入 stat 预检直接拒绝(文件与
|
|
143
|
+
附件路径统一 20MB 上限);
|
|
144
|
+
- **限流/5xx 自动重试**(感知 Retry-After 退避);端点**内容安全拒绝**明确
|
|
145
|
+
返回 `VISION_CONTENT_FILTERED`,不再误报后端不可用;
|
|
146
|
+
- **长截图 OCR 边界**:120s 总预算、40 块上限、取消检查、首块失败即停;
|
|
147
|
+
- **路径 containment**(相对输入禁止逃逸工作区);产物写入
|
|
148
|
+
`<工作区>/.dsh-tool-vision/`。
|
|
149
|
+
|
|
150
|
+
依赖 `sharp` / `potrace` / `puppeteer-core`(声明为**可选依赖**:平台安装失败
|
|
151
|
+
不会阻断插件安装;缺失时懒加载降级并给出安装提示,不影响其他工具)。
|
|
152
|
+
|
|
153
|
+
`vision_screenshot` 属于隐私敏感能力,**默认不注册**——在 tool-vision 设置中
|
|
154
|
+
开启 `desktopScreenshot: true` 后才会注册桌面截屏工具。
|
|
155
|
+
|
|
116
156
|
## 限制
|
|
117
157
|
|
|
118
158
|
- 被桥接的图片以文本指引进入对话(转录而非像素)——文本模型无法做像素级上下文推理;视觉模型的描述通过 `inspect_image` 回传。
|
|
@@ -121,4 +161,6 @@ DeepSeek 自家模型是纯文本的,而且 harness 的每次模型请求都**
|
|
|
121
161
|
|
|
122
162
|
## License
|
|
123
163
|
|
|
124
|
-
MIT
|
|
164
|
+
MIT —— 桥接预览与整合:xing666173。像素级视觉工具移植自
|
|
165
|
+
[dsh-vision-router](https://github.com/ysr666/dsh-vision-router)(© ysr666,MIT),
|
|
166
|
+
在此致谢。
|
package/client.js
CHANGED
|
@@ -79,10 +79,12 @@ window.__ModuleLoader__.load({
|
|
|
79
79
|
fieldBridgePreviewScanIntervalMs: "预览兜底扫描间隔(毫秒,0 = 关闭兜底)",
|
|
80
80
|
fieldBridgePreviewHideHint: "图片加载成功后隐藏桥接提示文本",
|
|
81
81
|
fieldBridgeAutoImage: "自动声明图片能力(纯文本模型也能粘贴图片)",
|
|
82
|
+
fieldDesktopScreenshot: "桌面截屏工具(vision_screenshot,默认关闭)",
|
|
82
83
|
hintBridgePreview: "纯展示层:不影响模型侧文本与 inspect_image 调用。",
|
|
83
84
|
hintBridgePreviewScanIntervalMs: "默认 2000ms;越小响应越快,越大越省资源。",
|
|
84
85
|
hintBridgePreviewHideHint: "加载失败时保留文本(安全降级,绝不出现既无图又无字)。",
|
|
85
86
|
hintBridgeAutoImage: "桥接开启时向宿主报告所有模型支持图片,绕过“模型不支持图片”的发送拦截;实际仍走桥接转文本。",
|
|
87
|
+
hintDesktopScreenshot: "隐私敏感能力:仅在开启后注册 vision_screenshot 桌面截屏工具,模型才可截取用户屏幕。",
|
|
86
88
|
save: "保存",
|
|
87
89
|
reset: "恢复默认",
|
|
88
90
|
saved: "已保存",
|
|
@@ -116,10 +118,12 @@ window.__ModuleLoader__.load({
|
|
|
116
118
|
fieldBridgePreviewScanIntervalMs: "Preview fallback scan interval (ms, 0 = disable)",
|
|
117
119
|
fieldBridgePreviewHideHint: "Hide the bridged hint text once the image has loaded",
|
|
118
120
|
fieldBridgeAutoImage: "Auto-declare image capability (paste images on text-only models)",
|
|
121
|
+
fieldDesktopScreenshot: "Desktop screenshot tool (vision_screenshot, off by default)",
|
|
119
122
|
hintBridgePreview: "Pure display layer: the model-facing text and the inspect_image chain are untouched.",
|
|
120
123
|
hintBridgePreviewScanIntervalMs: "Default 2000ms; lower is snappier, higher is cheaper.",
|
|
121
124
|
hintBridgePreviewHideHint: "Text is kept on load failure (safe degradation, never no image AND no text).",
|
|
122
125
|
hintBridgeAutoImage: "While the bridge is on, report image support for every model to bypass the host image-send gate; images still travel as bridged text hints.",
|
|
126
|
+
hintDesktopScreenshot: "Privacy-sensitive: the vision_screenshot desktop-capture tool is only registered (and visible to the model) when enabled.",
|
|
123
127
|
save: "Save",
|
|
124
128
|
reset: "Reset",
|
|
125
129
|
saved: "Saved",
|
|
@@ -145,7 +149,8 @@ window.__ModuleLoader__.load({
|
|
|
145
149
|
{ key: "bridgePreview", label: "fieldBridgePreview", type: "checkbox" },
|
|
146
150
|
{ key: "bridgePreviewScanIntervalMs", label: "fieldBridgePreviewScanIntervalMs", type: "number" },
|
|
147
151
|
{ key: "bridgePreviewHideHint", label: "fieldBridgePreviewHideHint", type: "checkbox" },
|
|
148
|
-
{ key: "bridgeAutoImage", label: "fieldBridgeAutoImage", type: "checkbox" }
|
|
152
|
+
{ key: "bridgeAutoImage", label: "fieldBridgeAutoImage", type: "checkbox" },
|
|
153
|
+
{ key: "desktopScreenshot", label: "fieldDesktopScreenshot", type: "checkbox" }
|
|
149
154
|
];
|
|
150
155
|
var ZH_HINTS = {
|
|
151
156
|
apiKey: "apiKeyHint",
|
|
@@ -158,7 +163,8 @@ window.__ModuleLoader__.load({
|
|
|
158
163
|
bridgePreview: "hintBridgePreview",
|
|
159
164
|
bridgePreviewScanIntervalMs: "hintBridgePreviewScanIntervalMs",
|
|
160
165
|
bridgePreviewHideHint: "hintBridgePreviewHideHint",
|
|
161
|
-
bridgeAutoImage: "hintBridgeAutoImage"
|
|
166
|
+
bridgeAutoImage: "hintBridgeAutoImage",
|
|
167
|
+
desktopScreenshot: "hintDesktopScreenshot"
|
|
162
168
|
};
|
|
163
169
|
|
|
164
170
|
function labelOf(f, t) {
|
|
@@ -177,7 +183,7 @@ window.__ModuleLoader__.load({
|
|
|
177
183
|
var [error, setError] = react.useState(null);
|
|
178
184
|
|
|
179
185
|
react.useEffect(function () {
|
|
180
|
-
scope.load();
|
|
186
|
+
if (typeof scope.load === "function") scope.load();
|
|
181
187
|
var alive = true;
|
|
182
188
|
var sync = function () { if (alive) setSnapshot(scope.getSnapshot()); };
|
|
183
189
|
var un = typeof scope.subscribe === "function" ? scope.subscribe(sync) : null;
|
|
@@ -240,7 +246,7 @@ window.__ModuleLoader__.load({
|
|
|
240
246
|
});
|
|
241
247
|
Promise.all(writes).then(function () {
|
|
242
248
|
setBusy(false); setNotice(t("saved"));
|
|
243
|
-
if (scope.load) scope.load();
|
|
249
|
+
if (typeof scope.load === "function") scope.load();
|
|
244
250
|
}).catch(function (e) {
|
|
245
251
|
setBusy(false); setError(t("error") + ": " + String(e && e.message || e));
|
|
246
252
|
});
|
|
@@ -476,7 +482,7 @@ window.__ModuleLoader__.load({
|
|
|
476
482
|
var t = ctx.locale.bind(NS);
|
|
477
483
|
ctx.effect(function () { return ctx.locale.register(NS, { zh: zh, en: en }); }, "dsh-tool-vision: dictionaries");
|
|
478
484
|
var scope = ctx.settingsScope.bind({ namespace: "tool-vision" });
|
|
479
|
-
scope.load();
|
|
485
|
+
if (typeof scope.load === "function") scope.load();
|
|
480
486
|
attachBridgePreview(ctx, scope);
|
|
481
487
|
ctx.slots.inject("settings.section", function () {
|
|
482
488
|
return ctx.slots.register({
|
package/cordis.patch.yml
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# dsh-tool-vision — bundle patch layer
|
|
2
|
-
#
|
|
3
|
-
# Declares the plugin as a Cordis loader entry, making the package a standard
|
|
4
|
-
# profile bundle: `dsh plugin --profile <p> add dsh-tool-vision` installs and
|
|
5
|
-
# mounts it without manual cordis.patch.yml edits.
|
|
6
|
-
- insert:
|
|
7
|
-
- id: tool-vision
|
|
8
|
-
name: 'dsh-tool-vision'
|
|
1
|
+
# dsh-tool-vision — bundle patch layer
|
|
2
|
+
#
|
|
3
|
+
# Declares the plugin as a Cordis loader entry, making the package a standard
|
|
4
|
+
# profile bundle: `dsh plugin --profile <p> add dsh-tool-vision` installs and
|
|
5
|
+
# mounts it without manual cordis.patch.yml edits.
|
|
6
|
+
- insert:
|
|
7
|
+
- id: tool-vision
|
|
8
|
+
name: 'dsh-tool-vision'
|
package/index.js
CHANGED
|
@@ -43,6 +43,7 @@ import z from "@deepseek-ai/schemastery";
|
|
|
43
43
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
44
44
|
import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
45
45
|
import { ensureSettingsNamespaceExposed } from "./vendor/dsh-settings-expose.js";
|
|
46
|
+
import { registerVisionTools, CONTENT_FILTER_RE } from "./lib/vision-tools.js";
|
|
46
47
|
|
|
47
48
|
/** Cordis plugin name. */
|
|
48
49
|
const name = "tool-vision";
|
|
@@ -110,6 +111,8 @@ const Config = z.object({
|
|
|
110
111
|
bridgePreviewScanIntervalMs: z.number().default(2000),
|
|
111
112
|
/** Hide the bridged hint text once the preview image has loaded (kept on failure — never "no image AND no text"). */
|
|
112
113
|
bridgePreviewHideHint: z.boolean().default(true),
|
|
114
|
+
/** Privacy gate for vision_screenshot: desktop capture is only registered when explicitly enabled. */
|
|
115
|
+
desktopScreenshot: z.boolean().default(false),
|
|
113
116
|
/**
|
|
114
117
|
* Advertise image input capability for every model while the bridge is on.
|
|
115
118
|
* The host admission gate (host-apiproxy `prompt`/`selectModel`) refuses
|
|
@@ -611,10 +614,27 @@ function apply(ctx, config) {
|
|
|
611
614
|
const cfg = getConfig();
|
|
612
615
|
const cwd = exec.agent?.session?.header?.cwd ?? process.cwd();
|
|
613
616
|
const { url, note } = await toImageUrl(args.path, cwd, cfg);
|
|
614
|
-
|
|
615
|
-
|
|
617
|
+
try {
|
|
618
|
+
const answer = await callVision(cfg, url, args.question, args.detail, exec.signal);
|
|
619
|
+
return note === url ? answer : `${answer}\n\n(image: ${note})`;
|
|
620
|
+
} catch (error) {
|
|
621
|
+
const raw = error && error.message ? String(error.message) : String(error);
|
|
622
|
+
if (CONTENT_FILTER_RE.test(raw)) {
|
|
623
|
+
throw new Error(
|
|
624
|
+
"inspect_image: 图片被视觉端点的内容安全策略拒绝(检测到敏感或不安全内容)。" +
|
|
625
|
+
"这不是网络或配置问题,请换一张图片或调整图片内容后再试。",
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
throw error;
|
|
629
|
+
}
|
|
616
630
|
},
|
|
617
631
|
}));
|
|
632
|
+
|
|
633
|
+
// ── pixel-level vision tools (ported from dsh-vision-router) ─────────────
|
|
634
|
+
// 14 vision_* tools driven by the SAME configured endpoint as inspect_image
|
|
635
|
+
// (baseURL/apiKey/model). No provider chain, no local models, no extra
|
|
636
|
+
// settings: everything comes from the existing tool-vision configuration.
|
|
637
|
+
registerVisionTools(ctx, getConfig);
|
|
618
638
|
}
|
|
619
639
|
|
|
620
640
|
export {
|