@omnicross/core 0.1.0

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 (114) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +57 -0
  3. package/README.md +15 -0
  4. package/dist/ApiKeyPoolService-BmMkau07.d.cts +170 -0
  5. package/dist/ApiKeyPoolService-BmMkau07.d.ts +170 -0
  6. package/dist/ProviderProxy-f_8ziIhW.d.cts +120 -0
  7. package/dist/ProviderProxy-vjt8sQQk.d.ts +120 -0
  8. package/dist/SubscriptionAuthSource-Cr4fVEYY.d.cts +264 -0
  9. package/dist/SubscriptionAuthSource-D89zmiSS.d.ts +264 -0
  10. package/dist/auth/GeminiCodeAssistProjectResolver.cjs +218 -0
  11. package/dist/auth/GeminiCodeAssistProjectResolver.d.cts +68 -0
  12. package/dist/auth/GeminiCodeAssistProjectResolver.d.ts +68 -0
  13. package/dist/auth/GeminiCodeAssistProjectResolver.js +189 -0
  14. package/dist/completion/ApiKeyPoolService.cjs +331 -0
  15. package/dist/completion/ApiKeyPoolService.d.cts +2 -0
  16. package/dist/completion/ApiKeyPoolService.d.ts +2 -0
  17. package/dist/completion/ApiKeyPoolService.js +306 -0
  18. package/dist/completion.cjs +4027 -0
  19. package/dist/completion.d.cts +17 -0
  20. package/dist/completion.d.ts +17 -0
  21. package/dist/completion.js +3983 -0
  22. package/dist/index-BTSmc9Sm.d.ts +645 -0
  23. package/dist/index-DXazdTzZ.d.cts +645 -0
  24. package/dist/index.cjs +10428 -0
  25. package/dist/index.d.cts +128 -0
  26. package/dist/index.d.ts +128 -0
  27. package/dist/index.js +10339 -0
  28. package/dist/outbound-api/subscriptionRegistryPort.cjs +38 -0
  29. package/dist/outbound-api/subscriptionRegistryPort.d.cts +73 -0
  30. package/dist/outbound-api/subscriptionRegistryPort.d.ts +73 -0
  31. package/dist/outbound-api/subscriptionRegistryPort.js +12 -0
  32. package/dist/outbound-api.cjs +5264 -0
  33. package/dist/outbound-api.d.cts +320 -0
  34. package/dist/outbound-api.d.ts +320 -0
  35. package/dist/outbound-api.js +5218 -0
  36. package/dist/pipeline/SubscriptionAuthSource.cjs +131 -0
  37. package/dist/pipeline/SubscriptionAuthSource.d.cts +3 -0
  38. package/dist/pipeline/SubscriptionAuthSource.d.ts +3 -0
  39. package/dist/pipeline/SubscriptionAuthSource.js +103 -0
  40. package/dist/pipeline/SubscriptionAuthStrategy.cjs +18 -0
  41. package/dist/pipeline/SubscriptionAuthStrategy.d.cts +61 -0
  42. package/dist/pipeline/SubscriptionAuthStrategy.d.ts +61 -0
  43. package/dist/pipeline/SubscriptionAuthStrategy.js +0 -0
  44. package/dist/ports/gemini-code-assist-resolver.cjs +38 -0
  45. package/dist/ports/gemini-code-assist-resolver.d.cts +26 -0
  46. package/dist/ports/gemini-code-assist-resolver.d.ts +26 -0
  47. package/dist/ports/gemini-code-assist-resolver.js +12 -0
  48. package/dist/ports.cjs +18 -0
  49. package/dist/ports.d.cts +15 -0
  50. package/dist/ports.d.ts +15 -0
  51. package/dist/ports.js +0 -0
  52. package/dist/provider-proxy/ingress/providerProxyShared.cjs +2958 -0
  53. package/dist/provider-proxy/ingress/providerProxyShared.d.cts +77 -0
  54. package/dist/provider-proxy/ingress/providerProxyShared.d.ts +77 -0
  55. package/dist/provider-proxy/ingress/providerProxyShared.js +2925 -0
  56. package/dist/provider-proxy/matchText.cjs +73 -0
  57. package/dist/provider-proxy/matchText.d.cts +47 -0
  58. package/dist/provider-proxy/matchText.d.ts +47 -0
  59. package/dist/provider-proxy/matchText.js +45 -0
  60. package/dist/provider-proxy/types.cjs +18 -0
  61. package/dist/provider-proxy/types.d.cts +12 -0
  62. package/dist/provider-proxy/types.d.ts +12 -0
  63. package/dist/provider-proxy/types.js +0 -0
  64. package/dist/provider-proxy.cjs +4667 -0
  65. package/dist/provider-proxy.d.cts +69 -0
  66. package/dist/provider-proxy.d.ts +69 -0
  67. package/dist/provider-proxy.js +4636 -0
  68. package/dist/serializeError.cjs +82 -0
  69. package/dist/serializeError.d.cts +24 -0
  70. package/dist/serializeError.d.ts +24 -0
  71. package/dist/serializeError.js +57 -0
  72. package/dist/sse-parser.cjs +456 -0
  73. package/dist/sse-parser.d.cts +143 -0
  74. package/dist/sse-parser.d.ts +143 -0
  75. package/dist/sse-parser.js +430 -0
  76. package/dist/transformer/TransformerChainExecutor.cjs +321 -0
  77. package/dist/transformer/TransformerChainExecutor.d.cts +104 -0
  78. package/dist/transformer/TransformerChainExecutor.d.ts +104 -0
  79. package/dist/transformer/TransformerChainExecutor.js +294 -0
  80. package/dist/transformer/TransformerService.cjs +290 -0
  81. package/dist/transformer/TransformerService.d.cts +138 -0
  82. package/dist/transformer/TransformerService.d.ts +138 -0
  83. package/dist/transformer/TransformerService.js +265 -0
  84. package/dist/transformer/transformers/GeminiCodeAssistTransformer.cjs +1115 -0
  85. package/dist/transformer/transformers/GeminiCodeAssistTransformer.d.cts +102 -0
  86. package/dist/transformer/transformers/GeminiCodeAssistTransformer.d.ts +102 -0
  87. package/dist/transformer/transformers/GeminiCodeAssistTransformer.js +1085 -0
  88. package/dist/transformer/transformers/GeminiTransformer.cjs +1013 -0
  89. package/dist/transformer/transformers/GeminiTransformer.d.cts +70 -0
  90. package/dist/transformer/transformers/GeminiTransformer.d.ts +70 -0
  91. package/dist/transformer/transformers/GeminiTransformer.js +986 -0
  92. package/dist/transformer/transformers/OpenAIResponseTransformer.cjs +538 -0
  93. package/dist/transformer/transformers/OpenAIResponseTransformer.d.cts +53 -0
  94. package/dist/transformer/transformers/OpenAIResponseTransformer.d.ts +53 -0
  95. package/dist/transformer/transformers/OpenAIResponseTransformer.js +513 -0
  96. package/dist/transformer/transformers/OpenCodeGoTransformer.cjs +73 -0
  97. package/dist/transformer/transformers/OpenCodeGoTransformer.d.cts +51 -0
  98. package/dist/transformer/transformers/OpenCodeGoTransformer.d.ts +51 -0
  99. package/dist/transformer/transformers/OpenCodeGoTransformer.js +48 -0
  100. package/dist/transformer/types.cjs +18 -0
  101. package/dist/transformer/types.d.cts +405 -0
  102. package/dist/transformer/types.d.ts +405 -0
  103. package/dist/transformer/types.js +0 -0
  104. package/dist/transformer.cjs +3736 -0
  105. package/dist/transformer.d.cts +33 -0
  106. package/dist/transformer.d.ts +33 -0
  107. package/dist/transformer.js +3712 -0
  108. package/dist/types-CGGrKqC_.d.cts +142 -0
  109. package/dist/types-CbCN2NQP.d.ts +142 -0
  110. package/dist/types-DCzHkhJt.d.ts +467 -0
  111. package/dist/types-DZIQbgp0.d.cts +467 -0
  112. package/dist/usage-event-sink-BX7FE1NL.d.cts +59 -0
  113. package/dist/usage-event-sink-BX7FE1NL.d.ts +59 -0
  114. package/package.json +62 -0
