@llm-ports/adapter-google 0.1.0-alpha.11

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.
package/dist/index.cjs ADDED
@@ -0,0 +1,601 @@
1
+ 'use strict';
2
+
3
+ var genai = require('@google/genai');
4
+ var core = require('@llm-ports/core');
5
+ var zodToJsonSchema = require('zod-to-json-schema');
6
+
7
+ // src/adapter.ts
8
+ var ADAPTER_NAME = "google";
9
+ function toGeminiParts(block) {
10
+ switch (block.type) {
11
+ case "text":
12
+ return [{ text: block.text }];
13
+ case "image": {
14
+ if (block.source.kind === "base64") {
15
+ return [
16
+ {
17
+ inlineData: {
18
+ mimeType: block.source.mediaType,
19
+ data: block.source.data
20
+ }
21
+ }
22
+ ];
23
+ }
24
+ return [
25
+ {
26
+ fileData: {
27
+ // Gemini infers mimeType from URL extension when not provided.
28
+ // We pass image/jpeg as a sane default; users wanting tighter
29
+ // control should pass base64 with explicit mediaType.
30
+ mimeType: "image/jpeg",
31
+ fileUri: block.source.url
32
+ }
33
+ }
34
+ ];
35
+ }
36
+ case "audio": {
37
+ if (block.source.kind === "base64") {
38
+ return [
39
+ {
40
+ inlineData: {
41
+ mimeType: block.source.mediaType,
42
+ data: block.source.data
43
+ }
44
+ }
45
+ ];
46
+ }
47
+ throw new core.ContentBlockUnsupportedError(ADAPTER_NAME, "audio (url; Gemini accepts base64 or fileData with fileUri)");
48
+ }
49
+ case "tool_use": {
50
+ const args = block.input !== null && typeof block.input === "object" ? block.input : { value: block.input };
51
+ return [{ functionCall: { name: block.name, args } }];
52
+ }
53
+ case "tool_result": {
54
+ const response = typeof block.content === "string" ? { result: block.content } : { result: extractTextOnly(block.content) };
55
+ return [
56
+ {
57
+ functionResponse: {
58
+ // Gemini's API requires the tool name; we use toolUseId since
59
+ // llm-ports' ToolResultBlock doesn't carry the name. Adapters
60
+ // that need the name can plumb it through a separate channel.
61
+ name: block.toolUseId,
62
+ response
63
+ }
64
+ }
65
+ ];
66
+ }
67
+ }
68
+ }
69
+ function extractTextOnly(blocks) {
70
+ return blocks.filter((b) => b.type === "text").map((b) => b.text).join("\n");
71
+ }
72
+ function toGeminiParts2(content) {
73
+ if (typeof content === "string") {
74
+ return [{ text: content }];
75
+ }
76
+ return content.flatMap(toGeminiParts);
77
+ }
78
+ function toGeminiRequest(messages) {
79
+ let systemInstruction;
80
+ const contents = [];
81
+ for (const msg of messages) {
82
+ if (msg.role === "system") {
83
+ const text = typeof msg.content === "string" ? msg.content : extractTextOnly(msg.content);
84
+ systemInstruction = systemInstruction === void 0 ? text : `${systemInstruction}
85
+
86
+ ${text}`;
87
+ continue;
88
+ }
89
+ const role = msg.role === "tool" ? "function" : msg.role === "assistant" ? "model" : "user";
90
+ contents.push({
91
+ role,
92
+ parts: toGeminiParts2(msg.content)
93
+ });
94
+ }
95
+ return systemInstruction !== void 0 ? { systemInstruction, contents } : { contents };
96
+ }
97
+ function extractGeminiText(parts) {
98
+ if (!parts) return "";
99
+ return parts.filter((p) => "text" in p).map((p) => p.text).join("");
100
+ }
101
+ function fromGeminiCandidate(candidate) {
102
+ const out = [];
103
+ const parts = candidate.content?.parts ?? [];
104
+ for (const part of parts) {
105
+ if ("text" in part && part.text.length > 0) {
106
+ out.push({ type: "text", text: part.text });
107
+ } else if ("functionCall" in part) {
108
+ out.push({
109
+ type: "tool_use",
110
+ id: part.functionCall.name,
111
+ name: part.functionCall.name,
112
+ input: part.functionCall.args
113
+ });
114
+ } else if ("inlineData" in part) {
115
+ const mt = part.inlineData.mimeType;
116
+ if (mt === "image/jpeg" || mt === "image/png" || mt === "image/gif" || mt === "image/webp") {
117
+ out.push({
118
+ type: "image",
119
+ source: { kind: "base64", mediaType: mt, data: part.inlineData.data }
120
+ });
121
+ }
122
+ }
123
+ }
124
+ return out;
125
+ }
126
+ function toGeminiTools(tools) {
127
+ const declarations = Object.entries(tools).map(
128
+ ([name, def]) => ({
129
+ name,
130
+ description: def.description,
131
+ parameters: zodToGeminiSchema(def.inputSchema)
132
+ })
133
+ );
134
+ return [{ functionDeclarations: declarations }];
135
+ }
136
+ function zodToGeminiSchema(schema) {
137
+ try {
138
+ const json = zodToJsonSchema.zodToJsonSchema(schema, {
139
+ target: "openApi3",
140
+ $refStrategy: "none"
141
+ });
142
+ return sanitizeGeminiSchema(json);
143
+ } catch {
144
+ return { type: "object", properties: {} };
145
+ }
146
+ }
147
+ function sanitizeGeminiSchema(input) {
148
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
149
+ return { type: "object", properties: {} };
150
+ }
151
+ const cleaned = {};
152
+ for (const [key, value] of Object.entries(input)) {
153
+ if (key === "$schema" || key === "definitions" || key === "$defs" || key === "$ref") {
154
+ continue;
155
+ }
156
+ if (key === "properties" && value && typeof value === "object") {
157
+ const props = {};
158
+ for (const [propName, propSchema] of Object.entries(value)) {
159
+ props[propName] = sanitizeGeminiSchema(propSchema);
160
+ }
161
+ cleaned[key] = props;
162
+ continue;
163
+ }
164
+ if (key === "items" && value && typeof value === "object" && !Array.isArray(value)) {
165
+ cleaned[key] = sanitizeGeminiSchema(value);
166
+ continue;
167
+ }
168
+ cleaned[key] = value;
169
+ }
170
+ return cleaned;
171
+ }
172
+ function detectUnsupportedSchemaFeature(input) {
173
+ if (!input || typeof input !== "object" || Array.isArray(input)) return null;
174
+ const obj = input;
175
+ for (const key of ["oneOf", "allOf", "not", "$ref"]) {
176
+ if (key in obj) return key;
177
+ }
178
+ if (obj["properties"] && typeof obj["properties"] === "object") {
179
+ for (const propSchema of Object.values(obj["properties"])) {
180
+ const nested = detectUnsupportedSchemaFeature(propSchema);
181
+ if (nested) return nested;
182
+ }
183
+ }
184
+ if (obj["items"] && typeof obj["items"] === "object" && !Array.isArray(obj["items"])) {
185
+ const nested = detectUnsupportedSchemaFeature(obj["items"]);
186
+ if (nested) return nested;
187
+ }
188
+ if (Array.isArray(obj["anyOf"])) {
189
+ for (const sub of obj["anyOf"]) {
190
+ const nested = detectUnsupportedSchemaFeature(sub);
191
+ if (nested) return nested;
192
+ }
193
+ }
194
+ return null;
195
+ }
196
+
197
+ // src/pricing.ts
198
+ var GEMINI_PRICING = {
199
+ // Gemini 2.5 family (2026-05 GA pricing)
200
+ "gemini-2.5-pro": {
201
+ inputPer1M: 1.25,
202
+ outputPer1M: 5,
203
+ cacheReadPer1M: 0.3125
204
+ },
205
+ "gemini-2.5-flash": {
206
+ inputPer1M: 0.075,
207
+ outputPer1M: 0.3,
208
+ cacheReadPer1M: 0.01875
209
+ },
210
+ "gemini-2.5-flash-lite": {
211
+ inputPer1M: 0.0375,
212
+ outputPer1M: 0.15,
213
+ cacheReadPer1M: 9375e-6
214
+ },
215
+ // Gemini 2.0 family (still available)
216
+ "gemini-2.0-flash": {
217
+ inputPer1M: 0.1,
218
+ outputPer1M: 0.4,
219
+ cacheReadPer1M: 0.025
220
+ },
221
+ "gemini-2.0-flash-lite": {
222
+ inputPer1M: 0.075,
223
+ outputPer1M: 0.3
224
+ }
225
+ };
226
+ function lookupGeminiPricing(modelId) {
227
+ return GEMINI_PRICING[modelId];
228
+ }
229
+
230
+ // src/adapter.ts
231
+ function pricingFor(ctx, modelId) {
232
+ const pricing = ctx.pricingOverrides[modelId] ?? GEMINI_PRICING[modelId];
233
+ if (!pricing) {
234
+ throw new Error(
235
+ `No pricing entry for Google Gemini model "${modelId}". Provide pricingOverrides or update src/pricing.ts.`
236
+ );
237
+ }
238
+ return pricing;
239
+ }
240
+ function createGoogleAdapter(opts) {
241
+ const mergedPricing = {
242
+ ...GEMINI_PRICING,
243
+ ...opts.pricingOverrides ?? {}
244
+ };
245
+ const ctx = {
246
+ client: new genai.GoogleGenAI({ apiKey: opts.apiKey }),
247
+ validationStrategy: opts.validationStrategy ?? {
248
+ kind: "retry-with-feedback",
249
+ maxAttempts: 2,
250
+ includeOriginalError: true
251
+ },
252
+ pricingOverrides: opts.pricingOverrides ?? {},
253
+ imageSizeLimitBytes: opts.imageSizeLimitBytes ?? 20 * 1024 * 1024
254
+ };
255
+ return {
256
+ name: "google",
257
+ pricing: mergedPricing,
258
+ createLLMPort: (modelId, alias) => createPort(ctx, modelId, alias)
259
+ };
260
+ }
261
+ function createPort(ctx, modelId, alias) {
262
+ const pricing = pricingFor(ctx, modelId);
263
+ const validateContent = (content) => {
264
+ if (Array.isArray(content)) {
265
+ core.validateImageBlocks(content, {
266
+ alias,
267
+ ...ctx.imageSizeLimitBytes > 0 ? { limitBytes: ctx.imageSizeLimitBytes } : {}
268
+ });
269
+ }
270
+ };
271
+ const validateMessages = (messages) => {
272
+ for (const msg of messages) validateContent(msg.content);
273
+ };
274
+ return {
275
+ async generateText(options) {
276
+ core.throwIfAborted(options.signal);
277
+ validateContent(options.prompt);
278
+ const start = Date.now();
279
+ try {
280
+ const parts = toGeminiParts2(options.prompt);
281
+ const response = await ctx.client.models.generateContent({
282
+ model: modelId,
283
+ contents: [{ role: "user", parts }],
284
+ config: {
285
+ ...options.instructions !== void 0 ? { systemInstruction: options.instructions } : {},
286
+ ...options.temperature !== void 0 ? { temperature: options.temperature } : {},
287
+ ...options.maxOutputTokens !== void 0 ? { maxOutputTokens: options.maxOutputTokens } : {},
288
+ ...options.signal ? { abortSignal: options.signal } : {}
289
+ }
290
+ });
291
+ const candidate = response.candidates?.[0];
292
+ const text = extractGeminiText(candidate?.content?.parts);
293
+ const usage = parseUsage(response);
294
+ return {
295
+ text,
296
+ usage,
297
+ cost: core.computeChatCost(usage, pricing),
298
+ modelId: response.modelVersion ?? modelId,
299
+ providerAlias: alias,
300
+ latencyMs: Date.now() - start
301
+ };
302
+ } catch (err) {
303
+ throw core.wrapProviderError(alias, err);
304
+ }
305
+ },
306
+ async generateStructured(options) {
307
+ core.throwIfAborted(options.signal);
308
+ validateContent(options.prompt);
309
+ const start = Date.now();
310
+ let attempts = 0;
311
+ const maxAttempts = ctx.validationStrategy.kind === "retry-with-feedback" ? ctx.validationStrategy.maxAttempts : 1;
312
+ const jsonSchema = zodToGeminiSchema(options.schema);
313
+ const unsupportedFeature = detectUnsupportedSchemaFeature(jsonSchema);
314
+ const useNativeResponseSchema = unsupportedFeature === null;
315
+ if (!useNativeResponseSchema) {
316
+ warnSchemaFallback(modelId, unsupportedFeature);
317
+ }
318
+ const sanitizedSchema = useNativeResponseSchema ? sanitizeGeminiSchema(jsonSchema) : null;
319
+ let correctionPrompt = null;
320
+ let lastUsage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
321
+ let lastModelId = modelId;
322
+ while (attempts < maxAttempts) {
323
+ attempts++;
324
+ const userText = correctionPrompt ? `${core.stringifyContentBlocks(options.prompt)}
325
+
326
+ ${correctionPrompt}` : useNativeResponseSchema ? core.stringifyContentBlocks(options.prompt) : `${core.stringifyContentBlocks(options.prompt)}
327
+
328
+ Reply with a single JSON object only. No prose, no code fences.`;
329
+ try {
330
+ const response = await ctx.client.models.generateContent({
331
+ model: modelId,
332
+ contents: [{ role: "user", parts: [{ text: userText }] }],
333
+ config: {
334
+ ...options.instructions !== void 0 ? { systemInstruction: options.instructions } : {},
335
+ temperature: options.temperature ?? 0,
336
+ ...options.maxOutputTokens !== void 0 ? { maxOutputTokens: options.maxOutputTokens } : {},
337
+ responseMimeType: "application/json",
338
+ ...sanitizedSchema ? { responseSchema: sanitizedSchema } : {},
339
+ ...options.signal ? { abortSignal: options.signal } : {}
340
+ }
341
+ });
342
+ const candidate = response.candidates?.[0];
343
+ const raw = extractGeminiText(candidate?.content?.parts);
344
+ lastUsage = core.mergeTokenUsage(lastUsage, parseUsage(response));
345
+ lastModelId = response.modelVersion ?? modelId;
346
+ const decoded = core.extractJSON(raw);
347
+ let parsed = options.schema.safeParse(decoded);
348
+ if (!parsed.success) {
349
+ const repaired = core.attemptValidationRepair(decoded, parsed.error);
350
+ const reparsed = options.schema.safeParse(repaired);
351
+ if (reparsed.success) parsed = reparsed;
352
+ }
353
+ if (parsed.success) {
354
+ return {
355
+ data: parsed.data,
356
+ usage: lastUsage,
357
+ cost: core.computeChatCost(lastUsage, pricing),
358
+ modelId: lastModelId,
359
+ providerAlias: alias,
360
+ latencyMs: Date.now() - start,
361
+ validationAttempts: attempts
362
+ };
363
+ }
364
+ if (ctx.validationStrategy.kind === "retry-with-feedback" && attempts < maxAttempts) {
365
+ const issues = parsed.error.issues.map((i) => `- ${i.path.join(".") || "<root>"}: ${i.message}`).join("\n");
366
+ correctionPrompt = `Your previous response failed validation:
367
+ ${issues}
368
+
369
+ Reply with a single corrected JSON object only.`;
370
+ continue;
371
+ }
372
+ core.failValidation(parsed.error.issues, attempts);
373
+ } catch (err) {
374
+ throw core.wrapProviderError(alias, err);
375
+ }
376
+ }
377
+ throw new Error("generateStructured exhausted attempts");
378
+ },
379
+ async *streamText(options) {
380
+ core.throwIfAborted(options.signal);
381
+ validateContent(options.prompt);
382
+ try {
383
+ const parts = toGeminiParts2(options.prompt);
384
+ const stream = await ctx.client.models.generateContentStream({
385
+ model: modelId,
386
+ contents: [{ role: "user", parts }],
387
+ config: {
388
+ ...options.instructions !== void 0 ? { systemInstruction: options.instructions } : {},
389
+ ...options.temperature !== void 0 ? { temperature: options.temperature } : {},
390
+ ...options.maxOutputTokens !== void 0 ? { maxOutputTokens: options.maxOutputTokens } : {},
391
+ ...options.signal ? { abortSignal: options.signal } : {}
392
+ }
393
+ });
394
+ for await (const chunk of stream) {
395
+ const text = extractGeminiText(
396
+ chunk.candidates?.[0]?.content?.parts
397
+ );
398
+ if (text.length > 0) yield text;
399
+ }
400
+ } catch (err) {
401
+ throw core.wrapProviderError(alias, err);
402
+ }
403
+ },
404
+ async *streamStructured(options) {
405
+ core.throwIfAborted(options.signal);
406
+ validateContent(options.prompt);
407
+ try {
408
+ const stream = await ctx.client.models.generateContentStream({
409
+ model: modelId,
410
+ contents: [
411
+ {
412
+ role: "user",
413
+ parts: [
414
+ {
415
+ text: `${core.stringifyContentBlocks(options.prompt)}
416
+
417
+ Reply with a single JSON object only. Stream the JSON progressively.`
418
+ }
419
+ ]
420
+ }
421
+ ],
422
+ config: {
423
+ ...options.instructions !== void 0 ? { systemInstruction: options.instructions } : {},
424
+ temperature: options.temperature ?? 0,
425
+ ...options.maxOutputTokens !== void 0 ? { maxOutputTokens: options.maxOutputTokens } : {},
426
+ responseMimeType: "application/json",
427
+ ...options.signal ? { abortSignal: options.signal } : {}
428
+ }
429
+ });
430
+ let buffer = "";
431
+ for await (const chunk of stream) {
432
+ const text = extractGeminiText(
433
+ chunk.candidates?.[0]?.content?.parts
434
+ );
435
+ if (text.length === 0) continue;
436
+ buffer += text;
437
+ const partial = core.tryParsePartialJSON(buffer);
438
+ if (partial !== null) yield partial;
439
+ }
440
+ } catch (err) {
441
+ throw core.wrapProviderError(alias, err);
442
+ }
443
+ },
444
+ async runAgent(options) {
445
+ core.throwIfAborted(options.signal);
446
+ validateMessages(options.messages);
447
+ const start = Date.now();
448
+ const maxSteps = options.maxSteps ?? 10;
449
+ const conversation = [...options.messages];
450
+ const toolCalls = [];
451
+ let stepsTaken = 0;
452
+ let totalUsage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
453
+ let finalText = "";
454
+ let lastModelId = modelId;
455
+ let terminationReason = "max_steps";
456
+ const toolsRegistered = Object.keys(options.tools).length > 0;
457
+ const geminiTools = toolsRegistered ? toGeminiTools(options.tools) : void 0;
458
+ try {
459
+ for (let step = 0; step < maxSteps; step++) {
460
+ core.throwIfAborted(options.signal);
461
+ stepsTaken = step + 1;
462
+ const { systemInstruction, contents } = toGeminiRequest(conversation);
463
+ const response = await ctx.client.models.generateContent({
464
+ model: modelId,
465
+ contents,
466
+ config: {
467
+ // options.instructions takes precedence over a system message
468
+ // baked into the messages array, matching the per-method pattern
469
+ // used elsewhere in the adapter.
470
+ ...options.instructions !== void 0 ? { systemInstruction: options.instructions } : systemInstruction !== void 0 ? { systemInstruction } : {},
471
+ ...options.temperature !== void 0 ? { temperature: options.temperature } : {},
472
+ ...options.maxOutputTokens !== void 0 ? { maxOutputTokens: options.maxOutputTokens } : {},
473
+ ...geminiTools ? { tools: geminiTools } : {},
474
+ ...options.signal ? { abortSignal: options.signal } : {}
475
+ }
476
+ });
477
+ totalUsage = core.mergeTokenUsage(totalUsage, parseUsage(response));
478
+ lastModelId = response.modelVersion ?? modelId;
479
+ const candidate = response.candidates?.[0];
480
+ const responseParts = candidate?.content?.parts ?? [];
481
+ const blocks = fromGeminiCandidate({
482
+ content: { parts: responseParts }
483
+ });
484
+ conversation.push({
485
+ role: "assistant",
486
+ content: blocks.length > 0 ? blocks : [{ type: "text", text: extractGeminiText(responseParts) }]
487
+ });
488
+ finalText = blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
489
+ const toolUses = blocks.filter(
490
+ (b) => b.type === "tool_use"
491
+ );
492
+ if (toolUses.length === 0) {
493
+ terminationReason = "completed";
494
+ break;
495
+ }
496
+ const toolResults = [];
497
+ for (const tu of toolUses) {
498
+ const def = options.tools[tu.name];
499
+ if (!def) {
500
+ toolResults.push({
501
+ type: "tool_result",
502
+ toolUseId: tu.id,
503
+ content: `Tool "${tu.name}" not found.`,
504
+ isError: true
505
+ });
506
+ continue;
507
+ }
508
+ try {
509
+ const output = await def.execute(tu.input);
510
+ toolCalls.push({
511
+ name: tu.name,
512
+ input: tu.input,
513
+ output
514
+ });
515
+ const text = typeof output === "string" ? output : JSON.stringify(output);
516
+ const truncated = def.maxOutputBytes !== void 0 && text.length > def.maxOutputBytes ? `${text.slice(0, def.maxOutputBytes)}
517
+ [truncated]` : text;
518
+ toolResults.push({
519
+ type: "tool_result",
520
+ toolUseId: tu.id,
521
+ content: truncated
522
+ });
523
+ } catch (toolErr) {
524
+ toolResults.push({
525
+ type: "tool_result",
526
+ toolUseId: tu.id,
527
+ content: toolErr instanceof Error ? toolErr.message : String(toolErr),
528
+ isError: true
529
+ });
530
+ }
531
+ }
532
+ conversation.push({ role: "tool", content: toolResults });
533
+ }
534
+ } catch (err) {
535
+ throw core.wrapProviderError(alias, err);
536
+ }
537
+ return {
538
+ text: finalText,
539
+ messages: conversation,
540
+ toolCalls,
541
+ usage: totalUsage,
542
+ cost: core.computeChatCost(totalUsage, pricing),
543
+ modelId: lastModelId,
544
+ providerAlias: alias,
545
+ latencyMs: Date.now() - start,
546
+ stepsTaken,
547
+ terminationReason
548
+ };
549
+ },
550
+ async listModels() {
551
+ try {
552
+ const out = [];
553
+ const pager = await ctx.client.models.list();
554
+ for await (const m of pager) {
555
+ const model = m;
556
+ if (!model.name) continue;
557
+ const id = model.name.startsWith("models/") ? model.name.slice("models/".length) : model.name;
558
+ out.push({
559
+ id,
560
+ ...model.displayName ? { displayName: model.displayName } : {},
561
+ ...model.inputTokenLimit ? { contextWindow: model.inputTokenLimit } : {},
562
+ ...model.outputTokenLimit ? { metadata: { outputTokenLimit: model.outputTokenLimit } } : {}
563
+ });
564
+ }
565
+ return out;
566
+ } catch (err) {
567
+ throw core.wrapProviderError(alias, err);
568
+ }
569
+ }
570
+ };
571
+ }
572
+ function parseUsage(response) {
573
+ const m = response.usageMetadata ?? {};
574
+ const inputTokens = m.promptTokenCount ?? 0;
575
+ const outputTokens = m.candidatesTokenCount ?? 0;
576
+ const totalTokens = m.totalTokenCount ?? inputTokens + outputTokens;
577
+ const usage = { inputTokens, outputTokens, totalTokens };
578
+ if (m.cachedContentTokenCount !== void 0 && m.cachedContentTokenCount > 0) {
579
+ usage.cacheReadTokens = m.cachedContentTokenCount;
580
+ }
581
+ return usage;
582
+ }
583
+ var warnedSchemaFallback = /* @__PURE__ */ new Set();
584
+ function warnSchemaFallback(modelId, feature) {
585
+ const key = `${modelId}::${feature}`;
586
+ if (warnedSchemaFallback.has(key)) return;
587
+ warnedSchemaFallback.add(key);
588
+ console.warn(
589
+ `[@llm-ports/adapter-google] generateStructured: model "${modelId}" schema contains "${feature}" which Gemini's responseSchema does not support. Falling back to prompted-JSON + Zod validation (still correct; just slightly weaker constrained-decoding guarantee).`
590
+ );
591
+ }
592
+ function _resetSchemaFallbackWarnings() {
593
+ warnedSchemaFallback.clear();
594
+ }
595
+
596
+ exports.GEMINI_PRICING = GEMINI_PRICING;
597
+ exports._resetSchemaFallbackWarnings = _resetSchemaFallbackWarnings;
598
+ exports.createGoogleAdapter = createGoogleAdapter;
599
+ exports.lookupGeminiPricing = lookupGeminiPricing;
600
+ //# sourceMappingURL=index.cjs.map
601
+ //# sourceMappingURL=index.cjs.map