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