@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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huai_xin/dsh-workbuddy",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "WorkBuddy model provider for DeepSeek Harness (international + China deployments)",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
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
- type: 'finish',
951
- reason: {
952
- kind: 'error',
953
- failure: {
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
- type: 'finish',
966
- reason: {
967
- kind: 'error',
968
- failure: {
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 { type: 'finish', reason: { kind: 'max-tokens' } }
1001
+ yield finishChunk({ kind: 'max-tokens' })
982
1002
  return
983
1003
  }
984
1004
  if (blockCount === 0) {
985
- yield { type: 'finish', reason: { kind: 'error', failure: { message: '模型返回了空响应', code: 'EMPTY_RESPONSE' } } }
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 { type: 'finish', reason }
1022
+ yield finishChunk(reason)
1003
1023
  }
1004
1024
  }
1005
1025