llm-simple-router 0.8.2 → 0.9.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.
Files changed (222) hide show
  1. package/README.en.md +319 -0
  2. package/README.md +2 -0
  3. package/config/recommended-providers.json +33 -9
  4. package/config/recommended-retry-rules.json +9 -8
  5. package/dist/admin/providers.js +11 -9
  6. package/dist/admin/quick-setup.d.ts +13 -0
  7. package/dist/admin/quick-setup.js +169 -0
  8. package/dist/admin/recommended.js +5 -1
  9. package/dist/admin/routes.js +2 -0
  10. package/dist/config/model-context.d.ts +8 -2
  11. package/dist/config/model-context.js +17 -5
  12. package/dist/config/recommended.d.ts +2 -1
  13. package/dist/config/recommended.js +5 -9
  14. package/dist/core/constants.js +2 -0
  15. package/dist/db/index.js +5 -0
  16. package/dist/db/migrations/033_add_adaptive_concurrency.sql +3 -0
  17. package/dist/db/migrations/036_add_openai_responses_api_type.sql +68 -0
  18. package/dist/db/migrations/037_fix_035_data_corruption.sql +54 -0
  19. package/dist/db/providers.d.ts +3 -3
  20. package/dist/index.js +7 -3
  21. package/dist/metrics/metrics-extractor.d.ts +3 -2
  22. package/dist/metrics/metrics-extractor.js +45 -0
  23. package/dist/metrics/sse-metrics-transform.d.ts +1 -1
  24. package/dist/metrics/sse-metrics-transform.js +10 -0
  25. package/dist/monitor/request-tracker.d.ts +1 -1
  26. package/dist/monitor/stream-content-accumulator.d.ts +1 -1
  27. package/dist/monitor/stream-extractor.d.ts +1 -1
  28. package/dist/monitor/stream-extractor.js +21 -0
  29. package/dist/monitor/types.d.ts +1 -1
  30. package/dist/proxy/handler/proxy-handler-utils.d.ts +1 -1
  31. package/dist/proxy/handler/proxy-handler.d.ts +1 -1
  32. package/dist/proxy/handler/proxy-handler.js +8 -2
  33. package/dist/proxy/handler/responses.d.ts +7 -0
  34. package/dist/proxy/handler/responses.js +48 -0
  35. package/dist/proxy/loop-prevention/tool-loop-guard.d.ts +1 -1
  36. package/dist/proxy/loop-prevention/tool-loop-guard.js +10 -0
  37. package/dist/proxy/orchestration/orchestrator.d.ts +1 -1
  38. package/dist/proxy/orchestration/semaphore.js +6 -0
  39. package/dist/proxy/patch/deepseek/index.d.ts +1 -1
  40. package/dist/proxy/patch/deepseek/patch-thinking-param.d.ts +1 -1
  41. package/dist/proxy/patch/index.d.ts +3 -0
  42. package/dist/proxy/patch/index.js +28 -0
  43. package/dist/proxy/patch/tool-round-limiter.d.ts +1 -1
  44. package/dist/proxy/patch/tool-round-limiter.js +16 -0
  45. package/dist/proxy/proxy-core.d.ts +1 -1
  46. package/dist/proxy/proxy-logging.d.ts +3 -3
  47. package/dist/proxy/response-transform.js +13 -0
  48. package/dist/proxy/transform/id-utils.d.ts +1 -0
  49. package/dist/proxy/transform/id-utils.js +3 -0
  50. package/dist/proxy/transform/plugin-types.d.ts +5 -5
  51. package/dist/proxy/transform/request-bridge-responses.d.ts +19 -0
  52. package/dist/proxy/transform/request-bridge-responses.js +311 -0
  53. package/dist/proxy/transform/request-transform-responses.d.ts +2 -0
  54. package/dist/proxy/transform/request-transform-responses.js +350 -0
  55. package/dist/proxy/transform/response-bridge-responses.d.ts +23 -0
  56. package/dist/proxy/transform/response-bridge-responses.js +173 -0
  57. package/dist/proxy/transform/response-transform-responses.d.ts +2 -0
  58. package/dist/proxy/transform/response-transform-responses.js +137 -0
  59. package/dist/proxy/transform/stream-ant2resp.d.ts +26 -0
  60. package/dist/proxy/transform/stream-ant2resp.js +322 -0
  61. package/dist/proxy/transform/stream-bridge-chat2resp.d.ts +40 -0
  62. package/dist/proxy/transform/stream-bridge-chat2resp.js +382 -0
  63. package/dist/proxy/transform/stream-bridge-resp2chat.d.ts +24 -0
  64. package/dist/proxy/transform/stream-bridge-resp2chat.js +237 -0
  65. package/dist/proxy/transform/stream-resp2ant.d.ts +21 -0
  66. package/dist/proxy/transform/stream-resp2ant.js +238 -0
  67. package/dist/proxy/transform/stream-transform-base.d.ts +1 -0
  68. package/dist/proxy/transform/stream-transform-base.js +3 -0
  69. package/dist/proxy/transform/transform-coordinator.d.ts +1 -0
  70. package/dist/proxy/transform/transform-coordinator.js +127 -8
  71. package/dist/proxy/transform/types-responses.d.ts +177 -0
  72. package/dist/proxy/transform/types-responses.js +27 -0
  73. package/dist/proxy/transform/types.d.ts +3 -1
  74. package/dist/proxy/transport/transport-fn.d.ts +1 -1
  75. package/frontend-dist/assets/CardContent-BhMXx-JD.js +1 -0
  76. package/frontend-dist/assets/CardTitle-DQDjTee3.js +1 -0
  77. package/frontend-dist/assets/CascadingModelSelect-JBQq3JJt.js +1 -0
  78. package/frontend-dist/assets/Checkbox-ByxbKP_C.js +1 -0
  79. package/frontend-dist/assets/CollapsibleContent-GecW2Jk_.js +1 -0
  80. package/frontend-dist/assets/CollapsibleTrigger-Cib3-OsK.js +1 -0
  81. package/frontend-dist/assets/Collection-Dbvdpa0m.js +1 -0
  82. package/frontend-dist/assets/Dashboard-3MJPLflT.js +3 -0
  83. package/frontend-dist/assets/DialogTitle-Ej_rtfV1.js +1 -0
  84. package/frontend-dist/assets/{Input-CAnKUBBK.js → Input-tcnrMp1v.js} +1 -1
  85. package/frontend-dist/assets/Label-BwzPFyL-.js +1 -0
  86. package/frontend-dist/assets/Login-Cdsw2pWC.js +1 -0
  87. package/frontend-dist/assets/Logs-5_CWiws5.js +1 -0
  88. package/frontend-dist/assets/MappingList-D8HRph05.js +1 -0
  89. package/frontend-dist/assets/ModelCard-CZbQcYNn.js +1 -0
  90. package/frontend-dist/assets/ModelMappings-CJqgl7O8.js +1 -0
  91. package/frontend-dist/assets/Monitor-B8v5a8fB.js +1 -0
  92. package/frontend-dist/assets/PopoverTrigger-C88SpJNZ.js +1 -0
  93. package/frontend-dist/assets/PopperContent-6BXua_FZ.js +1 -0
  94. package/frontend-dist/assets/Providers-DH0nvlGn.js +1 -0
  95. package/frontend-dist/assets/ProxyEnhancement-CAH-44W-.js +5 -0
  96. package/frontend-dist/assets/QuickSetup-CsDO-ZGP.js +1 -0
  97. package/frontend-dist/assets/RetryRules-8iT9fLsH.js +1 -0
  98. package/frontend-dist/assets/RouterKeys-BFoEmWgj.js +1 -0
  99. package/frontend-dist/assets/RovingFocusItem-DdPUFQHC.js +1 -0
  100. package/frontend-dist/assets/Schedules-B8Se31u4.js +1 -0
  101. package/frontend-dist/assets/SelectValue-CT2z_-6j.js +1 -0
  102. package/frontend-dist/assets/Settings-BHvtsJKD.js +6 -0
  103. package/frontend-dist/assets/Setup-k-l9KDC0.js +1 -0
  104. package/frontend-dist/assets/Switch-D1NdA4ax.js +1 -0
  105. package/frontend-dist/assets/TableHeader-CcMyOsUB.js +1 -0
  106. package/frontend-dist/assets/Teleport-Bmeh33lB.js +3 -0
  107. package/frontend-dist/assets/TooltipTrigger-LegC_Uvp.js +1 -0
  108. package/frontend-dist/assets/UnifiedRequestDialog-BVw6W2pk.js +3 -0
  109. package/frontend-dist/assets/UnifiedRequestDialog-C4MTxb25.css +1 -0
  110. package/frontend-dist/assets/VisuallyHidden-ogESfc9X.js +1 -0
  111. package/frontend-dist/assets/VisuallyHiddenInput-BQemVGau.js +1 -0
  112. package/frontend-dist/assets/alert-dialog-DzKCAoYJ.js +1 -0
  113. package/frontend-dist/assets/badge-C-9zPTgw.js +1 -0
  114. package/frontend-dist/assets/button-D27ClX8J.js +14 -0
  115. package/frontend-dist/assets/check-yTAivq1h.js +1 -0
  116. package/frontend-dist/assets/common-CWCbKHOK.js +1 -0
  117. package/frontend-dist/assets/common-D4xnnaqi.js +1 -0
  118. package/frontend-dist/assets/constants-B-VELBjk.js +1 -0
  119. package/frontend-dist/assets/copy-DWG9cQPR.js +1 -0
  120. package/frontend-dist/assets/dashboard-B8eI-t8c.js +1 -0
  121. package/frontend-dist/assets/dashboard-Dbe6A2lu.js +1 -0
  122. package/frontend-dist/assets/dialog-BnYR6_dh.js +1 -0
  123. package/frontend-dist/assets/file-text-D33FJAPX.js +1 -0
  124. package/frontend-dist/assets/format-BhxQSgt6.js +1 -0
  125. package/frontend-dist/assets/i18n-CwUfS0tE.js +1 -0
  126. package/frontend-dist/assets/index-B348nt-T.css +1 -0
  127. package/frontend-dist/assets/index-DPRxBo3N.js +1 -0
  128. package/frontend-dist/assets/lib-D0Ek2pPZ.js +1 -0
  129. package/frontend-dist/assets/loader-circle-EpKC006I.js +1 -0
  130. package/frontend-dist/assets/login-BTolYxVI.js +1 -0
  131. package/frontend-dist/assets/login-w_ICpiU5.js +1 -0
  132. package/frontend-dist/assets/logs-7dT2uyMa.js +1 -0
  133. package/frontend-dist/assets/logs-_3w8tDQa.js +1 -0
  134. package/frontend-dist/assets/mappings-Bbn3r2uJ.js +1 -0
  135. package/frontend-dist/assets/mappings-CTZ-zb1x.js +1 -0
  136. package/frontend-dist/assets/monitor-DN5m5n_x.js +1 -0
  137. package/frontend-dist/assets/monitor-DysWEOtt.js +1 -0
  138. package/frontend-dist/assets/providers-C1gQGzwa.js +1 -0
  139. package/frontend-dist/assets/providers-CCfko___.js +1 -0
  140. package/frontend-dist/assets/proxyEnhancement-BItabyLo.js +1 -0
  141. package/frontend-dist/assets/proxyEnhancement-DeMb7wIE.js +1 -0
  142. package/frontend-dist/assets/quickSetup-C75HMC_z.js +1 -0
  143. package/frontend-dist/assets/quickSetup-DStZWiuf.js +1 -0
  144. package/frontend-dist/assets/requestDetail-BoaPEQs-.js +1 -0
  145. package/frontend-dist/assets/requestDetail-CM5kFgy6.js +1 -0
  146. package/frontend-dist/assets/retryRules-CIF37gOl.js +1 -0
  147. package/frontend-dist/assets/retryRules-o_D8S5gy.js +1 -0
  148. package/frontend-dist/assets/routerKeys-BAvjW0V8.js +1 -0
  149. package/frontend-dist/assets/routerKeys-mQt2YPuE.js +1 -0
  150. package/frontend-dist/assets/schedules-BCV2rxK-.js +1 -0
  151. package/frontend-dist/assets/schedules-Qte9b7b_.js +1 -0
  152. package/frontend-dist/assets/settings-Bgu2lJfy.js +1 -0
  153. package/frontend-dist/assets/settings-UCmMSq_F.js +1 -0
  154. package/frontend-dist/assets/setup-B_fAfMoV.js +1 -0
  155. package/frontend-dist/assets/setup-Chc246Zi.js +1 -0
  156. package/frontend-dist/assets/sidebar-B7rejnZA.js +1 -0
  157. package/frontend-dist/assets/sidebar-CBMItLst.js +1 -0
  158. package/frontend-dist/assets/sun-BylRZIWt.js +1 -0
  159. package/frontend-dist/assets/trash-2-QNFff7V4.js +1 -0
  160. package/frontend-dist/assets/{useClipboard-BmmsNSGV.js → useClipboard-BFt5f-_-.js} +1 -1
  161. package/frontend-dist/assets/{useFocusGuards-A-9V2Y-b.js → useFocusGuards-DQBZKWnu.js} +1 -1
  162. package/frontend-dist/assets/useFormControl-T2RQNBqs.js +1 -0
  163. package/frontend-dist/assets/useLogRetention-NrrZrpPE.js +1 -0
  164. package/frontend-dist/assets/useNonce-DR38uny5.js +1 -0
  165. package/frontend-dist/assets/useTheme-CpTI547G.js +1 -0
  166. package/frontend-dist/assets/x-DSgLgKC_.js +1 -0
  167. package/frontend-dist/index.html +25 -22
  168. package/package.json +1 -1
  169. package/dist/db/migrations/033_add_pipeline_snapshot.sql +0 -1
  170. package/frontend-dist/assets/CardContent-BVMQ2_pg.js +0 -1
  171. package/frontend-dist/assets/CardTitle-GLv7QyIY.js +0 -1
  172. package/frontend-dist/assets/CascadingModelSelect-CBhqKFDX.js +0 -1
  173. package/frontend-dist/assets/Checkbox-HPVDmEdV.js +0 -1
  174. package/frontend-dist/assets/CollapsibleTrigger-DhxD9tpM.js +0 -1
  175. package/frontend-dist/assets/Collection-BRt7YxN8.js +0 -1
  176. package/frontend-dist/assets/Dashboard-D1Ys8Zog.js +0 -3
  177. package/frontend-dist/assets/DialogTitle-23q73lwF.js +0 -1
  178. package/frontend-dist/assets/Label-DWdYtVMI.js +0 -1
  179. package/frontend-dist/assets/Login-w5WFOinP.js +0 -1
  180. package/frontend-dist/assets/Logs-C1F1ZmWF.js +0 -1
  181. package/frontend-dist/assets/ModelMappings-BzmecWEH.js +0 -1
  182. package/frontend-dist/assets/Monitor-DrAZFTKR.js +0 -1
  183. package/frontend-dist/assets/PopoverTrigger-Bj65uUbv.js +0 -1
  184. package/frontend-dist/assets/PopperContent-gzzf1XHe.js +0 -1
  185. package/frontend-dist/assets/Providers-DSgf4mb6.js +0 -1
  186. package/frontend-dist/assets/ProxyEnhancement-Bb1cCP6d.js +0 -5
  187. package/frontend-dist/assets/RetryRules-BwPfEZtm.js +0 -1
  188. package/frontend-dist/assets/RouterKeys-CzTSq1Mx.js +0 -1
  189. package/frontend-dist/assets/RovingFocusItem-CXM_Yfkm.js +0 -1
  190. package/frontend-dist/assets/Schedules-DVilCXrC.js +0 -1
  191. package/frontend-dist/assets/SelectValue-C0-LzGQY.js +0 -1
  192. package/frontend-dist/assets/Settings-Bpk53zVX.js +0 -6
  193. package/frontend-dist/assets/Setup-Dn7EgC49.js +0 -1
  194. package/frontend-dist/assets/Switch-BO8Ooae6.js +0 -1
  195. package/frontend-dist/assets/TableHeader-Bded9VTC.js +0 -1
  196. package/frontend-dist/assets/TabsTrigger-BzKMi9AF.js +0 -1
  197. package/frontend-dist/assets/Teleport-DizRK5O3.js +0 -3
  198. package/frontend-dist/assets/TooltipTrigger-EiIy2zn8.js +0 -1
  199. package/frontend-dist/assets/UnifiedRequestDialog-BABsTaGb.js +0 -3
  200. package/frontend-dist/assets/UnifiedRequestDialog-BjEigSaR.css +0 -1
  201. package/frontend-dist/assets/VisuallyHidden-5AozJQza.js +0 -1
  202. package/frontend-dist/assets/VisuallyHiddenInput-DdiZrV2i.js +0 -1
  203. package/frontend-dist/assets/alert-dialog-DlKUuTPe.js +0 -1
  204. package/frontend-dist/assets/arrow-down-CxWKmZ2I.js +0 -1
  205. package/frontend-dist/assets/badge-9KJEMa53.js +0 -1
  206. package/frontend-dist/assets/button-Ul8WlrM5.js +0 -12
  207. package/frontend-dist/assets/check-7ahK--N4.js +0 -1
  208. package/frontend-dist/assets/constants-D_0jiLjw.js +0 -1
  209. package/frontend-dist/assets/copy-DzU2pAMG.js +0 -1
  210. package/frontend-dist/assets/dialog-B9j-FMrd.js +0 -1
  211. package/frontend-dist/assets/file-text-Bj3ZIo-E.js +0 -1
  212. package/frontend-dist/assets/format-Dln15Luw.js +0 -1
  213. package/frontend-dist/assets/index-Bz_ZaXNn.css +0 -1
  214. package/frontend-dist/assets/index-MedWZMHB.js +0 -1
  215. package/frontend-dist/assets/lib-Hhs3NqfD.js +0 -1
  216. package/frontend-dist/assets/loader-circle-5TJUukEe.js +0 -1
  217. package/frontend-dist/assets/useFormControl-DEO19lRe.js +0 -1
  218. package/frontend-dist/assets/useLogRetention-BfnBFZ5K.js +0 -1
  219. package/frontend-dist/assets/useNonce-BfwUJ1Ci.js +0 -1
  220. package/frontend-dist/assets/x-Cfopt3QL.js +0 -1
  221. /package/dist/db/migrations/{034_drop_redundant_log_columns.sql → 035_drop_redundant_log_columns.sql} +0 -0
  222. /package/frontend-dist/assets/{ohash.D__AXeF1-D5e5Wyzx.js → ohash.D__AXeF1-CTo5WcIm.js} +0 -0
