@acosmi/sdk-ts 2.19.2 → 2.19.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.
@@ -407,272 +407,15 @@ var init_errors = __esm({
407
407
  }
408
408
  });
409
409
 
410
- // src/models/betas.ts
411
- function buildBetas(caps, req) {
412
- const betas = [];
413
- if (caps.supports_isp) {
414
- betas.push(betaInterleavedThinking);
415
- betas.push(betaContextManagement);
416
- }
417
- if (caps.supports_redact_thinking && req.thinking && req.thinking.display === "summary") {
418
- betas.push(betaRedactThinking);
419
- }
420
- if (caps.supports_1m_context) {
421
- betas.push(betaContext1M);
422
- }
423
- const hasStructuredOutput = caps.supports_structured_output && req.outputConfig != null;
424
- if (hasStructuredOutput) {
425
- betas.push(betaStructuredOutputs);
426
- } else if (caps.supports_token_efficient) {
427
- betas.push(betaTokenEfficientTools);
428
- }
429
- if (caps.supports_tool_search) {
430
- betas.push(betaAdvancedToolUse);
431
- }
432
- const needsEffort = req.effort != null || req.thinking != null && req.thinking.level && req.thinking.level !== exports.ThinkingOff;
433
- if (caps.supports_effort && needsEffort) {
434
- betas.push(betaEffort);
435
- }
436
- if (caps.supports_fast_mode && req.speed === "fast") {
437
- betas.push(betaFastMode);
438
- }
439
- if (caps.supports_prompt_cache) {
440
- betas.push(betaPromptCachingScope);
441
- }
442
- return uniqueMerge(betas, req.betas ?? []);
443
- }
444
- function uniqueMerge(base, extra) {
445
- if (extra.length === 0) return base;
446
- const seen = new Set(base);
447
- for (const s of extra) {
448
- if (!seen.has(s)) {
449
- base.push(s);
450
- seen.add(s);
451
- }
452
- }
453
- return base;
454
- }
455
- var betaInterleavedThinking, betaContext1M, betaContextManagement, betaStructuredOutputs, betaAdvancedToolUse, betaEffort, betaPromptCachingScope, betaFastMode, betaRedactThinking, betaTokenEfficientTools;
456
- var init_betas = __esm({
457
- "src/models/betas.ts"() {
458
- init_types();
459
- betaInterleavedThinking = "interleaved-thinking-2025-05-14";
460
- betaContext1M = "context-1m-2025-08-07";
461
- betaContextManagement = "context-management-2025-06-27";
462
- betaStructuredOutputs = "structured-outputs-2025-11-13";
463
- betaAdvancedToolUse = "advanced-tool-use-2025-11-20";
464
- betaEffort = "effort-2025-11-24";
465
- betaPromptCachingScope = "prompt-caching-scope-2026-01-05";
466
- betaFastMode = "fast-mode-2026-02-01";
467
- betaRedactThinking = "redact-thinking-2026-02-12";
468
- betaTokenEfficientTools = "token-efficient-tools-2025-02-19";
469
- }
470
- });
471
-
472
- // src/models/adapters/anthropic.ts
473
- function resolveThinkingLevel(body, req, caps) {
474
- const level = req.thinking?.level ?? "";
475
- if (level === exports.ThinkingOff) {
476
- body["thinking"] = { type: "disabled" };
477
- return;
478
- }
479
- if (!caps.supports_adaptive_thinking && !caps.supports_thinking) {
480
- return;
481
- }
482
- let maxTokens = req.max_tokens ?? 0;
483
- if (maxTokens <= 0) {
484
- maxTokens = exports.ThinkingHighMinMaxTokens;
485
- }
486
- if (level === exports.ThinkingMax) {
487
- let modelMax = caps.max_output_tokens;
488
- if (modelMax <= 0) modelMax = exports.ThinkingMaxFallbackMaxTokens;
489
- if (maxTokens < modelMax) maxTokens = modelMax;
490
- } else {
491
- if (maxTokens < exports.ThinkingHighMinMaxTokens) maxTokens = exports.ThinkingHighMinMaxTokens;
492
- }
493
- body["max_tokens"] = maxTokens;
494
- if (caps.supports_adaptive_thinking) {
495
- const thinking = { type: "adaptive" };
496
- if (req.thinking?.display) thinking["display"] = req.thinking.display;
497
- body["thinking"] = thinking;
498
- } else if (caps.supports_thinking) {
499
- let budget = maxTokens - 1;
500
- if (budget < 1024) budget = 1024;
501
- const thinking = {
502
- type: "enabled",
503
- budget_tokens: budget
504
- };
505
- if (req.thinking?.display) thinking["display"] = req.thinking.display;
506
- body["thinking"] = thinking;
507
- }
508
- if (caps.supports_effort) {
509
- let effortLevel = "high";
510
- if (level === "low") effortLevel = "low";
511
- if (level === "medium" || level === "xhigh") effortLevel = level;
512
- if (level === exports.ThinkingMax && caps.supports_max_effort) {
513
- effortLevel = "max";
514
- }
515
- body["effort"] = { level: effortLevel };
516
- }
517
- delete body["temperature"];
518
- }
519
- var ANTHROPIC_SDK_MANAGED_BODY_KEYS; exports.AnthropicAdapter = void 0;
520
- var init_anthropic = __esm({
521
- "src/models/adapters/anthropic.ts"() {
522
- init_types();
523
- init_errors();
524
- init_betas();
525
- init_adapters();
526
- ANTHROPIC_SDK_MANAGED_BODY_KEYS = /* @__PURE__ */ new Set([
527
- "thinking",
528
- "effort",
529
- "max_tokens",
530
- "temperature",
531
- "betas"
532
- ]);
533
- exports.AnthropicAdapter = class {
534
- format() {
535
- return 0 /* Anthropic */;
536
- }
537
- endpointSuffix() {
538
- return "/anthropic";
539
- }
540
- /**
541
- * 构建 Anthropic 格式请求体
542
- * 逻辑等同于原 buildChatRequest, 包含完整的 betas/tools/serverTools/extraBody 处理
543
- */
544
- buildRequestBody(caps, req) {
545
- const body = {};
546
- if (req.rawMessages != null) {
547
- body["messages"] = req.rawMessages;
548
- } else if ((req.messages?.length ?? 0) > 0) {
549
- body["messages"] = req.messages;
550
- }
551
- body["stream"] = req.stream === true;
552
- if (req.max_tokens && req.max_tokens > 0) {
553
- body["max_tokens"] = req.max_tokens;
554
- }
555
- if (req.system != null) {
556
- body["system"] = req.system;
557
- }
558
- if (req.temperature != null) {
559
- body["temperature"] = req.temperature;
560
- }
561
- if (req.thinking && req.thinking.level && req.thinking.level !== "") {
562
- resolveThinkingLevel(body, req, caps);
563
- } else {
564
- if (req.thinking) {
565
- body["thinking"] = req.thinking;
566
- }
567
- if (req.effort) {
568
- body["effort"] = req.effort;
569
- }
570
- }
571
- if (req.metadata || req.endUserId && req.endUserId !== "") {
572
- const meta = {};
573
- if (req.metadata) {
574
- for (const [k, v] of Object.entries(req.metadata)) {
575
- meta[k] = v;
576
- }
577
- }
578
- if (req.endUserId && req.endUserId !== "" && meta["user_id"] === void 0) {
579
- meta["user_id"] = req.endUserId;
580
- }
581
- body["metadata"] = meta;
582
- }
583
- const allTools = [];
584
- if (req.tools != null) {
585
- try {
586
- const parsed = JSON.parse(JSON.stringify(req.tools));
587
- if (Array.isArray(parsed)) {
588
- for (const t of parsed) allTools.push(t);
589
- }
590
- } catch {
591
- }
592
- }
593
- for (const st of req.serverTools ?? []) {
594
- const schema = {
595
- type: st.type,
596
- name: st.name
597
- };
598
- if (st.config) {
599
- for (const [k, v] of Object.entries(st.config)) {
600
- schema[k] = v;
601
- }
602
- }
603
- allTools.push(schema);
604
- }
605
- if (allTools.length > 0) {
606
- body["tools"] = allTools;
607
- }
608
- if (req.speed && req.speed !== "") {
609
- body["speed"] = req.speed;
610
- }
611
- if (req.outputConfig) {
612
- body["output_config"] = req.outputConfig;
613
- }
614
- const betas = buildBetas(caps, req);
615
- if (betas.length > 0) {
616
- body["betas"] = betas;
617
- }
618
- if (req.extraBody) {
619
- for (const [k, v] of Object.entries(req.extraBody)) {
620
- if (ANTHROPIC_SDK_MANAGED_BODY_KEYS.has(k)) {
621
- console.warn(
622
- `acosmi-sdk: extraBody key "${k}" is SDK-managed and was ignored (use the dedicated request field instead).`
623
- );
624
- continue;
625
- }
626
- body[k] = v;
627
- }
628
- }
629
- return body;
630
- }
631
- /**
632
- * 解析 Anthropic 格式同步响应
633
- * 兼容 APIResponse 包装 {"code":0,"data":{...}} 和裸 Anthropic JSON 两种格式
634
- */
635
- parseResponse(bodyInput) {
636
- const bodyStr = typeof bodyInput === "string" ? bodyInput : new TextDecoder().decode(bodyInput);
637
- let raw = bodyStr;
638
- try {
639
- const wrapper = JSON.parse(bodyStr);
640
- if (wrapper.data != null && wrapper.data !== null) {
641
- if ((wrapper.code ?? 0) !== 0) {
642
- throw new exports.BusinessError(wrapper.code ?? 0, wrapper.message ?? "");
643
- }
644
- raw = JSON.stringify(wrapper.data);
645
- }
646
- } catch (e) {
647
- if (e instanceof exports.BusinessError) throw e;
648
- }
649
- let resp;
650
- try {
651
- resp = JSON.parse(raw);
652
- } catch (e) {
653
- throw new Error(
654
- `decode anthropic response: ${e instanceof Error ? e.message : String(e)}`
655
- );
656
- }
657
- resp.tokenRemaining = -1;
658
- resp.callRemaining = -1;
659
- resp.modelTokenRemaining = -1;
660
- resp.modelTokenRemainingETU = -1;
661
- return resp;
662
- }
663
- /**
664
- * 解析 Anthropic SSE 行
665
- *
666
- * Anthropic 原生协议无 [DONE] (message_stop 后上游关闭连接),
667
- * 但 Nexus Gateway 的 ChatStream 路径会追加 [DONE] 哨兵, 此处一并处理。
668
- */
669
- parseStreamLine(eventType, data) {
670
- if (data === "[DONE]") {
671
- return { event: { event: "", data: "" }, done: true };
672
- }
673
- return { event: { event: eventType, data }, done: false };
674
- }
675
- };
410
+ // src/models/adapters/format.ts
411
+ exports.ProviderFormat = void 0;
412
+ var init_format = __esm({
413
+ "src/models/adapters/format.ts"() {
414
+ exports.ProviderFormat = /* @__PURE__ */ ((ProviderFormat2) => {
415
+ ProviderFormat2[ProviderFormat2["Anthropic"] = 0] = "Anthropic";
416
+ ProviderFormat2[ProviderFormat2["OpenAI"] = 1] = "OpenAI";
417
+ return ProviderFormat2;
418
+ })(exports.ProviderFormat || {});
676
419
  }
677
420
  });
678
421
 
@@ -745,16 +488,19 @@ function convertOpenAIToChatResponse(oai) {
745
488
  role: "assistant",
746
489
  content: [],
747
490
  stop_reason: "",
491
+ // [W-SDK-OPENAI-PARITY] 缺 usage 的响应按 0 计数, 而不是让 `oai.usage.prompt_tokens`
492
+ // 抛 TypeError 把整条响应打死 —— 上游不计量是常见形态, 正文照样是有效的。
493
+ // 与 Go 侧同名函数逐字同语义 (那里 Usage 是值类型, 缺席即零值)。
748
494
  usage: {
749
- input_tokens: oai.usage.prompt_tokens,
750
- output_tokens: oai.usage.completion_tokens
495
+ input_tokens: oai.usage?.prompt_tokens ?? 0,
496
+ output_tokens: oai.usage?.completion_tokens ?? 0
751
497
  },
752
498
  tokenRemaining: -1,
753
499
  callRemaining: -1,
754
500
  modelTokenRemaining: -1,
755
501
  modelTokenRemainingETU: -1
756
502
  };
757
- if (oai.choices.length > 0) {
503
+ if (Array.isArray(oai.choices) && oai.choices.length > 0) {
758
504
  const choice = oai.choices[0];
759
505
  switch (choice.finish_reason) {
760
506
  case "stop":
@@ -828,12 +574,14 @@ function parseOpenAIResponseToAnthropic(raw) {
828
574
  content: [],
829
575
  model: oaiResp.model,
830
576
  stop_reason: "",
577
+ // [W-SDK-OPENAI-PARITY] 同 convertOpenAIToChatResponse: 缺 usage 按 0 计数, 不抛。
578
+ // 两处都要改 —— 只改一处等于「非流式路径修好了一半」, 而两条路径是同一族上游响应。
831
579
  usage: {
832
- input_tokens: oaiResp.usage.prompt_tokens,
833
- output_tokens: oaiResp.usage.completion_tokens
580
+ input_tokens: oaiResp.usage?.prompt_tokens ?? 0,
581
+ output_tokens: oaiResp.usage?.completion_tokens ?? 0
834
582
  }
835
583
  };
836
- if (oaiResp.choices.length > 0) {
584
+ if (Array.isArray(oaiResp.choices) && oaiResp.choices.length > 0) {
837
585
  const choice = oaiResp.choices[0];
838
586
  switch (choice.finish_reason) {
839
587
  case "stop":
@@ -871,6 +619,21 @@ function parseOpenAIResponseToAnthropic(raw) {
871
619
  }
872
620
  return resp;
873
621
  }
622
+ function readStreamUsage(chunk) {
623
+ const raw = chunk.usage;
624
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
625
+ return null;
626
+ }
627
+ const { prompt_tokens: promptTokens, completion_tokens: completionTokens } = raw;
628
+ const usage = {};
629
+ if (typeof promptTokens === "number" && Number.isFinite(promptTokens)) {
630
+ usage.input_tokens = promptTokens;
631
+ }
632
+ if (typeof completionTokens === "number" && Number.isFinite(completionTokens)) {
633
+ usage.output_tokens = completionTokens;
634
+ }
635
+ return usage;
636
+ }
874
637
  function newOpenAIStreamConverter() {
875
638
  return new OpenAIStreamConverter();
876
639
  }
@@ -879,7 +642,7 @@ var init_openai = __esm({
879
642
  "src/models/adapters/openai.ts"() {
880
643
  init_types();
881
644
  init_errors();
882
- init_adapters();
645
+ init_format();
883
646
  exports.OpenAIAdapter = class {
884
647
  format() {
885
648
  return 1 /* OpenAI */;
@@ -1004,8 +767,18 @@ var init_openai = __esm({
1004
767
  toolArgsAccum = /* @__PURE__ */ new Map();
1005
768
  /** 上一次解析出的 tool 键,供缺 index 且缺 id 的后续增量沿用。 */
1006
769
  lastToolKey = null;
1007
- /** 已发出 message_delta/message_stop,避免 finish_reason 与 `[DONE]` 各收一次。 */
770
+ /** 已发出 message_delta/message_stop,避免 finish_reason、usage 尾帧、`[DONE]` 与
771
+ * {@link flush} 重复收口 —— 整条流恰好一个 message_stop。 */
1008
772
  messageClosed = false;
773
+ /** 已为仍打开的块发出 content_block_stop。与 {@link messageClosed} 分开记:块在
774
+ * finish_reason 帧上就关,message_delta/message_stop 却可能推迟到之后的帧。 */
775
+ blocksClosed = false;
776
+ /** [W-QUAD-CHAIN-20260914] finish_reason 已到、但 message_delta/message_stop 因等待
777
+ * usage 尾帧而推迟时记下的 stop_reason;没有推迟中的收尾时为 null。 */
778
+ pendingStopReason = null;
779
+ /** [W-QUAD-CHAIN-20260914] 最近一次带 usage 对象的帧搬出的 usage(后到覆盖先到);
780
+ * 整条流从未出现 usage 对象时为 null,收尾 message_delta 据此不写 usage 键。 */
781
+ usage = null;
1009
782
  /**
1010
783
  * 解析一条 tool_call delta 归属的块键。
1011
784
  *
@@ -1030,15 +803,18 @@ var init_openai = __esm({
1030
803
  return 0;
1031
804
  }
1032
805
  /**
1033
- * 关闭仍打开的 text / thinking / tool 块,并收口 message。
806
+ * 关闭仍打开的 text / thinking / tool 块(整条流只关一次)。
1034
807
  *
1035
808
  * 由 `finish_reason` 分支与 `[DONE]` 分支共用:上游断流或只发 `[DONE]` 而不发
1036
809
  * `finish_reason` 时,此前一个 `content_block_stop` 都不会发,下游拿到的是一个
1037
810
  * 永不闭合的 tool_use 块。
811
+ *
812
+ * [W-QUAD-CHAIN-20260914] 此前关块与 message_delta/message_stop 是同一个方法里同一时刻
813
+ * 的事;usage 尾帧要求收尾推迟,于是拆成本方法与 {@link emitMessageEnd} 两段,各自防重。
1038
814
  */
1039
- closeOpenBlocks(events, stopReason) {
1040
- if (this.messageClosed) return;
1041
- this.messageClosed = true;
815
+ closeContentBlocks(events) {
816
+ if (this.blocksClosed) return;
817
+ this.blocksClosed = true;
1042
818
  if (this.textStarted) {
1043
819
  events.push({
1044
820
  event: "content_block_stop",
@@ -1058,10 +834,23 @@ var init_openai = __esm({
1058
834
  data: JSON.stringify({ type: "content_block_stop", index: idx })
1059
835
  });
1060
836
  }
1061
- events.push({
1062
- event: "message_delta",
1063
- data: JSON.stringify({ type: "message_delta", delta: { stop_reason: stopReason } })
1064
- });
837
+ }
838
+ /**
839
+ * 发出 message_delta + message_stop,整条流只发一次;同时清掉推迟中的收尾。
840
+ *
841
+ * 见过 usage 对象就把它放进 message_delta:usage 必须出现在唯一的 message_stop 之前,
842
+ * message_stop 之后下游已无处安放用量。整条流从未出现 usage 对象时不写 usage 键 ——
843
+ * 缺席表示「上游没给」,不是 0。
844
+ */
845
+ emitMessageEnd(events, stopReason) {
846
+ if (this.messageClosed) return;
847
+ this.messageClosed = true;
848
+ this.pendingStopReason = null;
849
+ const messageDelta = { type: "message_delta", delta: { stop_reason: stopReason } };
850
+ if (this.usage !== null) {
851
+ messageDelta.usage = this.usage;
852
+ }
853
+ events.push({ event: "message_delta", data: JSON.stringify(messageDelta) });
1065
854
  events.push({
1066
855
  event: "message_stop",
1067
856
  data: JSON.stringify({ type: "message_stop" })
@@ -1074,8 +863,11 @@ var init_openai = __esm({
1074
863
  convert(data) {
1075
864
  if (data === "[DONE]") {
1076
865
  const events2 = [];
1077
- if (this.messageStarted) {
1078
- this.closeOpenBlocks(events2, "end_turn");
866
+ if (this.pendingStopReason !== null) {
867
+ this.emitMessageEnd(events2, this.pendingStopReason);
868
+ } else if (this.messageStarted) {
869
+ this.closeContentBlocks(events2);
870
+ this.emitMessageEnd(events2, "end_turn");
1079
871
  }
1080
872
  return { events: events2, done: true };
1081
873
  }
@@ -1086,10 +878,18 @@ var init_openai = __esm({
1086
878
  throw new Error(`parse openai stream chunk: ${e instanceof Error ? e.message : String(e)}`);
1087
879
  }
1088
880
  const events = [];
1089
- if (chunk.choices.length === 0) {
881
+ const frameUsage = readStreamUsage(chunk);
882
+ if (frameUsage !== null) {
883
+ this.usage = frameUsage;
884
+ }
885
+ if (!Array.isArray(chunk.choices) || chunk.choices.length === 0) {
886
+ if (frameUsage !== null && this.pendingStopReason !== null) {
887
+ this.emitMessageEnd(events, this.pendingStopReason);
888
+ }
1090
889
  return { events, done: false };
1091
890
  }
1092
891
  const choice = chunk.choices[0];
892
+ const delta = choice.delta ?? {};
1093
893
  if (!this.messageStarted) {
1094
894
  this.messageStarted = true;
1095
895
  const msgJSON = JSON.stringify({
@@ -1104,8 +904,17 @@ var init_openai = __esm({
1104
904
  });
1105
905
  events.push({ event: "message_start", data: msgJSON });
1106
906
  }
1107
- if (choice.delta.reasoning_content && choice.delta.reasoning_content !== "") {
907
+ if (delta.reasoning_content && delta.reasoning_content !== "") {
1108
908
  if (!this.thinkingStarted) {
909
+ if (this.textStarted) {
910
+ const stopJSON = JSON.stringify({
911
+ type: "content_block_stop",
912
+ index: this.blockIndex
913
+ });
914
+ events.push({ event: "content_block_stop", data: stopJSON });
915
+ this.blockIndex++;
916
+ this.textStarted = false;
917
+ }
1109
918
  this.thinkingStarted = true;
1110
919
  this.thinkingBlockIndex = this.blockIndex;
1111
920
  const blockJSON = JSON.stringify({
@@ -1118,11 +927,11 @@ var init_openai = __esm({
1118
927
  const deltaJSON = JSON.stringify({
1119
928
  type: "content_block_delta",
1120
929
  index: this.thinkingBlockIndex,
1121
- delta: { type: "thinking_delta", thinking: choice.delta.reasoning_content }
930
+ delta: { type: "thinking_delta", thinking: delta.reasoning_content }
1122
931
  });
1123
932
  events.push({ event: "content_block_delta", data: deltaJSON });
1124
933
  }
1125
- if (choice.delta.content && choice.delta.content !== "") {
934
+ if (delta.content && delta.content !== "") {
1126
935
  if (this.thinkingStarted && !this.thinkingStopped) {
1127
936
  this.thinkingStopped = true;
1128
937
  const stopJSON = JSON.stringify({
@@ -1144,11 +953,11 @@ var init_openai = __esm({
1144
953
  const deltaJSON = JSON.stringify({
1145
954
  type: "content_block_delta",
1146
955
  index: this.blockIndex,
1147
- delta: { type: "text_delta", text: choice.delta.content }
956
+ delta: { type: "text_delta", text: delta.content }
1148
957
  });
1149
958
  events.push({ event: "content_block_delta", data: deltaJSON });
1150
959
  }
1151
- for (const tc of choice.delta.tool_calls ?? []) {
960
+ for (const tc of delta.tool_calls ?? []) {
1152
961
  const fn = tc.function;
1153
962
  const toolKey = this.resolveToolKey(tc);
1154
963
  if (!this.toolBlockIndex.has(toolKey)) {
@@ -1223,67 +1032,39 @@ var init_openai = __esm({
1223
1032
  default:
1224
1033
  stopReason = choice.finish_reason;
1225
1034
  }
1226
- this.closeOpenBlocks(events, stopReason);
1035
+ if (!this.messageClosed && this.pendingStopReason === null) {
1036
+ this.closeContentBlocks(events);
1037
+ if (this.usage !== null) {
1038
+ this.emitMessageEnd(events, stopReason);
1039
+ } else {
1040
+ this.pendingStopReason = stopReason;
1041
+ }
1042
+ }
1043
+ }
1044
+ if (frameUsage !== null && this.pendingStopReason !== null) {
1045
+ this.emitMessageEnd(events, this.pendingStopReason);
1227
1046
  }
1228
1047
  return { events, done: false };
1229
1048
  }
1049
+ /**
1050
+ * 流在**没有** `[DONE]` 的情况下结束 (EOF) 时, 由驱动方在读循环结束后调用一次。
1051
+ *
1052
+ * 只补发「finish_reason 已到、仅因等待 usage 尾帧而推迟」的 message_delta + message_stop。
1053
+ * 从未收到 finish_reason 的流是被截断的流, 这里刻意**不**替它伪造正常结束 —— 一个
1054
+ * `end_turn` 的 message_stop 会把被截断的回答当成完整回答交给下游。
1055
+ * 已经收口 (usage 帧 / `[DONE]` / 上一次 flush) 后再调用返回空数组, 不会发出第二个 message_stop。
1056
+ */
1057
+ flush() {
1058
+ const events = [];
1059
+ if (this.pendingStopReason !== null) {
1060
+ this.emitMessageEnd(events, this.pendingStopReason);
1061
+ }
1062
+ return events;
1063
+ }
1230
1064
  };
1231
1065
  }
1232
1066
  });
1233
1067
 
1234
- // src/models/adapters/index.ts
1235
- function getAdapter(provider) {
1236
- const a = adapterRegistry[provider.toLowerCase()];
1237
- if (a) return a;
1238
- return defaultOpenAIAdapter;
1239
- }
1240
- function getAdapterForModel(m) {
1241
- let hasAnthropic = false;
1242
- let hasOpenAI = false;
1243
- for (const f of m.supported_formats ?? []) {
1244
- switch (f.trim().toLowerCase()) {
1245
- case "anthropic":
1246
- hasAnthropic = true;
1247
- break;
1248
- case "openai":
1249
- hasOpenAI = true;
1250
- break;
1251
- }
1252
- }
1253
- const declared = hasAnthropic || hasOpenAI;
1254
- const pref = (m.preferred_format ?? "").trim().toLowerCase();
1255
- switch (pref) {
1256
- case "anthropic":
1257
- if (!declared || hasAnthropic) return new exports.AnthropicAdapter();
1258
- break;
1259
- case "openai":
1260
- if (!declared || hasOpenAI) return new exports.OpenAIAdapter();
1261
- break;
1262
- }
1263
- if (hasAnthropic) return new exports.AnthropicAdapter();
1264
- if (hasOpenAI) return new exports.OpenAIAdapter();
1265
- return getAdapter((m.provider ?? "").toLowerCase());
1266
- }
1267
- exports.ProviderFormat = void 0; var adapterRegistry, defaultOpenAIAdapter;
1268
- var init_adapters = __esm({
1269
- "src/models/adapters/index.ts"() {
1270
- init_anthropic();
1271
- init_openai();
1272
- init_openai();
1273
- exports.ProviderFormat = /* @__PURE__ */ ((ProviderFormat2) => {
1274
- ProviderFormat2[ProviderFormat2["Anthropic"] = 0] = "Anthropic";
1275
- ProviderFormat2[ProviderFormat2["OpenAI"] = 1] = "OpenAI";
1276
- return ProviderFormat2;
1277
- })(exports.ProviderFormat || {});
1278
- adapterRegistry = {
1279
- anthropic: new exports.AnthropicAdapter(),
1280
- /** Acosmi 自有模型走 Anthropic 格式 */
1281
- acosmi: new exports.AnthropicAdapter()
1282
- };
1283
- defaultOpenAIAdapter = new exports.OpenAIAdapter();
1284
- }
1285
- });
1286
-
1287
1068
  // src/core/credentials.ts
1288
1069
  var uuid = () => globalThis.crypto.randomUUID();
1289
1070
  var pause = () => new Promise((resolve) => setTimeout(resolve, 25));
@@ -2674,8 +2455,308 @@ function computeBackoff(p, attempt, err) {
2674
2455
  return d;
2675
2456
  }
2676
2457
 
2677
- // src/core/client.ts
2678
- init_adapters();
2458
+ // src/models/adapters/anthropic.ts
2459
+ init_types();
2460
+ init_errors();
2461
+
2462
+ // src/models/betas.ts
2463
+ init_types();
2464
+ var betaInterleavedThinking = "interleaved-thinking-2025-05-14";
2465
+ var betaContext1M = "context-1m-2025-08-07";
2466
+ var betaContextManagement = "context-management-2025-06-27";
2467
+ var betaStructuredOutputs = "structured-outputs-2025-11-13";
2468
+ var betaAdvancedToolUse = "advanced-tool-use-2025-11-20";
2469
+ var betaEffort = "effort-2025-11-24";
2470
+ var betaPromptCachingScope = "prompt-caching-scope-2026-01-05";
2471
+ var betaFastMode = "fast-mode-2026-02-01";
2472
+ var betaRedactThinking = "redact-thinking-2026-02-12";
2473
+ var betaTokenEfficientTools = "token-efficient-tools-2025-02-19";
2474
+ function buildBetas(caps, req) {
2475
+ const betas = [];
2476
+ if (caps.supports_isp) {
2477
+ betas.push(betaInterleavedThinking);
2478
+ betas.push(betaContextManagement);
2479
+ }
2480
+ if (caps.supports_redact_thinking && req.thinking && req.thinking.display === "summary") {
2481
+ betas.push(betaRedactThinking);
2482
+ }
2483
+ if (caps.supports_1m_context) {
2484
+ betas.push(betaContext1M);
2485
+ }
2486
+ const hasStructuredOutput = caps.supports_structured_output && req.outputConfig != null;
2487
+ if (hasStructuredOutput) {
2488
+ betas.push(betaStructuredOutputs);
2489
+ } else if (caps.supports_token_efficient) {
2490
+ betas.push(betaTokenEfficientTools);
2491
+ }
2492
+ if (caps.supports_tool_search) {
2493
+ betas.push(betaAdvancedToolUse);
2494
+ }
2495
+ const needsEffort = req.effort != null || req.thinking != null && req.thinking.level && req.thinking.level !== exports.ThinkingOff;
2496
+ if (caps.supports_effort && needsEffort) {
2497
+ betas.push(betaEffort);
2498
+ }
2499
+ if (caps.supports_fast_mode && req.speed === "fast") {
2500
+ betas.push(betaFastMode);
2501
+ }
2502
+ if (caps.supports_prompt_cache) {
2503
+ betas.push(betaPromptCachingScope);
2504
+ }
2505
+ return uniqueMerge(betas, req.betas ?? []);
2506
+ }
2507
+ function uniqueMerge(base, extra) {
2508
+ if (extra.length === 0) return base;
2509
+ const seen = new Set(base);
2510
+ for (const s of extra) {
2511
+ if (!seen.has(s)) {
2512
+ base.push(s);
2513
+ seen.add(s);
2514
+ }
2515
+ }
2516
+ return base;
2517
+ }
2518
+
2519
+ // src/models/adapters/anthropic.ts
2520
+ init_format();
2521
+ var ANTHROPIC_SDK_MANAGED_BODY_KEYS = /* @__PURE__ */ new Set([
2522
+ "thinking",
2523
+ "effort",
2524
+ "max_tokens",
2525
+ "temperature",
2526
+ "betas"
2527
+ ]);
2528
+ var AnthropicAdapter = class {
2529
+ format() {
2530
+ return 0 /* Anthropic */;
2531
+ }
2532
+ endpointSuffix() {
2533
+ return "/anthropic";
2534
+ }
2535
+ /**
2536
+ * 构建 Anthropic 格式请求体
2537
+ * 逻辑等同于原 buildChatRequest, 包含完整的 betas/tools/serverTools/extraBody 处理
2538
+ */
2539
+ buildRequestBody(caps, req) {
2540
+ const body = {};
2541
+ if (req.rawMessages != null) {
2542
+ body["messages"] = req.rawMessages;
2543
+ } else if ((req.messages?.length ?? 0) > 0) {
2544
+ body["messages"] = req.messages;
2545
+ }
2546
+ body["stream"] = req.stream === true;
2547
+ if (req.max_tokens && req.max_tokens > 0) {
2548
+ body["max_tokens"] = req.max_tokens;
2549
+ }
2550
+ if (req.system != null) {
2551
+ body["system"] = req.system;
2552
+ }
2553
+ if (req.temperature != null) {
2554
+ body["temperature"] = req.temperature;
2555
+ }
2556
+ if (req.thinking && req.thinking.level && req.thinking.level !== "") {
2557
+ resolveThinkingLevel(body, req, caps);
2558
+ } else {
2559
+ if (req.thinking) {
2560
+ body["thinking"] = req.thinking;
2561
+ }
2562
+ if (req.effort) {
2563
+ body["effort"] = req.effort;
2564
+ }
2565
+ }
2566
+ if (req.metadata || req.endUserId && req.endUserId !== "") {
2567
+ const meta = {};
2568
+ if (req.metadata) {
2569
+ for (const [k, v] of Object.entries(req.metadata)) {
2570
+ meta[k] = v;
2571
+ }
2572
+ }
2573
+ if (req.endUserId && req.endUserId !== "" && meta["user_id"] === void 0) {
2574
+ meta["user_id"] = req.endUserId;
2575
+ }
2576
+ body["metadata"] = meta;
2577
+ }
2578
+ const allTools = [];
2579
+ if (req.tools != null) {
2580
+ try {
2581
+ const parsed = JSON.parse(JSON.stringify(req.tools));
2582
+ if (Array.isArray(parsed)) {
2583
+ for (const t of parsed) allTools.push(t);
2584
+ }
2585
+ } catch {
2586
+ }
2587
+ }
2588
+ for (const st of req.serverTools ?? []) {
2589
+ const schema = {
2590
+ type: st.type,
2591
+ name: st.name
2592
+ };
2593
+ if (st.config) {
2594
+ for (const [k, v] of Object.entries(st.config)) {
2595
+ schema[k] = v;
2596
+ }
2597
+ }
2598
+ allTools.push(schema);
2599
+ }
2600
+ if (allTools.length > 0) {
2601
+ body["tools"] = allTools;
2602
+ }
2603
+ if (req.speed && req.speed !== "") {
2604
+ body["speed"] = req.speed;
2605
+ }
2606
+ if (req.outputConfig) {
2607
+ body["output_config"] = req.outputConfig;
2608
+ }
2609
+ const betas = buildBetas(caps, req);
2610
+ if (betas.length > 0) {
2611
+ body["betas"] = betas;
2612
+ }
2613
+ if (req.extraBody) {
2614
+ for (const [k, v] of Object.entries(req.extraBody)) {
2615
+ if (ANTHROPIC_SDK_MANAGED_BODY_KEYS.has(k)) {
2616
+ console.warn(
2617
+ `acosmi-sdk: extraBody key "${k}" is SDK-managed and was ignored (use the dedicated request field instead).`
2618
+ );
2619
+ continue;
2620
+ }
2621
+ body[k] = v;
2622
+ }
2623
+ }
2624
+ return body;
2625
+ }
2626
+ /**
2627
+ * 解析 Anthropic 格式同步响应
2628
+ * 兼容 APIResponse 包装 {"code":0,"data":{...}} 和裸 Anthropic JSON 两种格式
2629
+ */
2630
+ parseResponse(bodyInput) {
2631
+ const bodyStr = typeof bodyInput === "string" ? bodyInput : new TextDecoder().decode(bodyInput);
2632
+ let raw = bodyStr;
2633
+ try {
2634
+ const wrapper = JSON.parse(bodyStr);
2635
+ if (wrapper.data != null && wrapper.data !== null) {
2636
+ if ((wrapper.code ?? 0) !== 0) {
2637
+ throw new exports.BusinessError(wrapper.code ?? 0, wrapper.message ?? "");
2638
+ }
2639
+ raw = JSON.stringify(wrapper.data);
2640
+ }
2641
+ } catch (e) {
2642
+ if (e instanceof exports.BusinessError) throw e;
2643
+ }
2644
+ let resp;
2645
+ try {
2646
+ resp = JSON.parse(raw);
2647
+ } catch (e) {
2648
+ throw new Error(
2649
+ `decode anthropic response: ${e instanceof Error ? e.message : String(e)}`
2650
+ );
2651
+ }
2652
+ resp.tokenRemaining = -1;
2653
+ resp.callRemaining = -1;
2654
+ resp.modelTokenRemaining = -1;
2655
+ resp.modelTokenRemainingETU = -1;
2656
+ return resp;
2657
+ }
2658
+ /**
2659
+ * 解析 Anthropic SSE 行
2660
+ *
2661
+ * Anthropic 原生协议无 [DONE] (message_stop 后上游关闭连接),
2662
+ * 但 Nexus Gateway 的 ChatStream 路径会追加 [DONE] 哨兵, 此处一并处理。
2663
+ */
2664
+ parseStreamLine(eventType, data) {
2665
+ if (data === "[DONE]") {
2666
+ return { event: { event: "", data: "" }, done: true };
2667
+ }
2668
+ return { event: { event: eventType, data }, done: false };
2669
+ }
2670
+ };
2671
+ function resolveThinkingLevel(body, req, caps) {
2672
+ const level = req.thinking?.level ?? "";
2673
+ if (level === exports.ThinkingOff) {
2674
+ body["thinking"] = { type: "disabled" };
2675
+ return;
2676
+ }
2677
+ if (!caps.supports_adaptive_thinking && !caps.supports_thinking) {
2678
+ return;
2679
+ }
2680
+ let maxTokens = req.max_tokens ?? 0;
2681
+ if (maxTokens <= 0) {
2682
+ maxTokens = exports.ThinkingHighMinMaxTokens;
2683
+ }
2684
+ if (level === exports.ThinkingMax) {
2685
+ let modelMax = caps.max_output_tokens;
2686
+ if (modelMax <= 0) modelMax = exports.ThinkingMaxFallbackMaxTokens;
2687
+ if (maxTokens < modelMax) maxTokens = modelMax;
2688
+ } else {
2689
+ if (maxTokens < exports.ThinkingHighMinMaxTokens) maxTokens = exports.ThinkingHighMinMaxTokens;
2690
+ }
2691
+ body["max_tokens"] = maxTokens;
2692
+ if (caps.supports_adaptive_thinking) {
2693
+ const thinking = { type: "adaptive" };
2694
+ if (req.thinking?.display) thinking["display"] = req.thinking.display;
2695
+ body["thinking"] = thinking;
2696
+ } else if (caps.supports_thinking) {
2697
+ let budget = maxTokens - 1;
2698
+ if (budget < 1024) budget = 1024;
2699
+ const thinking = {
2700
+ type: "enabled",
2701
+ budget_tokens: budget
2702
+ };
2703
+ if (req.thinking?.display) thinking["display"] = req.thinking.display;
2704
+ body["thinking"] = thinking;
2705
+ }
2706
+ if (caps.supports_effort) {
2707
+ let effortLevel = "high";
2708
+ if (level === "low") effortLevel = "low";
2709
+ if (level === "medium" || level === "xhigh") effortLevel = level;
2710
+ if (level === exports.ThinkingMax && caps.supports_max_effort) {
2711
+ effortLevel = "max";
2712
+ }
2713
+ body["effort"] = { level: effortLevel };
2714
+ }
2715
+ delete body["temperature"];
2716
+ }
2717
+
2718
+ // src/models/adapters/index.ts
2719
+ init_openai();
2720
+ init_format();
2721
+ init_openai();
2722
+ var adapterRegistry = {
2723
+ anthropic: new AnthropicAdapter(),
2724
+ /** Acosmi 自有模型走 Anthropic 格式 */
2725
+ acosmi: new AnthropicAdapter()
2726
+ };
2727
+ var defaultOpenAIAdapter = new exports.OpenAIAdapter();
2728
+ function getAdapter(provider) {
2729
+ const a = adapterRegistry[provider.toLowerCase()];
2730
+ if (a) return a;
2731
+ return defaultOpenAIAdapter;
2732
+ }
2733
+ function getAdapterForModel(m) {
2734
+ let hasAnthropic = false;
2735
+ let hasOpenAI = false;
2736
+ for (const f of m.supported_formats ?? []) {
2737
+ switch (f.trim().toLowerCase()) {
2738
+ case "anthropic":
2739
+ hasAnthropic = true;
2740
+ break;
2741
+ case "openai":
2742
+ hasOpenAI = true;
2743
+ break;
2744
+ }
2745
+ }
2746
+ const declared = hasAnthropic || hasOpenAI;
2747
+ const pref = (m.preferred_format ?? "").trim().toLowerCase();
2748
+ switch (pref) {
2749
+ case "anthropic":
2750
+ if (!declared || hasAnthropic) return new AnthropicAdapter();
2751
+ break;
2752
+ case "openai":
2753
+ if (!declared || hasOpenAI) return new exports.OpenAIAdapter();
2754
+ break;
2755
+ }
2756
+ if (hasAnthropic) return new AnthropicAdapter();
2757
+ if (hasOpenAI) return new exports.OpenAIAdapter();
2758
+ return getAdapter((m.provider ?? "").toLowerCase());
2759
+ }
2679
2760
 
2680
2761
  // src/models/stream-meta.ts
2681
2762
  function extractAnthropicBlockMeta(eventType, data, blockTypeMap) {
@@ -4357,18 +4438,23 @@ var Client = class _Client {
4357
4438
  }
4358
4439
  if (adapter.format() === 1 /* OpenAI */) {
4359
4440
  const converter = newOpenAIStreamConverter();
4441
+ let currentEvent = "";
4360
4442
  for await (const line of iterSSELines(resp.body)) {
4361
4443
  notifyUpstreamActivity(onUpstreamActivity);
4362
4444
  if (isSSECommentLine(line)) continue;
4363
4445
  if (line.startsWith("event:")) {
4364
- line.slice("event:".length).trim();
4446
+ currentEvent = line.slice("event:".length).trim();
4365
4447
  } else if (line.startsWith("data:")) {
4366
4448
  const data = line.slice("data:".length).trim();
4449
+ if (currentEvent === "failed" || currentEvent === "error") {
4450
+ throw parseStreamError(data);
4451
+ }
4367
4452
  const { events, done } = converter.convert(data);
4368
4453
  for (const ev of events) yield ev;
4369
4454
  if (done) return;
4370
4455
  }
4371
4456
  }
4457
+ for (const ev of converter.flush()) yield ev;
4372
4458
  } else {
4373
4459
  const blockTypeMap = /* @__PURE__ */ new Map();
4374
4460
  let currentEvent = "";
@@ -5286,10 +5372,6 @@ function findDesktopVisualUnderstandingModel(models) {
5286
5372
  return candidates[0];
5287
5373
  }
5288
5374
 
5289
- // src/models/index.ts
5290
- init_adapters();
5291
- init_betas();
5292
-
5293
5375
  // src/billing/entitlements.ts
5294
5376
  Client.prototype.getBalance = async function(signal) {
5295
5377
  const resp = await this.doJSON(
@@ -8889,6 +8971,7 @@ exports.ALL_PLATFORMS = ALL_PLATFORMS;
8889
8971
  exports.ALL_REGIONS = ALL_REGIONS;
8890
8972
  exports.AgentRunStreamError = AgentRunStreamError;
8891
8973
  exports.AgentRunsClient = AgentRunsClient;
8974
+ exports.AnthropicAdapter = AnthropicAdapter;
8892
8975
  exports.AudienceEnum = AudienceEnum;
8893
8976
  exports.BillingModeEnum = BillingModeEnum;
8894
8977
  exports.CHAT_REQUEST_TIMEOUT_MS = CHAT_REQUEST_TIMEOUT_MS;