dsh-llm-workbuddy 0.1.6 → 0.1.9
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 +15 -4
- package/lib/index.js +141 -31
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# dsh-llm-workbuddy
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/dsh-llm-workbuddy)
|
|
4
|
+
[](https://github.com/zdk119746/dsh-llm-workbuddy/blob/main/LICENSE)
|
|
5
|
+
|
|
3
6
|
在 DeepSeek Harness 中使用你的 **WorkBuddy / CodeBuddy** 账号模型的 LLM 适配器插件。
|
|
4
7
|
|
|
5
8
|
它把 `workbuddy` 这个 provider 路由指向本地运行的
|
|
@@ -12,7 +15,8 @@
|
|
|
12
15
|
|
|
13
16
|
1. **模型能力**:Web 界面的模型选择器(composer 模型菜单或 `/model` 命令)会多出
|
|
14
17
|
一个 **WorkBuddy** 分组,模型(DeepSeek-V4、GLM-5.x、Kimi-K2.x、MiniMax-M3、
|
|
15
|
-
Hy3、Hunyuan
|
|
18
|
+
Hy3、Hunyuan…)随账号可用列表实时同步,点一下即可切换;支持推理的模型还会
|
|
19
|
+
显示**推理等级**选择器(Low / Medium / High)。
|
|
16
20
|
2. **Web 登录状态小组件**:在 Web GUI 右下角常驻一个状态胶囊,**实时显示登录/
|
|
17
21
|
代理状态**,未登录时一键在新标签页打开 WorkBuddy 登录页,登录完成后自动变绿。
|
|
18
22
|
无需再回到终端手动跑登录脚本。
|
|
@@ -269,13 +273,15 @@ DSH 的 `dsh.client` 机制只要求 `package.json` 里:
|
|
|
269
273
|
# defaultContextWindow: 200000 # 未在目录中标明容量的模型使用
|
|
270
274
|
# discovery: true # 实时拉取代理的 /v1/models(30s 缓存)
|
|
271
275
|
# models: [...] # 静态目录(代理不可达时的兜底)
|
|
276
|
+
# 每个条目可带 reasoningEffort: 'low'|'medium'|'high'(默认推理等级)
|
|
272
277
|
# loginScript: '' # 登录脚本绝对/相对路径;默认用包内 login_workbuddy.py
|
|
273
278
|
# sessionFile: ~/.codebuddy-session.json # 会话文件路径;默认同上
|
|
274
279
|
```
|
|
275
280
|
|
|
276
281
|
模型列表默认取插件内置目录;代理可达时改为实时拉取 `/v1/models`(支持
|
|
277
282
|
`{"models": [...]}` / `{"data": [...]}` 两种返回),未列出的模型 id 仍可原样
|
|
278
|
-
|
|
283
|
+
传递。每个模型的推理等级(`reasoningEffort`)默认来自内置目录,代理可达时优先
|
|
284
|
+
沿用目录值(代理 `/v1/models` 当前只上报 `High`,不区分模型)。
|
|
279
285
|
|
|
280
286
|
---
|
|
281
287
|
|
|
@@ -296,8 +302,13 @@ DSH 的 `dsh.client` 机制只要求 `package.json` 里:
|
|
|
296
302
|
|
|
297
303
|
## 限制
|
|
298
304
|
|
|
299
|
-
-
|
|
300
|
-
-
|
|
305
|
+
- 图片输入:声明支持图片的模型(`inputModalities: ["text", "image"]`,如 deepseek-v4-pro、
|
|
306
|
+
glm-5.2、kimi-k2.x、hy3 等)可附带图片,适配器会通过 DSH 的附件服务把图片编码为
|
|
307
|
+
`data:<mime>;base64,<bytes>` 以 OpenAI `image_url` 格式透传给代理。若附件服务不可用
|
|
308
|
+
(headless 等无附件场景),图片输入会以 `UNSUPPORTED_CONTENT` 稳定报错。
|
|
309
|
+
- 推理等级(reasoning effort):支持推理的模型(如 DeepSeek-V4、GLM、Kimi、MiniMax、
|
|
310
|
+
Hy3 等)会显示推理等级下拉(Low / Medium / High),默认值取平台默认强度。`reasoning_effort`
|
|
311
|
+
会透传给代理;若某模型平台侧只接受平台默认、忽略该参数,则退化为平台默认强度,不影响出字。
|
|
301
312
|
- 代理未运行时,模型请求会以 `TRANSPORT` 错误快速失败(连接被拒绝);但状态
|
|
302
313
|
小组件本身不依赖代理——代理挂了它仍能显示「代理未运行」并允许触发登录。
|
|
303
314
|
- 登录态有效期由 WorkBuddy 云端决定;过期后胶囊变红,重新点「登录」即可,
|
package/lib/index.js
CHANGED
|
@@ -73,28 +73,77 @@ const DISCOVERY_TIMEOUT_MS = 2_000;
|
|
|
73
73
|
* proxy is reachable; entries not announced by the proxy are still listed.
|
|
74
74
|
*/
|
|
75
75
|
const DEFAULT_MODELS = [
|
|
76
|
-
{ id: "deepseek-v4-pro", name: "Deepseek-V4-Pro", contextWindow: 1_000_000, maxTokens: 50_000, inputModalities: ["text", "image"] },
|
|
77
|
-
{ id: "deepseek-v4-flash", name: "Deepseek-V4-Flash", contextWindow: 1_000_000, maxTokens: 50_000, inputModalities: ["text", "image"] },
|
|
78
|
-
{ id: "glm-5.2", name: "GLM-5.2", contextWindow: 1_000_000, maxTokens: 48_000, inputModalities: ["text", "image"] },
|
|
79
|
-
{ id: "glm-5.1", name: "GLM-5.1", contextWindow: 200_000, maxTokens: 48_000 },
|
|
80
|
-
{ id: "glm-5.0", name: "GLM-5.0", contextWindow: 200_000, maxTokens: 48_000 },
|
|
81
|
-
{ id: "glm-4.7", name: "GLM-4.7", contextWindow: 200_000, maxTokens: 48_000 },
|
|
82
|
-
{ id: "glm-4.6", name: "GLM-4.6", contextWindow: 168_000, maxTokens: 32_000 },
|
|
83
|
-
{ id: "glm-5v-turbo", name: "GLM-5v-Turbo", contextWindow: 200_000, maxTokens: 64_000, inputModalities: ["text", "image"] },
|
|
84
|
-
{ id: "kimi-k3-1", name: "Kimi-K3", contextWindow: 1_000_000, maxTokens: 32_000, inputModalities: ["text", "image"] },
|
|
85
|
-
{ id: "kimi-k2.7", name: "Kimi-K2.7-Code", contextWindow: 256_000, maxTokens: 32_000, inputModalities: ["text", "image"] },
|
|
86
|
-
{ id: "kimi-k2.6", name: "Kimi-K2.6", contextWindow: 256_000, maxTokens: 32_000, inputModalities: ["text", "image"] },
|
|
87
|
-
{ id: "kimi-k2.5", name: "Kimi-K2.5", contextWindow: 164_000, maxTokens: 32_000, inputModalities: ["text", "image"] },
|
|
88
|
-
{ id: "kimi-k2-thinking", name: "Kimi-K2-Thinking", contextWindow: 164_000, maxTokens: 32_000 },
|
|
89
|
-
{ id: "minimax-m3", name: "MiniMax-M3", contextWindow: 512_000, maxTokens: 128_000, inputModalities: ["text", "image"] },
|
|
90
|
-
{ id: "minimax-m2.5", name: "MiniMax-M2.5", contextWindow: 200_000, maxTokens: 48_000 },
|
|
91
|
-
{ id: "hy3", name: "Hy3", contextWindow: 192_000, maxTokens: 64_000, inputModalities: ["text", "image"] },
|
|
92
|
-
{ id: "hunyuan-2.0-thinking", name: "Hunyuan-2.0-Thinking", contextWindow: 128_000, maxTokens: 24_000 },
|
|
76
|
+
{ id: "deepseek-v4-pro", name: "Deepseek-V4-Pro", contextWindow: 1_000_000, maxTokens: 50_000, inputModalities: ["text", "image"], reasoningEffort: "high" },
|
|
77
|
+
{ id: "deepseek-v4-flash", name: "Deepseek-V4-Flash", contextWindow: 1_000_000, maxTokens: 50_000, inputModalities: ["text", "image"], reasoningEffort: "high" },
|
|
78
|
+
{ id: "glm-5.2", name: "GLM-5.2", contextWindow: 1_000_000, maxTokens: 48_000, inputModalities: ["text", "image"], reasoningEffort: "medium" },
|
|
79
|
+
{ id: "glm-5.1", name: "GLM-5.1", contextWindow: 200_000, maxTokens: 48_000, reasoningEffort: "medium" },
|
|
80
|
+
{ id: "glm-5.0", name: "GLM-5.0", contextWindow: 200_000, maxTokens: 48_000, reasoningEffort: "high" },
|
|
81
|
+
{ id: "glm-4.7", name: "GLM-4.7", contextWindow: 200_000, maxTokens: 48_000, reasoningEffort: "high" },
|
|
82
|
+
{ id: "glm-4.6", name: "GLM-4.6", contextWindow: 168_000, maxTokens: 32_000, reasoningEffort: "high" },
|
|
83
|
+
{ id: "glm-5v-turbo", name: "GLM-5v-Turbo", contextWindow: 200_000, maxTokens: 64_000, inputModalities: ["text", "image"], reasoningEffort: "medium" },
|
|
84
|
+
{ id: "kimi-k3-1", name: "Kimi-K3", contextWindow: 1_000_000, maxTokens: 32_000, inputModalities: ["text", "image"], reasoningEffort: "medium" },
|
|
85
|
+
{ id: "kimi-k2.7", name: "Kimi-K2.7-Code", contextWindow: 256_000, maxTokens: 32_000, inputModalities: ["text", "image"], reasoningEffort: "medium" },
|
|
86
|
+
{ id: "kimi-k2.6", name: "Kimi-K2.6", contextWindow: 256_000, maxTokens: 32_000, inputModalities: ["text", "image"], reasoningEffort: "medium" },
|
|
87
|
+
{ id: "kimi-k2.5", name: "Kimi-K2.5", contextWindow: 164_000, maxTokens: 32_000, inputModalities: ["text", "image"], reasoningEffort: "medium" },
|
|
88
|
+
{ id: "kimi-k2-thinking", name: "Kimi-K2-Thinking", contextWindow: 164_000, maxTokens: 32_000, reasoningEffort: "medium" },
|
|
89
|
+
{ id: "minimax-m3", name: "MiniMax-M3", contextWindow: 512_000, maxTokens: 128_000, inputModalities: ["text", "image"], reasoningEffort: "medium" },
|
|
90
|
+
{ id: "minimax-m2.5", name: "MiniMax-M2.5", contextWindow: 200_000, maxTokens: 48_000, reasoningEffort: "medium" },
|
|
91
|
+
{ id: "hy3", name: "Hy3", contextWindow: 192_000, maxTokens: 64_000, inputModalities: ["text", "image"], reasoningEffort: "high" },
|
|
92
|
+
{ id: "hunyuan-2.0-thinking", name: "Hunyuan-2.0-Thinking", contextWindow: 128_000, maxTokens: 24_000, reasoningEffort: "medium" },
|
|
93
93
|
{ id: "hunyuan-chat", name: "Hunyuan-Turbos", contextWindow: 200_000, maxTokens: 8_192 },
|
|
94
|
-
{ id: "auto", name: "Auto", contextWindow: 168_000, maxTokens: 32_000, inputModalities: ["text", "image"] },
|
|
94
|
+
{ id: "auto", name: "Auto", contextWindow: 168_000, maxTokens: 32_000, inputModalities: ["text", "image"], reasoningEffort: "high" },
|
|
95
95
|
{ id: "default", name: "Default", contextWindow: 200_000, maxTokens: 24_000 },
|
|
96
96
|
];
|
|
97
97
|
|
|
98
|
+
/** Selectable reasoning efforts exposed to the harness UI, in display order. */
|
|
99
|
+
const REASONING_EFFORTS = [
|
|
100
|
+
{ id: "low", name: "Low" },
|
|
101
|
+
{ id: "medium", name: "Medium" },
|
|
102
|
+
{ id: "high", name: "High" },
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Adapter-owned reasoning-effort identifiers accepted by the wire. The harness
|
|
107
|
+
* `ReasoningEffortId` is opaque; we keep the catalog default and the request
|
|
108
|
+
* param in this vocabulary and map to/from the proxy as needed.
|
|
109
|
+
*/
|
|
110
|
+
const REASONING_EFFORT_IDS = new Set(["low", "medium", "high"]);
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Build the `reasoning` capability metadata for a model, per the dsh-llm
|
|
114
|
+
* `LlmModelReasoningInfo` contract. `effort` is the model's platform default
|
|
115
|
+
* (`high`/`medium`/`low`); absent means the model exposes no selectable
|
|
116
|
+
* reasoning (the harness then hides the selector). When the platform does not
|
|
117
|
+
* support per-request switching, callers may still omit an explicit effort and
|
|
118
|
+
* the proxy uses its own default — publishing the default keeps the UI honest
|
|
119
|
+
* about the level actually applied.
|
|
120
|
+
* @returns the `reasoning` block, or `undefined` when the model has none.
|
|
121
|
+
*/
|
|
122
|
+
function modelReasoningInfo(effort) {
|
|
123
|
+
if (effort === undefined || !REASONING_EFFORT_IDS.has(effort)) return undefined;
|
|
124
|
+
return {
|
|
125
|
+
efforts: REASONING_EFFORTS,
|
|
126
|
+
defaultEffort: effort,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Extract a model's default reasoning effort from a live `/v1/models` entry.
|
|
132
|
+
* The proxy reports `supported_reasoning_levels: [{ effort: 'High' }]` and
|
|
133
|
+
* `default_reasoning_level: null`, so we fall back to the catalog's
|
|
134
|
+
* `reasoningEffort` when the live answer is uninformative (as it currently is).
|
|
135
|
+
* @returns a lowercased effort id (`low`/`medium`/`high`) or `undefined`.
|
|
136
|
+
*/
|
|
137
|
+
function liveReasoningEffort(entry, catalogEffort) {
|
|
138
|
+
if (catalogEffort !== undefined) return catalogEffort;
|
|
139
|
+
const levels = Array.isArray(entry?.supported_reasoning_levels) ? entry.supported_reasoning_levels : [];
|
|
140
|
+
const wire = levels[0]?.effort;
|
|
141
|
+
if (typeof wire !== "string") return undefined;
|
|
142
|
+
const lower = wire.toLowerCase();
|
|
143
|
+
return lower === "low" || lower === "medium" || lower === "high" ? lower : undefined;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
98
147
|
// #region serialize
|
|
99
148
|
|
|
100
149
|
/** Join the text blocks of a message (used for user/tool-result content). */
|
|
@@ -120,14 +169,29 @@ function serializeAssistant(message) {
|
|
|
120
169
|
/**
|
|
121
170
|
* Serialize the harness conversation into OpenAI chat-completions wire
|
|
122
171
|
* messages. `tool-result` blocks become standalone `{role: 'tool'}` messages;
|
|
123
|
-
* image
|
|
172
|
+
* image blocks are read through the durable attachment service and emitted as
|
|
173
|
+
* OpenAI `image_url` parts (`data:<mime>;base64,<bytes>`), which the
|
|
174
|
+
* workbuddy2api proxy passes through to the upstream platform. When the
|
|
175
|
+
* attachment service is unavailable, image input degrades to the stable
|
|
176
|
+
* `UNSUPPORTED_CONTENT` error.
|
|
124
177
|
*/
|
|
125
|
-
function serializeMessages(messages) {
|
|
178
|
+
async function serializeMessages(messages, attachments, signal) {
|
|
179
|
+
const refs = new Map();
|
|
180
|
+
for (const message of messages) collectImageRefs(message.content, refs);
|
|
181
|
+
const requestImages = new Map();
|
|
182
|
+
if (refs.size > 0) {
|
|
183
|
+
if (attachments === undefined) {
|
|
184
|
+
throw new LlmError("WorkBuddy image input requires the durable attachment service", "UNSUPPORTED_CONTENT");
|
|
185
|
+
}
|
|
186
|
+
const policy = { maxPixels: 4_000_000, maxBytes: 20_000_000 };
|
|
187
|
+
const ordered = [...refs.values()];
|
|
188
|
+
const prepared = await Promise.all(ordered.map((ref) => attachments.readImageRequest(ref, policy, signal)));
|
|
189
|
+
for (let index = 0; index < ordered.length; index += 1) {
|
|
190
|
+
requestImages.set(ordered[index].attachmentId, prepared[index]);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
126
193
|
const wire = [];
|
|
127
194
|
for (const message of messages) {
|
|
128
|
-
if (message.content.some((block) => block.type === "image")) {
|
|
129
|
-
throw new LlmError("The WorkBuddy adapter does not support image content yet.", "UNSUPPORTED_CONTENT");
|
|
130
|
-
}
|
|
131
195
|
if (message.role === "system") {
|
|
132
196
|
wire.push({ role: "system", content: flattenText(message.content) });
|
|
133
197
|
continue;
|
|
@@ -138,7 +202,26 @@ function serializeMessages(messages) {
|
|
|
138
202
|
}
|
|
139
203
|
const toolResults = message.content.filter((block) => block.type === "tool-result");
|
|
140
204
|
const text = flattenText(message.content);
|
|
141
|
-
|
|
205
|
+
const images = message.content.filter((block) => block.type === "image");
|
|
206
|
+
if (text.length > 0 || toolResults.length === 0) {
|
|
207
|
+
if (images.length > 0) {
|
|
208
|
+
const parts = [];
|
|
209
|
+
if (text.length > 0) parts.push({ type: "text", text });
|
|
210
|
+
for (const block of images) {
|
|
211
|
+
const version = requestImages.get(block.attachment.attachmentId);
|
|
212
|
+
if (version === undefined) {
|
|
213
|
+
throw new LlmError("WorkBuddy image input missing attachment bytes", "UNSUPPORTED_CONTENT");
|
|
214
|
+
}
|
|
215
|
+
parts.push({
|
|
216
|
+
type: "image_url",
|
|
217
|
+
image_url: { url: `data:${version.mediaType};base64,${Buffer.from(version.data).toString("base64")}` },
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
wire.push({ role: "user", content: parts });
|
|
221
|
+
} else {
|
|
222
|
+
wire.push({ role: "user", content: text });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
142
225
|
for (const result of toolResults) {
|
|
143
226
|
wire.push({ role: "tool", tool_call_id: result.toolCallId, content: flattenText(result.content) || "(no output)" });
|
|
144
227
|
}
|
|
@@ -146,11 +229,19 @@ function serializeMessages(messages) {
|
|
|
146
229
|
return wire;
|
|
147
230
|
}
|
|
148
231
|
|
|
232
|
+
/** Collect image attachment refs from a content block list (recursing into tool results). */
|
|
233
|
+
function collectImageRefs(blocks, refs) {
|
|
234
|
+
for (const block of blocks) {
|
|
235
|
+
if (block.type === "image") refs.set(block.attachment.attachmentId, block.attachment);
|
|
236
|
+
else if (block.type === "tool-result") collectImageRefs(block.content, refs);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
149
240
|
/** Build the full wire request. Always streaming with usage reporting on. */
|
|
150
|
-
function serializeRequest(options) {
|
|
241
|
+
async function serializeRequest(options, attachments, signal) {
|
|
151
242
|
const messages = [];
|
|
152
243
|
if (options.system !== undefined) messages.push({ role: "system", content: options.system });
|
|
153
|
-
messages.push(...serializeMessages(options.messages));
|
|
244
|
+
messages.push(...await serializeMessages(options.messages, attachments, signal));
|
|
154
245
|
const tools = options.tools?.map((tool) => ({
|
|
155
246
|
type: "function",
|
|
156
247
|
function: { name: tool.name, description: tool.description, parameters: tool.parameters },
|
|
@@ -164,6 +255,9 @@ function serializeRequest(options) {
|
|
|
164
255
|
...(options.temperature !== undefined ? { temperature: options.temperature } : {}),
|
|
165
256
|
...(options.maxTokens === undefined ? {} : { max_tokens: options.maxTokens }),
|
|
166
257
|
...(options.stop !== undefined ? { stop: options.stop } : {}),
|
|
258
|
+
// reasoning_effort is opaque here (low|medium|high); the proxy passes it
|
|
259
|
+
// through verbatim to the upstream /v2/chat/completions body.
|
|
260
|
+
...(options.reasoningEffort === undefined ? {} : { reasoning_effort: options.reasoningEffort }),
|
|
167
261
|
};
|
|
168
262
|
}
|
|
169
263
|
|
|
@@ -363,12 +457,14 @@ async function* translate(payloads) {
|
|
|
363
457
|
|
|
364
458
|
/** Display metadata for one catalog entry. */
|
|
365
459
|
function modelInfo(provider, model) {
|
|
460
|
+
const reasoning = modelReasoningInfo(model.reasoningEffort);
|
|
366
461
|
return {
|
|
367
462
|
provider,
|
|
368
463
|
id: model.id,
|
|
369
464
|
name: model.name ?? model.id,
|
|
370
465
|
...(model.description === undefined ? {} : { description: model.description }),
|
|
371
466
|
inputModalities: model.inputModalities ?? ["text"],
|
|
467
|
+
...(reasoning === undefined ? {} : { reasoning }),
|
|
372
468
|
};
|
|
373
469
|
}
|
|
374
470
|
|
|
@@ -457,12 +553,15 @@ export class WorkBuddyAdapter extends LlmAdapter {
|
|
|
457
553
|
if (seen.has(entry.id)) continue;
|
|
458
554
|
seen.add(entry.id);
|
|
459
555
|
const catalog = byId.get(entry.id);
|
|
556
|
+
const effort = liveReasoningEffort(entry, catalog?.reasoningEffort);
|
|
557
|
+
const reasoning = modelReasoningInfo(effort);
|
|
460
558
|
merged.push({
|
|
461
559
|
provider,
|
|
462
560
|
id: entry.id,
|
|
463
561
|
name: entry.name ?? catalog?.name ?? entry.id,
|
|
464
562
|
...(catalog?.description !== undefined ? { description: catalog.description } : {}),
|
|
465
563
|
inputModalities: catalog?.inputModalities ?? ["text"],
|
|
564
|
+
...(reasoning === undefined ? {} : { reasoning }),
|
|
466
565
|
});
|
|
467
566
|
}
|
|
468
567
|
// Catalog entries the proxy did not announce (e.g. unauthenticated or
|
|
@@ -482,10 +581,11 @@ export class WorkBuddyAdapter extends LlmAdapter {
|
|
|
482
581
|
resolveModel(provider, model, _signal) {
|
|
483
582
|
const connection = this.config.options();
|
|
484
583
|
const configured = connection.models.find((entry) => entry.id === model);
|
|
584
|
+
const base = configured === undefined
|
|
585
|
+
? { provider, id: model, name: model, inputModalities: ["text"] }
|
|
586
|
+
: modelInfo(provider, configured);
|
|
485
587
|
return Promise.resolve({
|
|
486
|
-
...
|
|
487
|
-
? { provider, id: model, name: model, inputModalities: ["text"] }
|
|
488
|
-
: modelInfo(provider, configured)),
|
|
588
|
+
...base,
|
|
489
589
|
context: { contextWindow: configured?.contextWindow ?? connection.defaultContextWindow },
|
|
490
590
|
defaultMaxTokens: configured?.maxTokens ?? connection.maxTokens,
|
|
491
591
|
});
|
|
@@ -530,7 +630,8 @@ export class WorkBuddyAdapter extends LlmAdapter {
|
|
|
530
630
|
}
|
|
531
631
|
|
|
532
632
|
async *request(options, signal, connection, onComment) {
|
|
533
|
-
const
|
|
633
|
+
const attachments = this.config.resolveAttachments?.();
|
|
634
|
+
const body = await serializeRequest(options, attachments, signal);
|
|
534
635
|
const headers = {
|
|
535
636
|
"content-type": "application/json",
|
|
536
637
|
"accept": "text/event-stream",
|
|
@@ -586,6 +687,7 @@ const catalogModel = z.object({
|
|
|
586
687
|
contextWindow: z.number().step(1).min(1),
|
|
587
688
|
maxTokens: z.number().step(1).min(1),
|
|
588
689
|
inputModalities: z.array(z.union(MODEL_MODALITIES)).min(1).default(["text"]),
|
|
690
|
+
reasoningEffort: z.string(),
|
|
589
691
|
});
|
|
590
692
|
|
|
591
693
|
export const Config = z.object({
|
|
@@ -625,6 +727,10 @@ function resolveModels(models) {
|
|
|
625
727
|
}
|
|
626
728
|
if (seen.has(model.id)) throw new Error(`dsh-llm-workbuddy: duplicate catalog model "${model.id}"`);
|
|
627
729
|
seen.add(model.id);
|
|
730
|
+
const reasoningEffort = model.reasoningEffort;
|
|
731
|
+
if (reasoningEffort !== undefined && !REASONING_EFFORT_IDS.has(reasoningEffort)) {
|
|
732
|
+
throw new Error(`dsh-llm-workbuddy: catalog model "${model.id}" reasoningEffort must be one of low|medium|high`);
|
|
733
|
+
}
|
|
628
734
|
return {
|
|
629
735
|
id: model.id,
|
|
630
736
|
...(model.name === undefined ? {} : { name: model.name }),
|
|
@@ -632,6 +738,7 @@ function resolveModels(models) {
|
|
|
632
738
|
...(model.contextWindow === undefined ? {} : { contextWindow: model.contextWindow }),
|
|
633
739
|
...(model.maxTokens === undefined ? {} : { maxTokens: model.maxTokens }),
|
|
634
740
|
inputModalities: [...inputModalities],
|
|
741
|
+
...(reasoningEffort === undefined ? {} : { reasoningEffort }),
|
|
635
742
|
};
|
|
636
743
|
});
|
|
637
744
|
}
|
|
@@ -989,7 +1096,10 @@ export function apply(ctx, config) {
|
|
|
989
1096
|
}
|
|
990
1097
|
};
|
|
991
1098
|
options();
|
|
992
|
-
const adapter = new WorkBuddyAdapter({
|
|
1099
|
+
const adapter = new WorkBuddyAdapter({
|
|
1100
|
+
options,
|
|
1101
|
+
resolveAttachments: () => ctx.get("attachments"),
|
|
1102
|
+
});
|
|
993
1103
|
ctx.llm.registerConfigurableProviders([{
|
|
994
1104
|
provider: PROVIDER,
|
|
995
1105
|
displayName: "WorkBuddy",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-llm-workbuddy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "WorkBuddy (via the local workbuddy2api proxy) LLM provider adapter for DeepSeek Harness, with a Web login-status widget",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -48,9 +48,9 @@
|
|
|
48
48
|
],
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
51
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
52
|
-
"@deepseek-ai/dsh-settings": "^0.1.
|
|
53
|
-
"@deepseek-ai/dsh-timeout": "^0.1.
|
|
51
|
+
"@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
|
|
52
|
+
"@deepseek-ai/dsh-settings": "^0.1.1-rc.2",
|
|
53
|
+
"@deepseek-ai/dsh-timeout": "^0.1.1-rc.2",
|
|
54
54
|
"@deepseek-ai/schemastery": "^3.18.1"
|
|
55
55
|
}
|
|
56
56
|
}
|