dsh-llm-workbuddy 0.1.6 → 0.1.7
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 +11 -3
- package/lib/index.js +85 -21
- 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
|
+
显示**推理等级**选择器(低 / 中 / 高)。
|
|
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
|
|
|
@@ -297,7 +303,9 @@ DSH 的 `dsh.client` 机制只要求 `package.json` 里:
|
|
|
297
303
|
## 限制
|
|
298
304
|
|
|
299
305
|
- 当前为纯文本适配器:图片输入会以 `UNSUPPORTED_CONTENT` 拒绝(后续可加)。
|
|
300
|
-
-
|
|
306
|
+
- 推理等级(reasoning effort):支持推理的模型(如 DeepSeek-V4、GLM、Kimi、MiniMax、
|
|
307
|
+
Hy3 等)会显示推理等级下拉(低 / 中 / 高),默认值取平台默认强度。`reasoning_effort`
|
|
308
|
+
会透传给代理;若某模型平台侧只接受平台默认、忽略该参数,则退化为平台默认强度,不影响出字。
|
|
301
309
|
- 代理未运行时,模型请求会以 `TRANSPORT` 错误快速失败(连接被拒绝);但状态
|
|
302
310
|
小组件本身不依赖代理——代理挂了它仍能显示「代理未运行」并允许触发登录。
|
|
303
311
|
- 登录态有效期由 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: "低" },
|
|
101
|
+
{ id: "medium", name: "中" },
|
|
102
|
+
{ id: "high", name: "高" },
|
|
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). */
|
|
@@ -164,6 +213,9 @@ function serializeRequest(options) {
|
|
|
164
213
|
...(options.temperature !== undefined ? { temperature: options.temperature } : {}),
|
|
165
214
|
...(options.maxTokens === undefined ? {} : { max_tokens: options.maxTokens }),
|
|
166
215
|
...(options.stop !== undefined ? { stop: options.stop } : {}),
|
|
216
|
+
// reasoning_effort is opaque here (low|medium|high); the proxy passes it
|
|
217
|
+
// through verbatim to the upstream /v2/chat/completions body.
|
|
218
|
+
...(options.reasoningEffort === undefined ? {} : { reasoning_effort: options.reasoningEffort }),
|
|
167
219
|
};
|
|
168
220
|
}
|
|
169
221
|
|
|
@@ -363,12 +415,14 @@ async function* translate(payloads) {
|
|
|
363
415
|
|
|
364
416
|
/** Display metadata for one catalog entry. */
|
|
365
417
|
function modelInfo(provider, model) {
|
|
418
|
+
const reasoning = modelReasoningInfo(model.reasoningEffort);
|
|
366
419
|
return {
|
|
367
420
|
provider,
|
|
368
421
|
id: model.id,
|
|
369
422
|
name: model.name ?? model.id,
|
|
370
423
|
...(model.description === undefined ? {} : { description: model.description }),
|
|
371
424
|
inputModalities: model.inputModalities ?? ["text"],
|
|
425
|
+
...(reasoning === undefined ? {} : { reasoning }),
|
|
372
426
|
};
|
|
373
427
|
}
|
|
374
428
|
|
|
@@ -457,12 +511,15 @@ export class WorkBuddyAdapter extends LlmAdapter {
|
|
|
457
511
|
if (seen.has(entry.id)) continue;
|
|
458
512
|
seen.add(entry.id);
|
|
459
513
|
const catalog = byId.get(entry.id);
|
|
514
|
+
const effort = liveReasoningEffort(entry, catalog?.reasoningEffort);
|
|
515
|
+
const reasoning = modelReasoningInfo(effort);
|
|
460
516
|
merged.push({
|
|
461
517
|
provider,
|
|
462
518
|
id: entry.id,
|
|
463
519
|
name: entry.name ?? catalog?.name ?? entry.id,
|
|
464
520
|
...(catalog?.description !== undefined ? { description: catalog.description } : {}),
|
|
465
521
|
inputModalities: catalog?.inputModalities ?? ["text"],
|
|
522
|
+
...(reasoning === undefined ? {} : { reasoning }),
|
|
466
523
|
});
|
|
467
524
|
}
|
|
468
525
|
// Catalog entries the proxy did not announce (e.g. unauthenticated or
|
|
@@ -482,10 +539,11 @@ export class WorkBuddyAdapter extends LlmAdapter {
|
|
|
482
539
|
resolveModel(provider, model, _signal) {
|
|
483
540
|
const connection = this.config.options();
|
|
484
541
|
const configured = connection.models.find((entry) => entry.id === model);
|
|
542
|
+
const base = configured === undefined
|
|
543
|
+
? { provider, id: model, name: model, inputModalities: ["text"] }
|
|
544
|
+
: modelInfo(provider, configured);
|
|
485
545
|
return Promise.resolve({
|
|
486
|
-
...
|
|
487
|
-
? { provider, id: model, name: model, inputModalities: ["text"] }
|
|
488
|
-
: modelInfo(provider, configured)),
|
|
546
|
+
...base,
|
|
489
547
|
context: { contextWindow: configured?.contextWindow ?? connection.defaultContextWindow },
|
|
490
548
|
defaultMaxTokens: configured?.maxTokens ?? connection.maxTokens,
|
|
491
549
|
});
|
|
@@ -586,6 +644,7 @@ const catalogModel = z.object({
|
|
|
586
644
|
contextWindow: z.number().step(1).min(1),
|
|
587
645
|
maxTokens: z.number().step(1).min(1),
|
|
588
646
|
inputModalities: z.array(z.union(MODEL_MODALITIES)).min(1).default(["text"]),
|
|
647
|
+
reasoningEffort: z.string(),
|
|
589
648
|
});
|
|
590
649
|
|
|
591
650
|
export const Config = z.object({
|
|
@@ -625,6 +684,10 @@ function resolveModels(models) {
|
|
|
625
684
|
}
|
|
626
685
|
if (seen.has(model.id)) throw new Error(`dsh-llm-workbuddy: duplicate catalog model "${model.id}"`);
|
|
627
686
|
seen.add(model.id);
|
|
687
|
+
const reasoningEffort = model.reasoningEffort;
|
|
688
|
+
if (reasoningEffort !== undefined && !REASONING_EFFORT_IDS.has(reasoningEffort)) {
|
|
689
|
+
throw new Error(`dsh-llm-workbuddy: catalog model "${model.id}" reasoningEffort must be one of low|medium|high`);
|
|
690
|
+
}
|
|
628
691
|
return {
|
|
629
692
|
id: model.id,
|
|
630
693
|
...(model.name === undefined ? {} : { name: model.name }),
|
|
@@ -632,6 +695,7 @@ function resolveModels(models) {
|
|
|
632
695
|
...(model.contextWindow === undefined ? {} : { contextWindow: model.contextWindow }),
|
|
633
696
|
...(model.maxTokens === undefined ? {} : { maxTokens: model.maxTokens }),
|
|
634
697
|
inputModalities: [...inputModalities],
|
|
698
|
+
...(reasoningEffort === undefined ? {} : { reasoningEffort }),
|
|
635
699
|
};
|
|
636
700
|
});
|
|
637
701
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-llm-workbuddy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
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
|
}
|