dsh-connect-workbuddy 4.13.0 → 4.13.1
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/CHANGELOG.md +30 -0
- package/lib/bin.js +1 -1
- package/lib/{host-heartbeat-CxoHaypp.js → host-heartbeat-CwdR6teZ.js} +1 -1
- package/lib/index.js +110 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.13.1 (2026-10-21)
|
|
4
|
+
|
|
5
|
+
> 版本号是 **patch**:没有接口、配置项或请求结构的变更。发往上游的请求体**一个字节都没变**——修的是「请求根本没被发出去」这条本地失败路径:目录里没有 reasoning 元数据的模型,此前在组装请求时就被本地判定失败(`UNSUPPORTED_REASONING_EFFORT`),网络一个字节都没出。路由 id、模型 id、凭据、计费与换号规则一律未变。
|
|
6
|
+
|
|
7
|
+
### Fixes
|
|
8
|
+
|
|
9
|
+
- **issue #90(后半段):选到「没有推理档位元数据」的模型时,消息发不出去,本地直接报 `UNSUPPORTED_REASONING_EFFORT`**
|
|
10
|
+
- **报告**:`#90` 里一位用户在国内区被 11128 挡住(本版 4.13.0 修的那件事),想切国际区绕开,结果撞上**另一个纯本地**的失败:`pi-ai provider "workbuddy-global" model "default-model" does not support reasoning effort "high"`,请求未出网。这条与 11128 相互独立,4.13.0 没有修它。
|
|
11
|
+
- **真实原因:默认档位是「按路由」定的,而能不能接受档位是「按模型」定的。** `profileFor()` 给整条路由钉了 `reasoning: WORKBUDDY_DEFAULT_EFFORT`(`'high'`,issue #83 的修法),`dsh-llm-pi-ai` 每次解析请求都用 `options.reasoningEffort ?? profile.reasoning`。而目录里没有 `reasoning` 块的模型(`toPiModel()` 据此产出 `reasoning: false`)在 pi-ai 眼里只支持 `['off']` 一个档位,继承来的 `'high'` 它接不了,于是 `resolveReasoningLevel()` 在**组装请求时**就抛错。
|
|
12
|
+
- **影响面(实测,不是推测)**:用真实凭据打真实网关取目录——国内区 34 个模型里 **6 个**无 reasoning 元数据(`default`、`glm-5.0`、`glm-4.7`、`glm-4.6`、`hunyuan-chat`、`hunyuan-image-alpha`),国际区 21 个里 **2 个**(`default-model`、`deep-model`);而两份**静态兜底目录**(`FALLBACK_WORKBUDDY_MODELS` / `_GLOBAL`,共 32 条)**全部**没有 reasoning 字段,所以在第一次目录刷新落地之前,**每个模型都会中**。
|
|
13
|
+
- **两道门,都得开**:①宿主 `dsh-llm` 的 `resolveCallWithInfo()` 看到「模型元数据里根本没有 `reasoning`」而调用方**显式**给了任何档位(**连 `'off'` 也算**)就拒;②`PiAiAdapter` 在隐式继承路由默认时于 `resolveReasoningLevel()` 拒。前者早于 adapter,所以只改派发路径修不掉显式 `'off'`。
|
|
14
|
+
- **引入时间**:`git log -S 'reasoning: WORKBUDDY_DEFAULT_EFFORT'` 只指向 `1801b01`(v4.11.0,issue #83 的修法)。在那之前路由默认是缺省的,pi-ai 会自己就近降级(`clampThinkingLevel()` 不抛),所以这个回归是 **4.11.0 起的**。
|
|
15
|
+
- **修法:在插件自己的 adapter 层做「按模型」的档位归一化,不动路由默认。** 新增 `EffortNormalizingAdapter extends PiAiAdapter`(`src/adapter.ts`),判据 `effortCapable(model)` = `catalog.current().some(info => info.id === model && workBuddyThinkingLevelMap(info) !== undefined)`——与 `toPiModel()` 用的是同一个判据,且读**活目录**,所以目录刷新后自动生效,不需要重建 adapter。对判据为假的模型:
|
|
16
|
+
- `resolveModel()` / `prepareCall()` 返回的模型信息补上 `reasoning: { efforts: [{ id: 'off', name: 'Off' }] }`——这正是 pi-ai 认为它唯一支持的档位,于是宿主的门①放行,`'off'` 也成为用户在界面上唯一能选到的档位(此前这类模型只有「Provider default」一个不可选项);
|
|
17
|
+
- `prepareCall().stream` 与 `stream()` 把 `options.reasoningEffort` 归一成 `'off'`,于是门②放行,而 `profileOptions()` 又把 `'off'` 翻成「不下发 reasoning 字段」,pi-ai 侧 `model.reasoning === false` 也保证 `openai-completions` 不会写 `reasoning_effort` ⇒ **上游收到的请求体与修复前完全一致**(修复前是「没收到」)。
|
|
18
|
+
- **一个必须记下的坑:注入的 `reasoning` 不能带 `defaultEffort`。** 宿主 `prepareCall()` 会把 `defaultEffort` **物化**进每一个「没带档位」的请求(`resolvedConfig.reasoningEffort = effective`),随后用 `callConfigEquals()` 逐字段比对调用方的 config,不一致就抛 `INVALID_PREPARED_CALL`。补 `defaultEffort: 'off'` 会让宿主给这类请求凭空加一个字段,正好撞上这条校验——所以只给 `efforts`,让宿主保持「调用方没给就是没给」。
|
|
19
|
+
- **刻意不改的行为**:显式请求一个该模型做不到的档位(例如给 `glm-5.0` 选 `high`)仍然被宿主拒,消息如实说明原因。那是宿主自 v4.11.0 之前就有的契约,静默降级会把一次真实的配置错误藏起来。同理,`profileFor()` 的路由默认**一个字符都没动**:issue #83 靠它才成立(缺省默认在 pi-ai 眼里等于 `off`,`/compact` 因此 100% 400 / 11133),`tests/issue83-profile-default.spec.ts` 的三条断言也照旧全绿。
|
|
20
|
+
- **代价与边界**:判据来自目录的 `reasoning` 元数据;上游哪天给 `glm-5.0` 补上元数据,插件下一次目录刷新就会自动改走正常档位路径(新测试第 6 条钉的就是这个)。反过来,若上游漏报元数据而模型其实支持档位,这类模型会停在 `off`——与 4.11.0 之前的行为一致,不会更糟。
|
|
21
|
+
|
|
22
|
+
### Tests
|
|
23
|
+
|
|
24
|
+
- **`tests/issue90-unsupported-reasoning-effort.spec.ts` 新增 6 条用例**:真实 `Context` + 真实 `LlmRuntime` + 真实插件 `apply()`,只把网关调用替换成三帧 SSE 并录下请求体,钉的是「请求到底出没出去、body 里有没有 `reasoning_effort`」,不是函数里有没有那个 if:
|
|
25
|
+
- 无元数据模型不带档位 → 成功派发,body **不含** `reasoning_effort`(修复前:本地 `UNSUPPORTED_REASONING_EFFORT`,body 一个都没有);
|
|
26
|
+
- 显式 `'off'` → 同样成功(修复前宿主连 `'off'` 都拒);
|
|
27
|
+
- 显式 `'high'` → 仍被拒且 `code === 'UNSUPPORTED_REASONING_EFFORT'`、message 含 `does not support reasoning effort "high"`、**没有任何请求出网**;
|
|
28
|
+
- 有元数据模型不带档位 → body 里 `reasoning_effort === 'high'`(issue #83 的护栏,确保这次没把它修坏);
|
|
29
|
+
- `resolveModelInfo()` 对无元数据模型严格返回 `{ efforts: [{ id: 'off', name: 'Off' }] }`(**没有** `defaultEffort`),对有元数据模型仍是 `['low','high','max']` + `defaultEffort: 'high'`;
|
|
30
|
+
- 目录补上元数据后(改写 `liveConfig` + 发 `loader/volatile-update`)→ 档位自动回到 `high`,证明判据读的是活目录。
|
|
31
|
+
- **变异验证**:`git checkout src/adapter.ts` 回退修复后,6 条里 **4 条失败**(三条报 `+ "code": "UNSUPPORTED_REASONING_EFFORT"`,一条报 `expected undefined to deeply equal { efforts: [ { id: 'off', … } ] }`);仍然通过的 2 条正是「显式 high 必须被拒」与「有元数据模型档位必须上线」——它们在修复前后行为一致,是护栏而非修复证据。
|
|
32
|
+
|
|
3
33
|
## 4.13.0 (2026-10-21)
|
|
4
34
|
|
|
5
35
|
> 版本号是 **minor**:本版有两块改动,其中**请求体**首次新增一条 system 提示的「渠道指纹」归一化(只对开头命中上游清单的 system 消息前置一行占位语,未命中的 body 逐字节不变),这是发往上游的内容变化;另一块是**报错文案**的 405 判断与文案修正。路由 id、模型 id、凭据、计费与换号规则一律未变。
|
package/lib/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as WORKBUDDY_APP_EXECUTABLE_ENV, C as defaultDesktopAuthDirs, F as WorkBuddyUpstreamClient, O as legacyWorkbuddyOwnAuthPath, S as defaultDesktopAuthCandidates, a as readHostHeartbeat, at as findWorkbuddyAppExecutableWithSource, c as WORKBUDDY_CONNECT_VERSION, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, j as workbuddyOwnAuthPath, l as piAiRuntimeInfo, o as workbuddyHostHeartbeatPath, pt as hostPeersInfo, r as isHeartbeatProcessAlive, u as FALLBACK_WORKBUDDY_MODELS, v as WORKBUDDY_AUTH_FILE_ENV, y as WorkBuddyCredentialStore } from "./host-heartbeat-
|
|
2
|
+
import { $ as WORKBUDDY_APP_EXECUTABLE_ENV, C as defaultDesktopAuthDirs, F as WorkBuddyUpstreamClient, O as legacyWorkbuddyOwnAuthPath, S as defaultDesktopAuthCandidates, a as readHostHeartbeat, at as findWorkbuddyAppExecutableWithSource, c as WORKBUDDY_CONNECT_VERSION, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, j as workbuddyOwnAuthPath, l as piAiRuntimeInfo, o as workbuddyHostHeartbeatPath, pt as hostPeersInfo, r as isHeartbeatProcessAlive, u as FALLBACK_WORKBUDDY_MODELS, v as WORKBUDDY_AUTH_FILE_ENV, y as WorkBuddyCredentialStore } from "./host-heartbeat-CwdR6teZ.js";
|
|
3
3
|
import { realpathSync } from "node:fs";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
//#region src/bin.ts
|
|
@@ -4410,7 +4410,7 @@ function generationOf(version) {
|
|
|
4410
4410
|
//#endregion
|
|
4411
4411
|
//#region src/version.ts
|
|
4412
4412
|
/** The npm package version this build was produced from. */
|
|
4413
|
-
const WORKBUDDY_CONNECT_VERSION = "4.13.
|
|
4413
|
+
const WORKBUDDY_CONNECT_VERSION = "4.13.1";
|
|
4414
4414
|
//#endregion
|
|
4415
4415
|
//#region src/host-heartbeat.ts
|
|
4416
4416
|
/**
|
package/lib/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { $ as WORKBUDDY_APP_EXECUTABLE_ENV, A as workbuddyAccountId, B as modelIdOfBody, C as defaultDesktopAuthDirs, D as isVaultAccountId, E as isEncryptedCredentialError, F as WorkBuddyUpstreamClient, G as regionOf, H as parseReasoning, I as classifyUpstreamError, J as lastRefusalFor, K as REFUSES_OFF_MODEL_IDS, L as declaredTools, M as CREDENTIAL_REJECTED_CODE, N as WORKBUDDY_FALLBACK_SYSTEM_PROMPT, O as legacyWorkbuddyOwnAuthPath, P as WorkBuddyCredentialRejectedError, Q as withEffectiveOff, R as isCredentialRejectedError, S as defaultDesktopAuthCandidates, T as hasEncryptedCredentialFields, U as parseUpstreamModel, V as parseCreditMultiplier, W as prepareChatBody, X as offRefused, Y as offDefaultFor, Z as recordRefusal, _ as WORKBUDDY_AUTH_FILENAME, a as readHostHeartbeat, b as WorkBuddyEncryptedCredentialError, c as WORKBUDDY_CONNECT_VERSION, ct as macosBundleExecutable, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, dt as readAtRestKey, et as clearAtRestKeyCache, f as WorkBuddyCatalog, ft as workbuddyAppExecutableCandidates, g as ENCRYPTED_CREDENTIAL_CODE, gt as writeFileAtomic, h as fallbackModelsFor, ht as withFileLock, i as processStartTimeMs, it as findWorkbuddyAppExecutable, j as workbuddyOwnAuthPath, k as parseWorkBuddyAuth, l as piAiRuntimeInfo, lt as macosNestedAppBundles, m as deriveCatalog, mt as resolveDshHome, n as clearHostHeartbeat, nt as deriveAtRestKeyId, o as workbuddyHostHeartbeatPath, ot as isEncryptedFieldWrapper, p as applyContextBudgets, q as defaultOffModelIds, r as isHeartbeatProcessAlive, rt as fetchAtRestKeyPayload, s as writeHostHeartbeat, st as isWorkbuddyBundle, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, tt as deriveAtRestKey, u as FALLBACK_WORKBUDDY_MODELS, ut as openEncryptedField, v as WORKBUDDY_AUTH_FILE_ENV, w as defaultDesktopAuthPath, x as authFileName, y as WorkBuddyCredentialStore, z as knownRegionOf } from "./host-heartbeat-
|
|
1
|
+
import { $ as WORKBUDDY_APP_EXECUTABLE_ENV, A as workbuddyAccountId, B as modelIdOfBody, C as defaultDesktopAuthDirs, D as isVaultAccountId, E as isEncryptedCredentialError, F as WorkBuddyUpstreamClient, G as regionOf, H as parseReasoning, I as classifyUpstreamError, J as lastRefusalFor, K as REFUSES_OFF_MODEL_IDS, L as declaredTools, M as CREDENTIAL_REJECTED_CODE, N as WORKBUDDY_FALLBACK_SYSTEM_PROMPT, O as legacyWorkbuddyOwnAuthPath, P as WorkBuddyCredentialRejectedError, Q as withEffectiveOff, R as isCredentialRejectedError, S as defaultDesktopAuthCandidates, T as hasEncryptedCredentialFields, U as parseUpstreamModel, V as parseCreditMultiplier, W as prepareChatBody, X as offRefused, Y as offDefaultFor, Z as recordRefusal, _ as WORKBUDDY_AUTH_FILENAME, a as readHostHeartbeat, b as WorkBuddyEncryptedCredentialError, c as WORKBUDDY_CONNECT_VERSION, ct as macosBundleExecutable, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, dt as readAtRestKey, et as clearAtRestKeyCache, f as WorkBuddyCatalog, ft as workbuddyAppExecutableCandidates, g as ENCRYPTED_CREDENTIAL_CODE, gt as writeFileAtomic, h as fallbackModelsFor, ht as withFileLock, i as processStartTimeMs, it as findWorkbuddyAppExecutable, j as workbuddyOwnAuthPath, k as parseWorkBuddyAuth, l as piAiRuntimeInfo, lt as macosNestedAppBundles, m as deriveCatalog, mt as resolveDshHome, n as clearHostHeartbeat, nt as deriveAtRestKeyId, o as workbuddyHostHeartbeatPath, ot as isEncryptedFieldWrapper, p as applyContextBudgets, q as defaultOffModelIds, r as isHeartbeatProcessAlive, rt as fetchAtRestKeyPayload, s as writeHostHeartbeat, st as isWorkbuddyBundle, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, tt as deriveAtRestKey, u as FALLBACK_WORKBUDDY_MODELS, ut as openEncryptedField, v as WORKBUDDY_AUTH_FILE_ENV, w as defaultDesktopAuthPath, x as authFileName, y as WorkBuddyCredentialStore, z as knownRegionOf } from "./host-heartbeat-CwdR6teZ.js";
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
3
|
import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
4
|
import { randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { createAssistantMessageEventStream, createProvider, isContextOverflow } from "@earendil-works/pi-ai";
|
|
7
7
|
import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
|
|
8
|
-
import { resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
8
|
+
import { ReasoningEffortId, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
9
9
|
import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
|
|
10
10
|
import { createServer } from "node:http";
|
|
11
11
|
import { Readable } from "node:stream";
|
|
@@ -645,6 +645,103 @@ function withContextRescue(api, resolve) {
|
|
|
645
645
|
};
|
|
646
646
|
}
|
|
647
647
|
/**
|
|
648
|
+
* The tier {@link EffortNormalizingAdapter} pins a model to when the model
|
|
649
|
+
* carries no reasoning metadata of its own.
|
|
650
|
+
*
|
|
651
|
+
* `off` is the ONE tier pi-ai offers for such a model (`getSupportedThinkingLevels`
|
|
652
|
+
* returns `['off']` whenever `model.reasoning` is false), and the host's own
|
|
653
|
+
* translation of it is "send no reasoning field at all" (`profileOptions` turns
|
|
654
|
+
* `off` into `enabledReasoning = undefined`). So pinning it neither invents a
|
|
655
|
+
* capability nor changes one byte of the upstream request body.
|
|
656
|
+
*/
|
|
657
|
+
const NO_REASONING_EFFORT = ReasoningEffortId("off");
|
|
658
|
+
/** What the host is told a reasoning-less model supports: exactly that tier. */
|
|
659
|
+
const NO_REASONING_EFFORTS = { efforts: [{
|
|
660
|
+
id: NO_REASONING_EFFORT,
|
|
661
|
+
name: "Off"
|
|
662
|
+
}] };
|
|
663
|
+
/**
|
|
664
|
+
* pi-ai adapter that keeps a model WITHOUT reasoning metadata dispatchable
|
|
665
|
+
* (issue #90, the `UNSUPPORTED_REASONING_EFFORT` half).
|
|
666
|
+
*
|
|
667
|
+
* THE FAILURE. `profileFor` pins one default tier per ROUTE
|
|
668
|
+
* (`reasoning: WORKBUDDY_DEFAULT_EFFORT`, issue #83), and `dsh-llm-pi-ai`
|
|
669
|
+
* resolves every request as `options.reasoningEffort ?? profile.reasoning`. For
|
|
670
|
+
* a model whose catalog entry carries no `reasoning` block at all — the live CN
|
|
671
|
+
* catalog has six of them (`default`, `glm-5.0`, `glm-4.7`, `glm-4.6`,
|
|
672
|
+
* `hunyuan-chat`, `hunyuan-image-alpha`), the live global one two
|
|
673
|
+
* (`default-model`, `deep-model`), and EVERY entry of both static fallback lists
|
|
674
|
+
* — the inherited `high` is not a tier that model has. `resolveReasoningLevel`
|
|
675
|
+
* then throws `UNSUPPORTED_REASONING_EFFORT` before the request is even built,
|
|
676
|
+
* so the model is unusable locally: no wire call, no upstream error, nothing to
|
|
677
|
+
* retry. Introduced by v4.11.0, which made the profile default explicit; before
|
|
678
|
+
* it the default was absent and pi-ai clamped instead of throwing.
|
|
679
|
+
*
|
|
680
|
+
* TWO GATES, BOTH CLOSED. The host checks the adapter's own model metadata
|
|
681
|
+
* first: `resolveCallWithInfo` rejects ANY explicitly requested tier on a model
|
|
682
|
+
* whose `reasoning` is absent, and `reasoningInfo` omits the field exactly for
|
|
683
|
+
* models without metadata — so even `off` was refused. Then `PiAiAdapter.streamWithSnapshot`
|
|
684
|
+
* rejects the route default it inherited. A fix at either seam alone still
|
|
685
|
+
* fails at the other.
|
|
686
|
+
*
|
|
687
|
+
* THE FIX. Advertise the one tier pi-ai will actually accept for such a model
|
|
688
|
+
* (`off`) and normalize the request to it at the dispatch seam, so
|
|
689
|
+
* `resolveReasoningLevel` sees a supported tier and `profileOptions` sends no
|
|
690
|
+
* reasoning field. Nothing about the wire body changes — pi-ai never emits
|
|
691
|
+
* `reasoning_effort` for a model whose descriptor has `reasoning: false`.
|
|
692
|
+
*
|
|
693
|
+
* WHAT STAYS STRICT. A tier the caller EXPLICITLY asks for on a model that
|
|
694
|
+
* cannot do it is still rejected, by the host, with an accurate message. That
|
|
695
|
+
* is the pre-v4.11.0 contract too, and it is not this bug: the bug is a request
|
|
696
|
+
* that named no tier being killed by the route's own default.
|
|
697
|
+
*/
|
|
698
|
+
var EffortNormalizingAdapter = class extends PiAiAdapter {
|
|
699
|
+
/**
|
|
700
|
+
* Whether one exact model id currently carries reasoning metadata. Read live
|
|
701
|
+
* on every call because the catalog is refreshed behind the adapter
|
|
702
|
+
* (`invalidate`), and a model that gains metadata mid-session must stop being
|
|
703
|
+
* normalized at once.
|
|
704
|
+
*/
|
|
705
|
+
effortCapable;
|
|
706
|
+
constructor(config, effortCapable) {
|
|
707
|
+
super(config);
|
|
708
|
+
this.effortCapable = effortCapable;
|
|
709
|
+
}
|
|
710
|
+
async resolveModel(provider, model, signal) {
|
|
711
|
+
return this.advertiseOffOnly(await super.resolveModel(provider, model, signal));
|
|
712
|
+
}
|
|
713
|
+
async prepareCall(provider, model, signal) {
|
|
714
|
+
const call = await super.prepareCall(provider, model, signal);
|
|
715
|
+
return {
|
|
716
|
+
model: this.advertiseOffOnly(call.model),
|
|
717
|
+
stream: (options) => call.stream(this.pinEffort(options))
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
stream(options) {
|
|
721
|
+
return super.stream(this.pinEffort(options));
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Give a reasoning-less model the one tier it can dispatch at. No
|
|
725
|
+
* `defaultEffort` on purpose: the host materializes one into EVERY request
|
|
726
|
+
* that names no tier, and that would turn the host's own prepared-call config
|
|
727
|
+
* check (`callConfigEquals`) against callers that omit the field.
|
|
728
|
+
*/
|
|
729
|
+
advertiseOffOnly(info) {
|
|
730
|
+
return info.reasoning === void 0 ? {
|
|
731
|
+
...info,
|
|
732
|
+
reasoning: NO_REASONING_EFFORTS
|
|
733
|
+
} : info;
|
|
734
|
+
}
|
|
735
|
+
/** Replace an unusable tier with the one pi-ai accepts for this model. */
|
|
736
|
+
pinEffort(options) {
|
|
737
|
+
if (this.effortCapable(options.model)) return options;
|
|
738
|
+
return options.reasoningEffort === NO_REASONING_EFFORT ? options : {
|
|
739
|
+
...options,
|
|
740
|
+
reasoningEffort: NO_REASONING_EFFORT
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
/**
|
|
648
745
|
* Assemble the adapter. The provider's `getModels` reads the live catalog,
|
|
649
746
|
* and every model's `baseUrl` is re-resolved per read so the shim's
|
|
650
747
|
* ephemeral port applies from the first snapshot after startup.
|
|
@@ -707,13 +804,22 @@ function createWorkBuddyAdapter(options) {
|
|
|
707
804
|
});
|
|
708
805
|
const buildProfiles = () => new Map(modelIdentityKeysOf(providerId, modelIdentity).map((key) => [key, profileFor(key)]));
|
|
709
806
|
let profiles = buildProfiles();
|
|
807
|
+
/**
|
|
808
|
+
* Whether one exact model id currently advertises ANY thinking tier.
|
|
809
|
+
*
|
|
810
|
+
* Same predicate `toPiModel` derives the descriptor's `reasoning` flag from,
|
|
811
|
+
* so the two can never disagree — and it reads the live catalog, so a refresh
|
|
812
|
+
* that adds (or drops) metadata for a model takes effect on the next request
|
|
813
|
+
* without an `invalidate` round trip.
|
|
814
|
+
*/
|
|
815
|
+
const effortCapable = (model) => catalog.current().some((info) => info.id === model && workBuddyThinkingLevelMap(info) !== void 0);
|
|
710
816
|
return {
|
|
711
|
-
adapter: new
|
|
817
|
+
adapter: new EffortNormalizingAdapter({
|
|
712
818
|
profiles: () => profiles,
|
|
713
819
|
auth: INERT_AUTH,
|
|
714
820
|
resolveApiKey: async () => shim.token(),
|
|
715
821
|
...resolveAttachments === void 0 ? {} : { resolveAttachments }
|
|
716
|
-
}),
|
|
822
|
+
}, effortCapable),
|
|
717
823
|
invalidate: () => {
|
|
718
824
|
profiles = buildProfiles();
|
|
719
825
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-connect-workbuddy",
|
|
3
3
|
"displayName": "DSH Connect WorkBuddy",
|
|
4
4
|
"description": "把本机登录的 WorkBuddy 模型(国内版与国际版)接入 DeepSeek Harness:每次刷新按上游目录自动读取每个模型的积分倍率,支持逐模型图片输入开关、多账号切换与账号池自动换号,并提供每日签到、卡片与输入框里的胶囊积分读数等只读积分概览。",
|
|
5
|
-
"version": "4.13.
|
|
5
|
+
"version": "4.13.1",
|
|
6
6
|
"author": "dingminhua",
|
|
7
7
|
"copyright": "Copyright (c) 2026 LaoDing",
|
|
8
8
|
"license": "MIT",
|