@wszhoho/dsh-file-attachment 0.5.3 → 0.5.4
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 +4 -4
- package/lib/client.js +17 -6
- package/lib/index.js +78 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# dsh-file-attachment
|
|
2
2
|
|
|
3
|
-
DeepSeek Harness (dsh) web GUI
|
|
3
|
+
DeepSeek Harness (dsh) web GUI 插件:在会话输入框中拖入或 Ctrl+V 粘贴文档/图片,或通过DSH本体 📎 附件按钮上传(插件已接管本体上传按钮),图片调用配置好的vlm模型自动进行图片识别。
|
|
4
4
|
|
|
5
5
|
图片与文档走**同一条落盘管线**:图片在输入框**内联附件条**中以缩略图预览(点击放大),在聊天区渲染为**可点击放大的缩略图**;文档在附件条中以类型图标 + 文件名条目显示,在聊天区保持芯片样式。当前会话模型不支持多模态时,图片自动调用**可配置 VLM** 识别生成中文描述回填草稿,文本模型也能「看懂」图片。
|
|
6
6
|
|
|
@@ -21,7 +21,7 @@ DeepSeek Harness (dsh) web GUI 插件:在会话输入框中**拖入或 Ctrl+V
|
|
|
21
21
|
**设置页**:「设置 → 文件附件」页两部分:
|
|
22
22
|
|
|
23
23
|
- **可上传类型**:按 文档 / 代码 / 配置文件 三类增删扩展名(小写、不带点),图片恒可发送;
|
|
24
|
-
- **多模态识别参数(VLM)**:Base URL / API Key / 模型 /
|
|
24
|
+
- **多模态识别参数(VLM)**:Base URL / API Key / 模型 / 思考模式开关(默认禁用)/ 超时时间(默认 60 秒),仅当前会话模型不支持多模态时调用,未填 API Key 时静默跳过。
|
|
25
25
|
|
|
26
26
|

