@ank1015/providers 0.0.1 → 0.0.2

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 (169) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -383
  3. package/dist/agent/conversation.d.ts +97 -0
  4. package/dist/agent/conversation.d.ts.map +1 -0
  5. package/dist/agent/conversation.js +328 -0
  6. package/dist/agent/conversation.js.map +1 -0
  7. package/dist/agent/runner.d.ts +37 -0
  8. package/dist/agent/runner.d.ts.map +1 -0
  9. package/dist/agent/runner.js +169 -0
  10. package/dist/agent/runner.js.map +1 -0
  11. package/dist/agent/tools/calculate.d.ts +15 -0
  12. package/dist/agent/tools/calculate.d.ts.map +1 -0
  13. package/dist/agent/tools/calculate.js +23 -0
  14. package/dist/agent/tools/calculate.js.map +1 -0
  15. package/dist/agent/tools/get-current-time.d.ts +15 -0
  16. package/dist/agent/tools/get-current-time.d.ts.map +1 -0
  17. package/dist/agent/tools/get-current-time.js +38 -0
  18. package/dist/agent/tools/get-current-time.js.map +1 -0
  19. package/dist/agent/tools/index.d.ts +3 -0
  20. package/dist/agent/tools/index.d.ts.map +1 -0
  21. package/dist/agent/tools/index.js +3 -0
  22. package/dist/agent/tools/index.js.map +1 -0
  23. package/dist/agent/types.d.ts +53 -31
  24. package/dist/agent/types.d.ts.map +1 -1
  25. package/dist/agent/types.js +1 -2
  26. package/dist/agent/utils.d.ts +14 -0
  27. package/dist/agent/utils.d.ts.map +1 -0
  28. package/dist/agent/utils.js +59 -0
  29. package/dist/agent/utils.js.map +1 -0
  30. package/dist/index.d.ts +16 -9
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +16 -28
  33. package/dist/index.js.map +1 -1
  34. package/dist/llm.d.ts +15 -0
  35. package/dist/llm.d.ts.map +1 -0
  36. package/dist/llm.js +92 -0
  37. package/dist/llm.js.map +1 -0
  38. package/dist/models.d.ts +8 -1
  39. package/dist/models.d.ts.map +1 -1
  40. package/dist/models.generated.d.ts +25 -112
  41. package/dist/models.generated.d.ts.map +1 -1
  42. package/dist/models.generated.js +72 -227
  43. package/dist/models.generated.js.map +1 -1
  44. package/dist/models.js +30 -32
  45. package/dist/models.js.map +1 -1
  46. package/dist/providers/google/complete.d.ts +3 -0
  47. package/dist/providers/google/complete.d.ts.map +1 -0
  48. package/dist/providers/google/complete.js +53 -0
  49. package/dist/providers/google/complete.js.map +1 -0
  50. package/dist/providers/google/index.d.ts +6 -0
  51. package/dist/providers/google/index.d.ts.map +1 -0
  52. package/dist/providers/google/index.js +6 -0
  53. package/dist/providers/google/index.js.map +1 -0
  54. package/dist/providers/google/stream.d.ts +3 -0
  55. package/dist/providers/google/stream.d.ts.map +1 -0
  56. package/dist/providers/{google.js → google/stream.js} +67 -231
  57. package/dist/providers/google/stream.js.map +1 -0
  58. package/dist/providers/google/types.d.ts +8 -0
  59. package/dist/providers/google/types.d.ts.map +1 -0
  60. package/dist/providers/google/types.js +2 -0
  61. package/dist/providers/google/types.js.map +1 -0
  62. package/dist/providers/google/utils.d.ts +30 -0
  63. package/dist/providers/google/utils.d.ts.map +1 -0
  64. package/dist/providers/google/utils.js +354 -0
  65. package/dist/providers/google/utils.js.map +1 -0
  66. package/dist/providers/openai/complete.d.ts +3 -0
  67. package/dist/providers/openai/complete.d.ts.map +1 -0
  68. package/dist/providers/openai/complete.js +57 -0
  69. package/dist/providers/openai/complete.js.map +1 -0
  70. package/dist/providers/openai/index.d.ts +4 -0
  71. package/dist/providers/openai/index.d.ts.map +1 -0
  72. package/dist/providers/openai/index.js +4 -0
  73. package/dist/providers/openai/index.js.map +1 -0
  74. package/dist/providers/openai/stream.d.ts +3 -0
  75. package/dist/providers/openai/stream.d.ts.map +1 -0
  76. package/dist/providers/{openai.js → openai/stream.js} +74 -152
  77. package/dist/providers/openai/stream.js.map +1 -0
  78. package/dist/providers/openai/types.d.ts +8 -0
  79. package/dist/providers/openai/types.d.ts.map +1 -0
  80. package/dist/providers/openai/types.js +2 -0
  81. package/dist/providers/openai/types.js.map +1 -0
  82. package/dist/providers/openai/utils.d.ts +13 -0
  83. package/dist/providers/openai/utils.d.ts.map +1 -0
  84. package/dist/providers/openai/utils.js +285 -0
  85. package/dist/providers/openai/utils.js.map +1 -0
  86. package/dist/types.d.ts +95 -87
  87. package/dist/types.d.ts.map +1 -1
  88. package/dist/types.js +1 -9
  89. package/dist/types.js.map +1 -1
  90. package/dist/utils/event-stream.d.ts +2 -2
  91. package/dist/utils/event-stream.d.ts.map +1 -1
  92. package/dist/utils/event-stream.js +2 -7
  93. package/dist/utils/event-stream.js.map +1 -1
  94. package/dist/utils/json-parse.js +3 -6
  95. package/dist/utils/json-parse.js.map +1 -1
  96. package/dist/utils/overflow.d.ts +51 -0
  97. package/dist/utils/overflow.d.ts.map +1 -0
  98. package/dist/utils/overflow.js +106 -0
  99. package/dist/utils/overflow.js.map +1 -0
  100. package/dist/utils/sanitize-unicode.js +1 -4
  101. package/dist/utils/sanitize-unicode.js.map +1 -1
  102. package/dist/utils/uuid.d.ts +6 -0
  103. package/dist/utils/uuid.d.ts.map +1 -0
  104. package/dist/utils/uuid.js +9 -0
  105. package/dist/utils/uuid.js.map +1 -0
  106. package/dist/utils/validation.d.ts +10 -3
  107. package/dist/utils/validation.d.ts.map +1 -1
  108. package/dist/utils/validation.js +20 -12
  109. package/dist/utils/validation.js.map +1 -1
  110. package/package.json +45 -8
  111. package/biome.json +0 -43
  112. package/dist/agent/agent-loop.d.ts +0 -5
  113. package/dist/agent/agent-loop.d.ts.map +0 -1
  114. package/dist/agent/agent-loop.js +0 -219
  115. package/dist/agent/agent-loop.js.map +0 -1
  116. package/dist/providers/convert.d.ts +0 -6
  117. package/dist/providers/convert.d.ts.map +0 -1
  118. package/dist/providers/convert.js +0 -207
  119. package/dist/providers/convert.js.map +0 -1
  120. package/dist/providers/google.d.ts +0 -26
  121. package/dist/providers/google.d.ts.map +0 -1
  122. package/dist/providers/google.js.map +0 -1
  123. package/dist/providers/openai.d.ts +0 -17
  124. package/dist/providers/openai.d.ts.map +0 -1
  125. package/dist/providers/openai.js.map +0 -1
  126. package/dist/stream.d.ts +0 -4
  127. package/dist/stream.d.ts.map +0 -1
  128. package/dist/stream.js +0 -40
  129. package/dist/stream.js.map +0 -1
  130. package/dist/test-google-agent-loop.d.ts +0 -2
  131. package/dist/test-google-agent-loop.d.ts.map +0 -1
  132. package/dist/test-google-agent-loop.js +0 -186
  133. package/dist/test-google-agent-loop.js.map +0 -1
  134. package/dist/test-google.d.ts +0 -2
  135. package/dist/test-google.d.ts.map +0 -1
  136. package/dist/test-google.js +0 -41
  137. package/dist/test-google.js.map +0 -1
  138. package/src/agent/agent-loop.ts +0 -275
  139. package/src/agent/types.ts +0 -80
  140. package/src/index.ts +0 -72
  141. package/src/models.generated.ts +0 -314
  142. package/src/models.ts +0 -45
  143. package/src/providers/convert.ts +0 -222
  144. package/src/providers/google.ts +0 -496
  145. package/src/providers/openai.ts +0 -437
  146. package/src/stream.ts +0 -60
  147. package/src/types.ts +0 -198
  148. package/src/utils/event-stream.ts +0 -60
  149. package/src/utils/json-parse.ts +0 -28
  150. package/src/utils/sanitize-unicode.ts +0 -25
  151. package/src/utils/validation.ts +0 -69
  152. package/test/core/agent-loop.test.ts +0 -958
  153. package/test/core/stream.test.ts +0 -409
  154. package/test/data/red-circle.png +0 -0
  155. package/test/data/superintelligentwill.pdf +0 -0
  156. package/test/edge-cases/general.test.ts +0 -565
  157. package/test/integration/e2e.test.ts +0 -530
  158. package/test/models/cost.test.ts +0 -499
  159. package/test/models/registry.test.ts +0 -298
  160. package/test/providers/convert.test.ts +0 -846
  161. package/test/providers/google-schema.test.ts +0 -666
  162. package/test/providers/google-stream.test.ts +0 -369
  163. package/test/providers/openai-stream.test.ts +0 -251
  164. package/test/utils/event-stream.test.ts +0 -289
  165. package/test/utils/json-parse.test.ts +0 -344
  166. package/test/utils/sanitize-unicode.test.ts +0 -329
  167. package/test/utils/validation.test.ts +0 -614
  168. package/tsconfig.json +0 -21
  169. package/vitest.config.ts +0 -9
