@llm-ports/adapter-google 0.1.0-alpha.8 → 0.1.0-alpha.9
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 +234 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.mjs +235 -31
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var genai = require('@google/genai');
|
|
4
4
|
var core = require('@llm-ports/core');
|
|
5
|
+
var zodToJsonSchema = require('zod-to-json-schema');
|
|
5
6
|
|
|
6
7
|
// src/adapter.ts
|
|
7
8
|
var ADAPTER_NAME = "google";
|
|
@@ -97,6 +98,101 @@ function extractGeminiText(parts) {
|
|
|
97
98
|
if (!parts) return "";
|
|
98
99
|
return parts.filter((p) => "text" in p).map((p) => p.text).join("");
|
|
99
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
|
+
}
|
|
100
196
|
|
|
101
197
|
// src/pricing.ts
|
|
102
198
|
var GEMINI_PRICING = {
|
|
@@ -213,6 +309,13 @@ function createPort(ctx, modelId, alias) {
|
|
|
213
309
|
const start = Date.now();
|
|
214
310
|
let attempts = 0;
|
|
215
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;
|
|
216
319
|
let correctionPrompt = null;
|
|
217
320
|
let lastUsage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
218
321
|
let lastModelId = modelId;
|
|
@@ -220,7 +323,7 @@ function createPort(ctx, modelId, alias) {
|
|
|
220
323
|
attempts++;
|
|
221
324
|
const userText = correctionPrompt ? `${core.stringifyContentBlocks(options.prompt)}
|
|
222
325
|
|
|
223
|
-
${correctionPrompt}` : `${core.stringifyContentBlocks(options.prompt)}
|
|
326
|
+
${correctionPrompt}` : useNativeResponseSchema ? core.stringifyContentBlocks(options.prompt) : `${core.stringifyContentBlocks(options.prompt)}
|
|
224
327
|
|
|
225
328
|
Reply with a single JSON object only. No prose, no code fences.`;
|
|
226
329
|
try {
|
|
@@ -232,6 +335,7 @@ Reply with a single JSON object only. No prose, no code fences.`;
|
|
|
232
335
|
temperature: options.temperature ?? 0,
|
|
233
336
|
...options.maxOutputTokens !== void 0 ? { maxOutputTokens: options.maxOutputTokens } : {},
|
|
234
337
|
responseMimeType: "application/json",
|
|
338
|
+
...sanitizedSchema ? { responseSchema: sanitizedSchema } : {},
|
|
235
339
|
...options.signal ? { abortSignal: options.signal } : {}
|
|
236
340
|
}
|
|
237
341
|
});
|
|
@@ -341,36 +445,124 @@ Reply with a single JSON object only. Stream the JSON progressively.`
|
|
|
341
445
|
core.throwIfAborted(options.signal);
|
|
342
446
|
validateMessages(options.messages);
|
|
343
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;
|
|
344
458
|
try {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
contents
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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;
|
|
353
495
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
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;
|
|
374
566
|
} catch (err) {
|
|
375
567
|
throw core.wrapProviderError(alias, err);
|
|
376
568
|
}
|
|
@@ -388,8 +580,21 @@ function parseUsage(response) {
|
|
|
388
580
|
}
|
|
389
581
|
return usage;
|
|
390
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
|
+
}
|
|
391
595
|
|
|
392
596
|
exports.GEMINI_PRICING = GEMINI_PRICING;
|
|
597
|
+
exports._resetSchemaFallbackWarnings = _resetSchemaFallbackWarnings;
|
|
393
598
|
exports.createGoogleAdapter = createGoogleAdapter;
|
|
394
599
|
exports.lookupGeminiPricing = lookupGeminiPricing;
|
|
395
600
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/content.ts","../src/pricing.ts","../src/adapter.ts"],"names":["ContentBlockUnsupportedError","GoogleGenAI","validateImageBlocks","throwIfAborted","computeChatCost","wrapProviderError","stringifyContentBlocks","extractJSON","attemptValidationRepair","failValidation","tryParsePartialJSON"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * Content translation between llm-ports ContentBlock[] and Gemini's\r\n * Content + Part shapes.\r\n *\r\n * Gemini's content model:\r\n * Content = { role: \"user\" | \"model\" | \"function\", parts: Part[] }\r\n * Part shapes (the ones we care about):\r\n * - { text: string }\r\n * - { inlineData: { mimeType: string, data: string (base64) } }\r\n * - { fileData: { mimeType: string, fileUri: string } }\r\n * - { functionCall: { name: string, args: object } }\r\n * - { functionResponse: { name: string, response: object } }\r\n *\r\n * Notes:\r\n * - System messages map to a top-level `systemInstruction` field on the\r\n * request, NOT a Content with role: \"system\". `toGeminiRequest` handles\r\n * this split.\r\n * - The assistant role is `\"model\"` in Gemini's vocabulary.\r\n * - Tool results are mapped to `functionResponse` parts.\r\n */\r\n\r\nimport {\r\n ContentBlockUnsupportedError,\r\n type ContentBlock,\r\n type LLMMessage,\r\n type MessageContent,\r\n} from \"@llm-ports/core\";\r\n\r\nconst ADAPTER_NAME = \"google\";\r\n\r\n// ─── Outgoing: ContentBlock[] → Gemini Part[] ────────────────────────\r\n\r\ninterface GeminiTextPart {\r\n text: string;\r\n}\r\ninterface GeminiInlineDataPart {\r\n inlineData: { mimeType: string; data: string };\r\n}\r\ninterface GeminiFileDataPart {\r\n fileData: { mimeType: string; fileUri: string };\r\n}\r\ninterface GeminiFunctionCallPart {\r\n functionCall: { name: string; args: Record<string, unknown> };\r\n}\r\ninterface GeminiFunctionResponsePart {\r\n functionResponse: { name: string; response: Record<string, unknown> };\r\n}\r\nexport type GeminiPart =\r\n | GeminiTextPart\r\n | GeminiInlineDataPart\r\n | GeminiFileDataPart\r\n | GeminiFunctionCallPart\r\n | GeminiFunctionResponsePart;\r\n\r\nexport interface GeminiContent {\r\n role: \"user\" | \"model\" | \"function\";\r\n parts: GeminiPart[];\r\n}\r\n\r\n/** Translate a single ContentBlock to one or more GeminiParts. */\r\nfunction toGeminiParts(block: ContentBlock): GeminiPart[] {\r\n switch (block.type) {\r\n case \"text\":\r\n return [{ text: block.text }];\r\n case \"image\": {\r\n if (block.source.kind === \"base64\") {\r\n return [\r\n {\r\n inlineData: {\r\n mimeType: block.source.mediaType,\r\n data: block.source.data,\r\n },\r\n },\r\n ];\r\n }\r\n // URL form\r\n return [\r\n {\r\n fileData: {\r\n // Gemini infers mimeType from URL extension when not provided.\r\n // We pass image/jpeg as a sane default; users wanting tighter\r\n // control should pass base64 with explicit mediaType.\r\n mimeType: \"image/jpeg\",\r\n fileUri: block.source.url,\r\n },\r\n },\r\n ];\r\n }\r\n case \"audio\": {\r\n if (block.source.kind === \"base64\") {\r\n return [\r\n {\r\n inlineData: {\r\n mimeType: block.source.mediaType,\r\n data: block.source.data,\r\n },\r\n },\r\n ];\r\n }\r\n throw new ContentBlockUnsupportedError(ADAPTER_NAME, \"audio (url; Gemini accepts base64 or fileData with fileUri)\");\r\n }\r\n case \"tool_use\": {\r\n // Gemini's tool-call shape has args as a plain object; we pass the input\r\n // through if it's already an object, else wrap.\r\n const args =\r\n block.input !== null && typeof block.input === \"object\"\r\n ? (block.input as Record<string, unknown>)\r\n : { value: block.input };\r\n return [{ functionCall: { name: block.name, args } }];\r\n }\r\n case \"tool_result\": {\r\n // Gemini's functionResponse expects a `response` object. If the\r\n // ContentBlock.tool_result.content is a string, wrap it.\r\n const response: Record<string, unknown> =\r\n typeof block.content === \"string\"\r\n ? { result: block.content }\r\n : { result: extractTextOnly(block.content) };\r\n return [\r\n {\r\n functionResponse: {\r\n // Gemini's API requires the tool name; we use toolUseId since\r\n // llm-ports' ToolResultBlock doesn't carry the name. Adapters\r\n // that need the name can plumb it through a separate channel.\r\n name: block.toolUseId,\r\n response,\r\n },\r\n },\r\n ];\r\n }\r\n }\r\n}\r\n\r\nfunction extractTextOnly(blocks: ContentBlock[]): string {\r\n return blocks\r\n .filter((b): b is Extract<ContentBlock, { type: \"text\" }> => b.type === \"text\")\r\n .map((b) => b.text)\r\n .join(\"\\n\");\r\n}\r\n\r\n/** Translate a MessageContent to Gemini Parts. */\r\nexport function toGeminiParts2(content: MessageContent): GeminiPart[] {\r\n if (typeof content === \"string\") {\r\n return [{ text: content }];\r\n }\r\n return content.flatMap(toGeminiParts);\r\n}\r\n\r\n/**\r\n * Translate an array of LLMMessages into:\r\n * - `systemInstruction`: the concatenated system messages (Gemini puts\r\n * these at the top level of the request, not in `contents`).\r\n * - `contents`: the user + assistant + tool messages, with roles mapped.\r\n */\r\nexport function toGeminiRequest(messages: LLMMessage[]): {\r\n systemInstruction?: string;\r\n contents: GeminiContent[];\r\n} {\r\n let systemInstruction: string | undefined;\r\n const contents: GeminiContent[] = [];\r\n for (const msg of messages) {\r\n if (msg.role === \"system\") {\r\n const text =\r\n typeof msg.content === \"string\" ? msg.content : extractTextOnly(msg.content);\r\n systemInstruction = systemInstruction === undefined ? text : `${systemInstruction}\\n\\n${text}`;\r\n continue;\r\n }\r\n const role: GeminiContent[\"role\"] =\r\n msg.role === \"tool\" ? \"function\" : msg.role === \"assistant\" ? \"model\" : \"user\";\r\n contents.push({\r\n role,\r\n parts: toGeminiParts2(msg.content),\r\n });\r\n }\r\n return systemInstruction !== undefined\r\n ? { systemInstruction, contents }\r\n : { contents };\r\n}\r\n\r\n// ─── Incoming: Gemini response → ContentBlock[] ──────────────────────\r\n\r\ninterface GeminiResponseCandidate {\r\n content?: {\r\n role?: string;\r\n parts?: GeminiPart[];\r\n };\r\n finishReason?: string;\r\n}\r\n\r\n/**\r\n * Extract the assistant text from a Gemini response. Used by generateText\r\n * and by the structured-output path before JSON parsing.\r\n */\r\nexport function extractGeminiText(parts: GeminiPart[] | undefined): string {\r\n if (!parts) return \"\";\r\n return parts\r\n .filter((p): p is GeminiTextPart => \"text\" in p)\r\n .map((p) => p.text)\r\n .join(\"\");\r\n}\r\n\r\n/**\r\n * Translate a Gemini response candidate's parts back into ContentBlock[].\r\n * Used by runAgent to reconstruct the model's tool_use blocks.\r\n */\r\nexport function fromGeminiCandidate(candidate: GeminiResponseCandidate): ContentBlock[] {\r\n const out: ContentBlock[] = [];\r\n const parts = candidate.content?.parts ?? [];\r\n for (const part of parts) {\r\n if (\"text\" in part && part.text.length > 0) {\r\n out.push({ type: \"text\", text: part.text });\r\n } else if (\"functionCall\" in part) {\r\n out.push({\r\n type: \"tool_use\",\r\n id: `gemini-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\r\n name: part.functionCall.name,\r\n input: part.functionCall.args,\r\n });\r\n } else if (\"inlineData\" in part) {\r\n // Inline (base64) image in assistant response. Decode if media type\r\n // is one we support, else drop (consistent with adapter-openai's\r\n // unknown-media-type behavior).\r\n const mt = part.inlineData.mimeType;\r\n if (\r\n mt === \"image/jpeg\" ||\r\n mt === \"image/png\" ||\r\n mt === \"image/gif\" ||\r\n mt === \"image/webp\"\r\n ) {\r\n out.push({\r\n type: \"image\",\r\n source: { kind: \"base64\", mediaType: mt, data: part.inlineData.data },\r\n });\r\n }\r\n }\r\n // fileData / functionResponse in assistant responses: not currently observed.\r\n }\r\n return out;\r\n}\r\n","/**\r\n * Bundled pricing for Google Gemini models.\r\n *\r\n * Source: https://ai.google.dev/gemini-api/docs/pricing (verified 2026-05).\r\n * Override per model via `pricingOverrides` on the adapter options.\r\n *\r\n * Gemini pricing has separate tiers for prompts under 200k tokens vs over\r\n * 200k tokens (the \"long-context premium\"). The bundled values are the\r\n * UNDER-200k-token rates, which dominate typical usage. For long-context\r\n * workloads, supply `pricingOverrides` with the over-200k rates.\r\n */\r\n\r\nimport type { ModelPricing } from \"@llm-ports/core\";\r\n\r\nexport const GEMINI_PRICING: Record<string, ModelPricing> = {\r\n // Gemini 2.5 family (2026-05 GA pricing)\r\n \"gemini-2.5-pro\": {\r\n inputPer1M: 1.25,\r\n outputPer1M: 5.0,\r\n cacheReadPer1M: 0.3125,\r\n },\r\n \"gemini-2.5-flash\": {\r\n inputPer1M: 0.075,\r\n outputPer1M: 0.3,\r\n cacheReadPer1M: 0.01875,\r\n },\r\n \"gemini-2.5-flash-lite\": {\r\n inputPer1M: 0.0375,\r\n outputPer1M: 0.15,\r\n cacheReadPer1M: 0.009375,\r\n },\r\n // Gemini 2.0 family (still available)\r\n \"gemini-2.0-flash\": {\r\n inputPer1M: 0.1,\r\n outputPer1M: 0.4,\r\n cacheReadPer1M: 0.025,\r\n },\r\n \"gemini-2.0-flash-lite\": {\r\n inputPer1M: 0.075,\r\n outputPer1M: 0.3,\r\n },\r\n};\r\n\r\nexport function lookupGeminiPricing(modelId: string): ModelPricing | undefined {\r\n return GEMINI_PRICING[modelId];\r\n}\r\n","/**\r\n * Google Gemini adapter for llm-ports.\r\n *\r\n * Wraps @google/genai (the unified Gemini + Vertex SDK as of 2026) to\r\n * implement LLMPort. Provides:\r\n *\r\n * - Native multimodal: image content blocks pass through as inlineData\r\n * (base64) or fileData (URL). NO degradation, unlike OpenAI-compat\r\n * baseURL where image_url.detail is silently ignored.\r\n * - Native streaming via generateContentStream\r\n * - Structured output via prompted-JSON + Zod retry-with-feedback\r\n * + alpha.5 programmatic repair. Native Gemini responseSchema lands\r\n * in v0.2.\r\n * - Image-block boundary validation (size + URL scheme) — same shape\r\n * as adapter-anthropic and adapter-openai (alpha.5).\r\n *\r\n * Out of scope for v0.1 alpha:\r\n * - Embeddings (Gemini's embedding API is separate; lands in v0.2)\r\n * - Multi-turn runAgent through Gemini's native automatic tool calling\r\n * (v0.1 ships a single-turn shim consistent with adapter-vercel)\r\n * - Caching API (Gemini supports explicit context caching; lands in v0.2)\r\n * - Code execution tool (Gemini's built-in code interpreter; lands in v0.2)\r\n */\r\n\r\nimport { GoogleGenAI } from \"@google/genai\";\r\nimport {\r\n attemptValidationRepair,\r\n computeChatCost,\r\n extractJSON,\r\n failValidation,\r\n stringifyContentBlocks,\r\n throwIfAborted,\r\n tryParsePartialJSON,\r\n validateImageBlocks,\r\n wrapProviderError,\r\n type AgentResult,\r\n type ContentBlock,\r\n type GenerateStructuredOptions,\r\n type GenerateStructuredResult,\r\n type GenerateTextOptions,\r\n type GenerateTextResult,\r\n type LLMPort,\r\n type MessageContent,\r\n type ModelPricing,\r\n type RunAgentOptions,\r\n type StreamStructuredOptions,\r\n type StreamTextOptions,\r\n type TokenUsage,\r\n type ValidationStrategy,\r\n} from \"@llm-ports/core\";\r\nimport {\r\n extractGeminiText,\r\n toGeminiParts2,\r\n toGeminiRequest,\r\n type GeminiPart,\r\n} from \"./content.js\";\r\nimport { GEMINI_PRICING } from \"./pricing.js\";\r\n\r\n// ─── Adapter options ─────────────────────────────────────────────────\r\n\r\nexport interface GoogleAdapterOptions {\r\n /** Google AI API key (https://aistudio.google.com/apikey). */\r\n apiKey: string;\r\n /** Override Gemini pricing for any model id. Falls back to the bundled table. */\r\n pricingOverrides?: Record<string, ModelPricing>;\r\n /** Default validation strategy if the registry doesn't override per-call. */\r\n validationStrategy?: ValidationStrategy;\r\n /**\r\n * Maximum bytes per base64 image. Defaults to 20MB (Gemini accepts up to\r\n * 20MB inlined; fileData URLs are unconstrained but provider-fetched).\r\n * Set to 0 or a negative number to disable size validation.\r\n */\r\n imageSizeLimitBytes?: number;\r\n}\r\n\r\n// ─── Internal context ────────────────────────────────────────────────\r\n\r\ninterface AdapterContext {\r\n client: GoogleGenAI;\r\n validationStrategy: ValidationStrategy;\r\n pricingOverrides: Record<string, ModelPricing>;\r\n imageSizeLimitBytes: number;\r\n}\r\n\r\nfunction pricingFor(ctx: AdapterContext, modelId: string): ModelPricing {\r\n const pricing = ctx.pricingOverrides[modelId] ?? GEMINI_PRICING[modelId];\r\n if (!pricing) {\r\n throw new Error(\r\n `No pricing entry for Google Gemini model \"${modelId}\". Provide pricingOverrides or update src/pricing.ts.`,\r\n );\r\n }\r\n return pricing;\r\n}\r\n\r\n// ─── Public factory ──────────────────────────────────────────────────\r\n\r\nexport interface GoogleAdapter {\r\n name: \"google\";\r\n pricing: Record<string, ModelPricing>;\r\n createLLMPort: (modelId: string, alias: string) => LLMPort;\r\n}\r\n\r\nexport function createGoogleAdapter(opts: GoogleAdapterOptions): GoogleAdapter {\r\n const mergedPricing: Record<string, ModelPricing> = {\r\n ...GEMINI_PRICING,\r\n ...(opts.pricingOverrides ?? {}),\r\n };\r\n const ctx: AdapterContext = {\r\n client: new GoogleGenAI({ apiKey: opts.apiKey }),\r\n validationStrategy: opts.validationStrategy ?? {\r\n kind: \"retry-with-feedback\",\r\n maxAttempts: 2,\r\n includeOriginalError: true,\r\n },\r\n pricingOverrides: opts.pricingOverrides ?? {},\r\n imageSizeLimitBytes: opts.imageSizeLimitBytes ?? 20 * 1024 * 1024,\r\n };\r\n return {\r\n name: \"google\",\r\n pricing: mergedPricing,\r\n createLLMPort: (modelId, alias) => createPort(ctx, modelId, alias),\r\n };\r\n}\r\n\r\n// ─── Port implementation ─────────────────────────────────────────────\r\n\r\nfunction createPort(ctx: AdapterContext, modelId: string, alias: string): LLMPort {\r\n const pricing = pricingFor(ctx, modelId);\r\n\r\n // Image-block validation closure: throws ImageTooLargeError or\r\n // InvalidImageUrlError before the SDK call.\r\n const validateContent = (content: MessageContent): void => {\r\n if (Array.isArray(content)) {\r\n validateImageBlocks(content, {\r\n alias,\r\n ...(ctx.imageSizeLimitBytes > 0 ? { limitBytes: ctx.imageSizeLimitBytes } : {}),\r\n });\r\n }\r\n };\r\n const validateMessages = (messages: ReadonlyArray<{ content: MessageContent }>): void => {\r\n for (const msg of messages) validateContent(msg.content);\r\n };\r\n\r\n return {\r\n async generateText(options: GenerateTextOptions): Promise<GenerateTextResult> {\r\n throwIfAborted(options.signal);\r\n validateContent(options.prompt);\r\n const start = Date.now();\r\n try {\r\n const parts = toGeminiParts2(options.prompt);\r\n const response = await ctx.client.models.generateContent({\r\n model: modelId,\r\n contents: [{ role: \"user\", parts }],\r\n config: {\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : {}),\r\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\r\n ...(options.maxOutputTokens !== undefined\r\n ? { maxOutputTokens: options.maxOutputTokens }\r\n : {}),\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n const candidate = response.candidates?.[0];\r\n const text = extractGeminiText(candidate?.content?.parts as GeminiPart[] | undefined);\r\n const usage = parseUsage(response);\r\n return {\r\n text,\r\n usage,\r\n cost: computeChatCost(usage, pricing),\r\n modelId: response.modelVersion ?? modelId,\r\n providerAlias: alias,\r\n latencyMs: Date.now() - start,\r\n };\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n },\r\n\r\n async generateStructured<T>(\r\n options: GenerateStructuredOptions<T>,\r\n ): Promise<GenerateStructuredResult<T>> {\r\n throwIfAborted(options.signal);\r\n validateContent(options.prompt);\r\n const start = Date.now();\r\n let attempts = 0;\r\n const maxAttempts =\r\n ctx.validationStrategy.kind === \"retry-with-feedback\"\r\n ? ctx.validationStrategy.maxAttempts\r\n : 1;\r\n\r\n let correctionPrompt: string | null = null;\r\n let lastUsage: TokenUsage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };\r\n let lastModelId = modelId;\r\n\r\n while (attempts < maxAttempts) {\r\n attempts++;\r\n const userText = correctionPrompt\r\n ? `${stringifyContentBlocks(options.prompt)}\\n\\n${correctionPrompt}`\r\n : `${stringifyContentBlocks(options.prompt)}\\n\\nReply with a single JSON object only. No prose, no code fences.`;\r\n\r\n try {\r\n const response = await ctx.client.models.generateContent({\r\n model: modelId,\r\n contents: [{ role: \"user\", parts: [{ text: userText }] }],\r\n config: {\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : {}),\r\n temperature: options.temperature ?? 0,\r\n ...(options.maxOutputTokens !== undefined\r\n ? { maxOutputTokens: options.maxOutputTokens }\r\n : {}),\r\n responseMimeType: \"application/json\",\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n const candidate = response.candidates?.[0];\r\n const raw = extractGeminiText(candidate?.content?.parts as GeminiPart[] | undefined);\r\n lastUsage = parseUsage(response);\r\n lastModelId = response.modelVersion ?? modelId;\r\n\r\n const decoded = extractJSON(raw);\r\n let parsed = options.schema.safeParse(decoded);\r\n if (!parsed.success) {\r\n const repaired = attemptValidationRepair(decoded, parsed.error);\r\n const reparsed = options.schema.safeParse(repaired);\r\n if (reparsed.success) parsed = reparsed;\r\n }\r\n if (parsed.success) {\r\n return {\r\n data: parsed.data as T,\r\n usage: lastUsage,\r\n cost: computeChatCost(lastUsage, pricing),\r\n modelId: lastModelId,\r\n providerAlias: alias,\r\n latencyMs: Date.now() - start,\r\n validationAttempts: attempts,\r\n };\r\n }\r\n if (\r\n ctx.validationStrategy.kind === \"retry-with-feedback\" &&\r\n attempts < maxAttempts\r\n ) {\r\n const issues = parsed.error.issues\r\n .map((i) => `- ${i.path.join(\".\") || \"<root>\"}: ${i.message}`)\r\n .join(\"\\n\");\r\n correctionPrompt = `Your previous response failed validation:\\n${issues}\\n\\nReply with a single corrected JSON object only.`;\r\n continue;\r\n }\r\n failValidation(parsed.error.issues, attempts);\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n }\r\n throw new Error(\"generateStructured exhausted attempts\");\r\n },\r\n\r\n async *streamText(options: StreamTextOptions): AsyncIterable<string> {\r\n throwIfAborted(options.signal);\r\n validateContent(options.prompt);\r\n try {\r\n const parts = toGeminiParts2(options.prompt);\r\n const stream = await ctx.client.models.generateContentStream({\r\n model: modelId,\r\n contents: [{ role: \"user\", parts }],\r\n config: {\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : {}),\r\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\r\n ...(options.maxOutputTokens !== undefined\r\n ? { maxOutputTokens: options.maxOutputTokens }\r\n : {}),\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n for await (const chunk of stream) {\r\n const text = extractGeminiText(\r\n chunk.candidates?.[0]?.content?.parts as GeminiPart[] | undefined,\r\n );\r\n if (text.length > 0) yield text;\r\n }\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n },\r\n\r\n async *streamStructured<T>(options: StreamStructuredOptions<T>): AsyncIterable<Partial<T>> {\r\n throwIfAborted(options.signal);\r\n validateContent(options.prompt);\r\n try {\r\n const stream = await ctx.client.models.generateContentStream({\r\n model: modelId,\r\n contents: [\r\n {\r\n role: \"user\",\r\n parts: [\r\n {\r\n text: `${stringifyContentBlocks(options.prompt)}\\n\\nReply with a single JSON object only. Stream the JSON progressively.`,\r\n },\r\n ],\r\n },\r\n ],\r\n config: {\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : {}),\r\n temperature: options.temperature ?? 0,\r\n ...(options.maxOutputTokens !== undefined\r\n ? { maxOutputTokens: options.maxOutputTokens }\r\n : {}),\r\n responseMimeType: \"application/json\",\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n let buffer = \"\";\r\n for await (const chunk of stream) {\r\n const text = extractGeminiText(\r\n chunk.candidates?.[0]?.content?.parts as GeminiPart[] | undefined,\r\n );\r\n if (text.length === 0) continue;\r\n buffer += text;\r\n const partial = tryParsePartialJSON(buffer);\r\n if (partial !== null) yield partial as Partial<T>;\r\n }\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n },\r\n\r\n async runAgent(options: RunAgentOptions): Promise<AgentResult> {\r\n throwIfAborted(options.signal);\r\n validateMessages(options.messages);\r\n // v0.1: single-turn agent loop. Gemini's native automatic-function-calling\r\n // multi-turn runAgent ships in v0.2 (matches adapter-vercel's v0.1 shape).\r\n const start = Date.now();\r\n try {\r\n const { systemInstruction, contents } = toGeminiRequest(options.messages);\r\n const response = await ctx.client.models.generateContent({\r\n model: modelId,\r\n contents,\r\n config: {\r\n ...(systemInstruction !== undefined ? { systemInstruction } : {}),\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : {}),\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n const candidate = response.candidates?.[0];\r\n const text = extractGeminiText(candidate?.content?.parts as GeminiPart[] | undefined);\r\n const usage = parseUsage(response);\r\n const toolCalls: AgentResult[\"toolCalls\"] = [];\r\n // v0.1 stub: we surface no tool calls. Real tool-use is v0.2 scope.\r\n return {\r\n text,\r\n messages: [\r\n ...options.messages,\r\n { role: \"assistant\" as const, content: text },\r\n ],\r\n usage,\r\n cost: computeChatCost(usage, pricing),\r\n modelId: response.modelVersion ?? modelId,\r\n providerAlias: alias,\r\n latencyMs: Date.now() - start,\r\n toolCalls,\r\n stepsTaken: 1,\r\n terminationReason: \"completed\",\r\n };\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n },\r\n };\r\n}\r\n\r\n// ─── Helpers ─────────────────────────────────────────────────────────\r\n\r\ninterface GeminiUsageMetadata {\r\n promptTokenCount?: number;\r\n candidatesTokenCount?: number;\r\n totalTokenCount?: number;\r\n cachedContentTokenCount?: number;\r\n}\r\n\r\ninterface GeminiResponseShape {\r\n usageMetadata?: GeminiUsageMetadata;\r\n}\r\n\r\nfunction parseUsage(response: GeminiResponseShape): TokenUsage {\r\n const m = response.usageMetadata ?? {};\r\n const inputTokens = m.promptTokenCount ?? 0;\r\n const outputTokens = m.candidatesTokenCount ?? 0;\r\n const totalTokens = m.totalTokenCount ?? inputTokens + outputTokens;\r\n const usage: TokenUsage = { inputTokens, outputTokens, totalTokens };\r\n if (m.cachedContentTokenCount !== undefined && m.cachedContentTokenCount > 0) {\r\n usage.cacheReadTokens = m.cachedContentTokenCount;\r\n }\r\n return usage;\r\n}\r\n\r\n// Re-export ContentBlock for the rare adapter user that wants to type-check\r\n// outside of @llm-ports/core. Keeps the import surface symmetric with the\r\n// other adapters.\r\nexport type { ContentBlock };\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/content.ts","../src/pricing.ts","../src/adapter.ts"],"names":["ContentBlockUnsupportedError","zodToJsonSchema","GoogleGenAI","validateImageBlocks","throwIfAborted","computeChatCost","wrapProviderError","stringifyContentBlocks","extractJSON","attemptValidationRepair","failValidation","tryParsePartialJSON","mergeTokenUsage"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * Content translation between llm-ports ContentBlock[] and Gemini's\r\n * Content + Part shapes.\r\n *\r\n * Gemini's content model:\r\n * Content = { role: \"user\" | \"model\" | \"function\", parts: Part[] }\r\n * Part shapes (the ones we care about):\r\n * - { text: string }\r\n * - { inlineData: { mimeType: string, data: string (base64) } }\r\n * - { fileData: { mimeType: string, fileUri: string } }\r\n * - { functionCall: { name: string, args: object } }\r\n * - { functionResponse: { name: string, response: object } }\r\n *\r\n * Notes:\r\n * - System messages map to a top-level `systemInstruction` field on the\r\n * request, NOT a Content with role: \"system\". `toGeminiRequest` handles\r\n * this split.\r\n * - The assistant role is `\"model\"` in Gemini's vocabulary.\r\n * - Tool results are mapped to `functionResponse` parts.\r\n */\r\n\r\nimport { zodToJsonSchema } from \"zod-to-json-schema\";\r\nimport {\r\n ContentBlockUnsupportedError,\r\n type ContentBlock,\r\n type LLMMessage,\r\n type MessageContent,\r\n type ToolDefinition,\r\n} from \"@llm-ports/core\";\r\n\r\nconst ADAPTER_NAME = \"google\";\r\n\r\n// ─── Outgoing: ContentBlock[] → Gemini Part[] ────────────────────────\r\n\r\ninterface GeminiTextPart {\r\n text: string;\r\n}\r\ninterface GeminiInlineDataPart {\r\n inlineData: { mimeType: string; data: string };\r\n}\r\ninterface GeminiFileDataPart {\r\n fileData: { mimeType: string; fileUri: string };\r\n}\r\ninterface GeminiFunctionCallPart {\r\n functionCall: { name: string; args: Record<string, unknown> };\r\n}\r\ninterface GeminiFunctionResponsePart {\r\n functionResponse: { name: string; response: Record<string, unknown> };\r\n}\r\nexport type GeminiPart =\r\n | GeminiTextPart\r\n | GeminiInlineDataPart\r\n | GeminiFileDataPart\r\n | GeminiFunctionCallPart\r\n | GeminiFunctionResponsePart;\r\n\r\nexport interface GeminiContent {\r\n role: \"user\" | \"model\" | \"function\";\r\n parts: GeminiPart[];\r\n}\r\n\r\n/** Translate a single ContentBlock to one or more GeminiParts. */\r\nfunction toGeminiParts(block: ContentBlock): GeminiPart[] {\r\n switch (block.type) {\r\n case \"text\":\r\n return [{ text: block.text }];\r\n case \"image\": {\r\n if (block.source.kind === \"base64\") {\r\n return [\r\n {\r\n inlineData: {\r\n mimeType: block.source.mediaType,\r\n data: block.source.data,\r\n },\r\n },\r\n ];\r\n }\r\n // URL form\r\n return [\r\n {\r\n fileData: {\r\n // Gemini infers mimeType from URL extension when not provided.\r\n // We pass image/jpeg as a sane default; users wanting tighter\r\n // control should pass base64 with explicit mediaType.\r\n mimeType: \"image/jpeg\",\r\n fileUri: block.source.url,\r\n },\r\n },\r\n ];\r\n }\r\n case \"audio\": {\r\n if (block.source.kind === \"base64\") {\r\n return [\r\n {\r\n inlineData: {\r\n mimeType: block.source.mediaType,\r\n data: block.source.data,\r\n },\r\n },\r\n ];\r\n }\r\n throw new ContentBlockUnsupportedError(ADAPTER_NAME, \"audio (url; Gemini accepts base64 or fileData with fileUri)\");\r\n }\r\n case \"tool_use\": {\r\n // Gemini's tool-call shape has args as a plain object; we pass the input\r\n // through if it's already an object, else wrap.\r\n const args =\r\n block.input !== null && typeof block.input === \"object\"\r\n ? (block.input as Record<string, unknown>)\r\n : { value: block.input };\r\n return [{ functionCall: { name: block.name, args } }];\r\n }\r\n case \"tool_result\": {\r\n // Gemini's functionResponse expects a `response` object. If the\r\n // ContentBlock.tool_result.content is a string, wrap it.\r\n const response: Record<string, unknown> =\r\n typeof block.content === \"string\"\r\n ? { result: block.content }\r\n : { result: extractTextOnly(block.content) };\r\n return [\r\n {\r\n functionResponse: {\r\n // Gemini's API requires the tool name; we use toolUseId since\r\n // llm-ports' ToolResultBlock doesn't carry the name. Adapters\r\n // that need the name can plumb it through a separate channel.\r\n name: block.toolUseId,\r\n response,\r\n },\r\n },\r\n ];\r\n }\r\n }\r\n}\r\n\r\nfunction extractTextOnly(blocks: ContentBlock[]): string {\r\n return blocks\r\n .filter((b): b is Extract<ContentBlock, { type: \"text\" }> => b.type === \"text\")\r\n .map((b) => b.text)\r\n .join(\"\\n\");\r\n}\r\n\r\n/** Translate a MessageContent to Gemini Parts. */\r\nexport function toGeminiParts2(content: MessageContent): GeminiPart[] {\r\n if (typeof content === \"string\") {\r\n return [{ text: content }];\r\n }\r\n return content.flatMap(toGeminiParts);\r\n}\r\n\r\n/**\r\n * Translate an array of LLMMessages into:\r\n * - `systemInstruction`: the concatenated system messages (Gemini puts\r\n * these at the top level of the request, not in `contents`).\r\n * - `contents`: the user + assistant + tool messages, with roles mapped.\r\n */\r\nexport function toGeminiRequest(messages: LLMMessage[]): {\r\n systemInstruction?: string;\r\n contents: GeminiContent[];\r\n} {\r\n let systemInstruction: string | undefined;\r\n const contents: GeminiContent[] = [];\r\n for (const msg of messages) {\r\n if (msg.role === \"system\") {\r\n const text =\r\n typeof msg.content === \"string\" ? msg.content : extractTextOnly(msg.content);\r\n systemInstruction = systemInstruction === undefined ? text : `${systemInstruction}\\n\\n${text}`;\r\n continue;\r\n }\r\n const role: GeminiContent[\"role\"] =\r\n msg.role === \"tool\" ? \"function\" : msg.role === \"assistant\" ? \"model\" : \"user\";\r\n contents.push({\r\n role,\r\n parts: toGeminiParts2(msg.content),\r\n });\r\n }\r\n return systemInstruction !== undefined\r\n ? { systemInstruction, contents }\r\n : { contents };\r\n}\r\n\r\n// ─── Incoming: Gemini response → ContentBlock[] ──────────────────────\r\n\r\ninterface GeminiResponseCandidate {\r\n content?: {\r\n role?: string;\r\n parts?: GeminiPart[];\r\n };\r\n finishReason?: string;\r\n}\r\n\r\n/**\r\n * Extract the assistant text from a Gemini response. Used by generateText\r\n * and by the structured-output path before JSON parsing.\r\n */\r\nexport function extractGeminiText(parts: GeminiPart[] | undefined): string {\r\n if (!parts) return \"\";\r\n return parts\r\n .filter((p): p is GeminiTextPart => \"text\" in p)\r\n .map((p) => p.text)\r\n .join(\"\");\r\n}\r\n\r\n/**\r\n * Translate a Gemini response candidate's parts back into ContentBlock[].\r\n * Used by runAgent to reconstruct the model's tool_use blocks.\r\n */\r\nexport function fromGeminiCandidate(candidate: GeminiResponseCandidate): ContentBlock[] {\r\n const out: ContentBlock[] = [];\r\n const parts = candidate.content?.parts ?? [];\r\n for (const part of parts) {\r\n if (\"text\" in part && part.text.length > 0) {\r\n out.push({ type: \"text\", text: part.text });\r\n } else if (\"functionCall\" in part) {\r\n // Gemini's API doesn't return a tool-call ID. We synthesize one as\r\n // the function name so the tool_result round-trip (which maps\r\n // toolUseId → Gemini functionResponse.name) emits the correct name.\r\n out.push({\r\n type: \"tool_use\",\r\n id: part.functionCall.name,\r\n name: part.functionCall.name,\r\n input: part.functionCall.args,\r\n });\r\n } else if (\"inlineData\" in part) {\r\n // Inline (base64) image in assistant response. Decode if media type\r\n // is one we support, else drop (consistent with adapter-openai's\r\n // unknown-media-type behavior).\r\n const mt = part.inlineData.mimeType;\r\n if (\r\n mt === \"image/jpeg\" ||\r\n mt === \"image/png\" ||\r\n mt === \"image/gif\" ||\r\n mt === \"image/webp\"\r\n ) {\r\n out.push({\r\n type: \"image\",\r\n source: { kind: \"base64\", mediaType: mt, data: part.inlineData.data },\r\n });\r\n }\r\n }\r\n // fileData / functionResponse in assistant responses: not currently observed.\r\n }\r\n return out;\r\n}\r\n\r\n// ─── Tools: ToolDefinition[] → Gemini Tool[] ─────────────────────────\r\n\r\nexport interface GeminiFunctionDeclaration {\r\n name: string;\r\n description: string;\r\n parameters?: Record<string, unknown>;\r\n}\r\n\r\nexport interface GeminiTool {\r\n functionDeclarations: GeminiFunctionDeclaration[];\r\n}\r\n\r\n/**\r\n * Translate llm-ports tool definitions to Gemini's Tool[] shape.\r\n *\r\n * Gemini's FunctionDeclaration.parameters accepts an OpenAPI 3.0 subset of\r\n * JSON Schema. zodToJsonSchema with target \"openApi3\" emits the closest\r\n * dialect; we then strip $schema and definitions so the parameters object\r\n * is what Gemini expects.\r\n */\r\nexport function toGeminiTools(tools: Record<string, ToolDefinition>): GeminiTool[] {\r\n const declarations: GeminiFunctionDeclaration[] = Object.entries(tools).map(\r\n ([name, def]) => ({\r\n name,\r\n description: def.description,\r\n parameters: zodToGeminiSchema(def.inputSchema),\r\n }),\r\n );\r\n return [{ functionDeclarations: declarations }];\r\n}\r\n\r\n/**\r\n * Convert a Zod schema to a JSON Schema shape Gemini accepts (OpenAPI 3.0\r\n * subset). Strips fields Gemini rejects (`$schema`, `definitions`, `$defs`).\r\n *\r\n * Falls back to `{ type: \"object\", properties: {} }` if conversion fails so\r\n * a malformed tool definition doesn't crash the agent loop.\r\n */\r\nexport function zodToGeminiSchema(schema: unknown): Record<string, unknown> {\r\n try {\r\n const json = zodToJsonSchema(schema as never, {\r\n target: \"openApi3\",\r\n $refStrategy: \"none\",\r\n }) as Record<string, unknown>;\r\n return sanitizeGeminiSchema(json);\r\n } catch {\r\n return { type: \"object\", properties: {} };\r\n }\r\n}\r\n\r\n/**\r\n * Strip JSON Schema fields Gemini rejects. Applied recursively across\r\n * nested `properties` and `items`. Public so generateStructured can reuse\r\n * it for native responseSchema (alpha.9 §2.1).\r\n */\r\nexport function sanitizeGeminiSchema(input: unknown): Record<string, unknown> {\r\n if (!input || typeof input !== \"object\" || Array.isArray(input)) {\r\n return { type: \"object\", properties: {} };\r\n }\r\n const cleaned: Record<string, unknown> = {};\r\n for (const [key, value] of Object.entries(input as Record<string, unknown>)) {\r\n if (key === \"$schema\" || key === \"definitions\" || key === \"$defs\" || key === \"$ref\") {\r\n continue;\r\n }\r\n if (key === \"properties\" && value && typeof value === \"object\") {\r\n const props: Record<string, unknown> = {};\r\n for (const [propName, propSchema] of Object.entries(value as Record<string, unknown>)) {\r\n props[propName] = sanitizeGeminiSchema(propSchema);\r\n }\r\n cleaned[key] = props;\r\n continue;\r\n }\r\n if (key === \"items\" && value && typeof value === \"object\" && !Array.isArray(value)) {\r\n cleaned[key] = sanitizeGeminiSchema(value);\r\n continue;\r\n }\r\n cleaned[key] = value;\r\n }\r\n return cleaned;\r\n}\r\n\r\n/**\r\n * Detect JSON Schema features Gemini's responseSchema constrained-decoding\r\n * does not support. Returns the first unsupported feature found, or null\r\n * if the schema is clean. Used by generateStructured to decide between\r\n * native responseSchema and the prompted-JSON fallback.\r\n *\r\n * Unsupported (Gemini accepts OpenAPI 3.0 subset):\r\n * - `oneOf` (use anyOf instead; or fall back to prompted JSON)\r\n * - `allOf` (limited support; safer to fall back)\r\n * - `not`\r\n * - `$ref` (we strip these via sanitize, but their presence indicates\r\n * a recursive schema we shouldn't pass through)\r\n */\r\nexport function detectUnsupportedSchemaFeature(input: unknown): string | null {\r\n if (!input || typeof input !== \"object\" || Array.isArray(input)) return null;\r\n const obj = input as Record<string, unknown>;\r\n for (const key of [\"oneOf\", \"allOf\", \"not\", \"$ref\"]) {\r\n if (key in obj) return key;\r\n }\r\n if (obj[\"properties\"] && typeof obj[\"properties\"] === \"object\") {\r\n for (const propSchema of Object.values(obj[\"properties\"] as Record<string, unknown>)) {\r\n const nested = detectUnsupportedSchemaFeature(propSchema);\r\n if (nested) return nested;\r\n }\r\n }\r\n if (obj[\"items\"] && typeof obj[\"items\"] === \"object\" && !Array.isArray(obj[\"items\"])) {\r\n const nested = detectUnsupportedSchemaFeature(obj[\"items\"]);\r\n if (nested) return nested;\r\n }\r\n if (Array.isArray(obj[\"anyOf\"])) {\r\n for (const sub of obj[\"anyOf\"]) {\r\n const nested = detectUnsupportedSchemaFeature(sub);\r\n if (nested) return nested;\r\n }\r\n }\r\n return null;\r\n}\r\n","/**\r\n * Bundled pricing for Google Gemini models.\r\n *\r\n * Source: https://ai.google.dev/gemini-api/docs/pricing (verified 2026-05).\r\n * Override per model via `pricingOverrides` on the adapter options.\r\n *\r\n * Gemini pricing has separate tiers for prompts under 200k tokens vs over\r\n * 200k tokens (the \"long-context premium\"). The bundled values are the\r\n * UNDER-200k-token rates, which dominate typical usage. For long-context\r\n * workloads, supply `pricingOverrides` with the over-200k rates.\r\n */\r\n\r\nimport type { ModelPricing } from \"@llm-ports/core\";\r\n\r\nexport const GEMINI_PRICING: Record<string, ModelPricing> = {\r\n // Gemini 2.5 family (2026-05 GA pricing)\r\n \"gemini-2.5-pro\": {\r\n inputPer1M: 1.25,\r\n outputPer1M: 5.0,\r\n cacheReadPer1M: 0.3125,\r\n },\r\n \"gemini-2.5-flash\": {\r\n inputPer1M: 0.075,\r\n outputPer1M: 0.3,\r\n cacheReadPer1M: 0.01875,\r\n },\r\n \"gemini-2.5-flash-lite\": {\r\n inputPer1M: 0.0375,\r\n outputPer1M: 0.15,\r\n cacheReadPer1M: 0.009375,\r\n },\r\n // Gemini 2.0 family (still available)\r\n \"gemini-2.0-flash\": {\r\n inputPer1M: 0.1,\r\n outputPer1M: 0.4,\r\n cacheReadPer1M: 0.025,\r\n },\r\n \"gemini-2.0-flash-lite\": {\r\n inputPer1M: 0.075,\r\n outputPer1M: 0.3,\r\n },\r\n};\r\n\r\nexport function lookupGeminiPricing(modelId: string): ModelPricing | undefined {\r\n return GEMINI_PRICING[modelId];\r\n}\r\n","/**\r\n * Google Gemini adapter for llm-ports.\r\n *\r\n * Wraps @google/genai (the unified Gemini + Vertex SDK as of 2026) to\r\n * implement LLMPort. Provides:\r\n *\r\n * - Native multimodal: image content blocks pass through as inlineData\r\n * (base64) or fileData (URL). NO degradation, unlike OpenAI-compat\r\n * baseURL where image_url.detail is silently ignored.\r\n * - Native streaming via generateContentStream\r\n * - Structured output via prompted-JSON + Zod retry-with-feedback\r\n * + alpha.5 programmatic repair. Native Gemini responseSchema lands\r\n * in v0.2.\r\n * - Image-block boundary validation (size + URL scheme) — same shape\r\n * as adapter-anthropic and adapter-openai (alpha.5).\r\n *\r\n * Out of scope for v0.1 alpha:\r\n * - Embeddings (Gemini's embedding API is separate; lands in v0.2)\r\n * - Multi-turn runAgent through Gemini's native automatic tool calling\r\n * (v0.1 ships a single-turn shim consistent with adapter-vercel)\r\n * - Caching API (Gemini supports explicit context caching; lands in v0.2)\r\n * - Code execution tool (Gemini's built-in code interpreter; lands in v0.2)\r\n */\r\n\r\nimport { GoogleGenAI } from \"@google/genai\";\r\nimport {\r\n attemptValidationRepair,\r\n computeChatCost,\r\n extractJSON,\r\n failValidation,\r\n mergeTokenUsage,\r\n stringifyContentBlocks,\r\n throwIfAborted,\r\n tryParsePartialJSON,\r\n validateImageBlocks,\r\n wrapProviderError,\r\n type AgentResult,\r\n type ContentBlock,\r\n type GenerateStructuredOptions,\r\n type GenerateStructuredResult,\r\n type GenerateTextOptions,\r\n type GenerateTextResult,\r\n type LLMMessage,\r\n type LLMPort,\r\n type MessageContent,\r\n type ModelPricing,\r\n type ProviderModelInfo,\r\n type RunAgentOptions,\r\n type StreamStructuredOptions,\r\n type StreamTextOptions,\r\n type TokenUsage,\r\n type ValidationStrategy,\r\n} from \"@llm-ports/core\";\r\nimport {\r\n detectUnsupportedSchemaFeature,\r\n extractGeminiText,\r\n fromGeminiCandidate,\r\n sanitizeGeminiSchema,\r\n toGeminiParts2,\r\n toGeminiRequest,\r\n toGeminiTools,\r\n zodToGeminiSchema,\r\n type GeminiPart,\r\n} from \"./content.js\";\r\nimport { GEMINI_PRICING } from \"./pricing.js\";\r\n\r\n// ─── Adapter options ─────────────────────────────────────────────────\r\n\r\nexport interface GoogleAdapterOptions {\r\n /** Google AI API key (https://aistudio.google.com/apikey). */\r\n apiKey: string;\r\n /** Override Gemini pricing for any model id. Falls back to the bundled table. */\r\n pricingOverrides?: Record<string, ModelPricing>;\r\n /** Default validation strategy if the registry doesn't override per-call. */\r\n validationStrategy?: ValidationStrategy;\r\n /**\r\n * Maximum bytes per base64 image. Defaults to 20MB (Gemini accepts up to\r\n * 20MB inlined; fileData URLs are unconstrained but provider-fetched).\r\n * Set to 0 or a negative number to disable size validation.\r\n */\r\n imageSizeLimitBytes?: number;\r\n}\r\n\r\n// ─── Internal context ────────────────────────────────────────────────\r\n\r\ninterface AdapterContext {\r\n client: GoogleGenAI;\r\n validationStrategy: ValidationStrategy;\r\n pricingOverrides: Record<string, ModelPricing>;\r\n imageSizeLimitBytes: number;\r\n}\r\n\r\nfunction pricingFor(ctx: AdapterContext, modelId: string): ModelPricing {\r\n const pricing = ctx.pricingOverrides[modelId] ?? GEMINI_PRICING[modelId];\r\n if (!pricing) {\r\n throw new Error(\r\n `No pricing entry for Google Gemini model \"${modelId}\". Provide pricingOverrides or update src/pricing.ts.`,\r\n );\r\n }\r\n return pricing;\r\n}\r\n\r\n// ─── Public factory ──────────────────────────────────────────────────\r\n\r\nexport interface GoogleAdapter {\r\n name: \"google\";\r\n pricing: Record<string, ModelPricing>;\r\n createLLMPort: (modelId: string, alias: string) => LLMPort;\r\n}\r\n\r\nexport function createGoogleAdapter(opts: GoogleAdapterOptions): GoogleAdapter {\r\n const mergedPricing: Record<string, ModelPricing> = {\r\n ...GEMINI_PRICING,\r\n ...(opts.pricingOverrides ?? {}),\r\n };\r\n const ctx: AdapterContext = {\r\n client: new GoogleGenAI({ apiKey: opts.apiKey }),\r\n validationStrategy: opts.validationStrategy ?? {\r\n kind: \"retry-with-feedback\",\r\n maxAttempts: 2,\r\n includeOriginalError: true,\r\n },\r\n pricingOverrides: opts.pricingOverrides ?? {},\r\n imageSizeLimitBytes: opts.imageSizeLimitBytes ?? 20 * 1024 * 1024,\r\n };\r\n return {\r\n name: \"google\",\r\n pricing: mergedPricing,\r\n createLLMPort: (modelId, alias) => createPort(ctx, modelId, alias),\r\n };\r\n}\r\n\r\n// ─── Port implementation ─────────────────────────────────────────────\r\n\r\nfunction createPort(ctx: AdapterContext, modelId: string, alias: string): LLMPort {\r\n const pricing = pricingFor(ctx, modelId);\r\n\r\n // Image-block validation closure: throws ImageTooLargeError or\r\n // InvalidImageUrlError before the SDK call.\r\n const validateContent = (content: MessageContent): void => {\r\n if (Array.isArray(content)) {\r\n validateImageBlocks(content, {\r\n alias,\r\n ...(ctx.imageSizeLimitBytes > 0 ? { limitBytes: ctx.imageSizeLimitBytes } : {}),\r\n });\r\n }\r\n };\r\n const validateMessages = (messages: ReadonlyArray<{ content: MessageContent }>): void => {\r\n for (const msg of messages) validateContent(msg.content);\r\n };\r\n\r\n return {\r\n async generateText(options: GenerateTextOptions): Promise<GenerateTextResult> {\r\n throwIfAborted(options.signal);\r\n validateContent(options.prompt);\r\n const start = Date.now();\r\n try {\r\n const parts = toGeminiParts2(options.prompt);\r\n const response = await ctx.client.models.generateContent({\r\n model: modelId,\r\n contents: [{ role: \"user\", parts }],\r\n config: {\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : {}),\r\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\r\n ...(options.maxOutputTokens !== undefined\r\n ? { maxOutputTokens: options.maxOutputTokens }\r\n : {}),\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n const candidate = response.candidates?.[0];\r\n const text = extractGeminiText(candidate?.content?.parts as GeminiPart[] | undefined);\r\n const usage = parseUsage(response);\r\n return {\r\n text,\r\n usage,\r\n cost: computeChatCost(usage, pricing),\r\n modelId: response.modelVersion ?? modelId,\r\n providerAlias: alias,\r\n latencyMs: Date.now() - start,\r\n };\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n },\r\n\r\n async generateStructured<T>(\r\n options: GenerateStructuredOptions<T>,\r\n ): Promise<GenerateStructuredResult<T>> {\r\n throwIfAborted(options.signal);\r\n validateContent(options.prompt);\r\n const start = Date.now();\r\n let attempts = 0;\r\n const maxAttempts =\r\n ctx.validationStrategy.kind === \"retry-with-feedback\"\r\n ? ctx.validationStrategy.maxAttempts\r\n : 1;\r\n\r\n // Native responseSchema path: convert schema to JSON Schema; if no\r\n // Gemini-unsupported features (oneOf, $ref, etc.) emit it as\r\n // `config.responseSchema` so Gemini constrains decoding to the schema\r\n // before tokens are produced. We still validate with Zod (Gemini's\r\n // schema enforcement is best-effort) and still run the repair pass.\r\n // If the schema uses unsupported features, fall back to the prompted-\r\n // JSON path (existing behavior).\r\n const jsonSchema = zodToGeminiSchema(options.schema);\r\n const unsupportedFeature = detectUnsupportedSchemaFeature(jsonSchema);\r\n const useNativeResponseSchema = unsupportedFeature === null;\r\n if (!useNativeResponseSchema) {\r\n warnSchemaFallback(modelId, unsupportedFeature);\r\n }\r\n const sanitizedSchema = useNativeResponseSchema\r\n ? sanitizeGeminiSchema(jsonSchema)\r\n : null;\r\n\r\n let correctionPrompt: string | null = null;\r\n let lastUsage: TokenUsage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };\r\n let lastModelId = modelId;\r\n\r\n while (attempts < maxAttempts) {\r\n attempts++;\r\n // With native responseSchema, Gemini constrains the decoding — we\r\n // skip the \"Reply with a single JSON object only\" suffix on the\r\n // first attempt. Correction prompts still apply on retry-with-\r\n // feedback rounds.\r\n const userText = correctionPrompt\r\n ? `${stringifyContentBlocks(options.prompt)}\\n\\n${correctionPrompt}`\r\n : useNativeResponseSchema\r\n ? stringifyContentBlocks(options.prompt)\r\n : `${stringifyContentBlocks(options.prompt)}\\n\\nReply with a single JSON object only. No prose, no code fences.`;\r\n\r\n try {\r\n const response = await ctx.client.models.generateContent({\r\n model: modelId,\r\n contents: [{ role: \"user\", parts: [{ text: userText }] }],\r\n config: {\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : {}),\r\n temperature: options.temperature ?? 0,\r\n ...(options.maxOutputTokens !== undefined\r\n ? { maxOutputTokens: options.maxOutputTokens }\r\n : {}),\r\n responseMimeType: \"application/json\",\r\n ...(sanitizedSchema ? { responseSchema: sanitizedSchema } : {}),\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n const candidate = response.candidates?.[0];\r\n const raw = extractGeminiText(candidate?.content?.parts as GeminiPart[] | undefined);\r\n lastUsage = parseUsage(response);\r\n lastModelId = response.modelVersion ?? modelId;\r\n\r\n const decoded = extractJSON(raw);\r\n let parsed = options.schema.safeParse(decoded);\r\n if (!parsed.success) {\r\n const repaired = attemptValidationRepair(decoded, parsed.error);\r\n const reparsed = options.schema.safeParse(repaired);\r\n if (reparsed.success) parsed = reparsed;\r\n }\r\n if (parsed.success) {\r\n return {\r\n data: parsed.data as T,\r\n usage: lastUsage,\r\n cost: computeChatCost(lastUsage, pricing),\r\n modelId: lastModelId,\r\n providerAlias: alias,\r\n latencyMs: Date.now() - start,\r\n validationAttempts: attempts,\r\n };\r\n }\r\n if (\r\n ctx.validationStrategy.kind === \"retry-with-feedback\" &&\r\n attempts < maxAttempts\r\n ) {\r\n const issues = parsed.error.issues\r\n .map((i) => `- ${i.path.join(\".\") || \"<root>\"}: ${i.message}`)\r\n .join(\"\\n\");\r\n correctionPrompt = `Your previous response failed validation:\\n${issues}\\n\\nReply with a single corrected JSON object only.`;\r\n continue;\r\n }\r\n failValidation(parsed.error.issues, attempts);\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n }\r\n throw new Error(\"generateStructured exhausted attempts\");\r\n },\r\n\r\n async *streamText(options: StreamTextOptions): AsyncIterable<string> {\r\n throwIfAborted(options.signal);\r\n validateContent(options.prompt);\r\n try {\r\n const parts = toGeminiParts2(options.prompt);\r\n const stream = await ctx.client.models.generateContentStream({\r\n model: modelId,\r\n contents: [{ role: \"user\", parts }],\r\n config: {\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : {}),\r\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\r\n ...(options.maxOutputTokens !== undefined\r\n ? { maxOutputTokens: options.maxOutputTokens }\r\n : {}),\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n for await (const chunk of stream) {\r\n const text = extractGeminiText(\r\n chunk.candidates?.[0]?.content?.parts as GeminiPart[] | undefined,\r\n );\r\n if (text.length > 0) yield text;\r\n }\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n },\r\n\r\n async *streamStructured<T>(options: StreamStructuredOptions<T>): AsyncIterable<Partial<T>> {\r\n throwIfAborted(options.signal);\r\n validateContent(options.prompt);\r\n try {\r\n const stream = await ctx.client.models.generateContentStream({\r\n model: modelId,\r\n contents: [\r\n {\r\n role: \"user\",\r\n parts: [\r\n {\r\n text: `${stringifyContentBlocks(options.prompt)}\\n\\nReply with a single JSON object only. Stream the JSON progressively.`,\r\n },\r\n ],\r\n },\r\n ],\r\n config: {\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : {}),\r\n temperature: options.temperature ?? 0,\r\n ...(options.maxOutputTokens !== undefined\r\n ? { maxOutputTokens: options.maxOutputTokens }\r\n : {}),\r\n responseMimeType: \"application/json\",\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n let buffer = \"\";\r\n for await (const chunk of stream) {\r\n const text = extractGeminiText(\r\n chunk.candidates?.[0]?.content?.parts as GeminiPart[] | undefined,\r\n );\r\n if (text.length === 0) continue;\r\n buffer += text;\r\n const partial = tryParsePartialJSON(buffer);\r\n if (partial !== null) yield partial as Partial<T>;\r\n }\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n },\r\n\r\n async runAgent(options: RunAgentOptions): Promise<AgentResult> {\r\n throwIfAborted(options.signal);\r\n validateMessages(options.messages);\r\n const start = Date.now();\r\n const maxSteps = options.maxSteps ?? 10;\r\n const conversation: LLMMessage[] = [...options.messages];\r\n const toolCalls: AgentResult[\"toolCalls\"] = [];\r\n let stepsTaken = 0;\r\n let totalUsage: TokenUsage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };\r\n let finalText = \"\";\r\n let lastModelId = modelId;\r\n let terminationReason: AgentResult[\"terminationReason\"] = \"max_steps\";\r\n\r\n const toolsRegistered = Object.keys(options.tools).length > 0;\r\n const geminiTools = toolsRegistered ? toGeminiTools(options.tools) : undefined;\r\n\r\n try {\r\n for (let step = 0; step < maxSteps; step++) {\r\n // Re-check between steps so cancellation propagates even if the\r\n // model just emitted a function call but the user clicked cancel\r\n // before we send the result back.\r\n throwIfAborted(options.signal);\r\n stepsTaken = step + 1;\r\n\r\n const { systemInstruction, contents } = toGeminiRequest(conversation);\r\n const response = await ctx.client.models.generateContent({\r\n model: modelId,\r\n contents,\r\n config: {\r\n // options.instructions takes precedence over a system message\r\n // baked into the messages array, matching the per-method pattern\r\n // used elsewhere in the adapter.\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : systemInstruction !== undefined\r\n ? { systemInstruction }\r\n : {}),\r\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\r\n ...(options.maxOutputTokens !== undefined\r\n ? { maxOutputTokens: options.maxOutputTokens }\r\n : {}),\r\n ...(geminiTools ? { tools: geminiTools } : {}),\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n\r\n totalUsage = mergeTokenUsage(totalUsage, parseUsage(response));\r\n lastModelId = response.modelVersion ?? modelId;\r\n\r\n const candidate = response.candidates?.[0];\r\n const responseParts = (candidate?.content?.parts ?? []) as GeminiPart[];\r\n const blocks = fromGeminiCandidate({\r\n content: { parts: responseParts },\r\n });\r\n\r\n // Push the assistant's response (text + tool_use blocks) into the\r\n // canonical conversation so the next round sees it.\r\n conversation.push({\r\n role: \"assistant\",\r\n content: blocks.length > 0\r\n ? blocks\r\n : [{ type: \"text\", text: extractGeminiText(responseParts) }],\r\n });\r\n\r\n finalText = blocks\r\n .filter((b): b is Extract<ContentBlock, { type: \"text\" }> => b.type === \"text\")\r\n .map((b) => b.text)\r\n .join(\"\");\r\n\r\n const toolUses = blocks.filter(\r\n (b): b is Extract<ContentBlock, { type: \"tool_use\" }> => b.type === \"tool_use\",\r\n );\r\n if (toolUses.length === 0) {\r\n terminationReason = \"completed\";\r\n break;\r\n }\r\n\r\n // Execute every tool call the model emitted this turn. Gemini may\r\n // emit multiple functionCalls in a single response and expects all\r\n // responses before continuing.\r\n const toolResults: ContentBlock[] = [];\r\n for (const tu of toolUses) {\r\n const def = options.tools[tu.name];\r\n if (!def) {\r\n toolResults.push({\r\n type: \"tool_result\",\r\n toolUseId: tu.id,\r\n content: `Tool \"${tu.name}\" not found.`,\r\n isError: true,\r\n });\r\n continue;\r\n }\r\n try {\r\n const output = await def.execute(tu.input as never);\r\n toolCalls.push({\r\n name: tu.name,\r\n input: tu.input as Record<string, unknown>,\r\n output,\r\n });\r\n const text = typeof output === \"string\" ? output : JSON.stringify(output);\r\n const truncated =\r\n def.maxOutputBytes !== undefined && text.length > def.maxOutputBytes\r\n ? `${text.slice(0, def.maxOutputBytes)}\\n[truncated]`\r\n : text;\r\n toolResults.push({\r\n type: \"tool_result\",\r\n toolUseId: tu.id,\r\n content: truncated,\r\n });\r\n } catch (toolErr) {\r\n toolResults.push({\r\n type: \"tool_result\",\r\n toolUseId: tu.id,\r\n content: toolErr instanceof Error ? toolErr.message : String(toolErr),\r\n isError: true,\r\n });\r\n }\r\n }\r\n conversation.push({ role: \"tool\", content: toolResults });\r\n }\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n\r\n return {\r\n text: finalText,\r\n messages: conversation,\r\n toolCalls,\r\n usage: totalUsage,\r\n cost: computeChatCost(totalUsage, pricing),\r\n modelId: lastModelId,\r\n providerAlias: alias,\r\n latencyMs: Date.now() - start,\r\n stepsTaken,\r\n terminationReason,\r\n };\r\n },\r\n\r\n async listModels(): Promise<ProviderModelInfo[]> {\r\n try {\r\n const out: ProviderModelInfo[] = [];\r\n // The @google/genai SDK paginates; iterate once. Pricing is NOT\r\n // included in the response — Gemini exposes catalog metadata but\r\n // not USD rates. checkPricingFreshness() will detect added/removed\r\n // models but cannot detect rate-only drift for Gemini.\r\n const pager = await ctx.client.models.list();\r\n for await (const m of pager) {\r\n const model = m as {\r\n name?: string;\r\n displayName?: string;\r\n inputTokenLimit?: number;\r\n outputTokenLimit?: number;\r\n };\r\n if (!model.name) continue;\r\n // `name` comes back as e.g. \"models/gemini-2.5-flash\". Strip prefix.\r\n const id = model.name.startsWith(\"models/\")\r\n ? model.name.slice(\"models/\".length)\r\n : model.name;\r\n out.push({\r\n id,\r\n ...(model.displayName ? { displayName: model.displayName } : {}),\r\n ...(model.inputTokenLimit ? { contextWindow: model.inputTokenLimit } : {}),\r\n ...(model.outputTokenLimit\r\n ? { metadata: { outputTokenLimit: model.outputTokenLimit } }\r\n : {}),\r\n });\r\n }\r\n return out;\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n },\r\n };\r\n}\r\n\r\n// ─── Helpers ─────────────────────────────────────────────────────────\r\n\r\ninterface GeminiUsageMetadata {\r\n promptTokenCount?: number;\r\n candidatesTokenCount?: number;\r\n totalTokenCount?: number;\r\n cachedContentTokenCount?: number;\r\n}\r\n\r\ninterface GeminiResponseShape {\r\n usageMetadata?: GeminiUsageMetadata;\r\n}\r\n\r\nfunction parseUsage(response: GeminiResponseShape): TokenUsage {\r\n const m = response.usageMetadata ?? {};\r\n const inputTokens = m.promptTokenCount ?? 0;\r\n const outputTokens = m.candidatesTokenCount ?? 0;\r\n const totalTokens = m.totalTokenCount ?? inputTokens + outputTokens;\r\n const usage: TokenUsage = { inputTokens, outputTokens, totalTokens };\r\n if (m.cachedContentTokenCount !== undefined && m.cachedContentTokenCount > 0) {\r\n usage.cacheReadTokens = m.cachedContentTokenCount;\r\n }\r\n return usage;\r\n}\r\n\r\n// ─── Schema-fallback warning ─────────────────────────────────────────\r\n\r\nconst warnedSchemaFallback = new Set<string>();\r\n\r\n/**\r\n * Emit a one-time `console.warn` per model+feature when generateStructured\r\n * falls back from native responseSchema to prompted JSON because the Zod\r\n * schema contains a feature Gemini's constrained-decoding doesn't accept.\r\n *\r\n * Not a runtime-learned constraint (we know pre-call from inspecting the\r\n * schema), so no click-to-file URL — just a developer-facing hint that\r\n * simplifying the schema would unlock native responseSchema enforcement.\r\n */\r\nfunction warnSchemaFallback(modelId: string, feature: string): void {\r\n const key = `${modelId}::${feature}`;\r\n if (warnedSchemaFallback.has(key)) return;\r\n warnedSchemaFallback.add(key);\r\n console.warn(\r\n `[@llm-ports/adapter-google] generateStructured: model \"${modelId}\" schema ` +\r\n `contains \"${feature}\" which Gemini's responseSchema does not support. ` +\r\n `Falling back to prompted-JSON + Zod validation (still correct; just ` +\r\n `slightly weaker constrained-decoding guarantee).`,\r\n );\r\n}\r\n\r\n/** Test-only: reset the per-process fallback warning state. */\r\nexport function _resetSchemaFallbackWarnings(): void {\r\n warnedSchemaFallback.clear();\r\n}\r\n\r\n// Re-export ContentBlock for the rare adapter user that wants to type-check\r\n// outside of @llm-ports/core. Keeps the import surface symmetric with the\r\n// other adapters.\r\nexport type { ContentBlock };\r\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -44,6 +44,8 @@ interface GoogleAdapter {
|
|
|
44
44
|
createLLMPort: (modelId: string, alias: string) => LLMPort;
|
|
45
45
|
}
|
|
46
46
|
declare function createGoogleAdapter(opts: GoogleAdapterOptions): GoogleAdapter;
|
|
47
|
+
/** Test-only: reset the per-process fallback warning state. */
|
|
48
|
+
declare function _resetSchemaFallbackWarnings(): void;
|
|
47
49
|
|
|
48
50
|
/**
|
|
49
51
|
* Bundled pricing for Google Gemini models.
|
|
@@ -60,4 +62,4 @@ declare function createGoogleAdapter(opts: GoogleAdapterOptions): GoogleAdapter;
|
|
|
60
62
|
declare const GEMINI_PRICING: Record<string, ModelPricing>;
|
|
61
63
|
declare function lookupGeminiPricing(modelId: string): ModelPricing | undefined;
|
|
62
64
|
|
|
63
|
-
export { GEMINI_PRICING, type GoogleAdapter, type GoogleAdapterOptions, createGoogleAdapter, lookupGeminiPricing };
|
|
65
|
+
export { GEMINI_PRICING, type GoogleAdapter, type GoogleAdapterOptions, _resetSchemaFallbackWarnings, createGoogleAdapter, lookupGeminiPricing };
|
package/dist/index.d.ts
CHANGED
|
@@ -44,6 +44,8 @@ interface GoogleAdapter {
|
|
|
44
44
|
createLLMPort: (modelId: string, alias: string) => LLMPort;
|
|
45
45
|
}
|
|
46
46
|
declare function createGoogleAdapter(opts: GoogleAdapterOptions): GoogleAdapter;
|
|
47
|
+
/** Test-only: reset the per-process fallback warning state. */
|
|
48
|
+
declare function _resetSchemaFallbackWarnings(): void;
|
|
47
49
|
|
|
48
50
|
/**
|
|
49
51
|
* Bundled pricing for Google Gemini models.
|
|
@@ -60,4 +62,4 @@ declare function createGoogleAdapter(opts: GoogleAdapterOptions): GoogleAdapter;
|
|
|
60
62
|
declare const GEMINI_PRICING: Record<string, ModelPricing>;
|
|
61
63
|
declare function lookupGeminiPricing(modelId: string): ModelPricing | undefined;
|
|
62
64
|
|
|
63
|
-
export { GEMINI_PRICING, type GoogleAdapter, type GoogleAdapterOptions, createGoogleAdapter, lookupGeminiPricing };
|
|
65
|
+
export { GEMINI_PRICING, type GoogleAdapter, type GoogleAdapterOptions, _resetSchemaFallbackWarnings, createGoogleAdapter, lookupGeminiPricing };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { GoogleGenAI } from '@google/genai';
|
|
2
|
-
import { throwIfAborted,
|
|
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';
|
|
3
4
|
|
|
4
5
|
// src/adapter.ts
|
|
5
6
|
var ADAPTER_NAME = "google";
|
|
@@ -95,6 +96,101 @@ function extractGeminiText(parts) {
|
|
|
95
96
|
if (!parts) return "";
|
|
96
97
|
return parts.filter((p) => "text" in p).map((p) => p.text).join("");
|
|
97
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
|
+
}
|
|
98
194
|
|
|
99
195
|
// src/pricing.ts
|
|
100
196
|
var GEMINI_PRICING = {
|
|
@@ -211,6 +307,13 @@ function createPort(ctx, modelId, alias) {
|
|
|
211
307
|
const start = Date.now();
|
|
212
308
|
let attempts = 0;
|
|
213
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;
|
|
214
317
|
let correctionPrompt = null;
|
|
215
318
|
let lastUsage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
216
319
|
let lastModelId = modelId;
|
|
@@ -218,7 +321,7 @@ function createPort(ctx, modelId, alias) {
|
|
|
218
321
|
attempts++;
|
|
219
322
|
const userText = correctionPrompt ? `${stringifyContentBlocks(options.prompt)}
|
|
220
323
|
|
|
221
|
-
${correctionPrompt}` : `${stringifyContentBlocks(options.prompt)}
|
|
324
|
+
${correctionPrompt}` : useNativeResponseSchema ? stringifyContentBlocks(options.prompt) : `${stringifyContentBlocks(options.prompt)}
|
|
222
325
|
|
|
223
326
|
Reply with a single JSON object only. No prose, no code fences.`;
|
|
224
327
|
try {
|
|
@@ -230,6 +333,7 @@ Reply with a single JSON object only. No prose, no code fences.`;
|
|
|
230
333
|
temperature: options.temperature ?? 0,
|
|
231
334
|
...options.maxOutputTokens !== void 0 ? { maxOutputTokens: options.maxOutputTokens } : {},
|
|
232
335
|
responseMimeType: "application/json",
|
|
336
|
+
...sanitizedSchema ? { responseSchema: sanitizedSchema } : {},
|
|
233
337
|
...options.signal ? { abortSignal: options.signal } : {}
|
|
234
338
|
}
|
|
235
339
|
});
|
|
@@ -339,36 +443,124 @@ Reply with a single JSON object only. Stream the JSON progressively.`
|
|
|
339
443
|
throwIfAborted(options.signal);
|
|
340
444
|
validateMessages(options.messages);
|
|
341
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;
|
|
342
456
|
try {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
contents
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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;
|
|
351
493
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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;
|
|
372
564
|
} catch (err) {
|
|
373
565
|
throw wrapProviderError(alias, err);
|
|
374
566
|
}
|
|
@@ -386,7 +578,19 @@ function parseUsage(response) {
|
|
|
386
578
|
}
|
|
387
579
|
return usage;
|
|
388
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
|
+
}
|
|
389
593
|
|
|
390
|
-
export { GEMINI_PRICING, createGoogleAdapter, lookupGeminiPricing };
|
|
594
|
+
export { GEMINI_PRICING, _resetSchemaFallbackWarnings, createGoogleAdapter, lookupGeminiPricing };
|
|
391
595
|
//# sourceMappingURL=index.mjs.map
|
|
392
596
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/content.ts","../src/pricing.ts","../src/adapter.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\r\n * Content translation between llm-ports ContentBlock[] and Gemini's\r\n * Content + Part shapes.\r\n *\r\n * Gemini's content model:\r\n * Content = { role: \"user\" | \"model\" | \"function\", parts: Part[] }\r\n * Part shapes (the ones we care about):\r\n * - { text: string }\r\n * - { inlineData: { mimeType: string, data: string (base64) } }\r\n * - { fileData: { mimeType: string, fileUri: string } }\r\n * - { functionCall: { name: string, args: object } }\r\n * - { functionResponse: { name: string, response: object } }\r\n *\r\n * Notes:\r\n * - System messages map to a top-level `systemInstruction` field on the\r\n * request, NOT a Content with role: \"system\". `toGeminiRequest` handles\r\n * this split.\r\n * - The assistant role is `\"model\"` in Gemini's vocabulary.\r\n * - Tool results are mapped to `functionResponse` parts.\r\n */\r\n\r\nimport {\r\n ContentBlockUnsupportedError,\r\n type ContentBlock,\r\n type LLMMessage,\r\n type MessageContent,\r\n} from \"@llm-ports/core\";\r\n\r\nconst ADAPTER_NAME = \"google\";\r\n\r\n// ─── Outgoing: ContentBlock[] → Gemini Part[] ────────────────────────\r\n\r\ninterface GeminiTextPart {\r\n text: string;\r\n}\r\ninterface GeminiInlineDataPart {\r\n inlineData: { mimeType: string; data: string };\r\n}\r\ninterface GeminiFileDataPart {\r\n fileData: { mimeType: string; fileUri: string };\r\n}\r\ninterface GeminiFunctionCallPart {\r\n functionCall: { name: string; args: Record<string, unknown> };\r\n}\r\ninterface GeminiFunctionResponsePart {\r\n functionResponse: { name: string; response: Record<string, unknown> };\r\n}\r\nexport type GeminiPart =\r\n | GeminiTextPart\r\n | GeminiInlineDataPart\r\n | GeminiFileDataPart\r\n | GeminiFunctionCallPart\r\n | GeminiFunctionResponsePart;\r\n\r\nexport interface GeminiContent {\r\n role: \"user\" | \"model\" | \"function\";\r\n parts: GeminiPart[];\r\n}\r\n\r\n/** Translate a single ContentBlock to one or more GeminiParts. */\r\nfunction toGeminiParts(block: ContentBlock): GeminiPart[] {\r\n switch (block.type) {\r\n case \"text\":\r\n return [{ text: block.text }];\r\n case \"image\": {\r\n if (block.source.kind === \"base64\") {\r\n return [\r\n {\r\n inlineData: {\r\n mimeType: block.source.mediaType,\r\n data: block.source.data,\r\n },\r\n },\r\n ];\r\n }\r\n // URL form\r\n return [\r\n {\r\n fileData: {\r\n // Gemini infers mimeType from URL extension when not provided.\r\n // We pass image/jpeg as a sane default; users wanting tighter\r\n // control should pass base64 with explicit mediaType.\r\n mimeType: \"image/jpeg\",\r\n fileUri: block.source.url,\r\n },\r\n },\r\n ];\r\n }\r\n case \"audio\": {\r\n if (block.source.kind === \"base64\") {\r\n return [\r\n {\r\n inlineData: {\r\n mimeType: block.source.mediaType,\r\n data: block.source.data,\r\n },\r\n },\r\n ];\r\n }\r\n throw new ContentBlockUnsupportedError(ADAPTER_NAME, \"audio (url; Gemini accepts base64 or fileData with fileUri)\");\r\n }\r\n case \"tool_use\": {\r\n // Gemini's tool-call shape has args as a plain object; we pass the input\r\n // through if it's already an object, else wrap.\r\n const args =\r\n block.input !== null && typeof block.input === \"object\"\r\n ? (block.input as Record<string, unknown>)\r\n : { value: block.input };\r\n return [{ functionCall: { name: block.name, args } }];\r\n }\r\n case \"tool_result\": {\r\n // Gemini's functionResponse expects a `response` object. If the\r\n // ContentBlock.tool_result.content is a string, wrap it.\r\n const response: Record<string, unknown> =\r\n typeof block.content === \"string\"\r\n ? { result: block.content }\r\n : { result: extractTextOnly(block.content) };\r\n return [\r\n {\r\n functionResponse: {\r\n // Gemini's API requires the tool name; we use toolUseId since\r\n // llm-ports' ToolResultBlock doesn't carry the name. Adapters\r\n // that need the name can plumb it through a separate channel.\r\n name: block.toolUseId,\r\n response,\r\n },\r\n },\r\n ];\r\n }\r\n }\r\n}\r\n\r\nfunction extractTextOnly(blocks: ContentBlock[]): string {\r\n return blocks\r\n .filter((b): b is Extract<ContentBlock, { type: \"text\" }> => b.type === \"text\")\r\n .map((b) => b.text)\r\n .join(\"\\n\");\r\n}\r\n\r\n/** Translate a MessageContent to Gemini Parts. */\r\nexport function toGeminiParts2(content: MessageContent): GeminiPart[] {\r\n if (typeof content === \"string\") {\r\n return [{ text: content }];\r\n }\r\n return content.flatMap(toGeminiParts);\r\n}\r\n\r\n/**\r\n * Translate an array of LLMMessages into:\r\n * - `systemInstruction`: the concatenated system messages (Gemini puts\r\n * these at the top level of the request, not in `contents`).\r\n * - `contents`: the user + assistant + tool messages, with roles mapped.\r\n */\r\nexport function toGeminiRequest(messages: LLMMessage[]): {\r\n systemInstruction?: string;\r\n contents: GeminiContent[];\r\n} {\r\n let systemInstruction: string | undefined;\r\n const contents: GeminiContent[] = [];\r\n for (const msg of messages) {\r\n if (msg.role === \"system\") {\r\n const text =\r\n typeof msg.content === \"string\" ? msg.content : extractTextOnly(msg.content);\r\n systemInstruction = systemInstruction === undefined ? text : `${systemInstruction}\\n\\n${text}`;\r\n continue;\r\n }\r\n const role: GeminiContent[\"role\"] =\r\n msg.role === \"tool\" ? \"function\" : msg.role === \"assistant\" ? \"model\" : \"user\";\r\n contents.push({\r\n role,\r\n parts: toGeminiParts2(msg.content),\r\n });\r\n }\r\n return systemInstruction !== undefined\r\n ? { systemInstruction, contents }\r\n : { contents };\r\n}\r\n\r\n// ─── Incoming: Gemini response → ContentBlock[] ──────────────────────\r\n\r\ninterface GeminiResponseCandidate {\r\n content?: {\r\n role?: string;\r\n parts?: GeminiPart[];\r\n };\r\n finishReason?: string;\r\n}\r\n\r\n/**\r\n * Extract the assistant text from a Gemini response. Used by generateText\r\n * and by the structured-output path before JSON parsing.\r\n */\r\nexport function extractGeminiText(parts: GeminiPart[] | undefined): string {\r\n if (!parts) return \"\";\r\n return parts\r\n .filter((p): p is GeminiTextPart => \"text\" in p)\r\n .map((p) => p.text)\r\n .join(\"\");\r\n}\r\n\r\n/**\r\n * Translate a Gemini response candidate's parts back into ContentBlock[].\r\n * Used by runAgent to reconstruct the model's tool_use blocks.\r\n */\r\nexport function fromGeminiCandidate(candidate: GeminiResponseCandidate): ContentBlock[] {\r\n const out: ContentBlock[] = [];\r\n const parts = candidate.content?.parts ?? [];\r\n for (const part of parts) {\r\n if (\"text\" in part && part.text.length > 0) {\r\n out.push({ type: \"text\", text: part.text });\r\n } else if (\"functionCall\" in part) {\r\n out.push({\r\n type: \"tool_use\",\r\n id: `gemini-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\r\n name: part.functionCall.name,\r\n input: part.functionCall.args,\r\n });\r\n } else if (\"inlineData\" in part) {\r\n // Inline (base64) image in assistant response. Decode if media type\r\n // is one we support, else drop (consistent with adapter-openai's\r\n // unknown-media-type behavior).\r\n const mt = part.inlineData.mimeType;\r\n if (\r\n mt === \"image/jpeg\" ||\r\n mt === \"image/png\" ||\r\n mt === \"image/gif\" ||\r\n mt === \"image/webp\"\r\n ) {\r\n out.push({\r\n type: \"image\",\r\n source: { kind: \"base64\", mediaType: mt, data: part.inlineData.data },\r\n });\r\n }\r\n }\r\n // fileData / functionResponse in assistant responses: not currently observed.\r\n }\r\n return out;\r\n}\r\n","/**\r\n * Bundled pricing for Google Gemini models.\r\n *\r\n * Source: https://ai.google.dev/gemini-api/docs/pricing (verified 2026-05).\r\n * Override per model via `pricingOverrides` on the adapter options.\r\n *\r\n * Gemini pricing has separate tiers for prompts under 200k tokens vs over\r\n * 200k tokens (the \"long-context premium\"). The bundled values are the\r\n * UNDER-200k-token rates, which dominate typical usage. For long-context\r\n * workloads, supply `pricingOverrides` with the over-200k rates.\r\n */\r\n\r\nimport type { ModelPricing } from \"@llm-ports/core\";\r\n\r\nexport const GEMINI_PRICING: Record<string, ModelPricing> = {\r\n // Gemini 2.5 family (2026-05 GA pricing)\r\n \"gemini-2.5-pro\": {\r\n inputPer1M: 1.25,\r\n outputPer1M: 5.0,\r\n cacheReadPer1M: 0.3125,\r\n },\r\n \"gemini-2.5-flash\": {\r\n inputPer1M: 0.075,\r\n outputPer1M: 0.3,\r\n cacheReadPer1M: 0.01875,\r\n },\r\n \"gemini-2.5-flash-lite\": {\r\n inputPer1M: 0.0375,\r\n outputPer1M: 0.15,\r\n cacheReadPer1M: 0.009375,\r\n },\r\n // Gemini 2.0 family (still available)\r\n \"gemini-2.0-flash\": {\r\n inputPer1M: 0.1,\r\n outputPer1M: 0.4,\r\n cacheReadPer1M: 0.025,\r\n },\r\n \"gemini-2.0-flash-lite\": {\r\n inputPer1M: 0.075,\r\n outputPer1M: 0.3,\r\n },\r\n};\r\n\r\nexport function lookupGeminiPricing(modelId: string): ModelPricing | undefined {\r\n return GEMINI_PRICING[modelId];\r\n}\r\n","/**\r\n * Google Gemini adapter for llm-ports.\r\n *\r\n * Wraps @google/genai (the unified Gemini + Vertex SDK as of 2026) to\r\n * implement LLMPort. Provides:\r\n *\r\n * - Native multimodal: image content blocks pass through as inlineData\r\n * (base64) or fileData (URL). NO degradation, unlike OpenAI-compat\r\n * baseURL where image_url.detail is silently ignored.\r\n * - Native streaming via generateContentStream\r\n * - Structured output via prompted-JSON + Zod retry-with-feedback\r\n * + alpha.5 programmatic repair. Native Gemini responseSchema lands\r\n * in v0.2.\r\n * - Image-block boundary validation (size + URL scheme) — same shape\r\n * as adapter-anthropic and adapter-openai (alpha.5).\r\n *\r\n * Out of scope for v0.1 alpha:\r\n * - Embeddings (Gemini's embedding API is separate; lands in v0.2)\r\n * - Multi-turn runAgent through Gemini's native automatic tool calling\r\n * (v0.1 ships a single-turn shim consistent with adapter-vercel)\r\n * - Caching API (Gemini supports explicit context caching; lands in v0.2)\r\n * - Code execution tool (Gemini's built-in code interpreter; lands in v0.2)\r\n */\r\n\r\nimport { GoogleGenAI } from \"@google/genai\";\r\nimport {\r\n attemptValidationRepair,\r\n computeChatCost,\r\n extractJSON,\r\n failValidation,\r\n stringifyContentBlocks,\r\n throwIfAborted,\r\n tryParsePartialJSON,\r\n validateImageBlocks,\r\n wrapProviderError,\r\n type AgentResult,\r\n type ContentBlock,\r\n type GenerateStructuredOptions,\r\n type GenerateStructuredResult,\r\n type GenerateTextOptions,\r\n type GenerateTextResult,\r\n type LLMPort,\r\n type MessageContent,\r\n type ModelPricing,\r\n type RunAgentOptions,\r\n type StreamStructuredOptions,\r\n type StreamTextOptions,\r\n type TokenUsage,\r\n type ValidationStrategy,\r\n} from \"@llm-ports/core\";\r\nimport {\r\n extractGeminiText,\r\n toGeminiParts2,\r\n toGeminiRequest,\r\n type GeminiPart,\r\n} from \"./content.js\";\r\nimport { GEMINI_PRICING } from \"./pricing.js\";\r\n\r\n// ─── Adapter options ─────────────────────────────────────────────────\r\n\r\nexport interface GoogleAdapterOptions {\r\n /** Google AI API key (https://aistudio.google.com/apikey). */\r\n apiKey: string;\r\n /** Override Gemini pricing for any model id. Falls back to the bundled table. */\r\n pricingOverrides?: Record<string, ModelPricing>;\r\n /** Default validation strategy if the registry doesn't override per-call. */\r\n validationStrategy?: ValidationStrategy;\r\n /**\r\n * Maximum bytes per base64 image. Defaults to 20MB (Gemini accepts up to\r\n * 20MB inlined; fileData URLs are unconstrained but provider-fetched).\r\n * Set to 0 or a negative number to disable size validation.\r\n */\r\n imageSizeLimitBytes?: number;\r\n}\r\n\r\n// ─── Internal context ────────────────────────────────────────────────\r\n\r\ninterface AdapterContext {\r\n client: GoogleGenAI;\r\n validationStrategy: ValidationStrategy;\r\n pricingOverrides: Record<string, ModelPricing>;\r\n imageSizeLimitBytes: number;\r\n}\r\n\r\nfunction pricingFor(ctx: AdapterContext, modelId: string): ModelPricing {\r\n const pricing = ctx.pricingOverrides[modelId] ?? GEMINI_PRICING[modelId];\r\n if (!pricing) {\r\n throw new Error(\r\n `No pricing entry for Google Gemini model \"${modelId}\". Provide pricingOverrides or update src/pricing.ts.`,\r\n );\r\n }\r\n return pricing;\r\n}\r\n\r\n// ─── Public factory ──────────────────────────────────────────────────\r\n\r\nexport interface GoogleAdapter {\r\n name: \"google\";\r\n pricing: Record<string, ModelPricing>;\r\n createLLMPort: (modelId: string, alias: string) => LLMPort;\r\n}\r\n\r\nexport function createGoogleAdapter(opts: GoogleAdapterOptions): GoogleAdapter {\r\n const mergedPricing: Record<string, ModelPricing> = {\r\n ...GEMINI_PRICING,\r\n ...(opts.pricingOverrides ?? {}),\r\n };\r\n const ctx: AdapterContext = {\r\n client: new GoogleGenAI({ apiKey: opts.apiKey }),\r\n validationStrategy: opts.validationStrategy ?? {\r\n kind: \"retry-with-feedback\",\r\n maxAttempts: 2,\r\n includeOriginalError: true,\r\n },\r\n pricingOverrides: opts.pricingOverrides ?? {},\r\n imageSizeLimitBytes: opts.imageSizeLimitBytes ?? 20 * 1024 * 1024,\r\n };\r\n return {\r\n name: \"google\",\r\n pricing: mergedPricing,\r\n createLLMPort: (modelId, alias) => createPort(ctx, modelId, alias),\r\n };\r\n}\r\n\r\n// ─── Port implementation ─────────────────────────────────────────────\r\n\r\nfunction createPort(ctx: AdapterContext, modelId: string, alias: string): LLMPort {\r\n const pricing = pricingFor(ctx, modelId);\r\n\r\n // Image-block validation closure: throws ImageTooLargeError or\r\n // InvalidImageUrlError before the SDK call.\r\n const validateContent = (content: MessageContent): void => {\r\n if (Array.isArray(content)) {\r\n validateImageBlocks(content, {\r\n alias,\r\n ...(ctx.imageSizeLimitBytes > 0 ? { limitBytes: ctx.imageSizeLimitBytes } : {}),\r\n });\r\n }\r\n };\r\n const validateMessages = (messages: ReadonlyArray<{ content: MessageContent }>): void => {\r\n for (const msg of messages) validateContent(msg.content);\r\n };\r\n\r\n return {\r\n async generateText(options: GenerateTextOptions): Promise<GenerateTextResult> {\r\n throwIfAborted(options.signal);\r\n validateContent(options.prompt);\r\n const start = Date.now();\r\n try {\r\n const parts = toGeminiParts2(options.prompt);\r\n const response = await ctx.client.models.generateContent({\r\n model: modelId,\r\n contents: [{ role: \"user\", parts }],\r\n config: {\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : {}),\r\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\r\n ...(options.maxOutputTokens !== undefined\r\n ? { maxOutputTokens: options.maxOutputTokens }\r\n : {}),\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n const candidate = response.candidates?.[0];\r\n const text = extractGeminiText(candidate?.content?.parts as GeminiPart[] | undefined);\r\n const usage = parseUsage(response);\r\n return {\r\n text,\r\n usage,\r\n cost: computeChatCost(usage, pricing),\r\n modelId: response.modelVersion ?? modelId,\r\n providerAlias: alias,\r\n latencyMs: Date.now() - start,\r\n };\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n },\r\n\r\n async generateStructured<T>(\r\n options: GenerateStructuredOptions<T>,\r\n ): Promise<GenerateStructuredResult<T>> {\r\n throwIfAborted(options.signal);\r\n validateContent(options.prompt);\r\n const start = Date.now();\r\n let attempts = 0;\r\n const maxAttempts =\r\n ctx.validationStrategy.kind === \"retry-with-feedback\"\r\n ? ctx.validationStrategy.maxAttempts\r\n : 1;\r\n\r\n let correctionPrompt: string | null = null;\r\n let lastUsage: TokenUsage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };\r\n let lastModelId = modelId;\r\n\r\n while (attempts < maxAttempts) {\r\n attempts++;\r\n const userText = correctionPrompt\r\n ? `${stringifyContentBlocks(options.prompt)}\\n\\n${correctionPrompt}`\r\n : `${stringifyContentBlocks(options.prompt)}\\n\\nReply with a single JSON object only. No prose, no code fences.`;\r\n\r\n try {\r\n const response = await ctx.client.models.generateContent({\r\n model: modelId,\r\n contents: [{ role: \"user\", parts: [{ text: userText }] }],\r\n config: {\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : {}),\r\n temperature: options.temperature ?? 0,\r\n ...(options.maxOutputTokens !== undefined\r\n ? { maxOutputTokens: options.maxOutputTokens }\r\n : {}),\r\n responseMimeType: \"application/json\",\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n const candidate = response.candidates?.[0];\r\n const raw = extractGeminiText(candidate?.content?.parts as GeminiPart[] | undefined);\r\n lastUsage = parseUsage(response);\r\n lastModelId = response.modelVersion ?? modelId;\r\n\r\n const decoded = extractJSON(raw);\r\n let parsed = options.schema.safeParse(decoded);\r\n if (!parsed.success) {\r\n const repaired = attemptValidationRepair(decoded, parsed.error);\r\n const reparsed = options.schema.safeParse(repaired);\r\n if (reparsed.success) parsed = reparsed;\r\n }\r\n if (parsed.success) {\r\n return {\r\n data: parsed.data as T,\r\n usage: lastUsage,\r\n cost: computeChatCost(lastUsage, pricing),\r\n modelId: lastModelId,\r\n providerAlias: alias,\r\n latencyMs: Date.now() - start,\r\n validationAttempts: attempts,\r\n };\r\n }\r\n if (\r\n ctx.validationStrategy.kind === \"retry-with-feedback\" &&\r\n attempts < maxAttempts\r\n ) {\r\n const issues = parsed.error.issues\r\n .map((i) => `- ${i.path.join(\".\") || \"<root>\"}: ${i.message}`)\r\n .join(\"\\n\");\r\n correctionPrompt = `Your previous response failed validation:\\n${issues}\\n\\nReply with a single corrected JSON object only.`;\r\n continue;\r\n }\r\n failValidation(parsed.error.issues, attempts);\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n }\r\n throw new Error(\"generateStructured exhausted attempts\");\r\n },\r\n\r\n async *streamText(options: StreamTextOptions): AsyncIterable<string> {\r\n throwIfAborted(options.signal);\r\n validateContent(options.prompt);\r\n try {\r\n const parts = toGeminiParts2(options.prompt);\r\n const stream = await ctx.client.models.generateContentStream({\r\n model: modelId,\r\n contents: [{ role: \"user\", parts }],\r\n config: {\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : {}),\r\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\r\n ...(options.maxOutputTokens !== undefined\r\n ? { maxOutputTokens: options.maxOutputTokens }\r\n : {}),\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n for await (const chunk of stream) {\r\n const text = extractGeminiText(\r\n chunk.candidates?.[0]?.content?.parts as GeminiPart[] | undefined,\r\n );\r\n if (text.length > 0) yield text;\r\n }\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n },\r\n\r\n async *streamStructured<T>(options: StreamStructuredOptions<T>): AsyncIterable<Partial<T>> {\r\n throwIfAborted(options.signal);\r\n validateContent(options.prompt);\r\n try {\r\n const stream = await ctx.client.models.generateContentStream({\r\n model: modelId,\r\n contents: [\r\n {\r\n role: \"user\",\r\n parts: [\r\n {\r\n text: `${stringifyContentBlocks(options.prompt)}\\n\\nReply with a single JSON object only. Stream the JSON progressively.`,\r\n },\r\n ],\r\n },\r\n ],\r\n config: {\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : {}),\r\n temperature: options.temperature ?? 0,\r\n ...(options.maxOutputTokens !== undefined\r\n ? { maxOutputTokens: options.maxOutputTokens }\r\n : {}),\r\n responseMimeType: \"application/json\",\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n let buffer = \"\";\r\n for await (const chunk of stream) {\r\n const text = extractGeminiText(\r\n chunk.candidates?.[0]?.content?.parts as GeminiPart[] | undefined,\r\n );\r\n if (text.length === 0) continue;\r\n buffer += text;\r\n const partial = tryParsePartialJSON(buffer);\r\n if (partial !== null) yield partial as Partial<T>;\r\n }\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n },\r\n\r\n async runAgent(options: RunAgentOptions): Promise<AgentResult> {\r\n throwIfAborted(options.signal);\r\n validateMessages(options.messages);\r\n // v0.1: single-turn agent loop. Gemini's native automatic-function-calling\r\n // multi-turn runAgent ships in v0.2 (matches adapter-vercel's v0.1 shape).\r\n const start = Date.now();\r\n try {\r\n const { systemInstruction, contents } = toGeminiRequest(options.messages);\r\n const response = await ctx.client.models.generateContent({\r\n model: modelId,\r\n contents,\r\n config: {\r\n ...(systemInstruction !== undefined ? { systemInstruction } : {}),\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : {}),\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n const candidate = response.candidates?.[0];\r\n const text = extractGeminiText(candidate?.content?.parts as GeminiPart[] | undefined);\r\n const usage = parseUsage(response);\r\n const toolCalls: AgentResult[\"toolCalls\"] = [];\r\n // v0.1 stub: we surface no tool calls. Real tool-use is v0.2 scope.\r\n return {\r\n text,\r\n messages: [\r\n ...options.messages,\r\n { role: \"assistant\" as const, content: text },\r\n ],\r\n usage,\r\n cost: computeChatCost(usage, pricing),\r\n modelId: response.modelVersion ?? modelId,\r\n providerAlias: alias,\r\n latencyMs: Date.now() - start,\r\n toolCalls,\r\n stepsTaken: 1,\r\n terminationReason: \"completed\",\r\n };\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n },\r\n };\r\n}\r\n\r\n// ─── Helpers ─────────────────────────────────────────────────────────\r\n\r\ninterface GeminiUsageMetadata {\r\n promptTokenCount?: number;\r\n candidatesTokenCount?: number;\r\n totalTokenCount?: number;\r\n cachedContentTokenCount?: number;\r\n}\r\n\r\ninterface GeminiResponseShape {\r\n usageMetadata?: GeminiUsageMetadata;\r\n}\r\n\r\nfunction parseUsage(response: GeminiResponseShape): TokenUsage {\r\n const m = response.usageMetadata ?? {};\r\n const inputTokens = m.promptTokenCount ?? 0;\r\n const outputTokens = m.candidatesTokenCount ?? 0;\r\n const totalTokens = m.totalTokenCount ?? inputTokens + outputTokens;\r\n const usage: TokenUsage = { inputTokens, outputTokens, totalTokens };\r\n if (m.cachedContentTokenCount !== undefined && m.cachedContentTokenCount > 0) {\r\n usage.cacheReadTokens = m.cachedContentTokenCount;\r\n }\r\n return usage;\r\n}\r\n\r\n// Re-export ContentBlock for the rare adapter user that wants to type-check\r\n// outside of @llm-ports/core. Keeps the import surface symmetric with the\r\n// other adapters.\r\nexport type { ContentBlock };\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/content.ts","../src/pricing.ts","../src/adapter.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\r\n * Content translation between llm-ports ContentBlock[] and Gemini's\r\n * Content + Part shapes.\r\n *\r\n * Gemini's content model:\r\n * Content = { role: \"user\" | \"model\" | \"function\", parts: Part[] }\r\n * Part shapes (the ones we care about):\r\n * - { text: string }\r\n * - { inlineData: { mimeType: string, data: string (base64) } }\r\n * - { fileData: { mimeType: string, fileUri: string } }\r\n * - { functionCall: { name: string, args: object } }\r\n * - { functionResponse: { name: string, response: object } }\r\n *\r\n * Notes:\r\n * - System messages map to a top-level `systemInstruction` field on the\r\n * request, NOT a Content with role: \"system\". `toGeminiRequest` handles\r\n * this split.\r\n * - The assistant role is `\"model\"` in Gemini's vocabulary.\r\n * - Tool results are mapped to `functionResponse` parts.\r\n */\r\n\r\nimport { zodToJsonSchema } from \"zod-to-json-schema\";\r\nimport {\r\n ContentBlockUnsupportedError,\r\n type ContentBlock,\r\n type LLMMessage,\r\n type MessageContent,\r\n type ToolDefinition,\r\n} from \"@llm-ports/core\";\r\n\r\nconst ADAPTER_NAME = \"google\";\r\n\r\n// ─── Outgoing: ContentBlock[] → Gemini Part[] ────────────────────────\r\n\r\ninterface GeminiTextPart {\r\n text: string;\r\n}\r\ninterface GeminiInlineDataPart {\r\n inlineData: { mimeType: string; data: string };\r\n}\r\ninterface GeminiFileDataPart {\r\n fileData: { mimeType: string; fileUri: string };\r\n}\r\ninterface GeminiFunctionCallPart {\r\n functionCall: { name: string; args: Record<string, unknown> };\r\n}\r\ninterface GeminiFunctionResponsePart {\r\n functionResponse: { name: string; response: Record<string, unknown> };\r\n}\r\nexport type GeminiPart =\r\n | GeminiTextPart\r\n | GeminiInlineDataPart\r\n | GeminiFileDataPart\r\n | GeminiFunctionCallPart\r\n | GeminiFunctionResponsePart;\r\n\r\nexport interface GeminiContent {\r\n role: \"user\" | \"model\" | \"function\";\r\n parts: GeminiPart[];\r\n}\r\n\r\n/** Translate a single ContentBlock to one or more GeminiParts. */\r\nfunction toGeminiParts(block: ContentBlock): GeminiPart[] {\r\n switch (block.type) {\r\n case \"text\":\r\n return [{ text: block.text }];\r\n case \"image\": {\r\n if (block.source.kind === \"base64\") {\r\n return [\r\n {\r\n inlineData: {\r\n mimeType: block.source.mediaType,\r\n data: block.source.data,\r\n },\r\n },\r\n ];\r\n }\r\n // URL form\r\n return [\r\n {\r\n fileData: {\r\n // Gemini infers mimeType from URL extension when not provided.\r\n // We pass image/jpeg as a sane default; users wanting tighter\r\n // control should pass base64 with explicit mediaType.\r\n mimeType: \"image/jpeg\",\r\n fileUri: block.source.url,\r\n },\r\n },\r\n ];\r\n }\r\n case \"audio\": {\r\n if (block.source.kind === \"base64\") {\r\n return [\r\n {\r\n inlineData: {\r\n mimeType: block.source.mediaType,\r\n data: block.source.data,\r\n },\r\n },\r\n ];\r\n }\r\n throw new ContentBlockUnsupportedError(ADAPTER_NAME, \"audio (url; Gemini accepts base64 or fileData with fileUri)\");\r\n }\r\n case \"tool_use\": {\r\n // Gemini's tool-call shape has args as a plain object; we pass the input\r\n // through if it's already an object, else wrap.\r\n const args =\r\n block.input !== null && typeof block.input === \"object\"\r\n ? (block.input as Record<string, unknown>)\r\n : { value: block.input };\r\n return [{ functionCall: { name: block.name, args } }];\r\n }\r\n case \"tool_result\": {\r\n // Gemini's functionResponse expects a `response` object. If the\r\n // ContentBlock.tool_result.content is a string, wrap it.\r\n const response: Record<string, unknown> =\r\n typeof block.content === \"string\"\r\n ? { result: block.content }\r\n : { result: extractTextOnly(block.content) };\r\n return [\r\n {\r\n functionResponse: {\r\n // Gemini's API requires the tool name; we use toolUseId since\r\n // llm-ports' ToolResultBlock doesn't carry the name. Adapters\r\n // that need the name can plumb it through a separate channel.\r\n name: block.toolUseId,\r\n response,\r\n },\r\n },\r\n ];\r\n }\r\n }\r\n}\r\n\r\nfunction extractTextOnly(blocks: ContentBlock[]): string {\r\n return blocks\r\n .filter((b): b is Extract<ContentBlock, { type: \"text\" }> => b.type === \"text\")\r\n .map((b) => b.text)\r\n .join(\"\\n\");\r\n}\r\n\r\n/** Translate a MessageContent to Gemini Parts. */\r\nexport function toGeminiParts2(content: MessageContent): GeminiPart[] {\r\n if (typeof content === \"string\") {\r\n return [{ text: content }];\r\n }\r\n return content.flatMap(toGeminiParts);\r\n}\r\n\r\n/**\r\n * Translate an array of LLMMessages into:\r\n * - `systemInstruction`: the concatenated system messages (Gemini puts\r\n * these at the top level of the request, not in `contents`).\r\n * - `contents`: the user + assistant + tool messages, with roles mapped.\r\n */\r\nexport function toGeminiRequest(messages: LLMMessage[]): {\r\n systemInstruction?: string;\r\n contents: GeminiContent[];\r\n} {\r\n let systemInstruction: string | undefined;\r\n const contents: GeminiContent[] = [];\r\n for (const msg of messages) {\r\n if (msg.role === \"system\") {\r\n const text =\r\n typeof msg.content === \"string\" ? msg.content : extractTextOnly(msg.content);\r\n systemInstruction = systemInstruction === undefined ? text : `${systemInstruction}\\n\\n${text}`;\r\n continue;\r\n }\r\n const role: GeminiContent[\"role\"] =\r\n msg.role === \"tool\" ? \"function\" : msg.role === \"assistant\" ? \"model\" : \"user\";\r\n contents.push({\r\n role,\r\n parts: toGeminiParts2(msg.content),\r\n });\r\n }\r\n return systemInstruction !== undefined\r\n ? { systemInstruction, contents }\r\n : { contents };\r\n}\r\n\r\n// ─── Incoming: Gemini response → ContentBlock[] ──────────────────────\r\n\r\ninterface GeminiResponseCandidate {\r\n content?: {\r\n role?: string;\r\n parts?: GeminiPart[];\r\n };\r\n finishReason?: string;\r\n}\r\n\r\n/**\r\n * Extract the assistant text from a Gemini response. Used by generateText\r\n * and by the structured-output path before JSON parsing.\r\n */\r\nexport function extractGeminiText(parts: GeminiPart[] | undefined): string {\r\n if (!parts) return \"\";\r\n return parts\r\n .filter((p): p is GeminiTextPart => \"text\" in p)\r\n .map((p) => p.text)\r\n .join(\"\");\r\n}\r\n\r\n/**\r\n * Translate a Gemini response candidate's parts back into ContentBlock[].\r\n * Used by runAgent to reconstruct the model's tool_use blocks.\r\n */\r\nexport function fromGeminiCandidate(candidate: GeminiResponseCandidate): ContentBlock[] {\r\n const out: ContentBlock[] = [];\r\n const parts = candidate.content?.parts ?? [];\r\n for (const part of parts) {\r\n if (\"text\" in part && part.text.length > 0) {\r\n out.push({ type: \"text\", text: part.text });\r\n } else if (\"functionCall\" in part) {\r\n // Gemini's API doesn't return a tool-call ID. We synthesize one as\r\n // the function name so the tool_result round-trip (which maps\r\n // toolUseId → Gemini functionResponse.name) emits the correct name.\r\n out.push({\r\n type: \"tool_use\",\r\n id: part.functionCall.name,\r\n name: part.functionCall.name,\r\n input: part.functionCall.args,\r\n });\r\n } else if (\"inlineData\" in part) {\r\n // Inline (base64) image in assistant response. Decode if media type\r\n // is one we support, else drop (consistent with adapter-openai's\r\n // unknown-media-type behavior).\r\n const mt = part.inlineData.mimeType;\r\n if (\r\n mt === \"image/jpeg\" ||\r\n mt === \"image/png\" ||\r\n mt === \"image/gif\" ||\r\n mt === \"image/webp\"\r\n ) {\r\n out.push({\r\n type: \"image\",\r\n source: { kind: \"base64\", mediaType: mt, data: part.inlineData.data },\r\n });\r\n }\r\n }\r\n // fileData / functionResponse in assistant responses: not currently observed.\r\n }\r\n return out;\r\n}\r\n\r\n// ─── Tools: ToolDefinition[] → Gemini Tool[] ─────────────────────────\r\n\r\nexport interface GeminiFunctionDeclaration {\r\n name: string;\r\n description: string;\r\n parameters?: Record<string, unknown>;\r\n}\r\n\r\nexport interface GeminiTool {\r\n functionDeclarations: GeminiFunctionDeclaration[];\r\n}\r\n\r\n/**\r\n * Translate llm-ports tool definitions to Gemini's Tool[] shape.\r\n *\r\n * Gemini's FunctionDeclaration.parameters accepts an OpenAPI 3.0 subset of\r\n * JSON Schema. zodToJsonSchema with target \"openApi3\" emits the closest\r\n * dialect; we then strip $schema and definitions so the parameters object\r\n * is what Gemini expects.\r\n */\r\nexport function toGeminiTools(tools: Record<string, ToolDefinition>): GeminiTool[] {\r\n const declarations: GeminiFunctionDeclaration[] = Object.entries(tools).map(\r\n ([name, def]) => ({\r\n name,\r\n description: def.description,\r\n parameters: zodToGeminiSchema(def.inputSchema),\r\n }),\r\n );\r\n return [{ functionDeclarations: declarations }];\r\n}\r\n\r\n/**\r\n * Convert a Zod schema to a JSON Schema shape Gemini accepts (OpenAPI 3.0\r\n * subset). Strips fields Gemini rejects (`$schema`, `definitions`, `$defs`).\r\n *\r\n * Falls back to `{ type: \"object\", properties: {} }` if conversion fails so\r\n * a malformed tool definition doesn't crash the agent loop.\r\n */\r\nexport function zodToGeminiSchema(schema: unknown): Record<string, unknown> {\r\n try {\r\n const json = zodToJsonSchema(schema as never, {\r\n target: \"openApi3\",\r\n $refStrategy: \"none\",\r\n }) as Record<string, unknown>;\r\n return sanitizeGeminiSchema(json);\r\n } catch {\r\n return { type: \"object\", properties: {} };\r\n }\r\n}\r\n\r\n/**\r\n * Strip JSON Schema fields Gemini rejects. Applied recursively across\r\n * nested `properties` and `items`. Public so generateStructured can reuse\r\n * it for native responseSchema (alpha.9 §2.1).\r\n */\r\nexport function sanitizeGeminiSchema(input: unknown): Record<string, unknown> {\r\n if (!input || typeof input !== \"object\" || Array.isArray(input)) {\r\n return { type: \"object\", properties: {} };\r\n }\r\n const cleaned: Record<string, unknown> = {};\r\n for (const [key, value] of Object.entries(input as Record<string, unknown>)) {\r\n if (key === \"$schema\" || key === \"definitions\" || key === \"$defs\" || key === \"$ref\") {\r\n continue;\r\n }\r\n if (key === \"properties\" && value && typeof value === \"object\") {\r\n const props: Record<string, unknown> = {};\r\n for (const [propName, propSchema] of Object.entries(value as Record<string, unknown>)) {\r\n props[propName] = sanitizeGeminiSchema(propSchema);\r\n }\r\n cleaned[key] = props;\r\n continue;\r\n }\r\n if (key === \"items\" && value && typeof value === \"object\" && !Array.isArray(value)) {\r\n cleaned[key] = sanitizeGeminiSchema(value);\r\n continue;\r\n }\r\n cleaned[key] = value;\r\n }\r\n return cleaned;\r\n}\r\n\r\n/**\r\n * Detect JSON Schema features Gemini's responseSchema constrained-decoding\r\n * does not support. Returns the first unsupported feature found, or null\r\n * if the schema is clean. Used by generateStructured to decide between\r\n * native responseSchema and the prompted-JSON fallback.\r\n *\r\n * Unsupported (Gemini accepts OpenAPI 3.0 subset):\r\n * - `oneOf` (use anyOf instead; or fall back to prompted JSON)\r\n * - `allOf` (limited support; safer to fall back)\r\n * - `not`\r\n * - `$ref` (we strip these via sanitize, but their presence indicates\r\n * a recursive schema we shouldn't pass through)\r\n */\r\nexport function detectUnsupportedSchemaFeature(input: unknown): string | null {\r\n if (!input || typeof input !== \"object\" || Array.isArray(input)) return null;\r\n const obj = input as Record<string, unknown>;\r\n for (const key of [\"oneOf\", \"allOf\", \"not\", \"$ref\"]) {\r\n if (key in obj) return key;\r\n }\r\n if (obj[\"properties\"] && typeof obj[\"properties\"] === \"object\") {\r\n for (const propSchema of Object.values(obj[\"properties\"] as Record<string, unknown>)) {\r\n const nested = detectUnsupportedSchemaFeature(propSchema);\r\n if (nested) return nested;\r\n }\r\n }\r\n if (obj[\"items\"] && typeof obj[\"items\"] === \"object\" && !Array.isArray(obj[\"items\"])) {\r\n const nested = detectUnsupportedSchemaFeature(obj[\"items\"]);\r\n if (nested) return nested;\r\n }\r\n if (Array.isArray(obj[\"anyOf\"])) {\r\n for (const sub of obj[\"anyOf\"]) {\r\n const nested = detectUnsupportedSchemaFeature(sub);\r\n if (nested) return nested;\r\n }\r\n }\r\n return null;\r\n}\r\n","/**\r\n * Bundled pricing for Google Gemini models.\r\n *\r\n * Source: https://ai.google.dev/gemini-api/docs/pricing (verified 2026-05).\r\n * Override per model via `pricingOverrides` on the adapter options.\r\n *\r\n * Gemini pricing has separate tiers for prompts under 200k tokens vs over\r\n * 200k tokens (the \"long-context premium\"). The bundled values are the\r\n * UNDER-200k-token rates, which dominate typical usage. For long-context\r\n * workloads, supply `pricingOverrides` with the over-200k rates.\r\n */\r\n\r\nimport type { ModelPricing } from \"@llm-ports/core\";\r\n\r\nexport const GEMINI_PRICING: Record<string, ModelPricing> = {\r\n // Gemini 2.5 family (2026-05 GA pricing)\r\n \"gemini-2.5-pro\": {\r\n inputPer1M: 1.25,\r\n outputPer1M: 5.0,\r\n cacheReadPer1M: 0.3125,\r\n },\r\n \"gemini-2.5-flash\": {\r\n inputPer1M: 0.075,\r\n outputPer1M: 0.3,\r\n cacheReadPer1M: 0.01875,\r\n },\r\n \"gemini-2.5-flash-lite\": {\r\n inputPer1M: 0.0375,\r\n outputPer1M: 0.15,\r\n cacheReadPer1M: 0.009375,\r\n },\r\n // Gemini 2.0 family (still available)\r\n \"gemini-2.0-flash\": {\r\n inputPer1M: 0.1,\r\n outputPer1M: 0.4,\r\n cacheReadPer1M: 0.025,\r\n },\r\n \"gemini-2.0-flash-lite\": {\r\n inputPer1M: 0.075,\r\n outputPer1M: 0.3,\r\n },\r\n};\r\n\r\nexport function lookupGeminiPricing(modelId: string): ModelPricing | undefined {\r\n return GEMINI_PRICING[modelId];\r\n}\r\n","/**\r\n * Google Gemini adapter for llm-ports.\r\n *\r\n * Wraps @google/genai (the unified Gemini + Vertex SDK as of 2026) to\r\n * implement LLMPort. Provides:\r\n *\r\n * - Native multimodal: image content blocks pass through as inlineData\r\n * (base64) or fileData (URL). NO degradation, unlike OpenAI-compat\r\n * baseURL where image_url.detail is silently ignored.\r\n * - Native streaming via generateContentStream\r\n * - Structured output via prompted-JSON + Zod retry-with-feedback\r\n * + alpha.5 programmatic repair. Native Gemini responseSchema lands\r\n * in v0.2.\r\n * - Image-block boundary validation (size + URL scheme) — same shape\r\n * as adapter-anthropic and adapter-openai (alpha.5).\r\n *\r\n * Out of scope for v0.1 alpha:\r\n * - Embeddings (Gemini's embedding API is separate; lands in v0.2)\r\n * - Multi-turn runAgent through Gemini's native automatic tool calling\r\n * (v0.1 ships a single-turn shim consistent with adapter-vercel)\r\n * - Caching API (Gemini supports explicit context caching; lands in v0.2)\r\n * - Code execution tool (Gemini's built-in code interpreter; lands in v0.2)\r\n */\r\n\r\nimport { GoogleGenAI } from \"@google/genai\";\r\nimport {\r\n attemptValidationRepair,\r\n computeChatCost,\r\n extractJSON,\r\n failValidation,\r\n mergeTokenUsage,\r\n stringifyContentBlocks,\r\n throwIfAborted,\r\n tryParsePartialJSON,\r\n validateImageBlocks,\r\n wrapProviderError,\r\n type AgentResult,\r\n type ContentBlock,\r\n type GenerateStructuredOptions,\r\n type GenerateStructuredResult,\r\n type GenerateTextOptions,\r\n type GenerateTextResult,\r\n type LLMMessage,\r\n type LLMPort,\r\n type MessageContent,\r\n type ModelPricing,\r\n type ProviderModelInfo,\r\n type RunAgentOptions,\r\n type StreamStructuredOptions,\r\n type StreamTextOptions,\r\n type TokenUsage,\r\n type ValidationStrategy,\r\n} from \"@llm-ports/core\";\r\nimport {\r\n detectUnsupportedSchemaFeature,\r\n extractGeminiText,\r\n fromGeminiCandidate,\r\n sanitizeGeminiSchema,\r\n toGeminiParts2,\r\n toGeminiRequest,\r\n toGeminiTools,\r\n zodToGeminiSchema,\r\n type GeminiPart,\r\n} from \"./content.js\";\r\nimport { GEMINI_PRICING } from \"./pricing.js\";\r\n\r\n// ─── Adapter options ─────────────────────────────────────────────────\r\n\r\nexport interface GoogleAdapterOptions {\r\n /** Google AI API key (https://aistudio.google.com/apikey). */\r\n apiKey: string;\r\n /** Override Gemini pricing for any model id. Falls back to the bundled table. */\r\n pricingOverrides?: Record<string, ModelPricing>;\r\n /** Default validation strategy if the registry doesn't override per-call. */\r\n validationStrategy?: ValidationStrategy;\r\n /**\r\n * Maximum bytes per base64 image. Defaults to 20MB (Gemini accepts up to\r\n * 20MB inlined; fileData URLs are unconstrained but provider-fetched).\r\n * Set to 0 or a negative number to disable size validation.\r\n */\r\n imageSizeLimitBytes?: number;\r\n}\r\n\r\n// ─── Internal context ────────────────────────────────────────────────\r\n\r\ninterface AdapterContext {\r\n client: GoogleGenAI;\r\n validationStrategy: ValidationStrategy;\r\n pricingOverrides: Record<string, ModelPricing>;\r\n imageSizeLimitBytes: number;\r\n}\r\n\r\nfunction pricingFor(ctx: AdapterContext, modelId: string): ModelPricing {\r\n const pricing = ctx.pricingOverrides[modelId] ?? GEMINI_PRICING[modelId];\r\n if (!pricing) {\r\n throw new Error(\r\n `No pricing entry for Google Gemini model \"${modelId}\". Provide pricingOverrides or update src/pricing.ts.`,\r\n );\r\n }\r\n return pricing;\r\n}\r\n\r\n// ─── Public factory ──────────────────────────────────────────────────\r\n\r\nexport interface GoogleAdapter {\r\n name: \"google\";\r\n pricing: Record<string, ModelPricing>;\r\n createLLMPort: (modelId: string, alias: string) => LLMPort;\r\n}\r\n\r\nexport function createGoogleAdapter(opts: GoogleAdapterOptions): GoogleAdapter {\r\n const mergedPricing: Record<string, ModelPricing> = {\r\n ...GEMINI_PRICING,\r\n ...(opts.pricingOverrides ?? {}),\r\n };\r\n const ctx: AdapterContext = {\r\n client: new GoogleGenAI({ apiKey: opts.apiKey }),\r\n validationStrategy: opts.validationStrategy ?? {\r\n kind: \"retry-with-feedback\",\r\n maxAttempts: 2,\r\n includeOriginalError: true,\r\n },\r\n pricingOverrides: opts.pricingOverrides ?? {},\r\n imageSizeLimitBytes: opts.imageSizeLimitBytes ?? 20 * 1024 * 1024,\r\n };\r\n return {\r\n name: \"google\",\r\n pricing: mergedPricing,\r\n createLLMPort: (modelId, alias) => createPort(ctx, modelId, alias),\r\n };\r\n}\r\n\r\n// ─── Port implementation ─────────────────────────────────────────────\r\n\r\nfunction createPort(ctx: AdapterContext, modelId: string, alias: string): LLMPort {\r\n const pricing = pricingFor(ctx, modelId);\r\n\r\n // Image-block validation closure: throws ImageTooLargeError or\r\n // InvalidImageUrlError before the SDK call.\r\n const validateContent = (content: MessageContent): void => {\r\n if (Array.isArray(content)) {\r\n validateImageBlocks(content, {\r\n alias,\r\n ...(ctx.imageSizeLimitBytes > 0 ? { limitBytes: ctx.imageSizeLimitBytes } : {}),\r\n });\r\n }\r\n };\r\n const validateMessages = (messages: ReadonlyArray<{ content: MessageContent }>): void => {\r\n for (const msg of messages) validateContent(msg.content);\r\n };\r\n\r\n return {\r\n async generateText(options: GenerateTextOptions): Promise<GenerateTextResult> {\r\n throwIfAborted(options.signal);\r\n validateContent(options.prompt);\r\n const start = Date.now();\r\n try {\r\n const parts = toGeminiParts2(options.prompt);\r\n const response = await ctx.client.models.generateContent({\r\n model: modelId,\r\n contents: [{ role: \"user\", parts }],\r\n config: {\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : {}),\r\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\r\n ...(options.maxOutputTokens !== undefined\r\n ? { maxOutputTokens: options.maxOutputTokens }\r\n : {}),\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n const candidate = response.candidates?.[0];\r\n const text = extractGeminiText(candidate?.content?.parts as GeminiPart[] | undefined);\r\n const usage = parseUsage(response);\r\n return {\r\n text,\r\n usage,\r\n cost: computeChatCost(usage, pricing),\r\n modelId: response.modelVersion ?? modelId,\r\n providerAlias: alias,\r\n latencyMs: Date.now() - start,\r\n };\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n },\r\n\r\n async generateStructured<T>(\r\n options: GenerateStructuredOptions<T>,\r\n ): Promise<GenerateStructuredResult<T>> {\r\n throwIfAborted(options.signal);\r\n validateContent(options.prompt);\r\n const start = Date.now();\r\n let attempts = 0;\r\n const maxAttempts =\r\n ctx.validationStrategy.kind === \"retry-with-feedback\"\r\n ? ctx.validationStrategy.maxAttempts\r\n : 1;\r\n\r\n // Native responseSchema path: convert schema to JSON Schema; if no\r\n // Gemini-unsupported features (oneOf, $ref, etc.) emit it as\r\n // `config.responseSchema` so Gemini constrains decoding to the schema\r\n // before tokens are produced. We still validate with Zod (Gemini's\r\n // schema enforcement is best-effort) and still run the repair pass.\r\n // If the schema uses unsupported features, fall back to the prompted-\r\n // JSON path (existing behavior).\r\n const jsonSchema = zodToGeminiSchema(options.schema);\r\n const unsupportedFeature = detectUnsupportedSchemaFeature(jsonSchema);\r\n const useNativeResponseSchema = unsupportedFeature === null;\r\n if (!useNativeResponseSchema) {\r\n warnSchemaFallback(modelId, unsupportedFeature);\r\n }\r\n const sanitizedSchema = useNativeResponseSchema\r\n ? sanitizeGeminiSchema(jsonSchema)\r\n : null;\r\n\r\n let correctionPrompt: string | null = null;\r\n let lastUsage: TokenUsage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };\r\n let lastModelId = modelId;\r\n\r\n while (attempts < maxAttempts) {\r\n attempts++;\r\n // With native responseSchema, Gemini constrains the decoding — we\r\n // skip the \"Reply with a single JSON object only\" suffix on the\r\n // first attempt. Correction prompts still apply on retry-with-\r\n // feedback rounds.\r\n const userText = correctionPrompt\r\n ? `${stringifyContentBlocks(options.prompt)}\\n\\n${correctionPrompt}`\r\n : useNativeResponseSchema\r\n ? stringifyContentBlocks(options.prompt)\r\n : `${stringifyContentBlocks(options.prompt)}\\n\\nReply with a single JSON object only. No prose, no code fences.`;\r\n\r\n try {\r\n const response = await ctx.client.models.generateContent({\r\n model: modelId,\r\n contents: [{ role: \"user\", parts: [{ text: userText }] }],\r\n config: {\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : {}),\r\n temperature: options.temperature ?? 0,\r\n ...(options.maxOutputTokens !== undefined\r\n ? { maxOutputTokens: options.maxOutputTokens }\r\n : {}),\r\n responseMimeType: \"application/json\",\r\n ...(sanitizedSchema ? { responseSchema: sanitizedSchema } : {}),\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n const candidate = response.candidates?.[0];\r\n const raw = extractGeminiText(candidate?.content?.parts as GeminiPart[] | undefined);\r\n lastUsage = parseUsage(response);\r\n lastModelId = response.modelVersion ?? modelId;\r\n\r\n const decoded = extractJSON(raw);\r\n let parsed = options.schema.safeParse(decoded);\r\n if (!parsed.success) {\r\n const repaired = attemptValidationRepair(decoded, parsed.error);\r\n const reparsed = options.schema.safeParse(repaired);\r\n if (reparsed.success) parsed = reparsed;\r\n }\r\n if (parsed.success) {\r\n return {\r\n data: parsed.data as T,\r\n usage: lastUsage,\r\n cost: computeChatCost(lastUsage, pricing),\r\n modelId: lastModelId,\r\n providerAlias: alias,\r\n latencyMs: Date.now() - start,\r\n validationAttempts: attempts,\r\n };\r\n }\r\n if (\r\n ctx.validationStrategy.kind === \"retry-with-feedback\" &&\r\n attempts < maxAttempts\r\n ) {\r\n const issues = parsed.error.issues\r\n .map((i) => `- ${i.path.join(\".\") || \"<root>\"}: ${i.message}`)\r\n .join(\"\\n\");\r\n correctionPrompt = `Your previous response failed validation:\\n${issues}\\n\\nReply with a single corrected JSON object only.`;\r\n continue;\r\n }\r\n failValidation(parsed.error.issues, attempts);\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n }\r\n throw new Error(\"generateStructured exhausted attempts\");\r\n },\r\n\r\n async *streamText(options: StreamTextOptions): AsyncIterable<string> {\r\n throwIfAborted(options.signal);\r\n validateContent(options.prompt);\r\n try {\r\n const parts = toGeminiParts2(options.prompt);\r\n const stream = await ctx.client.models.generateContentStream({\r\n model: modelId,\r\n contents: [{ role: \"user\", parts }],\r\n config: {\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : {}),\r\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\r\n ...(options.maxOutputTokens !== undefined\r\n ? { maxOutputTokens: options.maxOutputTokens }\r\n : {}),\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n for await (const chunk of stream) {\r\n const text = extractGeminiText(\r\n chunk.candidates?.[0]?.content?.parts as GeminiPart[] | undefined,\r\n );\r\n if (text.length > 0) yield text;\r\n }\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n },\r\n\r\n async *streamStructured<T>(options: StreamStructuredOptions<T>): AsyncIterable<Partial<T>> {\r\n throwIfAborted(options.signal);\r\n validateContent(options.prompt);\r\n try {\r\n const stream = await ctx.client.models.generateContentStream({\r\n model: modelId,\r\n contents: [\r\n {\r\n role: \"user\",\r\n parts: [\r\n {\r\n text: `${stringifyContentBlocks(options.prompt)}\\n\\nReply with a single JSON object only. Stream the JSON progressively.`,\r\n },\r\n ],\r\n },\r\n ],\r\n config: {\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : {}),\r\n temperature: options.temperature ?? 0,\r\n ...(options.maxOutputTokens !== undefined\r\n ? { maxOutputTokens: options.maxOutputTokens }\r\n : {}),\r\n responseMimeType: \"application/json\",\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n let buffer = \"\";\r\n for await (const chunk of stream) {\r\n const text = extractGeminiText(\r\n chunk.candidates?.[0]?.content?.parts as GeminiPart[] | undefined,\r\n );\r\n if (text.length === 0) continue;\r\n buffer += text;\r\n const partial = tryParsePartialJSON(buffer);\r\n if (partial !== null) yield partial as Partial<T>;\r\n }\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n },\r\n\r\n async runAgent(options: RunAgentOptions): Promise<AgentResult> {\r\n throwIfAborted(options.signal);\r\n validateMessages(options.messages);\r\n const start = Date.now();\r\n const maxSteps = options.maxSteps ?? 10;\r\n const conversation: LLMMessage[] = [...options.messages];\r\n const toolCalls: AgentResult[\"toolCalls\"] = [];\r\n let stepsTaken = 0;\r\n let totalUsage: TokenUsage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };\r\n let finalText = \"\";\r\n let lastModelId = modelId;\r\n let terminationReason: AgentResult[\"terminationReason\"] = \"max_steps\";\r\n\r\n const toolsRegistered = Object.keys(options.tools).length > 0;\r\n const geminiTools = toolsRegistered ? toGeminiTools(options.tools) : undefined;\r\n\r\n try {\r\n for (let step = 0; step < maxSteps; step++) {\r\n // Re-check between steps so cancellation propagates even if the\r\n // model just emitted a function call but the user clicked cancel\r\n // before we send the result back.\r\n throwIfAborted(options.signal);\r\n stepsTaken = step + 1;\r\n\r\n const { systemInstruction, contents } = toGeminiRequest(conversation);\r\n const response = await ctx.client.models.generateContent({\r\n model: modelId,\r\n contents,\r\n config: {\r\n // options.instructions takes precedence over a system message\r\n // baked into the messages array, matching the per-method pattern\r\n // used elsewhere in the adapter.\r\n ...(options.instructions !== undefined\r\n ? { systemInstruction: options.instructions }\r\n : systemInstruction !== undefined\r\n ? { systemInstruction }\r\n : {}),\r\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\r\n ...(options.maxOutputTokens !== undefined\r\n ? { maxOutputTokens: options.maxOutputTokens }\r\n : {}),\r\n ...(geminiTools ? { tools: geminiTools } : {}),\r\n ...(options.signal ? { abortSignal: options.signal } : {}),\r\n },\r\n });\r\n\r\n totalUsage = mergeTokenUsage(totalUsage, parseUsage(response));\r\n lastModelId = response.modelVersion ?? modelId;\r\n\r\n const candidate = response.candidates?.[0];\r\n const responseParts = (candidate?.content?.parts ?? []) as GeminiPart[];\r\n const blocks = fromGeminiCandidate({\r\n content: { parts: responseParts },\r\n });\r\n\r\n // Push the assistant's response (text + tool_use blocks) into the\r\n // canonical conversation so the next round sees it.\r\n conversation.push({\r\n role: \"assistant\",\r\n content: blocks.length > 0\r\n ? blocks\r\n : [{ type: \"text\", text: extractGeminiText(responseParts) }],\r\n });\r\n\r\n finalText = blocks\r\n .filter((b): b is Extract<ContentBlock, { type: \"text\" }> => b.type === \"text\")\r\n .map((b) => b.text)\r\n .join(\"\");\r\n\r\n const toolUses = blocks.filter(\r\n (b): b is Extract<ContentBlock, { type: \"tool_use\" }> => b.type === \"tool_use\",\r\n );\r\n if (toolUses.length === 0) {\r\n terminationReason = \"completed\";\r\n break;\r\n }\r\n\r\n // Execute every tool call the model emitted this turn. Gemini may\r\n // emit multiple functionCalls in a single response and expects all\r\n // responses before continuing.\r\n const toolResults: ContentBlock[] = [];\r\n for (const tu of toolUses) {\r\n const def = options.tools[tu.name];\r\n if (!def) {\r\n toolResults.push({\r\n type: \"tool_result\",\r\n toolUseId: tu.id,\r\n content: `Tool \"${tu.name}\" not found.`,\r\n isError: true,\r\n });\r\n continue;\r\n }\r\n try {\r\n const output = await def.execute(tu.input as never);\r\n toolCalls.push({\r\n name: tu.name,\r\n input: tu.input as Record<string, unknown>,\r\n output,\r\n });\r\n const text = typeof output === \"string\" ? output : JSON.stringify(output);\r\n const truncated =\r\n def.maxOutputBytes !== undefined && text.length > def.maxOutputBytes\r\n ? `${text.slice(0, def.maxOutputBytes)}\\n[truncated]`\r\n : text;\r\n toolResults.push({\r\n type: \"tool_result\",\r\n toolUseId: tu.id,\r\n content: truncated,\r\n });\r\n } catch (toolErr) {\r\n toolResults.push({\r\n type: \"tool_result\",\r\n toolUseId: tu.id,\r\n content: toolErr instanceof Error ? toolErr.message : String(toolErr),\r\n isError: true,\r\n });\r\n }\r\n }\r\n conversation.push({ role: \"tool\", content: toolResults });\r\n }\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n\r\n return {\r\n text: finalText,\r\n messages: conversation,\r\n toolCalls,\r\n usage: totalUsage,\r\n cost: computeChatCost(totalUsage, pricing),\r\n modelId: lastModelId,\r\n providerAlias: alias,\r\n latencyMs: Date.now() - start,\r\n stepsTaken,\r\n terminationReason,\r\n };\r\n },\r\n\r\n async listModels(): Promise<ProviderModelInfo[]> {\r\n try {\r\n const out: ProviderModelInfo[] = [];\r\n // The @google/genai SDK paginates; iterate once. Pricing is NOT\r\n // included in the response — Gemini exposes catalog metadata but\r\n // not USD rates. checkPricingFreshness() will detect added/removed\r\n // models but cannot detect rate-only drift for Gemini.\r\n const pager = await ctx.client.models.list();\r\n for await (const m of pager) {\r\n const model = m as {\r\n name?: string;\r\n displayName?: string;\r\n inputTokenLimit?: number;\r\n outputTokenLimit?: number;\r\n };\r\n if (!model.name) continue;\r\n // `name` comes back as e.g. \"models/gemini-2.5-flash\". Strip prefix.\r\n const id = model.name.startsWith(\"models/\")\r\n ? model.name.slice(\"models/\".length)\r\n : model.name;\r\n out.push({\r\n id,\r\n ...(model.displayName ? { displayName: model.displayName } : {}),\r\n ...(model.inputTokenLimit ? { contextWindow: model.inputTokenLimit } : {}),\r\n ...(model.outputTokenLimit\r\n ? { metadata: { outputTokenLimit: model.outputTokenLimit } }\r\n : {}),\r\n });\r\n }\r\n return out;\r\n } catch (err) {\r\n throw wrapProviderError(alias, err);\r\n }\r\n },\r\n };\r\n}\r\n\r\n// ─── Helpers ─────────────────────────────────────────────────────────\r\n\r\ninterface GeminiUsageMetadata {\r\n promptTokenCount?: number;\r\n candidatesTokenCount?: number;\r\n totalTokenCount?: number;\r\n cachedContentTokenCount?: number;\r\n}\r\n\r\ninterface GeminiResponseShape {\r\n usageMetadata?: GeminiUsageMetadata;\r\n}\r\n\r\nfunction parseUsage(response: GeminiResponseShape): TokenUsage {\r\n const m = response.usageMetadata ?? {};\r\n const inputTokens = m.promptTokenCount ?? 0;\r\n const outputTokens = m.candidatesTokenCount ?? 0;\r\n const totalTokens = m.totalTokenCount ?? inputTokens + outputTokens;\r\n const usage: TokenUsage = { inputTokens, outputTokens, totalTokens };\r\n if (m.cachedContentTokenCount !== undefined && m.cachedContentTokenCount > 0) {\r\n usage.cacheReadTokens = m.cachedContentTokenCount;\r\n }\r\n return usage;\r\n}\r\n\r\n// ─── Schema-fallback warning ─────────────────────────────────────────\r\n\r\nconst warnedSchemaFallback = new Set<string>();\r\n\r\n/**\r\n * Emit a one-time `console.warn` per model+feature when generateStructured\r\n * falls back from native responseSchema to prompted JSON because the Zod\r\n * schema contains a feature Gemini's constrained-decoding doesn't accept.\r\n *\r\n * Not a runtime-learned constraint (we know pre-call from inspecting the\r\n * schema), so no click-to-file URL — just a developer-facing hint that\r\n * simplifying the schema would unlock native responseSchema enforcement.\r\n */\r\nfunction warnSchemaFallback(modelId: string, feature: string): void {\r\n const key = `${modelId}::${feature}`;\r\n if (warnedSchemaFallback.has(key)) return;\r\n warnedSchemaFallback.add(key);\r\n console.warn(\r\n `[@llm-ports/adapter-google] generateStructured: model \"${modelId}\" schema ` +\r\n `contains \"${feature}\" which Gemini's responseSchema does not support. ` +\r\n `Falling back to prompted-JSON + Zod validation (still correct; just ` +\r\n `slightly weaker constrained-decoding guarantee).`,\r\n );\r\n}\r\n\r\n/** Test-only: reset the per-process fallback warning state. */\r\nexport function _resetSchemaFallbackWarnings(): void {\r\n warnedSchemaFallback.clear();\r\n}\r\n\r\n// Re-export ContentBlock for the rare adapter user that wants to type-check\r\n// outside of @llm-ports/core. Keeps the import surface symmetric with the\r\n// other adapters.\r\nexport type { ContentBlock };\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llm-ports/adapter-google",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.9",
|
|
4
4
|
"description": "Google Gemini adapter for llm-ports — native @google/genai SDK integration with bundled pricing, content-block translation, validation repair, and image-size + URL validation.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Babak Abbaschian",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"README.md"
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"
|
|
23
|
+
"zod-to-json-schema": "^3.23.5",
|
|
24
|
+
"@llm-ports/core": "0.1.0-alpha.9"
|
|
24
25
|
},
|
|
25
26
|
"peerDependencies": {
|
|
26
27
|
"@google/genai": "^2.0.0",
|