dsh-tool-vision 0.1.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 +21 -0
- package/README.md +80 -0
- package/README.zh.md +60 -0
- package/index.js +181 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Scorp1o117
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# dsh-tool-vision
|
|
2
|
+
|
|
3
|
+
External vision model for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness).
|
|
4
|
+
|
|
5
|
+
DeepSeek's own models are text-only, and dsh-llm has no multimodal content
|
|
6
|
+
block yet. This plugin bridges the gap with a model-facing tool that sends an
|
|
7
|
+
image (local file, or http(s) URL) to **any OpenAI-compatible**
|
|
8
|
+
`/chat/completions` endpoint that supports `image_url` content parts, and
|
|
9
|
+
returns the vision model's textual answer into the agent loop.
|
|
10
|
+
|
|
11
|
+
- Zero dependencies beyond the dsh SDK — works with any compatible endpoint:
|
|
12
|
+
OpenAI GPT-4o, Qwen-VL (DashScope), GLM-4V (Zhipu), Moonshot, Gemini
|
|
13
|
+
compatible endpoints, local Ollama, etc.
|
|
14
|
+
- Registered on the **global tools layer**: every agent in the process can
|
|
15
|
+
call `inspect_image`.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
Mount in a profile patch (`$DSH_HOME/profiles/<name>/cordis.patch.yml`):
|
|
20
|
+
|
|
21
|
+
```yaml
|
|
22
|
+
- insert:
|
|
23
|
+
- id: tool-vision
|
|
24
|
+
name: 'dsh-tool-vision' # after: pnpm add dsh-tool-vision in the profile
|
|
25
|
+
config:
|
|
26
|
+
baseURL: 'https://api.openai.com/v1'
|
|
27
|
+
apiKeyEnv: 'VISION_API_KEY'
|
|
28
|
+
model: 'gpt-4o-mini'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or load it from a local path without npm:
|
|
32
|
+
|
|
33
|
+
```yaml
|
|
34
|
+
- id: tool-vision
|
|
35
|
+
name: './plugins/dsh-tool-vision/index.js'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Config
|
|
39
|
+
|
|
40
|
+
| Field | Default | Meaning |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `baseURL` | `https://api.openai.com/v1` | OpenAI-compatible API base URL. |
|
|
43
|
+
| `apiKey` | `''` | API key (takes precedence over env). |
|
|
44
|
+
| `apiKeyEnv` | `VISION_API_KEY` | Env var holding the key. |
|
|
45
|
+
| `model` | `gpt-4o-mini` | Vision model id. |
|
|
46
|
+
| `maxTokens` | `1024` | Max output tokens. |
|
|
47
|
+
| `timeoutMs` | `60000` | Per-request timeout. |
|
|
48
|
+
| `maxImageBytes` | `10MB` | Largest accepted local image. |
|
|
49
|
+
| `description` | default | Tool description shown to the model. |
|
|
50
|
+
|
|
51
|
+
Key resolution order: `config.apiKey` → `process.env[apiKeyEnv]` →
|
|
52
|
+
`process.env.OPENAI_API_KEY`.
|
|
53
|
+
|
|
54
|
+
## Tool: `inspect_image`
|
|
55
|
+
|
|
56
|
+
| Arg | Required | Meaning |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| `path` | ✅ | Image path (absolute, or relative to the current workspace) or http(s) URL. |
|
|
59
|
+
| `question` | – | Optional specific question about the image. |
|
|
60
|
+
| `detail` | – | `auto` / `low` / `high` resolution hint. |
|
|
61
|
+
|
|
62
|
+
Example endpoints (`baseURL`):
|
|
63
|
+
|
|
64
|
+
- **OpenAI**: `https://api.openai.com/v1` — `gpt-4o`, `gpt-4o-mini`
|
|
65
|
+
- **Alibaba DashScope (Qwen-VL)**: `https://dashscope.aliyuncs.com/compatible-mode/v1` — `qwen-vl-plus`, `qwen-vl-max`
|
|
66
|
+
- **Zhipu (GLM-4V)**: `https://open.bigmodel.cn/api/paas/v4` — `glm-4v-flash` (free tier), `glm-4v-plus`
|
|
67
|
+
- **Moonshot (Kimi)**: `https://api.moonshot.cn/v1` — `moonshot-v1-8k-vision-preview`
|
|
68
|
+
- **Ollama local**: `http://localhost:11434/v1` — `llama3.2-vision` (no key)
|
|
69
|
+
|
|
70
|
+
## Limitations
|
|
71
|
+
|
|
72
|
+
- The image enters the conversation as a text description (a transcript, not
|
|
73
|
+
pixels) — pixel-precise tasks may be inaccurate.
|
|
74
|
+
- Images are base64-transferred; mind privacy and size limits.
|
|
75
|
+
- Independent of the dsh-llm routing/retry system; failures return clear
|
|
76
|
+
errors to the agent.
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
MIT
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# dsh-tool-vision
|
|
2
|
+
|
|
3
|
+
给 DeepSeek Harness 外接**视觉模型**的工具插件。DeepSeek 自家模型是纯文本的,而 dsh-llm 目前还没有多模态内容块类型——本插件用一条 OpenAI 兼容接口补上这个缺口:
|
|
4
|
+
|
|
5
|
+
Agent 调用工具 `inspect_image`,把本地图片(或 http(s) 图片 URL)发给任意兼容 `image_url` 内容块的 `/chat/completions` 端点,把视觉模型的文字回答带回对话。
|
|
6
|
+
|
|
7
|
+
- 注册在**全局工具层**:进程内所有 Agent 都能调用 `inspect_image`
|
|
8
|
+
- 支持任意 OpenAI 兼容视觉端点:OpenAI GPT-4o / Qwen-VL (DashScope) / GLM-4V (智谱) / Moonshot / Gemini 兼容端点 / Ollama 本地等
|
|
9
|
+
- 本地图片自动转 base64 data URL,检查格式与大小上限
|
|
10
|
+
|
|
11
|
+
## 配置
|
|
12
|
+
|
|
13
|
+
| 字段 | 默认值 | 含义 |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| `baseURL` | `https://api.openai.com/v1` | OpenAI 兼容 API 基地址 |
|
|
16
|
+
| `apiKey` | `''` | API 密钥(优先于环境变量) |
|
|
17
|
+
| `apiKeyEnv` | `VISION_API_KEY` | 存放密钥的环境变量名 |
|
|
18
|
+
| `model` | `gpt-4o-mini` | 视觉模型 id |
|
|
19
|
+
| `maxTokens` | `1024` | 视觉调用最大输出 token |
|
|
20
|
+
| `timeoutMs` | `60000` | 单次请求超时 |
|
|
21
|
+
| `maxImageBytes` | `10MB` | 本地图片大小上限 |
|
|
22
|
+
| `description` | 默认描述 | 工具描述(模型可见) |
|
|
23
|
+
|
|
24
|
+
密钥解析顺序:`config.apiKey` → `process.env[apiKeyEnv]` → `process.env.OPENAI_API_KEY`。
|
|
25
|
+
|
|
26
|
+
## 挂载
|
|
27
|
+
|
|
28
|
+
在 profile 的 `cordis.patch.yml` 里 insert:
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
- insert:
|
|
32
|
+
- id: tool-vision
|
|
33
|
+
name: './plugins/dsh-tool-vision/index.js'
|
|
34
|
+
config:
|
|
35
|
+
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1'
|
|
36
|
+
apiKeyEnv: 'DASHSCOPE_API_KEY'
|
|
37
|
+
model: 'qwen-vl-plus'
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 工具:`inspect_image`
|
|
41
|
+
|
|
42
|
+
| 参数 | 必填 | 说明 |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `path` | ✅ | 图片路径(绝对路径,或相对当前 workspace)或 http(s) URL |
|
|
45
|
+
| `question` | – | 针对图片的具体问题;缺省为"详细描述" |
|
|
46
|
+
| `detail` | – | `auto` / `low` / `high`,图片分辨率提示 |
|
|
47
|
+
|
|
48
|
+
示例端点(`baseURL` 写法):
|
|
49
|
+
|
|
50
|
+
- **OpenAI**:`https://api.openai.com/v1`,模型 `gpt-4o` / `gpt-4o-mini`
|
|
51
|
+
- **阿里云百炼 (Qwen-VL)**:`https://dashscope.aliyuncs.com/compatible-mode/v1`,模型 `qwen-vl-plus` / `qwen-vl-max`
|
|
52
|
+
- **智谱 (GLM-4V)**:`https://open.bigmodel.cn/api/paas/v4`,模型 `glm-4v-flash`(免费档)/ `glm-4v-plus`
|
|
53
|
+
- **Moonshot (Kimi)**:`https://api.moonshot.cn/v1`,模型 `moonshot-v1-8k-vision-preview`
|
|
54
|
+
- **Ollama 本地**:`http://localhost:11434/v1`,模型 `llama3.2-vision`(无需 key)
|
|
55
|
+
|
|
56
|
+
## 局限
|
|
57
|
+
|
|
58
|
+
- 图片内容以文字描述进入对话,Agent 得到的是"转述"而非像素——精确到像素的任务(如测量)可能不够准
|
|
59
|
+
- 图片会在请求中 base64 传输,注意隐私与大小
|
|
60
|
+
- 不依赖 dsh-llm 服务,因此不走模型路由/重试体系;失败会以明确的错误信息返回给 Agent
|
package/index.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-tool-vision — external vision model for DeepSeek Harness.
|
|
3
|
+
*
|
|
4
|
+
* DeepSeek's own models are text-only, and dsh-llm has no multimodal content
|
|
5
|
+
* block type yet. This plugin bridges the gap with a model-facing tool that
|
|
6
|
+
* sends an image (local file, or http(s) URL) to any OpenAI-compatible
|
|
7
|
+
* chat/completions endpoint that supports `image_url` content parts, and
|
|
8
|
+
* returns the vision model's textual answer into the agent loop.
|
|
9
|
+
*
|
|
10
|
+
* Registered on the GLOBAL tools layer, so every agent in the process can
|
|
11
|
+
* call `inspect_image`.
|
|
12
|
+
*/
|
|
13
|
+
import { readFile, stat } from "node:fs/promises";
|
|
14
|
+
import { extname, isAbsolute, resolve as resolvePath } from "node:path";
|
|
15
|
+
import z from "@deepseek-ai/schemastery";
|
|
16
|
+
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
17
|
+
|
|
18
|
+
/** Cordis plugin name. */
|
|
19
|
+
const name = "tool-vision";
|
|
20
|
+
/** The tool registry this row contributes to. */
|
|
21
|
+
const inject = ["tools"];
|
|
22
|
+
|
|
23
|
+
const DEFAULT_DESCRIPTION =
|
|
24
|
+
"Analyze an image using an external vision-capable model through an OpenAI-compatible API. " +
|
|
25
|
+
"Provide the path to a local image file (absolute, or relative to the current workspace) or an http(s) URL, " +
|
|
26
|
+
"optionally with a specific question. Returns the vision model's textual description or answer. " +
|
|
27
|
+
"Use this whenever you need to read, describe, or extract information from image content, " +
|
|
28
|
+
"since the main model is text-only.";
|
|
29
|
+
|
|
30
|
+
/** Runtime schema for the tool-vision row. */
|
|
31
|
+
const Config = z.object({
|
|
32
|
+
/** Base URL of an OpenAI-compatible API, e.g. https://api.openai.com/v1 or https://dashscope.aliyuncs.com/compatible-mode/v1 */
|
|
33
|
+
baseURL: z.string().default("https://api.openai.com/v1"),
|
|
34
|
+
/** API key; takes precedence over apiKeyEnv. */
|
|
35
|
+
apiKey: z.string().default(""),
|
|
36
|
+
/** Environment variable holding the API key. */
|
|
37
|
+
apiKeyEnv: z.string().default("VISION_API_KEY"),
|
|
38
|
+
/** Vision model id served by the endpoint. */
|
|
39
|
+
model: z.string().default("gpt-4o-mini"),
|
|
40
|
+
/** Max output tokens for the vision call. */
|
|
41
|
+
maxTokens: z.number().default(1024),
|
|
42
|
+
/** Per-request timeout in milliseconds. */
|
|
43
|
+
timeoutMs: z.number().default(60000),
|
|
44
|
+
/** Largest local image accepted, in bytes. */
|
|
45
|
+
maxImageBytes: z.number().default(10 * 1024 * 1024),
|
|
46
|
+
/** Tool description shown to the model; overrides the default. */
|
|
47
|
+
description: z.string().default(DEFAULT_DESCRIPTION),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const MIME_BY_EXT = {
|
|
51
|
+
".png": "image/png",
|
|
52
|
+
".jpg": "image/jpeg",
|
|
53
|
+
".jpeg": "image/jpeg",
|
|
54
|
+
".gif": "image/gif",
|
|
55
|
+
".webp": "image/webp",
|
|
56
|
+
".bmp": "image/bmp",
|
|
57
|
+
".avif": "image/avif",
|
|
58
|
+
".svg": "image/svg+xml",
|
|
59
|
+
".ico": "image/x-icon",
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
function resolveApiKey(config) {
|
|
63
|
+
if (config.apiKey) return config.apiKey;
|
|
64
|
+
if (config.apiKeyEnv) {
|
|
65
|
+
const fromEnv = process.env[config.apiKeyEnv];
|
|
66
|
+
if (fromEnv) return fromEnv;
|
|
67
|
+
}
|
|
68
|
+
return process.env.OPENAI_API_KEY ?? "";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Turn a tool argument into an image_url payload: local file -> data URL, http(s) -> as-is. */
|
|
72
|
+
async function toImageUrl(target, cwd, config) {
|
|
73
|
+
if (/^https?:\/\//i.test(target)) return { url: target, note: target };
|
|
74
|
+
const abs = isAbsolute(target) ? target : resolvePath(cwd, target);
|
|
75
|
+
const info = await stat(abs).catch(() => null);
|
|
76
|
+
if (!info) throw new Error(`image not found: ${abs}`);
|
|
77
|
+
if (info.size > config.maxImageBytes) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`image too large: ${abs} (${info.size} bytes, limit ${config.maxImageBytes})`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
const mime = MIME_BY_EXT[extname(abs).toLowerCase()];
|
|
83
|
+
if (!mime) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`unsupported image extension: ${abs} (supported: ${Object.keys(MIME_BY_EXT).join(", ")})`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
const data = await readFile(abs);
|
|
89
|
+
return { url: `data:${mime};base64,${data.toString("base64")}`, note: abs };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** One OpenAI-compatible chat/completions call with an image_url content part. */
|
|
93
|
+
async function callVision(config, imageUrl, question, detail, signal) {
|
|
94
|
+
const key = resolveApiKey(config);
|
|
95
|
+
if (!key) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`vision API key missing: set the plugin config (apiKey / apiKeyEnv) or the OPENAI_API_KEY environment variable`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
const base = config.baseURL.endsWith("/") ? config.baseURL : `${config.baseURL}/`;
|
|
101
|
+
const endpoint = new URL("chat/completions", base);
|
|
102
|
+
const controller = new AbortController();
|
|
103
|
+
const timer = setTimeout(
|
|
104
|
+
() => controller.abort(new Error(`vision request timed out after ${config.timeoutMs}ms`)),
|
|
105
|
+
config.timeoutMs,
|
|
106
|
+
);
|
|
107
|
+
const onSignalAbort = () => controller.abort(signal?.reason);
|
|
108
|
+
if (signal) {
|
|
109
|
+
if (signal.aborted) controller.abort();
|
|
110
|
+
else signal.addEventListener("abort", onSignalAbort, { once: true });
|
|
111
|
+
}
|
|
112
|
+
const content = [
|
|
113
|
+
{ type: "text", text: question || "Describe this image in detail, including all key visual elements, text, and context you can see." },
|
|
114
|
+
{ type: "image_url", image_url: detail ? { url: imageUrl, detail } : { url: imageUrl } },
|
|
115
|
+
];
|
|
116
|
+
try {
|
|
117
|
+
const response = await fetch(endpoint, {
|
|
118
|
+
method: "POST",
|
|
119
|
+
headers: {
|
|
120
|
+
"Content-Type": "application/json",
|
|
121
|
+
Authorization: `Bearer ${key}`,
|
|
122
|
+
},
|
|
123
|
+
body: JSON.stringify({
|
|
124
|
+
model: config.model,
|
|
125
|
+
messages: [{ role: "user", content }],
|
|
126
|
+
max_tokens: config.maxTokens,
|
|
127
|
+
}),
|
|
128
|
+
signal: controller.signal,
|
|
129
|
+
});
|
|
130
|
+
const body = await response.json().catch(() => null);
|
|
131
|
+
if (!response.ok) {
|
|
132
|
+
const detailText = body?.error?.message ?? response.statusText;
|
|
133
|
+
throw new Error(
|
|
134
|
+
`vision endpoint returned ${response.status}: ${detailText} (endpoint ${endpoint})`,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
const answer = body?.choices?.[0]?.message?.content;
|
|
138
|
+
if (typeof answer !== "string" || !answer.trim()) {
|
|
139
|
+
throw new Error("vision endpoint returned an empty response");
|
|
140
|
+
}
|
|
141
|
+
return answer.trim();
|
|
142
|
+
} finally {
|
|
143
|
+
clearTimeout(timer);
|
|
144
|
+
signal?.removeEventListener("abort", onSignalAbort);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function apply(ctx, config) {
|
|
149
|
+
ctx.tools.register(defineTool({
|
|
150
|
+
name: "inspect_image",
|
|
151
|
+
description: config.description,
|
|
152
|
+
parameters: {
|
|
153
|
+
path: {
|
|
154
|
+
type: "string",
|
|
155
|
+
required: true,
|
|
156
|
+
description: "Path to the image file (absolute, or relative to the current workspace) or an http(s) URL.",
|
|
157
|
+
},
|
|
158
|
+
question: {
|
|
159
|
+
type: "string",
|
|
160
|
+
description: "Optional specific question about the image. Omit for a general detailed description.",
|
|
161
|
+
},
|
|
162
|
+
detail: {
|
|
163
|
+
type: "string",
|
|
164
|
+
enum: ["auto", "low", "high"],
|
|
165
|
+
description: "Optional image resolution hint for the vision API (auto by default).",
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
output: {
|
|
169
|
+
schema: { type: "string" },
|
|
170
|
+
render: (_args, value) => [{ type: "text", text: value }],
|
|
171
|
+
},
|
|
172
|
+
async execute(args, exec) {
|
|
173
|
+
const cwd = exec.agent?.session?.header?.cwd ?? process.cwd();
|
|
174
|
+
const { url, note } = await toImageUrl(args.path, cwd, config);
|
|
175
|
+
const answer = await callVision(config, url, args.question, args.detail, exec.signal);
|
|
176
|
+
return note === url ? answer : `${answer}\n\n(image: ${note})`;
|
|
177
|
+
},
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export { Config, DEFAULT_DESCRIPTION, apply, inject, name };
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-tool-vision",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "External vision model for DeepSeek Harness: inspect_image sends local images or http(s) image URLs to any OpenAI-compatible chat/completions endpoint and returns the vision model's text answer.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"index.js",
|
|
12
|
+
"README.md",
|
|
13
|
+
"README.zh.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"keywords": [
|
|
17
|
+
"dsh",
|
|
18
|
+
"dsh-plugin",
|
|
19
|
+
"deepseek-harness",
|
|
20
|
+
"vision",
|
|
21
|
+
"multimodal",
|
|
22
|
+
"image",
|
|
23
|
+
"openai-compatible",
|
|
24
|
+
"qwen-vl",
|
|
25
|
+
"glm-4v",
|
|
26
|
+
"ollama"
|
|
27
|
+
],
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=20.18"
|
|
31
|
+
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/Scorp1o117/dsh-tool-vision.git"
|
|
35
|
+
},
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/Scorp1o117/dsh-tool-vision/issues"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
|
|
41
|
+
"@deepseek-ai/schemastery": "^3.18.1"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|