@@ -1,496 +0,0 @@
1
- import {
2
- type Content,
3
- FinishReason,
4
- FunctionCallingConfigMode,
5
- type GenerateContentConfig,
6
- type GenerateContentParameters,
7
- GenerateContentResponse,
8
- GoogleGenAI,
9
- type Part,
10
- ThinkingLevel,
11
- } from "@google/genai";
12
-
13
- import { sanitizeSurrogates } from "../utils/sanitize-unicode";
14
- import { Model, StreamFunction, Context, Tool, Api, AssistantMessage, AssistantTextContent, AssistantThinkingContent, AssistantToolCall, StopReason } from "../types";
15
- import { AssistantMessageEventStream } from "../utils/event-stream";
16
- import { buildGoogleMessages } from "./convert";
17
- import { calculateCost } from "../models";
18
- import { validateToolArguments } from "../utils/validation";
19
-
20
- export interface GoogleProviderOptions {
21
- apiKey?: string;
22
- signal?: AbortSignal;
23
- temperature?: number;
24
- maxOutputTokens?: number;
25
- responseMimeType?: string;
26
- thinkingConfig? : {
27
- thinkingLevel: ThinkingLevel
28
- };
29
- imageConfig? : {
30
- aspectRatio?: "1:1" | "2:3" | "3:2" | "3:4" | "4:3" | "9:16" | "16:9" | "21:9";
31
- imageSize?: '1K' | '2K' | '4K';
32
- }
33
- }
34
-
35
- // Counter for generating unique tool call IDs
36
- let toolCallCounter = 0;
37
-
38
- export const streamGoogle: StreamFunction<'google'> = (
39
- model: Model<'google'>,
40
- context: Context,
41
- options: GoogleProviderOptions
42
- ) => {
43
- const stream = new AssistantMessageEventStream();
44
-
45
- (async () => {
46
-
47
- const output: AssistantMessage = {
48
- role: "assistant",
49
- content: [],
50
- api: "google-generative-ai" as Api,
51
- model: model.id,
52
- usage: {
53
- input: 0,
54
- output: 0,
55
- cacheRead: 0,
56
- cacheWrite: 0,
57
- totalTokens: 0,
58
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
59
- },
60
- stopReason: "stop",
61
- timestamp: Date.now(),
62
- };
63
-
64
- let finalResponse: GenerateContentResponse = {
65
- text: '',
66
- data: '',
67
- functionCalls: [],
68
- executableCode: '',
69
- codeExecutionResult: ''
70
- }
71
-
72
- try {
73
- const client = createClient(model, options?.apiKey);
74
- const params = buildParams(model, context, options);
75
- const googleStream = await client.models.generateContentStream(params);
76
-
77
- stream.push({ type: "start", partial: output });
78
- let currentBlock: AssistantTextContent | AssistantThinkingContent | null = null;
79
- const blocks = output.content;
80
- const blockIndex = () => blocks.length - 1;
81
- const messageInputs: Content[] = [];
82
- const accumulatedParts: Part[] = [];
83
-
84
- for await (const chunk of googleStream) {
85
- finalResponse = chunk
86
- const candidate = chunk.candidates?.[0];
87
- if (candidate?.content?.parts) {
88
- // Accumulate parts, merging consecutive parts of the same type
89
- for (const part of candidate.content.parts) {
90
- const lastPart = accumulatedParts[accumulatedParts.length - 1];
91
-
92
- // Check if we can merge with the last part
93
- const canMerge = lastPart &&
94
- part.text !== undefined &&
95
- lastPart.text !== undefined &&
96
- part.thought === lastPart.thought; // Both thinking or both regular text
97
-
98
- if (canMerge) {
99
- // Merge the text into the last part
100
- if(part.text){
101
- lastPart.text += part.text;
102
- }
103
- // Copy over thoughtSignature if present
104
- if (part.thoughtSignature) {
105
- lastPart.thoughtSignature = part.thoughtSignature;
106
- }
107
- } else {
108
- // Add as a new part
109
- accumulatedParts.push({ ...part });
110
- }
111
- }
112
-
113
- for (const part of candidate.content.parts) {
114
- if (part.text !== undefined) {
115
- const isThinking = part.thought === true;
116
- if (
117
- !currentBlock ||
118
- (isThinking && currentBlock.type !== "thinking") ||
119
- (!isThinking && currentBlock.type !== "text")
120
- ) {
121
- if (currentBlock) {
122
- if (currentBlock.type === "text") {
123
- stream.push({
124
- type: "text_end",
125
- contentIndex: blocks.length - 1,
126
- content: currentBlock.text,
127
- partial: output,
128
- });
129
- } else {
130
- stream.push({
131
- type: "thinking_end",
132
- contentIndex: blockIndex(),
133
- content: currentBlock.thinking,
134
- partial: output,
135
- });
136
- }
137
- }
138
- if (isThinking) {
139
- currentBlock = { type: "thinking", thinking: "" };
140
- output.content.push(currentBlock);
141
- stream.push({ type: "thinking_start", contentIndex: blockIndex(), partial: output });
142
- } else {
143
- currentBlock = { type: "text", text: "" };
144
- output.content.push(currentBlock);
145
- stream.push({ type: "text_start", contentIndex: blockIndex(), partial: output });
146
- }
147
- }
148
- if (currentBlock.type === "thinking") {
149
- currentBlock.thinking += part.text;
150
- stream.push({
151
- type: "thinking_delta",
152
- contentIndex: blockIndex(),
153
- delta: part.text,
154
- partial: output,
155
- });
156
- } else {
157
- currentBlock.text += part.text;
158
- stream.push({
159
- type: "text_delta",
160
- contentIndex: blockIndex(),
161
- delta: part.text,
162
- partial: output,
163
- });
164
- }
165
- }
166
-
167
- if (part.functionCall) {
168
- if (currentBlock) {
169
- if (currentBlock.type === "text") {
170
- stream.push({
171
- type: "text_end",
172
- contentIndex: blockIndex(),
173
- content: currentBlock.text,
174
- partial: output,
175
- });
176
- } else {
177
- stream.push({
178
- type: "thinking_end",
179
- contentIndex: blockIndex(),
180
- content: currentBlock.thinking,
181
- partial: output,
182
- });
183
- }
184
- currentBlock = null;
185
- }
186
-
187
- // Generate unique ID if not provided or if it's a duplicate
188
- const providedId = part.functionCall.id;
189
- const needsNewId =
190
- !providedId || output.content.some((b) => b.type === "toolCall" && b.id === providedId);
191
- const toolCallId = needsNewId
192
- ? `${part.functionCall.name}_${Date.now()}_${++toolCallCounter}`
193
- : providedId;
194
-
195
- const toolCall: AssistantToolCall = {
196
- type: "toolCall",
197
- id: toolCallId,
198
- name: part.functionCall.name || "",
199
- arguments: part.functionCall.args as Record<string, any>,
200
- ...(part.thoughtSignature && { thoughtSignature: part.thoughtSignature }),
201
- };
202
-
203
-
204
- // Validate tool arguments if tool definition is available
205
- if (context.tools) {
206
- const tool = context.tools.find((t) => t.name === toolCall.name);
207
- if (tool) {
208
- toolCall.arguments = validateToolArguments(tool, toolCall)as Record<string, any>;
209
- }
210
- }
211
-
212
- output.content.push(toolCall);
213
- stream.push({ type: "toolcall_start", contentIndex: blockIndex(), partial: output });
214
- stream.push({
215
- type: "toolcall_delta",
216
- contentIndex: blockIndex(),
217
- delta: JSON.stringify(toolCall.arguments),
218
- partial: output,
219
- });
220
- stream.push({ type: "toolcall_end", contentIndex: blockIndex(), toolCall, partial: output });
221
- }
222
- }
223
- }
224
-
225
- if (candidate?.finishReason) {
226
- output.stopReason = mapStopReason(candidate.finishReason);
227
- if (output.content.some((b) => b.type === "toolCall")) {
228
- output.stopReason = "toolUse";
229
- }
230
- }
231
-
232
- if (chunk.usageMetadata) {
233
- output.usage = {
234
- input: chunk.usageMetadata.promptTokenCount || 0,
235
- output:
236
- (chunk.usageMetadata.candidatesTokenCount || 0) + (chunk.usageMetadata.thoughtsTokenCount || 0),
237
- cacheRead: chunk.usageMetadata.cachedContentTokenCount || 0,
238
- cacheWrite: 0,
239
- totalTokens: chunk.usageMetadata.totalTokenCount || 0,
240
- cost: {
241
- input: 0,
242
- output: 0,
243
- cacheRead: 0,
244
- cacheWrite: 0,
245
- total: 0,
246
- },
247
- };
248
- calculateCost(model, output.usage);
249
- }
250
- }
251
-
252
- if (currentBlock) {
253
- if (currentBlock.type === "text") {
254
- stream.push({
255
- type: "text_end",
256
- contentIndex: blockIndex(),
257
- content: currentBlock.text,
258
- partial: output,
259
- });
260
- } else {
261
- stream.push({
262
- type: "thinking_end",
263
- contentIndex: blockIndex(),
264
- content: currentBlock.thinking,
265
- partial: output,
266
- });
267
- }
268
- }
269
-
270
- if (options?.signal?.aborted) {
271
- throw new Error("Request was aborted");
272
- }
273
-
274
- if (output.stopReason === "aborted" || output.stopReason === "error") {
275
- throw new Error("An unkown error ocurred");
276
- }
277
-
278
- // Build the complete Content from accumulated parts
279
- if (accumulatedParts.length > 0) {
280
- messageInputs.push({
281
- role: 'model',
282
- parts: accumulatedParts
283
- });
284
- }
285
- finalResponse.candidates = [];
286
- for(let i=0; i < messageInputs.length; i++){
287
- finalResponse.candidates?.push({
288
- content: messageInputs[i]
289
- })
290
- }
291
- stream.push({ type: "done", reason: output.stopReason, message: output });
292
- stream.end({
293
- _provider: 'google',
294
- role: 'assistant',
295
- message: finalResponse
296
- })
297
-
298
- } catch(error){
299
- for (const block of output.content) delete (block as any).index;
300
- output.stopReason = options?.signal?.aborted ? "aborted" : "error";
301
- output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
302
- stream.push({ type: "error", reason: output.stopReason, error: output });
303
-
304
- stream.end({
305
- _provider: 'google',
306
- role: 'assistant',
307
- message: finalResponse
308
- })
309
- }
310
-
311
- })()
312
-
313
- return stream;
314
- }
315
-
316
- function createClient(model: Model<"google">, apiKey?: string): GoogleGenAI {
317
- if (!apiKey) {
318
- if (!process.env.GEMINI_API_KEY) {
319
- throw new Error(
320
- "Gemini API key is required. Set GEMINI_API_KEY environment variable or pass it as an argument.",
321
- );
322
- }
323
- apiKey = process.env.GEMINI_API_KEY;
324
- }
325
- return new GoogleGenAI({
326
- apiKey,
327
- httpOptions: model.headers ? { headers: model.headers } : undefined,
328
- });
329
- }
330
-
331
- function buildParams(model: Model<"google">, context: Context, options?: GoogleProviderOptions){
332
-
333
- const contents = buildGoogleMessages(model, context);
334
-
335
- const config: GenerateContentConfig = {}
336
-
337
- if(options?.signal){
338
- config.abortSignal = options.signal;
339
- }
340
- if(context.systemPrompt){
341
- config.systemInstruction = sanitizeSurrogates(context.systemPrompt);
342
- }
343
- if(options?.temperature){
344
- config.temperature = options.temperature;
345
- }
346
- if(options?.maxOutputTokens){
347
- config.maxOutputTokens = options.maxOutputTokens;
348
- }
349
- if(options?.responseMimeType){
350
- config.responseMimeType = options.responseMimeType;
351
- }
352
- if(context.tools){
353
- config.tools = convertTools(context.tools)
354
- }
355
- if(options?.thinkingConfig){
356
- config.thinkingConfig = {
357
- includeThoughts: true,
358
- thinkingLevel: options.thinkingConfig.thinkingLevel
359
- }
360
- }
361
- if(options?.imageConfig){
362
- config.imageConfig = {
363
- imageSize: options.imageConfig.imageSize,
364
- aspectRatio: options.imageConfig.aspectRatio
365
- }
366
- }
367
-
368
- const params: GenerateContentParameters = {
369
- model: model.id,
370
- contents,
371
- config
372
- };
373
-
374
- return params;
375
- }
376
-
377
- /**
378
- * Transforms a JSON Schema to Google's supported subset.
379
- * Main transformations:
380
- * - Converts { "const": "value" } to { "enum": ["value"] }
381
- * - Converts { "anyOf": [{ "const": "a" }, { "const": "b" }] } to { "enum": ["a", "b"] }
382
- * - Recursively processes nested objects and arrays
383
- */
384
- export function transformSchemaForGoogle(schema: any): any {
385
- if (!schema || typeof schema !== 'object') {
386
- return schema;
387
- }
388
-
389
- // Handle arrays
390
- if (Array.isArray(schema)) {
391
- return schema.map(transformSchemaForGoogle);
392
- }
393
-
394
- const transformed: any = {};
395
-
396
- // Handle const keyword - convert to enum
397
- if ('const' in schema) {
398
- transformed.enum = [schema.const];
399
- // Copy over other properties except const
400
- for (const key in schema) {
401
- if (key !== 'const') {
402
- transformed[key] = schema[key];
403
- }
404
- }
405
- return transformed;
406
- }
407
-
408
- // Handle anyOf with const values - convert to enum
409
- if ('anyOf' in schema && Array.isArray(schema.anyOf) && schema.anyOf.length > 0) {
410
- const allConst = schema.anyOf.every((item: any) => item && typeof item === 'object' && 'const' in item);
411
- if (allConst) {
412
- // Extract all const values into a single enum
413
- transformed.enum = schema.anyOf.map((item: any) => item.const);
414
- // Copy over other properties from the parent schema
415
- for (const key in schema) {
416
- if (key !== 'anyOf') {
417
- transformed[key] = schema[key];
418
- }
419
- }
420
- // Copy type and other properties from the first anyOf item if not already set
421
- if (schema.anyOf.length > 0) {
422
- const firstItem = schema.anyOf[0];
423
- for (const key in firstItem) {
424
- if (key !== 'const' && !(key in transformed)) {
425
- transformed[key] = firstItem[key];
426
- }
427
- }
428
- }
429
- return transformed;
430
- }
431
- }
432
-
433
- // Recursively process all properties
434
- for (const key in schema) {
435
- if (key === 'properties' && typeof schema.properties === 'object') {
436
- // Recursively transform each property
437
- transformed.properties = {};
438
- for (const propKey in schema.properties) {
439
- transformed.properties[propKey] = transformSchemaForGoogle(schema.properties[propKey]);
440
- }
441
- } else if (key === 'items' && schema.items) {
442
- // Recursively transform array items schema
443
- transformed.items = transformSchemaForGoogle(schema.items);
444
- } else if (key === 'anyOf' || key === 'oneOf' || key === 'allOf') {
445
- // Recursively transform union/intersection schemas
446
- transformed[key] = Array.isArray(schema[key])
447
- ? schema[key].map(transformSchemaForGoogle)
448
- : transformSchemaForGoogle(schema[key]);
449
- } else {
450
- // Copy other properties as-is
451
- transformed[key] = schema[key];
452
- }
453
- }
454
-
455
- return transformed;
456
- }
457
-
458
- function convertTools(tools: readonly Tool[]): any[] | undefined {
459
- if (tools.length === 0) return undefined;
460
- return [
461
- {
462
- functionDeclarations: tools.map((tool) => ({
463
- name: tool.name,
464
- description: tool.description,
465
- parameters: transformSchemaForGoogle(tool.parameters),
466
- })),
467
- },
468
- ];
469
- }
470
-
471
- function mapStopReason(reason: FinishReason): StopReason {
472
- switch (reason) {
473
- case FinishReason.STOP:
474
- return "stop";
475
- case FinishReason.MAX_TOKENS:
476
- return "length";
477
- case FinishReason.BLOCKLIST:
478
- case FinishReason.PROHIBITED_CONTENT:
479
- case FinishReason.SPII:
480
- case FinishReason.SAFETY:
481
- case FinishReason.IMAGE_SAFETY:
482
- case FinishReason.IMAGE_PROHIBITED_CONTENT:
483
- case FinishReason.RECITATION:
484
- case FinishReason.FINISH_REASON_UNSPECIFIED:
485
- case FinishReason.OTHER:
486
- case FinishReason.LANGUAGE:
487
- case FinishReason.MALFORMED_FUNCTION_CALL:
488
- case FinishReason.UNEXPECTED_TOOL_CALL:
489
- case FinishReason.NO_IMAGE:
490
- return "error";
491
- default: {
492
- const _exhaustive: never = reason;
493
- throw new Error(`Unhandled stop reason: ${_exhaustive}`);
494
- }
495
- }
496
- }