dsh-tool-vision 0.6.1 → 0.6.3
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 +12 -10
- package/README.zh.md +4 -1
- package/client.js +1 -0
- package/index.js +0 -7
- package/package.json +4 -3
- package/vendor/dsh-settings-expose.js +0 -75
package/README.md
CHANGED
|
@@ -10,10 +10,12 @@ Part of the [DeepSeek Harness Enhancement Suite](https://github.com/Scorp1o117/d
|
|
|
10
10
|
|
|
11
11
|
External vision model for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness).
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
DSH 0.1.1 adds native image input for DeepSeek's vision catalog. This plugin
|
|
14
|
+
remains useful when you want a separate OpenAI-compatible vision endpoint,
|
|
15
|
+
pixel-level image tools, screenshots, or a text-model bridge. The harness
|
|
16
|
+
derives every model request strictly from the session log (`llm/stream`
|
|
17
|
+
requests must equal the durable derivation — the agent-loop invariant), so the
|
|
18
|
+
bridge keeps its conversion inside that durable path:
|
|
17
19
|
|
|
18
20
|
1. **`inspect_image` tool** — sends an image (local file, or http(s) URL) to
|
|
19
21
|
**any OpenAI-compatible** `/chat/completions` endpoint that supports
|
|
@@ -164,12 +166,11 @@ Example endpoints (`baseURL`):
|
|
|
164
166
|
> - This plugin is a standard **profile bundle** (`dsh.bundle.patch`):
|
|
165
167
|
> `dsh plugin --profile web add dsh-tool-vision` installs and mounts it in
|
|
166
168
|
> one step — no manual `cordis.patch.yml` edits needed.
|
|
167
|
-
> -
|
|
168
|
-
>
|
|
169
|
-
>
|
|
170
|
-
>
|
|
171
|
-
>
|
|
172
|
-
> - Tested against DSH `0.1.0-rc.6`, `0.1.0-rc.7`, and `0.1.0-rc.8`.
|
|
169
|
+
> - Settings changes hot-apply (no restart needed).
|
|
170
|
+
> - Version 0.6.3 and newer require DSH `0.1.0-rc.7` or newer and are tested
|
|
171
|
+
> against `0.1.0-rc.7`, `0.1.0-rc.8`, and `0.1.1-rc.1`.
|
|
172
|
+
> - DSH `0.1.0-rc.6` users must pin `dsh-tool-vision@0.6.1`, the last release
|
|
173
|
+
> carrying the legacy settings-allowlist compatibility patch.
|
|
173
174
|
|
|
174
175
|
## Pixel-level vision tools (v0.6.0, ported from dsh-vision-router)
|
|
175
176
|
|
|
@@ -231,3 +232,4 @@ enable desktop capture.
|
|
|
231
232
|
MIT — bridge preview & integration: xing666173. Pixel vision tools ported
|
|
232
233
|
from [dsh-vision-router](https://github.com/ysr666/dsh-vision-router)
|
|
233
234
|
(© ysr666, MIT) with gratitude.
|
|
235
|
+
|
package/README.zh.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
给 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 外接**视觉模型**的插件。
|
|
10
10
|
|
|
11
|
-
DeepSeek
|
|
11
|
+
DSH 0.1.1 已为 DeepSeek 视觉模型目录加入原生图片输入。本插件继续提供独立的 OpenAI 兼容视觉端点、像素级图片工具、截图,以及文本模型图片桥。Harness 的每次模型请求都**严格从会话日志推导**(`llm/stream` 请求必须与持久化推导一致,否则 agent-loop invariant 会报 `log-reconstruction desync`),因此图片桥仍在可持久重建的路径内完成转换:
|
|
12
12
|
|
|
13
13
|
1. **`inspect_image` 工具** —— 把图片(本地文件或 http(s) URL)发给任意支持 `image_url` 内容块的 OpenAI 兼容 `/chat/completions` 端点,把视觉模型的文字回答带回对话。
|
|
14
14
|
2. **图片桥(v0.2.1)** —— 粘贴的图片在**进入持久化日志之前**就被转换成 `inspect_image` 指引文本,拦截点是 `agent/pre-step` waterfall(这是 harness 唯一允许插件替换"进入某一步的消息"的缝;替换后的消息会**成为**持久化的 `user/message` 日志,所以请求重建 invariant 天然满足)。旧版本已经写进日志的图片消息,会在该会话下一次 pre-step 时用 surface `replace` 惰性修复。只有 `multimodalModels` 白名单内的模型直收图片块;**不参考模型的 `inputModalities` 声明**——因为很多配置为了通过 prompt 准入检查,会给纯文本模型声明 `input: [text, image]`(那只是声明,不代表上游真的能吃 `image_url`)。
|
|
@@ -155,6 +155,9 @@ DeepSeek 自家模型是纯文本的,而且 harness 的每次模型请求都**
|
|
|
155
155
|
|
|
156
156
|
## 限制
|
|
157
157
|
|
|
158
|
+
- 从 `0.6.3` 起最低支持 DSH `0.1.0-rc.7`,已针对 `0.1.0-rc.7`、
|
|
159
|
+
`0.1.0-rc.8` 和 `0.1.1-rc.1` 测试。仍使用 DSH `0.1.0-rc.6` 的用户请锁定
|
|
160
|
+
`dsh-tool-vision@0.6.1`;这是最后一个包含旧 settings 白名单兼容补丁的版本。
|
|
158
161
|
- 被桥接的图片以文本指引进入对话(转录而非像素)——文本模型无法做像素级上下文推理;视觉模型的描述通过 `inspect_image` 回传。
|
|
159
162
|
- 图片以 base64 传输;注意隐私与大小限制。
|
|
160
163
|
- 独立于 dsh-llm 的路由/重试体系;失败会向 Agent 返回明确错误。
|
package/client.js
CHANGED
package/index.js
CHANGED
|
@@ -42,7 +42,6 @@ import os from "node:os";
|
|
|
42
42
|
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
|
-
import { ensureSettingsNamespaceExposed } from "./vendor/dsh-settings-expose.js";
|
|
46
45
|
import { registerVisionTools, CONTENT_FILTER_RE } from "./lib/vision-tools.js";
|
|
47
46
|
|
|
48
47
|
/** Cordis plugin name. */
|
|
@@ -556,12 +555,6 @@ function apply(ctx, config) {
|
|
|
556
555
|
onChange: () => {},
|
|
557
556
|
});
|
|
558
557
|
|
|
559
|
-
// dsh-host-apiproxy hard-codes which settings namespaces the Web client may
|
|
560
|
-
// see; without this, the settings section answers `settings-not-exposed`
|
|
561
|
-
// on any stock install. Patch the allowlist idempotently (self-heals after
|
|
562
|
-
// dsh updates overwrite the file).
|
|
563
|
-
ensureSettingsNamespaceExposed(ctx, "tool-vision", ctx.logger);
|
|
564
|
-
|
|
565
558
|
// ── image bridge: pasted images become inspect_image hints on text-only models ──
|
|
566
559
|
if (getConfig().bridgeTextOnly) {
|
|
567
560
|
const exportDir = getConfig().bridgeExportDir || join(os.tmpdir(), "dsh-vision-bridge");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-tool-vision",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "DeepSeek Harness 外置视觉模型插件:inspect_image 把本地图片或 http(s) 图片 URL 发给任意 OpenAI 兼容端点,视觉模型看图的文字回答直接带回对话;附 Web UI 设置栏。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -59,8 +59,8 @@
|
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
61
|
"react": "^18.2.0",
|
|
62
|
-
"@deepseek-ai/dsh-settings": "^0.1.0-rc.
|
|
63
|
-
"@deepseek-ai/dsh-tools": "^0.1.0-rc.
|
|
62
|
+
"@deepseek-ai/dsh-settings": "^0.1.0-rc.7 || ^0.1.1-rc.1",
|
|
63
|
+
"@deepseek-ai/dsh-tools": "^0.1.0-rc.7 || ^0.1.1-rc.1"
|
|
64
64
|
},
|
|
65
65
|
"optionalDependencies": {
|
|
66
66
|
"potrace": "^2.1.8",
|
|
@@ -68,3 +68,4 @@
|
|
|
68
68
|
"sharp": ">=0.35.3 <1"
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
+
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* dsh-host-apiproxy exposes only namespaces listed in its hard-coded
|
|
3
|
-
* WEB_SETTINGS_NAMESPACES allowlist to the Web settings client. A namespace
|
|
4
|
-
* registered by a third-party plugin answers `settings-not-exposed` even
|
|
5
|
-
* though it is registered — upstream explicitly defers letting plugins
|
|
6
|
-
* expose their own configuration.
|
|
7
|
-
*
|
|
8
|
-
* This module idempotently patches that allowlist in the dsh installation
|
|
9
|
-
* actually loaded by the host process, so the plugin's settings section
|
|
10
|
-
* becomes visible in the Web UI without manual edits. A dsh update
|
|
11
|
-
* overwrites the file; the next plugin start re-patches it.
|
|
12
|
-
*/
|
|
13
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
14
|
-
import { createRequire } from "node:module";
|
|
15
|
-
import { dirname, join, sep } from "node:path";
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Ensure `nsName` is present in dsh-host-apiproxy's WEB_SETTINGS_NAMESPACES.
|
|
19
|
-
* No-op when already exposed or when the file cannot be located/patched.
|
|
20
|
-
* @param {import("cordis").Context} ctx
|
|
21
|
-
* @param {string} nsName - settings namespace short name (e.g. "tdai-memory").
|
|
22
|
-
* @param {{info?: Function, warn?: Function}} logger - dsh logger.
|
|
23
|
-
*/
|
|
24
|
-
export function ensureSettingsNamespaceExposed(ctx, nsName, logger) {
|
|
25
|
-
void ctx;
|
|
26
|
-
try {
|
|
27
|
-
const target = findApiproxyIndex();
|
|
28
|
-
if (!target) {
|
|
29
|
-
logger?.warn?.(`[settings-expose] could not locate dsh-host-apiproxy; add "${nsName}" to WEB_SETTINGS_NAMESPACES in dsh-host-apiproxy/lib/index.js to get a Web settings section`);
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
let src;
|
|
33
|
-
try {
|
|
34
|
-
src = readFileSync(target, "utf8");
|
|
35
|
-
} catch (error) {
|
|
36
|
-
logger?.warn?.(`[settings-expose] cannot read ${target}: ${String(error)}`);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
const body = src.match(/const WEB_SETTINGS_NAMESPACES = \[([\s\S]*?)\];/)?.[1] ?? "";
|
|
40
|
-
if (body.includes(`"${nsName}"`)) return; // already exposed (manual or previous patch)
|
|
41
|
-
const patched = src.replace(/(const WEB_SETTINGS_NAMESPACES = \[[\s\S]*?)(\n\s*\];)/, (_, pre, post) => {
|
|
42
|
-
const trailingComma = /,\s*$/.test(pre) ? "" : ",";
|
|
43
|
-
const sep2 = pre.trimEnd().endsWith("[") ? "" : trailingComma;
|
|
44
|
-
return `${pre}${sep2}\n\t"${nsName}"${post}`;
|
|
45
|
-
});
|
|
46
|
-
if (patched === src) {
|
|
47
|
-
logger?.warn?.(`[settings-expose] allowlist pattern not found in ${target}; add "${nsName}" to WEB_SETTINGS_NAMESPACES manually`);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
writeFileSync(target, patched, "utf8");
|
|
51
|
-
logger?.info?.(`[settings-expose] added "${nsName}" to WEB_SETTINGS_NAMESPACES (${target}) — restart dsh web for the settings section to appear`);
|
|
52
|
-
} catch (error) {
|
|
53
|
-
logger?.warn?.(`[settings-expose] failed: ${String(error)}`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function findApiproxyIndex() {
|
|
58
|
-
// 1) The host process has already loaded dsh-host-apiproxy: read the real
|
|
59
|
-
// module path from the CommonJS module cache (any install layout).
|
|
60
|
-
try {
|
|
61
|
-
const Module = createRequire(import.meta.url)("module");
|
|
62
|
-
const cache = Module._cache ?? {};
|
|
63
|
-
for (const key of Object.keys(cache)) {
|
|
64
|
-
if (key.includes(`${sep}dsh-host-apiproxy${sep}`) && key.endsWith(`${sep}index.js`)) return key;
|
|
65
|
-
}
|
|
66
|
-
} catch { /* fall through */ }
|
|
67
|
-
// 2) Fallback: sibling of @deepseek-ai/dsh-settings (dsh's nested layout).
|
|
68
|
-
try {
|
|
69
|
-
const require = createRequire(import.meta.url);
|
|
70
|
-
const settingsEntry = require.resolve("@deepseek-ai/dsh-settings");
|
|
71
|
-
const candidate = join(dirname(dirname(dirname(settingsEntry))), "dsh-host-apiproxy", "lib", "index.js");
|
|
72
|
-
if (existsSync(candidate)) return candidate;
|
|
73
|
-
} catch { /* fall through */ }
|
|
74
|
-
return "";
|
|
75
|
-
}
|