@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,1013 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/transformer/transformers/GeminiTransformer.ts
21
+ var GeminiTransformer_exports = {};
22
+ __export(GeminiTransformer_exports, {
23
+ GeminiTransformer: () => GeminiTransformer
24
+ });
25
+ module.exports = __toCommonJS(GeminiTransformer_exports);
26
+
27
+ // src/transformer/transformers/utils/gemini.response-in.ts
28
+ var FINISH_REASON_TO_GEMINI = {
29
+ stop: "STOP",
30
+ length: "MAX_TOKENS",
31
+ tool_calls: "STOP",
32
+ content_filter: "SAFETY",
33
+ // Already-lowercased Gemini reasons (pass-through from another transformer)
34
+ max_tokens: "MAX_TOKENS",
35
+ safety: "SAFETY"
36
+ };
37
+ function toGeminiFinishReason(openaiReason) {
38
+ if (!openaiReason) return null;
39
+ return FINISH_REASON_TO_GEMINI[openaiReason] || openaiReason.toUpperCase();
40
+ }
41
+ function convertOpenAIResponseToGemini(openaiData) {
42
+ const choice = openaiData.choices?.[0];
43
+ const message = choice?.message ?? {};
44
+ const usage = openaiData.usage;
45
+ const usagePromptDetails = usage?.prompt_tokens_details;
46
+ const usageOutputDetails = usage?.output_tokens_details;
47
+ const parts = [];
48
+ const thinking = message.thinking;
49
+ if (thinking?.content) {
50
+ parts.push({ text: thinking.content, thought: true });
51
+ }
52
+ if (thinking?.signature) {
53
+ parts.push({ thoughtSignature: thinking.signature });
54
+ }
55
+ if (message.content) {
56
+ parts.push({ text: message.content });
57
+ }
58
+ const toolCalls = message.tool_calls;
59
+ if (toolCalls?.length) {
60
+ for (const tc of toolCalls) {
61
+ const func = tc.function;
62
+ let args = {};
63
+ try {
64
+ const raw = func.arguments;
65
+ args = typeof raw === "string" ? JSON.parse(raw) : raw;
66
+ } catch {
67
+ args = {};
68
+ }
69
+ parts.push({
70
+ functionCall: {
71
+ id: tc.id,
72
+ name: func.name,
73
+ args
74
+ }
75
+ });
76
+ }
77
+ }
78
+ if (parts.length === 0) {
79
+ parts.push({ text: "" });
80
+ }
81
+ return {
82
+ responseId: openaiData.id || "",
83
+ modelVersion: openaiData.model || "",
84
+ candidates: [{
85
+ content: { parts },
86
+ finishReason: toGeminiFinishReason(choice?.finish_reason)
87
+ }],
88
+ usageMetadata: usage ? {
89
+ promptTokenCount: usage.prompt_tokens || 0,
90
+ candidatesTokenCount: usage.completion_tokens || 0,
91
+ totalTokenCount: usage.total_tokens || 0,
92
+ cachedContentTokenCount: usagePromptDetails?.cached_tokens || 0,
93
+ thoughtsTokenCount: usageOutputDetails?.reasoning_tokens || 0
94
+ } : void 0
95
+ };
96
+ }
97
+ function convertOpenAIStreamToGemini(openaiStream, logger) {
98
+ const decoder = new TextDecoder();
99
+ const encoder = new TextEncoder();
100
+ const pendingToolCalls = /* @__PURE__ */ new Map();
101
+ let model = "";
102
+ let responseId = "";
103
+ return new ReadableStream({
104
+ start: async (controller) => {
105
+ const reader = openaiStream.getReader();
106
+ let buffer = "";
107
+ let isClosed = false;
108
+ const emit = (data) => {
109
+ if (isClosed) return;
110
+ try {
111
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
112
+
113
+ `));
114
+ } catch {
115
+ isClosed = true;
116
+ }
117
+ };
118
+ const flushToolCalls = () => {
119
+ if (pendingToolCalls.size === 0) return;
120
+ const parts = [];
121
+ for (const tc of pendingToolCalls.values()) {
122
+ let args = {};
123
+ try {
124
+ args = JSON.parse(tc.args || "{}");
125
+ } catch {
126
+ }
127
+ parts.push({
128
+ functionCall: { id: tc.id, name: tc.name, args }
129
+ });
130
+ }
131
+ pendingToolCalls.clear();
132
+ emit({
133
+ responseId,
134
+ modelVersion: model,
135
+ candidates: [{ content: { parts }, finishReason: null }]
136
+ });
137
+ };
138
+ try {
139
+ while (true) {
140
+ const { done, value } = await reader.read();
141
+ if (done) break;
142
+ buffer += decoder.decode(value, { stream: true });
143
+ const lines = buffer.split("\n");
144
+ buffer = lines.pop() || "";
145
+ for (const line of lines) {
146
+ if (isClosed) break;
147
+ if (!line.startsWith("data:")) continue;
148
+ const data = line.slice(5).trim();
149
+ if (!data || data === "[DONE]") continue;
150
+ try {
151
+ const chunk = JSON.parse(data);
152
+ if (!responseId && chunk.id) responseId = chunk.id;
153
+ if (!model && chunk.model) model = chunk.model;
154
+ const choice = chunk.choices?.[0];
155
+ if (!choice) continue;
156
+ const delta = choice.delta ?? {};
157
+ if (delta.thinking) {
158
+ const parts = [];
159
+ if (delta.thinking.content) {
160
+ parts.push({ text: delta.thinking.content, thought: true });
161
+ }
162
+ if (delta.thinking.signature) {
163
+ parts.push({ thoughtSignature: delta.thinking.signature });
164
+ }
165
+ if (parts.length > 0) {
166
+ emit({
167
+ responseId,
168
+ modelVersion: model,
169
+ candidates: [{ content: { parts }, finishReason: null }]
170
+ });
171
+ }
172
+ }
173
+ if (delta.content) {
174
+ emit({
175
+ responseId,
176
+ modelVersion: model,
177
+ candidates: [{
178
+ content: { parts: [{ text: delta.content }] },
179
+ finishReason: null
180
+ }]
181
+ });
182
+ }
183
+ if (delta.tool_calls) {
184
+ for (const tc of delta.tool_calls) {
185
+ const idx = tc.index ?? 0;
186
+ const existing = pendingToolCalls.get(idx);
187
+ const func = tc.function;
188
+ if (existing) {
189
+ if (func?.arguments) {
190
+ existing.args += func.arguments;
191
+ }
192
+ } else {
193
+ pendingToolCalls.set(idx, {
194
+ id: tc.id || `tool_${Date.now()}_${idx}`,
195
+ name: func?.name || "",
196
+ args: func?.arguments || ""
197
+ });
198
+ }
199
+ }
200
+ }
201
+ if (choice.finish_reason) {
202
+ flushToolCalls();
203
+ const geminiUsage = chunk.usage ? {
204
+ promptTokenCount: chunk.usage.prompt_tokens || 0,
205
+ candidatesTokenCount: chunk.usage.completion_tokens || 0,
206
+ totalTokenCount: chunk.usage.total_tokens || 0,
207
+ cachedContentTokenCount: chunk.usage.prompt_tokens_details?.cached_tokens || 0,
208
+ thoughtsTokenCount: chunk.usage.output_tokens_details?.reasoning_tokens || 0
209
+ } : void 0;
210
+ emit({
211
+ responseId,
212
+ modelVersion: model,
213
+ candidates: [{
214
+ content: { parts: [{ text: "" }] },
215
+ finishReason: toGeminiFinishReason(choice.finish_reason) || "STOP"
216
+ }],
217
+ usageMetadata: geminiUsage
218
+ });
219
+ }
220
+ } catch (e) {
221
+ logger?.error(`Error parsing OpenAI stream chunk for Gemini conversion: ${e}`);
222
+ }
223
+ }
224
+ }
225
+ flushToolCalls();
226
+ } catch (e) {
227
+ if (!isClosed) controller.error(e);
228
+ } finally {
229
+ if (!isClosed) {
230
+ try {
231
+ controller.close();
232
+ } catch {
233
+ }
234
+ }
235
+ reader.releaseLock();
236
+ }
237
+ }
238
+ });
239
+ }
240
+ async function transformResponseIn(response, logger) {
241
+ const contentType = response.headers.get("Content-Type") ?? "";
242
+ if (contentType.includes("text/event-stream")) {
243
+ if (!response.body) {
244
+ throw new Error("Stream response body is null");
245
+ }
246
+ const geminiStream = convertOpenAIStreamToGemini(response.body, logger);
247
+ return new Response(geminiStream, {
248
+ headers: {
249
+ "Content-Type": "text/event-stream",
250
+ "Cache-Control": "no-cache",
251
+ Connection: "keep-alive"
252
+ }
253
+ });
254
+ }
255
+ const data = await response.json();
256
+ const geminiResponse = convertOpenAIResponseToGemini(data);
257
+ return new Response(JSON.stringify(geminiResponse), {
258
+ status: response.status,
259
+ statusText: response.statusText,
260
+ headers: { "Content-Type": "application/json" }
261
+ });
262
+ }
263
+
264
+ // src/transformer/transformers/utils/gemini.stream.ts
265
+ async function transformResponseOut(response, providerName, logger) {
266
+ const contentType = response.headers.get("Content-Type") ?? "";
267
+ if (contentType.includes("application/json")) {
268
+ return handleJsonResponse(response, providerName, logger);
269
+ } else if (contentType.includes("stream") || contentType.includes("text/event-stream")) {
270
+ return handleStreamResponse(response, providerName, logger);
271
+ }
272
+ return response;
273
+ }
274
+ async function handleJsonResponse(response, providerName, logger) {
275
+ const jsonResponse = await response.json();
276
+ logger?.debug(`${providerName} JSON response received`);
277
+ const parts = jsonResponse.candidates?.[0]?.content?.parts || [];
278
+ let thinkingContent = "";
279
+ let thinkingSignature = "";
280
+ const nonThinkingParts = [];
281
+ for (const part of parts) {
282
+ if (part.text && part.thought === true) {
283
+ thinkingContent += part.text;
284
+ } else {
285
+ nonThinkingParts.push(part);
286
+ }
287
+ }
288
+ thinkingSignature = parts.find((part) => part.thoughtSignature)?.thoughtSignature ?? "";
289
+ const toolCalls = nonThinkingParts.filter((part) => part.functionCall).map((part) => ({
290
+ id: part.functionCall?.id || `tool_${Math.random().toString(36).substring(2, 15)}`,
291
+ type: "function",
292
+ function: {
293
+ name: part.functionCall?.name ?? "",
294
+ arguments: JSON.stringify(part.functionCall?.args || {})
295
+ }
296
+ }));
297
+ const textContent = nonThinkingParts.filter((part) => part.text).map((part) => part.text).join("\n");
298
+ const openAIResponse = {
299
+ id: jsonResponse.responseId ?? "",
300
+ choices: [
301
+ {
302
+ finish_reason: (jsonResponse.candidates?.[0]?.finishReason ?? "").toLowerCase() || null,
303
+ index: 0,
304
+ message: {
305
+ content: textContent,
306
+ role: "assistant",
307
+ tool_calls: toolCalls.length > 0 ? toolCalls : void 0,
308
+ ...thinkingSignature && {
309
+ thinking: {
310
+ content: thinkingContent || "(no content)",
311
+ signature: thinkingSignature
312
+ }
313
+ }
314
+ }
315
+ }
316
+ ],
317
+ created: Math.floor(Date.now() / 1e3),
318
+ model: jsonResponse.modelVersion ?? "",
319
+ object: "chat.completion",
320
+ usage: {
321
+ completion_tokens: jsonResponse.usageMetadata?.candidatesTokenCount || 0,
322
+ prompt_tokens: jsonResponse.usageMetadata?.promptTokenCount || 0,
323
+ prompt_tokens_details: {
324
+ cached_tokens: jsonResponse.usageMetadata?.cachedContentTokenCount || 0
325
+ },
326
+ total_tokens: jsonResponse.usageMetadata?.totalTokenCount || 0,
327
+ output_tokens_details: {
328
+ reasoning_tokens: jsonResponse.usageMetadata?.thoughtsTokenCount || 0
329
+ }
330
+ }
331
+ };
332
+ return new Response(JSON.stringify(openAIResponse), {
333
+ status: response.status,
334
+ statusText: response.statusText,
335
+ headers: response.headers
336
+ });
337
+ }
338
+ function handleStreamResponse(response, providerName, logger) {
339
+ if (!response.body) {
340
+ return response;
341
+ }
342
+ const decoder = new TextDecoder();
343
+ const encoder = new TextEncoder();
344
+ let signatureSent = false;
345
+ let contentSent = false;
346
+ let hasThinkingContent = false;
347
+ let pendingContent = "";
348
+ let contentIndex = 0;
349
+ let toolCallIndex = -1;
350
+ const stream = new ReadableStream({
351
+ async start(controller) {
352
+ const reader = response.body.getReader();
353
+ let buffer = "";
354
+ const processLine = async (line) => {
355
+ if (!line.startsWith("data: ")) return;
356
+ const chunkStr = line.slice(6).trim();
357
+ if (!chunkStr) return;
358
+ logger?.debug(`${providerName} chunk: ${chunkStr.substring(0, 100)}...`);
359
+ try {
360
+ const chunk = JSON.parse(chunkStr);
361
+ if (!chunk.candidates?.[0]) {
362
+ logger?.debug("Invalid chunk structure");
363
+ return;
364
+ }
365
+ const candidate = chunk.candidates[0];
366
+ const parts = candidate.content?.parts || [];
367
+ parts.filter((part) => part.text && part.thought === true).forEach((part) => {
368
+ hasThinkingContent = true;
369
+ const thinkingChunk = createChunk({
370
+ responseId: chunk.responseId,
371
+ modelVersion: chunk.modelVersion,
372
+ contentIndex,
373
+ delta: {
374
+ role: "assistant",
375
+ content: null,
376
+ thinking: { content: part.text }
377
+ }
378
+ });
379
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(thinkingChunk)}
380
+
381
+ `));
382
+ });
383
+ const signature = parts.find((part) => part.thoughtSignature)?.thoughtSignature;
384
+ if (signature && !signatureSent) {
385
+ if (!hasThinkingContent) {
386
+ const thinkingChunk = createChunk({
387
+ responseId: chunk.responseId,
388
+ modelVersion: chunk.modelVersion,
389
+ contentIndex,
390
+ delta: {
391
+ role: "assistant",
392
+ content: null,
393
+ thinking: { content: "" }
394
+ }
395
+ });
396
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(thinkingChunk)}
397
+
398
+ `));
399
+ }
400
+ const signatureChunk = createChunk({
401
+ responseId: chunk.responseId,
402
+ modelVersion: chunk.modelVersion,
403
+ contentIndex,
404
+ delta: {
405
+ role: "assistant",
406
+ content: null,
407
+ thinking: { signature }
408
+ }
409
+ });
410
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(signatureChunk)}
411
+
412
+ `));
413
+ signatureSent = true;
414
+ contentIndex++;
415
+ if (pendingContent) {
416
+ const pendingChunk = createChunk({
417
+ responseId: chunk.responseId,
418
+ modelVersion: chunk.modelVersion,
419
+ contentIndex,
420
+ delta: { role: "assistant", content: pendingContent }
421
+ });
422
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(pendingChunk)}
423
+
424
+ `));
425
+ pendingContent = "";
426
+ contentSent = true;
427
+ }
428
+ }
429
+ const toolCalls = parts.filter((part) => part.functionCall).map((part) => ({
430
+ id: part.functionCall?.id || `ccr_tool_${Math.random().toString(36).substring(2, 15)}`,
431
+ type: "function",
432
+ function: {
433
+ name: part.functionCall?.name ?? "",
434
+ arguments: JSON.stringify(part.functionCall?.args || {})
435
+ }
436
+ }));
437
+ const textContent = parts.filter((part) => part.text && part.thought !== true).map((part) => part.text).join("\n");
438
+ if (!textContent && signatureSent && !contentSent) {
439
+ contentSent = true;
440
+ }
441
+ if (hasThinkingContent && textContent && !signatureSent) {
442
+ if (chunk.modelVersion?.includes("3")) {
443
+ pendingContent += textContent;
444
+ return;
445
+ } else {
446
+ const signatureChunk = createChunk({
447
+ responseId: chunk.responseId,
448
+ modelVersion: chunk.modelVersion,
449
+ contentIndex,
450
+ delta: {
451
+ role: "assistant",
452
+ content: null,
453
+ thinking: { signature: `ccr_${Date.now()}` }
454
+ }
455
+ });
456
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(signatureChunk)}
457
+
458
+ `));
459
+ signatureSent = true;
460
+ }
461
+ }
462
+ if (textContent) {
463
+ if (!pendingContent) contentIndex++;
464
+ const contentChunk = createChunk({
465
+ responseId: chunk.responseId,
466
+ modelVersion: chunk.modelVersion,
467
+ contentIndex,
468
+ delta: { role: "assistant", content: textContent },
469
+ finishReason: candidate.finishReason,
470
+ usageMetadata: chunk.usageMetadata,
471
+ groundingMetadata: candidate.groundingMetadata
472
+ });
473
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(contentChunk)}
474
+
475
+ `));
476
+ contentSent = true;
477
+ }
478
+ if (toolCalls.length > 0) {
479
+ for (const tool of toolCalls) {
480
+ contentIndex++;
481
+ toolCallIndex++;
482
+ const toolChunk = createChunk({
483
+ responseId: chunk.responseId,
484
+ modelVersion: chunk.modelVersion,
485
+ contentIndex,
486
+ delta: {
487
+ role: "assistant",
488
+ tool_calls: [{ ...tool, index: toolCallIndex }]
489
+ },
490
+ finishReason: candidate.finishReason,
491
+ usageMetadata: chunk.usageMetadata,
492
+ groundingMetadata: candidate.groundingMetadata
493
+ });
494
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(toolChunk)}
495
+
496
+ `));
497
+ }
498
+ contentSent = true;
499
+ }
500
+ } catch (_error) {
501
+ logger?.error(`Error parsing ${providerName} stream chunk: ${chunkStr}`);
502
+ }
503
+ };
504
+ try {
505
+ while (true) {
506
+ const { done, value } = await reader.read();
507
+ if (done) {
508
+ if (buffer) await processLine(buffer);
509
+ break;
510
+ }
511
+ buffer += decoder.decode(value, { stream: true });
512
+ const lines = buffer.split("\n");
513
+ buffer = lines.pop() || "";
514
+ for (const line of lines) {
515
+ await processLine(line);
516
+ }
517
+ }
518
+ } catch (error) {
519
+ controller.error(error);
520
+ } finally {
521
+ controller.close();
522
+ }
523
+ }
524
+ });
525
+ return new Response(stream, {
526
+ status: response.status,
527
+ statusText: response.statusText,
528
+ headers: response.headers
529
+ });
530
+ }
531
+ function createChunk(options) {
532
+ const {
533
+ responseId,
534
+ modelVersion,
535
+ contentIndex,
536
+ delta,
537
+ finishReason,
538
+ usageMetadata,
539
+ groundingMetadata
540
+ } = options;
541
+ const chunk = {
542
+ choices: [
543
+ {
544
+ delta,
545
+ finish_reason: finishReason?.toLowerCase() || null,
546
+ index: contentIndex,
547
+ logprobs: null
548
+ }
549
+ ],
550
+ created: Math.floor(Date.now() / 1e3),
551
+ id: responseId || "",
552
+ model: modelVersion || "",
553
+ object: "chat.completion.chunk",
554
+ system_fingerprint: "fp_a49d71b8a1"
555
+ };
556
+ if (usageMetadata) {
557
+ chunk.usage = {
558
+ completion_tokens: usageMetadata.candidatesTokenCount || 0,
559
+ prompt_tokens: usageMetadata.promptTokenCount || 0,
560
+ prompt_tokens_details: {
561
+ cached_tokens: usageMetadata.cachedContentTokenCount || 0
562
+ },
563
+ total_tokens: usageMetadata.totalTokenCount || 0,
564
+ output_tokens_details: {
565
+ reasoning_tokens: usageMetadata.thoughtsTokenCount || 0
566
+ }
567
+ };
568
+ }
569
+ if (groundingMetadata?.groundingChunks?.length) {
570
+ const annotations = groundingMetadata.groundingChunks.map((groundingChunk, index) => {
571
+ const support = groundingMetadata.groundingSupports?.find(
572
+ (s) => s.groundingChunkIndices?.includes(index)
573
+ );
574
+ return {
575
+ type: "url_citation",
576
+ url_citation: {
577
+ url: groundingChunk.web?.uri || "",
578
+ title: groundingChunk.web?.title || "",
579
+ content: support?.segment?.text || "",
580
+ start_index: support?.segment?.startIndex || 0,
581
+ end_index: support?.segment?.endIndex || 0
582
+ }
583
+ };
584
+ });
585
+ chunk.choices[0].delta.annotations = annotations;
586
+ }
587
+ return chunk;
588
+ }
589
+
590
+ // src/transformer/transformers/utils/gemini.schema.ts
591
+ var GeminiType = {
592
+ TYPE_UNSPECIFIED: "TYPE_UNSPECIFIED",
593
+ STRING: "STRING",
594
+ NUMBER: "NUMBER",
595
+ INTEGER: "INTEGER",
596
+ BOOLEAN: "BOOLEAN",
597
+ ARRAY: "ARRAY",
598
+ OBJECT: "OBJECT",
599
+ NULL: "NULL"
600
+ };
601
+ function flattenTypeArrayToAnyOf(typeList, resultingSchema) {
602
+ if (typeList.includes("null")) {
603
+ resultingSchema.nullable = true;
604
+ }
605
+ const listWithoutNull = typeList.filter((type) => type !== "null");
606
+ if (listWithoutNull.length === 1) {
607
+ const upperCaseType = listWithoutNull[0].toUpperCase();
608
+ resultingSchema.type = Object.values(GeminiType).includes(upperCaseType) ? upperCaseType : GeminiType.TYPE_UNSPECIFIED;
609
+ } else {
610
+ resultingSchema.anyOf = listWithoutNull.map((typeName) => {
611
+ const upperCaseType = typeName.toUpperCase();
612
+ return {
613
+ type: Object.values(GeminiType).includes(upperCaseType) ? upperCaseType : GeminiType.TYPE_UNSPECIFIED
614
+ };
615
+ });
616
+ }
617
+ }
618
+ function processJsonSchema(jsonSchema) {
619
+ const genAISchema = {};
620
+ const schemaFieldNames = ["items"];
621
+ const listSchemaFieldNames = ["anyOf"];
622
+ const dictSchemaFieldNames = ["properties"];
623
+ let workingSchema = jsonSchema;
624
+ if (workingSchema.type && workingSchema.anyOf) {
625
+ throw new Error("type and anyOf cannot be both populated.");
626
+ }
627
+ const incomingAnyOf = workingSchema.anyOf;
628
+ if (incomingAnyOf && Array.isArray(incomingAnyOf) && incomingAnyOf.length === 2) {
629
+ if (incomingAnyOf[0]?.type === "null") {
630
+ genAISchema.nullable = true;
631
+ workingSchema = incomingAnyOf[1];
632
+ } else if (incomingAnyOf[1]?.type === "null") {
633
+ genAISchema.nullable = true;
634
+ workingSchema = incomingAnyOf[0];
635
+ }
636
+ }
637
+ if (workingSchema.type && Array.isArray(workingSchema.type)) {
638
+ flattenTypeArrayToAnyOf(workingSchema.type, genAISchema);
639
+ }
640
+ for (const [fieldName, fieldValue] of Object.entries(workingSchema)) {
641
+ if (fieldValue == null) {
642
+ continue;
643
+ }
644
+ if (fieldName === "type") {
645
+ if (fieldValue === "null") {
646
+ throw new Error("type: null cannot be the only possible type for the field.");
647
+ }
648
+ if (Array.isArray(fieldValue)) {
649
+ continue;
650
+ }
651
+ const upperCaseValue = fieldValue.toUpperCase();
652
+ genAISchema.type = Object.values(GeminiType).includes(upperCaseValue) ? upperCaseValue : GeminiType.TYPE_UNSPECIFIED;
653
+ } else if (schemaFieldNames.includes(fieldName)) {
654
+ genAISchema[fieldName] = processJsonSchema(fieldValue);
655
+ } else if (listSchemaFieldNames.includes(fieldName)) {
656
+ const listValue = [];
657
+ for (const item of fieldValue) {
658
+ if (item.type === "null") {
659
+ genAISchema.nullable = true;
660
+ continue;
661
+ }
662
+ listValue.push(processJsonSchema(item));
663
+ }
664
+ genAISchema[fieldName] = listValue;
665
+ } else if (dictSchemaFieldNames.includes(fieldName)) {
666
+ const dictValue = {};
667
+ for (const [key, value] of Object.entries(fieldValue)) {
668
+ dictValue[key] = processJsonSchema(value);
669
+ }
670
+ genAISchema[fieldName] = dictValue;
671
+ } else {
672
+ if (fieldName === "additionalProperties") {
673
+ continue;
674
+ }
675
+ genAISchema[fieldName] = fieldValue;
676
+ }
677
+ }
678
+ return genAISchema;
679
+ }
680
+ function transformTool(tool) {
681
+ const functionDeclarations = tool.functionDeclarations;
682
+ if (functionDeclarations) {
683
+ for (const functionDeclaration of functionDeclarations) {
684
+ if (functionDeclaration.parameters) {
685
+ const params = functionDeclaration.parameters;
686
+ if (!Object.keys(params).includes("$schema")) {
687
+ functionDeclaration.parameters = processJsonSchema(params);
688
+ } else {
689
+ if (!functionDeclaration.parametersJsonSchema) {
690
+ functionDeclaration.parametersJsonSchema = functionDeclaration.parameters;
691
+ delete functionDeclaration.parameters;
692
+ }
693
+ }
694
+ }
695
+ if (functionDeclaration.response) {
696
+ const response = functionDeclaration.response;
697
+ if (!Object.keys(response).includes("$schema")) {
698
+ functionDeclaration.response = processJsonSchema(response);
699
+ } else {
700
+ if (!functionDeclaration.responseJsonSchema) {
701
+ functionDeclaration.responseJsonSchema = functionDeclaration.response;
702
+ delete functionDeclaration.response;
703
+ }
704
+ }
705
+ }
706
+ }
707
+ }
708
+ return tool;
709
+ }
710
+
711
+ // src/transformer/transformers/utils/gemini.util.ts
712
+ function buildRequestBody(request) {
713
+ const tools = [];
714
+ const functionDeclarations = request.tools?.filter((tool) => tool.function.name !== "web_search")?.map((tool) => ({
715
+ name: tool.function.name,
716
+ description: tool.function.description,
717
+ parametersJsonSchema: tool.function.parameters
718
+ }));
719
+ if (functionDeclarations?.length) {
720
+ tools.push(
721
+ transformTool({
722
+ functionDeclarations
723
+ })
724
+ );
725
+ }
726
+ const webSearch = request.tools?.find((tool) => tool.function.name === "web_search");
727
+ if (webSearch) {
728
+ tools.push({ googleSearch: {} });
729
+ }
730
+ const contents = [];
731
+ const toolResponses = request.messages.filter((item) => item.role === "tool");
732
+ request.messages.filter((item) => item.role !== "tool").forEach((message) => {
733
+ let role;
734
+ if (message.role === "assistant") {
735
+ role = "model";
736
+ } else if (["user", "system"].includes(message.role)) {
737
+ role = "user";
738
+ } else {
739
+ role = "user";
740
+ }
741
+ const parts = [];
742
+ if (typeof message.content === "string") {
743
+ const part = { text: message.content };
744
+ if (message.thinking?.signature) {
745
+ part.thoughtSignature = message.thinking.signature;
746
+ }
747
+ parts.push(part);
748
+ } else if (Array.isArray(message.content)) {
749
+ for (const content of message.content) {
750
+ if (content.type === "text") {
751
+ parts.push({ text: content.text || "" });
752
+ } else if (content.type === "image_url") {
753
+ const imageUrl = content.image_url?.url ?? "";
754
+ if (imageUrl.startsWith("http")) {
755
+ parts.push({
756
+ file_data: {
757
+ mime_type: content.media_type,
758
+ file_uri: imageUrl
759
+ }
760
+ });
761
+ } else {
762
+ const data = imageUrl.split(",").pop() || imageUrl;
763
+ parts.push({
764
+ inlineData: {
765
+ mime_type: content.media_type || "image/png",
766
+ data
767
+ }
768
+ });
769
+ }
770
+ }
771
+ }
772
+ } else if (message.content && typeof message.content === "object") {
773
+ const contentObj = message.content;
774
+ if (contentObj.text) {
775
+ parts.push({ text: contentObj.text });
776
+ } else {
777
+ parts.push({ text: JSON.stringify(message.content) });
778
+ }
779
+ }
780
+ if (Array.isArray(message.tool_calls)) {
781
+ for (let index = 0; index < message.tool_calls.length; index++) {
782
+ const toolCall = message.tool_calls[index];
783
+ const functionCallPart = {
784
+ functionCall: {
785
+ id: toolCall.id || `tool_${Math.random().toString(36).substring(2, 15)}`,
786
+ name: toolCall.function.name,
787
+ args: JSON.parse(toolCall.function.arguments || "{}")
788
+ }
789
+ };
790
+ if (index === 0 && message.thinking?.signature) {
791
+ functionCallPart.thoughtSignature = message.thinking.signature;
792
+ }
793
+ parts.push(functionCallPart);
794
+ }
795
+ }
796
+ if (parts.length === 0) {
797
+ parts.push({ text: "" });
798
+ }
799
+ contents.push({ role, parts });
800
+ if (role === "model" && message.tool_calls) {
801
+ const functionResponses = message.tool_calls.map(
802
+ (tool) => {
803
+ const response = toolResponses.find((item) => item.tool_call_id === tool.id);
804
+ return {
805
+ functionResponse: {
806
+ name: tool.function?.name ?? "",
807
+ response: { result: response?.content }
808
+ }
809
+ };
810
+ }
811
+ );
812
+ contents.push({
813
+ role: "user",
814
+ parts: functionResponses
815
+ });
816
+ }
817
+ });
818
+ const generationConfig = {};
819
+ if (request.reasoning?.effort && request.reasoning.effort !== "none") {
820
+ generationConfig.thinkingConfig = {
821
+ includeThoughts: true
822
+ };
823
+ if (request.model.includes("gemini-3")) {
824
+ generationConfig.thinkingConfig.thinkingLevel = request.reasoning.effort;
825
+ } else {
826
+ const thinkingBudgets = request.model.includes("pro") ? [128, 32768] : [0, 24576];
827
+ const maxTokens = request.reasoning.max_tokens;
828
+ if (typeof maxTokens !== "undefined") {
829
+ let thinkingBudget;
830
+ if (maxTokens >= thinkingBudgets[0] && maxTokens <= thinkingBudgets[1]) {
831
+ thinkingBudget = maxTokens;
832
+ } else if (maxTokens < thinkingBudgets[0]) {
833
+ thinkingBudget = thinkingBudgets[0];
834
+ } else {
835
+ thinkingBudget = thinkingBudgets[1];
836
+ }
837
+ generationConfig.thinkingConfig.thinkingBudget = thinkingBudget;
838
+ }
839
+ }
840
+ }
841
+ const body = {
842
+ contents,
843
+ tools: tools.length > 0 ? tools : void 0,
844
+ generationConfig: Object.keys(generationConfig).length > 0 ? generationConfig : void 0
845
+ };
846
+ if (request.tool_choice) {
847
+ const toolConfig = {
848
+ functionCallingConfig: {}
849
+ };
850
+ if (request.tool_choice === "auto") {
851
+ toolConfig.functionCallingConfig.mode = "auto";
852
+ } else if (request.tool_choice === "none") {
853
+ toolConfig.functionCallingConfig.mode = "none";
854
+ } else if (request.tool_choice === "required") {
855
+ toolConfig.functionCallingConfig.mode = "any";
856
+ } else if (typeof request.tool_choice === "object" && request.tool_choice.function?.name) {
857
+ toolConfig.functionCallingConfig.mode = "any";
858
+ toolConfig.functionCallingConfig.allowedFunctionNames = [
859
+ request.tool_choice.function.name
860
+ ];
861
+ }
862
+ body.toolConfig = toolConfig;
863
+ }
864
+ return body;
865
+ }
866
+ function transformRequestOut(request) {
867
+ const contents = request.contents;
868
+ const tools = request.tools;
869
+ const model = request.model;
870
+ const maxTokens = request.max_tokens;
871
+ const temperature = request.temperature;
872
+ const stream = request.stream;
873
+ const toolChoice = request.tool_choice;
874
+ const unifiedRequest = {
875
+ messages: [],
876
+ model,
877
+ max_tokens: maxTokens,
878
+ temperature,
879
+ stream,
880
+ tool_choice: toolChoice
881
+ };
882
+ if (Array.isArray(contents)) {
883
+ for (const content of contents) {
884
+ if (typeof content === "string") {
885
+ unifiedRequest.messages.push({
886
+ role: "user",
887
+ content
888
+ });
889
+ } else if ("text" in content && typeof content.text === "string") {
890
+ unifiedRequest.messages.push({
891
+ role: "user",
892
+ content: content.text || null
893
+ });
894
+ } else if ("role" in content && content.role === "user") {
895
+ const geminiContent = content;
896
+ unifiedRequest.messages.push({
897
+ role: "user",
898
+ content: geminiContent.parts?.map((part) => ({
899
+ type: "text",
900
+ text: part.text || ""
901
+ })) || []
902
+ });
903
+ } else if (content.role === "model") {
904
+ unifiedRequest.messages.push({
905
+ role: "assistant",
906
+ content: content.parts?.map((part) => ({
907
+ type: "text",
908
+ text: part.text || ""
909
+ })) || []
910
+ });
911
+ }
912
+ }
913
+ }
914
+ if (Array.isArray(tools)) {
915
+ unifiedRequest.tools = [];
916
+ for (const tool of tools) {
917
+ if (Array.isArray(tool.functionDeclarations)) {
918
+ for (const funcDecl of tool.functionDeclarations) {
919
+ unifiedRequest.tools.push({
920
+ type: "function",
921
+ function: {
922
+ name: funcDecl.name,
923
+ description: funcDecl.description ?? "",
924
+ parameters: funcDecl.parameters ?? {}
925
+ }
926
+ });
927
+ }
928
+ }
929
+ }
930
+ }
931
+ return unifiedRequest;
932
+ }
933
+
934
+ // src/transformer/transformers/GeminiTransformer.ts
935
+ var GeminiTransformer = class {
936
+ static TransformerName = "gemini";
937
+ name = "gemini";
938
+ logger;
939
+ /**
940
+ * API endpoint pattern for Gemini
941
+ * :modelAndAction will be replaced with actual model and action
942
+ */
943
+ endPoint = "/v1beta/models/:modelAndAction";
944
+ /** Use Bearer token instead of x-goog-api-key (for relay providers) */
945
+ useBearer;
946
+ constructor(options) {
947
+ this.useBearer = options?.UseBearer ?? false;
948
+ }
949
+ /**
950
+ * Handle authentication
951
+ * - Official Gemini: x-goog-api-key header
952
+ * - Relay providers: Authorization: Bearer header
953
+ */
954
+ async auth(request, provider, _context) {
955
+ const headers = {};
956
+ if (this.useBearer) {
957
+ headers["authorization"] = `Bearer ${provider.apiKey}`;
958
+ headers["x-goog-api-key"] = void 0;
959
+ } else {
960
+ headers["x-goog-api-key"] = provider.apiKey;
961
+ headers["authorization"] = void 0;
962
+ }
963
+ return {
964
+ body: request,
965
+ config: { headers }
966
+ };
967
+ }
968
+ /**
969
+ * Transform request from unified format to Gemini format
970
+ * Also builds the correct URL for the Gemini API
971
+ */
972
+ async transformRequestIn(request, provider, _context) {
973
+ const body = buildRequestBody(request);
974
+ const action = request.stream ? "streamGenerateContent?alt=sse" : "generateContent";
975
+ const url = new URL(`./${request.model}:${action}`, provider.baseUrl);
976
+ const headers = {};
977
+ if (this.useBearer) {
978
+ headers["authorization"] = `Bearer ${provider.apiKey}`;
979
+ headers["x-goog-api-key"] = void 0;
980
+ } else {
981
+ headers["x-goog-api-key"] = provider.apiKey;
982
+ headers["Authorization"] = void 0;
983
+ }
984
+ return {
985
+ body,
986
+ config: { url, headers }
987
+ };
988
+ }
989
+ /**
990
+ * Transform incoming request to unified format
991
+ * (For requests coming into the Gemini endpoint)
992
+ */
993
+ async transformRequestOut(request, _context) {
994
+ return transformRequestOut(request);
995
+ }
996
+ /**
997
+ * Transform Gemini response to OpenAI-compatible format
998
+ */
999
+ async transformResponseOut(response, _context) {
1000
+ return transformResponseOut(response, this.name, this.logger);
1001
+ }
1002
+ /**
1003
+ * Transform OpenAI-compatible response back to Gemini format
1004
+ * (For endpoint mode — returning Gemini-format responses to the client)
1005
+ */
1006
+ async transformResponseIn(response, _context) {
1007
+ return transformResponseIn(response, this.logger);
1008
+ }
1009
+ };
1010
+ // Annotate the CommonJS export names for ESM import in node:
1011
+ 0 && (module.exports = {
1012
+ GeminiTransformer
1013
+ });