dsh-tool-vision 0.3.10 → 0.5.0
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/LICENSE +23 -21
- package/README.md +186 -156
- package/README.zh.md +124 -108
- package/client.js +498 -316
- package/index.js +641 -432
- package/package.json +4 -1
package/LICENSE
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Scorp1o117
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Scorp1o117
|
|
4
|
+
Bridge image preview contributed by xing666173 (dsh-bridge-preview),
|
|
5
|
+
Copyright (c) 2026 xing666173
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,156 +1,186 @@
|
|
|
1
|
-
# dsh-tool-vision
|
|
2
|
-
|
|
3
|
-
[](README.zh.md)
|
|
4
|
-
|
|
5
|
-
**GitHub**: [Scorp1o117/dsh-tool-vision](https://github.com/Scorp1o117/dsh-tool-vision) · **npm**: [dsh-tool-vision](https://www.npmjs.com/package/dsh-tool-vision)
|
|
6
|
-
|
|
7
|
-
[](https://github.com/Scorp1o117/dsh-enhancement-suite) [](https://www.npmjs.com/package/dsh-enhancement-suite)
|
|
8
|
-
|
|
9
|
-
Part of the [DeepSeek Harness Enhancement Suite](https://github.com/Scorp1o117/dsh-enhancement-suite) — Vision · Soul/Persona · Long-term Memory · Plugin Marketplace.
|
|
10
|
-
|
|
11
|
-
External vision model for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness).
|
|
12
|
-
|
|
13
|
-
DeepSeek's own models are text-only, and the harness derives every model
|
|
14
|
-
request strictly from the session log (`llm/stream` requests must equal the
|
|
15
|
-
durable derivation — the agent-loop invariant). This plugin bridges the gap
|
|
16
|
-
in two ways:
|
|
17
|
-
|
|
18
|
-
1. **`inspect_image` tool** — sends an image (local file, or http(s) URL) to
|
|
19
|
-
**any OpenAI-compatible** `/chat/completions` endpoint that supports
|
|
20
|
-
`image_url` content parts, and returns the vision model's textual answer
|
|
21
|
-
into the agent loop.
|
|
22
|
-
2. **Image bridge (v0.2.1)** — pasted images are turned into `inspect_image`
|
|
23
|
-
hints *before they enter the durable log*, on the `agent/pre-step`
|
|
24
|
-
waterfall (the one seam where the harness lets a plugin replace the
|
|
25
|
-
messages of a proposed step). Images already logged by an older version
|
|
26
|
-
are repaired lazily with a surface `replace` on the session's first
|
|
27
|
-
pre-step. Only models listed in `multimodalModels` receive image blocks
|
|
28
|
-
directly; a model's declared `inputModalities` are never consulted,
|
|
29
|
-
because profiles routinely declare `input: [text, image]` on text-only
|
|
30
|
-
models just to pass the harness's prompt-admission check.
|
|
31
|
-
|
|
32
|
-
- Zero dependencies beyond the dsh SDK — works with any compatible endpoint:
|
|
33
|
-
OpenAI GPT-4o, Qwen-VL (DashScope), GLM-4V (Zhipu), Moonshot, Gemini
|
|
34
|
-
compatible endpoints, local Ollama, etc.
|
|
35
|
-
- Registered on the **global tools layer**: every agent in the process can
|
|
36
|
-
call `inspect_image`.
|
|
37
|
-
- **Web UI settings section (v0.3.0)**: Settings → 视觉模型 edits the
|
|
38
|
-
`tool-vision` namespace (API endpoint, write-only key, model, bridge
|
|
39
|
-
options) in `settings.yaml`; changes hot-apply without a restart. The API
|
|
40
|
-
key lives in `settings.yaml`, not the profile patch. Mount by package name
|
|
41
|
-
(`name: 'dsh-tool-vision'`) so the web client bundle is discovered.
|
|
42
|
-
|
|
43
|
-
## Install
|
|
44
|
-
|
|
45
|
-
Mount in a profile patch (`$DSH_HOME/profiles/<name>/cordis.patch.yml`):
|
|
46
|
-
|
|
47
|
-
```yaml
|
|
48
|
-
- insert:
|
|
49
|
-
- id: tool-vision
|
|
50
|
-
name: 'dsh-tool-vision' # after: pnpm add dsh-tool-vision in the profile
|
|
51
|
-
config:
|
|
52
|
-
baseURL: 'https://api.openai.com/v1'
|
|
53
|
-
apiKeyEnv: 'VISION_API_KEY'
|
|
54
|
-
model: 'gpt-4o-mini'
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Or load it from a local path without npm:
|
|
58
|
-
|
|
59
|
-
```yaml
|
|
60
|
-
- id: tool-vision
|
|
61
|
-
name: './plugins/dsh-tool-vision/index.js'
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## Config
|
|
65
|
-
|
|
66
|
-
| Field | Default | Meaning |
|
|
67
|
-
|---|---|---|
|
|
68
|
-
| `baseURL` | `https://api.openai.com/v1` | OpenAI-compatible API base URL. |
|
|
69
|
-
| `apiKey` | `''` | API key (takes precedence over env). |
|
|
70
|
-
| `apiKeyEnv` | `VISION_API_KEY` | Env var holding the key. |
|
|
71
|
-
| `model` | `gpt-4o-mini` | Vision model id. |
|
|
72
|
-
| `maxTokens` | `1024` | Max output tokens. |
|
|
73
|
-
| `timeoutMs` | `60000` | Per-request timeout. |
|
|
74
|
-
| `maxImageBytes` | `10MB` | Largest accepted local image. |
|
|
75
|
-
| `description` | default | Tool description shown to the model. |
|
|
76
|
-
| `bridgeTextOnly` | `true` | Bridge pasted images to text hints on models that cannot see images. |
|
|
77
|
-
| `bridgeExportDir` | temp | Export dir for bridged images (`os.tmpdir()/dsh-vision-bridge`). |
|
|
78
|
-
| `multimodalModels` | `[]` | Model ids that receive image blocks directly (e.g. `mimo-v2.5`). |
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
>
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
`
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
- **
|
|
128
|
-
|
|
129
|
-
- **
|
|
130
|
-
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
1
|
+
# dsh-tool-vision
|
|
2
|
+
|
|
3
|
+
[](README.zh.md)
|
|
4
|
+
|
|
5
|
+
**GitHub**: [Scorp1o117/dsh-tool-vision](https://github.com/Scorp1o117/dsh-tool-vision) · **npm**: [dsh-tool-vision](https://www.npmjs.com/package/dsh-tool-vision)
|
|
6
|
+
|
|
7
|
+
[](https://github.com/Scorp1o117/dsh-enhancement-suite) [](https://www.npmjs.com/package/dsh-enhancement-suite)
|
|
8
|
+
|
|
9
|
+
Part of the [DeepSeek Harness Enhancement Suite](https://github.com/Scorp1o117/dsh-enhancement-suite) — Vision · Soul/Persona · Long-term Memory · Plugin Marketplace.
|
|
10
|
+
|
|
11
|
+
External vision model for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness).
|
|
12
|
+
|
|
13
|
+
DeepSeek's own models are text-only, and the harness derives every model
|
|
14
|
+
request strictly from the session log (`llm/stream` requests must equal the
|
|
15
|
+
durable derivation — the agent-loop invariant). This plugin bridges the gap
|
|
16
|
+
in two ways:
|
|
17
|
+
|
|
18
|
+
1. **`inspect_image` tool** — sends an image (local file, or http(s) URL) to
|
|
19
|
+
**any OpenAI-compatible** `/chat/completions` endpoint that supports
|
|
20
|
+
`image_url` content parts, and returns the vision model's textual answer
|
|
21
|
+
into the agent loop.
|
|
22
|
+
2. **Image bridge (v0.2.1)** — pasted images are turned into `inspect_image`
|
|
23
|
+
hints *before they enter the durable log*, on the `agent/pre-step`
|
|
24
|
+
waterfall (the one seam where the harness lets a plugin replace the
|
|
25
|
+
messages of a proposed step). Images already logged by an older version
|
|
26
|
+
are repaired lazily with a surface `replace` on the session's first
|
|
27
|
+
pre-step. Only models listed in `multimodalModels` receive image blocks
|
|
28
|
+
directly; a model's declared `inputModalities` are never consulted,
|
|
29
|
+
because profiles routinely declare `input: [text, image]` on text-only
|
|
30
|
+
models just to pass the harness's prompt-admission check.
|
|
31
|
+
|
|
32
|
+
- Zero dependencies beyond the dsh SDK — works with any compatible endpoint:
|
|
33
|
+
OpenAI GPT-4o, Qwen-VL (DashScope), GLM-4V (Zhipu), Moonshot, Gemini
|
|
34
|
+
compatible endpoints, local Ollama, etc.
|
|
35
|
+
- Registered on the **global tools layer**: every agent in the process can
|
|
36
|
+
call `inspect_image`.
|
|
37
|
+
- **Web UI settings section (v0.3.0)**: Settings → 视觉模型 edits the
|
|
38
|
+
`tool-vision` namespace (API endpoint, write-only key, model, bridge
|
|
39
|
+
options) in `settings.yaml`; changes hot-apply without a restart. The API
|
|
40
|
+
key lives in `settings.yaml`, not the profile patch. Mount by package name
|
|
41
|
+
(`name: 'dsh-tool-vision'`) so the web client bundle is discovered.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
Mount in a profile patch (`$DSH_HOME/profiles/<name>/cordis.patch.yml`):
|
|
46
|
+
|
|
47
|
+
```yaml
|
|
48
|
+
- insert:
|
|
49
|
+
- id: tool-vision
|
|
50
|
+
name: 'dsh-tool-vision' # after: pnpm add dsh-tool-vision in the profile
|
|
51
|
+
config:
|
|
52
|
+
baseURL: 'https://api.openai.com/v1'
|
|
53
|
+
apiKeyEnv: 'VISION_API_KEY'
|
|
54
|
+
model: 'gpt-4o-mini'
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or load it from a local path without npm:
|
|
58
|
+
|
|
59
|
+
```yaml
|
|
60
|
+
- id: tool-vision
|
|
61
|
+
name: './plugins/dsh-tool-vision/index.js'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Config
|
|
65
|
+
|
|
66
|
+
| Field | Default | Meaning |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| `baseURL` | `https://api.openai.com/v1` | OpenAI-compatible API base URL. |
|
|
69
|
+
| `apiKey` | `''` | API key (takes precedence over env). |
|
|
70
|
+
| `apiKeyEnv` | `VISION_API_KEY` | Env var holding the key. |
|
|
71
|
+
| `model` | `gpt-4o-mini` | Vision model id. |
|
|
72
|
+
| `maxTokens` | `1024` | Max output tokens. |
|
|
73
|
+
| `timeoutMs` | `60000` | Per-request timeout. |
|
|
74
|
+
| `maxImageBytes` | `10MB` | Largest accepted local image. |
|
|
75
|
+
| `description` | default | Tool description shown to the model. |
|
|
76
|
+
| `bridgeTextOnly` | `true` | Bridge pasted images to text hints on models that cannot see images. |
|
|
77
|
+
| `bridgeExportDir` | temp | Export dir for bridged images (`os.tmpdir()/dsh-vision-bridge`). |
|
|
78
|
+
| `multimodalModels` | `[]` | Model ids that receive image blocks directly (e.g. `mimo-v2.5`). |
|
|
79
|
+
| `bridgePreview` | `true` | Inline preview for bridged images: thumbnail above the hint text in the user bubble (click to zoom). |
|
|
80
|
+
| `bridgePreviewScanIntervalMs` | `2000` | Fallback scan interval for the preview scanner (ms); `0` disables the fallback. |
|
|
81
|
+
| `bridgePreviewHideHint` | `true` | Hide the bridged hint text once the preview image has loaded (kept on failure — safe degradation). |
|
|
82
|
+
| `bridgeAutoImage` | `true` | While the bridge is on, report image input capability for **every** model to the host admission gate, so pasted images are accepted on text-only models without hand-editing provider configs. |
|
|
83
|
+
|
|
84
|
+
## Image bridge setup
|
|
85
|
+
|
|
86
|
+
1. (Optional, usually not needed) If `bridgeAutoImage` is disabled, declare
|
|
87
|
+
image input on the models you paste images onto, so the harness admits
|
|
88
|
+
image messages (pi-ai style):
|
|
89
|
+
```yaml
|
|
90
|
+
llm-pi-ai:
|
|
91
|
+
providers:
|
|
92
|
+
your-provider:
|
|
93
|
+
models:
|
|
94
|
+
- id: deepseek-v4-flash
|
|
95
|
+
input: [text, image]
|
|
96
|
+
```
|
|
97
|
+
2. List genuinely multimodal models in the plugin config so they receive
|
|
98
|
+
image blocks untouched:
|
|
99
|
+
```yaml
|
|
100
|
+
- id: tool-vision
|
|
101
|
+
name: 'dsh-tool-vision'
|
|
102
|
+
config:
|
|
103
|
+
multimodalModels: ['mimo-v2.5', 'grok-4.5']
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Then pasting an image while on a text-only model stores a hint like
|
|
107
|
+
`[User sent an image, exported to: <path>. Inspect it with the inspect_image tool...]`
|
|
108
|
+
in the transcript (the pasted image no longer renders as pixels in that
|
|
109
|
+
message), and the agent inspects it through the configured vision endpoint.
|
|
110
|
+
|
|
111
|
+
> Why not `llm/stream`? The harness freezes every request and the agent-loop
|
|
112
|
+
> invariant fails any request whose messages diverge from the session-log
|
|
113
|
+
> derivation (`log-reconstruction desync`), and this cordis waterfall's
|
|
114
|
+
> `next()` cannot replace request arguments. The `agent/pre-step` waterfall is
|
|
115
|
+
> the supported seam: its decision messages *become* the durable log, so the
|
|
116
|
+
> invariant stays satisfied.
|
|
117
|
+
|
|
118
|
+
Key resolution order: `config.apiKey` → `process.env[apiKeyEnv]` →
|
|
119
|
+
`process.env.OPENAI_API_KEY`.
|
|
120
|
+
|
|
121
|
+
## Bridge image preview (v0.4.0)
|
|
122
|
+
|
|
123
|
+
On text-only models, pasted images become `[User sent an image...]` hint
|
|
124
|
+
text in the transcript. With `bridgePreview` enabled (default), the browser
|
|
125
|
+
half renders those hints as inline thumbnails **in the display layer only**:
|
|
126
|
+
|
|
127
|
+
- **Thumbnail + lightbox**: click to zoom full-screen; click anywhere or
|
|
128
|
+
press `Esc` to close;
|
|
129
|
+
- **Immediate + fallback**: new messages are handled by a MutationObserver;
|
|
130
|
+
history is back-filled by a periodic scan (interval via
|
|
131
|
+
`bridgePreviewScanIntervalMs`);
|
|
132
|
+
- **Hide the hint (P2)**: with `bridgePreviewHideHint` on, the hint text is
|
|
133
|
+
hidden once the image has loaded, leaving just the image; on load failure
|
|
134
|
+
the text stays (safe degradation — never "no image AND no text");
|
|
135
|
+
- **Precise identification**: bridged hints carry an invisible prefix marker
|
|
136
|
+
(`\u200b[bridge]`), so ordinary user text that happens to contain
|
|
137
|
+
"exported to:" is never misidentified;
|
|
138
|
+
- **Display-layer red line**: persisted messages, the transcript, the
|
|
139
|
+
model-facing text and the `inspect_image` chain are untouched.
|
|
140
|
+
|
|
141
|
+
Preview images are served by the same-origin loopback route
|
|
142
|
+
`/plugins/dsh-tool-vision/image`: read-only access to the bridge export
|
|
143
|
+
directory, localhost-only Host, image extensions only, ≤ 20MB per file,
|
|
144
|
+
path-traversal protected.
|
|
145
|
+
|
|
146
|
+
## Tool: `inspect_image`
|
|
147
|
+
|
|
148
|
+
| Arg | Required | Meaning |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| `path` | ✅ | Image path (absolute, or relative to the current workspace) or http(s) URL. |
|
|
151
|
+
| `question` | – | Optional specific question about the image. |
|
|
152
|
+
| `detail` | – | `auto` / `low` / `high` resolution hint. |
|
|
153
|
+
|
|
154
|
+
Example endpoints (`baseURL`):
|
|
155
|
+
|
|
156
|
+
- **OpenAI**: `https://api.openai.com/v1` — `gpt-4o`, `gpt-4o-mini`
|
|
157
|
+
- **Alibaba DashScope (Qwen-VL)**: `https://dashscope.aliyuncs.com/compatible-mode/v1` — `qwen-vl-plus`, `qwen-vl-max`
|
|
158
|
+
- **Zhipu (GLM-4V)**: `https://open.bigmodel.cn/api/paas/v4` — `glm-4v-flash` (free tier), `glm-4v-plus`
|
|
159
|
+
- **Moonshot (Kimi)**: `https://api.moonshot.cn/v1` — `moonshot-v1-8k-vision-preview`
|
|
160
|
+
- **Ollama local**: `http://localhost:11434/v1` — `llama3.2-vision` (no key)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
> **Note for users**
|
|
164
|
+
> - This plugin is a standard **profile bundle** (`dsh.bundle.patch`):
|
|
165
|
+
> `dsh plugin --profile web add dsh-tool-vision` installs and mounts it in
|
|
166
|
+
> one step — no manual `cordis.patch.yml` edits needed.
|
|
167
|
+
> - The settings section needs the `dsh-host-apiproxy` namespace allowlist;
|
|
168
|
+
> the plugin patches it automatically on first start — **restart `dsh web`
|
|
169
|
+
> once more** and the section appears. A dsh update overwrites the patch;
|
|
170
|
+
> the next plugin start re-applies it.
|
|
171
|
+
> - Settings changes hot-apply (no restart needed).
|
|
172
|
+
> - Tested against DSH `0.1.0-rc.6`.
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
## Limitations
|
|
176
|
+
|
|
177
|
+
- A bridged image enters the conversation as a text hint (a transcript, not
|
|
178
|
+
pixels) — pixel-precise in-context reasoning is not available to text-only
|
|
179
|
+
models; the vision model's description comes back through `inspect_image`.
|
|
180
|
+
- Images are base64-transferred; mind privacy and size limits.
|
|
181
|
+
- Independent of the dsh-llm routing/retry system; failures return clear
|
|
182
|
+
errors to the agent.
|
|
183
|
+
|
|
184
|
+
## License
|
|
185
|
+
|
|
186
|
+
MIT
|