@@ -0,0 +1,21 @@
1
+ import { BaseSSETransform } from "./stream-transform-base.js";
2
+ export declare class ResponsesToAnthropicTransform extends BaseSSETransform {
3
+ private hasSentMessageStart;
4
+ private blockIndex;
5
+ private msgId;
6
+ private inputTokens;
7
+ private outputTokens;
8
+ private currentOutputIndex;
9
+ private hasSentMessageStop;
10
+ private hasFunctionCall;
11
+ private activeItems;
12
+ private ensureMessageStart;
13
+ protected processEvent(event: {
14
+ event?: string;
15
+ data?: string;
16
+ }): void;
17
+ private emitStopSequence;
18
+ private resolveStopReason;
19
+ protected flushPendingData(): void;
20
+ protected ensureTerminated(): void;
21
+ }
@@ -0,0 +1,238 @@
1
+ import { BaseSSETransform } from "./stream-transform-base.js";
2
+ import { generateMsgId } from "./id-utils.js";
3
+ export class ResponsesToAnthropicTransform extends BaseSSETransform {
4
+ hasSentMessageStart = false;
5
+ blockIndex = 0;
6
+ msgId = generateMsgId();
7
+ inputTokens = 0;
8
+ outputTokens = 0;
9
+ currentOutputIndex = -1;
10
+ hasSentMessageStop = false;
11
+ hasFunctionCall = false;
12
+ // Track current items by output_index
13
+ activeItems = new Map();
14
+ ensureMessageStart() {
15
+ if (this.hasSentMessageStart)
16
+ return;
17
+ this.hasSentMessageStart = true;
18
+ this.pushAnthropicSSE("message_start", {
19
+ type: "message_start",
20
+ message: {
21
+ id: this.msgId,
22
+ type: "message",
23
+ role: "assistant",
24
+ content: [],
25
+ model: this.model,
26
+ status: "in_progress",
27
+ usage: { input_tokens: this.inputTokens },
28
+ },
29
+ });
30
+ }
31
+ processEvent(event) {
32
+ const eventType = event.event;
33
+ let payload;
34
+ try {
35
+ payload = JSON.parse(event.data);
36
+ }
37
+ catch (err) {
38
+ this.emit("warning", err);
39
+ return;
40
+ }
41
+ switch (eventType) {
42
+ case "response.created":
43
+ case "response.in_progress":
44
+ case "response.queued": {
45
+ // Extract usage from response object if present
46
+ const resp = payload.response;
47
+ if (resp?.usage) {
48
+ const usage = resp.usage;
49
+ this.inputTokens = usage.input_tokens ?? this.inputTokens;
50
+ }
51
+ break;
52
+ }
53
+ case "response.output_item.added": {
54
+ this.ensureMessageStart();
55
+ const item = payload.item;
56
+ const outputIdx = payload.output_index ?? this.blockIndex;
57
+ this.currentOutputIndex = outputIdx;
58
+ const itemType = item?.type;
59
+ const tracked = {
60
+ type: itemType,
61
+ id: item?.id ?? "",
62
+ name: item?.name,
63
+ callId: item?.call_id ?? undefined,
64
+ outputIndex: outputIdx,
65
+ };
66
+ this.activeItems.set(outputIdx, tracked);
67
+ if (itemType === "message") {
68
+ // Don't start content block yet — wait for content_part.added
69
+ }
70
+ else if (itemType === "function_call") {
71
+ this.hasFunctionCall = true;
72
+ const rawCallId = tracked.callId ?? tracked.id;
73
+ // Anthropic requires "toolu_" prefix on tool_use IDs
74
+ const toolId = rawCallId.startsWith("toolu_") ? rawCallId : `toolu_${rawCallId}`;
75
+ const toolName = tracked.name ?? "";
76
+ this.pushAnthropicSSE("content_block_start", {
77
+ type: "content_block_start",
78
+ index: this.blockIndex,
79
+ content_block: { type: "tool_use", id: toolId, name: toolName, input: {} },
80
+ });
81
+ }
82
+ else if (itemType === "reasoning") {
83
+ this.pushAnthropicSSE("content_block_start", {
84
+ type: "content_block_start",
85
+ index: this.blockIndex,
86
+ content_block: { type: "thinking", thinking: "" },
87
+ });
88
+ }
89
+ break;
90
+ }
91
+ case "response.content_part.added": {
92
+ this.ensureMessageStart();
93
+ const part = payload.part;
94
+ const partType = part?.type;
95
+ if (partType === "output_text") {
96
+ this.pushAnthropicSSE("content_block_start", {
97
+ type: "content_block_start",
98
+ index: this.blockIndex,
99
+ content_block: { type: "text", text: "" },
100
+ });
101
+ }
102
+ break;
103
+ }
104
+ case "response.output_text.delta": {
105
+ const delta = payload.delta;
106
+ if (delta) {
107
+ this.pushAnthropicSSE("content_block_delta", {
108
+ type: "content_block_delta",
109
+ index: this.blockIndex,
110
+ delta: { type: "text_delta", text: delta },
111
+ });
112
+ }
113
+ break;
114
+ }
115
+ case "response.output_text.done":
116
+ case "response.content_part.done": {
117
+ // Nothing to emit — wait for output_item.done to send content_block_stop
118
+ break;
119
+ }
120
+ case "response.reasoning_summary_text.delta": {
121
+ const delta = payload.delta;
122
+ if (delta) {
123
+ this.pushAnthropicSSE("content_block_delta", {
124
+ type: "content_block_delta",
125
+ index: this.blockIndex,
126
+ delta: { type: "thinking_delta", thinking: delta },
127
+ });
128
+ }
129
+ break;
130
+ }
131
+ case "response.reasoning_summary_part.added":
132
+ case "response.reasoning_summary_text.done":
133
+ case "response.reasoning_summary_part.done": {
134
+ // Sub-events of reasoning — no Anthropic equivalent, handled at output_item.done
135
+ break;
136
+ }
137
+ case "response.function_call_arguments.delta": {
138
+ const delta = payload.delta;
139
+ if (delta) {
140
+ this.pushAnthropicSSE("content_block_delta", {
141
+ type: "content_block_delta",
142
+ index: this.blockIndex,
143
+ delta: { type: "input_json_delta", partial_json: delta },
144
+ });
145
+ }
146
+ break;
147
+ }
148
+ case "response.function_call_arguments.done": {
149
+ // Nothing — wait for output_item.done
150
+ break;
151
+ }
152
+ case "response.output_item.done": {
153
+ const outputIdx = payload.output_index ?? this.currentOutputIndex;
154
+ // content_block_stop — same for all item types (index-only)
155
+ this.pushAnthropicSSE("content_block_stop", {
156
+ type: "content_block_stop",
157
+ index: this.blockIndex,
158
+ });
159
+ this.activeItems.delete(outputIdx);
160
+ this.blockIndex++;
161
+ break;
162
+ }
163
+ case "response.completed": {
164
+ const resp = payload.response;
165
+ if (resp?.usage) {
166
+ const usage = resp.usage;
167
+ this.inputTokens = usage.input_tokens ?? this.inputTokens;
168
+ this.outputTokens = usage.output_tokens ?? this.outputTokens;
169
+ }
170
+ this.emitStopSequence(resp?.status);
171
+ break;
172
+ }
173
+ case "response.incomplete": {
174
+ const resp = payload.response;
175
+ if (resp?.usage) {
176
+ const usage = resp.usage;
177
+ this.outputTokens = usage.output_tokens ?? this.outputTokens;
178
+ }
179
+ this.emitStopSequence("incomplete");
180
+ break;
181
+ }
182
+ case "response.failed": {
183
+ const resp = payload.response;
184
+ const err = resp?.error;
185
+ this.pushAnthropicSSE("error", {
186
+ type: "error",
187
+ error: {
188
+ type: "api_error",
189
+ message: err?.message ?? "Upstream error",
190
+ },
191
+ });
192
+ break;
193
+ }
194
+ case "error": {
195
+ this.pushAnthropicSSE("error", {
196
+ type: "error",
197
+ error: {
198
+ type: "api_error",
199
+ message: payload.message ?? "Stream error",
200
+ },
201
+ });
202
+ break;
203
+ }
204
+ default: {
205
+ this.emit("warning", { event: "unknown_sse_event", eventType });
206
+ break;
207
+ }
208
+ }
209
+ }
210
+ emitStopSequence(status) {
211
+ if (this.hasSentMessageStop)
212
+ return;
213
+ const stopReason = this.resolveStopReason(status);
214
+ this.pushAnthropicSSE("message_delta", {
215
+ type: "message_delta",
216
+ delta: { stop_reason: stopReason, stop_sequence: null },
217
+ usage: { output_tokens: this.outputTokens },
218
+ });
219
+ this.pushAnthropicSSE("message_stop", { type: "message_stop" });
220
+ this.hasSentMessageStop = true;
221
+ }
222
+ resolveStopReason(status) {
223
+ if (status === "incomplete")
224
+ return "max_tokens";
225
+ if (this.hasFunctionCall)
226
+ return "tool_use";
227
+ return "end_turn";
228
+ }
229
+ flushPendingData() {
230
+ // No buffered data
231
+ }
232
+ ensureTerminated() {
233
+ if (!this.hasSentMessageStop) {
234
+ this.ensureMessageStart();
235
+ this.emitStopSequence("completed");
236
+ }
237
+ }
238
+ }
@@ -15,5 +15,6 @@ export declare abstract class BaseSSETransform extends Transform {
15
15
  protected abstract ensureTerminated(): void;
16
16
  protected pushAnthropicSSE(eventType: string, data: unknown): void;
17
17
  protected pushOpenAISSE(data: unknown): void;
18
+ protected pushResponsesSSE(eventType: string, data: unknown): void;
18
19
  protected pushDone(): void;
19
20
  }
