dsh-connect-trae 4.6.3 → 4.6.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/CHANGELOG.md +46 -0
- package/lib/index.d.ts +38 -1
- package/lib/index.js +27 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.6.4 (2026-10-28)
|
|
4
|
+
|
|
5
|
+
> **缓存命中恒为 0% 不是插件丢了数字,是上游没有分配缓存池。** Trae 的
|
|
6
|
+
> `metadata` 事件里 `prompt_cache_pool_record` 非空才代表这次请求挂了前缀
|
|
7
|
+
> 缓存池;实测 CN/AI 双区、4 个账号、7 个模型、4 种 function,该字段
|
|
8
|
+
> **100% 为 `null`**。没有池,`cache_read_input_tokens` 只能是 0 —— 0% 是
|
|
9
|
+
> 正确读数。本版把这个事实变成可观测的,用户不必再怀疑插件吞了数据。
|
|
10
|
+
|
|
11
|
+
### 修复
|
|
12
|
+
|
|
13
|
+
- **上游的缓存池事实与管线名不再被丢弃**(#67)。
|
|
14
|
+
- **现象**:无论简单还是复杂提问,缓存命中恒为 0%,十个会话一致;
|
|
15
|
+
同时 tps 在 60–80 之间浮动。
|
|
16
|
+
- **根因**:不是插件。issue #10 的转发修复仍在(`lib/index.js` 含
|
|
17
|
+
`cached_tokens`,同版本下 `workbuddy`/`dsh-opencode-go` 命中显示正常),
|
|
18
|
+
是上游 `llm_utils_chat` 改由 `llm_raw_chat_v2` 管线、火山方舟 `ep-…`
|
|
19
|
+
端点承载后,不再为这些请求分配前缀缓存池。当初产出 5248/9216 的
|
|
20
|
+
`scripts/probe-cache-convention.mjs` 原样重跑,现在三轮全是 0。
|
|
21
|
+
- **修法**:`sse.ts` 新增 `diagnostic` 事件类型,解码 `metadata`
|
|
22
|
+
(`prompt_cache_pool_record`)与 `timing_cost`(`name` 管线名、
|
|
23
|
+
`provider_model_name`);`solo-bridge.ts` 通过可选 `log` 上报
|
|
24
|
+
`promptCachePoolRecord` / `pipeline` / `providerModelName`;
|
|
25
|
+
`index.ts` 四个 `TraeSoloBridge` 构造点接入 `ctx.logger.warn`。
|
|
26
|
+
诊断事件不进答案文本、不改 usage 映射,**对外行为不变**。
|
|
27
|
+
- **为什么区分 `null` 与「字段不存在」**:`null` 是上游明确表示「没挂池」,
|
|
28
|
+
字段缺失只是没这条信息;两者混为一谈就又回到「看不出是谁的问题」。
|
|
29
|
+
|
|
30
|
+
### 修复(顺带守住一个回归)
|
|
31
|
+
|
|
32
|
+
- **`metadata` 不能被 peek 当成「上游已应答」**。它现在是被认识的事件类型,
|
|
33
|
+
若据此停止 peek,紧跟其后的拒绝(如 `1005` 未订阅)会退化成截断的 200 流
|
|
34
|
+
—— 正是 issue #19 的故障形态。`openTraeSource` 显式把 `diagnostic` 排除在
|
|
35
|
+
「已应答」之外。
|
|
36
|
+
|
|
37
|
+
### 测试
|
|
38
|
+
|
|
39
|
+
- `tests/sse.spec.ts`:`metadata` / `timing_cost` 解码为 `diagnostic`,
|
|
40
|
+
保留 `prompt_cache_pool_record: null`,且区分字段缺失。
|
|
41
|
+
- `tests/solo-bridge.spec.ts`:诊断事实上报(含 `null` 原样保留、事件不泄漏进
|
|
42
|
+
答案);`metadata` 之后紧跟的 `1005` 仍转成 402 `hard_credit`。
|
|
43
|
+
|
|
44
|
+
### 说明
|
|
45
|
+
|
|
46
|
+
- 诊断细节与完整证据链见 `docs/ISSUE67_DIAGNOSIS.md`(不随 npm 包发布)。
|
|
47
|
+
- 上游若开始分配缓存池,`cached_tokens` 会自动恢复显示,插件侧无需再改。
|
|
48
|
+
|
|
3
49
|
## 4.6.3 (2026-10-27)
|
|
4
50
|
|
|
5
51
|
> **国际版美国区账号不会再被当成新加坡区。** 它的凭据明明写着
|
package/lib/index.d.ts
CHANGED
|
@@ -3426,6 +3426,29 @@ type TraeStreamEvent = {
|
|
|
3426
3426
|
*/
|
|
3427
3427
|
cacheReadTokens?: number;
|
|
3428
3428
|
cacheWriteTokens?: number;
|
|
3429
|
+
} | {
|
|
3430
|
+
/**
|
|
3431
|
+
* The upstream's own account of how the request was served: `metadata`
|
|
3432
|
+
* (acceptance, plus `prompt_cache_pool_record`) and `timing_cost` (the
|
|
3433
|
+
* pipeline and the backing provider model).
|
|
3434
|
+
*
|
|
3435
|
+
* These events were previously folded into `unknown` and dropped. That
|
|
3436
|
+
* mattered for issue #67: `prompt_cache_pool_record` is the ONLY upstream
|
|
3437
|
+
* signal that says whether a prompt-cache pool was even attached to the
|
|
3438
|
+
* request, so with it dropped a "0% cache" report could not be told apart
|
|
3439
|
+
* from a plugin that lost the numbers. See docs/ISSUE67_DIAGNOSIS.md.
|
|
3440
|
+
*/
|
|
3441
|
+
type: 'diagnostic';
|
|
3442
|
+
/**
|
|
3443
|
+
* `metadata.prompt_cache_pool_record`. `null` is a real, observed value
|
|
3444
|
+
* (100% of measured requests on 2026-10-28) and means the upstream attached
|
|
3445
|
+
* no cache pool; it is kept distinct from "the field was absent".
|
|
3446
|
+
*/
|
|
3447
|
+
promptCachePoolRecord?: unknown;
|
|
3448
|
+
/** `timing_cost.name`, e.g. `llm_raw_chat_v2`. */
|
|
3449
|
+
pipeline?: string;
|
|
3450
|
+
/** `timing_cost.provider_model_name` — the model that actually answered. */
|
|
3451
|
+
providerModelName?: string;
|
|
3429
3452
|
} | {
|
|
3430
3453
|
type: 'done';
|
|
3431
3454
|
finishReason: string;
|
|
@@ -3569,7 +3592,21 @@ declare class TraeSoloBridge implements TraeUpstreamClient {
|
|
|
3569
3592
|
private readonly upstream;
|
|
3570
3593
|
private readonly catalog?;
|
|
3571
3594
|
private readonly wireResolver?;
|
|
3572
|
-
|
|
3595
|
+
/**
|
|
3596
|
+
* Optional sink for the upstream's own diagnostics (`metadata` /
|
|
3597
|
+
* `timing_cost`). Issue #67: without it, `prompt_cache_pool_record: null`
|
|
3598
|
+
* was invisible, so "the upstream attached no cache pool" and "the plugin
|
|
3599
|
+
* lost the numbers" looked identical to a user reading a 0% cache rate.
|
|
3600
|
+
*/
|
|
3601
|
+
private readonly log?;
|
|
3602
|
+
constructor(upstream: TraeUpstreamClient, catalog?: Pick<TraeCatalog, "current"> | undefined, wireResolver?: TraeWireResolver | undefined,
|
|
3603
|
+
/**
|
|
3604
|
+
* Optional sink for the upstream's own diagnostics (`metadata` /
|
|
3605
|
+
* `timing_cost`). Issue #67: without it, `prompt_cache_pool_record: null`
|
|
3606
|
+
* was invisible, so "the upstream attached no cache pool" and "the plugin
|
|
3607
|
+
* lost the numbers" looked identical to a user reading a 0% cache rate.
|
|
3608
|
+
*/
|
|
3609
|
+
log?: ((message: string, detail?: unknown) => void) | undefined);
|
|
3573
3610
|
chatStream(bodyJson: string, signal?: AbortSignal): Promise<TraeChatResult>;
|
|
3574
3611
|
}
|
|
3575
3612
|
//#endregion
|
package/lib/index.js
CHANGED
|
@@ -5581,6 +5581,12 @@ function decodeTraeEvent(event) {
|
|
|
5581
5581
|
...typeof record["cache_read_input_tokens"] === "number" ? { cacheReadTokens: record["cache_read_input_tokens"] } : {},
|
|
5582
5582
|
...typeof record["cache_creation_input_tokens"] === "number" ? { cacheWriteTokens: record["cache_creation_input_tokens"] } : {}
|
|
5583
5583
|
};
|
|
5584
|
+
if (event.event === "metadata" || event.event === "timing_cost") return {
|
|
5585
|
+
type: "diagnostic",
|
|
5586
|
+
...record["prompt_cache_pool_record"] === void 0 ? {} : { promptCachePoolRecord: record["prompt_cache_pool_record"] },
|
|
5587
|
+
...typeof record["name"] === "string" ? { pipeline: record["name"] } : {},
|
|
5588
|
+
...typeof record["provider_model_name"] === "string" ? { providerModelName: record["provider_model_name"] } : {}
|
|
5589
|
+
};
|
|
5584
5590
|
if (event.event === "done" || typeof record["finish_reason"] === "string" && record["response"] === void 0) return {
|
|
5585
5591
|
type: "done",
|
|
5586
5592
|
finishReason: typeof record["finish_reason"] === "string" ? record["finish_reason"] : "stop"
|
|
@@ -5747,7 +5753,7 @@ function bridgeTraeSoloStream(response, model) {
|
|
|
5747
5753
|
return bridgeTraeSource(source, model);
|
|
5748
5754
|
}
|
|
5749
5755
|
/** {@link bridgeTraeSoloStream} over an already-opened byte stream. */
|
|
5750
|
-
function bridgeTraeSource(source, model) {
|
|
5756
|
+
function bridgeTraeSource(source, model, log) {
|
|
5751
5757
|
const id = `chatcmpl-${randomUUID().replaceAll("-", "").slice(0, 24)}`;
|
|
5752
5758
|
const created = Math.floor(Date.now() / 1e3);
|
|
5753
5759
|
const decoder = new TextDecoder();
|
|
@@ -5780,6 +5786,15 @@ function bridgeTraeSource(source, model) {
|
|
|
5780
5786
|
if (failure !== void 0) upstreamError = new Error(describeTraeFailure(failure));
|
|
5781
5787
|
return;
|
|
5782
5788
|
}
|
|
5789
|
+
if (decoded.type === "diagnostic") {
|
|
5790
|
+
if (decoded.promptCachePoolRecord !== void 0 || decoded.pipeline !== void 0) log?.("dsh-connect-trae: upstream request diagnostics", {
|
|
5791
|
+
model,
|
|
5792
|
+
promptCachePoolRecord: decoded.promptCachePoolRecord,
|
|
5793
|
+
pipeline: decoded.pipeline,
|
|
5794
|
+
providerModelName: decoded.providerModelName
|
|
5795
|
+
});
|
|
5796
|
+
return;
|
|
5797
|
+
}
|
|
5783
5798
|
if (decoded.type === "delta") {
|
|
5784
5799
|
const delta = {};
|
|
5785
5800
|
if (decoded.text !== "") delta["content"] = decoded.text;
|
|
@@ -5932,7 +5947,7 @@ async function openTraeSource(response) {
|
|
|
5932
5947
|
message: describeTraeFailure(failure)
|
|
5933
5948
|
};
|
|
5934
5949
|
}
|
|
5935
|
-
if (decoded.type !== "unknown") answered = true;
|
|
5950
|
+
if (decoded.type !== "unknown" && decoded.type !== "diagnostic") answered = true;
|
|
5936
5951
|
}
|
|
5937
5952
|
if (answered) break;
|
|
5938
5953
|
}
|
|
@@ -5956,12 +5971,12 @@ async function openTraeSource(response) {
|
|
|
5956
5971
|
* The sibling {@link bridgeTraeSoloStream} stays synchronous and always answers
|
|
5957
5972
|
* 200; it is the primitive, and this is the policy the bridge class uses.
|
|
5958
5973
|
*/
|
|
5959
|
-
async function bridgeTraeSoloStreamOrFail(response, model) {
|
|
5974
|
+
async function bridgeTraeSoloStreamOrFail(response, model, log) {
|
|
5960
5975
|
const opened = await openTraeSource(response);
|
|
5961
5976
|
if (!opened.ok) return opened;
|
|
5962
5977
|
return {
|
|
5963
5978
|
ok: true,
|
|
5964
|
-
response: bridgeTraeSource(opened.source, model)
|
|
5979
|
+
response: bridgeTraeSource(opened.source, model, log)
|
|
5965
5980
|
};
|
|
5966
5981
|
}
|
|
5967
5982
|
/**
|
|
@@ -6002,10 +6017,12 @@ var TraeSoloBridge = class {
|
|
|
6002
6017
|
upstream;
|
|
6003
6018
|
catalog;
|
|
6004
6019
|
wireResolver;
|
|
6005
|
-
|
|
6020
|
+
log;
|
|
6021
|
+
constructor(upstream, catalog, wireResolver, log) {
|
|
6006
6022
|
this.upstream = upstream;
|
|
6007
6023
|
this.catalog = catalog;
|
|
6008
6024
|
this.wireResolver = wireResolver;
|
|
6025
|
+
this.log = log;
|
|
6009
6026
|
}
|
|
6010
6027
|
async chatStream(bodyJson, signal) {
|
|
6011
6028
|
let model = "glm-5.2";
|
|
@@ -6093,7 +6110,7 @@ var TraeSoloBridge = class {
|
|
|
6093
6110
|
}
|
|
6094
6111
|
const result = await this.upstream.chatStream(prepared, signal);
|
|
6095
6112
|
if (!result.ok) return result;
|
|
6096
|
-
return await bridgeTraeSoloStreamOrFail(result.response, model);
|
|
6113
|
+
return await bridgeTraeSoloStreamOrFail(result.response, model, this.log);
|
|
6097
6114
|
}
|
|
6098
6115
|
};
|
|
6099
6116
|
//#endregion
|
|
@@ -11189,7 +11206,7 @@ function apply(ctx, config) {
|
|
|
11189
11206
|
route,
|
|
11190
11207
|
log: (message, detail) => ctx.logger.warn(message, detail)
|
|
11191
11208
|
});
|
|
11192
|
-
const delegating = new TraeDelegatingUpstreamClient(new TraeSoloBridge(client, catalog, wireResolver));
|
|
11209
|
+
const delegating = new TraeDelegatingUpstreamClient(new TraeSoloBridge(client, catalog, wireResolver, (message, detail) => ctx.logger.warn(message, detail)));
|
|
11193
11210
|
routeClients[provider] = {
|
|
11194
11211
|
provider,
|
|
11195
11212
|
route,
|
|
@@ -11230,7 +11247,7 @@ function apply(ctx, config) {
|
|
|
11230
11247
|
runtime: resolved.runtime
|
|
11231
11248
|
}
|
|
11232
11249
|
}),
|
|
11233
|
-
solo: new TraeSoloBridge(primary.client, catalog, wireResolver),
|
|
11250
|
+
solo: new TraeSoloBridge(primary.client, catalog, wireResolver, (message, detail) => ctx.logger.warn(message, detail)),
|
|
11234
11251
|
endpoint: `${REGION_GATEWAYS.cn.chat}/api/ide/v2/llm_raw_chat`,
|
|
11235
11252
|
edition: resolved.identity.edition,
|
|
11236
11253
|
identity: {
|
|
@@ -11627,7 +11644,7 @@ function apply(ctx, config) {
|
|
|
11627
11644
|
identity: () => stack.identity(),
|
|
11628
11645
|
route: stack.routeOf(providerOfPanel(panel)),
|
|
11629
11646
|
log: (message, detail) => ctx.logger.warn(message, detail)
|
|
11630
|
-
}), stack.catalog, stack.wireResolver)
|
|
11647
|
+
}), stack.catalog, stack.wireResolver, (message, detail) => ctx.logger.warn(message, detail))
|
|
11631
11648
|
};
|
|
11632
11649
|
}
|
|
11633
11650
|
/**
|
|
@@ -11709,7 +11726,7 @@ function apply(ctx, config) {
|
|
|
11709
11726
|
identity: () => stack.identity(),
|
|
11710
11727
|
route: stack.routeOf(providerOfPanel(panel)),
|
|
11711
11728
|
log: (message, detail) => ctx.logger.warn(message, detail)
|
|
11712
|
-
}), stack.catalog, stack.wireResolver);
|
|
11729
|
+
}), stack.catalog, stack.wireResolver, (message, detail) => ctx.logger.warn(message, detail));
|
|
11713
11730
|
const chat = (body, signal) => bridge.chatStream(body, signal);
|
|
11714
11731
|
bridges.set(accountId, chat);
|
|
11715
11732
|
return chat;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-connect-trae",
|
|
3
3
|
"displayName": "DSH Connect Trae",
|
|
4
4
|
"description": "把本机登录的 Trae 模型接入 DeepSeek Harness:国内版与国际版双供应商并行,提供用量/积分概览与每日签到领取。",
|
|
5
|
-
"version": "4.6.
|
|
5
|
+
"version": "4.6.4",
|
|
6
6
|
"author": "dingminhua",
|
|
7
7
|
"copyright": "Copyright (c) 2026 LaoDing",
|
|
8
8
|
"license": "MIT",
|