@@ -0,0 +1,2958 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/provider-proxy/ingress/providerProxyShared.ts
31
+ var providerProxyShared_exports = {};
32
+ __export(providerProxyShared_exports, {
33
+ getAnthropicEndpointTransformer: () => getAnthropicEndpointTransformer,
34
+ getGeminiEndpointTransformer: () => getGeminiEndpointTransformer,
35
+ getResponsesEndpointTransformer: () => getResponsesEndpointTransformer,
36
+ getSharedExecutor: () => getSharedExecutor,
37
+ readBody: () => readBody,
38
+ relayResponse: () => relayResponse,
39
+ resolvePoolBoundKey: () => resolvePoolBoundKey,
40
+ writeError: () => writeError
41
+ });
42
+ module.exports = __toCommonJS(providerProxyShared_exports);
43
+
44
+ // src/completion/DirectApiHandler.ts
45
+ var import_thinking_config2 = require("@omnicross/contracts/thinking-config");
46
+
47
+ // src/provider-proxy/ProviderProxy.ts
48
+ var import_node_http = __toESM(require("http"), 1);
49
+
50
+ // src/provider-proxy/providerProxyRouteMap.ts
51
+ var import_node_crypto = require("crypto");
52
+ var DEFAULT_ROUTE_IDLE_MS = 10 * 60 * 1e3;
53
+
54
+ // src/completion/url-builder.ts
55
+ var import_endpoint_resolver = require("@omnicross/contracts/endpoint-resolver");
56
+ var resolveProviderEndpoint = import_endpoint_resolver.resolveProviderEndpoint;
57
+
58
+ // src/transformer/anthropicBetaInject.ts
59
+ var import_extended_context = require("@omnicross/contracts/extended-context");
60
+ var EXTENDED_CONTEXT_BETA = "context-1m-2025-08-07";
61
+ var ANTHROPIC_BETA_HEADER = "anthropic-beta";
62
+ function injectExtendedContextBeta(headers, model, useExtendedContext) {
63
+ if (!useExtendedContext) return;
64
+ if (!(0, import_extended_context.isExtendedContextCapable)(model)) return;
65
+ let existingValue = "";
66
+ for (const key of Object.keys(headers)) {
67
+ if (key.toLowerCase() === ANTHROPIC_BETA_HEADER) {
68
+ const v = headers[key];
69
+ if (typeof v === "string") existingValue = v;
70
+ if (key !== ANTHROPIC_BETA_HEADER) delete headers[key];
71
+ }
72
+ }
73
+ const parts = existingValue.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
74
+ if (!parts.includes(EXTENDED_CONTEXT_BETA)) {
75
+ parts.push(EXTENDED_CONTEXT_BETA);
76
+ }
77
+ headers[ANTHROPIC_BETA_HEADER] = parts.join(",");
78
+ }
79
+
80
+ // src/outbound-api/OutboundApiServer.ts
81
+ var import_node_http2 = __toESM(require("http"), 1);
82
+ var import_node_os = require("os");
83
+
84
+ // src/outbound-api/outboundApiRouter.ts
85
+ var import_node_stream = require("stream");
86
+
87
+ // src/outbound-api/outboundApiKeyAuth.ts
88
+ var import_node_crypto2 = require("crypto");
89
+
90
+ // src/outbound-api/roleDetection.ts
91
+ var import_canonical_models = require("@omnicross/contracts/canonical-models");
92
+
93
+ // src/outbound-api/subscriptionSupport.ts
94
+ var SUBSCRIPTION_PROVIDER_IDS = [
95
+ "claude",
96
+ "codex",
97
+ "gemini",
98
+ "opencodego"
99
+ ];
100
+ var SUBSCRIPTION_ID_SET = new Set(SUBSCRIPTION_PROVIDER_IDS);
101
+
102
+ // src/transformer/TransformerChainExecutor.ts
103
+ var defaultLogger = {
104
+ debug: (msg, ...args) => console.debug(`[ChainExecutor] ${msg}`, ...args),
105
+ info: (msg, ...args) => console.info(`[ChainExecutor] ${msg}`, ...args),
106
+ warn: (msg, ...args) => console.warn(`[ChainExecutor] ${msg}`, ...args),
107
+ error: (msg, ...args) => console.error(`[ChainExecutor] ${msg}`, ...args)
108
+ };
109
+ var TransformerChainExecutor = class {
110
+ logger;
111
+ constructor(logger) {
112
+ this.logger = logger ?? defaultLogger;
113
+ }
114
+ /**
115
+ * Execute the request transformation chain
116
+ *
117
+ * @param request - Original request body
118
+ * @param provider - LLM provider configuration
119
+ * @param chain - Resolved transformer chain
120
+ * @param options - Execution options
121
+ * @returns Transformed request result
122
+ */
123
+ async executeRequestChain(request, provider, chain, options = {}) {
124
+ const { endpointTransformer, headers, extendedContext } = options;
125
+ const context = {
126
+ logger: this.logger,
127
+ providerName: provider.name
128
+ };
129
+ let requestBody = request;
130
+ let config = {};
131
+ let bypass = false;
132
+ bypass = this.shouldBypassTransformers(
133
+ chain,
134
+ endpointTransformer,
135
+ requestBody
136
+ );
137
+ if (bypass) {
138
+ if (headers) {
139
+ const cleanHeaders = this.cleanHeaders(headers);
140
+ config.headers = cleanHeaders;
141
+ }
142
+ this.logger.debug("Bypass mode enabled - skipping transformations");
143
+ }
144
+ if (!bypass && endpointTransformer?.transformRequestOut) {
145
+ this.logger.debug("Executing transformRequestOut");
146
+ try {
147
+ const transformOut = await endpointTransformer.transformRequestOut(requestBody, context);
148
+ if (transformOut && typeof transformOut === "object") {
149
+ if ("body" in transformOut) {
150
+ requestBody = transformOut.body;
151
+ config = transformOut.config ?? {};
152
+ } else {
153
+ requestBody = transformOut;
154
+ }
155
+ }
156
+ } catch (error) {
157
+ this.logger.error(`transformRequestOut error: ${this.getErrorMessage(error)}`);
158
+ throw error;
159
+ }
160
+ }
161
+ if (!bypass && chain.providerTransformers.length > 0) {
162
+ this.logger.debug(`Executing ${chain.providerTransformers.length} provider transformers`);
163
+ for (const transformer of chain.providerTransformers) {
164
+ if (transformer.transformRequestIn) {
165
+ try {
166
+ const transformIn = await transformer.transformRequestIn(
167
+ requestBody,
168
+ provider,
169
+ context
170
+ );
171
+ if (transformIn && typeof transformIn === "object") {
172
+ if ("body" in transformIn) {
173
+ requestBody = transformIn.body;
174
+ config = { ...config, ...transformIn.config };
175
+ } else {
176
+ requestBody = transformIn;
177
+ }
178
+ }
179
+ } catch (error) {
180
+ this.logger.error(
181
+ `Provider transformer ${transformer.name} error: ${this.getErrorMessage(error)}`
182
+ );
183
+ throw error;
184
+ }
185
+ }
186
+ }
187
+ }
188
+ if (!bypass && chain.modelTransformers.length > 0) {
189
+ this.logger.debug(`Executing ${chain.modelTransformers.length} model transformers`);
190
+ for (const transformer of chain.modelTransformers) {
191
+ if (transformer.transformRequestIn) {
192
+ try {
193
+ const result = await transformer.transformRequestIn(
194
+ requestBody,
195
+ provider,
196
+ context
197
+ );
198
+ requestBody = result;
199
+ } catch (error) {
200
+ this.logger.error(
201
+ `Model transformer ${transformer.name} error: ${this.getErrorMessage(error)}`
202
+ );
203
+ throw error;
204
+ }
205
+ }
206
+ }
207
+ }
208
+ if (extendedContext?.enabled) {
209
+ if (!config.headers || typeof config.headers !== "object") {
210
+ config.headers = {};
211
+ }
212
+ injectExtendedContextBeta(
213
+ config.headers,
214
+ extendedContext.model,
215
+ true
216
+ );
217
+ }
218
+ return { requestBody, config, bypass };
219
+ }
220
+ /**
221
+ * Execute the response transformation chain
222
+ *
223
+ * @param request - Original request (for context)
224
+ * @param response - Response from provider
225
+ * @param provider - LLM provider configuration
226
+ * @param chain - Resolved transformer chain
227
+ * @param options - Execution options
228
+ * @returns Transformed response
229
+ */
230
+ async executeResponseChain(request, response, provider, chain, options = {}) {
231
+ const { endpointTransformer } = options;
232
+ const context = {
233
+ logger: this.logger,
234
+ providerName: provider.name
235
+ };
236
+ let finalResponse = response;
237
+ const bypass = this.shouldBypassTransformers(chain, endpointTransformer, request);
238
+ if (bypass) {
239
+ this.logger.debug("Bypass mode - skipping response transformations");
240
+ return finalResponse;
241
+ }
242
+ if (chain.modelTransformers.length > 0) {
243
+ const reversedModelTransformers = [...chain.modelTransformers].reverse();
244
+ this.logger.debug(
245
+ `Executing ${reversedModelTransformers.length} model response transformers (reversed)`
246
+ );
247
+ for (const transformer of reversedModelTransformers) {
248
+ if (transformer.transformResponseOut) {
249
+ try {
250
+ finalResponse = await transformer.transformResponseOut(finalResponse, context);
251
+ } catch (error) {
252
+ this.logger.error(
253
+ `Model transformer ${transformer.name} response error: ${this.getErrorMessage(error)}`
254
+ );
255
+ throw error;
256
+ }
257
+ }
258
+ }
259
+ }
260
+ if (chain.providerTransformers.length > 0) {
261
+ const reversedProviderTransformers = [...chain.providerTransformers].reverse();
262
+ this.logger.debug(
263
+ `Executing ${reversedProviderTransformers.length} provider response transformers (reversed)`
264
+ );
265
+ for (const transformer of reversedProviderTransformers) {
266
+ if (transformer.transformResponseOut) {
267
+ try {
268
+ finalResponse = await transformer.transformResponseOut(finalResponse, context);
269
+ } catch (error) {
270
+ this.logger.error(
271
+ `Provider transformer ${transformer.name} response error: ${this.getErrorMessage(error)}`
272
+ );
273
+ throw error;
274
+ }
275
+ }
276
+ }
277
+ }
278
+ if (endpointTransformer?.transformResponseIn) {
279
+ this.logger.debug("Executing transformResponseIn");
280
+ try {
281
+ finalResponse = await endpointTransformer.transformResponseIn(finalResponse, context);
282
+ } catch (error) {
283
+ this.logger.error(`transformResponseIn error: ${this.getErrorMessage(error)}`);
284
+ throw error;
285
+ }
286
+ }
287
+ return finalResponse;
288
+ }
289
+ /**
290
+ * Execute authentication handler if available
291
+ *
292
+ * @param request - Request body
293
+ * @param provider - LLM provider
294
+ * @param endpointTransformer - Endpoint transformer with auth handler
295
+ * @param context - Transformer context
296
+ * @returns Auth result with potentially modified request and config
297
+ */
298
+ async executeAuth(request, provider, endpointTransformer, context) {
299
+ let requestBody = request;
300
+ let config = {};
301
+ if (endpointTransformer?.auth) {
302
+ this.logger.debug("Executing auth handler");
303
+ try {
304
+ const auth = await endpointTransformer.auth(requestBody, provider, context);
305
+ if (auth && typeof auth === "object") {
306
+ if ("body" in auth) {
307
+ requestBody = auth.body;
308
+ const authConfig = auth.config;
309
+ if (authConfig) {
310
+ const headers = { ...config.headers ?? {}, ...authConfig.headers ?? {} };
311
+ delete headers["host"];
312
+ config = { ...config, ...authConfig, headers };
313
+ }
314
+ } else {
315
+ requestBody = auth;
316
+ }
317
+ }
318
+ } catch (error) {
319
+ this.logger.error(`Auth handler error: ${this.getErrorMessage(error)}`);
320
+ throw error;
321
+ }
322
+ }
323
+ return { requestBody, config };
324
+ }
325
+ /**
326
+ * Check if transformers should be bypassed (optimization)
327
+ *
328
+ * Bypass is enabled when:
329
+ * - Provider has only one transformer that matches the endpoint transformer
330
+ * - Model has no specific transformers or only the same endpoint transformer
331
+ */
332
+ shouldBypassTransformers(chain, endpointTransformer, _request) {
333
+ if (!endpointTransformer?.name) {
334
+ return false;
335
+ }
336
+ const providerHasOnlyEndpoint = chain.providerTransformers.length === 1 && chain.providerTransformers[0]?.name === endpointTransformer.name;
337
+ const modelHasNoTransformers = chain.modelTransformers.length === 0;
338
+ const modelHasOnlyEndpoint = chain.modelTransformers.length === 1 && chain.modelTransformers[0]?.name === endpointTransformer.name;
339
+ return providerHasOnlyEndpoint && (modelHasNoTransformers || modelHasOnlyEndpoint);
340
+ }
341
+ /**
342
+ * Clean headers for pass-through
343
+ */
344
+ cleanHeaders(headers) {
345
+ const result = {};
346
+ if (headers instanceof Headers) {
347
+ headers.forEach((value, key) => {
348
+ if (key.toLowerCase() !== "content-length") {
349
+ result[key] = value;
350
+ }
351
+ });
352
+ } else {
353
+ for (const [key, value] of Object.entries(headers)) {
354
+ if (key.toLowerCase() !== "content-length") {
355
+ result[key] = value;
356
+ }
357
+ }
358
+ }
359
+ return result;
360
+ }
361
+ /**
362
+ * Get error message from unknown error
363
+ */
364
+ getErrorMessage(error) {
365
+ if (error instanceof Error) {
366
+ return error.message;
367
+ }
368
+ return String(error);
369
+ }
370
+ };
371
+
372
+ // src/transformer/transformers/AnthropicToolHandling.ts
373
+ function isServerSideTool(tool) {
374
+ const type = String(tool.type || "");
375
+ return type.startsWith("web_search_") || type.startsWith("code_execution_") || type.startsWith("text_editor_") || type.startsWith("memory_") || type.startsWith("web_fetch_") || type.startsWith("search_tool_");
376
+ }
377
+ function convertAnthropicToolsToOpenAI(tools) {
378
+ return tools.filter((tool) => !isServerSideTool(tool)).map((tool) => ({
379
+ type: "function",
380
+ function: {
381
+ name: String(tool.name),
382
+ description: String(tool.description || ""),
383
+ parameters: tool.input_schema
384
+ }
385
+ }));
386
+ }
387
+
388
+ // src/transformer/transformers/AnthropicTypes.ts
389
+ function getThinkLevel(budgetTokens) {
390
+ if (!budgetTokens || budgetTokens <= 0) return "none";
391
+ if (budgetTokens < 4096) return "low";
392
+ if (budgetTokens < 16384) return "medium";
393
+ return "high";
394
+ }
395
+ function formatBase64(data, mediaType) {
396
+ if (data.startsWith("data:")) return data;
397
+ return `data:${mediaType || "image/png"};base64,${data}`;
398
+ }
399
+
400
+ // src/transformer/transformers/AnthropicRequestBuilder.ts
401
+ function buildAnthropicRequestBody(request) {
402
+ let systemContent;
403
+ const anthropicMessages = [];
404
+ for (let i = 0; i < request.messages.length; i++) {
405
+ const msg = request.messages[i];
406
+ if (msg.role === "system") {
407
+ if (typeof msg.content === "string") {
408
+ systemContent = msg.content;
409
+ } else if (Array.isArray(msg.content)) {
410
+ systemContent = msg.content.filter((c) => c.type === "text").map((c) => ({
411
+ type: "text",
412
+ text: c.text,
413
+ ...c.cache_control ? { cache_control: c.cache_control } : {}
414
+ }));
415
+ }
416
+ continue;
417
+ }
418
+ if (msg.role === "assistant") {
419
+ const content = [];
420
+ if (msg.thinking?.content) {
421
+ const block = {
422
+ type: "thinking",
423
+ thinking: msg.thinking.content
424
+ };
425
+ if (msg.thinking.signature) {
426
+ block.signature = msg.thinking.signature;
427
+ }
428
+ content.push(block);
429
+ }
430
+ if (msg.content) {
431
+ const text = typeof msg.content === "string" ? msg.content : msg.content.filter((c) => c.type === "text").map((c) => c.text).join("\n");
432
+ if (text) {
433
+ content.push({ type: "text", text });
434
+ }
435
+ }
436
+ if (msg.tool_calls?.length) {
437
+ for (const tc of msg.tool_calls) {
438
+ let input;
439
+ try {
440
+ input = typeof tc.function.arguments === "string" ? JSON.parse(tc.function.arguments) : tc.function.arguments;
441
+ } catch {
442
+ input = { text: tc.function.arguments || "" };
443
+ }
444
+ content.push({
445
+ type: "tool_use",
446
+ id: tc.id,
447
+ name: tc.function.name,
448
+ input
449
+ });
450
+ }
451
+ }
452
+ anthropicMessages.push({
453
+ role: "assistant",
454
+ content: content.length > 0 ? content : ""
455
+ });
456
+ continue;
457
+ }
458
+ if (msg.role === "tool") {
459
+ const toolResults = [];
460
+ let j = i;
461
+ while (j < request.messages.length && request.messages[j].role === "tool") {
462
+ const t = request.messages[j];
463
+ toolResults.push({
464
+ type: "tool_result",
465
+ tool_use_id: t.tool_call_id || "",
466
+ content: typeof t.content === "string" ? t.content : JSON.stringify(t.content),
467
+ ...t.cache_control ? { cache_control: t.cache_control } : {}
468
+ });
469
+ j++;
470
+ }
471
+ anthropicMessages.push({ role: "user", content: toolResults });
472
+ i = j - 1;
473
+ continue;
474
+ }
475
+ if (typeof msg.content === "string") {
476
+ anthropicMessages.push({ role: "user", content: msg.content });
477
+ } else if (Array.isArray(msg.content)) {
478
+ const content = msg.content.map((part) => {
479
+ if (part.type === "image_url") {
480
+ const url = part.image_url.url;
481
+ if (url.startsWith("data:")) {
482
+ const match = url.match(/^data:([^;]+);base64,(.+)$/);
483
+ if (match) {
484
+ return {
485
+ type: "image",
486
+ source: { type: "base64", media_type: match[1], data: match[2] }
487
+ };
488
+ }
489
+ }
490
+ return {
491
+ type: "image",
492
+ source: { type: "url", url }
493
+ };
494
+ }
495
+ return { type: "text", text: part.text };
496
+ });
497
+ anthropicMessages.push({ role: "user", content });
498
+ }
499
+ }
500
+ const body = {
501
+ model: request.model,
502
+ messages: anthropicMessages,
503
+ max_tokens: request.max_tokens || 4096,
504
+ stream: request.stream ?? false
505
+ };
506
+ if (request.temperature !== void 0) {
507
+ body.temperature = request.temperature;
508
+ }
509
+ if (systemContent !== void 0) {
510
+ body.system = systemContent;
511
+ }
512
+ if (request.tools?.length) {
513
+ body.tools = request.tools.map((tool) => ({
514
+ name: tool.function.name,
515
+ description: tool.function.description || "",
516
+ input_schema: tool.function.parameters
517
+ }));
518
+ }
519
+ const serverSideTools = request._serverSideTools;
520
+ if (serverSideTools?.length) {
521
+ body.tools = [...body.tools || [], ...serverSideTools];
522
+ }
523
+ if (request.tool_choice) {
524
+ if (typeof request.tool_choice === "string") {
525
+ if (request.tool_choice === "required") {
526
+ body.tool_choice = { type: "any" };
527
+ } else if (request.tool_choice !== "none") {
528
+ body.tool_choice = { type: request.tool_choice };
529
+ }
530
+ } else if (typeof request.tool_choice === "object" && "function" in request.tool_choice) {
531
+ body.tool_choice = { type: "tool", name: request.tool_choice.function.name };
532
+ }
533
+ }
534
+ if (request.reasoning?.enabled) {
535
+ const budgetMap = { low: 2048, medium: 8192, high: 32768 };
536
+ const budget = request.reasoning.max_tokens || budgetMap[request.reasoning.effort || "medium"] || 8192;
537
+ body.thinking = { type: "enabled", budget_tokens: budget };
538
+ body.temperature = 1;
539
+ }
540
+ return body;
541
+ }
542
+
543
+ // src/transformer/transformers/AnthropicResponseConversion.ts
544
+ function convertAnthropicResponseToOpenAI(anthropicResponse) {
545
+ const content = anthropicResponse.content || [];
546
+ const textParts = content.filter((c) => c.type === "text").map((c) => c.text);
547
+ const toolUses = content.filter((c) => c.type === "tool_use" || c.type === "server_tool_use");
548
+ const thinkingBlock = content.find((c) => c.type === "thinking");
549
+ const searchResults = content.filter((c) => c.type === "web_search_tool_result");
550
+ for (const sr of searchResults) {
551
+ const searches = sr.content;
552
+ if (searches?.length) {
553
+ const formatted = searches.map((s) => `[${s.title}](${s.url}): ${s.page_content || s.snippet || ""}`).join("\n");
554
+ textParts.push(`
555
+
556
+ **Search Results:**
557
+ ${formatted}`);
558
+ }
559
+ }
560
+ const message = {
561
+ role: "assistant",
562
+ content: textParts.join("") || null
563
+ };
564
+ if (toolUses.length > 0) {
565
+ message.tool_calls = toolUses.map((tc) => ({
566
+ id: tc.id,
567
+ type: "function",
568
+ function: {
569
+ name: tc.name,
570
+ arguments: JSON.stringify(tc.input || {})
571
+ }
572
+ }));
573
+ }
574
+ if (thinkingBlock) {
575
+ message.thinking = {
576
+ content: thinkingBlock.thinking,
577
+ signature: thinkingBlock.signature
578
+ };
579
+ }
580
+ const stopReasonMapping = {
581
+ end_turn: "stop",
582
+ max_tokens: "length",
583
+ tool_use: "tool_calls",
584
+ stop_sequence: "stop"
585
+ };
586
+ const usage = anthropicResponse.usage;
587
+ return {
588
+ id: anthropicResponse.id || `chatcmpl-${Date.now()}`,
589
+ object: "chat.completion",
590
+ created: Math.floor(Date.now() / 1e3),
591
+ model: anthropicResponse.model || "unknown",
592
+ choices: [{
593
+ index: 0,
594
+ message,
595
+ finish_reason: stopReasonMapping[anthropicResponse.stop_reason] || "stop"
596
+ }],
597
+ usage: usage ? {
598
+ prompt_tokens: usage.input_tokens || 0,
599
+ completion_tokens: usage.output_tokens || 0,
600
+ total_tokens: (usage.input_tokens || 0) + (usage.output_tokens || 0)
601
+ } : void 0
602
+ };
603
+ }
604
+ function convertOpenAIResponseToAnthropic2(openaiResponse) {
605
+ const choice = openaiResponse.choices?.[0];
606
+ if (!choice) {
607
+ throw new Error("No choices found in OpenAI response");
608
+ }
609
+ const message = choice.message;
610
+ const content = [];
611
+ if (message.content) {
612
+ content.push({
613
+ type: "text",
614
+ text: message.content
615
+ });
616
+ }
617
+ const toolCalls = message.tool_calls;
618
+ if (toolCalls?.length) {
619
+ for (const toolCall of toolCalls) {
620
+ const func = toolCall.function;
621
+ let parsedInput = {};
622
+ try {
623
+ const args = func.arguments;
624
+ parsedInput = typeof args === "string" ? JSON.parse(args) : args;
625
+ } catch {
626
+ parsedInput = { text: func.arguments || "" };
627
+ }
628
+ content.push({
629
+ type: "tool_use",
630
+ id: toolCall.id,
631
+ name: func.name,
632
+ input: parsedInput
633
+ });
634
+ }
635
+ }
636
+ const thinking = message.thinking;
637
+ if (thinking?.content) {
638
+ content.push({
639
+ type: "thinking",
640
+ thinking: thinking.content,
641
+ signature: thinking.signature
642
+ });
643
+ }
644
+ const finishReason = choice.finish_reason;
645
+ const stopReasonMapping = {
646
+ stop: "end_turn",
647
+ length: "max_tokens",
648
+ tool_calls: "tool_use",
649
+ content_filter: "stop_sequence"
650
+ };
651
+ const usage = openaiResponse.usage;
652
+ const usageDetails = usage?.prompt_tokens_details;
653
+ return {
654
+ id: openaiResponse.id,
655
+ type: "message",
656
+ role: "assistant",
657
+ model: openaiResponse.model,
658
+ content,
659
+ stop_reason: stopReasonMapping[finishReason] || "end_turn",
660
+ stop_sequence: null,
661
+ usage: {
662
+ input_tokens: (usage?.prompt_tokens || 0) - (usageDetails?.cached_tokens || 0),
663
+ output_tokens: usage?.completion_tokens || 0,
664
+ cache_read_input_tokens: usageDetails?.cached_tokens || 0
665
+ }
666
+ };
667
+ }
668
+
669
+ // src/transformer/transformers/AnthropicConversion.ts
670
+ function transformAnthropicRequestToUnified(request) {
671
+ const anthropicRequest = request;
672
+ const messages = [];
673
+ if (anthropicRequest.system) {
674
+ if (typeof anthropicRequest.system === "string") {
675
+ messages.push({
676
+ role: "system",
677
+ content: anthropicRequest.system
678
+ });
679
+ } else if (Array.isArray(anthropicRequest.system)) {
680
+ const textParts = anthropicRequest.system.filter((item) => item.type === "text" && item.text).map((item) => ({
681
+ type: "text",
682
+ text: item.text,
683
+ cache_control: item.cache_control
684
+ }));
685
+ if (textParts.length > 0) {
686
+ messages.push({
687
+ role: "system",
688
+ content: textParts
689
+ });
690
+ }
691
+ }
692
+ }
693
+ const requestMessages = JSON.parse(JSON.stringify(anthropicRequest.messages || []));
694
+ for (const msg of requestMessages) {
695
+ if (msg.role !== "user" && msg.role !== "assistant") continue;
696
+ if (typeof msg.content === "string") {
697
+ messages.push({
698
+ role: msg.role,
699
+ content: msg.content
700
+ });
701
+ continue;
702
+ }
703
+ if (Array.isArray(msg.content)) {
704
+ if (msg.role === "user") {
705
+ const toolParts = msg.content.filter(
706
+ (c) => c.type === "tool_result" && c.tool_use_id
707
+ );
708
+ for (const tool of toolParts) {
709
+ messages.push({
710
+ role: "tool",
711
+ content: typeof tool.content === "string" ? tool.content : JSON.stringify(tool.content),
712
+ tool_call_id: tool.tool_use_id,
713
+ cache_control: tool.cache_control
714
+ });
715
+ }
716
+ const textAndMediaParts = msg.content.filter(
717
+ (c) => c.type === "text" && c.text || c.type === "image" && c.source
718
+ );
719
+ if (textAndMediaParts.length > 0) {
720
+ messages.push({
721
+ role: "user",
722
+ content: textAndMediaParts.map((part) => {
723
+ if (part.type === "image") {
724
+ const imagePart = part;
725
+ return {
726
+ type: "image_url",
727
+ image_url: {
728
+ url: imagePart.source.type === "base64" ? formatBase64(imagePart.source.data || "", imagePart.source.media_type) : imagePart.source.url || ""
729
+ },
730
+ media_type: imagePart.source.media_type
731
+ };
732
+ }
733
+ return {
734
+ type: "text",
735
+ text: part.text
736
+ };
737
+ })
738
+ });
739
+ }
740
+ } else if (msg.role === "assistant") {
741
+ const assistantMessage = {
742
+ role: "assistant",
743
+ content: ""
744
+ };
745
+ const textParts = msg.content.filter(
746
+ (c) => c.type === "text" && c.text
747
+ );
748
+ if (textParts.length > 0) {
749
+ assistantMessage.content = textParts.map((t) => t.text).join("\n");
750
+ }
751
+ const toolCallParts = msg.content.filter(
752
+ (c) => c.type === "tool_use" && c.id
753
+ );
754
+ if (toolCallParts.length > 0) {
755
+ assistantMessage.tool_calls = toolCallParts.map((tool) => ({
756
+ id: tool.id,
757
+ type: "function",
758
+ function: {
759
+ name: tool.name,
760
+ arguments: JSON.stringify(tool.input || {})
761
+ }
762
+ }));
763
+ }
764
+ const thinkingPart = msg.content.find(
765
+ (c) => c.type === "thinking"
766
+ );
767
+ if (thinkingPart?.thinking) {
768
+ assistantMessage.thinking = {
769
+ content: thinkingPart.thinking,
770
+ signature: thinkingPart.signature
771
+ };
772
+ }
773
+ messages.push(assistantMessage);
774
+ }
775
+ }
776
+ }
777
+ const rawTools = anthropicRequest.tools || [];
778
+ const serverSideTools = rawTools.filter((t) => isServerSideTool(t));
779
+ const functionTools = rawTools.length > 0 ? convertAnthropicToolsToOpenAI(rawTools) : void 0;
780
+ const result = {
781
+ messages,
782
+ model: anthropicRequest.model,
783
+ max_tokens: anthropicRequest.max_tokens,
784
+ temperature: anthropicRequest.temperature,
785
+ stream: anthropicRequest.stream,
786
+ tools: functionTools?.length ? functionTools : void 0
787
+ };
788
+ if (serverSideTools.length > 0) {
789
+ result._serverSideTools = serverSideTools;
790
+ }
791
+ if (anthropicRequest.thinking) {
792
+ result.reasoning = {
793
+ effort: getThinkLevel(anthropicRequest.thinking.budget_tokens),
794
+ enabled: anthropicRequest.thinking.type === "enabled"
795
+ };
796
+ }
797
+ if (anthropicRequest.tool_choice) {
798
+ if (anthropicRequest.tool_choice.type === "tool" && anthropicRequest.tool_choice.name) {
799
+ result.tool_choice = {
800
+ type: "function",
801
+ function: { name: anthropicRequest.tool_choice.name }
802
+ };
803
+ } else {
804
+ result.tool_choice = anthropicRequest.tool_choice.type;
805
+ }
806
+ }
807
+ return result;
808
+ }
809
+
810
+ // src/transformer/transformers/AnthropicAnthropicToOpenAIStream.ts
811
+ function convertAnthropicStreamToOpenAI2(anthropicStream, logger) {
812
+ const decoder = new TextDecoder();
813
+ const encoder = new TextEncoder();
814
+ const activeToolCalls = /* @__PURE__ */ new Map();
815
+ let toolCallCounter = 0;
816
+ let model = "unknown";
817
+ let messageId = `chatcmpl-${Date.now()}`;
818
+ return new ReadableStream({
819
+ start: async (controller) => {
820
+ const reader = anthropicStream.getReader();
821
+ let buffer = "";
822
+ let isClosed = false;
823
+ const safeEnqueue = (data) => {
824
+ if (!isClosed) {
825
+ try {
826
+ controller.enqueue(encoder.encode(data));
827
+ } catch {
828
+ isClosed = true;
829
+ }
830
+ }
831
+ };
832
+ try {
833
+ while (true) {
834
+ const { done, value } = await reader.read();
835
+ if (done) break;
836
+ buffer += decoder.decode(value, { stream: true });
837
+ const lines = buffer.split("\n");
838
+ buffer = lines.pop() || "";
839
+ for (const line of lines) {
840
+ if (isClosed) break;
841
+ if (!line.startsWith("data:")) continue;
842
+ const data = line.slice(5).trim();
843
+ if (!data || data === "[DONE]") continue;
844
+ try {
845
+ const event = JSON.parse(data);
846
+ if (event.type === "message_start" && event.message) {
847
+ model = event.message.model || model;
848
+ messageId = event.message.id || messageId;
849
+ }
850
+ if (event.type === "content_block_delta" && event.delta) {
851
+ const chunk = {
852
+ id: messageId,
853
+ object: "chat.completion.chunk",
854
+ created: Math.floor(Date.now() / 1e3),
855
+ model,
856
+ choices: [{ index: 0, delta: {}, finish_reason: null }]
857
+ };
858
+ const delta = chunk.choices[0].delta;
859
+ if (event.delta.type === "text_delta") {
860
+ delta.content = event.delta.text;
861
+ } else if (event.delta.type === "input_json_delta") {
862
+ const toolInfo = activeToolCalls.get(event.index);
863
+ if (toolInfo) {
864
+ delta.tool_calls = [{
865
+ index: toolInfo.index,
866
+ function: { arguments: event.delta.partial_json }
867
+ }];
868
+ }
869
+ } else if (event.delta.type === "thinking_delta") {
870
+ delta.thinking = { content: event.delta.thinking };
871
+ } else if (event.delta.type === "signature_delta") {
872
+ delta.thinking = { signature: event.delta.signature };
873
+ } else {
874
+ continue;
875
+ }
876
+ safeEnqueue(`data: ${JSON.stringify(chunk)}
877
+
878
+ `);
879
+ }
880
+ if (event.type === "content_block_start" && event.content_block) {
881
+ if (event.content_block.type === "tool_use" || event.content_block.type === "server_tool_use") {
882
+ const tcIndex = toolCallCounter++;
883
+ activeToolCalls.set(event.index, {
884
+ id: event.content_block.id,
885
+ name: event.content_block.name,
886
+ index: tcIndex
887
+ });
888
+ const chunk = {
889
+ id: messageId,
890
+ object: "chat.completion.chunk",
891
+ created: Math.floor(Date.now() / 1e3),
892
+ model,
893
+ choices: [{
894
+ index: 0,
895
+ delta: {
896
+ tool_calls: [{
897
+ index: tcIndex,
898
+ id: event.content_block.id,
899
+ type: "function",
900
+ function: { name: event.content_block.name, arguments: "" }
901
+ }]
902
+ },
903
+ finish_reason: null
904
+ }]
905
+ };
906
+ safeEnqueue(`data: ${JSON.stringify(chunk)}
907
+
908
+ `);
909
+ } else if (event.content_block.type === "web_search_tool_result") {
910
+ const searches = event.content_block.content;
911
+ if (searches?.length) {
912
+ const formatted = searches.map((s) => `[${s.title}](${s.url}): ${s.page_content || s.snippet || ""}`).join("\n");
913
+ const chunk = {
914
+ id: messageId,
915
+ object: "chat.completion.chunk",
916
+ created: Math.floor(Date.now() / 1e3),
917
+ model,
918
+ choices: [{
919
+ index: 0,
920
+ delta: { content: `
921
+
922
+ **Search Results:**
923
+ ${formatted}` },
924
+ finish_reason: null
925
+ }]
926
+ };
927
+ safeEnqueue(`data: ${JSON.stringify(chunk)}
928
+
929
+ `);
930
+ }
931
+ }
932
+ }
933
+ if (event.type === "message_delta" && event.delta) {
934
+ const stopReasonMapping = {
935
+ end_turn: "stop",
936
+ max_tokens: "length",
937
+ tool_use: "tool_calls",
938
+ stop_sequence: "stop"
939
+ };
940
+ const chunk = {
941
+ id: messageId,
942
+ object: "chat.completion.chunk",
943
+ created: Math.floor(Date.now() / 1e3),
944
+ model,
945
+ choices: [{
946
+ index: 0,
947
+ delta: {},
948
+ finish_reason: stopReasonMapping[event.delta.stop_reason] || "stop"
949
+ }]
950
+ };
951
+ if (event.usage) {
952
+ chunk.usage = {
953
+ prompt_tokens: event.usage.input_tokens || 0,
954
+ completion_tokens: event.usage.output_tokens || 0,
955
+ total_tokens: (event.usage.input_tokens || 0) + (event.usage.output_tokens || 0)
956
+ };
957
+ }
958
+ safeEnqueue(`data: ${JSON.stringify(chunk)}
959
+
960
+ `);
961
+ }
962
+ } catch (e) {
963
+ logger?.error("Error parsing Anthropic stream event:", e);
964
+ }
965
+ }
966
+ }
967
+ } catch (e) {
968
+ if (!isClosed) {
969
+ controller.error(e);
970
+ }
971
+ } finally {
972
+ safeEnqueue("data: [DONE]\n\n");
973
+ if (!isClosed) {
974
+ try {
975
+ controller.close();
976
+ } catch {
977
+ }
978
+ }
979
+ reader.releaseLock();
980
+ }
981
+ }
982
+ });
983
+ }
984
+
985
+ // src/transformer/transformers/AnthropicOpenAIToAnthropicStream.ts
986
+ function convertOpenAIStreamToAnthropic2(openaiStream, _context, logger) {
987
+ const decoder = new TextDecoder();
988
+ const encoder = new TextEncoder();
989
+ let hasStarted = false;
990
+ let hasTextContentStarted = false;
991
+ let isThinkingStarted = false;
992
+ let contentIndex = 0;
993
+ let currentContentBlockIndex = -1;
994
+ const toolCallIndexToContentBlockIndex = /* @__PURE__ */ new Map();
995
+ return new ReadableStream({
996
+ start: async (controller) => {
997
+ const reader = openaiStream.getReader();
998
+ let buffer = "";
999
+ const messageId = `msg_${Date.now()}`;
1000
+ let model = "unknown";
1001
+ let isClosed = false;
1002
+ let stopReasonDelta = null;
1003
+ const safeEnqueue = (data) => {
1004
+ if (!isClosed) {
1005
+ try {
1006
+ controller.enqueue(encoder.encode(data));
1007
+ } catch (_e) {
1008
+ isClosed = true;
1009
+ }
1010
+ }
1011
+ };
1012
+ const assignContentBlockIndex = () => {
1013
+ return contentIndex++;
1014
+ };
1015
+ const safeClose = () => {
1016
+ if (isClosed) return;
1017
+ if (currentContentBlockIndex >= 0) {
1018
+ safeEnqueue(`event: content_block_stop
1019
+ data: ${JSON.stringify({
1020
+ type: "content_block_stop",
1021
+ index: currentContentBlockIndex
1022
+ })}
1023
+
1024
+ `);
1025
+ }
1026
+ if (stopReasonDelta) {
1027
+ safeEnqueue(`event: message_delta
1028
+ data: ${JSON.stringify(stopReasonDelta)}
1029
+
1030
+ `);
1031
+ } else {
1032
+ safeEnqueue(`event: message_delta
1033
+ data: ${JSON.stringify({
1034
+ type: "message_delta",
1035
+ delta: { stop_reason: "end_turn", stop_sequence: null },
1036
+ usage: { input_tokens: 0, output_tokens: 0 }
1037
+ })}
1038
+
1039
+ `);
1040
+ }
1041
+ safeEnqueue(`event: message_stop
1042
+ data: ${JSON.stringify({ type: "message_stop" })}
1043
+
1044
+ `);
1045
+ try {
1046
+ controller.close();
1047
+ } catch (_e) {
1048
+ }
1049
+ isClosed = true;
1050
+ };
1051
+ try {
1052
+ while (true) {
1053
+ const { done, value } = await reader.read();
1054
+ if (done) break;
1055
+ buffer += decoder.decode(value, { stream: true });
1056
+ const lines = buffer.split("\n");
1057
+ buffer = lines.pop() || "";
1058
+ for (const line of lines) {
1059
+ if (isClosed) break;
1060
+ if (!line.startsWith("data:")) continue;
1061
+ const data = line.slice(5).trim();
1062
+ if (data === "[DONE]") continue;
1063
+ try {
1064
+ const chunk = JSON.parse(data);
1065
+ if (chunk.error) {
1066
+ safeEnqueue(`event: error
1067
+ data: ${JSON.stringify({
1068
+ type: "error",
1069
+ message: { type: "api_error", message: JSON.stringify(chunk.error) }
1070
+ })}
1071
+
1072
+ `);
1073
+ continue;
1074
+ }
1075
+ model = chunk.model || model;
1076
+ if (!hasStarted) {
1077
+ hasStarted = true;
1078
+ safeEnqueue(`event: message_start
1079
+ data: ${JSON.stringify({
1080
+ type: "message_start",
1081
+ message: {
1082
+ id: messageId,
1083
+ type: "message",
1084
+ role: "assistant",
1085
+ content: [],
1086
+ model,
1087
+ stop_reason: null,
1088
+ stop_sequence: null,
1089
+ usage: { input_tokens: 0, output_tokens: 0 }
1090
+ }
1091
+ })}
1092
+
1093
+ `);
1094
+ }
1095
+ const choice = chunk.choices?.[0];
1096
+ if (!choice) continue;
1097
+ if (chunk.usage) {
1098
+ stopReasonDelta = {
1099
+ type: "message_delta",
1100
+ delta: { stop_reason: "end_turn", stop_sequence: null },
1101
+ usage: {
1102
+ input_tokens: (chunk.usage.prompt_tokens || 0) - (chunk.usage.prompt_tokens_details?.cached_tokens || 0),
1103
+ output_tokens: chunk.usage.completion_tokens || 0,
1104
+ cache_read_input_tokens: chunk.usage.prompt_tokens_details?.cached_tokens || 0
1105
+ }
1106
+ };
1107
+ }
1108
+ if (choice.delta?.thinking) {
1109
+ if (!isThinkingStarted) {
1110
+ const thinkingBlockIndex = assignContentBlockIndex();
1111
+ safeEnqueue(`event: content_block_start
1112
+ data: ${JSON.stringify({
1113
+ type: "content_block_start",
1114
+ index: thinkingBlockIndex,
1115
+ content_block: { type: "thinking", thinking: "" }
1116
+ })}
1117
+
1118
+ `);
1119
+ currentContentBlockIndex = thinkingBlockIndex;
1120
+ isThinkingStarted = true;
1121
+ }
1122
+ if (choice.delta.thinking.signature) {
1123
+ safeEnqueue(`event: content_block_delta
1124
+ data: ${JSON.stringify({
1125
+ type: "content_block_delta",
1126
+ index: currentContentBlockIndex,
1127
+ delta: { type: "signature_delta", signature: choice.delta.thinking.signature }
1128
+ })}
1129
+
1130
+ `);
1131
+ safeEnqueue(`event: content_block_stop
1132
+ data: ${JSON.stringify({
1133
+ type: "content_block_stop",
1134
+ index: currentContentBlockIndex
1135
+ })}
1136
+
1137
+ `);
1138
+ currentContentBlockIndex = -1;
1139
+ } else if (choice.delta.thinking.content) {
1140
+ safeEnqueue(`event: content_block_delta
1141
+ data: ${JSON.stringify({
1142
+ type: "content_block_delta",
1143
+ index: currentContentBlockIndex,
1144
+ delta: { type: "thinking_delta", thinking: choice.delta.thinking.content }
1145
+ })}
1146
+
1147
+ `);
1148
+ }
1149
+ }
1150
+ if (choice.delta?.content) {
1151
+ if (!hasTextContentStarted) {
1152
+ if (currentContentBlockIndex >= 0 && isThinkingStarted) {
1153
+ safeEnqueue(`event: content_block_stop
1154
+ data: ${JSON.stringify({
1155
+ type: "content_block_stop",
1156
+ index: currentContentBlockIndex
1157
+ })}
1158
+
1159
+ `);
1160
+ }
1161
+ hasTextContentStarted = true;
1162
+ const textBlockIndex = assignContentBlockIndex();
1163
+ safeEnqueue(`event: content_block_start
1164
+ data: ${JSON.stringify({
1165
+ type: "content_block_start",
1166
+ index: textBlockIndex,
1167
+ content_block: { type: "text", text: "" }
1168
+ })}
1169
+
1170
+ `);
1171
+ currentContentBlockIndex = textBlockIndex;
1172
+ }
1173
+ safeEnqueue(`event: content_block_delta
1174
+ data: ${JSON.stringify({
1175
+ type: "content_block_delta",
1176
+ index: currentContentBlockIndex,
1177
+ delta: { type: "text_delta", text: choice.delta.content }
1178
+ })}
1179
+
1180
+ `);
1181
+ }
1182
+ if (choice.delta?.tool_calls) {
1183
+ for (const toolCall of choice.delta.tool_calls) {
1184
+ const toolCallIndex = toolCall.index ?? 0;
1185
+ if (!toolCallIndexToContentBlockIndex.has(toolCallIndex)) {
1186
+ if (currentContentBlockIndex >= 0) {
1187
+ safeEnqueue(`event: content_block_stop
1188
+ data: ${JSON.stringify({
1189
+ type: "content_block_stop",
1190
+ index: currentContentBlockIndex
1191
+ })}
1192
+
1193
+ `);
1194
+ hasTextContentStarted = false;
1195
+ }
1196
+ const newBlockIndex = assignContentBlockIndex();
1197
+ toolCallIndexToContentBlockIndex.set(toolCallIndex, newBlockIndex);
1198
+ safeEnqueue(`event: content_block_start
1199
+ data: ${JSON.stringify({
1200
+ type: "content_block_start",
1201
+ index: newBlockIndex,
1202
+ content_block: {
1203
+ type: "tool_use",
1204
+ id: toolCall.id || `call_${Date.now()}_${toolCallIndex}`,
1205
+ name: toolCall.function?.name || `tool_${toolCallIndex}`,
1206
+ input: {}
1207
+ }
1208
+ })}
1209
+
1210
+ `);
1211
+ currentContentBlockIndex = newBlockIndex;
1212
+ }
1213
+ if (toolCall.function?.arguments) {
1214
+ const blockIndex = toolCallIndexToContentBlockIndex.get(toolCallIndex);
1215
+ if (blockIndex !== void 0) {
1216
+ safeEnqueue(`event: content_block_delta
1217
+ data: ${JSON.stringify({
1218
+ type: "content_block_delta",
1219
+ index: blockIndex,
1220
+ delta: { type: "input_json_delta", partial_json: toolCall.function.arguments }
1221
+ })}
1222
+
1223
+ `);
1224
+ }
1225
+ }
1226
+ }
1227
+ }
1228
+ if (choice.finish_reason) {
1229
+ const stopReasonMapping = {
1230
+ stop: "end_turn",
1231
+ length: "max_tokens",
1232
+ tool_calls: "tool_use",
1233
+ content_filter: "stop_sequence"
1234
+ };
1235
+ stopReasonDelta = {
1236
+ type: "message_delta",
1237
+ delta: {
1238
+ stop_reason: stopReasonMapping[choice.finish_reason] || "end_turn",
1239
+ stop_sequence: null
1240
+ },
1241
+ usage: {
1242
+ input_tokens: (chunk.usage?.prompt_tokens || 0) - (chunk.usage?.prompt_tokens_details?.cached_tokens || 0),
1243
+ output_tokens: chunk.usage?.completion_tokens || 0,
1244
+ cache_read_input_tokens: chunk.usage?.prompt_tokens_details?.cached_tokens || 0
1245
+ }
1246
+ };
1247
+ }
1248
+ } catch (e) {
1249
+ logger?.error("Error parsing stream chunk:", e);
1250
+ }
1251
+ }
1252
+ }
1253
+ } catch (e) {
1254
+ if (!isClosed) {
1255
+ controller.error(e);
1256
+ }
1257
+ } finally {
1258
+ safeClose();
1259
+ reader.releaseLock();
1260
+ }
1261
+ }
1262
+ });
1263
+ }
1264
+
1265
+ // src/transformer/transformers/AnthropicTransformer.ts
1266
+ var AnthropicTransformer = class {
1267
+ static TransformerName = "anthropic";
1268
+ name = "anthropic";
1269
+ logger;
1270
+ endPoint = "/v1/messages";
1271
+ useBearer;
1272
+ constructor(options) {
1273
+ this.useBearer = options?.UseBearer ?? false;
1274
+ }
1275
+ /**
1276
+ * Handle authentication - Anthropic uses x-api-key header
1277
+ */
1278
+ async auth(request, provider, _context) {
1279
+ const headers = {};
1280
+ if (this.useBearer) {
1281
+ headers["authorization"] = `Bearer ${provider.apiKey}`;
1282
+ headers["x-api-key"] = void 0;
1283
+ } else {
1284
+ headers["x-api-key"] = provider.apiKey;
1285
+ headers["authorization"] = void 0;
1286
+ }
1287
+ return {
1288
+ body: request,
1289
+ config: { headers }
1290
+ };
1291
+ }
1292
+ /**
1293
+ * Transform Anthropic request to unified format.
1294
+ */
1295
+ async transformRequestOut(request, _context) {
1296
+ return transformAnthropicRequestToUnified(request);
1297
+ }
1298
+ /**
1299
+ * Transform OpenAI/unified response back to Anthropic format
1300
+ * (auto-detects stream vs JSON via Content-Type).
1301
+ */
1302
+ async transformResponseIn(response, context) {
1303
+ const contentType = response.headers.get("Content-Type") ?? "";
1304
+ if (contentType.includes("text/event-stream")) {
1305
+ if (!response.body) {
1306
+ throw new Error("Stream response body is null");
1307
+ }
1308
+ const convertedStream = convertOpenAIStreamToAnthropic2(response.body, context, this.logger);
1309
+ return new Response(convertedStream, {
1310
+ headers: {
1311
+ "Content-Type": "text/event-stream",
1312
+ "Cache-Control": "no-cache",
1313
+ Connection: "keep-alive"
1314
+ }
1315
+ });
1316
+ } else {
1317
+ const data = await response.json();
1318
+ const anthropicResponse = convertOpenAIResponseToAnthropic2(data);
1319
+ return new Response(JSON.stringify(anthropicResponse), {
1320
+ headers: { "Content-Type": "application/json" }
1321
+ });
1322
+ }
1323
+ }
1324
+ /**
1325
+ * Transform unified request to Anthropic Messages API format.
1326
+ * This is the reverse of transformRequestOut — converts OpenAI/unified format
1327
+ * to Anthropic's expected request body structure.
1328
+ */
1329
+ async transformRequestIn(request, _provider, _context) {
1330
+ return buildAnthropicRequestBody(request);
1331
+ }
1332
+ /**
1333
+ * Transform Anthropic response to OpenAI/unified format
1334
+ * (auto-detects stream vs JSON via Content-Type).
1335
+ */
1336
+ async transformResponseOut(response, _context) {
1337
+ const contentType = response.headers.get("Content-Type") ?? "";
1338
+ if (contentType.includes("text/event-stream")) {
1339
+ if (!response.body) {
1340
+ throw new Error("Stream response body is null");
1341
+ }
1342
+ const convertedStream = convertAnthropicStreamToOpenAI2(response.body, this.logger);
1343
+ return new Response(convertedStream, {
1344
+ headers: {
1345
+ "Content-Type": "text/event-stream",
1346
+ "Cache-Control": "no-cache",
1347
+ Connection: "keep-alive"
1348
+ }
1349
+ });
1350
+ } else {
1351
+ const data = await response.json();
1352
+ const openaiResponse = convertAnthropicResponseToOpenAI(data);
1353
+ return new Response(JSON.stringify(openaiResponse), {
1354
+ headers: { "Content-Type": "application/json" }
1355
+ });
1356
+ }
1357
+ }
1358
+ };
1359
+
1360
+ // src/transformer/transformers/utils/gemini.response-in.ts
1361
+ var FINISH_REASON_TO_GEMINI = {
1362
+ stop: "STOP",
1363
+ length: "MAX_TOKENS",
1364
+ tool_calls: "STOP",
1365
+ content_filter: "SAFETY",
1366
+ // Already-lowercased Gemini reasons (pass-through from another transformer)
1367
+ max_tokens: "MAX_TOKENS",
1368
+ safety: "SAFETY"
1369
+ };
1370
+ function toGeminiFinishReason(openaiReason) {
1371
+ if (!openaiReason) return null;
1372
+ return FINISH_REASON_TO_GEMINI[openaiReason] || openaiReason.toUpperCase();
1373
+ }
1374
+ function convertOpenAIResponseToGemini(openaiData) {
1375
+ const choice = openaiData.choices?.[0];
1376
+ const message = choice?.message ?? {};
1377
+ const usage = openaiData.usage;
1378
+ const usagePromptDetails = usage?.prompt_tokens_details;
1379
+ const usageOutputDetails = usage?.output_tokens_details;
1380
+ const parts = [];
1381
+ const thinking = message.thinking;
1382
+ if (thinking?.content) {
1383
+ parts.push({ text: thinking.content, thought: true });
1384
+ }
1385
+ if (thinking?.signature) {
1386
+ parts.push({ thoughtSignature: thinking.signature });
1387
+ }
1388
+ if (message.content) {
1389
+ parts.push({ text: message.content });
1390
+ }
1391
+ const toolCalls = message.tool_calls;
1392
+ if (toolCalls?.length) {
1393
+ for (const tc of toolCalls) {
1394
+ const func = tc.function;
1395
+ let args = {};
1396
+ try {
1397
+ const raw = func.arguments;
1398
+ args = typeof raw === "string" ? JSON.parse(raw) : raw;
1399
+ } catch {
1400
+ args = {};
1401
+ }
1402
+ parts.push({
1403
+ functionCall: {
1404
+ id: tc.id,
1405
+ name: func.name,
1406
+ args
1407
+ }
1408
+ });
1409
+ }
1410
+ }
1411
+ if (parts.length === 0) {
1412
+ parts.push({ text: "" });
1413
+ }
1414
+ return {
1415
+ responseId: openaiData.id || "",
1416
+ modelVersion: openaiData.model || "",
1417
+ candidates: [{
1418
+ content: { parts },
1419
+ finishReason: toGeminiFinishReason(choice?.finish_reason)
1420
+ }],
1421
+ usageMetadata: usage ? {
1422
+ promptTokenCount: usage.prompt_tokens || 0,
1423
+ candidatesTokenCount: usage.completion_tokens || 0,
1424
+ totalTokenCount: usage.total_tokens || 0,
1425
+ cachedContentTokenCount: usagePromptDetails?.cached_tokens || 0,
1426
+ thoughtsTokenCount: usageOutputDetails?.reasoning_tokens || 0
1427
+ } : void 0
1428
+ };
1429
+ }
1430
+ function convertOpenAIStreamToGemini(openaiStream, logger) {
1431
+ const decoder = new TextDecoder();
1432
+ const encoder = new TextEncoder();
1433
+ const pendingToolCalls = /* @__PURE__ */ new Map();
1434
+ let model = "";
1435
+ let responseId = "";
1436
+ return new ReadableStream({
1437
+ start: async (controller) => {
1438
+ const reader = openaiStream.getReader();
1439
+ let buffer = "";
1440
+ let isClosed = false;
1441
+ const emit2 = (data) => {
1442
+ if (isClosed) return;
1443
+ try {
1444
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
1445
+
1446
+ `));
1447
+ } catch {
1448
+ isClosed = true;
1449
+ }
1450
+ };
1451
+ const flushToolCalls = () => {
1452
+ if (pendingToolCalls.size === 0) return;
1453
+ const parts = [];
1454
+ for (const tc of pendingToolCalls.values()) {
1455
+ let args = {};
1456
+ try {
1457
+ args = JSON.parse(tc.args || "{}");
1458
+ } catch {
1459
+ }
1460
+ parts.push({
1461
+ functionCall: { id: tc.id, name: tc.name, args }
1462
+ });
1463
+ }
1464
+ pendingToolCalls.clear();
1465
+ emit2({
1466
+ responseId,
1467
+ modelVersion: model,
1468
+ candidates: [{ content: { parts }, finishReason: null }]
1469
+ });
1470
+ };
1471
+ try {
1472
+ while (true) {
1473
+ const { done, value } = await reader.read();
1474
+ if (done) break;
1475
+ buffer += decoder.decode(value, { stream: true });
1476
+ const lines = buffer.split("\n");
1477
+ buffer = lines.pop() || "";
1478
+ for (const line of lines) {
1479
+ if (isClosed) break;
1480
+ if (!line.startsWith("data:")) continue;
1481
+ const data = line.slice(5).trim();
1482
+ if (!data || data === "[DONE]") continue;
1483
+ try {
1484
+ const chunk = JSON.parse(data);
1485
+ if (!responseId && chunk.id) responseId = chunk.id;
1486
+ if (!model && chunk.model) model = chunk.model;
1487
+ const choice = chunk.choices?.[0];
1488
+ if (!choice) continue;
1489
+ const delta = choice.delta ?? {};
1490
+ if (delta.thinking) {
1491
+ const parts = [];
1492
+ if (delta.thinking.content) {
1493
+ parts.push({ text: delta.thinking.content, thought: true });
1494
+ }
1495
+ if (delta.thinking.signature) {
1496
+ parts.push({ thoughtSignature: delta.thinking.signature });
1497
+ }
1498
+ if (parts.length > 0) {
1499
+ emit2({
1500
+ responseId,
1501
+ modelVersion: model,
1502
+ candidates: [{ content: { parts }, finishReason: null }]
1503
+ });
1504
+ }
1505
+ }
1506
+ if (delta.content) {
1507
+ emit2({
1508
+ responseId,
1509
+ modelVersion: model,
1510
+ candidates: [{
1511
+ content: { parts: [{ text: delta.content }] },
1512
+ finishReason: null
1513
+ }]
1514
+ });
1515
+ }
1516
+ if (delta.tool_calls) {
1517
+ for (const tc of delta.tool_calls) {
1518
+ const idx = tc.index ?? 0;
1519
+ const existing = pendingToolCalls.get(idx);
1520
+ const func = tc.function;
1521
+ if (existing) {
1522
+ if (func?.arguments) {
1523
+ existing.args += func.arguments;
1524
+ }
1525
+ } else {
1526
+ pendingToolCalls.set(idx, {
1527
+ id: tc.id || `tool_${Date.now()}_${idx}`,
1528
+ name: func?.name || "",
1529
+ args: func?.arguments || ""
1530
+ });
1531
+ }
1532
+ }
1533
+ }
1534
+ if (choice.finish_reason) {
1535
+ flushToolCalls();
1536
+ const geminiUsage = chunk.usage ? {
1537
+ promptTokenCount: chunk.usage.prompt_tokens || 0,
1538
+ candidatesTokenCount: chunk.usage.completion_tokens || 0,
1539
+ totalTokenCount: chunk.usage.total_tokens || 0,
1540
+ cachedContentTokenCount: chunk.usage.prompt_tokens_details?.cached_tokens || 0,
1541
+ thoughtsTokenCount: chunk.usage.output_tokens_details?.reasoning_tokens || 0
1542
+ } : void 0;
1543
+ emit2({
1544
+ responseId,
1545
+ modelVersion: model,
1546
+ candidates: [{
1547
+ content: { parts: [{ text: "" }] },
1548
+ finishReason: toGeminiFinishReason(choice.finish_reason) || "STOP"
1549
+ }],
1550
+ usageMetadata: geminiUsage
1551
+ });
1552
+ }
1553
+ } catch (e) {
1554
+ logger?.error(`Error parsing OpenAI stream chunk for Gemini conversion: ${e}`);
1555
+ }
1556
+ }
1557
+ }
1558
+ flushToolCalls();
1559
+ } catch (e) {
1560
+ if (!isClosed) controller.error(e);
1561
+ } finally {
1562
+ if (!isClosed) {
1563
+ try {
1564
+ controller.close();
1565
+ } catch {
1566
+ }
1567
+ }
1568
+ reader.releaseLock();
1569
+ }
1570
+ }
1571
+ });
1572
+ }
1573
+ async function transformResponseIn(response, logger) {
1574
+ const contentType = response.headers.get("Content-Type") ?? "";
1575
+ if (contentType.includes("text/event-stream")) {
1576
+ if (!response.body) {
1577
+ throw new Error("Stream response body is null");
1578
+ }
1579
+ const geminiStream = convertOpenAIStreamToGemini(response.body, logger);
1580
+ return new Response(geminiStream, {
1581
+ headers: {
1582
+ "Content-Type": "text/event-stream",
1583
+ "Cache-Control": "no-cache",
1584
+ Connection: "keep-alive"
1585
+ }
1586
+ });
1587
+ }
1588
+ const data = await response.json();
1589
+ const geminiResponse = convertOpenAIResponseToGemini(data);
1590
+ return new Response(JSON.stringify(geminiResponse), {
1591
+ status: response.status,
1592
+ statusText: response.statusText,
1593
+ headers: { "Content-Type": "application/json" }
1594
+ });
1595
+ }
1596
+
1597
+ // src/transformer/transformers/utils/gemini.stream.ts
1598
+ async function transformResponseOut(response, providerName, logger) {
1599
+ const contentType = response.headers.get("Content-Type") ?? "";
1600
+ if (contentType.includes("application/json")) {
1601
+ return handleJsonResponse(response, providerName, logger);
1602
+ } else if (contentType.includes("stream") || contentType.includes("text/event-stream")) {
1603
+ return handleStreamResponse(response, providerName, logger);
1604
+ }
1605
+ return response;
1606
+ }
1607
+ async function handleJsonResponse(response, providerName, logger) {
1608
+ const jsonResponse = await response.json();
1609
+ logger?.debug(`${providerName} JSON response received`);
1610
+ const parts = jsonResponse.candidates?.[0]?.content?.parts || [];
1611
+ let thinkingContent = "";
1612
+ let thinkingSignature = "";
1613
+ const nonThinkingParts = [];
1614
+ for (const part of parts) {
1615
+ if (part.text && part.thought === true) {
1616
+ thinkingContent += part.text;
1617
+ } else {
1618
+ nonThinkingParts.push(part);
1619
+ }
1620
+ }
1621
+ thinkingSignature = parts.find((part) => part.thoughtSignature)?.thoughtSignature ?? "";
1622
+ const toolCalls = nonThinkingParts.filter((part) => part.functionCall).map((part) => ({
1623
+ id: part.functionCall?.id || `tool_${Math.random().toString(36).substring(2, 15)}`,
1624
+ type: "function",
1625
+ function: {
1626
+ name: part.functionCall?.name ?? "",
1627
+ arguments: JSON.stringify(part.functionCall?.args || {})
1628
+ }
1629
+ }));
1630
+ const textContent = nonThinkingParts.filter((part) => part.text).map((part) => part.text).join("\n");
1631
+ const openAIResponse = {
1632
+ id: jsonResponse.responseId ?? "",
1633
+ choices: [
1634
+ {
1635
+ finish_reason: (jsonResponse.candidates?.[0]?.finishReason ?? "").toLowerCase() || null,
1636
+ index: 0,
1637
+ message: {
1638
+ content: textContent,
1639
+ role: "assistant",
1640
+ tool_calls: toolCalls.length > 0 ? toolCalls : void 0,
1641
+ ...thinkingSignature && {
1642
+ thinking: {
1643
+ content: thinkingContent || "(no content)",
1644
+ signature: thinkingSignature
1645
+ }
1646
+ }
1647
+ }
1648
+ }
1649
+ ],
1650
+ created: Math.floor(Date.now() / 1e3),
1651
+ model: jsonResponse.modelVersion ?? "",
1652
+ object: "chat.completion",
1653
+ usage: {
1654
+ completion_tokens: jsonResponse.usageMetadata?.candidatesTokenCount || 0,
1655
+ prompt_tokens: jsonResponse.usageMetadata?.promptTokenCount || 0,
1656
+ prompt_tokens_details: {
1657
+ cached_tokens: jsonResponse.usageMetadata?.cachedContentTokenCount || 0
1658
+ },
1659
+ total_tokens: jsonResponse.usageMetadata?.totalTokenCount || 0,
1660
+ output_tokens_details: {
1661
+ reasoning_tokens: jsonResponse.usageMetadata?.thoughtsTokenCount || 0
1662
+ }
1663
+ }
1664
+ };
1665
+ return new Response(JSON.stringify(openAIResponse), {
1666
+ status: response.status,
1667
+ statusText: response.statusText,
1668
+ headers: response.headers
1669
+ });
1670
+ }
1671
+ function handleStreamResponse(response, providerName, logger) {
1672
+ if (!response.body) {
1673
+ return response;
1674
+ }
1675
+ const decoder = new TextDecoder();
1676
+ const encoder = new TextEncoder();
1677
+ let signatureSent = false;
1678
+ let contentSent = false;
1679
+ let hasThinkingContent = false;
1680
+ let pendingContent = "";
1681
+ let contentIndex = 0;
1682
+ let toolCallIndex = -1;
1683
+ const stream = new ReadableStream({
1684
+ async start(controller) {
1685
+ const reader = response.body.getReader();
1686
+ let buffer = "";
1687
+ const processLine = async (line) => {
1688
+ if (!line.startsWith("data: ")) return;
1689
+ const chunkStr = line.slice(6).trim();
1690
+ if (!chunkStr) return;
1691
+ logger?.debug(`${providerName} chunk: ${chunkStr.substring(0, 100)}...`);
1692
+ try {
1693
+ const chunk = JSON.parse(chunkStr);
1694
+ if (!chunk.candidates?.[0]) {
1695
+ logger?.debug("Invalid chunk structure");
1696
+ return;
1697
+ }
1698
+ const candidate = chunk.candidates[0];
1699
+ const parts = candidate.content?.parts || [];
1700
+ parts.filter((part) => part.text && part.thought === true).forEach((part) => {
1701
+ hasThinkingContent = true;
1702
+ const thinkingChunk = createChunk({
1703
+ responseId: chunk.responseId,
1704
+ modelVersion: chunk.modelVersion,
1705
+ contentIndex,
1706
+ delta: {
1707
+ role: "assistant",
1708
+ content: null,
1709
+ thinking: { content: part.text }
1710
+ }
1711
+ });
1712
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(thinkingChunk)}
1713
+
1714
+ `));
1715
+ });
1716
+ const signature = parts.find((part) => part.thoughtSignature)?.thoughtSignature;
1717
+ if (signature && !signatureSent) {
1718
+ if (!hasThinkingContent) {
1719
+ const thinkingChunk = createChunk({
1720
+ responseId: chunk.responseId,
1721
+ modelVersion: chunk.modelVersion,
1722
+ contentIndex,
1723
+ delta: {
1724
+ role: "assistant",
1725
+ content: null,
1726
+ thinking: { content: "" }
1727
+ }
1728
+ });
1729
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(thinkingChunk)}
1730
+
1731
+ `));
1732
+ }
1733
+ const signatureChunk = createChunk({
1734
+ responseId: chunk.responseId,
1735
+ modelVersion: chunk.modelVersion,
1736
+ contentIndex,
1737
+ delta: {
1738
+ role: "assistant",
1739
+ content: null,
1740
+ thinking: { signature }
1741
+ }
1742
+ });
1743
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(signatureChunk)}
1744
+
1745
+ `));
1746
+ signatureSent = true;
1747
+ contentIndex++;
1748
+ if (pendingContent) {
1749
+ const pendingChunk = createChunk({
1750
+ responseId: chunk.responseId,
1751
+ modelVersion: chunk.modelVersion,
1752
+ contentIndex,
1753
+ delta: { role: "assistant", content: pendingContent }
1754
+ });
1755
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(pendingChunk)}
1756
+
1757
+ `));
1758
+ pendingContent = "";
1759
+ contentSent = true;
1760
+ }
1761
+ }
1762
+ const toolCalls = parts.filter((part) => part.functionCall).map((part) => ({
1763
+ id: part.functionCall?.id || `ccr_tool_${Math.random().toString(36).substring(2, 15)}`,
1764
+ type: "function",
1765
+ function: {
1766
+ name: part.functionCall?.name ?? "",
1767
+ arguments: JSON.stringify(part.functionCall?.args || {})
1768
+ }
1769
+ }));
1770
+ const textContent = parts.filter((part) => part.text && part.thought !== true).map((part) => part.text).join("\n");
1771
+ if (!textContent && signatureSent && !contentSent) {
1772
+ contentSent = true;
1773
+ }
1774
+ if (hasThinkingContent && textContent && !signatureSent) {
1775
+ if (chunk.modelVersion?.includes("3")) {
1776
+ pendingContent += textContent;
1777
+ return;
1778
+ } else {
1779
+ const signatureChunk = createChunk({
1780
+ responseId: chunk.responseId,
1781
+ modelVersion: chunk.modelVersion,
1782
+ contentIndex,
1783
+ delta: {
1784
+ role: "assistant",
1785
+ content: null,
1786
+ thinking: { signature: `ccr_${Date.now()}` }
1787
+ }
1788
+ });
1789
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(signatureChunk)}
1790
+
1791
+ `));
1792
+ signatureSent = true;
1793
+ }
1794
+ }
1795
+ if (textContent) {
1796
+ if (!pendingContent) contentIndex++;
1797
+ const contentChunk = createChunk({
1798
+ responseId: chunk.responseId,
1799
+ modelVersion: chunk.modelVersion,
1800
+ contentIndex,
1801
+ delta: { role: "assistant", content: textContent },
1802
+ finishReason: candidate.finishReason,
1803
+ usageMetadata: chunk.usageMetadata,
1804
+ groundingMetadata: candidate.groundingMetadata
1805
+ });
1806
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(contentChunk)}
1807
+
1808
+ `));
1809
+ contentSent = true;
1810
+ }
1811
+ if (toolCalls.length > 0) {
1812
+ for (const tool of toolCalls) {
1813
+ contentIndex++;
1814
+ toolCallIndex++;
1815
+ const toolChunk = createChunk({
1816
+ responseId: chunk.responseId,
1817
+ modelVersion: chunk.modelVersion,
1818
+ contentIndex,
1819
+ delta: {
1820
+ role: "assistant",
1821
+ tool_calls: [{ ...tool, index: toolCallIndex }]
1822
+ },
1823
+ finishReason: candidate.finishReason,
1824
+ usageMetadata: chunk.usageMetadata,
1825
+ groundingMetadata: candidate.groundingMetadata
1826
+ });
1827
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(toolChunk)}
1828
+
1829
+ `));
1830
+ }
1831
+ contentSent = true;
1832
+ }
1833
+ } catch (_error) {
1834
+ logger?.error(`Error parsing ${providerName} stream chunk: ${chunkStr}`);
1835
+ }
1836
+ };
1837
+ try {
1838
+ while (true) {
1839
+ const { done, value } = await reader.read();
1840
+ if (done) {
1841
+ if (buffer) await processLine(buffer);
1842
+ break;
1843
+ }
1844
+ buffer += decoder.decode(value, { stream: true });
1845
+ const lines = buffer.split("\n");
1846
+ buffer = lines.pop() || "";
1847
+ for (const line of lines) {
1848
+ await processLine(line);
1849
+ }
1850
+ }
1851
+ } catch (error) {
1852
+ controller.error(error);
1853
+ } finally {
1854
+ controller.close();
1855
+ }
1856
+ }
1857
+ });
1858
+ return new Response(stream, {
1859
+ status: response.status,
1860
+ statusText: response.statusText,
1861
+ headers: response.headers
1862
+ });
1863
+ }
1864
+ function createChunk(options) {
1865
+ const {
1866
+ responseId,
1867
+ modelVersion,
1868
+ contentIndex,
1869
+ delta,
1870
+ finishReason,
1871
+ usageMetadata,
1872
+ groundingMetadata
1873
+ } = options;
1874
+ const chunk = {
1875
+ choices: [
1876
+ {
1877
+ delta,
1878
+ finish_reason: finishReason?.toLowerCase() || null,
1879
+ index: contentIndex,
1880
+ logprobs: null
1881
+ }
1882
+ ],
1883
+ created: Math.floor(Date.now() / 1e3),
1884
+ id: responseId || "",
1885
+ model: modelVersion || "",
1886
+ object: "chat.completion.chunk",
1887
+ system_fingerprint: "fp_a49d71b8a1"
1888
+ };
1889
+ if (usageMetadata) {
1890
+ chunk.usage = {
1891
+ completion_tokens: usageMetadata.candidatesTokenCount || 0,
1892
+ prompt_tokens: usageMetadata.promptTokenCount || 0,
1893
+ prompt_tokens_details: {
1894
+ cached_tokens: usageMetadata.cachedContentTokenCount || 0
1895
+ },
1896
+ total_tokens: usageMetadata.totalTokenCount || 0,
1897
+ output_tokens_details: {
1898
+ reasoning_tokens: usageMetadata.thoughtsTokenCount || 0
1899
+ }
1900
+ };
1901
+ }
1902
+ if (groundingMetadata?.groundingChunks?.length) {
1903
+ const annotations = groundingMetadata.groundingChunks.map((groundingChunk, index) => {
1904
+ const support = groundingMetadata.groundingSupports?.find(
1905
+ (s) => s.groundingChunkIndices?.includes(index)
1906
+ );
1907
+ return {
1908
+ type: "url_citation",
1909
+ url_citation: {
1910
+ url: groundingChunk.web?.uri || "",
1911
+ title: groundingChunk.web?.title || "",
1912
+ content: support?.segment?.text || "",
1913
+ start_index: support?.segment?.startIndex || 0,
1914
+ end_index: support?.segment?.endIndex || 0
1915
+ }
1916
+ };
1917
+ });
1918
+ chunk.choices[0].delta.annotations = annotations;
1919
+ }
1920
+ return chunk;
1921
+ }
1922
+
1923
+ // src/transformer/transformers/utils/gemini.schema.ts
1924
+ var GeminiType = {
1925
+ TYPE_UNSPECIFIED: "TYPE_UNSPECIFIED",
1926
+ STRING: "STRING",
1927
+ NUMBER: "NUMBER",
1928
+ INTEGER: "INTEGER",
1929
+ BOOLEAN: "BOOLEAN",
1930
+ ARRAY: "ARRAY",
1931
+ OBJECT: "OBJECT",
1932
+ NULL: "NULL"
1933
+ };
1934
+ function flattenTypeArrayToAnyOf(typeList, resultingSchema) {
1935
+ if (typeList.includes("null")) {
1936
+ resultingSchema.nullable = true;
1937
+ }
1938
+ const listWithoutNull = typeList.filter((type) => type !== "null");
1939
+ if (listWithoutNull.length === 1) {
1940
+ const upperCaseType = listWithoutNull[0].toUpperCase();
1941
+ resultingSchema.type = Object.values(GeminiType).includes(upperCaseType) ? upperCaseType : GeminiType.TYPE_UNSPECIFIED;
1942
+ } else {
1943
+ resultingSchema.anyOf = listWithoutNull.map((typeName) => {
1944
+ const upperCaseType = typeName.toUpperCase();
1945
+ return {
1946
+ type: Object.values(GeminiType).includes(upperCaseType) ? upperCaseType : GeminiType.TYPE_UNSPECIFIED
1947
+ };
1948
+ });
1949
+ }
1950
+ }
1951
+ function processJsonSchema(jsonSchema) {
1952
+ const genAISchema = {};
1953
+ const schemaFieldNames = ["items"];
1954
+ const listSchemaFieldNames = ["anyOf"];
1955
+ const dictSchemaFieldNames = ["properties"];
1956
+ let workingSchema = jsonSchema;
1957
+ if (workingSchema.type && workingSchema.anyOf) {
1958
+ throw new Error("type and anyOf cannot be both populated.");
1959
+ }
1960
+ const incomingAnyOf = workingSchema.anyOf;
1961
+ if (incomingAnyOf && Array.isArray(incomingAnyOf) && incomingAnyOf.length === 2) {
1962
+ if (incomingAnyOf[0]?.type === "null") {
1963
+ genAISchema.nullable = true;
1964
+ workingSchema = incomingAnyOf[1];
1965
+ } else if (incomingAnyOf[1]?.type === "null") {
1966
+ genAISchema.nullable = true;
1967
+ workingSchema = incomingAnyOf[0];
1968
+ }
1969
+ }
1970
+ if (workingSchema.type && Array.isArray(workingSchema.type)) {
1971
+ flattenTypeArrayToAnyOf(workingSchema.type, genAISchema);
1972
+ }
1973
+ for (const [fieldName, fieldValue] of Object.entries(workingSchema)) {
1974
+ if (fieldValue == null) {
1975
+ continue;
1976
+ }
1977
+ if (fieldName === "type") {
1978
+ if (fieldValue === "null") {
1979
+ throw new Error("type: null cannot be the only possible type for the field.");
1980
+ }
1981
+ if (Array.isArray(fieldValue)) {
1982
+ continue;
1983
+ }
1984
+ const upperCaseValue = fieldValue.toUpperCase();
1985
+ genAISchema.type = Object.values(GeminiType).includes(upperCaseValue) ? upperCaseValue : GeminiType.TYPE_UNSPECIFIED;
1986
+ } else if (schemaFieldNames.includes(fieldName)) {
1987
+ genAISchema[fieldName] = processJsonSchema(fieldValue);
1988
+ } else if (listSchemaFieldNames.includes(fieldName)) {
1989
+ const listValue = [];
1990
+ for (const item of fieldValue) {
1991
+ if (item.type === "null") {
1992
+ genAISchema.nullable = true;
1993
+ continue;
1994
+ }
1995
+ listValue.push(processJsonSchema(item));
1996
+ }
1997
+ genAISchema[fieldName] = listValue;
1998
+ } else if (dictSchemaFieldNames.includes(fieldName)) {
1999
+ const dictValue = {};
2000
+ for (const [key, value] of Object.entries(fieldValue)) {
2001
+ dictValue[key] = processJsonSchema(value);
2002
+ }
2003
+ genAISchema[fieldName] = dictValue;
2004
+ } else {
2005
+ if (fieldName === "additionalProperties") {
2006
+ continue;
2007
+ }
2008
+ genAISchema[fieldName] = fieldValue;
2009
+ }
2010
+ }
2011
+ return genAISchema;
2012
+ }
2013
+ function transformTool(tool) {
2014
+ const functionDeclarations = tool.functionDeclarations;
2015
+ if (functionDeclarations) {
2016
+ for (const functionDeclaration of functionDeclarations) {
2017
+ if (functionDeclaration.parameters) {
2018
+ const params = functionDeclaration.parameters;
2019
+ if (!Object.keys(params).includes("$schema")) {
2020
+ functionDeclaration.parameters = processJsonSchema(params);
2021
+ } else {
2022
+ if (!functionDeclaration.parametersJsonSchema) {
2023
+ functionDeclaration.parametersJsonSchema = functionDeclaration.parameters;
2024
+ delete functionDeclaration.parameters;
2025
+ }
2026
+ }
2027
+ }
2028
+ if (functionDeclaration.response) {
2029
+ const response = functionDeclaration.response;
2030
+ if (!Object.keys(response).includes("$schema")) {
2031
+ functionDeclaration.response = processJsonSchema(response);
2032
+ } else {
2033
+ if (!functionDeclaration.responseJsonSchema) {
2034
+ functionDeclaration.responseJsonSchema = functionDeclaration.response;
2035
+ delete functionDeclaration.response;
2036
+ }
2037
+ }
2038
+ }
2039
+ }
2040
+ }
2041
+ return tool;
2042
+ }
2043
+
2044
+ // src/transformer/transformers/utils/gemini.util.ts
2045
+ function buildRequestBody(request) {
2046
+ const tools = [];
2047
+ const functionDeclarations = request.tools?.filter((tool) => tool.function.name !== "web_search")?.map((tool) => ({
2048
+ name: tool.function.name,
2049
+ description: tool.function.description,
2050
+ parametersJsonSchema: tool.function.parameters
2051
+ }));
2052
+ if (functionDeclarations?.length) {
2053
+ tools.push(
2054
+ transformTool({
2055
+ functionDeclarations
2056
+ })
2057
+ );
2058
+ }
2059
+ const webSearch = request.tools?.find((tool) => tool.function.name === "web_search");
2060
+ if (webSearch) {
2061
+ tools.push({ googleSearch: {} });
2062
+ }
2063
+ const contents = [];
2064
+ const toolResponses = request.messages.filter((item) => item.role === "tool");
2065
+ request.messages.filter((item) => item.role !== "tool").forEach((message) => {
2066
+ let role;
2067
+ if (message.role === "assistant") {
2068
+ role = "model";
2069
+ } else if (["user", "system"].includes(message.role)) {
2070
+ role = "user";
2071
+ } else {
2072
+ role = "user";
2073
+ }
2074
+ const parts = [];
2075
+ if (typeof message.content === "string") {
2076
+ const part = { text: message.content };
2077
+ if (message.thinking?.signature) {
2078
+ part.thoughtSignature = message.thinking.signature;
2079
+ }
2080
+ parts.push(part);
2081
+ } else if (Array.isArray(message.content)) {
2082
+ for (const content of message.content) {
2083
+ if (content.type === "text") {
2084
+ parts.push({ text: content.text || "" });
2085
+ } else if (content.type === "image_url") {
2086
+ const imageUrl = content.image_url?.url ?? "";
2087
+ if (imageUrl.startsWith("http")) {
2088
+ parts.push({
2089
+ file_data: {
2090
+ mime_type: content.media_type,
2091
+ file_uri: imageUrl
2092
+ }
2093
+ });
2094
+ } else {
2095
+ const data = imageUrl.split(",").pop() || imageUrl;
2096
+ parts.push({
2097
+ inlineData: {
2098
+ mime_type: content.media_type || "image/png",
2099
+ data
2100
+ }
2101
+ });
2102
+ }
2103
+ }
2104
+ }
2105
+ } else if (message.content && typeof message.content === "object") {
2106
+ const contentObj = message.content;
2107
+ if (contentObj.text) {
2108
+ parts.push({ text: contentObj.text });
2109
+ } else {
2110
+ parts.push({ text: JSON.stringify(message.content) });
2111
+ }
2112
+ }
2113
+ if (Array.isArray(message.tool_calls)) {
2114
+ for (let index = 0; index < message.tool_calls.length; index++) {
2115
+ const toolCall = message.tool_calls[index];
2116
+ const functionCallPart = {
2117
+ functionCall: {
2118
+ id: toolCall.id || `tool_${Math.random().toString(36).substring(2, 15)}`,
2119
+ name: toolCall.function.name,
2120
+ args: JSON.parse(toolCall.function.arguments || "{}")
2121
+ }
2122
+ };
2123
+ if (index === 0 && message.thinking?.signature) {
2124
+ functionCallPart.thoughtSignature = message.thinking.signature;
2125
+ }
2126
+ parts.push(functionCallPart);
2127
+ }
2128
+ }
2129
+ if (parts.length === 0) {
2130
+ parts.push({ text: "" });
2131
+ }
2132
+ contents.push({ role, parts });
2133
+ if (role === "model" && message.tool_calls) {
2134
+ const functionResponses = message.tool_calls.map(
2135
+ (tool) => {
2136
+ const response = toolResponses.find((item) => item.tool_call_id === tool.id);
2137
+ return {
2138
+ functionResponse: {
2139
+ name: tool.function?.name ?? "",
2140
+ response: { result: response?.content }
2141
+ }
2142
+ };
2143
+ }
2144
+ );
2145
+ contents.push({
2146
+ role: "user",
2147
+ parts: functionResponses
2148
+ });
2149
+ }
2150
+ });
2151
+ const generationConfig = {};
2152
+ if (request.reasoning?.effort && request.reasoning.effort !== "none") {
2153
+ generationConfig.thinkingConfig = {
2154
+ includeThoughts: true
2155
+ };
2156
+ if (request.model.includes("gemini-3")) {
2157
+ generationConfig.thinkingConfig.thinkingLevel = request.reasoning.effort;
2158
+ } else {
2159
+ const thinkingBudgets = request.model.includes("pro") ? [128, 32768] : [0, 24576];
2160
+ const maxTokens = request.reasoning.max_tokens;
2161
+ if (typeof maxTokens !== "undefined") {
2162
+ let thinkingBudget;
2163
+ if (maxTokens >= thinkingBudgets[0] && maxTokens <= thinkingBudgets[1]) {
2164
+ thinkingBudget = maxTokens;
2165
+ } else if (maxTokens < thinkingBudgets[0]) {
2166
+ thinkingBudget = thinkingBudgets[0];
2167
+ } else {
2168
+ thinkingBudget = thinkingBudgets[1];
2169
+ }
2170
+ generationConfig.thinkingConfig.thinkingBudget = thinkingBudget;
2171
+ }
2172
+ }
2173
+ }
2174
+ const body = {
2175
+ contents,
2176
+ tools: tools.length > 0 ? tools : void 0,
2177
+ generationConfig: Object.keys(generationConfig).length > 0 ? generationConfig : void 0
2178
+ };
2179
+ if (request.tool_choice) {
2180
+ const toolConfig = {
2181
+ functionCallingConfig: {}
2182
+ };
2183
+ if (request.tool_choice === "auto") {
2184
+ toolConfig.functionCallingConfig.mode = "auto";
2185
+ } else if (request.tool_choice === "none") {
2186
+ toolConfig.functionCallingConfig.mode = "none";
2187
+ } else if (request.tool_choice === "required") {
2188
+ toolConfig.functionCallingConfig.mode = "any";
2189
+ } else if (typeof request.tool_choice === "object" && request.tool_choice.function?.name) {
2190
+ toolConfig.functionCallingConfig.mode = "any";
2191
+ toolConfig.functionCallingConfig.allowedFunctionNames = [
2192
+ request.tool_choice.function.name
2193
+ ];
2194
+ }
2195
+ body.toolConfig = toolConfig;
2196
+ }
2197
+ return body;
2198
+ }
2199
+ function transformRequestOut(request) {
2200
+ const contents = request.contents;
2201
+ const tools = request.tools;
2202
+ const model = request.model;
2203
+ const maxTokens = request.max_tokens;
2204
+ const temperature = request.temperature;
2205
+ const stream = request.stream;
2206
+ const toolChoice = request.tool_choice;
2207
+ const unifiedRequest = {
2208
+ messages: [],
2209
+ model,
2210
+ max_tokens: maxTokens,
2211
+ temperature,
2212
+ stream,
2213
+ tool_choice: toolChoice
2214
+ };
2215
+ if (Array.isArray(contents)) {
2216
+ for (const content of contents) {
2217
+ if (typeof content === "string") {
2218
+ unifiedRequest.messages.push({
2219
+ role: "user",
2220
+ content
2221
+ });
2222
+ } else if ("text" in content && typeof content.text === "string") {
2223
+ unifiedRequest.messages.push({
2224
+ role: "user",
2225
+ content: content.text || null
2226
+ });
2227
+ } else if ("role" in content && content.role === "user") {
2228
+ const geminiContent = content;
2229
+ unifiedRequest.messages.push({
2230
+ role: "user",
2231
+ content: geminiContent.parts?.map((part) => ({
2232
+ type: "text",
2233
+ text: part.text || ""
2234
+ })) || []
2235
+ });
2236
+ } else if (content.role === "model") {
2237
+ unifiedRequest.messages.push({
2238
+ role: "assistant",
2239
+ content: content.parts?.map((part) => ({
2240
+ type: "text",
2241
+ text: part.text || ""
2242
+ })) || []
2243
+ });
2244
+ }
2245
+ }
2246
+ }
2247
+ if (Array.isArray(tools)) {
2248
+ unifiedRequest.tools = [];
2249
+ for (const tool of tools) {
2250
+ if (Array.isArray(tool.functionDeclarations)) {
2251
+ for (const funcDecl of tool.functionDeclarations) {
2252
+ unifiedRequest.tools.push({
2253
+ type: "function",
2254
+ function: {
2255
+ name: funcDecl.name,
2256
+ description: funcDecl.description ?? "",
2257
+ parameters: funcDecl.parameters ?? {}
2258
+ }
2259
+ });
2260
+ }
2261
+ }
2262
+ }
2263
+ }
2264
+ return unifiedRequest;
2265
+ }
2266
+
2267
+ // src/transformer/transformers/GeminiTransformer.ts
2268
+ var GeminiTransformer = class {
2269
+ static TransformerName = "gemini";
2270
+ name = "gemini";
2271
+ logger;
2272
+ /**
2273
+ * API endpoint pattern for Gemini
2274
+ * :modelAndAction will be replaced with actual model and action
2275
+ */
2276
+ endPoint = "/v1beta/models/:modelAndAction";
2277
+ /** Use Bearer token instead of x-goog-api-key (for relay providers) */
2278
+ useBearer;
2279
+ constructor(options) {
2280
+ this.useBearer = options?.UseBearer ?? false;
2281
+ }
2282
+ /**
2283
+ * Handle authentication
2284
+ * - Official Gemini: x-goog-api-key header
2285
+ * - Relay providers: Authorization: Bearer header
2286
+ */
2287
+ async auth(request, provider, _context) {
2288
+ const headers = {};
2289
+ if (this.useBearer) {
2290
+ headers["authorization"] = `Bearer ${provider.apiKey}`;
2291
+ headers["x-goog-api-key"] = void 0;
2292
+ } else {
2293
+ headers["x-goog-api-key"] = provider.apiKey;
2294
+ headers["authorization"] = void 0;
2295
+ }
2296
+ return {
2297
+ body: request,
2298
+ config: { headers }
2299
+ };
2300
+ }
2301
+ /**
2302
+ * Transform request from unified format to Gemini format
2303
+ * Also builds the correct URL for the Gemini API
2304
+ */
2305
+ async transformRequestIn(request, provider, _context) {
2306
+ const body = buildRequestBody(request);
2307
+ const action = request.stream ? "streamGenerateContent?alt=sse" : "generateContent";
2308
+ const url = new URL(`./${request.model}:${action}`, provider.baseUrl);
2309
+ const headers = {};
2310
+ if (this.useBearer) {
2311
+ headers["authorization"] = `Bearer ${provider.apiKey}`;
2312
+ headers["x-goog-api-key"] = void 0;
2313
+ } else {
2314
+ headers["x-goog-api-key"] = provider.apiKey;
2315
+ headers["Authorization"] = void 0;
2316
+ }
2317
+ return {
2318
+ body,
2319
+ config: { url, headers }
2320
+ };
2321
+ }
2322
+ /**
2323
+ * Transform incoming request to unified format
2324
+ * (For requests coming into the Gemini endpoint)
2325
+ */
2326
+ async transformRequestOut(request, _context) {
2327
+ return transformRequestOut(request);
2328
+ }
2329
+ /**
2330
+ * Transform Gemini response to OpenAI-compatible format
2331
+ */
2332
+ async transformResponseOut(response, _context) {
2333
+ return transformResponseOut(response, this.name, this.logger);
2334
+ }
2335
+ /**
2336
+ * Transform OpenAI-compatible response back to Gemini format
2337
+ * (For endpoint mode — returning Gemini-format responses to the client)
2338
+ */
2339
+ async transformResponseIn(response, _context) {
2340
+ return transformResponseIn(response, this.logger);
2341
+ }
2342
+ };
2343
+
2344
+ // src/transformer/transformers/OpenAIResponseTransformer.ts
2345
+ var OpenAIResponseTransformer = class {
2346
+ static TransformerName = "openai-response";
2347
+ name = "openai-response";
2348
+ endPoint = "/v1/responses";
2349
+ logger;
2350
+ /**
2351
+ * Handle authentication - Bearer token
2352
+ */
2353
+ async auth(request, provider, _context) {
2354
+ return {
2355
+ body: request,
2356
+ config: {
2357
+ headers: {
2358
+ Authorization: `Bearer ${provider.apiKey}`,
2359
+ "Content-Type": "application/json"
2360
+ }
2361
+ }
2362
+ };
2363
+ }
2364
+ /**
2365
+ * Transform unified request → Response API format
2366
+ */
2367
+ async transformRequestIn(request, provider, _context) {
2368
+ const input = [];
2369
+ for (const msg of request.messages) {
2370
+ if (msg.role === "system") {
2371
+ input.push({
2372
+ role: "developer",
2373
+ content: typeof msg.content === "string" ? msg.content : flattenContent(msg.content)
2374
+ });
2375
+ } else if (msg.role === "tool") {
2376
+ input.push({
2377
+ type: "function_call_output",
2378
+ call_id: msg.tool_call_id,
2379
+ output: typeof msg.content === "string" ? msg.content : ""
2380
+ });
2381
+ } else {
2382
+ const entry = {
2383
+ role: msg.role,
2384
+ content: typeof msg.content === "string" ? msg.content : flattenContent(msg.content)
2385
+ };
2386
+ if (msg.role === "assistant" && msg.tool_calls?.length) {
2387
+ input.push(entry);
2388
+ for (const tc of msg.tool_calls) {
2389
+ input.push({
2390
+ type: "function_call",
2391
+ id: tc.id,
2392
+ call_id: tc.id,
2393
+ name: tc.function.name,
2394
+ arguments: tc.function.arguments
2395
+ });
2396
+ }
2397
+ continue;
2398
+ }
2399
+ input.push(entry);
2400
+ }
2401
+ }
2402
+ const body = {
2403
+ model: request.model,
2404
+ input,
2405
+ stream: request.stream ?? false,
2406
+ ...request.max_tokens ? { max_output_tokens: request.max_tokens } : {},
2407
+ ...request.temperature !== void 0 ? { temperature: request.temperature } : {}
2408
+ };
2409
+ if (request.reasoning?.effort && request.reasoning.effort !== "none") {
2410
+ body.reasoning = { effort: request.reasoning.effort, summary: "auto" };
2411
+ }
2412
+ if (request.tools?.length) {
2413
+ body.tools = request.tools.map((tool) => ({
2414
+ type: "function",
2415
+ name: tool.function.name,
2416
+ description: tool.function.description,
2417
+ parameters: tool.function.parameters
2418
+ }));
2419
+ }
2420
+ if (request.tool_choice) {
2421
+ if (typeof request.tool_choice === "string") {
2422
+ body.tool_choice = request.tool_choice;
2423
+ } else if (typeof request.tool_choice === "object" && "function" in request.tool_choice) {
2424
+ body.tool_choice = { type: "function", name: request.tool_choice.function.name };
2425
+ }
2426
+ }
2427
+ const url = new URL("/v1/responses", provider.baseUrl);
2428
+ return { body, config: { url } };
2429
+ }
2430
+ /**
2431
+ * Transform Response API request → unified format
2432
+ */
2433
+ async transformRequestOut(request, _context) {
2434
+ const req = request;
2435
+ const messages = [];
2436
+ if (req.input) {
2437
+ for (const item of req.input) {
2438
+ const entry = item;
2439
+ if (entry.type === "function_call_output") {
2440
+ messages.push({
2441
+ role: "tool",
2442
+ content: entry.output || "",
2443
+ tool_call_id: entry.call_id || void 0
2444
+ });
2445
+ continue;
2446
+ }
2447
+ if (entry.type === "function_call") {
2448
+ const toolCall = {
2449
+ id: (entry.call_id ?? entry.id) || "",
2450
+ type: "function",
2451
+ function: {
2452
+ name: entry.name || "",
2453
+ arguments: typeof entry.arguments === "string" ? entry.arguments : ""
2454
+ }
2455
+ };
2456
+ const last = messages[messages.length - 1];
2457
+ if (last && last.role === "assistant") {
2458
+ (last.tool_calls ??= []).push(toolCall);
2459
+ } else {
2460
+ messages.push({ role: "assistant", content: null, tool_calls: [toolCall] });
2461
+ }
2462
+ continue;
2463
+ }
2464
+ const role = entry.role;
2465
+ if (role === "developer") {
2466
+ messages.push({
2467
+ role: "system",
2468
+ content: typeof entry.content === "string" ? entry.content : JSON.stringify(entry.content)
2469
+ });
2470
+ } else if (role === "user" || role === "assistant") {
2471
+ messages.push({
2472
+ role,
2473
+ content: typeof entry.content === "string" ? entry.content : JSON.stringify(entry.content)
2474
+ });
2475
+ }
2476
+ }
2477
+ }
2478
+ const result = {
2479
+ messages,
2480
+ model: req.model,
2481
+ max_tokens: req.max_output_tokens,
2482
+ temperature: req.temperature,
2483
+ stream: req.stream
2484
+ };
2485
+ if (req.reasoning?.effort) {
2486
+ result.reasoning = {
2487
+ effort: req.reasoning.effort,
2488
+ enabled: true
2489
+ };
2490
+ }
2491
+ if (req.tools?.length) {
2492
+ result.tools = req.tools.filter((t) => t.type === "function").map((t) => ({
2493
+ type: "function",
2494
+ function: {
2495
+ name: t.name || "",
2496
+ description: t.description || "",
2497
+ parameters: t.parameters || {}
2498
+ }
2499
+ }));
2500
+ }
2501
+ return result;
2502
+ }
2503
+ /**
2504
+ * Transform Response API response → unified (OpenAI CC) format
2505
+ */
2506
+ async transformResponseOut(response, _context) {
2507
+ const contentType = response.headers.get("Content-Type") ?? "";
2508
+ if (contentType.includes("text/event-stream")) {
2509
+ if (!response.body) {
2510
+ throw new Error("Stream response body is null");
2511
+ }
2512
+ return new Response(convertResponseApiStreamToOpenAI(response.body), {
2513
+ headers: {
2514
+ "Content-Type": "text/event-stream",
2515
+ "Cache-Control": "no-cache",
2516
+ Connection: "keep-alive"
2517
+ }
2518
+ });
2519
+ }
2520
+ const data = await response.json();
2521
+ return new Response(JSON.stringify(convertResponseApiJsonToOpenAI(data)), {
2522
+ headers: { "Content-Type": "application/json" }
2523
+ });
2524
+ }
2525
+ /**
2526
+ * Transform OpenAI CC response → Response API format
2527
+ */
2528
+ async transformResponseIn(response, _context) {
2529
+ const contentType = response.headers.get("Content-Type") ?? "";
2530
+ if (contentType.includes("text/event-stream")) {
2531
+ if (!response.body) {
2532
+ throw new Error("Stream response body is null");
2533
+ }
2534
+ return new Response(convertOpenAIStreamToResponseApi(response.body), {
2535
+ headers: {
2536
+ "Content-Type": "text/event-stream",
2537
+ "Cache-Control": "no-cache",
2538
+ Connection: "keep-alive"
2539
+ }
2540
+ });
2541
+ }
2542
+ const data = await response.json();
2543
+ return new Response(JSON.stringify(convertOpenAIJsonToResponseApi(data)), {
2544
+ headers: { "Content-Type": "application/json" }
2545
+ });
2546
+ }
2547
+ };
2548
+ function flattenContent(content) {
2549
+ if (typeof content === "string") return content;
2550
+ if (!content) return "";
2551
+ if (Array.isArray(content)) {
2552
+ return content.filter((c) => c.type === "text").map((c) => c.text || "").join("\n");
2553
+ }
2554
+ return "";
2555
+ }
2556
+ function convertResponseApiJsonToOpenAI(data) {
2557
+ let textContent = "";
2558
+ const toolCalls = [];
2559
+ const output = data.output;
2560
+ if (output) {
2561
+ for (const item of output) {
2562
+ if (item.type === "message") {
2563
+ const content = item.content;
2564
+ if (content) {
2565
+ for (const part of content) {
2566
+ if (part.type === "output_text" && typeof part.text === "string") {
2567
+ textContent += part.text;
2568
+ }
2569
+ }
2570
+ }
2571
+ } else if (item.type === "function_call") {
2572
+ toolCalls.push({
2573
+ id: item.call_id || item.id || `call_${Date.now()}`,
2574
+ type: "function",
2575
+ function: {
2576
+ name: item.name,
2577
+ arguments: typeof item.arguments === "string" ? item.arguments : JSON.stringify(item.arguments || {})
2578
+ }
2579
+ });
2580
+ }
2581
+ }
2582
+ }
2583
+ const usage = data.usage;
2584
+ const message = {
2585
+ role: "assistant",
2586
+ content: textContent || null
2587
+ };
2588
+ if (toolCalls.length > 0) {
2589
+ message.tool_calls = toolCalls;
2590
+ }
2591
+ return {
2592
+ id: data.id || `chatcmpl-${Date.now()}`,
2593
+ object: "chat.completion",
2594
+ created: Math.floor(Date.now() / 1e3),
2595
+ model: data.model || "unknown",
2596
+ choices: [
2597
+ {
2598
+ index: 0,
2599
+ message,
2600
+ finish_reason: toolCalls.length > 0 ? "tool_calls" : "stop"
2601
+ }
2602
+ ],
2603
+ usage: usage ? {
2604
+ prompt_tokens: usage.input_tokens || 0,
2605
+ completion_tokens: usage.output_tokens || 0,
2606
+ total_tokens: (usage.input_tokens || 0) + (usage.output_tokens || 0)
2607
+ } : void 0
2608
+ };
2609
+ }
2610
+ function convertOpenAIJsonToResponseApi(data) {
2611
+ const choices = data.choices;
2612
+ const message = choices?.[0]?.message;
2613
+ const output = [];
2614
+ if (message) {
2615
+ const contentParts = [];
2616
+ if (message.content) {
2617
+ contentParts.push({ type: "output_text", text: message.content });
2618
+ }
2619
+ if (contentParts.length > 0) {
2620
+ output.push({ type: "message", role: "assistant", content: contentParts });
2621
+ }
2622
+ const toolCalls = message.tool_calls;
2623
+ if (toolCalls?.length) {
2624
+ for (const tc of toolCalls) {
2625
+ const func = tc.function;
2626
+ output.push({
2627
+ type: "function_call",
2628
+ id: tc.id,
2629
+ call_id: tc.id,
2630
+ name: func?.name,
2631
+ arguments: func?.arguments
2632
+ });
2633
+ }
2634
+ }
2635
+ }
2636
+ const usage = data.usage;
2637
+ return {
2638
+ id: data.id || `resp_${Date.now()}`,
2639
+ object: "response",
2640
+ status: "completed",
2641
+ model: data.model || "unknown",
2642
+ output,
2643
+ usage: usage ? {
2644
+ input_tokens: usage.prompt_tokens || 0,
2645
+ output_tokens: usage.completion_tokens || 0,
2646
+ total_tokens: usage.total_tokens || (usage.prompt_tokens || 0) + (usage.completion_tokens || 0)
2647
+ } : void 0
2648
+ };
2649
+ }
2650
+ function convertResponseApiStreamToOpenAI(responseApiStream) {
2651
+ const decoder = new TextDecoder();
2652
+ const encoder = new TextEncoder();
2653
+ return new ReadableStream({
2654
+ start: async (controller) => {
2655
+ const reader = responseApiStream.getReader();
2656
+ let buffer = "";
2657
+ let isClosed = false;
2658
+ const messageId = `chatcmpl-${Date.now()}`;
2659
+ let model = "unknown";
2660
+ let hasEmittedRole = false;
2661
+ const safeEnqueue = (str) => {
2662
+ if (!isClosed) {
2663
+ try {
2664
+ controller.enqueue(encoder.encode(str));
2665
+ } catch {
2666
+ isClosed = true;
2667
+ }
2668
+ }
2669
+ };
2670
+ const emitChunk = (choices, usage) => {
2671
+ const chunk = {
2672
+ id: messageId,
2673
+ object: "chat.completion.chunk",
2674
+ created: Math.floor(Date.now() / 1e3),
2675
+ model,
2676
+ choices
2677
+ };
2678
+ if (usage) chunk.usage = usage;
2679
+ safeEnqueue(`data: ${JSON.stringify(chunk)}
2680
+
2681
+ `);
2682
+ };
2683
+ try {
2684
+ while (true) {
2685
+ const { done, value } = await reader.read();
2686
+ if (done) break;
2687
+ buffer += decoder.decode(value, { stream: true });
2688
+ const lines = buffer.split("\n");
2689
+ buffer = lines.pop() || "";
2690
+ for (const line of lines) {
2691
+ if (isClosed) break;
2692
+ if (!line.startsWith("data:")) continue;
2693
+ const data = line.slice(5).trim();
2694
+ if (data === "[DONE]") continue;
2695
+ try {
2696
+ const event = JSON.parse(data);
2697
+ model = event.model || event.response?.model || model;
2698
+ switch (event.type) {
2699
+ case "response.output_text.delta":
2700
+ if (event.delta) {
2701
+ if (!hasEmittedRole) {
2702
+ emitChunk([{ index: 0, delta: { role: "assistant", content: "" }, finish_reason: null }]);
2703
+ hasEmittedRole = true;
2704
+ }
2705
+ emitChunk([{ index: 0, delta: { content: event.delta }, finish_reason: null }]);
2706
+ }
2707
+ break;
2708
+ case "response.reasoning_summary_text.delta":
2709
+ if (event.delta) {
2710
+ emitChunk([{
2711
+ index: 0,
2712
+ delta: { thinking: { content: event.delta } },
2713
+ finish_reason: null
2714
+ }]);
2715
+ }
2716
+ break;
2717
+ case "response.completed": {
2718
+ const resp = event.response;
2719
+ const respUsage = resp?.usage;
2720
+ const usage = respUsage ? {
2721
+ prompt_tokens: respUsage.input_tokens || 0,
2722
+ completion_tokens: respUsage.output_tokens || 0,
2723
+ total_tokens: (respUsage.input_tokens || 0) + (respUsage.output_tokens || 0)
2724
+ } : void 0;
2725
+ emitChunk([{ index: 0, delta: {}, finish_reason: "stop" }], usage);
2726
+ safeEnqueue("data: [DONE]\n\n");
2727
+ break;
2728
+ }
2729
+ case "error":
2730
+ emitChunk([{
2731
+ index: 0,
2732
+ delta: { content: `[Error: ${event.error?.message || "Unknown error"}]` },
2733
+ finish_reason: "stop"
2734
+ }]);
2735
+ break;
2736
+ default:
2737
+ break;
2738
+ }
2739
+ } catch {
2740
+ }
2741
+ }
2742
+ }
2743
+ } catch (e) {
2744
+ if (!isClosed) controller.error(e);
2745
+ } finally {
2746
+ if (!isClosed) {
2747
+ try {
2748
+ controller.close();
2749
+ } catch {
2750
+ }
2751
+ }
2752
+ reader.releaseLock();
2753
+ }
2754
+ }
2755
+ });
2756
+ }
2757
+ function convertOpenAIStreamToResponseApi(openaiStream) {
2758
+ const decoder = new TextDecoder();
2759
+ const encoder = new TextEncoder();
2760
+ return new ReadableStream({
2761
+ start: async (controller) => {
2762
+ const reader = openaiStream.getReader();
2763
+ let buffer = "";
2764
+ let isClosed = false;
2765
+ let accumulatedContent = "";
2766
+ let model = "unknown";
2767
+ const responseId = `resp_${Date.now()}`;
2768
+ const safeEnqueue = (str) => {
2769
+ if (!isClosed) {
2770
+ try {
2771
+ controller.enqueue(encoder.encode(str));
2772
+ } catch {
2773
+ isClosed = true;
2774
+ }
2775
+ }
2776
+ };
2777
+ const emitEvent = (event) => {
2778
+ safeEnqueue(`data: ${JSON.stringify(event)}
2779
+
2780
+ `);
2781
+ };
2782
+ emitEvent({
2783
+ type: "response.created",
2784
+ response: { id: responseId, status: "in_progress" }
2785
+ });
2786
+ try {
2787
+ while (true) {
2788
+ const { done, value } = await reader.read();
2789
+ if (done) break;
2790
+ buffer += decoder.decode(value, { stream: true });
2791
+ const lines = buffer.split("\n");
2792
+ buffer = lines.pop() || "";
2793
+ for (const line of lines) {
2794
+ if (isClosed) break;
2795
+ if (!line.startsWith("data:")) continue;
2796
+ const data = line.slice(5).trim();
2797
+ if (data === "[DONE]") continue;
2798
+ try {
2799
+ const chunk = JSON.parse(data);
2800
+ const choice = chunk.choices?.[0];
2801
+ model = chunk.model || model;
2802
+ if (!choice) continue;
2803
+ if (choice.delta?.content) {
2804
+ accumulatedContent += choice.delta.content;
2805
+ emitEvent({ type: "response.output_text.delta", delta: choice.delta.content });
2806
+ }
2807
+ if (choice.delta?.thinking?.content) {
2808
+ emitEvent({
2809
+ type: "response.reasoning_summary_text.delta",
2810
+ delta: choice.delta.thinking.content
2811
+ });
2812
+ }
2813
+ if (choice.finish_reason) {
2814
+ emitEvent({ type: "response.output_text.done", text: accumulatedContent });
2815
+ emitEvent({
2816
+ type: "response.completed",
2817
+ response: {
2818
+ id: responseId,
2819
+ status: "completed",
2820
+ model,
2821
+ output: [
2822
+ {
2823
+ type: "message",
2824
+ role: "assistant",
2825
+ content: [{ type: "output_text", text: accumulatedContent }]
2826
+ }
2827
+ ],
2828
+ usage: chunk.usage ? {
2829
+ input_tokens: chunk.usage.prompt_tokens || 0,
2830
+ output_tokens: chunk.usage.completion_tokens || 0,
2831
+ total_tokens: chunk.usage.total_tokens || 0
2832
+ } : void 0
2833
+ }
2834
+ });
2835
+ }
2836
+ } catch {
2837
+ }
2838
+ }
2839
+ }
2840
+ } catch (e) {
2841
+ if (!isClosed) controller.error(e);
2842
+ } finally {
2843
+ if (!isClosed) {
2844
+ try {
2845
+ controller.close();
2846
+ } catch {
2847
+ }
2848
+ }
2849
+ reader.releaseLock();
2850
+ }
2851
+ }
2852
+ });
2853
+ }
2854
+
2855
+ // src/transformer/transformers/ReasoningTransformer.ts
2856
+ var import_thinking_config = require("@omnicross/contracts/thinking-config");
2857
+
2858
+ // src/completion/StreamHandler.ts
2859
+ var import_thinking_config3 = require("@omnicross/contracts/thinking-config");
2860
+
2861
+ // src/completion/ThinkingResolver.ts
2862
+ var import_thinking_config4 = require("@omnicross/contracts/thinking-config");
2863
+
2864
+ // src/completion/message-converter.ts
2865
+ var MAX_INLINE_VIDEO_BYTES = 25 * 1024 * 1024;
2866
+
2867
+ // src/provider-proxy/ingress/providerProxyShared.ts
2868
+ var sharedExecutor = null;
2869
+ function getSharedExecutor() {
2870
+ if (!sharedExecutor) {
2871
+ sharedExecutor = new TransformerChainExecutor();
2872
+ }
2873
+ return sharedExecutor;
2874
+ }
2875
+ var sharedResponses = null;
2876
+ function getResponsesEndpointTransformer() {
2877
+ if (!sharedResponses) sharedResponses = new OpenAIResponseTransformer();
2878
+ return sharedResponses;
2879
+ }
2880
+ var sharedAnthropic = null;
2881
+ function getAnthropicEndpointTransformer() {
2882
+ if (!sharedAnthropic) sharedAnthropic = new AnthropicTransformer();
2883
+ return sharedAnthropic;
2884
+ }
2885
+ var sharedGemini = null;
2886
+ function getGeminiEndpointTransformer() {
2887
+ if (!sharedGemini) sharedGemini = new GeminiTransformer();
2888
+ return sharedGemini;
2889
+ }
2890
+ function readBody(req) {
2891
+ return new Promise((resolve, reject) => {
2892
+ const chunks = [];
2893
+ req.on("data", (chunk) => chunks.push(chunk));
2894
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
2895
+ req.on("error", reject);
2896
+ });
2897
+ }
2898
+ async function relayResponse(res, providerResponse, isStream) {
2899
+ const contentType = providerResponse.headers.get("Content-Type") ?? "";
2900
+ const status = providerResponse.status && providerResponse.status >= 100 ? providerResponse.status : 200;
2901
+ if (isStream || contentType.includes("text/event-stream")) {
2902
+ res.writeHead(status, {
2903
+ "Content-Type": "text/event-stream",
2904
+ "Cache-Control": "no-cache",
2905
+ Connection: "keep-alive"
2906
+ });
2907
+ if (!providerResponse.body) {
2908
+ res.end();
2909
+ return null;
2910
+ }
2911
+ const reader = providerResponse.body.getReader();
2912
+ try {
2913
+ for (; ; ) {
2914
+ const { done, value } = await reader.read();
2915
+ if (done) break;
2916
+ res.write(value);
2917
+ }
2918
+ } finally {
2919
+ reader.releaseLock();
2920
+ res.end();
2921
+ }
2922
+ return null;
2923
+ }
2924
+ const bodyText = await providerResponse.text();
2925
+ res.writeHead(status, { "Content-Type": contentType.includes("json") ? contentType : "application/json" });
2926
+ res.end(bodyText);
2927
+ return bodyText;
2928
+ }
2929
+ function writeError(res, status, message) {
2930
+ if (res.headersSent) return;
2931
+ res.writeHead(status, { "Content-Type": "application/json" });
2932
+ res.end(JSON.stringify({ error: { type: "provider_proxy_error", message } }));
2933
+ }
2934
+ function resolveApiKey(apiKey) {
2935
+ if (!apiKey) return "";
2936
+ if (apiKey.startsWith("$")) {
2937
+ return process.env[apiKey.slice(1)] || "";
2938
+ }
2939
+ return apiKey;
2940
+ }
2941
+ async function resolvePoolBoundKey(deps, providerId, provider, sessionId) {
2942
+ if (deps.apiKeyPool && sessionId) {
2943
+ const poolKey = await deps.apiKeyPool.getKeyForSession(providerId, sessionId);
2944
+ if (poolKey) return poolKey;
2945
+ }
2946
+ return resolveApiKey(resolveProviderEndpoint(provider).apiKey);
2947
+ }
2948
+ // Annotate the CommonJS export names for ESM import in node:
2949
+ 0 && (module.exports = {
2950
+ getAnthropicEndpointTransformer,
2951
+ getGeminiEndpointTransformer,
2952
+ getResponsesEndpointTransformer,
2953
+ getSharedExecutor,
2954
+ readBody,
2955
+ relayResponse,
2956
+ resolvePoolBoundKey,
2957
+ writeError
2958
+ });