@@ -54,6 +54,9 @@ export class BaseSSETransform extends Transform {
54
54
  pushOpenAISSE(data) {
55
55
  this.push(`data: ${JSON.stringify(data)}\n\n`);
56
56
  }
57
+ pushResponsesSSE(eventType, data) {
58
+ this.push(`event: ${eventType}\ndata: ${JSON.stringify(data)}\n\n`);
59
+ }
57
60
  pushDone() {
58
61
  this.push("data: [DONE]\n\n");
59
62
  this.done = true;
@@ -8,4 +8,5 @@ export declare class TransformCoordinator {
8
8
  transformResponse(bodyStr: string, sourceApiType: string, targetApiType: string): string;
9
9
  transformErrorResponse(bodyStr: string, sourceApiType: string, targetApiType: string): string;
10
10
  createFormatTransform(entryApiType: string, providerApiType: string, model: string): Transform | undefined;
11
+ private getUpstreamPath;
11
12
  }
@@ -1,32 +1,151 @@
1
1
  import { transformRequestBody } from "./request-transform.js";
2
- import { transformResponseBody, transformErrorResponse } from "./response-transform.js";
2
+ import { transformResponseBody, transformErrorResponse as transformErrorBody, } from "./response-transform.js";
3
3
  import { OpenAIToAnthropicTransform } from "./stream-oa2ant.js";
4
4
  import { AnthropicToOpenAITransform } from "./stream-ant2oa.js";
5
+ import { responsesToAnthropicRequest, anthropicToResponsesRequest, } from "./request-transform-responses.js";
6
+ import { responsesToAnthropicResponse, anthropicToResponsesResponse, } from "./response-transform-responses.js";
7
+ import { responsesToChatRequest, chatToResponsesRequest, } from "./request-bridge-responses.js";
8
+ import { responsesToChatResponse, chatToResponsesResponse, } from "./response-bridge-responses.js";
9
+ import { AnthropicToResponsesTransform } from "./stream-ant2resp.js";
10
+ import { ResponsesToAnthropicTransform } from "./stream-resp2ant.js";
11
+ import { ChatToResponsesBridgeTransform } from "./stream-bridge-chat2resp.js";
12
+ import { ResponsesToChatBridgeTransform } from "./stream-bridge-resp2chat.js";
5
13
  export class TransformCoordinator {
6
14
  needsTransform(entryApiType, providerApiType) {
7
15
  return entryApiType !== providerApiType;
8
16
  }
9
17
  transformRequest(body, entryApiType, providerApiType, model) {
10
- const upstreamPath = providerApiType === "openai" ? "/v1/chat/completions" : "/v1/messages";
11
- return { body: transformRequestBody(body, entryApiType, providerApiType, model), upstreamPath };
18
+ if (entryApiType === providerApiType) {
19
+ return { body, upstreamPath: this.getUpstreamPath(providerApiType) };
20
+ }
21
+ // Tier-1: Responses ↔ Anthropic
22
+ if (entryApiType === "openai-responses" && providerApiType === "anthropic") {
23
+ return { body: responsesToAnthropicRequest(body), upstreamPath: "/v1/messages" };
24
+ }
25
+ if (entryApiType === "anthropic" && providerApiType === "openai-responses") {
26
+ return { body: anthropicToResponsesRequest(body), upstreamPath: "/v1/responses" };
27
+ }
28
+ // Existing: OpenAI Chat ↔ Anthropic
29
+ if (entryApiType === "openai" && providerApiType === "anthropic") {
30
+ return { body: transformRequestBody(body, "openai", "anthropic", model), upstreamPath: "/v1/messages" };
31
+ }
32
+ if (entryApiType === "anthropic" && providerApiType === "openai") {
33
+ return { body: transformRequestBody(body, "anthropic", "openai", model), upstreamPath: "/v1/chat/completions" };
34
+ }
35
+ // Bridge: Responses ↔ Chat
36
+ if (entryApiType === "openai-responses" && providerApiType === "openai") {
37
+ return { body: responsesToChatRequest(body), upstreamPath: "/v1/chat/completions" };
38
+ }
39
+ if (entryApiType === "openai" && providerApiType === "openai-responses") {
40
+ return { body: chatToResponsesRequest(body), upstreamPath: "/v1/responses" };
41
+ }
42
+ return { body, upstreamPath: this.getUpstreamPath(providerApiType) };
12
43
  }
13
44
  transformResponse(bodyStr, sourceApiType, targetApiType) {
14
- return transformResponseBody(bodyStr, sourceApiType, targetApiType);
45
+ if (sourceApiType === targetApiType)
46
+ return bodyStr;
47
+ // Tier-1
48
+ if (sourceApiType === "openai-responses" && targetApiType === "anthropic") {
49
+ return responsesToAnthropicResponse(bodyStr);
50
+ }
51
+ if (sourceApiType === "anthropic" && targetApiType === "openai-responses") {
52
+ return anthropicToResponsesResponse(bodyStr);
53
+ }
54
+ // Existing
55
+ if (sourceApiType === "openai" && targetApiType === "anthropic") {
56
+ return transformResponseBody(bodyStr, "openai", "anthropic");
57
+ }
58
+ if (sourceApiType === "anthropic" && targetApiType === "openai") {
59
+ return transformResponseBody(bodyStr, "anthropic", "openai");
60
+ }
61
+ // Bridge
62
+ if (sourceApiType === "openai-responses" && targetApiType === "openai") {
63
+ return responsesToChatResponse(bodyStr);
64
+ }
65
+ if (sourceApiType === "openai" && targetApiType === "openai-responses") {
66
+ return chatToResponsesResponse(bodyStr);
67
+ }
68
+ return bodyStr;
15
69
  }
16
70
  transformErrorResponse(bodyStr, sourceApiType, targetApiType) {
17
- return transformErrorResponse(bodyStr, sourceApiType, targetApiType);
71
+ if (sourceApiType === targetApiType)
72
+ return bodyStr;
73
+ try {
74
+ const parsed = JSON.parse(bodyStr);
75
+ // Responses ↔ Anthropic error conversion
76
+ if (sourceApiType === "openai-responses" && targetApiType === "anthropic") {
77
+ const err = parsed.error ?? parsed;
78
+ return JSON.stringify({
79
+ type: "error",
80
+ error: { type: "api_error", message: err.message ?? String(err) },
81
+ });
82
+ }
83
+ if (sourceApiType === "anthropic" && targetApiType === "openai-responses") {
84
+ const err = parsed.error ?? parsed;
85
+ return JSON.stringify({
86
+ error: {
87
+ message: err.message ?? String(err),
88
+ type: "invalid_request_error",
89
+ code: "upstream_error",
90
+ },
91
+ });
92
+ }
93
+ // Responses ↔ Chat error conversion
94
+ if (sourceApiType === "openai-responses" && targetApiType === "openai") {
95
+ const err = parsed.error ?? parsed;
96
+ return JSON.stringify({
97
+ error: { message: err.message ?? String(err), type: "api_error", code: "upstream_error" },
98
+ });
99
+ }
100
+ if (sourceApiType === "openai" && targetApiType === "openai-responses") {
101
+ const err = parsed.error ?? parsed;
102
+ return JSON.stringify({
103
+ error: {
104
+ message: err.message ?? String(err),
105
+ type: "invalid_request_error",
106
+ code: "upstream_error",
107
+ },
108
+ });
109
+ }
110
+ // Fall through to existing Chat ↔ Anthropic error conversion
111
+ return transformErrorBody(bodyStr, sourceApiType, targetApiType);
112
+ }
113
+ catch {
114
+ return bodyStr;
115
+ }
18
116
  }
19
117
  createFormatTransform(entryApiType, providerApiType, model) {
20
- if (!this.needsTransform(entryApiType, providerApiType))
118
+ if (entryApiType === providerApiType)
21
119
  return undefined;
22
- // 上游=provider格式, 客户端=entry格式
23
- // OA provider + Ant client OpenAIToAnthropicTransform
120
+ // Tier-1 streaming
121
+ if (providerApiType === "anthropic" && entryApiType === "openai-responses") {
122
+ return new ResponsesToAnthropicTransform(model);
123
+ }
124
+ if (providerApiType === "openai-responses" && entryApiType === "anthropic") {
125
+ return new AnthropicToResponsesTransform(model);
126
+ }
127
+ // Existing streaming
24
128
  if (providerApiType === "openai" && entryApiType === "anthropic") {
25
129
  return new OpenAIToAnthropicTransform(model);
26
130
  }
27
131
  if (providerApiType === "anthropic" && entryApiType === "openai") {
28
132
  return new AnthropicToOpenAITransform(model);
29
133
  }
134
+ // Bridge streaming
135
+ if (providerApiType === "openai" && entryApiType === "openai-responses") {
136
+ return new ResponsesToChatBridgeTransform(model);
137
+ }
138
+ if (providerApiType === "openai-responses" && entryApiType === "openai") {
139
+ return new ChatToResponsesBridgeTransform(model);
140
+ }
30
141
  return undefined;
31
142
  }
143
+ getUpstreamPath(apiType) {
144
+ switch (apiType) {
145
+ case "openai": return "/v1/chat/completions";
146
+ case "openai-responses": return "/v1/responses";
147
+ case "anthropic": return "/v1/messages";
148
+ default: return "/v1/chat/completions";
149
+ }
150
+ }
32
151
  }
@@ -0,0 +1,177 @@
1
+ export interface ResponseInputMessage {
2
+ type: "message";
3
+ role: "user" | "assistant" | "system" | "developer";
4
+ content: string | ResponseInputContentPart[];
5
+ }
6
+ export interface ResponseInputText {
7
+ type: "input_text";
8
+ text: string;
9
+ }
10
+ export interface ResponseInputImage {
11
+ type: "input_image";
12
+ image_url: string;
13
+ detail?: "auto" | "low" | "high";
14
+ }
15
+ export interface ResponseFunctionCallInput {
16
+ type: "function_call";
17
+ id: string;
18
+ call_id: string;
19
+ name: string;
20
+ arguments: string;
21
+ }
22
+ export interface ResponseFunctionCallOutputInput {
23
+ type: "function_call_output";
24
+ call_id: string;
25
+ output: string;
26
+ }
27
+ export interface ResponseReasoningInput {
28
+ type: "reasoning";
29
+ id: string;
30
+ summary: Array<{
31
+ type: "summary_text";
32
+ text: string;
33
+ }>;
34
+ encrypted_content?: string;
35
+ }
36
+ export type ResponseInputItem = ResponseInputMessage | ResponseInputText | ResponseInputImage | ResponseFunctionCallInput | ResponseFunctionCallOutputInput | ResponseReasoningInput;
37
+ export interface ResponseInputContentPart {
38
+ type: "input_text" | "input_image" | "input_file";
39
+ text?: string;
40
+ image_url?: string;
41
+ file_url?: string;
42
+ file_data?: string;
43
+ filename?: string;
44
+ }
45
+ export interface ResponseFunctionTool {
46
+ type: "function";
47
+ name: string;
48
+ description?: string;
49
+ parameters?: Record<string, unknown>;
50
+ strict?: boolean;
51
+ }
52
+ export interface ResponseWebSearchTool {
53
+ type: "web_search_preview";
54
+ search_context_size?: "low" | "medium" | "high";
55
+ user_location?: {
56
+ type: "approximate";
57
+ city?: string;
58
+ country?: string;
59
+ };
60
+ }
61
+ export interface ResponseFileSearchTool {
62
+ type: "file_search";
63
+ vector_store_ids?: string[];
64
+ }
65
+ export type ResponseTool = ResponseFunctionTool | ResponseWebSearchTool | ResponseFileSearchTool | Record<string, unknown>;
66
+ export interface ResponseOutputMessage {
67
+ type: "message";
68
+ id: string;
69
+ role: "assistant";
70
+ content: ResponseOutputContent[];
71
+ status?: string;
72
+ }
73
+ export interface ResponseOutputContent {
74
+ type: "output_text";
75
+ text: string;
76
+ annotations?: unknown[];
77
+ }
78
+ export interface ResponseFunctionCallOutput {
79
+ type: "function_call";
80
+ id: string;
81
+ call_id: string;
82
+ name: string;
83
+ arguments: string;
84
+ status?: string;
85
+ }
86
+ export interface ResponseReasoningOutput {
87
+ type: "reasoning";
88
+ id: string;
89
+ summary: Array<{
90
+ type: "summary_text";
91
+ text: string;
92
+ }>;
93
+ encrypted_content?: string;
94
+ }
95
+ export type ResponseOutputItem = ResponseOutputMessage | ResponseFunctionCallOutput | ResponseReasoningOutput | Record<string, unknown>;
96
+ export interface ResponsesApiRequest {
97
+ model: string;
98
+ input: string | ResponseInputItem[];
99
+ instructions?: string;
100
+ max_output_tokens?: number;
101
+ temperature?: number;
102
+ top_p?: number;
103
+ tools?: ResponseTool[];
104
+ tool_choice?: "auto" | "none" | "required" | {
105
+ type: "function";
106
+ name: string;
107
+ };
108
+ stream?: boolean;
109
+ stream_options?: {
110
+ include_usage?: boolean;
111
+ };
112
+ reasoning?: {
113
+ effort?: "low" | "medium" | "high";
114
+ max_tokens?: number;
115
+ summary?: "auto" | "concise" | "detailed";
116
+ };
117
+ previous_response_id?: string;
118
+ metadata?: Record<string, string>;
119
+ text?: {
120
+ format: {
121
+ type: "json_schema";
122
+ json_schema: unknown;
123
+ };
124
+ };
125
+ parallel_tool_calls?: boolean;
126
+ store?: boolean;
127
+ }
128
+ export interface ResponsesApiResponse {
129
+ id: string;
130
+ object: "response";
131
+ model: string;
132
+ status: "completed" | "failed" | "in_progress" | "incomplete";
133
+ output: ResponseOutputItem[];
134
+ usage?: ResponsesApiUsage;
135
+ error?: {
136
+ code: string;
137
+ message: string;
138
+ };
139
+ created_at?: number;
140
+ completed_at?: number;
141
+ }
142
+ export interface ResponsesApiUsage {
143
+ input_tokens: number;
144
+ output_tokens: number;
145
+ total_tokens: number;
146
+ input_tokens_details?: {
147
+ cached_tokens: number;
148
+ };
149
+ output_tokens_details?: {
150
+ reasoning_tokens: number;
151
+ };
152
+ }
153
+ export declare const RESPONSES_SSE_EVENTS: {
154
+ readonly CREATED: "response.created";
155
+ readonly IN_PROGRESS: "response.in_progress";
156
+ readonly QUEUED: "response.queued";
157
+ readonly OUTPUT_ITEM_ADDED: "response.output_item.added";
158
+ readonly OUTPUT_ITEM_DONE: "response.output_item.done";
159
+ readonly CONTENT_PART_ADDED: "response.content_part.added";
160
+ readonly CONTENT_PART_DONE: "response.content_part.done";
161
+ readonly OUTPUT_TEXT_DELTA: "response.output_text.delta";
162
+ readonly OUTPUT_TEXT_DONE: "response.output_text.done";
163
+ readonly REFUSAL_DELTA: "response.refusal.delta";
164
+ readonly REFUSAL_DONE: "response.refusal.done";
165
+ readonly FUNCTION_CALL_ARGUMENTS_DELTA: "response.function_call_arguments.delta";
166
+ readonly FUNCTION_CALL_ARGUMENTS_DONE: "response.function_call_arguments.done";
167
+ readonly REASONING_SUMMARY_PART_ADDED: "response.reasoning_summary_part.added";
168
+ readonly REASONING_SUMMARY_PART_DONE: "response.reasoning_summary_part.done";
169
+ readonly REASONING_SUMMARY_TEXT_DELTA: "response.reasoning_summary_text.delta";
170
+ readonly REASONING_SUMMARY_TEXT_DONE: "response.reasoning_summary_text.done";
171
+ readonly REASONING_TEXT_DELTA: "response.reasoning_text.delta";
172
+ readonly REASONING_TEXT_DONE: "response.reasoning_text.done";
173
+ readonly COMPLETED: "response.completed";
174
+ readonly FAILED: "response.failed";
175
+ readonly INCOMPLETE: "response.incomplete";
176
+ readonly ERROR: "error";
177
+ };
@@ -0,0 +1,27 @@
1
+ // ---------- Responses API 输入 item 类型 ----------
2
+ // ---------- Responses SSE 流式事件类型常量 ----------
3
+ export const RESPONSES_SSE_EVENTS = {
4
+ CREATED: "response.created",
5
+ IN_PROGRESS: "response.in_progress",
6
+ QUEUED: "response.queued",
7
+ OUTPUT_ITEM_ADDED: "response.output_item.added",
8
+ OUTPUT_ITEM_DONE: "response.output_item.done",
9
+ CONTENT_PART_ADDED: "response.content_part.added",
10
+ CONTENT_PART_DONE: "response.content_part.done",
11
+ OUTPUT_TEXT_DELTA: "response.output_text.delta",
12
+ OUTPUT_TEXT_DONE: "response.output_text.done",
13
+ REFUSAL_DELTA: "response.refusal.delta",
14
+ REFUSAL_DONE: "response.refusal.done",
15
+ FUNCTION_CALL_ARGUMENTS_DELTA: "response.function_call_arguments.delta",
16
+ FUNCTION_CALL_ARGUMENTS_DONE: "response.function_call_arguments.done",
17
+ REASONING_SUMMARY_PART_ADDED: "response.reasoning_summary_part.added",
18
+ REASONING_SUMMARY_PART_DONE: "response.reasoning_summary_part.done",
19
+ REASONING_SUMMARY_TEXT_DELTA: "response.reasoning_summary_text.delta",
20
+ REASONING_SUMMARY_TEXT_DONE: "response.reasoning_summary_text.done",
21
+ REASONING_TEXT_DELTA: "response.reasoning_text.delta",
22
+ REASONING_TEXT_DONE: "response.reasoning_text.done",
23
+ COMPLETED: "response.completed",
24
+ FAILED: "response.failed",
25
+ INCOMPLETE: "response.incomplete",
26
+ ERROR: "error",
27
+ };
@@ -1,5 +1,7 @@
1
1
  /** 格式转换方向 */
2
- export type TransformDirection = "openai-to-anthropic" | "anthropic-to-openai";
2
+ export type TransformDirection = "openai-to-anthropic" | "anthropic-to-openai" | "openai-responses-to-anthropic" | "anthropic-to-openai-responses" | "openai-to-openai-responses" | "openai-responses-to-openai";
3
+ /** 所有支持的 API 格式类型 */
4
+ export type ApiType = "openai" | "openai-responses" | "anthropic";
3
5
  export interface AnthropicTextBlock {
4
6
  type: "text";
5
7
  text: string;
@@ -11,7 +11,7 @@ export interface TransportFnParams {
11
11
  cliHdrs: RawHeaders;
12
12
  reply: FastifyReply;
13
13
  upstreamPath: string;
14
- apiType: "openai" | "anthropic";
14
+ apiType: "openai" | "openai-responses" | "anthropic";
15
15
  isStream: boolean;
16
16
  startTime: number;
17
17
  logId: string;
@@ -0,0 +1 @@
1
+ import{Bt as e,Q as t,Rt as n,Y as r,pt as i,r as a,ut as o}from"./button-D27ClX8J.js";var s=[`data-size`],c=t({__name:`Card`,props:{class:{type:[Boolean,null,String,Object,Array]},size:{default:`default`}},setup(t){let c=t;return(l,u)=>(o(),r(`div`,{"data-slot":`card`,"data-size":t.size,class:e(n(a)(`ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-lg py-4 text-sm ring-1 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-lg *:[img:last-child]:rounded-b-lg group/card flex flex-col`,c.class))},[i(l.$slots,`default`)],10,s))}}),l=t({__name:`CardContent`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(t){let s=t;return(t,c)=>(o(),r(`div`,{"data-slot":`card-content`,class:e(n(a)(`px-4 group-data-[size=sm]/card:px-3`,s.class))},[i(t.$slots,`default`)],2))}});export{c as n,l as t};