@huai_xin/dsh-workbuddy 0.1.3 → 0.1.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/docs/workbuddy-protocol.md +5 -0
- package/package.json +1 -1
- package/src/adapter.js +41 -21
|
@@ -248,6 +248,11 @@ data: [DONE]
|
|
|
248
248
|
频率实测工具:`node test/live/stream-cut-probe.mjs [cn|intl] [次数] [模型] [--long] [--selftest]`
|
|
249
249
|
(2026-10-22 实测:大陆版短问答 4/4 正常、海外版 `deepseek-v4.1-flash` 长回答 10/10 正常,
|
|
250
250
|
即断流是间歇性的,常规探测不易复现)
|
|
251
|
+
- **finish 帧带一个 `response` 诊断块**(`kind`/`version`/`api`/`provider`/`model`/`responseId`/
|
|
252
|
+
`stopReason`/`outputTokens`/`blocks`)。它不是 harness 的契约字段(`dsh-llm` 的类型里没有
|
|
253
|
+
`response` / `stopReason`),纯属遥测:会话诊断里会原样显示,用来一眼回答"这一步是谁答的、
|
|
254
|
+
上游叫它什么 id",不必再从 `request/header` 反推。`responseId` 取自上游流里的 `id`
|
|
255
|
+
(形如 `chatcmpl-…`),可以直接和上游日志对账;harness 不会据此改变行为 ✅
|
|
251
256
|
- `usage`(末帧携带):`prompt_tokens` / `completion_tokens` / `total_tokens` / `prompt_tokens_details.cached_tokens` / `completion_tokens_details.reasoning_tokens` / `prompt_cache_hit_tokens` / `prompt_cache_miss_tokens` / `credit` ✅
|
|
252
257
|
|
|
253
258
|
---
|
package/package.json
CHANGED
package/src/adapter.js
CHANGED
|
@@ -789,6 +789,31 @@ export class WorkBuddyAdapter {
|
|
|
789
789
|
let lastDelta = '无内容'
|
|
790
790
|
/** Output tokens the last usage frame reported, for the truncation log. */
|
|
791
791
|
let outputTokens
|
|
792
|
+
/** The gateway's own response id, for correlating with upstream logs. */
|
|
793
|
+
let responseId
|
|
794
|
+
/**
|
|
795
|
+
* The finish chunk's diagnostics block.
|
|
796
|
+
*
|
|
797
|
+
* Not part of the harness contract — the other adapters attach their own and
|
|
798
|
+
* the session diagnostics show it verbatim, which is how a "who answered this,
|
|
799
|
+
* and what did upstream call it" question gets answered without reconstructing
|
|
800
|
+
* it from the request headers. Carries nothing the harness acts on.
|
|
801
|
+
*/
|
|
802
|
+
const finishChunk = (reason) => ({
|
|
803
|
+
type: 'finish',
|
|
804
|
+
reason,
|
|
805
|
+
response: {
|
|
806
|
+
kind: 'workbuddy',
|
|
807
|
+
version: 1,
|
|
808
|
+
api: 'openai-completions',
|
|
809
|
+
provider: this.region.provider,
|
|
810
|
+
model: options.model,
|
|
811
|
+
...(responseId === undefined ? {} : { responseId }),
|
|
812
|
+
stopReason: finishReason ?? reason.kind,
|
|
813
|
+
...(outputTokens === undefined ? {} : { outputTokens }),
|
|
814
|
+
blocks: blocks.map((block) => ({ type: block.kind }))
|
|
815
|
+
}
|
|
816
|
+
})
|
|
792
817
|
|
|
793
818
|
const textBlock = () => {
|
|
794
819
|
let block = blocks.find((candidate) => candidate.kind === 'text')
|
|
@@ -838,6 +863,7 @@ export class WorkBuddyAdapter {
|
|
|
838
863
|
}
|
|
839
864
|
const choice = data.choices?.[0]
|
|
840
865
|
const delta = choice?.delta
|
|
866
|
+
if (responseId === undefined && typeof data.id === 'string' && data.id.length > 0) responseId = data.id
|
|
841
867
|
if (typeof choice?.finish_reason === 'string' && choice.finish_reason.length > 0) finishReason = choice.finish_reason
|
|
842
868
|
|
|
843
869
|
if (typeof delta?.content === 'string' && delta.content.length > 0) {
|
|
@@ -946,31 +972,25 @@ export class WorkBuddyAdapter {
|
|
|
946
972
|
if (!streamEnded && finishReason === undefined) {
|
|
947
973
|
const detail = describeCut({ receivedBytes, startedAt, lastDataAt, lastDelta })
|
|
948
974
|
this.ctx.logger.warn(`[workbuddy] ${this.region.id}: ${options.model} 的响应在 finish_reason/[DONE] 之前中断(${detail}),按传输错误上报,可自动重试`)
|
|
949
|
-
yield {
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
message: `${this.region.provider}: 上游没有给出结束原因就断开了响应(${detail};可重试的传输错误)`,
|
|
955
|
-
code: 'TRANSPORT'
|
|
956
|
-
}
|
|
975
|
+
yield finishChunk({
|
|
976
|
+
kind: 'error',
|
|
977
|
+
failure: {
|
|
978
|
+
message: `${this.region.provider}: 上游没有给出结束原因就断开了响应(${detail};可重试的传输错误)`,
|
|
979
|
+
code: 'TRANSPORT'
|
|
957
980
|
}
|
|
958
|
-
}
|
|
981
|
+
})
|
|
959
982
|
return
|
|
960
983
|
}
|
|
961
984
|
if (argsTruncated && !streamEnded) {
|
|
962
985
|
const detail = describeCut({ receivedBytes, startedAt, lastDataAt, lastDelta })
|
|
963
986
|
this.ctx.logger.warn(`[workbuddy] ${this.region.id}: ${options.model} 的工具调用参数在传输中被截断(${detail}),按传输错误上报,可自动重试`)
|
|
964
|
-
yield {
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
message: `${this.region.provider}: 工具调用的参数在传输中被截断(${detail};可重试的传输错误)`,
|
|
970
|
-
code: 'TRANSPORT'
|
|
971
|
-
}
|
|
987
|
+
yield finishChunk({
|
|
988
|
+
kind: 'error',
|
|
989
|
+
failure: {
|
|
990
|
+
message: `${this.region.provider}: 工具调用的参数在传输中被截断(${detail};可重试的传输错误)`,
|
|
991
|
+
code: 'TRANSPORT'
|
|
972
992
|
}
|
|
973
|
-
}
|
|
993
|
+
})
|
|
974
994
|
return
|
|
975
995
|
}
|
|
976
996
|
// Truncated arguments are checked before the empty-response rule: a step whose
|
|
@@ -978,11 +998,11 @@ export class WorkBuddyAdapter {
|
|
|
978
998
|
// that "empty" would hide what happened.
|
|
979
999
|
if (argsTruncated) {
|
|
980
1000
|
this.ctx.logger.warn(`[workbuddy] ${this.region.id}: ${options.model} 的工具调用参数被输出上限截断,本步按 max-tokens 上报(harness 可续跑)`)
|
|
981
|
-
yield {
|
|
1001
|
+
yield finishChunk({ kind: 'max-tokens' })
|
|
982
1002
|
return
|
|
983
1003
|
}
|
|
984
1004
|
if (blockCount === 0) {
|
|
985
|
-
yield {
|
|
1005
|
+
yield finishChunk({ kind: 'error', failure: { message: '模型返回了空响应', code: 'EMPTY_RESPONSE' } })
|
|
986
1006
|
return
|
|
987
1007
|
}
|
|
988
1008
|
const reason = finishReason === 'tool_calls' || toolOrder.length > 0
|
|
@@ -999,7 +1019,7 @@ export class WorkBuddyAdapter {
|
|
|
999
1019
|
} else if (reason.kind === 'stop' && finishReason === undefined) {
|
|
1000
1020
|
this.ctx.logger.warn(`[workbuddy] ${this.region.id}: ${options.model} 的响应以 [DONE] 收尾但没有 finish_reason,按 stop 处理(输出 ${outputTokens ?? '未知'} tokens)`)
|
|
1001
1021
|
}
|
|
1002
|
-
yield
|
|
1022
|
+
yield finishChunk(reason)
|
|
1003
1023
|
}
|
|
1004
1024
|
}
|
|
1005
1025
|
|