|
|
27
27
|
|
|
@@ -97,7 +97,7 @@ packages/dsh-file-attachment/
|
|
|
97
97
|
- **Host 半**:`webServer.register` 前缀路由 `/dsh-file-attachment`:
|
|
98
98
|
- POST `/save`:接收 `{name, data(base64), sessionId}`,base64 解码后用 `node:fs/promises` 写盘到会话工作区 `.dsh-file-attachment/`,返回 `{ok, value:{path,dir,name,size}}`;
|
|
99
99
|
- GET/POST `/config`:读写插件配置(可上传类型 + VLM 参数);
|
|
100
|
-
- POST `/describe`:接收 `{dataUrl, prompt}`,调 VLM(OpenAI 兼容 `chat/completions
|
|
100
|
+
- POST `/describe`:接收 `{dataUrl, prompt}`,调 VLM(OpenAI 兼容 `chat/completions`)识别图片,返回中文描述与当前思考模式配置(`thinkingType`);
|
|
101
101
|
- `describe_image` 工具(Host 注册):agent 需要理解 `@图片` 引用时调用——读已落盘图片 → VLM 识别 → 描述直接作为工具输出返回(UI 展示 + 模型可见)。
|
|
102
102
|
- **Client 半**槽位:
|
|
103
103
|
- `conversation.input.attachments`(shadow 原生附件条,priority -100):**内联附件条**,渲染自维护的附件登记表——图片为 data URL 缩略图(点击放大)、文档为类型图标 + 文件名;条目可移除(同步清除草稿 `@引用`),发送后自动清空,不依赖 dsh 附件草稿链路(文本模型可正常发送);
|
|
@@ -113,7 +113,7 @@ packages/dsh-file-attachment/
|
|
|
113
113
|
- `lib/*.js`、`package.json`、`cordis.patch.yml`、`README.md` 当前均为 **UTF-8 无 BOM**(以仓库实测为准);编辑时保持原编码,不引入 BOM。
|
|
114
114
|
- Host 半用 `webServer.register({ kind: 'prefix', path: '/dsh-file-attachment', handler })` 提供 `/save`、`/config`、`/describe` 三个路由,并经 `tools.register` 注册 `describe_image` 工具;纯 ESM 无构建,不依赖装饰器/远程反射。
|
|
115
115
|
- Client 半保存用 `fetch('/dsh-file-attachment/save', { method: 'POST', body: JSON.stringify({ name, data, sessionId }) })`,信封为 `{ ok, value | error }`。
|
|
116
|
-
- VLM 走 OpenAI 兼容 `chat/completions`(Base URL / API Key / 模型 /
|
|
116
|
+
- VLM 走 OpenAI 兼容 `chat/completions`(Base URL / API Key / 模型 / 思考模式 / 超时时间可在设置页配置,思考模式默认关闭,超时默认 60 秒);请求按 MiMo 官方格式发送 `thinking: { type: "enabled"|"disabled" }`(扁平 `thinkingType` 会被 API 忽略,而 mimo-v2.5 默认开启深度思考,导致识别极慢——实测 206 秒 vs 关闭后 22~73 秒,服务延迟波动较大,若偶发超时可调大);超时后工具调用中止并明确报错「VLM 请求超时(N 秒)」,不再挂死会话;`describe_image` 工具输出 JSON 附带 `thinkingType`(enabled/disabled),直观展示当前思考模式配置。未填 API Key 时识别静默跳过,不影响落盘。
|
|
117
117
|
- 项目根 = 会话工作区(`session.header.cwd` → `sandboxPolicy.workspaceRoot` → `process.cwd()` 兜底)。
|
|
118
118
|
- 50MB 上限两侧一致(client 跳过 + host 校验)。
|
|
119
119
|
|
package/lib/client.js
CHANGED
|
@@ -49,8 +49,8 @@ window.__ModuleLoader__.load({
|
|
|
49
49
|
config: ['json', 'yml', 'yaml', 'toml', 'ini', 'conf', 'cfg', 'env', 'properties', 'xml', 'html', 'css'],
|
|
50
50
|
}
|
|
51
51
|
const allowedTypes = { doc: DEFAULT_TYPES.doc.slice(), code: DEFAULT_TYPES.code.slice(), config: DEFAULT_TYPES.config.slice() }
|
|
52
|
-
// 多模态 VLM 识别参数(仅当前模型非多模态时调用):baseURL/apiKey/model/thinkingType
|
|
53
|
-
let vlmCfg = { baseURL: 'https://api.xiaomimimo.com/v1', apiKey: '', model: 'mimo-v2.5', thinkingType: 'disabled' }
|
|
52
|
+
// 多模态 VLM 识别参数(仅当前模型非多模态时调用):baseURL/apiKey/model/thinkingType/timeout
|
|
53
|
+
let vlmCfg = { baseURL: 'https://api.xiaomimimo.com/v1', apiKey: '', model: 'mimo-v2.5', thinkingType: 'disabled', timeout: 60 }
|
|
54
54
|
let allowedExts = null // Set<ext>,rebuildAllowed 填充;上传校验用
|
|
55
55
|
let acceptString = 'image/*' // 文件选择器 <input accept>,buildAccept 生成;劫持📎打开前赋值
|
|
56
56
|
// 规范化扩展名列表(小写/去点/去重/仅 [a-z0-9]),与 host 侧同逻辑
|
|
@@ -135,12 +135,15 @@ window.__ModuleLoader__.load({
|
|
|
135
135
|
const timer = setInterval(scan, 1500)
|
|
136
136
|
return { mo, timer }
|
|
137
137
|
}
|
|
138
|
-
// 客户端侧多模态参数规范化(空值回退默认,与 host
|
|
138
|
+
// 客户端侧多模态参数规范化(空值回退默认,与 host normalizeVlm 一致)
|
|
139
139
|
function normalizeVlmClient(raw) {
|
|
140
|
-
const d = { baseURL: 'https://api.xiaomimimo.com/v1', apiKey: '', model: 'mimo-v2.5', thinkingType: 'disabled' }
|
|
140
|
+
const d = { baseURL: 'https://api.xiaomimimo.com/v1', apiKey: '', model: 'mimo-v2.5', thinkingType: 'disabled', timeout: 60 }
|
|
141
141
|
if (raw === null || typeof raw !== 'object') return d
|
|
142
142
|
const pick = (k) => (typeof raw[k] === 'string' && raw[k] !== '') ? raw[k] : d[k]
|
|
143
|
-
|
|
143
|
+
// 超时(秒):正整数 1~600,非法回退默认
|
|
144
|
+
const t = Number(raw && raw.timeout)
|
|
145
|
+
const timeout = Number.isFinite(t) && t >= 1 && t <= 600 ? Math.floor(t) : d.timeout
|
|
146
|
+
return { baseURL: pick('baseURL'), apiKey: pick('apiKey'), model: pick('model'), thinkingType: raw.thinkingType === 'enabled' ? 'enabled' : 'disabled', timeout }
|
|
144
147
|
}
|
|
145
148
|
// 应用配置:更新 allowedTypes → 重建校验 Set + accept(劫持📎在打开选择器时读取 acceptString)
|
|
146
149
|
function applyConfig(cfg) {
|
|
@@ -1140,6 +1143,7 @@ window.__ModuleLoader__.load({
|
|
|
1140
1143
|
const [vlmApiKey, setVlmApiKey] = react.useState('')
|
|
1141
1144
|
const [vlmModel, setVlmModel] = react.useState('')
|
|
1142
1145
|
const [vlmThinking, setVlmThinking] = react.useState(false)
|
|
1146
|
+
const [vlmTimeout, setVlmTimeout] = react.useState('30')
|
|
1143
1147
|
const [docIn, setDocIn] = react.useState('')
|
|
1144
1148
|
const [codeIn, setCodeIn] = react.useState('')
|
|
1145
1149
|
const [cfgIn, setCfgIn] = react.useState('')
|
|
@@ -1157,6 +1161,7 @@ window.__ModuleLoader__.load({
|
|
|
1157
1161
|
setVlmApiKey(vlmCfg.apiKey)
|
|
1158
1162
|
setVlmModel(vlmCfg.model)
|
|
1159
1163
|
setVlmThinking(vlmCfg.thinkingType === 'enabled')
|
|
1164
|
+
setVlmTimeout(String(vlmCfg.timeout || 60))
|
|
1160
1165
|
})
|
|
1161
1166
|
return () => { live = false }
|
|
1162
1167
|
}, [])
|
|
@@ -1169,7 +1174,7 @@ window.__ModuleLoader__.load({
|
|
|
1169
1174
|
async function save() {
|
|
1170
1175
|
setSaving(true); setSaved(false)
|
|
1171
1176
|
try {
|
|
1172
|
-
const body = { doc: doc, code: code, config: config, vlm: { baseURL: vlmBaseURL, apiKey: vlmApiKey, model: vlmModel, thinkingType: vlmThinking ? 'enabled' : 'disabled' } }
|
|
1177
|
+
const body = { doc: doc, code: code, config: config, vlm: { baseURL: vlmBaseURL, apiKey: vlmApiKey, model: vlmModel, thinkingType: vlmThinking ? 'enabled' : 'disabled', timeout: Math.min(600, Math.max(1, Math.floor(Number(vlmTimeout) || 60))) } }
|
|
1173
1178
|
const r = await fetch('/dsh-file-attachment/config', {
|
|
1174
1179
|
method: 'POST',
|
|
1175
1180
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -1202,6 +1207,8 @@ window.__ModuleLoader__.load({
|
|
|
1202
1207
|
react.createElement('input', { value: vlmApiKey, onChange: (e) => setVlmApiKey(e.target.value), type: 'password', placeholder: 'sk-...', style: { padding: '6px 8px', borderRadius: '6px', border: '1px solid rgba(128,128,128,0.3)', background: 'transparent', color: 'inherit', fontSize: '12px' } }),
|
|
1203
1208
|
react.createElement('span', { style: { fontSize: '12px' } }, t('vlm.model')),
|
|
1204
1209
|
react.createElement('input', { value: vlmModel, onChange: (e) => setVlmModel(e.target.value), placeholder: 'mimo-v2.5', style: { padding: '6px 8px', borderRadius: '6px', border: '1px solid rgba(128,128,128,0.3)', background: 'transparent', color: 'inherit', fontSize: '12px' } }),
|
|
1210
|
+
react.createElement('span', { style: { fontSize: '12px' } }, t('vlm.timeout')),
|
|
1211
|
+
react.createElement('input', { value: vlmTimeout, onChange: (e) => setVlmTimeout(e.target.value), placeholder: '60', title: t('vlm.timeoutHint'), style: { padding: '6px 8px', borderRadius: '6px', border: '1px solid rgba(128,128,128,0.3)', background: 'transparent', color: 'inherit', fontSize: '12px' } }),
|
|
1205
1212
|
react.createElement('span', { style: { fontSize: '12px' } }),
|
|
1206
1213
|
react.createElement('label', { style: { display: 'inline-flex', alignItems: 'center', gap: '6px', fontSize: '12px' } },
|
|
1207
1214
|
react.createElement('input', { type: 'checkbox', checked: vlmThinking, onChange: (e) => setVlmThinking(e.target.checked) }),
|
|
@@ -1242,6 +1249,8 @@ window.__ModuleLoader__.load({
|
|
|
1242
1249
|
'vlm.apiKey': 'API Key',
|
|
1243
1250
|
'vlm.model': '模型',
|
|
1244
1251
|
'vlm.thinking': '启用思考模式(默认禁用)',
|
|
1252
|
+
'vlm.timeout': '超时(秒)',
|
|
1253
|
+
'vlm.timeoutHint': '图片识别超时时间,默认 60 秒;识别慢或卡顿时可调大',
|
|
1245
1254
|
'save': '保存',
|
|
1246
1255
|
'saving': '保存中…',
|
|
1247
1256
|
'saved': '已保存',
|
|
@@ -1258,6 +1267,8 @@ window.__ModuleLoader__.load({
|
|
|
1258
1267
|
'vlm.apiKey': 'API Key',
|
|
1259
1268
|
'vlm.model': 'Model',
|
|
1260
1269
|
'vlm.thinking': 'Enable thinking mode (disabled by default)',
|
|
1270
|
+
'vlm.timeout': 'Timeout (s)',
|
|
1271
|
+
'vlm.timeoutHint': 'Image recognition timeout, default 60s; increase if recognition is slow or hangs',
|
|
1261
1272
|
'save': 'Save',
|
|
1262
1273
|
'saving': 'Saving…',
|
|
1263
1274
|
'saved': 'Saved',
|
package/lib/index.js
CHANGED
|
@@ -143,17 +143,22 @@ const VLM_DEFAULTS = {
|
|
|
143
143
|
apiKey: '',
|
|
144
144
|
model: 'mimo-v2.5',
|
|
145
145
|
thinkingType: 'disabled',
|
|
146
|
+
timeout: 60, // 识别超时(秒)。实测 VLM 延迟波动大(12~73s),30s 会误杀多数请求
|
|
146
147
|
}
|
|
147
148
|
|
|
148
149
|
/** 规范化多模态参数(缺省/空值回退默认,trim 去空白)。 */
|
|
149
150
|
function normalizeVlm(raw) {
|
|
150
151
|
const r = (raw && typeof raw === 'object') ? raw : {}
|
|
151
152
|
const pick = (key) => (typeof r[key] === 'string' && r[key].trim() !== '' ? r[key].trim() : VLM_DEFAULTS[key])
|
|
153
|
+
// 超时(秒):正整数 1~600,非法回退默认
|
|
154
|
+
const t = Number(r && r.timeout)
|
|
155
|
+
const timeout = Number.isFinite(t) && t >= 1 && t <= 600 ? Math.floor(t) : VLM_DEFAULTS.timeout
|
|
152
156
|
return {
|
|
153
157
|
baseURL: pick('baseURL'),
|
|
154
158
|
apiKey: pick('apiKey'),
|
|
155
159
|
model: pick('model'),
|
|
156
160
|
thinkingType: pick('thinkingType'),
|
|
161
|
+
timeout,
|
|
157
162
|
}
|
|
158
163
|
}
|
|
159
164
|
|
|
@@ -219,19 +224,47 @@ async function handleSave(root, body) {
|
|
|
219
224
|
return { path: target, dir, name: safe, size: bytes.length }
|
|
220
225
|
}
|
|
221
226
|
|
|
227
|
+
// 仅思考模型识别:这类模型(旗舰推理版 / 带 thinking 后缀)始终思考、不可关闭,
|
|
228
|
+
// 传 thinking.type='disabled' 会报错(Kimi k2.7-code、GLM-5.3、Qwen3-VL-*-Thinking 等)。
|
|
229
|
+
function isThinkingOnlyModel(model) {
|
|
230
|
+
const m = String(model || '').toLowerCase()
|
|
231
|
+
if (/-thinking\b/.test(m)) return true
|
|
232
|
+
if (/kimi-k3\b/.test(m)) return true
|
|
233
|
+
if (/kimi-k2\.7-code/.test(m)) return true
|
|
234
|
+
if (/^glm-5\.3/.test(m)) return true
|
|
235
|
+
return false
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// 思考参数适配器:各家 OpenAI 兼容 API 的「思考开关」字段形式不统一。
|
|
239
|
+
// 按 model 名称判断(不依赖 baseURL host——中转站/代理转发时 host 不可靠,而 model 字段稳定跟随请求)。
|
|
240
|
+
// - 仅思考模型 → 不传任何参数(服务端默认开启思考;传 disabled 会报错)
|
|
241
|
+
// - 通义千问 Qwen / QVQ(含 VL 视觉)→ 扁平布尔 enable_thinking
|
|
242
|
+
// - 其余主流(小米 MiMo / DeepSeek / 智谱 GLM / Kimi 官方)→ 嵌套对象 thinking:{type}
|
|
243
|
+
function buildThinkingParams(vlm) {
|
|
244
|
+
if (isThinkingOnlyModel(vlm.model)) return {}
|
|
245
|
+
const wantEnabled = vlm.thinkingType === 'enabled'
|
|
246
|
+
const model = String(vlm.model || '').toLowerCase()
|
|
247
|
+
if (model.includes('qwen') || model.includes('qvq')) {
|
|
248
|
+
return { enable_thinking: wantEnabled }
|
|
249
|
+
}
|
|
250
|
+
return { thinking: { type: wantEnabled ? 'enabled' : 'disabled' } }
|
|
251
|
+
}
|
|
252
|
+
|
|
222
253
|
/**
|
|
223
254
|
* 调 VLM(OpenAI 兼容 chat/completions)识别图片,返回中文描述。
|
|
224
|
-
* @param vlm - 多模态参数 { baseURL, apiKey, model, thinkingType }
|
|
255
|
+
* @param vlm - 多模态参数 { baseURL, apiKey, model, thinkingType, timeout }(来自配置,timeout 单位秒)。
|
|
225
256
|
* @param dataUrl - base64 data URL(形如 data:image/png;base64,xxx)。
|
|
226
257
|
* @returns 图片的简洁中文描述。
|
|
227
|
-
* @throws VLM
|
|
258
|
+
* @throws VLM 请求失败或未返回有效内容时抛错;超时抛「VLM 请求超时」。
|
|
228
259
|
*/
|
|
229
260
|
async function describeImage(vlm, dataUrl, prompt) {
|
|
230
261
|
const url = vlm.baseURL.replace(/\/+$/u, '') + '/chat/completions'
|
|
231
262
|
const userPrompt = (typeof prompt === 'string' && prompt.trim() !== '') ? prompt.trim() : '请描述这张图片。'
|
|
263
|
+
// 思考开关:各家 OpenAI 兼容 API 字段形式不统一,由 buildThinkingParams 按 model 名称适配
|
|
264
|
+
// (Qwen/QVQ → 扁平 enable_thinking;MiMo/DeepSeek/GLM/Kimi → 嵌套 thinking.type;仅思考模型 → 不传)。
|
|
232
265
|
const payload = {
|
|
233
266
|
model: vlm.model,
|
|
234
|
-
|
|
267
|
+
...buildThinkingParams(vlm),
|
|
235
268
|
messages: [
|
|
236
269
|
{ role: 'system', content: '你是图片描述助手,用简洁中文回答。' },
|
|
237
270
|
{
|
|
@@ -243,22 +276,35 @@ async function describeImage(vlm, dataUrl, prompt) {
|
|
|
243
276
|
},
|
|
244
277
|
],
|
|
245
278
|
}
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
279
|
+
const timeoutSec = (Number.isFinite(vlm.timeout) && vlm.timeout >= 1 && vlm.timeout <= 600) ? vlm.timeout : VLM_DEFAULTS.timeout
|
|
280
|
+
const controller = new AbortController()
|
|
281
|
+
const timer = setTimeout(() => controller.abort(), timeoutSec * 1000)
|
|
282
|
+
try {
|
|
283
|
+
const resp = await fetch(url, {
|
|
284
|
+
method: 'POST',
|
|
285
|
+
headers: {
|
|
286
|
+
'content-type': 'application/json',
|
|
287
|
+
'authorization': 'Bearer ' + vlm.apiKey,
|
|
288
|
+
},
|
|
289
|
+
body: JSON.stringify(payload),
|
|
290
|
+
signal: controller.signal,
|
|
291
|
+
})
|
|
292
|
+
if (!resp.ok) {
|
|
293
|
+
const text = await resp.text().catch(() => '')
|
|
294
|
+
throw new Error('VLM 请求失败 ' + resp.status + (text !== '' ? ' ' + text.slice(0, 200) : ''))
|
|
295
|
+
}
|
|
296
|
+
const data = await resp.json()
|
|
297
|
+
const content = data && data.choices && data.choices[0] && data.choices[0].message && data.choices[0].message.content
|
|
298
|
+
if (typeof content !== 'string' || content === '') throw new Error('VLM 未返回有效内容')
|
|
299
|
+
return content
|
|
300
|
+
} catch (err) {
|
|
301
|
+
if (err && err.name === 'AbortError') {
|
|
302
|
+
throw new Error('VLM 请求超时(' + timeoutSec + ' 秒),可在「设置→文件附件」调大超时时间')
|
|
303
|
+
}
|
|
304
|
+
throw err
|
|
305
|
+
} finally {
|
|
306
|
+
clearTimeout(timer)
|
|
257
307
|
}
|
|
258
|
-
const data = await resp.json()
|
|
259
|
-
const content = data && data.choices && data.choices[0] && data.choices[0].message && data.choices[0].message.content
|
|
260
|
-
if (typeof content !== 'string' || content === '') throw new Error('VLM 未返回有效内容')
|
|
261
|
-
return content
|
|
262
308
|
}
|
|
263
309
|
|
|
264
310
|
/** 注册 /dsh-file-attachment 前缀路由(save POST)。 */
|
|
@@ -327,7 +373,7 @@ function registerRoutes(ctx) {
|
|
|
327
373
|
}
|
|
328
374
|
try {
|
|
329
375
|
const description = await describeImage(vlm, dataUrl, body.prompt)
|
|
330
|
-
json(res, { ok: true, value: { description } })
|
|
376
|
+
json(res, { ok: true, value: { description, thinkingType: vlm.thinkingType } })
|
|
331
377
|
return
|
|
332
378
|
} catch (err) {
|
|
333
379
|
const message = err && err.message ? err.message : '识别失败'
|
|
@@ -383,6 +429,11 @@ function registerTools(ctx) {
|
|
|
383
429
|
type: 'string',
|
|
384
430
|
description: '可选。告诉多模态模型要关注什么(如"识别图中的文字"、"描述 UI 布局")。不填则默认"请描述这张图片"。',
|
|
385
431
|
},
|
|
432
|
+
thinkingType: {
|
|
433
|
+
type: 'string',
|
|
434
|
+
enum: ['disabled', 'enabled'],
|
|
435
|
+
description: 'VLM 思考模式开关:disabled=关闭(默认)/ enabled=开启。本工具默认关闭思考以加速识别,请填 disabled。',
|
|
436
|
+
},
|
|
386
437
|
},
|
|
387
438
|
output: {
|
|
388
439
|
schema: {
|
|
@@ -391,11 +442,13 @@ function registerTools(ctx) {
|
|
|
391
442
|
properties: {
|
|
392
443
|
ok: { type: 'boolean', required: true },
|
|
393
444
|
description: { type: 'string' },
|
|
445
|
+
thinkingType: { type: 'string', description: '当前 VLM 思考模式配置:enabled=开启 / disabled=关闭' },
|
|
394
446
|
},
|
|
395
447
|
},
|
|
396
448
|
render: (_args, value) => {
|
|
449
|
+
const thinkingText = (value && value.thinkingType === 'enabled') ? '开启' : '关闭'
|
|
397
450
|
if (value && value.ok === true && typeof value.description === 'string' && value.description !== '') {
|
|
398
|
-
return [{ type: 'text', text: '图片识别结果:' + value.description }]
|
|
451
|
+
return [{ type: 'text', text: '图片识别结果:' + value.description + '(思考模式:' + thinkingText + ')' }]
|
|
399
452
|
}
|
|
400
453
|
return [{ type: 'text', text: (value && value.ok === true) ? '(图片已识别)' : '(图片识别失败)' }]
|
|
401
454
|
},
|
|
@@ -413,7 +466,11 @@ function registerTools(ctx) {
|
|
|
413
466
|
const description = await describeImage(cfg.vlm, dataUrl, args.prompt)
|
|
414
467
|
// 识别结果直接作为工具输出返回:UI 直接展示描述文本,
|
|
415
468
|
// 同时该输出就是模型可见内容(tool/result 的 content),无需额外 deferContext 注入。
|
|
416
|
-
|
|
469
|
+
// 注意 thinkingType 的双重角色:
|
|
470
|
+
// - 输入参数(args.thinkingType):模型按 description 填充,仅用于让「工具调用 JSON」直观可见思考开关,
|
|
471
|
+
// 实际调用 VLM 时不读它(真实开关以 cfg.vlm.thinkingType 为准,见 describeImage 内的 thinking.type)。
|
|
472
|
+
// - 输出(此处返回值):回填配置真实值,使结果与输入展示一致、可核对。
|
|
473
|
+
return { ok: true, description, thinkingType: cfg.vlm.thinkingType }
|
|
417
474
|
},
|
|
418
475
|
}))
|
|
419
476
|
console.log('[dsh-file-attachment] describe_image tool registered')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wszhoho/dsh-file-attachment",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.4",
|
|
4
4
|
"description": "文件附件:拖拽/粘贴/上传文件(支持多文件);图片与文档统一落盘到 .dsh-file-attachment 并以 @绝对路径 引用发送(文本模型可正常使用);输入框内联显示图片缩略图预览与文件条目,聊天区图片同样渲染为可点击放大的缩略图、文件保持芯片样式;非多模态模型下图片自动调用可配置 VLM 识别生成中文描述回填草稿;文档/代码/配置文件可上传类型可在设置页配置;支持 PC 与移动端浏览器",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|