@gullabs/google 0.13.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -17,9 +17,18 @@ function requireApiKey(auth) {
17
17
  var FLEX_DEFAULT_TIMEOUT_MS = 15e5;
18
18
  var STANDARD_DEFAULT_TIMEOUT_MS = 3e5;
19
19
  var TRANSPORT_TIMEOUT_BUFFER_MS = 5e3;
20
+ var MAX_TIMER_MS = 2147483647;
21
+ var GOOGLE_MAX_TIMEOUT_MS = MAX_TIMER_MS - TRANSPORT_TIMEOUT_BUFFER_MS;
22
+ var GEMINI_API_ROOT = "https://generativelanguage.googleapis.com";
23
+ async function newGoogleGenAI(auth) {
24
+ const { GoogleGenAI: Sdk } = await import('@google/genai');
25
+ return new Sdk({
26
+ apiKey: requireApiKey(auth),
27
+ httpOptions: { baseUrl: `${GEMINI_API_ROOT}/` }
28
+ });
29
+ }
20
30
  async function buildGoogleClient(auth) {
21
- const { GoogleGenAI } = await import('@google/genai');
22
- const ai = new GoogleGenAI({ apiKey: requireApiKey(auth) });
31
+ const ai = await newGoogleGenAI(auth);
23
32
  return {
24
33
  models: {
25
34
  async generateContent(params) {
@@ -27,12 +36,59 @@ async function buildGoogleClient(auth) {
27
36
  return result;
28
37
  },
29
38
  async countTokens(params) {
39
+ if (params.systemInstruction !== void 0 || params.tools !== void 0) {
40
+ return countTokensWithRequest(requireApiKey(auth), params);
41
+ }
30
42
  const result = await ai.models.countTokens(params);
31
43
  return result;
32
44
  }
33
45
  }
34
46
  };
35
47
  }
48
+ var GEMINI_API_BASE = `${GEMINI_API_ROOT}/v1beta`;
49
+ var MAX_ERROR_BODY_CHARS = 500;
50
+ async function countTokensWithRequest(apiKey, params) {
51
+ const { ApiError } = await import('@google/genai');
52
+ const model = params.model.startsWith("models/") ? params.model : `models/${params.model}`;
53
+ const response = await fetch(`${GEMINI_API_BASE}/${model}:countTokens`, {
54
+ method: "POST",
55
+ headers: { "content-type": "application/json", "x-goog-api-key": apiKey },
56
+ body: JSON.stringify({
57
+ generateContentRequest: {
58
+ model,
59
+ contents: params.contents,
60
+ ...params.systemInstruction !== void 0 ? { systemInstruction: params.systemInstruction } : {},
61
+ ...params.tools !== void 0 ? { tools: params.tools } : {}
62
+ }
63
+ }),
64
+ ...params.config?.abortSignal !== void 0 ? { signal: params.config.abortSignal } : {}
65
+ });
66
+ const raw = await response.text();
67
+ let parsed;
68
+ try {
69
+ parsed = JSON.parse(raw);
70
+ } catch {
71
+ parsed = void 0;
72
+ }
73
+ if (!response.ok) {
74
+ const body = typeof parsed === "object" && parsed !== null && typeof parsed.error === "object" ? parsed : {
75
+ error: {
76
+ message: raw.length > MAX_ERROR_BODY_CHARS ? `${raw.slice(0, MAX_ERROR_BODY_CHARS)}\u2026` : raw,
77
+ code: response.status,
78
+ status: response.statusText
79
+ }
80
+ };
81
+ throw new ApiError({ message: JSON.stringify(body), status: response.status });
82
+ }
83
+ if (typeof parsed !== "object" || parsed === null) {
84
+ throw new core.LlmError("Gemini countTokens response is not a JSON object", {
85
+ kind: "server",
86
+ retryable: true,
87
+ provider: "google"
88
+ });
89
+ }
90
+ return parsed;
91
+ }
36
92
 
37
93
  // src/reasoning-budget.ts
38
94
  var GOOGLE_REASONING_EFFORT_BUDGET = {
@@ -41,6 +97,69 @@ var GOOGLE_REASONING_EFFORT_BUDGET = {
41
97
  medium: 8192,
42
98
  high: 24576
43
99
  };
100
+ var GOOGLE_HIGH_EFFORT_MIN_OUTPUT_TOKENS = 4096;
101
+
102
+ // src/json-schema.ts
103
+ var GEMINI_KEYWORDS = [
104
+ "type",
105
+ "properties",
106
+ "required",
107
+ "additionalProperties",
108
+ "enum",
109
+ "anyOf",
110
+ "$ref",
111
+ "$defs",
112
+ "items",
113
+ "prefixItems",
114
+ "minItems",
115
+ "maxItems",
116
+ "minimum",
117
+ "maximum",
118
+ "format",
119
+ "pattern",
120
+ "minLength",
121
+ "maxLength"
122
+ ];
123
+ var GEMMA_IGNORED_KEYWORDS = /* @__PURE__ */ new Set([
124
+ "format",
125
+ "minLength",
126
+ "maxLength"
127
+ ]);
128
+ var GOOGLE_FORMATS = ["date-time", "date", "email"];
129
+ var GEMINI_PROFILE = {
130
+ provider: "google",
131
+ keywords: GEMINI_KEYWORDS,
132
+ formats: GOOGLE_FORMATS,
133
+ limits: {},
134
+ circularRefs: true,
135
+ booleanItems: true,
136
+ patternSubset: true
137
+ };
138
+ var GEMMA_PROFILE = {
139
+ ...GEMINI_PROFILE,
140
+ keywords: GEMINI_KEYWORDS.filter((keyword) => !GEMMA_IGNORED_KEYWORDS.has(keyword))
141
+ };
142
+ function googleJsonSchemaProfile(canonicalModel) {
143
+ return canonicalModel.startsWith("gemma-") ? GEMMA_PROFILE : GEMINI_PROFILE;
144
+ }
145
+
146
+ // src/safety-settings.ts
147
+ var GOOGLE_SAFETY_CATEGORIES = [
148
+ "HARM_CATEGORY_HARASSMENT",
149
+ "HARM_CATEGORY_HATE_SPEECH",
150
+ "HARM_CATEGORY_SEXUALLY_EXPLICIT",
151
+ "HARM_CATEGORY_DANGEROUS_CONTENT",
152
+ "HARM_CATEGORY_CIVIC_INTEGRITY",
153
+ "HARM_CATEGORY_JAILBREAK"
154
+ ];
155
+ var GOOGLE_SAFETY_THRESHOLDS = [
156
+ "HARM_BLOCK_THRESHOLD_UNSPECIFIED",
157
+ "BLOCK_LOW_AND_ABOVE",
158
+ "BLOCK_MEDIUM_AND_ABOVE",
159
+ "BLOCK_ONLY_HIGH",
160
+ "BLOCK_NONE",
161
+ "OFF"
162
+ ];
44
163
 
45
164
  // src/grounding.ts
46
165
  function hostnameFrom(url) {
@@ -69,23 +188,116 @@ function toCitation(chunk) {
69
188
  sourceName
70
189
  };
71
190
  }
72
- function normalizeGroundingCitations(groundingMetadata) {
191
+ function utf16IndexAtByte(text, byte) {
192
+ if (!Number.isInteger(byte) || byte < 0) return void 0;
193
+ let bytes = 0;
194
+ let units = 0;
195
+ if (byte === 0) return 0;
196
+ for (const char of text) {
197
+ const code = char.codePointAt(0);
198
+ bytes += code < 128 ? 1 : code < 2048 ? 2 : code < 65536 ? 3 : 4;
199
+ units += char.length;
200
+ if (bytes === byte) return units;
201
+ if (bytes > byte) return void 0;
202
+ }
203
+ return void 0;
204
+ }
205
+ function segmentRange(segment, answerParts, joined, onDropped) {
206
+ if (segment === null || typeof segment !== "object") return void 0;
207
+ const raw = segment;
208
+ const partIndex = raw["partIndex"] ?? 0;
209
+ const startByte = raw["startIndex"] ?? 0;
210
+ const endByte = raw["endIndex"];
211
+ if (typeof partIndex !== "number" || typeof startByte !== "number" || typeof endByte !== "number") {
212
+ return void 0;
213
+ }
214
+ const part = answerParts[partIndex];
215
+ if (part === void 0) return void 0;
216
+ const start = utf16IndexAtByte(part.text, startByte);
217
+ const end = utf16IndexAtByte(part.text, endByte);
218
+ if (start === void 0 || end === void 0 || end <= start) return void 0;
219
+ const range = { start: part.offset + start, end: part.offset + end };
220
+ const expected = raw["text"];
221
+ if (typeof expected === "string" && joined.slice(range.start, range.end) !== expected) {
222
+ onDropped(
223
+ `google: dropped a textRange for a grounding segment (partIndex ${partIndex}, bytes ${startByte}-${endByte}): the answer at that range does not equal segment.text. The source stays cited without a range.`
224
+ );
225
+ return void 0;
226
+ }
227
+ return range;
228
+ }
229
+ function readSupports(groundingMetadata, answerParts, onDropped) {
230
+ const supports = groundingMetadata["groundingSupports"];
231
+ if (!Array.isArray(supports)) return void 0;
232
+ const joined = answerParts.map((part) => part?.text ?? "").join("");
233
+ const byChunk = /* @__PURE__ */ new Map();
234
+ for (const support of supports) {
235
+ if (support === null || typeof support !== "object") continue;
236
+ const record = support;
237
+ const indices = record["groundingChunkIndices"];
238
+ if (!Array.isArray(indices)) continue;
239
+ const range = segmentRange(record["segment"], answerParts, joined, onDropped);
240
+ for (const index of indices) {
241
+ if (typeof index !== "number") continue;
242
+ if (!byChunk.has(index) || byChunk.get(index) === void 0 && range) {
243
+ byChunk.set(index, range);
244
+ }
245
+ }
246
+ }
247
+ return byChunk;
248
+ }
249
+ function normalizeGroundingCitations(groundingMetadata, answerParts = [], onDropped = () => {
250
+ }) {
73
251
  if (groundingMetadata === null || typeof groundingMetadata !== "object") return [];
74
- const chunks = groundingMetadata["groundingChunks"];
252
+ const metadata = groundingMetadata;
253
+ const chunks = metadata["groundingChunks"];
75
254
  if (!Array.isArray(chunks)) return [];
76
- const seen = /* @__PURE__ */ new Set();
77
- const citations = [];
78
- for (const chunk of chunks) {
255
+ const supports = readSupports(metadata, answerParts, onDropped);
256
+ const byUrl = /* @__PURE__ */ new Map();
257
+ chunks.forEach((chunk, chunkIndex) => {
79
258
  const citation = toCitation(chunk);
80
- if (citation === void 0) continue;
81
- if (seen.has(citation.url)) continue;
82
- seen.add(citation.url);
83
- citations.push(citation);
259
+ if (citation === void 0) return;
260
+ const existing = byUrl.get(citation.url);
261
+ const target = existing ?? citation;
262
+ if (existing === void 0) byUrl.set(citation.url, citation);
263
+ if (supports === void 0) return;
264
+ if (supports.has(chunkIndex)) {
265
+ target.cited = true;
266
+ const range = supports.get(chunkIndex);
267
+ if (range !== void 0 && target.textRange === void 0) {
268
+ target.textRange = { ...range };
269
+ }
270
+ } else if (target.cited === void 0) {
271
+ target.cited = false;
272
+ }
273
+ });
274
+ return [...byUrl.values()];
275
+ }
276
+ function countWebSearchQueries(groundingMetadata) {
277
+ if (groundingMetadata === null || typeof groundingMetadata !== "object") {
278
+ return void 0;
84
279
  }
85
- return citations;
280
+ const queries = groundingMetadata["webSearchQueries"];
281
+ if (!Array.isArray(queries)) return void 0;
282
+ const named = queries.filter((q) => typeof q === "string" && q.length > 0).length;
283
+ return named === 0 && queries.length > 0 ? void 0 : named;
284
+ }
285
+ function readSearchEntryPoint(groundingMetadata) {
286
+ if (groundingMetadata === null || typeof groundingMetadata !== "object") {
287
+ return void 0;
288
+ }
289
+ const entry = groundingMetadata["searchEntryPoint"];
290
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry)) {
291
+ return void 0;
292
+ }
293
+ return Object.keys(entry).length > 0 ? entry : void 0;
86
294
  }
87
295
 
88
296
  // src/tool-call-id.ts
297
+ var SYNTHESIZED_PREFIX = "anyllm_call_";
298
+ function isSynthesizedToolCallId(id) {
299
+ return id.startsWith(SYNTHESIZED_PREFIX);
300
+ }
89
301
  function reserveProviderToolCallIds(ids) {
90
302
  const reserved = /* @__PURE__ */ new Set();
91
303
  for (const id of ids) {
@@ -98,7 +310,7 @@ function nextFallbackToolCallId(toolName, counters, reserved, counterKey = toolN
98
310
  let id;
99
311
  do {
100
312
  n += 1;
101
- id = `call_${toolName}_${n}`;
313
+ id = `${SYNTHESIZED_PREFIX}${toolName}_${n}`;
102
314
  } while (reserved.has(id));
103
315
  counters.set(counterKey, n);
104
316
  return id;
@@ -111,78 +323,645 @@ function resolveToolCallId(providerId, toolName, counters, reserved, counterKey)
111
323
  }
112
324
 
113
325
  // src/flex-fallback.ts
114
- var CAPACITY_PATTERNS = [
115
- /capacity/i,
116
- /overload/i,
117
- /overloaded/i,
118
- /unavailable/i,
119
- /no\s+capacity/i,
120
- /temporar(?:y|ily)/i,
121
- /try\s+again/i
122
- ];
123
- var QUOTA_PATTERNS = [
124
- /quota/i,
125
- /billing/i,
126
- /billable/i,
127
- /payment/i,
128
- /rate\s+limit/i,
129
- /exceeded/i,
130
- /insufficient/i
131
- ];
132
326
  function isGeminiCapacityError(err) {
133
- if (err.kind === "server") return err.httpStatus === 503;
134
- if (err.kind !== "rate_limited") return false;
135
- const message = err.message;
136
- if (QUOTA_PATTERNS.some((pattern) => pattern.test(message))) {
137
- return false;
138
- }
139
- return CAPACITY_PATTERNS.some((pattern) => pattern.test(message));
140
- }
141
- var GOOGLE_TRANSPORT_ERROR_PATTERN = /fetch failed|connection error|econnreset|econnrefused|etimedout|eai_again|epipe|socket hang up/i;
142
- function matchesGoogleTransportSignature(err) {
143
- if (!(err instanceof Error)) return false;
144
- if (GOOGLE_TRANSPORT_ERROR_PATTERN.test(err.message)) return true;
145
- const code = err.code;
146
- return typeof code === "string" && GOOGLE_TRANSPORT_ERROR_PATTERN.test(code);
147
- }
148
- function isGoogleTransportError(rawErr) {
149
- if (matchesGoogleTransportSignature(rawErr)) return true;
150
- if (rawErr instanceof Error) {
151
- const cause = rawErr.cause;
152
- if (matchesGoogleTransportSignature(cause)) return true;
153
- }
154
- return false;
155
- }
156
- function isGoogleModelNotFound(rawErr) {
157
- if (!(rawErr instanceof Error)) return false;
158
- if (rawErr.status !== 404) return false;
327
+ return err.httpStatus === 503;
328
+ }
329
+ function isRecord(value) {
330
+ return typeof value === "object" && value !== null && !Array.isArray(value);
331
+ }
332
+ function parseGoogleErrorBody(rawErr) {
333
+ const source = rawErr instanceof core.LlmError ? rawErr.cause : rawErr;
334
+ if (!(source instanceof Error)) return void 0;
335
+ let parsed;
159
336
  try {
160
- const parsed = JSON.parse(rawErr.message);
161
- return parsed.error?.code === 404 && parsed.error.status === "NOT_FOUND";
337
+ parsed = JSON.parse(source.message);
162
338
  } catch {
163
- return false;
339
+ return void 0;
340
+ }
341
+ if (!isRecord(parsed) || !isRecord(parsed["error"])) return void 0;
342
+ const error = parsed["error"];
343
+ const details = Array.isArray(error["details"]) ? error["details"].filter(isRecord) : [];
344
+ return {
345
+ ...typeof error["status"] === "string" ? { status: error["status"] } : {},
346
+ ...typeof error["message"] === "string" ? { message: error["message"] } : {},
347
+ details
348
+ };
349
+ }
350
+ function detailOfType(body, type) {
351
+ return body.details.filter((d) => d["@type"] === `type.googleapis.com/${type}`);
352
+ }
353
+ function errorInfoReasons(body) {
354
+ return detailOfType(body, "google.rpc.ErrorInfo").flatMap(
355
+ (d) => typeof d["reason"] === "string" ? [d["reason"]] : []
356
+ );
357
+ }
358
+ function isDailyQuota(body) {
359
+ return detailOfType(body, "google.rpc.QuotaFailure").some(
360
+ (failure) => Array.isArray(failure["violations"]) && failure["violations"].some(
361
+ (v) => isRecord(v) && typeof v["quotaId"] === "string" && v["quotaId"].includes("PerDay")
362
+ )
363
+ );
364
+ }
365
+ var PROTO_DURATION = /^\d+(?:\.\d{1,9})?s$/;
366
+ function durationText(delay) {
367
+ if (typeof delay === "string") return PROTO_DURATION.test(delay) ? delay : void 0;
368
+ if (!isRecord(delay)) return void 0;
369
+ const { seconds, nanos } = delay;
370
+ const whole = typeof seconds === "number" && Number.isSafeInteger(seconds) && seconds >= 0 ? String(seconds) : typeof seconds === "string" && /^\d+$/.test(seconds) ? seconds : seconds === void 0 ? "0" : void 0;
371
+ const fraction = typeof nanos === "number" && Number.isInteger(nanos) && nanos >= 0 && nanos < 1e9 ? String(nanos).padStart(9, "0") : nanos === void 0 ? "0" : void 0;
372
+ return whole !== void 0 && fraction !== void 0 ? `${whole}.${fraction}s` : void 0;
373
+ }
374
+ function retryDelayMs(body) {
375
+ for (const info of detailOfType(body, "google.rpc.RetryInfo")) {
376
+ const text = durationText(info["retryDelay"]);
377
+ if (text === void 0) continue;
378
+ const ms = core.parseRetryAfter({ "retry-after": text }, Date.now());
379
+ if (ms !== void 0) return ms;
164
380
  }
381
+ return void 0;
382
+ }
383
+ var API_KEY_REASONS = /* @__PURE__ */ new Set(["API_KEY_INVALID", "API_KEY_EXPIRED"]);
384
+ var STALE_CACHE_MESSAGE = "CachedContent not found";
385
+ function isGoogleNotFoundError(err) {
386
+ if (typeof err !== "object" || err === null) return false;
387
+ const obj = err;
388
+ const httpStatus = core.classifyError(err).httpStatus ?? numericStatus(obj["httpStatus"]);
389
+ if (httpStatus === 404 || obj["status"] === "NOT_FOUND" || obj["code"] === "NOT_FOUND") {
390
+ return true;
391
+ }
392
+ const body = parseGoogleErrorBody(err);
393
+ const message = body?.message ?? (typeof obj["message"] === "string" ? obj["message"] : "");
394
+ if (httpStatus === 403) return /may not exist|not found/i.test(message);
395
+ const statusKnown = httpStatus !== void 0 || body?.status !== void 0 || typeof obj["status"] === "string" || [obj["status"], obj["code"]].some((value) => typeof value === "number");
396
+ if (statusKnown) return false;
397
+ return /not\s*found|404/i.test(message) && /file|cachedcontent/i.test(message);
398
+ }
399
+ function numericStatus(value) {
400
+ const n = typeof value === "number" ? value : typeof value === "string" && /^\d{3}$/.test(value.trim()) ? Number(value) : void 0;
401
+ return n !== void 0 && Number.isInteger(n) && n >= 100 && n <= 599 ? n : void 0;
165
402
  }
166
403
  function classifyGoogleError(rawErr, extra) {
167
404
  const base = core.classifyError(rawErr);
168
- const reclassifyAsTransport = base.kind === "unknown" && isGoogleTransportError(rawErr);
169
- const reclassifyAsBadRequest = base.kind === "unknown" && isGoogleModelNotFound(rawErr);
170
- return new core.LlmError(base.message, {
171
- kind: reclassifyAsTransport ? "server" : reclassifyAsBadRequest ? "bad_request" : base.kind,
172
- retryable: reclassifyAsTransport ? true : base.retryable,
405
+ const body = parseGoogleErrorBody(rawErr);
406
+ let kind = base.kind;
407
+ let retryable = base.retryable;
408
+ let reason = base.reason;
409
+ let retryAfterMs = base.retryAfterMs;
410
+ let message = base.message;
411
+ if (extra?.transportTimeout !== void 0) {
412
+ kind = "timeout";
413
+ retryable = false;
414
+ reason = "transport_timeout";
415
+ retryAfterMs = void 0;
416
+ message = extra.transportTimeout;
417
+ } else if (!(rawErr instanceof core.LlmError) && base.kind === "timeout" && base.httpStatus === void 0) {
418
+ retryable = false;
419
+ reason = "transport_timeout";
420
+ retryAfterMs = void 0;
421
+ }
422
+ if (body !== void 0 && base.httpStatus !== void 0) {
423
+ if (errorInfoReasons(body).some((r) => API_KEY_REASONS.has(r))) {
424
+ kind = "invalid_auth";
425
+ retryable = false;
426
+ } else if (base.httpStatus === 403 && body.message?.startsWith(STALE_CACHE_MESSAGE) === true) {
427
+ kind = "bad_request";
428
+ retryable = false;
429
+ reason = "cache_not_found";
430
+ } else if (base.httpStatus === 429) {
431
+ if (isDailyQuota(body)) {
432
+ kind = "rate_limited";
433
+ retryable = false;
434
+ reason = "daily_quota";
435
+ retryAfterMs = void 0;
436
+ } else {
437
+ retryAfterMs = retryDelayMs(body) ?? retryAfterMs;
438
+ }
439
+ }
440
+ }
441
+ return new core.LlmError(message, {
442
+ kind,
443
+ retryable,
444
+ ...reason !== void 0 ? { reason } : {},
173
445
  ...base.httpStatus !== void 0 ? { httpStatus: base.httpStatus } : {},
174
- ...base.retryAfterMs !== void 0 ? { retryAfterMs: base.retryAfterMs } : {},
446
+ ...retryAfterMs !== void 0 ? { retryAfterMs } : {},
175
447
  provider: "google",
176
448
  cause: base.cause ?? rawErr,
177
449
  ...extra?.servedServiceTier !== void 0 ? { servedServiceTier: extra.servedServiceTier } : {}
178
450
  });
179
451
  }
180
452
 
453
+ // src/platform-scheduler.ts
454
+ var PLATFORM_SCHEDULER = {
455
+ setTimeout: (callback, ms) => globalThis.setTimeout(callback, ms),
456
+ clearTimeout: (handle) => {
457
+ globalThis.clearTimeout(handle);
458
+ }
459
+ };
460
+
461
+ // src/pricing.ts
462
+ var pricingVersion = "gemini-2026-10-03";
463
+ var GEMINI_PRICED_TIERS = ["standard", "flex"];
464
+ function freezeRates(rates) {
465
+ if (rates.gt200k !== void 0) Object.freeze(rates.gt200k);
466
+ if (rates.audio !== void 0) Object.freeze(rates.audio);
467
+ return Object.freeze(rates);
468
+ }
469
+ function tiers(standard, flex) {
470
+ return Object.freeze({ standard: freezeRates(standard), flex: freezeRates(flex) });
471
+ }
472
+ var GEMINI_PRICING = Object.freeze({
473
+ // Gemini 2.5 Pro. Flex cached equals standard on both context bands. No separate audio price.
474
+ "gemini-2.5-pro": tiers(
475
+ {
476
+ inputPerM: 125e4,
477
+ cachedPerM: 125e3,
478
+ outputPerM: 1e7,
479
+ gt200k: { inputPerM: 25e5, cachedPerM: 25e4, outputPerM: 15e6 }
480
+ },
481
+ {
482
+ inputPerM: 625e3,
483
+ cachedPerM: 125e3,
484
+ outputPerM: 5e6,
485
+ gt200k: { inputPerM: 125e4, cachedPerM: 25e4, outputPerM: 75e5 }
486
+ }
487
+ ),
488
+ // Gemini 2.5 Flash. Flex cached stays $0.03. Audio: $1.00 / cached $0.10 standard,
489
+ // $0.50 / cached $0.10 flex.
490
+ "gemini-2.5-flash": tiers(
491
+ {
492
+ inputPerM: 3e5,
493
+ cachedPerM: 3e4,
494
+ outputPerM: 25e5,
495
+ audio: { inputPerM: 1e6, cachedPerM: 1e5 }
496
+ },
497
+ {
498
+ inputPerM: 15e4,
499
+ cachedPerM: 3e4,
500
+ outputPerM: 125e4,
501
+ audio: { inputPerM: 5e5, cachedPerM: 1e5 }
502
+ }
503
+ ),
504
+ // Gemini 2.5 Flash-Lite. Flex cached stays $0.01. Audio: $0.30 / cached $0.03
505
+ // standard, $0.15 / cached $0.03 flex.
506
+ "gemini-2.5-flash-lite": tiers(
507
+ {
508
+ inputPerM: 1e5,
509
+ cachedPerM: 1e4,
510
+ outputPerM: 4e5,
511
+ audio: { inputPerM: 3e5, cachedPerM: 3e4 }
512
+ },
513
+ {
514
+ inputPerM: 5e4,
515
+ cachedPerM: 1e4,
516
+ outputPerM: 2e5,
517
+ audio: { inputPerM: 15e4, cachedPerM: 3e4 }
518
+ }
519
+ ),
520
+ // Gemini 3.1 Flash-Lite. Flex cached is the published $0.0125. Audio: $0.50 /
521
+ // cached $0.05 standard, $0.25 / cached $0.025 flex.
522
+ "gemini-3.1-flash-lite": tiers(
523
+ {
524
+ inputPerM: 25e4,
525
+ cachedPerM: 25e3,
526
+ outputPerM: 15e5,
527
+ audio: { inputPerM: 5e5, cachedPerM: 5e4 }
528
+ },
529
+ {
530
+ inputPerM: 125e3,
531
+ cachedPerM: 12500,
532
+ outputPerM: 75e4,
533
+ audio: { inputPerM: 25e4, cachedPerM: 25e3 }
534
+ }
535
+ ),
536
+ // Gemini 3.8 / 3.7 / 3.6 Flash intro rates (2026-09-25), one rate for all
537
+ // modalities. Flex cached is half of the intro cached rate. Re-snapshot on 2027-01-01.
538
+ "gemini-3.8-flash": tiers(
539
+ { inputPerM: 75e4, cachedPerM: 75e3, outputPerM: 375e4 },
540
+ { inputPerM: 375e3, cachedPerM: 37500, outputPerM: 1875e3 }
541
+ ),
542
+ "gemini-3.7-flash": tiers(
543
+ { inputPerM: 75e4, cachedPerM: 75e3, outputPerM: 375e4 },
544
+ { inputPerM: 375e3, cachedPerM: 37500, outputPerM: 1875e3 }
545
+ ),
546
+ "gemini-3.6-flash": tiers(
547
+ { inputPerM: 75e4, cachedPerM: 75e3, outputPerM: 375e4 },
548
+ { inputPerM: 375e3, cachedPerM: 37500, outputPerM: 1875e3 }
549
+ ),
550
+ // Gemini 3.5 Flash-Lite, one rate for all modalities (audio included). Flex
551
+ // cached is the published $0.02, not half of $0.03.
552
+ "gemini-3.5-flash-lite": tiers(
553
+ { inputPerM: 3e5, cachedPerM: 3e4, outputPerM: 25e5 },
554
+ { inputPerM: 15e4, cachedPerM: 2e4, outputPerM: 125e4 }
555
+ ),
556
+ // Gemini 3.1 Pro Preview. Flex cached equals standard on both bands. No separate audio price.
557
+ "gemini-3.1-pro-preview": tiers(
558
+ {
559
+ inputPerM: 2e6,
560
+ cachedPerM: 2e5,
561
+ outputPerM: 12e6,
562
+ gt200k: { inputPerM: 4e6, cachedPerM: 4e5, outputPerM: 18e6 }
563
+ },
564
+ {
565
+ inputPerM: 1e6,
566
+ cachedPerM: 2e5,
567
+ outputPerM: 6e6,
568
+ gt200k: { inputPerM: 2e6, cachedPerM: 4e5, outputPerM: 9e6 }
569
+ }
570
+ )
571
+ });
572
+ var PER_QUERY = Object.freeze({
573
+ unit: "query",
574
+ microUsdPerUnit: 14e3
575
+ });
576
+ var PER_GROUNDED_PROMPT = Object.freeze({
577
+ unit: "prompt",
578
+ microUsdPerUnit: 35e3
579
+ });
580
+ var GEMINI_GROUNDING_PRICING = Object.freeze({
581
+ "gemini-2.5-pro": PER_GROUNDED_PROMPT,
582
+ "gemini-2.5-flash": PER_GROUNDED_PROMPT,
583
+ "gemini-2.5-flash-lite": PER_GROUNDED_PROMPT,
584
+ "gemini-3.1-flash-lite": PER_QUERY,
585
+ "gemini-3.8-flash": PER_QUERY,
586
+ "gemini-3.7-flash": PER_QUERY,
587
+ "gemini-3.6-flash": PER_QUERY,
588
+ "gemini-3.5-flash-lite": PER_QUERY,
589
+ "gemini-3.1-pro-preview": PER_QUERY
590
+ });
591
+ function resolveGeminiGroundingRate(model) {
592
+ return Object.hasOwn(GEMINI_GROUNDING_PRICING, model) ? GEMINI_GROUNDING_PRICING[model] : void 0;
593
+ }
594
+ function isPricedGeminiTier(tier) {
595
+ return GEMINI_PRICED_TIERS.includes(tier);
596
+ }
597
+ function lookupGeminiTierRates(model, tier) {
598
+ const entry = Object.hasOwn(GEMINI_PRICING, model) ? GEMINI_PRICING[model] : void 0;
599
+ if (entry === void 0) return void 0;
600
+ if (tier !== void 0 && !isPricedGeminiTier(tier)) return void 0;
601
+ return entry;
602
+ }
603
+ function resolveGeminiRates(model, tier) {
604
+ const entry = lookupGeminiTierRates(model, tier);
605
+ if (entry === void 0) return void 0;
606
+ const key = tier ?? "standard";
607
+ return entry[key];
608
+ }
609
+
610
+ // src/cost.ts
611
+ function tokenDetail(usage, key) {
612
+ const value = usage.details[key];
613
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : void 0;
614
+ }
615
+ function modalitySplitTotal(usage, prefix) {
616
+ let total;
617
+ for (const [key, value] of Object.entries(usage.details)) {
618
+ if (key.startsWith(prefix) && Number.isFinite(value) && value >= 0) {
619
+ total = (total ?? 0) + value;
620
+ }
621
+ }
622
+ return total;
623
+ }
624
+ function promptLanes(usage) {
625
+ const total = usage.inputTokens;
626
+ const cachedReported = usage.cachedInputTokens ?? 0;
627
+ const promptAudio = tokenDetail(usage, "input_audio");
628
+ const cachedAudio = tokenDetail(usage, "cached_audio");
629
+ const gaps = [];
630
+ const gap = (reason) => {
631
+ if (!gaps.includes(reason)) gaps.push(reason);
632
+ };
633
+ const cached = Math.min(cachedReported, total);
634
+ if (cachedReported > total) gap("inconsistent");
635
+ const audioCached = Math.min(cachedAudio ?? 0, cached);
636
+ if ((cachedAudio ?? 0) > cached) gap("inconsistent");
637
+ if (promptAudio !== void 0 && promptAudio < audioCached) gap("inconsistent");
638
+ const audioTotal = Math.min(Math.max(promptAudio ?? 0, audioCached), total);
639
+ if ((promptAudio ?? 0) > total) gap("inconsistent");
640
+ const audioUncached = Math.min(audioTotal - audioCached, total - cached);
641
+ if (audioTotal - audioCached > total - cached) gap("inconsistent");
642
+ if (usage.details["audio_input_requested"] === 1 && !((promptAudio ?? 0) > 0)) {
643
+ gap("audio-unreported");
644
+ }
645
+ const promptSplit = modalitySplitTotal(usage, "input_");
646
+ const promptHoldsNoAudio = promptAudio === 0 || promptAudio === void 0 && promptSplit !== void 0 && promptSplit >= total;
647
+ if (cached > 0 && cachedAudio === void 0 && !promptHoldsNoAudio) {
648
+ gap("cached-audio-unknown");
649
+ }
650
+ return {
651
+ audioUncached,
652
+ audioCached,
653
+ otherUncached: total - cached - audioUncached,
654
+ otherCached: cached - audioCached,
655
+ gaps
656
+ };
657
+ }
658
+ function priceCall(model, usage, tier) {
659
+ if (usage.details["usage_missing"] === 1) {
660
+ return {
661
+ microUsd: null,
662
+ usd: null,
663
+ pricingVersion,
664
+ confidence: "estimated",
665
+ details: { input: 0, cached: 0, output: 0, tools: 0 },
666
+ unpricedReason: "The response carried no usageMetadata, so the tokens billed are unknown."
667
+ };
668
+ }
669
+ const modelRates = resolveGeminiRates(model, tier);
670
+ const audioRates = modelRates?.audio;
671
+ let cost;
672
+ if (audioRates === void 0) {
673
+ cost = core.computeCost(model, usage, tier, resolveGeminiRates, pricingVersion);
674
+ } else {
675
+ const lanes = promptLanes(usage);
676
+ const textUsage = {
677
+ ...usage,
678
+ inputTokens: lanes.otherUncached + lanes.otherCached,
679
+ cachedInputTokens: lanes.otherCached
680
+ };
681
+ const text = core.computeCost(model, textUsage, tier, resolveGeminiRates, pricingVersion);
682
+ const audioUncachedCost = Math.round(
683
+ lanes.audioUncached * audioRates.inputPerM / 1e6
684
+ );
685
+ const audioCachedCost = Math.round(
686
+ lanes.audioCached * audioRates.cachedPerM / 1e6
687
+ );
688
+ const microUsd2 = (text.microUsd ?? 0) + audioUncachedCost + audioCachedCost;
689
+ cost = {
690
+ ...text,
691
+ microUsd: microUsd2,
692
+ usd: microUsd2 / 1e6,
693
+ // An audio share the response does not pin down can understate the amount.
694
+ ...lanes.gaps.length > 0 ? { confidence: "estimated" } : {},
695
+ details: {
696
+ ...text.details,
697
+ input: text.details.input + audioUncachedCost,
698
+ cached: text.details.cached + audioCachedCost
699
+ }
700
+ };
701
+ }
702
+ if (cost.microUsd === null) return cost;
703
+ if (usage.details["web_search_requested"] !== 1) return cost;
704
+ const calls = usage.details["web_search_calls"];
705
+ if (calls === 0) return cost;
706
+ const rate = resolveGeminiGroundingRate(model);
707
+ const tools = rate === void 0 || calls === void 0 ? 0 : rate.unit === "query" ? Math.round(calls * rate.microUsdPerUnit) : rate.microUsdPerUnit;
708
+ const microUsd = cost.microUsd + tools;
709
+ return {
710
+ ...cost,
711
+ microUsd,
712
+ usd: microUsd / 1e6,
713
+ confidence: "estimated",
714
+ details: { ...cost.details, tools }
715
+ };
716
+ }
717
+ function geminiPricingSource() {
718
+ return {
719
+ version: pricingVersion,
720
+ price(model, usage, tier) {
721
+ return priceCall(model, usage, tier);
722
+ },
723
+ hasModel(model) {
724
+ return resolveGeminiRates(model, void 0) !== void 0;
725
+ },
726
+ listModels() {
727
+ return Object.keys(GEMINI_PRICING);
728
+ }
729
+ };
730
+ }
731
+
732
+ // src/utf8.ts
733
+ function utf8ByteLength(text) {
734
+ let bytes = 0;
735
+ for (let i = 0; i < text.length; i += 1) {
736
+ const unit = text.charCodeAt(i);
737
+ if (unit < 128) bytes += 1;
738
+ else if (unit < 2048) bytes += 2;
739
+ else if (unit >= 55296 && unit <= 56319) {
740
+ const next = text.charCodeAt(i + 1);
741
+ if (next >= 56320 && next <= 57343) {
742
+ bytes += 4;
743
+ i += 1;
744
+ } else bytes += 3;
745
+ } else bytes += 3;
746
+ }
747
+ return bytes;
748
+ }
749
+ var STATE_PATH = "transientProviderState";
750
+ var SHA256_HEX = /^[0-9a-f]{64}$/;
751
+ var ENTRY_KEYS = /* @__PURE__ */ new Set([
752
+ "messageIndex",
753
+ "partIndex",
754
+ "kind",
755
+ "model",
756
+ "partSha256",
757
+ "signature"
758
+ ]);
759
+ function badState(path, why) {
760
+ return new core.LlmError(`${path}: ${why}`, {
761
+ kind: "bad_request",
762
+ retryable: false,
763
+ provider: "google",
764
+ issues: [{ path, message: why }]
765
+ });
766
+ }
767
+ function isRecord2(value) {
768
+ return typeof value === "object" && value !== null && !Array.isArray(value);
769
+ }
770
+ function hashedForm(part, path) {
771
+ switch (part.kind) {
772
+ case "text":
773
+ return { kind: "text", text: part.text };
774
+ case "tool-call":
775
+ return {
776
+ kind: "tool-call",
777
+ toolCallId: part.toolCallId,
778
+ toolName: part.toolName,
779
+ args: part.args
780
+ };
781
+ default:
782
+ throw badState(
783
+ path,
784
+ `a thought signature cannot be attached to a "${part.kind}" part`
785
+ );
786
+ }
787
+ }
788
+ function partSha256(part, path = "part") {
789
+ return core.sha256Hex(core.canonicalJson(hashedForm(part, path)));
790
+ }
791
+ function parseSignatureState(value) {
792
+ if (value === void 0) return [];
793
+ if (!isRecord2(value)) {
794
+ throw badState(STATE_PATH, "must be { google: { signatures: [...] } }");
795
+ }
796
+ for (const key of Object.keys(value)) {
797
+ if (key !== "google") {
798
+ throw badState(
799
+ `${STATE_PATH}.${key}`,
800
+ `holds another provider's state; Google state is { google: { signatures: [...] } }`
801
+ );
802
+ }
803
+ }
804
+ const google = value["google"];
805
+ if (!isRecord2(google)) {
806
+ throw badState(`${STATE_PATH}.google`, "must be { signatures: [...] }");
807
+ }
808
+ for (const key of Object.keys(google)) {
809
+ if (key !== "signatures") {
810
+ throw badState(`${STATE_PATH}.google.${key}`, "is not a known key");
811
+ }
812
+ }
813
+ const list = google["signatures"];
814
+ if (!Array.isArray(list)) {
815
+ throw badState(`${STATE_PATH}.google.signatures`, "must be an array");
816
+ }
817
+ return list.map((raw, index) => {
818
+ const path = `${STATE_PATH}.google.signatures.${index}`;
819
+ if (!isRecord2(raw)) throw badState(path, "must be an object");
820
+ for (const key of Object.keys(raw)) {
821
+ if (!ENTRY_KEYS.has(key)) throw badState(`${path}.${key}`, "is not a known key");
822
+ }
823
+ const { messageIndex, partIndex, kind, model, partSha256: digest, signature } = raw;
824
+ if (typeof messageIndex !== "number" || !Number.isInteger(messageIndex) || messageIndex < 0) {
825
+ throw badState(`${path}.messageIndex`, "must be a non-negative integer");
826
+ }
827
+ if (typeof partIndex !== "number" || !Number.isInteger(partIndex) || partIndex < 0) {
828
+ throw badState(`${path}.partIndex`, "must be a non-negative integer");
829
+ }
830
+ if (kind !== "text" && kind !== "tool-call") {
831
+ throw badState(`${path}.kind`, 'must be "text" or "tool-call"');
832
+ }
833
+ if (typeof model !== "string" || model.length === 0) {
834
+ throw badState(`${path}.model`, "must be a non-empty string");
835
+ }
836
+ if (typeof digest !== "string" || !SHA256_HEX.test(digest)) {
837
+ throw badState(`${path}.partSha256`, "must be 64 lowercase hex characters");
838
+ }
839
+ if (typeof signature !== "string" || signature.length === 0) {
840
+ throw badState(`${path}.signature`, "must be a non-empty string");
841
+ }
842
+ return { messageIndex, partIndex, kind, model, partSha256: digest, signature };
843
+ });
844
+ }
845
+ var STALE = "history was edited, reordered or produced by another model after the signature was issued";
846
+ function resolveSignatures(entries, messages, model) {
847
+ const bySlot = /* @__PURE__ */ new Map();
848
+ const kept = [];
849
+ const dropped = [];
850
+ const seen = /* @__PURE__ */ new Set();
851
+ for (const [index, entry] of entries.entries()) {
852
+ const path = `${STATE_PATH}.google.signatures.${index}`;
853
+ const slot = `${entry.messageIndex}:${entry.partIndex}`;
854
+ const where = `messages.${entry.messageIndex}.parts.${entry.partIndex}`;
855
+ const stale = (field, why) => {
856
+ if (entry.kind === "tool-call") throw badState(`${path}.${field}`, why);
857
+ dropped.push(`${where}: ${why}`);
858
+ };
859
+ if (seen.has(slot)) throw badState(path, `duplicates the entry for ${where}`);
860
+ seen.add(slot);
861
+ if (entry.model !== model) {
862
+ stale(
863
+ "model",
864
+ `was issued for model "${entry.model}" but this request names "${model}"; signatures are not replayed across models (${STALE})`
865
+ );
866
+ continue;
867
+ }
868
+ const message = messages[entry.messageIndex];
869
+ if (message === void 0 || message.role !== "assistant") {
870
+ stale(
871
+ "messageIndex",
872
+ `does not point at an assistant message in messages (${STALE})`
873
+ );
874
+ continue;
875
+ }
876
+ const part = message.parts[entry.partIndex];
877
+ if (part === void 0) {
878
+ stale("partIndex", `is out of range for the message (${STALE})`);
879
+ continue;
880
+ }
881
+ if (part.kind !== entry.kind) {
882
+ stale(
883
+ "kind",
884
+ `is for a "${entry.kind}" part but ${where} is a "${part.kind}" part (${STALE})`
885
+ );
886
+ continue;
887
+ }
888
+ if (partSha256(part, where) !== entry.partSha256) {
889
+ stale("partSha256", `does not match ${where} (${STALE})`);
890
+ continue;
891
+ }
892
+ bySlot.set(slot, entry.signature);
893
+ kept.push(entry);
894
+ }
895
+ for (const [mi, message] of messages.entries()) {
896
+ if (message.role !== "assistant") continue;
897
+ const first = message.parts.findIndex((part) => part.kind === "tool-call");
898
+ if (first === -1 || bySlot.has(`${mi}:${first}`)) continue;
899
+ const call = message.parts[first];
900
+ throw badState(
901
+ `messages.${mi}.parts.${first}`,
902
+ `replays tool call "${call?.kind === "tool-call" ? call.toolCallId : ""}" without its thought signature in transientProviderState; Gemini 3 rejects a function call that lost it. Send the transientProviderState from the result that produced this message, and the history unedited, with the same model string. If you removed messages from the history, remove their entries with dropMessagesFromSignatureState`
903
+ );
904
+ }
905
+ return { bySlot, kept, dropped };
906
+ }
907
+ function signatureEntry(messageIndex, partIndex, model, part, signature) {
908
+ if (part.kind !== "text" && part.kind !== "tool-call") {
909
+ throw badState(
910
+ `messages.${messageIndex}.parts.${partIndex}`,
911
+ `a thought signature cannot be attached to a "${part.kind}" part`
912
+ );
913
+ }
914
+ return {
915
+ messageIndex,
916
+ partIndex,
917
+ kind: part.kind,
918
+ model,
919
+ partSha256: partSha256(part),
920
+ signature
921
+ };
922
+ }
923
+ function dropMessagesFromSignatureState(state, indices) {
924
+ const removed = /* @__PURE__ */ new Set();
925
+ for (const [i, value] of indices.entries()) {
926
+ if (!Number.isInteger(value) || value < 0) {
927
+ throw new core.LlmError(
928
+ `dropMessagesFromSignatureState: indices.${i} must be a non-negative integer.`,
929
+ {
930
+ kind: "bad_request",
931
+ retryable: false,
932
+ issues: [{ path: `indices.${i}`, message: "must be a non-negative integer" }]
933
+ }
934
+ );
935
+ }
936
+ if (removed.has(value)) {
937
+ throw new core.LlmError(
938
+ `dropMessagesFromSignatureState: indices.${i} repeats message ${value}.`,
939
+ {
940
+ kind: "bad_request",
941
+ retryable: false,
942
+ issues: [{ path: `indices.${i}`, message: "is a duplicate" }]
943
+ }
944
+ );
945
+ }
946
+ removed.add(value);
947
+ }
948
+ const sorted = [...removed].sort((a, b) => a - b);
949
+ const signatures = [];
950
+ for (const entry of parseSignatureState(state)) {
951
+ if (removed.has(entry.messageIndex)) continue;
952
+ const shift = sorted.filter((index) => index < entry.messageIndex).length;
953
+ signatures.push({ ...entry, messageIndex: entry.messageIndex - shift });
954
+ }
955
+ return signatures.length > 0 ? { google: { signatures } } : void 0;
956
+ }
957
+
181
958
  // src/adapter.ts
182
959
  var ALLOWED_GOOGLE_PROVIDER_OPTION_KEYS = /* @__PURE__ */ new Set([
960
+ "allowSchemaWithSearch",
183
961
  "cachedContent",
184
962
  "flexFallback",
185
963
  "httpOptions",
964
+ "requireGrounding",
186
965
  "safetySettings",
187
966
  "tools"
188
967
  ]);
@@ -243,6 +1022,8 @@ function parseGoogleTool(tool, model) {
243
1022
  );
244
1023
  }
245
1024
  }
1025
+ var SAFETY_CATEGORY_SET = new Set(GOOGLE_SAFETY_CATEGORIES);
1026
+ var SAFETY_THRESHOLD_SET = new Set(GOOGLE_SAFETY_THRESHOLDS);
246
1027
  function parseGoogleSafetySetting(setting, index, model) {
247
1028
  if (!isPlainRecord(setting)) {
248
1029
  throw badGoogleProviderOptions(
@@ -258,14 +1039,14 @@ function parseGoogleSafetySetting(setting, index, model) {
258
1039
  )}] for model "${model}". Allowed keys: category, threshold.`
259
1040
  );
260
1041
  }
261
- if (typeof setting["category"] !== "string" || setting["category"].length === 0) {
1042
+ if (typeof setting["category"] !== "string" || !SAFETY_CATEGORY_SET.has(setting["category"])) {
262
1043
  throw badGoogleProviderOptions(
263
- `providerOptions.google.safetySettings[${index}].category must be a non-empty string for model "${model}".`
1044
+ `providerOptions.google.safetySettings[${index}].category must be one of ${GOOGLE_SAFETY_CATEGORIES.join(", ")} for model "${model}".`
264
1045
  );
265
1046
  }
266
- if (typeof setting["threshold"] !== "string" || setting["threshold"].length === 0) {
1047
+ if (typeof setting["threshold"] !== "string" || !SAFETY_THRESHOLD_SET.has(setting["threshold"])) {
267
1048
  throw badGoogleProviderOptions(
268
- `providerOptions.google.safetySettings[${index}].threshold must be a non-empty string for model "${model}".`
1049
+ `providerOptions.google.safetySettings[${index}].threshold must be one of ${GOOGLE_SAFETY_THRESHOLDS.join(", ")} for model "${model}".`
269
1050
  );
270
1051
  }
271
1052
  return {
@@ -273,11 +1054,28 @@ function parseGoogleSafetySetting(setting, index, model) {
273
1054
  threshold: setting["threshold"]
274
1055
  };
275
1056
  }
1057
+ function describeType(value) {
1058
+ return value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
1059
+ }
1060
+ function noSchemaWithSearchEvidence(model) {
1061
+ return `Structured output with googleSearch is not supported for model "${model}": no live capture shows Search running when a response schema is attached to this model (the captures cover Gemini 3.x only), so there is no measured behaviour to opt into and providerOptions.google.allowSchemaWithSearch does not apply. Make two calls instead: grounded research without a schema, then structured synthesis (the two-call recipe in docs/grounded-structured.md).`;
1062
+ }
1063
+ function assertSchemaWithSearchAllowed(model, structuredOutputWithTools, allowSchemaWithSearch, declaredBy) {
1064
+ if (structuredOutputWithTools === void 0) {
1065
+ throw badGoogleProviderOptions(noSchemaWithSearchEvidence(model));
1066
+ }
1067
+ if (allowSchemaWithSearch !== true) {
1068
+ throw badGoogleProviderOptions(
1069
+ `Structured output with googleSearch is not enabled for model "${model}" (Search is declared by ${declaredBy}): the provider accepts the request but Search does not reliably run when a response schema is attached. Make two calls instead: grounded research without a schema, then structured synthesis (the two-call recipe in docs/grounded-structured.md). To send both in one call anyway, set providerOptions.google.allowSchemaWithSearch: true; the call then fails unless the response proves Search ran (requireGrounding), and that failure is not retryable because the same call keeps missing.`
1070
+ );
1071
+ }
1072
+ }
276
1073
  function mapGoogleProviderOptions({
277
1074
  googleOpts,
278
1075
  model,
279
1076
  structuredOutputRequested,
280
1077
  descriptorGrounding,
1078
+ descriptorCaching,
281
1079
  structuredOutputWithTools
282
1080
  }) {
283
1081
  if (googleOpts === void 0) {
@@ -299,23 +1097,43 @@ function mapGoogleProviderOptions({
299
1097
  );
300
1098
  }
301
1099
  const unknownKeys = Object.keys(googleOpts).filter(
302
- (key) => !ALLOWED_GOOGLE_PROVIDER_OPTION_KEYS.has(key) && !RESERVED_GOOGLE_PROVIDER_OPTION_KEYS.has(key)
1100
+ (key) => !ALLOWED_GOOGLE_PROVIDER_OPTION_KEYS.has(key)
303
1101
  );
304
1102
  if (unknownKeys.length > 0) {
305
1103
  throw badGoogleProviderOptions(
306
1104
  `providerOptions.google contains unsupported keys [${unknownKeys.join(
307
1105
  ", "
308
- )}] for model "${model}". Allowed keys: cachedContent, flexFallback, httpOptions, safetySettings, tools.`
1106
+ )}] for model "${model}". Allowed keys: allowSchemaWithSearch, cachedContent, flexFallback, httpOptions, requireGrounding, safetySettings, tools.`
309
1107
  );
310
1108
  }
311
1109
  const mapped = {};
312
1110
  if (googleOpts["cachedContent"] !== void 0) {
313
- if (typeof googleOpts["cachedContent"] !== "string" || googleOpts["cachedContent"].length === 0) {
1111
+ if (!descriptorCaching) {
1112
+ throw badGoogleProviderOptions(
1113
+ `providerOptions.google.cachedContent is not supported for model "${model}": the model has no explicit-caching capability.`
1114
+ );
1115
+ }
1116
+ const cached = googleOpts["cachedContent"];
1117
+ if (typeof cached === "string" && cached.length > 0) {
1118
+ mapped.cachedContent = cached;
1119
+ } else if (isPlainRecord(cached)) {
1120
+ const extraKeys = Object.keys(cached).filter(
1121
+ (key) => key !== "cacheName" && key !== "toolKinds"
1122
+ );
1123
+ const cacheName = cached["cacheName"];
1124
+ const toolKinds = cached["toolKinds"];
1125
+ if (extraKeys.length > 0 || typeof cacheName !== "string" || cacheName.length === 0 || toolKinds !== void 0 && (!Array.isArray(toolKinds) || !toolKinds.every((kind) => typeof kind === "string"))) {
1126
+ throw badGoogleProviderOptions(
1127
+ `providerOptions.google.cachedContent must be a non-empty cache name, or { cacheName: non-empty string, toolKinds?: string[] } and nothing else (pass handle.cacheName and handle.toolKinds, not the whole handle), for model "${model}".`
1128
+ );
1129
+ }
1130
+ mapped.cachedContent = cacheName;
1131
+ if (toolKinds !== void 0) mapped.cachedToolKinds = toolKinds;
1132
+ } else {
314
1133
  throw badGoogleProviderOptions(
315
- `providerOptions.google.cachedContent must be a non-empty string for model "${model}".`
1134
+ `providerOptions.google.cachedContent must be a non-empty cache name, or { cacheName: non-empty string, toolKinds?: string[] }, for model "${model}".`
316
1135
  );
317
1136
  }
318
- mapped.cachedContent = googleOpts["cachedContent"];
319
1137
  }
320
1138
  if (googleOpts["flexFallback"] !== void 0) {
321
1139
  if (typeof googleOpts["flexFallback"] !== "boolean") {
@@ -343,9 +1161,9 @@ function mapGoogleProviderOptions({
343
1161
  }
344
1162
  const timeout = googleOpts["httpOptions"]["timeout"];
345
1163
  if (timeout !== void 0) {
346
- if (typeof timeout !== "number" || !Number.isInteger(timeout) || timeout <= 0) {
1164
+ if (typeof timeout !== "number" || !Number.isInteger(timeout) || timeout <= 0 || timeout > MAX_TIMER_MS) {
347
1165
  throw badGoogleProviderOptions(
348
- `providerOptions.google.httpOptions.timeout must be a positive integer for model "${model}".`
1166
+ `providerOptions.google.httpOptions.timeout must be a positive integer of at most ${MAX_TIMER_MS} ms (the longest delay a Node timer holds; a larger one aborts the call after 1 ms) for model "${model}".`
349
1167
  );
350
1168
  }
351
1169
  mapped.httpOptions = { timeout };
@@ -363,6 +1181,18 @@ function mapGoogleProviderOptions({
363
1181
  (setting, index) => parseGoogleSafetySetting(setting, index, model)
364
1182
  );
365
1183
  }
1184
+ const allowSchemaWithSearch = googleOpts["allowSchemaWithSearch"];
1185
+ if (allowSchemaWithSearch !== void 0 && typeof allowSchemaWithSearch !== "boolean") {
1186
+ throw badGoogleProviderOptions(
1187
+ `providerOptions.google.allowSchemaWithSearch must be a boolean for model "${model}", received ${describeType(allowSchemaWithSearch)}.`
1188
+ );
1189
+ }
1190
+ const requireGrounding = googleOpts["requireGrounding"];
1191
+ if (requireGrounding !== void 0 && typeof requireGrounding !== "boolean") {
1192
+ throw badGoogleProviderOptions(
1193
+ `providerOptions.google.requireGrounding must be a boolean for model "${model}", received ${describeType(requireGrounding)}.`
1194
+ );
1195
+ }
366
1196
  if (googleOpts["tools"] !== void 0) {
367
1197
  if (!Array.isArray(googleOpts["tools"])) {
368
1198
  throw badGoogleProviderOptions(
@@ -376,12 +1206,54 @@ function mapGoogleProviderOptions({
376
1206
  );
377
1207
  }
378
1208
  if (structuredOutputRequested && structuredOutputWithTools !== true) {
379
- throw badGoogleProviderOptions(
380
- `Structured output with googleSearch is not supported for model "${model}".`
1209
+ assertSchemaWithSearchAllowed(
1210
+ model,
1211
+ structuredOutputWithTools,
1212
+ allowSchemaWithSearch,
1213
+ "providerOptions.google.tools"
381
1214
  );
382
1215
  }
383
1216
  mapped.tools = tools;
384
1217
  }
1218
+ const searchInCache = mapped.cachedToolKinds?.includes("googleSearch") === true;
1219
+ if (searchInCache) {
1220
+ if (descriptorGrounding !== true) {
1221
+ throw badGoogleProviderOptions(
1222
+ `providerOptions.google.cachedContent.toolKinds lists googleSearch, which is not supported for model "${model}": the model does not support grounding.`
1223
+ );
1224
+ }
1225
+ if (structuredOutputRequested && structuredOutputWithTools !== true) {
1226
+ assertSchemaWithSearchAllowed(
1227
+ model,
1228
+ structuredOutputWithTools,
1229
+ allowSchemaWithSearch,
1230
+ "the cache handle (cachedContent.toolKinds)"
1231
+ );
1232
+ }
1233
+ }
1234
+ const searchSent = searchInCache || mapped.tools?.some((tool) => "googleSearch" in tool) === true;
1235
+ if (allowSchemaWithSearch === true) {
1236
+ if (descriptorGrounding !== true) {
1237
+ throw badGoogleProviderOptions(
1238
+ `providerOptions.google.allowSchemaWithSearch is not supported for model "${model}": the model does not support grounding.`
1239
+ );
1240
+ }
1241
+ if (!searchSent || !structuredOutputRequested) {
1242
+ throw badGoogleProviderOptions(
1243
+ `providerOptions.google.allowSchemaWithSearch requires both Search (providerOptions.google.tools: [{ googleSearch: {} }], or a cachedContent handle whose toolKinds lists googleSearch) and output.jsonSchema for model "${model}".`
1244
+ );
1245
+ }
1246
+ if (structuredOutputWithTools === void 0) {
1247
+ throw badGoogleProviderOptions(noSchemaWithSearchEvidence(model));
1248
+ }
1249
+ }
1250
+ if (requireGrounding === true && !searchSent) {
1251
+ throw badGoogleProviderOptions(
1252
+ `providerOptions.google.requireGrounding requires Search: providerOptions.google.tools: [{ googleSearch: {} }], or a cachedContent handle whose toolKinds lists googleSearch, for model "${model}".`
1253
+ );
1254
+ }
1255
+ const effectiveRequireGrounding = requireGrounding ?? allowSchemaWithSearch === true;
1256
+ if (effectiveRequireGrounding) mapped.requireGrounding = true;
385
1257
  return mapped;
386
1258
  }
387
1259
  function assertSamplingAllowed(config, model, sampling) {
@@ -405,6 +1277,99 @@ function assertSamplingAllowed(config, model, sampling) {
405
1277
  );
406
1278
  }
407
1279
  }
1280
+ var MAX_INLINE_REQUEST_BYTES = 100 * 1024 * 1024;
1281
+ var MAX_INLINE_PDF_BYTES = 50 * 1024 * 1024;
1282
+ var PDF_MEDIA_TYPES = ["application/pdf"];
1283
+ function assertInlinePayloadWithinLimits(contents, system) {
1284
+ let total = system === void 0 ? 0 : utf8ByteLength(system);
1285
+ contents.forEach((content, mi) => {
1286
+ content.parts.forEach((part, pi) => {
1287
+ if ("text" in part) total += utf8ByteLength(part.text);
1288
+ if (!("inlineData" in part)) return;
1289
+ const { mimeType, data } = part.inlineData;
1290
+ total += data.length;
1291
+ if (core.isMediaTypeAdmitted(mimeType, PDF_MEDIA_TYPES)) {
1292
+ const decoded = Math.floor(data.length * 3 / 4);
1293
+ if (decoded > MAX_INLINE_PDF_BYTES) {
1294
+ throw new core.LlmError(
1295
+ `messages[${mi}].parts[${pi}] is an inline PDF of about ${decoded} bytes, over Google's 50 MB inline PDF limit. Upload it with GoogleFileStore and send a file-uri part.`,
1296
+ {
1297
+ kind: "bad_request",
1298
+ retryable: false,
1299
+ provider: "google",
1300
+ issues: [
1301
+ {
1302
+ path: `messages[${mi}].parts[${pi}]`,
1303
+ message: "inline PDF over 50 MB"
1304
+ }
1305
+ ]
1306
+ }
1307
+ );
1308
+ }
1309
+ }
1310
+ });
1311
+ });
1312
+ if (total > MAX_INLINE_REQUEST_BYTES) {
1313
+ throw new core.LlmError(
1314
+ `The request carries at least ${total} bytes of inline data and text, over Google's 100 MB request limit. Upload large media with GoogleFileStore and send file-uri parts.`,
1315
+ {
1316
+ kind: "bad_request",
1317
+ retryable: false,
1318
+ provider: "google",
1319
+ issues: [{ path: "messages", message: "request over 100 MB" }]
1320
+ }
1321
+ );
1322
+ }
1323
+ }
1324
+ var CANDIDATE_METADATA_KEYS = [
1325
+ "finishReason",
1326
+ "finishMessage",
1327
+ "safetyRatings",
1328
+ "citationMetadata",
1329
+ "urlContextMetadata"
1330
+ ];
1331
+ var MAX_FINISH_MESSAGE_CHARS = 512;
1332
+ var MAX_METADATA_ARRAY = 50;
1333
+ var MAX_METADATA_STRING = 2048;
1334
+ var MAX_METADATA_DEPTH = 8;
1335
+ var MAX_RATINGS_IN_MESSAGE = 12;
1336
+ function truncateText(text, max) {
1337
+ return text.length > max ? `${text.slice(0, max)}\u2026` : text;
1338
+ }
1339
+ function boundMetadata(value, state, depth = 0) {
1340
+ if (typeof value === "string") {
1341
+ if (value.length > MAX_METADATA_STRING) state.truncated = true;
1342
+ return truncateText(value, MAX_METADATA_STRING);
1343
+ }
1344
+ if (value === null || typeof value !== "object") return value;
1345
+ if (depth >= MAX_METADATA_DEPTH) {
1346
+ state.truncated = true;
1347
+ return null;
1348
+ }
1349
+ if (Array.isArray(value)) {
1350
+ if (value.length > MAX_METADATA_ARRAY) state.truncated = true;
1351
+ return value.slice(0, MAX_METADATA_ARRAY).map((item) => boundMetadata(item, state, depth + 1));
1352
+ }
1353
+ const out = {};
1354
+ for (const [key, item] of Object.entries(value)) {
1355
+ if (item !== void 0) out[key] = boundMetadata(item, state, depth + 1);
1356
+ }
1357
+ return out;
1358
+ }
1359
+ function describeSafetyRatings(ratings) {
1360
+ if (!Array.isArray(ratings) || ratings.length === 0) return "";
1361
+ const parts = ratings.slice(0, MAX_RATINGS_IN_MESSAGE).flatMap((rating) => {
1362
+ if (typeof rating !== "object" || rating === null) return [];
1363
+ const { category, probability, blocked } = rating;
1364
+ if (typeof category !== "string") return [];
1365
+ return [
1366
+ `${truncateText(category, 80)}=${typeof probability === "string" ? truncateText(probability, 40) : "unknown"}${blocked === true ? " (blocked)" : ""}`
1367
+ ];
1368
+ });
1369
+ if (parts.length === 0) return "";
1370
+ const more = ratings.length > MAX_RATINGS_IN_MESSAGE ? ` and ${ratings.length - MAX_RATINGS_IN_MESSAGE} more` : "";
1371
+ return `${parts.join(", ")}${more}`;
1372
+ }
408
1373
  function mapFinishReason(raw) {
409
1374
  if (raw === void 0) return void 0;
410
1375
  switch (raw) {
@@ -416,7 +1381,10 @@ function mapFinishReason(raw) {
416
1381
  case "RECITATION":
417
1382
  case "BLOCKLIST":
418
1383
  case "PROHIBITED_CONTENT":
1384
+ case "SPII":
419
1385
  case "IMAGE_SAFETY":
1386
+ case "IMAGE_PROHIBITED_CONTENT":
1387
+ case "IMAGE_RECITATION":
420
1388
  return "content_filter";
421
1389
  default:
422
1390
  return "other";
@@ -439,6 +1407,7 @@ function mapUsage(meta) {
439
1407
  const candidatesTokenCount = meta?.candidatesTokenCount ?? 0;
440
1408
  const cachedContentTokenCount = meta?.cachedContentTokenCount;
441
1409
  const thoughtsTokenCount = meta?.thoughtsTokenCount;
1410
+ const toolUsePromptTokenCount = meta?.toolUsePromptTokenCount;
442
1411
  const outputTokens = candidatesTokenCount + (thoughtsTokenCount ?? 0);
443
1412
  const inputTokens = promptTokenCount;
444
1413
  const totalTokens = meta?.totalTokenCount;
@@ -446,8 +1415,36 @@ function mapUsage(meta) {
446
1415
  input: inputTokens,
447
1416
  output: outputTokens,
448
1417
  ...cachedContentTokenCount !== void 0 ? { cached: cachedContentTokenCount } : {},
449
- ...thoughtsTokenCount !== void 0 ? { thinking: thoughtsTokenCount } : {}
1418
+ ...thoughtsTokenCount !== void 0 ? { thinking: thoughtsTokenCount } : {},
1419
+ // Tokens of Search results fed back to the model. They sit in
1420
+ // `totalTokenCount` but outside `promptTokenCount`. Google's pricing page
1421
+ // (read 2026-10) says retrieved search results are not charged
1422
+ // as input tokens, so they are recorded and not priced; no live billing
1423
+ // reconciliation has confirmed it, so the total mismatch still marks the
1424
+ // cost `estimated`.
1425
+ ...toolUsePromptTokenCount !== void 0 ? { tool_use_prompt: toolUsePromptTokenCount } : {}
450
1426
  };
1427
+ let cachedSplitTokens;
1428
+ for (const [prefix, entries] of [
1429
+ ["input", meta?.promptTokensDetails],
1430
+ ["cached", meta?.cacheTokensDetails]
1431
+ ]) {
1432
+ if (!Array.isArray(entries)) continue;
1433
+ if (prefix === "cached") cachedSplitTokens = 0;
1434
+ for (const entry of entries) {
1435
+ if (typeof entry !== "object" || entry === null) continue;
1436
+ const { modality, tokenCount: count } = entry;
1437
+ if (typeof modality !== "string" || modality === "" || typeof count !== "number" || !Number.isFinite(count) || count < 0) {
1438
+ continue;
1439
+ }
1440
+ const key = `${prefix}_${modality.toLowerCase()}`;
1441
+ details[key] = (details[key] ?? 0) + count;
1442
+ if (prefix === "cached") cachedSplitTokens = (cachedSplitTokens ?? 0) + count;
1443
+ }
1444
+ }
1445
+ if (cachedSplitTokens !== void 0 && cachedContentTokenCount !== void 0 && cachedContentTokenCount > 0 && cachedSplitTokens >= cachedContentTokenCount && details["cached_audio"] === void 0) {
1446
+ details["cached_audio"] = 0;
1447
+ }
451
1448
  const raw = meta !== void 0 ? meta : null;
452
1449
  const usage = {
453
1450
  inputTokens,
@@ -466,10 +1463,20 @@ function mapGoogleToolChoice(choice) {
466
1463
  if (choice === "none") return { mode: "NONE" };
467
1464
  return { mode: "ANY", allowedFunctionNames: [choice.name] };
468
1465
  }
469
- function mapPart(p) {
1466
+ function toFunctionResponseObject(p) {
1467
+ if (p.isError === true) return { error: p.result };
1468
+ if (typeof p.result === "object" && p.result !== null && !Array.isArray(p.result)) {
1469
+ return p.result;
1470
+ }
1471
+ return { output: p.result };
1472
+ }
1473
+ function mapPart(p, signature) {
470
1474
  switch (p.kind) {
471
1475
  case "text":
472
- return { text: p.text };
1476
+ return {
1477
+ text: p.text,
1478
+ ...signature !== void 0 ? { thoughtSignature: signature } : {}
1479
+ };
473
1480
  case "inline-media": {
474
1481
  return {
475
1482
  inlineData: {
@@ -496,30 +1503,35 @@ function mapPart(p) {
496
1503
  case "tool-call":
497
1504
  return {
498
1505
  functionCall: {
499
- id: p.toolCallId,
1506
+ // A synthesized id never reached Gemini; sending it would only be noise.
1507
+ ...isSynthesizedToolCallId(p.toolCallId) ? {} : { id: p.toolCallId },
500
1508
  name: p.toolName,
501
1509
  args: p.args
502
- }
1510
+ },
1511
+ ...signature !== void 0 ? { thoughtSignature: signature } : {}
503
1512
  };
504
1513
  case "tool-result":
505
1514
  return {
506
1515
  functionResponse: {
507
- id: p.toolCallId,
1516
+ ...isSynthesizedToolCallId(p.toolCallId) ? {} : { id: p.toolCallId },
508
1517
  name: p.toolName,
509
- response: p.isError === true ? { error: p.result } : p.result
1518
+ response: toFunctionResponseObject(p)
510
1519
  }
511
1520
  };
512
1521
  default:
513
1522
  return core.assertNever(p);
514
1523
  }
515
1524
  }
516
- function mapMessagesToGeminiContents(messages) {
517
- return messages.map((msg) => ({
1525
+ function mapMessagesToGeminiContents(messages, signatures) {
1526
+ return messages.map((msg, mi) => ({
518
1527
  role: msg.role === "assistant" ? "model" : "user",
519
- parts: msg.parts.map(mapPart)
1528
+ parts: msg.parts.map((part, pi) => mapPart(part, signatures?.get(`${mi}:${pi}`)))
520
1529
  }));
521
1530
  }
522
1531
  function geminiAdapter(opts) {
1532
+ return geminiAdapterWithClientFactory(opts?.client, buildGoogleClient);
1533
+ }
1534
+ function geminiAdapterWithClientFactory(injectedClient, buildClient) {
523
1535
  return {
524
1536
  id: "google",
525
1537
  async run(req, ctx) {
@@ -532,23 +1544,37 @@ function geminiAdapter(opts) {
532
1544
  const warnings = [];
533
1545
  const model = req.model;
534
1546
  const descriptor = req.modelDescriptor;
535
- if (descriptor?.model !== model || descriptor.provider !== "google") {
536
- throw new core.LlmError(`No matching Google model descriptor for "${model}".`, {
1547
+ core.assertModelMatchesDescriptor(req, descriptor, "google");
1548
+ core.assertInputMimeTypesAdmitted(req.messages, descriptor, "google");
1549
+ const signsHistory = descriptor.capabilities?.providerState === true;
1550
+ if (req.transientProviderState !== void 0 && !signsHistory) {
1551
+ throw new core.LlmError(`Model "${model}" does not admit transientProviderState.`, {
537
1552
  kind: "bad_request",
538
1553
  retryable: false
539
1554
  });
540
1555
  }
541
- if (req.transientProviderState !== void 0) {
542
- throw new core.LlmError(`Model "${model}" does not admit transientProviderState.`, {
543
- kind: "bad_request",
544
- retryable: false
1556
+ const resolved = signsHistory ? resolveSignatures(
1557
+ parseSignatureState(req.transientProviderState),
1558
+ req.messages,
1559
+ model
1560
+ ) : void 0;
1561
+ const incomingSignatures = resolved?.kept ?? [];
1562
+ if (resolved !== void 0 && resolved.dropped.length > 0) {
1563
+ warnings.push({
1564
+ type: "other",
1565
+ message: `google: dropped ${resolved.dropped.length} stale text signature(s) from transientProviderState (${resolved.dropped.join("; ")}); Google treats text signatures as optional, so nothing required was lost.`
545
1566
  });
546
1567
  }
547
- const contents = mapMessagesToGeminiContents(req.messages);
1568
+ const contents = mapMessagesToGeminiContents(
1569
+ req.messages,
1570
+ resolved?.bySlot
1571
+ );
1572
+ const system = req.system !== void 0 && req.system !== "" ? req.system : void 0;
1573
+ assertInlinePayloadWithinLimits(contents, system);
548
1574
  const genConfig = req.config;
549
1575
  const config = {};
550
- if (req.system !== void 0) {
551
- config.systemInstruction = { parts: [{ text: req.system }] };
1576
+ if (system !== void 0) {
1577
+ config.systemInstruction = { parts: [{ text: system }] };
552
1578
  }
553
1579
  if (genConfig.temperature !== void 0) {
554
1580
  config.temperature = genConfig.temperature;
@@ -605,6 +1631,12 @@ function geminiAdapter(opts) {
605
1631
  );
606
1632
  }
607
1633
  const budget = reasoning.budgetTokens !== void 0 ? reasoning.budgetTokens : reasoning.effort !== void 0 ? GOOGLE_REASONING_EFFORT_BUDGET[reasoning.effort] : void 0;
1634
+ if (budget !== void 0 && budget > 0 && genConfig.maxOutputTokens !== void 0 && budget >= genConfig.maxOutputTokens) {
1635
+ warnings.push({
1636
+ type: "other",
1637
+ message: `google: thinkingBudget (${budget}) is not below maxOutputTokens (${genConfig.maxOutputTokens}); thinking may consume the whole cap and leave no answer. Raise maxOutputTokens or lower the reasoning budget.`
1638
+ });
1639
+ }
608
1640
  config.thinkingConfig = {
609
1641
  ...budget !== void 0 ? { thinkingBudget: budget } : {},
610
1642
  ...reasoning.includeThoughts === true ? { includeThoughts: true } : {}
@@ -648,6 +1680,12 @@ function geminiAdapter(opts) {
648
1680
  core.assertNever(reasoning.effort);
649
1681
  }
650
1682
  }
1683
+ if (thinkingLevel === "HIGH" && genConfig.maxOutputTokens !== void 0 && genConfig.maxOutputTokens < GOOGLE_HIGH_EFFORT_MIN_OUTPUT_TOKENS) {
1684
+ warnings.push({
1685
+ type: "other",
1686
+ message: `google: reasoning.effort "high" can spend several thousand thinking tokens (measured up to 8,859) and maxOutputTokens is ${genConfig.maxOutputTokens}, below ${GOOGLE_HIGH_EFFORT_MIN_OUTPUT_TOKENS}; thinking may consume the whole cap and leave no answer. Raise maxOutputTokens or lower the effort.`
1687
+ });
1688
+ }
651
1689
  config.thinkingConfig = {
652
1690
  ...thinkingLevel !== void 0 ? { thinkingLevel } : {},
653
1691
  ...reasoning.includeThoughts === true ? { includeThoughts: true } : {}
@@ -661,24 +1699,148 @@ function geminiAdapter(opts) {
661
1699
  }
662
1700
  const structuredOutputRequested = req.outputJsonSchema !== void 0;
663
1701
  if (structuredOutputRequested) {
664
- const nativeStructuredOutput = descriptor.capabilities?.nativeStructuredOutput !== false;
665
- if (nativeStructuredOutput) {
666
- config.responseMimeType = "application/json";
667
- config.responseSchema = req.outputJsonSchema;
1702
+ if (descriptor.capabilities?.nativeStructuredOutput === false) {
1703
+ throw new core.LlmError(
1704
+ `output.jsonSchema is not supported for model "${model}": the model has no native structured output and the Google adapter has no other path.`,
1705
+ {
1706
+ kind: "bad_request",
1707
+ retryable: false,
1708
+ provider: "google",
1709
+ issues: [
1710
+ {
1711
+ path: "output.jsonSchema",
1712
+ message: "model has no native structured output"
1713
+ }
1714
+ ]
1715
+ }
1716
+ );
668
1717
  }
1718
+ core.assertJsonSchemaProfile(
1719
+ req.outputJsonSchema,
1720
+ "output.jsonSchema",
1721
+ googleJsonSchemaProfile(descriptor.model)
1722
+ );
1723
+ config.responseMimeType = "application/json";
1724
+ config.responseJsonSchema = req.outputJsonSchema;
669
1725
  }
670
1726
  const googleProviderConfig = mapGoogleProviderOptions({
671
1727
  googleOpts: genConfig.providerOptions?.["google"],
672
1728
  model,
673
1729
  structuredOutputRequested,
674
1730
  descriptorGrounding: descriptor.capabilities?.grounding,
1731
+ descriptorCaching: descriptor.capabilities?.caching !== void 0,
675
1732
  structuredOutputWithTools: descriptor.capabilities?.structuredOutputWithTools
676
1733
  });
1734
+ const googleSearchSent = googleProviderConfig.tools?.some((tool) => "googleSearch" in tool) === true;
1735
+ const searchDeclared = googleSearchSent || googleProviderConfig.cachedToolKinds?.includes("googleSearch") === true;
1736
+ const requireGrounding = googleProviderConfig.requireGrounding === true;
1737
+ const schemaSearchUndeclared = structuredOutputRequested && !searchDeclared && descriptor.capabilities?.structuredOutputWithTools !== true;
1738
+ const audioRequested = req.messages.some(
1739
+ (message) => message.parts.some(
1740
+ (part) => (part.kind === "inline-media" || part.kind === "file-uri") && part.mimeType.toLowerCase().startsWith("audio/")
1741
+ )
1742
+ );
1743
+ const mapUsageWithAudioMarker = (meta) => {
1744
+ const mapped = mapUsage(meta);
1745
+ if (audioRequested) mapped.details["audio_input_requested"] = 1;
1746
+ return mapped;
1747
+ };
1748
+ const usageFor = (meta, groundingMetadata2) => {
1749
+ const mapped = mapUsageWithAudioMarker(meta);
1750
+ const queries = countWebSearchQueries(groundingMetadata2);
1751
+ if (searchDeclared) {
1752
+ mapped.details["web_search_requested"] = 1;
1753
+ if (queries !== void 0) mapped.details["web_search_calls"] = queries;
1754
+ } else if (groundingMetadata2 !== void 0) {
1755
+ mapped.details["web_search_requested"] = 1;
1756
+ if (queries !== void 0 && queries > 0) {
1757
+ mapped.details["web_search_calls"] = queries;
1758
+ }
1759
+ }
1760
+ return mapped;
1761
+ };
1762
+ const groundingWarnings = (groundingMetadata2) => {
1763
+ if (!searchDeclared) {
1764
+ if (groundingMetadata2 === void 0) return [];
1765
+ const queries = countWebSearchQueries(groundingMetadata2);
1766
+ const undeclaredSearchWarnings = schemaSearchUndeclared && queries !== void 0 && queries > 0 ? [
1767
+ {
1768
+ type: "other",
1769
+ message: `google: this call attached a response schema and the response reports search queries, so Search ran from the cache named in cachedContent. Schema plus Search is not admitted by default for model "${model}" (Search does not reliably run when a schema is attached) and no googleSearch was declared, so nothing checked that it would run or opted in; the result is returned, the Search fee is priced from the observed queries (cost.confidence is "estimated"), and this pattern is unreliable. Declare Search with cachedContent: { cacheName, toolKinds: ['googleSearch'] } together with allowSchemaWithSearch: true (the call then fails unless the response proves Search ran), or use the two-call recipe in docs/grounded-structured.md.`
1770
+ }
1771
+ ] : [];
1772
+ return [
1773
+ ...undeclaredSearchWarnings,
1774
+ {
1775
+ type: "other",
1776
+ message: queries !== void 0 && queries > 0 ? `google: the response reports ${queries} search quer${queries === 1 ? "y" : "ies"} but the request did not declare googleSearch (a cachedContent cache can hold the tool); the Search fee was priced from the observed queries, and cost.confidence is "estimated" because the free allowance is unknowable per call.` : 'google: the response carries groundingMetadata but the request did not declare googleSearch and the metadata names no query, so the number of searches is unknown; grounding fees are not included in cost, so cost.confidence is "estimated".'
1777
+ }
1778
+ ];
1779
+ }
1780
+ if (groundingMetadata2 === void 0) {
1781
+ return [
1782
+ {
1783
+ type: "other",
1784
+ message: 'google: googleSearch was requested (sent, or held by the cache named in cachedContent) but the response carries no groundingMetadata, so Search may not have run, or may have run without being reported; grounding fees are not included in cost, so cost.confidence is "estimated".'
1785
+ }
1786
+ ];
1787
+ }
1788
+ if (countWebSearchQueries(groundingMetadata2) === void 0) {
1789
+ return [
1790
+ {
1791
+ type: "other",
1792
+ message: 'google: groundingMetadata has no webSearchQueries, so the number of searches is unknown; grounding fees are not included in cost, so cost.confidence is "estimated".'
1793
+ }
1794
+ ];
1795
+ }
1796
+ return [];
1797
+ };
1798
+ const modalityWarnings = (meta) => {
1799
+ if (meta === void 0) return [];
1800
+ const warnings2 = [];
1801
+ const { gaps } = promptLanes(mapUsageWithAudioMarker(meta));
1802
+ if (gaps.includes("audio-unreported")) {
1803
+ warnings2.push({
1804
+ type: "other",
1805
+ message: 'google: the request carries audio but usageMetadata.promptTokensDetails reports no AUDIO tokens, so the audio input rate could not be applied; on a model that prices audio apart from text, cost.confidence is "estimated" and the amount can understate.'
1806
+ });
1807
+ }
1808
+ if (gaps.includes("cached-audio-unknown")) {
1809
+ warnings2.push({
1810
+ type: "other",
1811
+ message: 'google: usageMetadata reports cached tokens without showing how many are audio (no AUDIO entry in cacheTokensDetails, and no per-modality prompt split that rules audio out), so audio in the cached content cannot be ruled out; on a model that prices audio apart from text, cost.confidence is "estimated" and the amount can understate.'
1812
+ });
1813
+ }
1814
+ if (gaps.includes("inconsistent")) {
1815
+ warnings2.push({
1816
+ type: "other",
1817
+ message: 'google: the per-modality token counts in usageMetadata contradict each other (audio above the prompt or the cache, or lanes that exceed the prompt); the counts were clamped, and on a model that prices audio apart from text, cost.confidence is "estimated".'
1818
+ });
1819
+ }
1820
+ return warnings2;
1821
+ };
1822
+ const billedFailure = (meta, groundingMetadata2) => {
1823
+ if (meta === void 0) return {};
1824
+ const failureWarnings = [
1825
+ ...groundingWarnings(groundingMetadata2),
1826
+ ...modalityWarnings(meta)
1827
+ ];
1828
+ return {
1829
+ usage: usageFor(meta, groundingMetadata2),
1830
+ ...failureWarnings.length > 0 ? { warnings: failureWarnings } : {}
1831
+ };
1832
+ };
677
1833
  if (googleProviderConfig.cachedContent !== void 0) {
678
1834
  config.cachedContent = googleProviderConfig.cachedContent;
679
1835
  }
680
1836
  if (googleProviderConfig.httpOptions !== void 0) {
681
1837
  config.httpOptions = googleProviderConfig.httpOptions;
1838
+ const sdkTimeout = googleProviderConfig.httpOptions.timeout;
1839
+ if (sdkTimeout !== void 0 && genConfig.timeoutMs !== void 0 && sdkTimeout < genConfig.timeoutMs + TRANSPORT_TIMEOUT_BUFFER_MS) {
1840
+ throw badGoogleProviderOptions(
1841
+ `providerOptions.google.httpOptions.timeout (${sdkTimeout} ms) must be at least timeoutMs + ${TRANSPORT_TIMEOUT_BUFFER_MS} ms (${genConfig.timeoutMs + TRANSPORT_TIMEOUT_BUFFER_MS} ms) for model "${model}": a shorter SDK timer would abort the call before the engine's own timeout and surface a raw SDK abort.`
1842
+ );
1843
+ }
682
1844
  }
683
1845
  if (googleProviderConfig.safetySettings !== void 0) {
684
1846
  config.safetySettings = googleProviderConfig.safetySettings;
@@ -687,7 +1849,7 @@ function geminiAdapter(opts) {
687
1849
  config.tools = googleProviderConfig.tools;
688
1850
  }
689
1851
  if (req.tools !== void 0 && req.tools.length > 0) {
690
- if (req.modelDescriptor?.capabilities?.functionCalling !== true) {
1852
+ if (descriptor.capabilities?.functionCalling !== true) {
691
1853
  throw new core.LlmError(
692
1854
  `tools is not supported for google model "${model}" (capabilities.functionCalling is not true).`,
693
1855
  { kind: "bad_request", retryable: false, provider: "google" }
@@ -695,16 +1857,24 @@ function geminiAdapter(opts) {
695
1857
  }
696
1858
  if (googleProviderConfig.tools !== void 0) {
697
1859
  throw new core.LlmError(
698
- "tools cannot be combined with providerOptions.google.tools (googleSearch) in this iteration.",
1860
+ "tools cannot be combined with providerOptions.google.tools (googleSearch): this adapter does not send function declarations and Search in one request.",
699
1861
  { kind: "bad_request", retryable: false, provider: "google" }
700
1862
  );
701
1863
  }
1864
+ const toolProfile = googleJsonSchemaProfile(descriptor.model);
1865
+ req.tools.forEach((tool, index) => {
1866
+ core.assertJsonSchemaProfile(
1867
+ tool.inputJsonSchema,
1868
+ `tools[${index}].inputJsonSchema`,
1869
+ toolProfile
1870
+ );
1871
+ });
702
1872
  config.tools = [
703
1873
  {
704
1874
  functionDeclarations: req.tools.map((tool) => ({
705
1875
  name: tool.name,
706
1876
  description: tool.description,
707
- parameters: tool.inputJsonSchema
1877
+ parametersJsonSchema: tool.inputJsonSchema
708
1878
  }))
709
1879
  }
710
1880
  ];
@@ -714,11 +1884,37 @@ function geminiAdapter(opts) {
714
1884
  };
715
1885
  }
716
1886
  }
717
- assertSamplingAllowed(config, model, req.modelDescriptor?.capabilities?.sampling);
1887
+ if (config.cachedContent !== void 0) {
1888
+ const conflicts = [
1889
+ ...system !== void 0 ? ["system"] : [],
1890
+ ...req.tools !== void 0 && req.tools.length > 0 ? ["tools"] : [],
1891
+ ...googleProviderConfig.tools !== void 0 ? ["providerOptions.google.tools"] : []
1892
+ ];
1893
+ if (conflicts.length > 0) {
1894
+ throw new core.LlmError(
1895
+ `providerOptions.google.cachedContent cannot be combined with ${conflicts.join(
1896
+ " or "
1897
+ )} for model "${model}": Gemini requires the system instruction and tools to be stored in the cache. Put them in GoogleCacheStore.create and omit them from the request.`,
1898
+ {
1899
+ kind: "bad_request",
1900
+ retryable: false,
1901
+ provider: "google",
1902
+ issues: conflicts.map((path) => ({
1903
+ path,
1904
+ message: "cannot be sent with cachedContent"
1905
+ }))
1906
+ }
1907
+ );
1908
+ }
1909
+ }
1910
+ assertSamplingAllowed(config, model, descriptor.capabilities?.sampling);
1911
+ const timers = ctx.scheduler ?? PLATFORM_SCHEDULER;
718
1912
  let tierTimeoutHandle;
1913
+ let ceilingFiredMs;
719
1914
  const clearTierTimeout = () => {
1915
+ ceilingFiredMs = void 0;
720
1916
  if (tierTimeoutHandle !== void 0) {
721
- clearTimeout(tierTimeoutHandle);
1917
+ timers.clearTimeout(tierTimeoutHandle);
722
1918
  tierTimeoutHandle = void 0;
723
1919
  }
724
1920
  };
@@ -733,7 +1929,8 @@ function geminiAdapter(opts) {
733
1929
  `${tierLabel} timeout: call exceeded ${defaultTimeoutMs}ms client-side ceiling (@google/genai #1277 belt-and-suspenders)`,
734
1930
  "TimeoutError"
735
1931
  );
736
- tierTimeoutHandle = setTimeout(() => {
1932
+ tierTimeoutHandle = timers.setTimeout(() => {
1933
+ ceilingFiredMs = defaultTimeoutMs;
737
1934
  tierController.abort(timeoutReason);
738
1935
  }, defaultTimeoutMs);
739
1936
  config.abortSignal = ctx.signal !== void 0 ? AbortSignal.any([tierController.signal, ctx.signal]) : tierController.signal;
@@ -763,8 +1960,7 @@ function geminiAdapter(opts) {
763
1960
  contents,
764
1961
  config: dispatchConfig
765
1962
  };
766
- const buildClient = opts?._clientFactory ?? buildGoogleClient;
767
- const client = opts?.client !== void 0 ? opts.client : await buildClient(ctx.auth);
1963
+ const client = injectedClient !== void 0 ? injectedClient : await buildClient(ctx.auth);
768
1964
  ctx.logger.debug(
769
1965
  {
770
1966
  model,
@@ -775,16 +1971,32 @@ function geminiAdapter(opts) {
775
1971
  );
776
1972
  return client.models.generateContent(params);
777
1973
  };
1974
+ const transportTimeoutOf = (rawErr) => {
1975
+ if (ctx.signal?.aborted === true) return void 0;
1976
+ if (ceilingFiredMs !== void 0) {
1977
+ return `Google call hit the ${ceilingFiredMs}ms client-side ceiling`;
1978
+ }
1979
+ const sdkTimeoutMs = config.httpOptions?.timeout;
1980
+ if (sdkTimeoutMs !== void 0 && rawErr instanceof Error && rawErr.name === "AbortError") {
1981
+ return `Google call hit the SDK transport timeout of ${sdkTimeoutMs}ms`;
1982
+ }
1983
+ return void 0;
1984
+ };
778
1985
  try {
779
1986
  response = await dispatch();
780
1987
  } catch (rawErr) {
781
- const typed = classifyGoogleError(
782
- rawErr,
783
- servedServiceTier !== void 0 ? { servedServiceTier } : void 0
784
- );
1988
+ const transportTimeout = transportTimeoutOf(rawErr);
1989
+ const typed = classifyGoogleError(rawErr, {
1990
+ ...servedServiceTier !== void 0 ? { servedServiceTier } : {},
1991
+ ...transportTimeout !== void 0 ? { transportTimeout } : {}
1992
+ });
785
1993
  if (config.serviceTier === "flex" && googleProviderConfig.flexFallback !== false && isGeminiCapacityError(typed)) {
786
1994
  config.serviceTier = "standard";
787
1995
  servedServiceTier = "standard";
1996
+ warnings.push({
1997
+ type: "other",
1998
+ message: `google: the flex call hit a capacity error (${typed.httpStatus ?? "no status"}) and was sent again at the standard tier, billed at standard rates.${genConfig.timeoutMs === void 0 ? ` Without timeoutMs the standard attempt runs under the ${STANDARD_DEFAULT_TIMEOUT_MS} ms client-side ceiling, not the flex ${FLEX_DEFAULT_TIMEOUT_MS} ms one.` : ""}`
1999
+ });
788
2000
  const fallbackTimeout = genConfig.timeoutMs !== void 0 ? genConfig.timeoutMs + TRANSPORT_TIMEOUT_BUFFER_MS : STANDARD_DEFAULT_TIMEOUT_MS;
789
2001
  config.httpOptions = {
790
2002
  timeout: fallbackTimeout,
@@ -794,7 +2006,11 @@ function geminiAdapter(opts) {
794
2006
  try {
795
2007
  response = await dispatch();
796
2008
  } catch (fallbackRawErr) {
797
- throw classifyGoogleError(fallbackRawErr, { servedServiceTier: "standard" });
2009
+ const fallbackTransportTimeout = transportTimeoutOf(fallbackRawErr);
2010
+ throw classifyGoogleError(fallbackRawErr, {
2011
+ servedServiceTier: "standard",
2012
+ ...fallbackTransportTimeout !== void 0 ? { transportTimeout: fallbackTransportTimeout } : {}
2013
+ });
798
2014
  }
799
2015
  } else {
800
2016
  throw typed;
@@ -813,49 +2029,105 @@ function geminiAdapter(opts) {
813
2029
  servedServiceTier = echoedTier;
814
2030
  }
815
2031
  const hasBlockReason = response.promptFeedback?.blockReason !== void 0;
816
- const hasCandidates = response.candidates !== void 0 && response.candidates.length > 0;
817
- if (hasBlockReason || !hasCandidates) {
2032
+ const candidate = response.candidates?.[0];
2033
+ if (hasBlockReason || candidate === void 0) {
818
2034
  const reason = response.promptFeedback?.blockReason ?? "NO_CANDIDATES";
819
- throw new core.LlmError(`Gemini response has no usable candidate: ${reason}`, {
820
- kind: hasBlockReason ? "content_filter" : "server",
821
- retryable: !hasBlockReason,
822
- provider: "google",
823
- ...response.usageMetadata !== void 0 ? { usage: mapUsage(response.usageMetadata) } : {},
824
- ...servedServiceTier !== void 0 ? { servedServiceTier } : {}
825
- });
2035
+ const thoughtTokens = response.usageMetadata?.thoughtsTokenCount ?? 0;
2036
+ const reasoningHint = !hasBlockReason && thoughtTokens > 0 ? `. The call billed ${thoughtTokens} reasoning tokens, and maxOutputTokens (${config.maxOutputTokens ?? "the provider default"}) includes reasoning tokens, so a low cap can be used up by reasoning before any answer is produced` : "";
2037
+ const promptRatings = hasBlockReason ? describeSafetyRatings(response.promptFeedback?.safetyRatings) : "";
2038
+ throw new core.LlmError(
2039
+ `Gemini response has no usable candidate: ${reason}${promptRatings !== "" ? ` (safetyRatings: ${promptRatings})` : ""}${reasoningHint}`,
2040
+ {
2041
+ kind: hasBlockReason ? "content_filter" : "server",
2042
+ retryable: !hasBlockReason && thoughtTokens === 0,
2043
+ provider: "google",
2044
+ ...billedFailure(response.usageMetadata),
2045
+ ...servedServiceTier !== void 0 ? { servedServiceTier } : {}
2046
+ }
2047
+ );
826
2048
  }
827
- const candidates = response.candidates;
828
- if (candidates === void 0 || candidates.length === 0) {
829
- throw new core.LlmError("Gemini response has no usable candidate: NO_CANDIDATES", {
830
- kind: "server",
831
- retryable: true,
832
- provider: "google",
833
- ...response.usageMetadata !== void 0 ? { usage: mapUsage(response.usageMetadata) } : {},
834
- ...servedServiceTier !== void 0 ? { servedServiceTier } : {}
835
- });
2049
+ const groundingMetadata = candidate.groundingMetadata;
2050
+ const parts = candidate.content?.parts ?? [];
2051
+ const callsComplete = candidate.finishReason === void 0 || candidate.finishReason === "STOP";
2052
+ const isFunctionCall = (part) => part.functionCall !== void 0 && typeof part.functionCall.name === "string";
2053
+ const hasAnswer = parts.some(
2054
+ (part) => part.thought !== true && typeof part.text === "string" && part.text.length > 0 || callsComplete && isFunctionCall(part)
2055
+ );
2056
+ const filteredCandidateError = (note) => {
2057
+ const finishMessage = candidate.finishMessage !== void 0 ? truncateText(candidate.finishMessage, MAX_FINISH_MESSAGE_CHARS) : void 0;
2058
+ const ratings = describeSafetyRatings(candidate.safetyRatings);
2059
+ const bounded = { truncated: false };
2060
+ return new core.LlmError(
2061
+ `Gemini candidate was filtered (finishReason ${candidate.finishReason}${finishMessage !== void 0 ? `: ${finishMessage}` : ""}${ratings !== "" ? `; safetyRatings: ${ratings}` : ""}); ${note}. The attempt was billed.`,
2062
+ {
2063
+ kind: "content_filter",
2064
+ retryable: false,
2065
+ provider: "google",
2066
+ // The raw evidence, bounded: which category blocked, and why.
2067
+ cause: {
2068
+ finishReason: candidate.finishReason ?? null,
2069
+ ...finishMessage !== void 0 ? { finishMessage } : {},
2070
+ ...candidate.safetyRatings !== void 0 ? { safetyRatings: boundMetadata(candidate.safetyRatings, bounded) } : {}
2071
+ },
2072
+ ...billedFailure(response.usageMetadata, groundingMetadata),
2073
+ ...servedServiceTier !== void 0 ? { servedServiceTier } : {}
2074
+ }
2075
+ );
2076
+ };
2077
+ if (mapFinishReason(candidate.finishReason) === "content_filter" && !hasAnswer) {
2078
+ throw filteredCandidateError(
2079
+ "it carries no answer text and no complete tool call"
2080
+ );
836
2081
  }
837
- const candidate = candidates[0];
838
- if (candidate === void 0) {
839
- throw new core.LlmError("Gemini response has no usable candidate: NO_CANDIDATES", {
840
- kind: "server",
841
- retryable: true,
842
- provider: "google",
843
- ...response.usageMetadata !== void 0 ? { usage: mapUsage(response.usageMetadata) } : {},
844
- ...servedServiceTier !== void 0 ? { servedServiceTier } : {}
845
- });
2082
+ if (requireGrounding) {
2083
+ const queries = countWebSearchQueries(groundingMetadata);
2084
+ if (groundingMetadata === void 0 || queries === void 0 || queries < 1) {
2085
+ const finishReason2 = mapFinishReason(candidate.finishReason);
2086
+ if (finishReason2 === "content_filter") {
2087
+ throw filteredCandidateError("the grounding check was not applied");
2088
+ }
2089
+ if (candidate.finishReason === void 0 || candidate.finishReason === "STOP") {
2090
+ const why = groundingMetadata === void 0 ? "the response has no groundingMetadata" : queries === void 0 ? "groundingMetadata has no webSearchQueries" : "groundingMetadata reports zero webSearchQueries";
2091
+ const retryable = !structuredOutputRequested;
2092
+ throw new core.LlmError(
2093
+ `google: requireGrounding is set but there is no evidence that Search ran: ${why}. The attempt was billed for its tokens${retryable ? "; a retry may ground." : "; it is not retryable, because a call with a response schema attached keeps missing (use the two-call recipe in docs/grounded-structured.md)."}`,
2094
+ {
2095
+ kind: "server",
2096
+ retryable,
2097
+ reason: "grounding_missing",
2098
+ provider: "google",
2099
+ ...billedFailure(response.usageMetadata, groundingMetadata),
2100
+ ...servedServiceTier !== void 0 ? { servedServiceTier } : {}
2101
+ }
2102
+ );
2103
+ }
2104
+ }
846
2105
  }
847
- const parts = candidate.content?.parts ?? [];
848
2106
  const textParts = [];
849
2107
  const thoughtParts = [];
850
2108
  const toolCalls = [];
2109
+ const messageParts = [];
2110
+ const issuedSignatures = [];
2111
+ let droppedSignatures = 0;
2112
+ const droppedCalls = [];
851
2113
  const nameCounts = /* @__PURE__ */ new Map();
852
- const reservedIds = reserveProviderToolCallIds(
853
- parts.map((part) => part.functionCall?.id)
854
- );
2114
+ const reservedIds = reserveProviderToolCallIds([
2115
+ ...parts.map((part) => part.functionCall?.id),
2116
+ ...req.messages.flatMap(
2117
+ (message) => message.parts.flatMap(
2118
+ (part) => part.kind === "tool-call" || part.kind === "tool-result" ? [part.toolCallId] : []
2119
+ )
2120
+ )
2121
+ ]);
855
2122
  for (const part of parts) {
856
- if (part.functionCall !== void 0 && typeof part.functionCall.name === "string") {
2123
+ const signature = typeof part.thoughtSignature === "string" && part.thoughtSignature.length > 0 ? part.thoughtSignature : void 0;
2124
+ let represented = false;
2125
+ if (isFunctionCall(part) && !callsComplete) {
2126
+ droppedCalls.push(part.functionCall?.name ?? "");
2127
+ represented = true;
2128
+ } else if (part.functionCall !== void 0 && typeof part.functionCall.name === "string") {
857
2129
  const toolName = part.functionCall.name;
858
- toolCalls.push({
2130
+ const call = {
859
2131
  toolCallId: resolveToolCallId(
860
2132
  part.functionCall.id,
861
2133
  toolName,
@@ -864,31 +2136,115 @@ function geminiAdapter(opts) {
864
2136
  ),
865
2137
  toolName,
866
2138
  args: part.functionCall.args ?? {}
867
- });
2139
+ };
2140
+ toolCalls.push(call);
2141
+ if (signature !== void 0) {
2142
+ issuedSignatures.push({ partIndex: messageParts.length, signature });
2143
+ }
2144
+ messageParts.push({ kind: "tool-call", ...call });
2145
+ represented = true;
868
2146
  }
869
2147
  if (part.text !== void 0) {
870
2148
  if (part.thought === true) {
871
2149
  thoughtParts.push(part.text);
872
2150
  } else {
873
2151
  textParts.push(part.text);
2152
+ if (part.text.length > 0) {
2153
+ if (signature !== void 0 && !represented) {
2154
+ issuedSignatures.push({ partIndex: messageParts.length, signature });
2155
+ }
2156
+ messageParts.push({ kind: "text", text: part.text });
2157
+ represented = true;
2158
+ }
874
2159
  }
875
2160
  }
2161
+ if (signature !== void 0 && !represented) droppedSignatures += 1;
2162
+ }
2163
+ if (droppedCalls.length > 0) {
2164
+ const named = droppedCalls.map((name) => `"${name}"`).join(", ");
2165
+ warnings.push({
2166
+ type: "other",
2167
+ message: `google: dropped ${droppedCalls.length} function call(s) (${named}) because the candidate ended with finishReason ${candidate.finishReason ?? "unspecified"}, not STOP: ${candidate.finishReason === "MAX_TOKENS" ? "the call was cut by the output cap and is incomplete" : "a call beside an abnormal stop is not a call to run"}. The result carries no tool call; finishReason is "${mapFinishReason(candidate.finishReason) ?? "other"}".`
2168
+ });
876
2169
  }
877
2170
  const text = textParts.join("");
878
2171
  const reasoningText = thoughtParts.length > 0 ? thoughtParts.join("") : void 0;
2172
+ let transientProviderState;
2173
+ if (signsHistory) {
2174
+ const issued = [];
2175
+ for (const { partIndex, signature } of issuedSignatures) {
2176
+ const part = messageParts[partIndex];
2177
+ try {
2178
+ issued.push(
2179
+ signatureEntry(req.messages.length, partIndex, model, part, signature)
2180
+ );
2181
+ } catch (error) {
2182
+ if (!(error instanceof core.LlmError)) throw error;
2183
+ warnings.push({
2184
+ type: "other",
2185
+ message: `google: no signature entry for messages.${req.messages.length}.parts.${partIndex} (a "${part.kind}" part): ${error.message} The result is returned without it; ${part.kind === "tool-call" ? "replaying this function call on the next turn will be rejected" : "a text signature is optional, so nothing required is lost"}.`
2186
+ });
2187
+ }
2188
+ }
2189
+ const signatures = [...incomingSignatures, ...issued];
2190
+ if (signatures.length > 0) {
2191
+ transientProviderState = { google: { signatures } };
2192
+ }
2193
+ if (droppedSignatures > 0) {
2194
+ warnings.push({
2195
+ type: "other",
2196
+ message: `google: dropped ${droppedSignatures} thoughtSignature(s) on parts that have no message representation (thought or empty parts); Gemini requires only the function-call ones.`
2197
+ });
2198
+ }
2199
+ const firstCall = messageParts.findIndex((part) => part.kind === "tool-call");
2200
+ if (firstCall !== -1 && !issuedSignatures.some((entry) => entry.partIndex === firstCall)) {
2201
+ warnings.push({
2202
+ type: "other",
2203
+ message: "google: the first function call in this response carries no thoughtSignature; replaying it on the next turn will be rejected."
2204
+ });
2205
+ }
2206
+ }
879
2207
  let rawStructured;
880
2208
  if (structuredOutputRequested && text.length > 0) {
881
2209
  try {
882
2210
  rawStructured = JSON.parse(text);
883
2211
  } catch {
2212
+ if (descriptor.model.startsWith("gemma-") && /^\s*```/.test(text)) {
2213
+ warnings.push({
2214
+ type: "other",
2215
+ message: `google: gemma_fenced_json: the answer from "${model}" is wrapped in a markdown code fence, so it is not parseable JSON and outputParsed is false. Gemma 4 fenced 67 of 162 schema answers (41%) in a live probe (2026-10-03) although the schema is native. The text is returned as the model sent it; call again, or unwrap the fence on the host side.`
2216
+ });
2217
+ }
884
2218
  }
885
2219
  }
886
- const usage = mapUsage(response.usageMetadata);
2220
+ const usage = usageFor(response.usageMetadata, groundingMetadata);
2221
+ if (response.usageMetadata === void 0) {
2222
+ usage.details["usage_missing"] = 1;
2223
+ warnings.push({
2224
+ type: "other",
2225
+ message: 'google: the response carries no usageMetadata, so token usage is unknown; the call is recorded with zero tokens and is not priced (cost.microUsd is null, cost.confidence is "estimated").'
2226
+ });
2227
+ }
887
2228
  const finishReason = mapFinishReason(candidate.finishReason);
2229
+ warnings.push(...groundingWarnings(groundingMetadata));
2230
+ warnings.push(...modalityWarnings(response.usageMetadata));
2231
+ const answerParts = [];
2232
+ let answerOffset = 0;
2233
+ for (const part of parts) {
2234
+ if (part.thought === true) continue;
2235
+ if (typeof part.text === "string") {
2236
+ answerParts.push({ text: part.text, offset: answerOffset });
2237
+ answerOffset += part.text.length;
2238
+ } else {
2239
+ answerParts.push(void 0);
2240
+ }
2241
+ }
888
2242
  const result = {
889
2243
  model,
2244
+ message: { role: "assistant", parts: messageParts },
890
2245
  usage,
891
2246
  warnings,
2247
+ ...transientProviderState !== void 0 ? { transientProviderState } : {},
892
2248
  ...servedServiceTier !== void 0 ? { servedServiceTier } : {},
893
2249
  ...text.length > 0 ? { text } : {},
894
2250
  ...reasoningText !== void 0 ? { reasoningText } : {},
@@ -896,24 +2252,59 @@ function geminiAdapter(opts) {
896
2252
  ...toolCalls.length > 0 ? { toolCalls, finishReason: "tool_calls" } : finishReason !== void 0 ? { finishReason } : {},
897
2253
  ...response.modelVersion !== void 0 ? { modelVersion: response.modelVersion } : {},
898
2254
  ...response.responseId !== void 0 ? { responseId: response.responseId } : {},
899
- // Build providerMetadata — merge promptFeedback + groundingMetadata when present.
2255
+ // Build providerMetadata — merge promptFeedback + groundingMetadata when
2256
+ // present. `google.searchEntryPoint` is the Search Suggestions widget
2257
+ // Google requires a grounded answer to display.
900
2258
  ...(() => {
901
2259
  const pf = response.promptFeedback;
902
- const gm = candidate.groundingMetadata;
903
- if (pf === void 0 && gm === void 0) return {};
2260
+ const gm = groundingMetadata;
2261
+ const candidateFields = {};
2262
+ const bounded = { truncated: false };
2263
+ for (const key of CANDIDATE_METADATA_KEYS) {
2264
+ const value = candidate[key];
2265
+ if (value === void 0) continue;
2266
+ candidateFields[key] = key === "finishMessage" && typeof value === "string" ? (() => {
2267
+ if (value.length > MAX_FINISH_MESSAGE_CHARS) bounded.truncated = true;
2268
+ return truncateText(value, MAX_FINISH_MESSAGE_CHARS);
2269
+ })() : boundMetadata(value, bounded);
2270
+ }
2271
+ const googleMeta = {};
2272
+ if (Object.keys(candidateFields).length > 0) {
2273
+ googleMeta["candidate"] = candidateFields;
2274
+ }
2275
+ if (pf === void 0 && gm === void 0 && Object.keys(googleMeta).length === 0) {
2276
+ return {};
2277
+ }
904
2278
  const meta = {};
905
2279
  if (pf !== void 0) {
906
- meta["promptFeedback"] = pf;
2280
+ meta["promptFeedback"] = boundMetadata(pf, bounded);
907
2281
  }
908
2282
  if (gm !== void 0) {
909
- meta["groundingMetadata"] = gm;
2283
+ const searchEntryPoint = readSearchEntryPoint(gm);
2284
+ if (searchEntryPoint !== void 0) {
2285
+ const { searchEntryPoint: _widget, ...rest } = gm;
2286
+ meta["groundingMetadata"] = boundMetadata(rest, bounded);
2287
+ googleMeta["searchEntryPoint"] = searchEntryPoint;
2288
+ } else {
2289
+ meta["groundingMetadata"] = boundMetadata(gm, bounded);
2290
+ }
910
2291
  }
2292
+ if (bounded.truncated) {
2293
+ warnings.push({
2294
+ type: "other",
2295
+ message: `google: providerMetadata was truncated (finishMessage over ${MAX_FINISH_MESSAGE_CHARS} characters, a string over ${MAX_METADATA_STRING}, a list over ${MAX_METADATA_ARRAY} entries or nesting over ${MAX_METADATA_DEPTH} levels, in the candidate fields, promptFeedback or groundingMetadata); the citations are built from the full response.`
2296
+ });
2297
+ }
2298
+ if (Object.keys(googleMeta).length > 0) meta["google"] = googleMeta;
911
2299
  return { providerMetadata: meta };
912
2300
  })(),
913
2301
  ...(() => {
914
- const gm = candidate.groundingMetadata;
915
- if (gm === void 0) return {};
916
- const citations = normalizeGroundingCitations(gm);
2302
+ if (groundingMetadata === void 0) return {};
2303
+ const citations = normalizeGroundingCitations(
2304
+ groundingMetadata,
2305
+ answerParts,
2306
+ (message) => warnings.push({ type: "other", message })
2307
+ );
917
2308
  return citations.length > 0 ? { citations } : {};
918
2309
  })()
919
2310
  };
@@ -926,30 +2317,49 @@ function geminiAdapter(opts) {
926
2317
  { kind: "bad_request", retryable: false }
927
2318
  );
928
2319
  }
929
- const contents = mapMessagesToGeminiContents(req.messages);
930
- const countTools = req.tools !== void 0 && req.tools.length > 0 ? [
931
- {
932
- functionDeclarations: req.tools.map((tool) => ({
933
- name: tool.name,
934
- description: tool.description,
935
- parameters: tool.inputJsonSchema
936
- }))
2320
+ const system = req.system !== void 0 && req.system !== "" ? req.system : void 0;
2321
+ const tools = req.tools !== void 0 && req.tools.length > 0 ? req.tools : void 0;
2322
+ if (tools !== void 0) {
2323
+ const descriptor = ctx.modelDescriptor;
2324
+ if (descriptor !== void 0 && descriptor.capabilities?.functionCalling !== true) {
2325
+ throw new core.LlmError(
2326
+ `tools is not supported for google model "${req.model}" (capabilities.functionCalling is not true).`,
2327
+ { kind: "bad_request", retryable: false, provider: "google" }
2328
+ );
937
2329
  }
938
- ] : void 0;
2330
+ const toolProfile = googleJsonSchemaProfile(descriptor?.model ?? req.model);
2331
+ tools.forEach((tool, index) => {
2332
+ core.assertJsonSchemaProfile(
2333
+ tool.inputJsonSchema,
2334
+ `tools[${index}].inputJsonSchema`,
2335
+ toolProfile
2336
+ );
2337
+ });
2338
+ }
2339
+ if (ctx.modelDescriptor !== void 0) {
2340
+ core.assertInputMimeTypesAdmitted(req.messages, ctx.modelDescriptor, "google");
2341
+ }
2342
+ const contents = mapMessagesToGeminiContents(req.messages);
2343
+ assertInlinePayloadWithinLimits(contents, system);
939
2344
  const params = {
940
2345
  model: req.model,
941
2346
  contents,
942
- ...req.system !== void 0 || ctx.signal !== void 0 || countTools !== void 0 ? {
943
- config: {
944
- ...req.system !== void 0 ? { systemInstruction: { parts: [{ text: req.system }] } } : {},
945
- ...ctx.signal !== void 0 ? { abortSignal: ctx.signal } : {},
946
- ...countTools !== void 0 ? { tools: countTools } : {}
947
- }
948
- } : {}
2347
+ ...system !== void 0 ? { systemInstruction: { parts: [{ text: system }] } } : {},
2348
+ ...tools !== void 0 ? {
2349
+ tools: [
2350
+ {
2351
+ functionDeclarations: tools.map((tool) => ({
2352
+ name: tool.name,
2353
+ description: tool.description,
2354
+ parametersJsonSchema: tool.inputJsonSchema
2355
+ }))
2356
+ }
2357
+ ]
2358
+ } : {},
2359
+ ...ctx.signal !== void 0 ? { config: { abortSignal: ctx.signal } } : {}
949
2360
  };
950
2361
  try {
951
- const buildClient = opts?._clientFactory ?? buildGoogleClient;
952
- const client = opts?.client !== void 0 ? opts.client : await buildClient(ctx.auth);
2362
+ const client = injectedClient !== void 0 ? injectedClient : await buildClient(ctx.auth);
953
2363
  const response = await client.models.countTokens(params);
954
2364
  if (response.totalTokens === void 0) {
955
2365
  throw new core.LlmError(
@@ -958,9 +2368,12 @@ function geminiAdapter(opts) {
958
2368
  );
959
2369
  }
960
2370
  const details = response.cachedContentTokenCount !== void 0 ? { cached: response.cachedContentTokenCount } : void 0;
2371
+ const omitsSignatures = ctx.modelDescriptor?.capabilities?.providerState === true && req.messages.some(
2372
+ (message) => message.role === "assistant" && message.parts.some((part) => part.kind === "tool-call")
2373
+ );
961
2374
  return {
962
2375
  totalTokens: response.totalTokens,
963
- accuracy: "exact",
2376
+ accuracy: omitsSignatures ? "estimated" : "exact",
964
2377
  ...details !== void 0 ? { details } : {},
965
2378
  raw: response
966
2379
  };
@@ -970,129 +2383,52 @@ function geminiAdapter(opts) {
970
2383
  }
971
2384
  };
972
2385
  }
973
-
974
- // src/pricing.ts
975
- var pricingVersion = "gemini-2026-09-25";
976
- var GEMINI_PRICED_TIERS = ["standard", "flex", "batch"];
977
- function tiers(standard, flex, batch) {
978
- return Object.freeze({ standard, flex, batch });
979
- }
980
- var GEMINI_PRICING = Object.freeze({
981
- // Gemini 2.5 Pro. Flex/batch cached equals standard on both context bands.
982
- "gemini-2.5-pro": tiers(
983
- {
984
- inputPerM: 125e4,
985
- cachedPerM: 125e3,
986
- outputPerM: 1e7,
987
- gt200k: { inputPerM: 25e5, cachedPerM: 25e4, outputPerM: 15e6 }
988
- },
989
- {
990
- inputPerM: 625e3,
991
- cachedPerM: 125e3,
992
- outputPerM: 5e6,
993
- gt200k: { inputPerM: 125e4, cachedPerM: 25e4, outputPerM: 75e5 }
994
- },
995
- {
996
- inputPerM: 625e3,
997
- cachedPerM: 125e3,
998
- outputPerM: 5e6,
999
- gt200k: { inputPerM: 125e4, cachedPerM: 25e4, outputPerM: 75e5 }
1000
- }
1001
- ),
1002
- // Gemini 2.5 Flash. Flex/batch cached stays $0.03.
1003
- "gemini-2.5-flash": tiers(
1004
- { inputPerM: 3e5, cachedPerM: 3e4, outputPerM: 25e5 },
1005
- { inputPerM: 15e4, cachedPerM: 3e4, outputPerM: 125e4 },
1006
- { inputPerM: 15e4, cachedPerM: 3e4, outputPerM: 125e4 }
1007
- ),
1008
- // Gemini 2.5 Flash-Lite. Flex/batch cached stays $0.01.
1009
- "gemini-2.5-flash-lite": tiers(
1010
- { inputPerM: 1e5, cachedPerM: 1e4, outputPerM: 4e5 },
1011
- { inputPerM: 5e4, cachedPerM: 1e4, outputPerM: 2e5 },
1012
- { inputPerM: 5e4, cachedPerM: 1e4, outputPerM: 2e5 }
1013
- ),
1014
- // Gemini 3.1 Flash-Lite. Flex/batch cached is the published $0.0125.
1015
- "gemini-3.1-flash-lite": tiers(
1016
- { inputPerM: 25e4, cachedPerM: 25e3, outputPerM: 15e5 },
1017
- { inputPerM: 125e3, cachedPerM: 12500, outputPerM: 75e4 },
1018
- { inputPerM: 125e3, cachedPerM: 12500, outputPerM: 75e4 }
1019
- ),
1020
- // Gemini 3.8 / 3.7 / 3.6 Flash intro rates (2026-09-25). Flex/batch cached
1021
- // is half of the intro cached rate. Re-snapshot on 2027-01-01.
1022
- "gemini-3.8-flash": tiers(
1023
- { inputPerM: 75e4, cachedPerM: 75e3, outputPerM: 375e4 },
1024
- { inputPerM: 375e3, cachedPerM: 37500, outputPerM: 1875e3 },
1025
- { inputPerM: 375e3, cachedPerM: 37500, outputPerM: 1875e3 }
1026
- ),
1027
- "gemini-3.7-flash": tiers(
1028
- { inputPerM: 75e4, cachedPerM: 75e3, outputPerM: 375e4 },
1029
- { inputPerM: 375e3, cachedPerM: 37500, outputPerM: 1875e3 },
1030
- { inputPerM: 375e3, cachedPerM: 37500, outputPerM: 1875e3 }
1031
- ),
1032
- "gemini-3.6-flash": tiers(
1033
- { inputPerM: 75e4, cachedPerM: 75e3, outputPerM: 375e4 },
1034
- { inputPerM: 375e3, cachedPerM: 37500, outputPerM: 1875e3 },
1035
- { inputPerM: 375e3, cachedPerM: 37500, outputPerM: 1875e3 }
1036
- ),
1037
- // Gemini 3.5 Flash-Lite. Flex/batch cached is the published $0.02, not half of $0.03.
1038
- "gemini-3.5-flash-lite": tiers(
1039
- { inputPerM: 3e5, cachedPerM: 3e4, outputPerM: 25e5 },
1040
- { inputPerM: 15e4, cachedPerM: 2e4, outputPerM: 125e4 },
1041
- { inputPerM: 15e4, cachedPerM: 2e4, outputPerM: 125e4 }
1042
- ),
1043
- // Gemini 3.1 Pro Preview. Flex/batch cached equals standard on both bands.
1044
- "gemini-3.1-pro-preview": tiers(
1045
- {
1046
- inputPerM: 2e6,
1047
- cachedPerM: 2e5,
1048
- outputPerM: 12e6,
1049
- gt200k: { inputPerM: 4e6, cachedPerM: 4e5, outputPerM: 18e6 }
1050
- },
1051
- {
1052
- inputPerM: 1e6,
1053
- cachedPerM: 2e5,
1054
- outputPerM: 6e6,
1055
- gt200k: { inputPerM: 2e6, cachedPerM: 4e5, outputPerM: 9e6 }
1056
- },
1057
- {
1058
- inputPerM: 1e6,
1059
- cachedPerM: 2e5,
1060
- outputPerM: 6e6,
1061
- gt200k: { inputPerM: 2e6, cachedPerM: 4e5, outputPerM: 9e6 }
1062
- }
1063
- )
2386
+ var cachedContentSchema = zod.z.union([
2387
+ zod.z.string().min(1),
2388
+ zod.z.strictObject({
2389
+ cacheName: zod.z.string().min(1).meta({
2390
+ title: "Cache Name",
2391
+ description: "Google cached content resource name."
2392
+ }),
2393
+ toolKinds: zod.z.array(zod.z.string().min(1)).optional().meta({
2394
+ title: "Tool Kinds",
2395
+ description: "The tool kinds the cache holds (`GoogleCacheHandle.toolKinds`), e.g. googleSearch."
2396
+ })
2397
+ })
2398
+ ]).optional().meta({
2399
+ title: "Cached Content",
2400
+ description: "Google cached content: the resource name, or { cacheName, toolKinds } from a cache handle."
1064
2401
  });
1065
- function isPricedGeminiTier(tier) {
1066
- return GEMINI_PRICED_TIERS.includes(tier);
1067
- }
1068
- function lookupGeminiTierRates(model, tier) {
1069
- const entry = Object.hasOwn(GEMINI_PRICING, model) ? GEMINI_PRICING[model] : void 0;
1070
- if (entry === void 0) return void 0;
1071
- if (tier !== void 0 && !isPricedGeminiTier(tier)) return void 0;
1072
- return entry;
1073
- }
1074
- function resolveGeminiRates(model, tier) {
1075
- const entry = lookupGeminiTierRates(model, tier);
1076
- if (entry === void 0) return void 0;
1077
- const key = tier ?? "standard";
1078
- return entry[key];
1079
- }
1080
2402
 
1081
- // src/cost.ts
1082
- function geminiPricingSource() {
1083
- return {
1084
- version: pricingVersion,
1085
- price(model, usage, tier) {
1086
- return core.computeCost(model, usage, tier, resolveGeminiRates, pricingVersion);
1087
- },
1088
- hasModel(model) {
1089
- return resolveGeminiRates(model, void 0) !== void 0;
1090
- },
1091
- listModels() {
1092
- return Object.keys(GEMINI_PRICING);
1093
- }
1094
- };
1095
- }
2403
+ // src/model-limits.ts
2404
+ var geminiLimits = () => Object.freeze({ contextWindow: 1048576, maxOutputTokens: 65536 });
2405
+ var gemma4Limits = () => Object.freeze({ contextWindow: 262144, maxOutputTokens: null });
2406
+ var GOOGLE_MODEL_LIMITS = {
2407
+ "gemini-2.5-pro": geminiLimits(),
2408
+ "gemini-2.5-flash": geminiLimits(),
2409
+ "gemini-2.5-flash-lite": geminiLimits(),
2410
+ "gemini-3.1-flash-lite": geminiLimits(),
2411
+ "gemini-3.1-pro-preview": geminiLimits(),
2412
+ "gemini-3.8-flash": geminiLimits(),
2413
+ "gemini-3.7-flash": geminiLimits(),
2414
+ "gemini-3.6-flash": geminiLimits(),
2415
+ "gemini-3.5-flash-lite": geminiLimits(),
2416
+ "gemma-4-31b-it": gemma4Limits(),
2417
+ "gemma-4-26b-a4b-it": gemma4Limits()
2418
+ };
2419
+ var GEMINI_INPUT_MIME_TYPES = Object.freeze([
2420
+ "application/pdf",
2421
+ "text/*",
2422
+ "image/*",
2423
+ "audio/*",
2424
+ "video/*"
2425
+ ]);
2426
+ var GEMMA_INPUT_MIME_TYPES = Object.freeze([
2427
+ "image/*",
2428
+ "video/*"
2429
+ ]);
2430
+
2431
+ // src/model-config/gemini-2.5-pro.ts
1096
2432
  var Gemini25ProConfigSchema = zod.z.union([
1097
2433
  zod.z.strictObject({
1098
2434
  temperature: zod.z.number().min(0).max(2).optional().meta({
@@ -1108,7 +2444,7 @@ var Gemini25ProConfigSchema = zod.z.union([
1108
2444
  title: "Top K",
1109
2445
  description: "Top-k sampling limit for gemini-2.5-pro."
1110
2446
  }),
1111
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
2447
+ maxOutputTokens: core.maxOutputTokensSchema(GOOGLE_MODEL_LIMITS["gemini-2.5-pro"]).optional().meta({
1112
2448
  title: "Max Output Tokens",
1113
2449
  description: "Maximum output token cap for gemini-2.5-pro."
1114
2450
  }),
@@ -1151,25 +2487,26 @@ var Gemini25ProConfigSchema = zod.z.union([
1151
2487
  title: "Reasoning",
1152
2488
  description: "Gemini 2.5 Pro thinkingBudget configuration."
1153
2489
  }),
1154
- timeoutMs: zod.z.number().int().positive().optional().meta({
2490
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
1155
2491
  title: "Timeout",
1156
2492
  description: "Logical request timeout in milliseconds."
1157
2493
  }),
1158
2494
  providerOptions: zod.z.strictObject({
1159
2495
  google: zod.z.strictObject({
1160
- cachedContent: zod.z.string().min(1).optional().meta({
1161
- title: "Cached Content",
1162
- description: "Google cached content resource name."
2496
+ requireGrounding: zod.z.boolean().optional().meta({
2497
+ title: "Require Grounding",
2498
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
1163
2499
  }),
2500
+ cachedContent: cachedContentSchema,
1164
2501
  safetySettings: zod.z.array(
1165
2502
  zod.z.strictObject({
1166
- category: zod.z.string().min(1).meta({
2503
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
1167
2504
  title: "Safety Category",
1168
- description: "Google safety category identifier."
2505
+ description: "Documented Google safety category."
1169
2506
  }),
1170
- threshold: zod.z.string().min(1).meta({
2507
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
1171
2508
  title: "Safety Threshold",
1172
- description: "Google safety threshold identifier."
2509
+ description: "Documented Google safety threshold."
1173
2510
  })
1174
2511
  })
1175
2512
  ).optional().meta({
@@ -1188,9 +2525,9 @@ var Gemini25ProConfigSchema = zod.z.union([
1188
2525
  description: "Allowlisted Google tools for gemini-2.5-pro."
1189
2526
  }),
1190
2527
  httpOptions: zod.z.strictObject({
1191
- timeout: zod.z.number().int().positive().optional().meta({
2528
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
1192
2529
  title: "HTTP Timeout",
1193
- description: "Per-request Google transport timeout in milliseconds."
2530
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
1194
2531
  })
1195
2532
  }).optional().meta({
1196
2533
  title: "HTTP Options",
@@ -1223,7 +2560,7 @@ var Gemini25ProConfigSchema = zod.z.union([
1223
2560
  title: "Top K",
1224
2561
  description: "Top-k sampling limit for gemini-2.5-pro."
1225
2562
  }),
1226
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
2563
+ maxOutputTokens: core.maxOutputTokensSchema(GOOGLE_MODEL_LIMITS["gemini-2.5-pro"]).optional().meta({
1227
2564
  title: "Max Output Tokens",
1228
2565
  description: "Maximum output token cap for gemini-2.5-pro."
1229
2566
  }),
@@ -1266,25 +2603,26 @@ var Gemini25ProConfigSchema = zod.z.union([
1266
2603
  title: "Reasoning",
1267
2604
  description: "Gemini 2.5 Pro thinkingBudget configuration."
1268
2605
  }),
1269
- timeoutMs: zod.z.number().int().positive().optional().meta({
2606
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
1270
2607
  title: "Timeout",
1271
2608
  description: "Logical request timeout in milliseconds."
1272
2609
  }),
1273
2610
  providerOptions: zod.z.strictObject({
1274
2611
  google: zod.z.strictObject({
1275
- cachedContent: zod.z.string().min(1).optional().meta({
1276
- title: "Cached Content",
1277
- description: "Google cached content resource name."
2612
+ requireGrounding: zod.z.boolean().optional().meta({
2613
+ title: "Require Grounding",
2614
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
1278
2615
  }),
2616
+ cachedContent: cachedContentSchema,
1279
2617
  safetySettings: zod.z.array(
1280
2618
  zod.z.strictObject({
1281
- category: zod.z.string().min(1).meta({
2619
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
1282
2620
  title: "Safety Category",
1283
- description: "Google safety category identifier."
2621
+ description: "Documented Google safety category."
1284
2622
  }),
1285
- threshold: zod.z.string().min(1).meta({
2623
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
1286
2624
  title: "Safety Threshold",
1287
- description: "Google safety threshold identifier."
2625
+ description: "Documented Google safety threshold."
1288
2626
  })
1289
2627
  })
1290
2628
  ).optional().meta({
@@ -1303,9 +2641,9 @@ var Gemini25ProConfigSchema = zod.z.union([
1303
2641
  description: "Allowlisted Google tools for gemini-2.5-pro."
1304
2642
  }),
1305
2643
  httpOptions: zod.z.strictObject({
1306
- timeout: zod.z.number().int().positive().optional().meta({
2644
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
1307
2645
  title: "HTTP Timeout",
1308
- description: "Per-request Google transport timeout in milliseconds."
2646
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
1309
2647
  })
1310
2648
  }).optional().meta({
1311
2649
  title: "HTTP Options",
@@ -1340,7 +2678,7 @@ var Gemini25FlashConfigSchema = zod.z.union([
1340
2678
  title: "Top K",
1341
2679
  description: "Top-k sampling limit for gemini-2.5-flash."
1342
2680
  }),
1343
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
2681
+ maxOutputTokens: core.maxOutputTokensSchema(GOOGLE_MODEL_LIMITS["gemini-2.5-flash"]).optional().meta({
1344
2682
  title: "Max Output Tokens",
1345
2683
  description: "Maximum output token cap for gemini-2.5-flash."
1346
2684
  }),
@@ -1383,25 +2721,26 @@ var Gemini25FlashConfigSchema = zod.z.union([
1383
2721
  title: "Reasoning",
1384
2722
  description: "Gemini 2.5 Flash thinkingBudget configuration."
1385
2723
  }),
1386
- timeoutMs: zod.z.number().int().positive().optional().meta({
2724
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
1387
2725
  title: "Timeout",
1388
2726
  description: "Logical request timeout in milliseconds."
1389
2727
  }),
1390
2728
  providerOptions: zod.z.strictObject({
1391
2729
  google: zod.z.strictObject({
1392
- cachedContent: zod.z.string().min(1).optional().meta({
1393
- title: "Cached Content",
1394
- description: "Google cached content resource name."
2730
+ requireGrounding: zod.z.boolean().optional().meta({
2731
+ title: "Require Grounding",
2732
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
1395
2733
  }),
2734
+ cachedContent: cachedContentSchema,
1396
2735
  safetySettings: zod.z.array(
1397
2736
  zod.z.strictObject({
1398
- category: zod.z.string().min(1).meta({
2737
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
1399
2738
  title: "Safety Category",
1400
- description: "Google safety category identifier."
2739
+ description: "Documented Google safety category."
1401
2740
  }),
1402
- threshold: zod.z.string().min(1).meta({
2741
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
1403
2742
  title: "Safety Threshold",
1404
- description: "Google safety threshold identifier."
2743
+ description: "Documented Google safety threshold."
1405
2744
  })
1406
2745
  })
1407
2746
  ).optional().meta({
@@ -1420,9 +2759,9 @@ var Gemini25FlashConfigSchema = zod.z.union([
1420
2759
  description: "Allowlisted Google tools for gemini-2.5-flash."
1421
2760
  }),
1422
2761
  httpOptions: zod.z.strictObject({
1423
- timeout: zod.z.number().int().positive().optional().meta({
2762
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
1424
2763
  title: "HTTP Timeout",
1425
- description: "Per-request Google transport timeout in milliseconds."
2764
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
1426
2765
  })
1427
2766
  }).optional().meta({
1428
2767
  title: "HTTP Options",
@@ -1455,7 +2794,7 @@ var Gemini25FlashConfigSchema = zod.z.union([
1455
2794
  title: "Top K",
1456
2795
  description: "Top-k sampling limit for gemini-2.5-flash."
1457
2796
  }),
1458
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
2797
+ maxOutputTokens: core.maxOutputTokensSchema(GOOGLE_MODEL_LIMITS["gemini-2.5-flash"]).optional().meta({
1459
2798
  title: "Max Output Tokens",
1460
2799
  description: "Maximum output token cap for gemini-2.5-flash."
1461
2800
  }),
@@ -1498,25 +2837,26 @@ var Gemini25FlashConfigSchema = zod.z.union([
1498
2837
  title: "Reasoning",
1499
2838
  description: "Gemini 2.5 Flash thinkingBudget configuration."
1500
2839
  }),
1501
- timeoutMs: zod.z.number().int().positive().optional().meta({
2840
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
1502
2841
  title: "Timeout",
1503
2842
  description: "Logical request timeout in milliseconds."
1504
2843
  }),
1505
2844
  providerOptions: zod.z.strictObject({
1506
2845
  google: zod.z.strictObject({
1507
- cachedContent: zod.z.string().min(1).optional().meta({
1508
- title: "Cached Content",
1509
- description: "Google cached content resource name."
2846
+ requireGrounding: zod.z.boolean().optional().meta({
2847
+ title: "Require Grounding",
2848
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
1510
2849
  }),
2850
+ cachedContent: cachedContentSchema,
1511
2851
  safetySettings: zod.z.array(
1512
2852
  zod.z.strictObject({
1513
- category: zod.z.string().min(1).meta({
2853
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
1514
2854
  title: "Safety Category",
1515
- description: "Google safety category identifier."
2855
+ description: "Documented Google safety category."
1516
2856
  }),
1517
- threshold: zod.z.string().min(1).meta({
2857
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
1518
2858
  title: "Safety Threshold",
1519
- description: "Google safety threshold identifier."
2859
+ description: "Documented Google safety threshold."
1520
2860
  })
1521
2861
  })
1522
2862
  ).optional().meta({
@@ -1535,9 +2875,9 @@ var Gemini25FlashConfigSchema = zod.z.union([
1535
2875
  description: "Allowlisted Google tools for gemini-2.5-flash."
1536
2876
  }),
1537
2877
  httpOptions: zod.z.strictObject({
1538
- timeout: zod.z.number().int().positive().optional().meta({
2878
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
1539
2879
  title: "HTTP Timeout",
1540
- description: "Per-request Google transport timeout in milliseconds."
2880
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
1541
2881
  })
1542
2882
  }).optional().meta({
1543
2883
  title: "HTTP Options",
@@ -1572,7 +2912,7 @@ var Gemini25FlashLiteConfigSchema = zod.z.union([
1572
2912
  title: "Top K",
1573
2913
  description: "Top-k sampling limit for gemini-2.5-flash-lite."
1574
2914
  }),
1575
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
2915
+ maxOutputTokens: core.maxOutputTokensSchema(GOOGLE_MODEL_LIMITS["gemini-2.5-flash-lite"]).optional().meta({
1576
2916
  title: "Max Output Tokens",
1577
2917
  description: "Maximum output token cap for gemini-2.5-flash-lite."
1578
2918
  }),
@@ -1615,25 +2955,26 @@ var Gemini25FlashLiteConfigSchema = zod.z.union([
1615
2955
  title: "Reasoning",
1616
2956
  description: "Gemini 2.5 Flash-Lite thinkingBudget configuration."
1617
2957
  }),
1618
- timeoutMs: zod.z.number().int().positive().optional().meta({
2958
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
1619
2959
  title: "Timeout",
1620
2960
  description: "Logical request timeout in milliseconds."
1621
2961
  }),
1622
2962
  providerOptions: zod.z.strictObject({
1623
2963
  google: zod.z.strictObject({
1624
- cachedContent: zod.z.string().min(1).optional().meta({
1625
- title: "Cached Content",
1626
- description: "Google cached content resource name."
2964
+ requireGrounding: zod.z.boolean().optional().meta({
2965
+ title: "Require Grounding",
2966
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
1627
2967
  }),
2968
+ cachedContent: cachedContentSchema,
1628
2969
  safetySettings: zod.z.array(
1629
2970
  zod.z.strictObject({
1630
- category: zod.z.string().min(1).meta({
2971
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
1631
2972
  title: "Safety Category",
1632
- description: "Google safety category identifier."
2973
+ description: "Documented Google safety category."
1633
2974
  }),
1634
- threshold: zod.z.string().min(1).meta({
2975
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
1635
2976
  title: "Safety Threshold",
1636
- description: "Google safety threshold identifier."
2977
+ description: "Documented Google safety threshold."
1637
2978
  })
1638
2979
  })
1639
2980
  ).optional().meta({
@@ -1652,9 +2993,9 @@ var Gemini25FlashLiteConfigSchema = zod.z.union([
1652
2993
  description: "Allowlisted Google tools for gemini-2.5-flash-lite."
1653
2994
  }),
1654
2995
  httpOptions: zod.z.strictObject({
1655
- timeout: zod.z.number().int().positive().optional().meta({
2996
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
1656
2997
  title: "HTTP Timeout",
1657
- description: "Per-request Google transport timeout in milliseconds."
2998
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
1658
2999
  })
1659
3000
  }).optional().meta({
1660
3001
  title: "HTTP Options",
@@ -1687,7 +3028,7 @@ var Gemini25FlashLiteConfigSchema = zod.z.union([
1687
3028
  title: "Top K",
1688
3029
  description: "Top-k sampling limit for gemini-2.5-flash-lite."
1689
3030
  }),
1690
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
3031
+ maxOutputTokens: core.maxOutputTokensSchema(GOOGLE_MODEL_LIMITS["gemini-2.5-flash-lite"]).optional().meta({
1691
3032
  title: "Max Output Tokens",
1692
3033
  description: "Maximum output token cap for gemini-2.5-flash-lite."
1693
3034
  }),
@@ -1730,25 +3071,26 @@ var Gemini25FlashLiteConfigSchema = zod.z.union([
1730
3071
  title: "Reasoning",
1731
3072
  description: "Gemini 2.5 Flash-Lite thinkingBudget configuration."
1732
3073
  }),
1733
- timeoutMs: zod.z.number().int().positive().optional().meta({
3074
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
1734
3075
  title: "Timeout",
1735
3076
  description: "Logical request timeout in milliseconds."
1736
3077
  }),
1737
3078
  providerOptions: zod.z.strictObject({
1738
3079
  google: zod.z.strictObject({
1739
- cachedContent: zod.z.string().min(1).optional().meta({
1740
- title: "Cached Content",
1741
- description: "Google cached content resource name."
3080
+ requireGrounding: zod.z.boolean().optional().meta({
3081
+ title: "Require Grounding",
3082
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
1742
3083
  }),
3084
+ cachedContent: cachedContentSchema,
1743
3085
  safetySettings: zod.z.array(
1744
3086
  zod.z.strictObject({
1745
- category: zod.z.string().min(1).meta({
3087
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
1746
3088
  title: "Safety Category",
1747
- description: "Google safety category identifier."
3089
+ description: "Documented Google safety category."
1748
3090
  }),
1749
- threshold: zod.z.string().min(1).meta({
3091
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
1750
3092
  title: "Safety Threshold",
1751
- description: "Google safety threshold identifier."
3093
+ description: "Documented Google safety threshold."
1752
3094
  })
1753
3095
  })
1754
3096
  ).optional().meta({
@@ -1767,9 +3109,9 @@ var Gemini25FlashLiteConfigSchema = zod.z.union([
1767
3109
  description: "Allowlisted Google tools for gemini-2.5-flash-lite."
1768
3110
  }),
1769
3111
  httpOptions: zod.z.strictObject({
1770
- timeout: zod.z.number().int().positive().optional().meta({
3112
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
1771
3113
  title: "HTTP Timeout",
1772
- description: "Per-request Google transport timeout in milliseconds."
3114
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
1773
3115
  })
1774
3116
  }).optional().meta({
1775
3117
  title: "HTTP Options",
@@ -1791,7 +3133,7 @@ var Gemini25FlashLiteConfigSchema = zod.z.union([
1791
3133
  });
1792
3134
  var Gemini31FlashLiteConfigSchema = zod.z.union([
1793
3135
  zod.z.strictObject({
1794
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
3136
+ maxOutputTokens: core.maxOutputTokensSchema(GOOGLE_MODEL_LIMITS["gemini-3.1-flash-lite"]).optional().meta({
1795
3137
  title: "Max Output Tokens",
1796
3138
  description: "Maximum output token cap for gemini-3.1-flash-lite."
1797
3139
  }),
@@ -1824,25 +3166,30 @@ var Gemini31FlashLiteConfigSchema = zod.z.union([
1824
3166
  title: "Reasoning",
1825
3167
  description: "Gemini 3.1 Flash-Lite thinkingLevel configuration."
1826
3168
  }),
1827
- timeoutMs: zod.z.number().int().positive().optional().meta({
3169
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
1828
3170
  title: "Timeout",
1829
3171
  description: "Logical request timeout in milliseconds."
1830
3172
  }),
1831
3173
  providerOptions: zod.z.strictObject({
1832
3174
  google: zod.z.strictObject({
1833
- cachedContent: zod.z.string().min(1).optional().meta({
1834
- title: "Cached Content",
1835
- description: "Google cached content resource name."
3175
+ allowSchemaWithSearch: zod.z.boolean().optional().meta({
3176
+ title: "Allow Schema With Search",
3177
+ description: "Admit googleSearch together with a response schema. Turns requireGrounding on unless it is false."
1836
3178
  }),
3179
+ requireGrounding: zod.z.boolean().optional().meta({
3180
+ title: "Require Grounding",
3181
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
3182
+ }),
3183
+ cachedContent: cachedContentSchema,
1837
3184
  safetySettings: zod.z.array(
1838
3185
  zod.z.strictObject({
1839
- category: zod.z.string().min(1).meta({
3186
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
1840
3187
  title: "Safety Category",
1841
- description: "Google safety category identifier."
3188
+ description: "Documented Google safety category."
1842
3189
  }),
1843
- threshold: zod.z.string().min(1).meta({
3190
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
1844
3191
  title: "Safety Threshold",
1845
- description: "Google safety threshold identifier."
3192
+ description: "Documented Google safety threshold."
1846
3193
  })
1847
3194
  })
1848
3195
  ).optional().meta({
@@ -1861,9 +3208,9 @@ var Gemini31FlashLiteConfigSchema = zod.z.union([
1861
3208
  description: "Allowlisted Google tools for gemini-3.1-flash-lite."
1862
3209
  }),
1863
3210
  httpOptions: zod.z.strictObject({
1864
- timeout: zod.z.number().int().positive().optional().meta({
3211
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
1865
3212
  title: "HTTP Timeout",
1866
- description: "Per-request Google transport timeout in milliseconds."
3213
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
1867
3214
  })
1868
3215
  }).optional().meta({
1869
3216
  title: "HTTP Options",
@@ -1883,7 +3230,7 @@ var Gemini31FlashLiteConfigSchema = zod.z.union([
1883
3230
  })
1884
3231
  }),
1885
3232
  zod.z.strictObject({
1886
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
3233
+ maxOutputTokens: core.maxOutputTokensSchema(GOOGLE_MODEL_LIMITS["gemini-3.1-flash-lite"]).optional().meta({
1887
3234
  title: "Max Output Tokens",
1888
3235
  description: "Maximum output token cap for gemini-3.1-flash-lite."
1889
3236
  }),
@@ -1916,25 +3263,30 @@ var Gemini31FlashLiteConfigSchema = zod.z.union([
1916
3263
  title: "Reasoning",
1917
3264
  description: "Gemini 3.1 Flash-Lite thinkingLevel configuration."
1918
3265
  }),
1919
- timeoutMs: zod.z.number().int().positive().optional().meta({
3266
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
1920
3267
  title: "Timeout",
1921
3268
  description: "Logical request timeout in milliseconds."
1922
3269
  }),
1923
3270
  providerOptions: zod.z.strictObject({
1924
3271
  google: zod.z.strictObject({
1925
- cachedContent: zod.z.string().min(1).optional().meta({
1926
- title: "Cached Content",
1927
- description: "Google cached content resource name."
3272
+ allowSchemaWithSearch: zod.z.boolean().optional().meta({
3273
+ title: "Allow Schema With Search",
3274
+ description: "Admit googleSearch together with a response schema. Turns requireGrounding on unless it is false."
3275
+ }),
3276
+ requireGrounding: zod.z.boolean().optional().meta({
3277
+ title: "Require Grounding",
3278
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
1928
3279
  }),
3280
+ cachedContent: cachedContentSchema,
1929
3281
  safetySettings: zod.z.array(
1930
3282
  zod.z.strictObject({
1931
- category: zod.z.string().min(1).meta({
3283
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
1932
3284
  title: "Safety Category",
1933
- description: "Google safety category identifier."
3285
+ description: "Documented Google safety category."
1934
3286
  }),
1935
- threshold: zod.z.string().min(1).meta({
3287
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
1936
3288
  title: "Safety Threshold",
1937
- description: "Google safety threshold identifier."
3289
+ description: "Documented Google safety threshold."
1938
3290
  })
1939
3291
  })
1940
3292
  ).optional().meta({
@@ -1953,9 +3305,9 @@ var Gemini31FlashLiteConfigSchema = zod.z.union([
1953
3305
  description: "Allowlisted Google tools for gemini-3.1-flash-lite."
1954
3306
  }),
1955
3307
  httpOptions: zod.z.strictObject({
1956
- timeout: zod.z.number().int().positive().optional().meta({
3308
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
1957
3309
  title: "HTTP Timeout",
1958
- description: "Per-request Google transport timeout in milliseconds."
3310
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
1959
3311
  })
1960
3312
  }).optional().meta({
1961
3313
  title: "HTTP Options",
@@ -1977,7 +3329,9 @@ var Gemini31FlashLiteConfigSchema = zod.z.union([
1977
3329
  });
1978
3330
  var Gemini31ProPreviewConfigSchema = zod.z.union([
1979
3331
  zod.z.strictObject({
1980
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
3332
+ maxOutputTokens: core.maxOutputTokensSchema(
3333
+ GOOGLE_MODEL_LIMITS["gemini-3.1-pro-preview"]
3334
+ ).optional().meta({
1981
3335
  title: "Max Output Tokens",
1982
3336
  description: "Maximum output token cap for gemini-3.1-pro-preview."
1983
3337
  }),
@@ -2010,25 +3364,30 @@ var Gemini31ProPreviewConfigSchema = zod.z.union([
2010
3364
  title: "Reasoning",
2011
3365
  description: "Gemini 3.1 Pro Preview thinkingLevel configuration."
2012
3366
  }),
2013
- timeoutMs: zod.z.number().int().positive().optional().meta({
3367
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
2014
3368
  title: "Timeout",
2015
3369
  description: "Logical request timeout in milliseconds."
2016
3370
  }),
2017
3371
  providerOptions: zod.z.strictObject({
2018
3372
  google: zod.z.strictObject({
2019
- cachedContent: zod.z.string().min(1).optional().meta({
2020
- title: "Cached Content",
2021
- description: "Google cached content resource name."
3373
+ allowSchemaWithSearch: zod.z.boolean().optional().meta({
3374
+ title: "Allow Schema With Search",
3375
+ description: "Admit googleSearch together with a response schema. Turns requireGrounding on unless it is false."
2022
3376
  }),
3377
+ requireGrounding: zod.z.boolean().optional().meta({
3378
+ title: "Require Grounding",
3379
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
3380
+ }),
3381
+ cachedContent: cachedContentSchema,
2023
3382
  safetySettings: zod.z.array(
2024
3383
  zod.z.strictObject({
2025
- category: zod.z.string().min(1).meta({
3384
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
2026
3385
  title: "Safety Category",
2027
- description: "Google safety category identifier."
3386
+ description: "Documented Google safety category."
2028
3387
  }),
2029
- threshold: zod.z.string().min(1).meta({
3388
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
2030
3389
  title: "Safety Threshold",
2031
- description: "Google safety threshold identifier."
3390
+ description: "Documented Google safety threshold."
2032
3391
  })
2033
3392
  })
2034
3393
  ).optional().meta({
@@ -2047,9 +3406,9 @@ var Gemini31ProPreviewConfigSchema = zod.z.union([
2047
3406
  description: "Allowlisted Google tools for gemini-3.1-pro-preview."
2048
3407
  }),
2049
3408
  httpOptions: zod.z.strictObject({
2050
- timeout: zod.z.number().int().positive().optional().meta({
3409
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
2051
3410
  title: "HTTP Timeout",
2052
- description: "Per-request Google transport timeout in milliseconds."
3411
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
2053
3412
  })
2054
3413
  }).optional().meta({
2055
3414
  title: "HTTP Options",
@@ -2069,7 +3428,9 @@ var Gemini31ProPreviewConfigSchema = zod.z.union([
2069
3428
  })
2070
3429
  }),
2071
3430
  zod.z.strictObject({
2072
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
3431
+ maxOutputTokens: core.maxOutputTokensSchema(
3432
+ GOOGLE_MODEL_LIMITS["gemini-3.1-pro-preview"]
3433
+ ).optional().meta({
2073
3434
  title: "Max Output Tokens",
2074
3435
  description: "Maximum output token cap for gemini-3.1-pro-preview."
2075
3436
  }),
@@ -2102,25 +3463,30 @@ var Gemini31ProPreviewConfigSchema = zod.z.union([
2102
3463
  title: "Reasoning",
2103
3464
  description: "Gemini 3.1 Pro Preview thinkingLevel configuration."
2104
3465
  }),
2105
- timeoutMs: zod.z.number().int().positive().optional().meta({
3466
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
2106
3467
  title: "Timeout",
2107
3468
  description: "Logical request timeout in milliseconds."
2108
3469
  }),
2109
3470
  providerOptions: zod.z.strictObject({
2110
3471
  google: zod.z.strictObject({
2111
- cachedContent: zod.z.string().min(1).optional().meta({
2112
- title: "Cached Content",
2113
- description: "Google cached content resource name."
3472
+ allowSchemaWithSearch: zod.z.boolean().optional().meta({
3473
+ title: "Allow Schema With Search",
3474
+ description: "Admit googleSearch together with a response schema. Turns requireGrounding on unless it is false."
3475
+ }),
3476
+ requireGrounding: zod.z.boolean().optional().meta({
3477
+ title: "Require Grounding",
3478
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
2114
3479
  }),
3480
+ cachedContent: cachedContentSchema,
2115
3481
  safetySettings: zod.z.array(
2116
3482
  zod.z.strictObject({
2117
- category: zod.z.string().min(1).meta({
3483
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
2118
3484
  title: "Safety Category",
2119
- description: "Google safety category identifier."
3485
+ description: "Documented Google safety category."
2120
3486
  }),
2121
- threshold: zod.z.string().min(1).meta({
3487
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
2122
3488
  title: "Safety Threshold",
2123
- description: "Google safety threshold identifier."
3489
+ description: "Documented Google safety threshold."
2124
3490
  })
2125
3491
  })
2126
3492
  ).optional().meta({
@@ -2139,9 +3505,9 @@ var Gemini31ProPreviewConfigSchema = zod.z.union([
2139
3505
  description: "Allowlisted Google tools for gemini-3.1-pro-preview."
2140
3506
  }),
2141
3507
  httpOptions: zod.z.strictObject({
2142
- timeout: zod.z.number().int().positive().optional().meta({
3508
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
2143
3509
  title: "HTTP Timeout",
2144
- description: "Per-request Google transport timeout in milliseconds."
3510
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
2145
3511
  })
2146
3512
  }).optional().meta({
2147
3513
  title: "HTTP Options",
@@ -2163,7 +3529,7 @@ var Gemini31ProPreviewConfigSchema = zod.z.union([
2163
3529
  });
2164
3530
  var Gemini35FlashLiteConfigSchema = zod.z.union([
2165
3531
  zod.z.strictObject({
2166
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
3532
+ maxOutputTokens: core.maxOutputTokensSchema(GOOGLE_MODEL_LIMITS["gemini-3.5-flash-lite"]).optional().meta({
2167
3533
  title: "Max Output Tokens",
2168
3534
  description: "Maximum output token cap for gemini-3.5-flash-lite."
2169
3535
  }),
@@ -2196,25 +3562,30 @@ var Gemini35FlashLiteConfigSchema = zod.z.union([
2196
3562
  title: "Reasoning",
2197
3563
  description: "Gemini 3.5 Flash-Lite thinkingLevel configuration."
2198
3564
  }),
2199
- timeoutMs: zod.z.number().int().positive().optional().meta({
3565
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
2200
3566
  title: "Timeout",
2201
3567
  description: "Logical request timeout in milliseconds."
2202
3568
  }),
2203
3569
  providerOptions: zod.z.strictObject({
2204
3570
  google: zod.z.strictObject({
2205
- cachedContent: zod.z.string().min(1).optional().meta({
2206
- title: "Cached Content",
2207
- description: "Google cached content resource name."
3571
+ allowSchemaWithSearch: zod.z.boolean().optional().meta({
3572
+ title: "Allow Schema With Search",
3573
+ description: "Admit googleSearch together with a response schema. Turns requireGrounding on unless it is false."
2208
3574
  }),
3575
+ requireGrounding: zod.z.boolean().optional().meta({
3576
+ title: "Require Grounding",
3577
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
3578
+ }),
3579
+ cachedContent: cachedContentSchema,
2209
3580
  safetySettings: zod.z.array(
2210
3581
  zod.z.strictObject({
2211
- category: zod.z.string().min(1).meta({
3582
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
2212
3583
  title: "Safety Category",
2213
- description: "Google safety category identifier."
3584
+ description: "Documented Google safety category."
2214
3585
  }),
2215
- threshold: zod.z.string().min(1).meta({
3586
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
2216
3587
  title: "Safety Threshold",
2217
- description: "Google safety threshold identifier."
3588
+ description: "Documented Google safety threshold."
2218
3589
  })
2219
3590
  })
2220
3591
  ).optional().meta({
@@ -2233,9 +3604,9 @@ var Gemini35FlashLiteConfigSchema = zod.z.union([
2233
3604
  description: "Allowlisted Google tools for gemini-3.5-flash-lite."
2234
3605
  }),
2235
3606
  httpOptions: zod.z.strictObject({
2236
- timeout: zod.z.number().int().positive().optional().meta({
3607
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
2237
3608
  title: "HTTP Timeout",
2238
- description: "Per-request Google transport timeout in milliseconds."
3609
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
2239
3610
  })
2240
3611
  }).optional().meta({
2241
3612
  title: "HTTP Options",
@@ -2255,7 +3626,7 @@ var Gemini35FlashLiteConfigSchema = zod.z.union([
2255
3626
  })
2256
3627
  }),
2257
3628
  zod.z.strictObject({
2258
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
3629
+ maxOutputTokens: core.maxOutputTokensSchema(GOOGLE_MODEL_LIMITS["gemini-3.5-flash-lite"]).optional().meta({
2259
3630
  title: "Max Output Tokens",
2260
3631
  description: "Maximum output token cap for gemini-3.5-flash-lite."
2261
3632
  }),
@@ -2288,25 +3659,30 @@ var Gemini35FlashLiteConfigSchema = zod.z.union([
2288
3659
  title: "Reasoning",
2289
3660
  description: "Gemini 3.5 Flash-Lite thinkingLevel configuration."
2290
3661
  }),
2291
- timeoutMs: zod.z.number().int().positive().optional().meta({
3662
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
2292
3663
  title: "Timeout",
2293
3664
  description: "Logical request timeout in milliseconds."
2294
3665
  }),
2295
3666
  providerOptions: zod.z.strictObject({
2296
3667
  google: zod.z.strictObject({
2297
- cachedContent: zod.z.string().min(1).optional().meta({
2298
- title: "Cached Content",
2299
- description: "Google cached content resource name."
3668
+ allowSchemaWithSearch: zod.z.boolean().optional().meta({
3669
+ title: "Allow Schema With Search",
3670
+ description: "Admit googleSearch together with a response schema. Turns requireGrounding on unless it is false."
3671
+ }),
3672
+ requireGrounding: zod.z.boolean().optional().meta({
3673
+ title: "Require Grounding",
3674
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
2300
3675
  }),
3676
+ cachedContent: cachedContentSchema,
2301
3677
  safetySettings: zod.z.array(
2302
3678
  zod.z.strictObject({
2303
- category: zod.z.string().min(1).meta({
3679
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
2304
3680
  title: "Safety Category",
2305
- description: "Google safety category identifier."
3681
+ description: "Documented Google safety category."
2306
3682
  }),
2307
- threshold: zod.z.string().min(1).meta({
3683
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
2308
3684
  title: "Safety Threshold",
2309
- description: "Google safety threshold identifier."
3685
+ description: "Documented Google safety threshold."
2310
3686
  })
2311
3687
  })
2312
3688
  ).optional().meta({
@@ -2325,9 +3701,9 @@ var Gemini35FlashLiteConfigSchema = zod.z.union([
2325
3701
  description: "Allowlisted Google tools for gemini-3.5-flash-lite."
2326
3702
  }),
2327
3703
  httpOptions: zod.z.strictObject({
2328
- timeout: zod.z.number().int().positive().optional().meta({
3704
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
2329
3705
  title: "HTTP Timeout",
2330
- description: "Per-request Google transport timeout in milliseconds."
3706
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
2331
3707
  })
2332
3708
  }).optional().meta({
2333
3709
  title: "HTTP Options",
@@ -2349,7 +3725,7 @@ var Gemini35FlashLiteConfigSchema = zod.z.union([
2349
3725
  });
2350
3726
  var Gemini36FlashConfigSchema = zod.z.union([
2351
3727
  zod.z.strictObject({
2352
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
3728
+ maxOutputTokens: core.maxOutputTokensSchema(GOOGLE_MODEL_LIMITS["gemini-3.6-flash"]).optional().meta({
2353
3729
  title: "Max Output Tokens",
2354
3730
  description: "Maximum output token cap for gemini-3.6-flash."
2355
3731
  }),
@@ -2382,25 +3758,30 @@ var Gemini36FlashConfigSchema = zod.z.union([
2382
3758
  title: "Reasoning",
2383
3759
  description: "Gemini 3.6 Flash thinkingLevel configuration."
2384
3760
  }),
2385
- timeoutMs: zod.z.number().int().positive().optional().meta({
3761
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
2386
3762
  title: "Timeout",
2387
3763
  description: "Logical request timeout in milliseconds."
2388
3764
  }),
2389
3765
  providerOptions: zod.z.strictObject({
2390
3766
  google: zod.z.strictObject({
2391
- cachedContent: zod.z.string().min(1).optional().meta({
2392
- title: "Cached Content",
2393
- description: "Google cached content resource name."
3767
+ allowSchemaWithSearch: zod.z.boolean().optional().meta({
3768
+ title: "Allow Schema With Search",
3769
+ description: "Admit googleSearch together with a response schema. Turns requireGrounding on unless it is false."
2394
3770
  }),
3771
+ requireGrounding: zod.z.boolean().optional().meta({
3772
+ title: "Require Grounding",
3773
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
3774
+ }),
3775
+ cachedContent: cachedContentSchema,
2395
3776
  safetySettings: zod.z.array(
2396
3777
  zod.z.strictObject({
2397
- category: zod.z.string().min(1).meta({
3778
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
2398
3779
  title: "Safety Category",
2399
- description: "Google safety category identifier."
3780
+ description: "Documented Google safety category."
2400
3781
  }),
2401
- threshold: zod.z.string().min(1).meta({
3782
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
2402
3783
  title: "Safety Threshold",
2403
- description: "Google safety threshold identifier."
3784
+ description: "Documented Google safety threshold."
2404
3785
  })
2405
3786
  })
2406
3787
  ).optional().meta({
@@ -2419,9 +3800,9 @@ var Gemini36FlashConfigSchema = zod.z.union([
2419
3800
  description: "Allowlisted Google tools for gemini-3.6-flash."
2420
3801
  }),
2421
3802
  httpOptions: zod.z.strictObject({
2422
- timeout: zod.z.number().int().positive().optional().meta({
3803
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
2423
3804
  title: "HTTP Timeout",
2424
- description: "Per-request Google transport timeout in milliseconds."
3805
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
2425
3806
  })
2426
3807
  }).optional().meta({
2427
3808
  title: "HTTP Options",
@@ -2441,7 +3822,7 @@ var Gemini36FlashConfigSchema = zod.z.union([
2441
3822
  })
2442
3823
  }),
2443
3824
  zod.z.strictObject({
2444
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
3825
+ maxOutputTokens: core.maxOutputTokensSchema(GOOGLE_MODEL_LIMITS["gemini-3.6-flash"]).optional().meta({
2445
3826
  title: "Max Output Tokens",
2446
3827
  description: "Maximum output token cap for gemini-3.6-flash."
2447
3828
  }),
@@ -2474,25 +3855,30 @@ var Gemini36FlashConfigSchema = zod.z.union([
2474
3855
  title: "Reasoning",
2475
3856
  description: "Gemini 3.6 Flash thinkingLevel configuration."
2476
3857
  }),
2477
- timeoutMs: zod.z.number().int().positive().optional().meta({
3858
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
2478
3859
  title: "Timeout",
2479
3860
  description: "Logical request timeout in milliseconds."
2480
3861
  }),
2481
3862
  providerOptions: zod.z.strictObject({
2482
3863
  google: zod.z.strictObject({
2483
- cachedContent: zod.z.string().min(1).optional().meta({
2484
- title: "Cached Content",
2485
- description: "Google cached content resource name."
3864
+ allowSchemaWithSearch: zod.z.boolean().optional().meta({
3865
+ title: "Allow Schema With Search",
3866
+ description: "Admit googleSearch together with a response schema. Turns requireGrounding on unless it is false."
3867
+ }),
3868
+ requireGrounding: zod.z.boolean().optional().meta({
3869
+ title: "Require Grounding",
3870
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
2486
3871
  }),
3872
+ cachedContent: cachedContentSchema,
2487
3873
  safetySettings: zod.z.array(
2488
3874
  zod.z.strictObject({
2489
- category: zod.z.string().min(1).meta({
3875
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
2490
3876
  title: "Safety Category",
2491
- description: "Google safety category identifier."
3877
+ description: "Documented Google safety category."
2492
3878
  }),
2493
- threshold: zod.z.string().min(1).meta({
3879
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
2494
3880
  title: "Safety Threshold",
2495
- description: "Google safety threshold identifier."
3881
+ description: "Documented Google safety threshold."
2496
3882
  })
2497
3883
  })
2498
3884
  ).optional().meta({
@@ -2511,9 +3897,9 @@ var Gemini36FlashConfigSchema = zod.z.union([
2511
3897
  description: "Allowlisted Google tools for gemini-3.6-flash."
2512
3898
  }),
2513
3899
  httpOptions: zod.z.strictObject({
2514
- timeout: zod.z.number().int().positive().optional().meta({
3900
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
2515
3901
  title: "HTTP Timeout",
2516
- description: "Per-request Google transport timeout in milliseconds."
3902
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
2517
3903
  })
2518
3904
  }).optional().meta({
2519
3905
  title: "HTTP Options",
@@ -2535,7 +3921,7 @@ var Gemini36FlashConfigSchema = zod.z.union([
2535
3921
  });
2536
3922
  var Gemini37FlashConfigSchema = zod.z.union([
2537
3923
  zod.z.strictObject({
2538
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
3924
+ maxOutputTokens: core.maxOutputTokensSchema(GOOGLE_MODEL_LIMITS["gemini-3.7-flash"]).optional().meta({
2539
3925
  title: "Max Output Tokens",
2540
3926
  description: "Maximum output token cap for gemini-3.7-flash."
2541
3927
  }),
@@ -2568,25 +3954,30 @@ var Gemini37FlashConfigSchema = zod.z.union([
2568
3954
  title: "Reasoning",
2569
3955
  description: "Gemini 3.7 Flash thinkingLevel configuration."
2570
3956
  }),
2571
- timeoutMs: zod.z.number().int().positive().optional().meta({
3957
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
2572
3958
  title: "Timeout",
2573
3959
  description: "Logical request timeout in milliseconds."
2574
3960
  }),
2575
3961
  providerOptions: zod.z.strictObject({
2576
3962
  google: zod.z.strictObject({
2577
- cachedContent: zod.z.string().min(1).optional().meta({
2578
- title: "Cached Content",
2579
- description: "Google cached content resource name."
3963
+ allowSchemaWithSearch: zod.z.boolean().optional().meta({
3964
+ title: "Allow Schema With Search",
3965
+ description: "Admit googleSearch together with a response schema. Turns requireGrounding on unless it is false."
2580
3966
  }),
3967
+ requireGrounding: zod.z.boolean().optional().meta({
3968
+ title: "Require Grounding",
3969
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
3970
+ }),
3971
+ cachedContent: cachedContentSchema,
2581
3972
  safetySettings: zod.z.array(
2582
3973
  zod.z.strictObject({
2583
- category: zod.z.string().min(1).meta({
3974
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
2584
3975
  title: "Safety Category",
2585
- description: "Google safety category identifier."
3976
+ description: "Documented Google safety category."
2586
3977
  }),
2587
- threshold: zod.z.string().min(1).meta({
3978
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
2588
3979
  title: "Safety Threshold",
2589
- description: "Google safety threshold identifier."
3980
+ description: "Documented Google safety threshold."
2590
3981
  })
2591
3982
  })
2592
3983
  ).optional().meta({
@@ -2605,9 +3996,9 @@ var Gemini37FlashConfigSchema = zod.z.union([
2605
3996
  description: "Allowlisted Google tools for gemini-3.7-flash."
2606
3997
  }),
2607
3998
  httpOptions: zod.z.strictObject({
2608
- timeout: zod.z.number().int().positive().optional().meta({
3999
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
2609
4000
  title: "HTTP Timeout",
2610
- description: "Per-request Google transport timeout in milliseconds."
4001
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
2611
4002
  })
2612
4003
  }).optional().meta({
2613
4004
  title: "HTTP Options",
@@ -2627,7 +4018,7 @@ var Gemini37FlashConfigSchema = zod.z.union([
2627
4018
  })
2628
4019
  }),
2629
4020
  zod.z.strictObject({
2630
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
4021
+ maxOutputTokens: core.maxOutputTokensSchema(GOOGLE_MODEL_LIMITS["gemini-3.7-flash"]).optional().meta({
2631
4022
  title: "Max Output Tokens",
2632
4023
  description: "Maximum output token cap for gemini-3.7-flash."
2633
4024
  }),
@@ -2660,25 +4051,30 @@ var Gemini37FlashConfigSchema = zod.z.union([
2660
4051
  title: "Reasoning",
2661
4052
  description: "Gemini 3.7 Flash thinkingLevel configuration."
2662
4053
  }),
2663
- timeoutMs: zod.z.number().int().positive().optional().meta({
4054
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
2664
4055
  title: "Timeout",
2665
4056
  description: "Logical request timeout in milliseconds."
2666
4057
  }),
2667
4058
  providerOptions: zod.z.strictObject({
2668
4059
  google: zod.z.strictObject({
2669
- cachedContent: zod.z.string().min(1).optional().meta({
2670
- title: "Cached Content",
2671
- description: "Google cached content resource name."
4060
+ allowSchemaWithSearch: zod.z.boolean().optional().meta({
4061
+ title: "Allow Schema With Search",
4062
+ description: "Admit googleSearch together with a response schema. Turns requireGrounding on unless it is false."
4063
+ }),
4064
+ requireGrounding: zod.z.boolean().optional().meta({
4065
+ title: "Require Grounding",
4066
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
2672
4067
  }),
4068
+ cachedContent: cachedContentSchema,
2673
4069
  safetySettings: zod.z.array(
2674
4070
  zod.z.strictObject({
2675
- category: zod.z.string().min(1).meta({
4071
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
2676
4072
  title: "Safety Category",
2677
- description: "Google safety category identifier."
4073
+ description: "Documented Google safety category."
2678
4074
  }),
2679
- threshold: zod.z.string().min(1).meta({
4075
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
2680
4076
  title: "Safety Threshold",
2681
- description: "Google safety threshold identifier."
4077
+ description: "Documented Google safety threshold."
2682
4078
  })
2683
4079
  })
2684
4080
  ).optional().meta({
@@ -2697,9 +4093,9 @@ var Gemini37FlashConfigSchema = zod.z.union([
2697
4093
  description: "Allowlisted Google tools for gemini-3.7-flash."
2698
4094
  }),
2699
4095
  httpOptions: zod.z.strictObject({
2700
- timeout: zod.z.number().int().positive().optional().meta({
4096
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
2701
4097
  title: "HTTP Timeout",
2702
- description: "Per-request Google transport timeout in milliseconds."
4098
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
2703
4099
  })
2704
4100
  }).optional().meta({
2705
4101
  title: "HTTP Options",
@@ -2721,7 +4117,7 @@ var Gemini37FlashConfigSchema = zod.z.union([
2721
4117
  });
2722
4118
  var Gemini38FlashConfigSchema = zod.z.union([
2723
4119
  zod.z.strictObject({
2724
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
4120
+ maxOutputTokens: core.maxOutputTokensSchema(GOOGLE_MODEL_LIMITS["gemini-3.8-flash"]).optional().meta({
2725
4121
  title: "Max Output Tokens",
2726
4122
  description: "Maximum output token cap for gemini-3.8-flash."
2727
4123
  }),
@@ -2754,25 +4150,30 @@ var Gemini38FlashConfigSchema = zod.z.union([
2754
4150
  title: "Reasoning",
2755
4151
  description: "Gemini 3.8 Flash thinkingLevel configuration."
2756
4152
  }),
2757
- timeoutMs: zod.z.number().int().positive().optional().meta({
4153
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
2758
4154
  title: "Timeout",
2759
4155
  description: "Logical request timeout in milliseconds."
2760
4156
  }),
2761
4157
  providerOptions: zod.z.strictObject({
2762
4158
  google: zod.z.strictObject({
2763
- cachedContent: zod.z.string().min(1).optional().meta({
2764
- title: "Cached Content",
2765
- description: "Google cached content resource name."
4159
+ allowSchemaWithSearch: zod.z.boolean().optional().meta({
4160
+ title: "Allow Schema With Search",
4161
+ description: "Admit googleSearch together with a response schema. Turns requireGrounding on unless it is false."
2766
4162
  }),
4163
+ requireGrounding: zod.z.boolean().optional().meta({
4164
+ title: "Require Grounding",
4165
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
4166
+ }),
4167
+ cachedContent: cachedContentSchema,
2767
4168
  safetySettings: zod.z.array(
2768
4169
  zod.z.strictObject({
2769
- category: zod.z.string().min(1).meta({
4170
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
2770
4171
  title: "Safety Category",
2771
- description: "Google safety category identifier."
4172
+ description: "Documented Google safety category."
2772
4173
  }),
2773
- threshold: zod.z.string().min(1).meta({
4174
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
2774
4175
  title: "Safety Threshold",
2775
- description: "Google safety threshold identifier."
4176
+ description: "Documented Google safety threshold."
2776
4177
  })
2777
4178
  })
2778
4179
  ).optional().meta({
@@ -2791,9 +4192,9 @@ var Gemini38FlashConfigSchema = zod.z.union([
2791
4192
  description: "Allowlisted Google tools for gemini-3.8-flash."
2792
4193
  }),
2793
4194
  httpOptions: zod.z.strictObject({
2794
- timeout: zod.z.number().int().positive().optional().meta({
4195
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
2795
4196
  title: "HTTP Timeout",
2796
- description: "Per-request Google transport timeout in milliseconds."
4197
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
2797
4198
  })
2798
4199
  }).optional().meta({
2799
4200
  title: "HTTP Options",
@@ -2813,7 +4214,7 @@ var Gemini38FlashConfigSchema = zod.z.union([
2813
4214
  })
2814
4215
  }),
2815
4216
  zod.z.strictObject({
2816
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
4217
+ maxOutputTokens: core.maxOutputTokensSchema(GOOGLE_MODEL_LIMITS["gemini-3.8-flash"]).optional().meta({
2817
4218
  title: "Max Output Tokens",
2818
4219
  description: "Maximum output token cap for gemini-3.8-flash."
2819
4220
  }),
@@ -2846,25 +4247,30 @@ var Gemini38FlashConfigSchema = zod.z.union([
2846
4247
  title: "Reasoning",
2847
4248
  description: "Gemini 3.8 Flash thinkingLevel configuration."
2848
4249
  }),
2849
- timeoutMs: zod.z.number().int().positive().optional().meta({
4250
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
2850
4251
  title: "Timeout",
2851
4252
  description: "Logical request timeout in milliseconds."
2852
4253
  }),
2853
4254
  providerOptions: zod.z.strictObject({
2854
4255
  google: zod.z.strictObject({
2855
- cachedContent: zod.z.string().min(1).optional().meta({
2856
- title: "Cached Content",
2857
- description: "Google cached content resource name."
4256
+ allowSchemaWithSearch: zod.z.boolean().optional().meta({
4257
+ title: "Allow Schema With Search",
4258
+ description: "Admit googleSearch together with a response schema. Turns requireGrounding on unless it is false."
4259
+ }),
4260
+ requireGrounding: zod.z.boolean().optional().meta({
4261
+ title: "Require Grounding",
4262
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
2858
4263
  }),
4264
+ cachedContent: cachedContentSchema,
2859
4265
  safetySettings: zod.z.array(
2860
4266
  zod.z.strictObject({
2861
- category: zod.z.string().min(1).meta({
4267
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
2862
4268
  title: "Safety Category",
2863
- description: "Google safety category identifier."
4269
+ description: "Documented Google safety category."
2864
4270
  }),
2865
- threshold: zod.z.string().min(1).meta({
4271
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
2866
4272
  title: "Safety Threshold",
2867
- description: "Google safety threshold identifier."
4273
+ description: "Documented Google safety threshold."
2868
4274
  })
2869
4275
  })
2870
4276
  ).optional().meta({
@@ -2883,9 +4289,9 @@ var Gemini38FlashConfigSchema = zod.z.union([
2883
4289
  description: "Allowlisted Google tools for gemini-3.8-flash."
2884
4290
  }),
2885
4291
  httpOptions: zod.z.strictObject({
2886
- timeout: zod.z.number().int().positive().optional().meta({
4292
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
2887
4293
  title: "HTTP Timeout",
2888
- description: "Per-request Google transport timeout in milliseconds."
4294
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
2889
4295
  })
2890
4296
  }).optional().meta({
2891
4297
  title: "HTTP Options",
@@ -2919,7 +4325,7 @@ var Gemma431bItConfigSchema = zod.z.strictObject({
2919
4325
  title: "Top K",
2920
4326
  description: "Top-k sampling limit for gemma-4-31b-it."
2921
4327
  }),
2922
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
4328
+ maxOutputTokens: core.maxOutputTokensSchema(GOOGLE_MODEL_LIMITS["gemma-4-31b-it"]).optional().meta({
2923
4329
  title: "Max Output Tokens",
2924
4330
  description: "Maximum output token cap for gemma-4-31b-it."
2925
4331
  }),
@@ -2948,25 +4354,25 @@ var Gemma431bItConfigSchema = zod.z.strictObject({
2948
4354
  title: "Reasoning",
2949
4355
  description: "Gemma 4 31B IT thinkingLevel configuration."
2950
4356
  }),
2951
- timeoutMs: zod.z.number().int().positive().optional().meta({
4357
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
2952
4358
  title: "Timeout",
2953
4359
  description: "Logical request timeout in milliseconds."
2954
4360
  }),
2955
4361
  providerOptions: zod.z.strictObject({
2956
4362
  google: zod.z.strictObject({
2957
- cachedContent: zod.z.string().min(1).optional().meta({
2958
- title: "Cached Content",
2959
- description: "Google cached content resource name."
4363
+ requireGrounding: zod.z.boolean().optional().meta({
4364
+ title: "Require Grounding",
4365
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
2960
4366
  }),
2961
4367
  safetySettings: zod.z.array(
2962
4368
  zod.z.strictObject({
2963
- category: zod.z.string().min(1).meta({
4369
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
2964
4370
  title: "Safety Category",
2965
- description: "Google safety category identifier."
4371
+ description: "Documented Google safety category."
2966
4372
  }),
2967
- threshold: zod.z.string().min(1).meta({
4373
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
2968
4374
  title: "Safety Threshold",
2969
- description: "Google safety threshold identifier."
4375
+ description: "Documented Google safety threshold."
2970
4376
  })
2971
4377
  })
2972
4378
  ).optional().meta({
@@ -2985,9 +4391,9 @@ var Gemma431bItConfigSchema = zod.z.strictObject({
2985
4391
  description: "Allowlisted Google tools for gemma-4-31b-it."
2986
4392
  }),
2987
4393
  httpOptions: zod.z.strictObject({
2988
- timeout: zod.z.number().int().positive().optional().meta({
4394
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
2989
4395
  title: "HTTP Timeout",
2990
- description: "Per-request Google transport timeout in milliseconds."
4396
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
2991
4397
  })
2992
4398
  }).optional().meta({
2993
4399
  title: "HTTP Options",
@@ -3020,7 +4426,7 @@ var Gemma426bA4bItConfigSchema = zod.z.strictObject({
3020
4426
  title: "Top K",
3021
4427
  description: "Top-k sampling limit for gemma-4-26b-a4b-it."
3022
4428
  }),
3023
- maxOutputTokens: zod.z.number().int().positive().optional().meta({
4429
+ maxOutputTokens: core.maxOutputTokensSchema(GOOGLE_MODEL_LIMITS["gemma-4-26b-a4b-it"]).optional().meta({
3024
4430
  title: "Max Output Tokens",
3025
4431
  description: "Maximum output token cap for gemma-4-26b-a4b-it."
3026
4432
  }),
@@ -3049,25 +4455,25 @@ var Gemma426bA4bItConfigSchema = zod.z.strictObject({
3049
4455
  title: "Reasoning",
3050
4456
  description: "Gemma 4 26B A4B IT thinkingLevel configuration."
3051
4457
  }),
3052
- timeoutMs: zod.z.number().int().positive().optional().meta({
4458
+ timeoutMs: zod.z.number().int().positive().max(GOOGLE_MAX_TIMEOUT_MS).optional().meta({
3053
4459
  title: "Timeout",
3054
4460
  description: "Logical request timeout in milliseconds."
3055
4461
  }),
3056
4462
  providerOptions: zod.z.strictObject({
3057
4463
  google: zod.z.strictObject({
3058
- cachedContent: zod.z.string().min(1).optional().meta({
3059
- title: "Cached Content",
3060
- description: "Google cached content resource name."
4464
+ requireGrounding: zod.z.boolean().optional().meta({
4465
+ title: "Require Grounding",
4466
+ description: "Fail the call unless the response proves Google Search ran (grounding metadata with at least one query). Judged only on a candidate that finished with STOP: a MAX_TOKENS or other abnormal finish returns with its own finish reason, a filtered one throws content_filter. The error is retryable only when no response schema is attached."
3061
4467
  }),
3062
4468
  safetySettings: zod.z.array(
3063
4469
  zod.z.strictObject({
3064
- category: zod.z.string().min(1).meta({
4470
+ category: zod.z.enum(GOOGLE_SAFETY_CATEGORIES).meta({
3065
4471
  title: "Safety Category",
3066
- description: "Google safety category identifier."
4472
+ description: "Documented Google safety category."
3067
4473
  }),
3068
- threshold: zod.z.string().min(1).meta({
4474
+ threshold: zod.z.enum(GOOGLE_SAFETY_THRESHOLDS).meta({
3069
4475
  title: "Safety Threshold",
3070
- description: "Google safety threshold identifier."
4476
+ description: "Documented Google safety threshold."
3071
4477
  })
3072
4478
  })
3073
4479
  ).optional().meta({
@@ -3086,9 +4492,9 @@ var Gemma426bA4bItConfigSchema = zod.z.strictObject({
3086
4492
  description: "Allowlisted Google tools for gemma-4-26b-a4b-it."
3087
4493
  }),
3088
4494
  httpOptions: zod.z.strictObject({
3089
- timeout: zod.z.number().int().positive().optional().meta({
4495
+ timeout: zod.z.number().int().positive().max(MAX_TIMER_MS).optional().meta({
3090
4496
  title: "HTTP Timeout",
3091
- description: "Per-request Google transport timeout in milliseconds."
4497
+ description: "Per-request Google transport timeout in milliseconds, at most 2147483647; with timeoutMs set, at least timeoutMs + 5000."
3092
4498
  })
3093
4499
  }).optional().meta({
3094
4500
  title: "HTTP Options",
@@ -3126,12 +4532,13 @@ var geminiModelDescriptors = [
3126
4532
  {
3127
4533
  model: "gemini-2.5-pro",
3128
4534
  provider: "google",
4535
+ limits: GOOGLE_MODEL_LIMITS["gemini-2.5-pro"],
3129
4536
  pricingFamily: "gemini-2.5-pro",
3130
4537
  capabilities: {
4538
+ inputMimeTypes: GEMINI_INPUT_MIME_TYPES,
3131
4539
  reasoning: true,
3132
4540
  structuredOutput: true,
3133
4541
  nativeStructuredOutput: true,
3134
- vision: true,
3135
4542
  reasoningApi: "budget",
3136
4543
  admittedReasoningEfforts: GEMINI_STANDARD_REASONING_EFFORTS,
3137
4544
  sampling: "tunable",
@@ -3141,18 +4548,20 @@ var geminiModelDescriptors = [
3141
4548
  serviceTiers: ["flex", "standard"]
3142
4549
  },
3143
4550
  configSchema: Gemini25ProConfigSchema,
4551
+ configKeys: core.toConfigKeys(Gemini25ProConfigSchema),
3144
4552
  configJsonSchema: core.toConfigJsonSchema(Gemini25ProConfigSchema),
3145
4553
  validateConfig: core.zodToStandardSchema(Gemini25ProConfigSchema)
3146
4554
  },
3147
4555
  {
3148
4556
  model: "gemini-2.5-flash",
3149
4557
  provider: "google",
4558
+ limits: GOOGLE_MODEL_LIMITS["gemini-2.5-flash"],
3150
4559
  pricingFamily: "gemini-2.5-flash",
3151
4560
  capabilities: {
4561
+ inputMimeTypes: GEMINI_INPUT_MIME_TYPES,
3152
4562
  reasoning: true,
3153
4563
  structuredOutput: true,
3154
4564
  nativeStructuredOutput: true,
3155
- vision: true,
3156
4565
  reasoningApi: "budget",
3157
4566
  admittedReasoningEfforts: GEMINI_LEVEL_WITH_NONE_REASONING_EFFORTS,
3158
4567
  sampling: "tunable",
@@ -3162,18 +4571,20 @@ var geminiModelDescriptors = [
3162
4571
  serviceTiers: ["flex", "standard"]
3163
4572
  },
3164
4573
  configSchema: Gemini25FlashConfigSchema,
4574
+ configKeys: core.toConfigKeys(Gemini25FlashConfigSchema),
3165
4575
  configJsonSchema: core.toConfigJsonSchema(Gemini25FlashConfigSchema),
3166
4576
  validateConfig: core.zodToStandardSchema(Gemini25FlashConfigSchema)
3167
4577
  },
3168
4578
  {
3169
4579
  model: "gemini-2.5-flash-lite",
3170
4580
  provider: "google",
4581
+ limits: GOOGLE_MODEL_LIMITS["gemini-2.5-flash-lite"],
3171
4582
  pricingFamily: "gemini-2.5-flash-lite",
3172
4583
  capabilities: {
4584
+ inputMimeTypes: GEMINI_INPUT_MIME_TYPES,
3173
4585
  reasoning: true,
3174
4586
  structuredOutput: true,
3175
4587
  nativeStructuredOutput: true,
3176
- vision: true,
3177
4588
  reasoningApi: "budget",
3178
4589
  admittedReasoningEfforts: GEMINI_LEVEL_WITH_NONE_REASONING_EFFORTS,
3179
4590
  sampling: "tunable",
@@ -3183,138 +4594,167 @@ var geminiModelDescriptors = [
3183
4594
  serviceTiers: ["flex", "standard"]
3184
4595
  },
3185
4596
  configSchema: Gemini25FlashLiteConfigSchema,
4597
+ configKeys: core.toConfigKeys(Gemini25FlashLiteConfigSchema),
3186
4598
  configJsonSchema: core.toConfigJsonSchema(Gemini25FlashLiteConfigSchema),
3187
4599
  validateConfig: core.zodToStandardSchema(Gemini25FlashLiteConfigSchema)
3188
4600
  },
3189
4601
  {
3190
4602
  model: "gemini-3.1-flash-lite",
3191
4603
  provider: "google",
4604
+ // Google's deprecations page (https://ai.google.dev/gemini-api/docs/deprecations,
4605
+ // "Page last updated" 2026-10-01, read 2026-10-03) lists a May 7, 2027
4606
+ // shutdown, replacement gemini-3.5-flash-lite.
4607
+ shutdownDate: "2027-05-07",
4608
+ limits: GOOGLE_MODEL_LIMITS["gemini-3.1-flash-lite"],
3192
4609
  pricingFamily: "gemini-3.1-flash-lite",
3193
4610
  capabilities: {
4611
+ inputMimeTypes: GEMINI_INPUT_MIME_TYPES,
3194
4612
  reasoning: true,
3195
4613
  structuredOutput: true,
3196
4614
  nativeStructuredOutput: true,
3197
- vision: true,
3198
4615
  reasoningApi: "level",
3199
4616
  admittedReasoningEfforts: GEMINI_LEVEL_WITH_NONE_REASONING_EFFORTS,
3200
4617
  sampling: "fixed",
3201
4618
  caching: { explicit: true, minTokens: 1024 },
3202
4619
  grounding: true,
3203
4620
  functionCalling: true,
3204
- structuredOutputWithTools: true,
4621
+ structuredOutputWithTools: false,
4622
+ continuation: "history",
4623
+ providerState: true,
3205
4624
  serviceTiers: ["flex", "standard"]
3206
4625
  },
3207
4626
  configSchema: Gemini31FlashLiteConfigSchema,
4627
+ configKeys: core.toConfigKeys(Gemini31FlashLiteConfigSchema),
3208
4628
  configJsonSchema: core.toConfigJsonSchema(Gemini31FlashLiteConfigSchema),
3209
4629
  validateConfig: core.zodToStandardSchema(Gemini31FlashLiteConfigSchema)
3210
4630
  },
3211
4631
  {
3212
4632
  model: "gemini-3.1-pro-preview",
3213
4633
  provider: "google",
4634
+ limits: GOOGLE_MODEL_LIMITS["gemini-3.1-pro-preview"],
3214
4635
  pricingFamily: "gemini-3.1-pro-preview",
3215
4636
  capabilities: {
4637
+ inputMimeTypes: GEMINI_INPUT_MIME_TYPES,
3216
4638
  reasoning: true,
3217
4639
  structuredOutput: true,
3218
4640
  nativeStructuredOutput: true,
3219
- vision: true,
3220
4641
  reasoningApi: "level",
3221
4642
  admittedReasoningEfforts: GEMINI_STANDARD_REASONING_EFFORTS,
3222
4643
  sampling: "fixed",
3223
4644
  caching: { explicit: true, minTokens: 1024 },
3224
4645
  grounding: true,
3225
4646
  functionCalling: true,
3226
- structuredOutputWithTools: true,
4647
+ structuredOutputWithTools: false,
4648
+ continuation: "history",
4649
+ providerState: true,
3227
4650
  serviceTiers: ["flex", "standard"]
3228
4651
  },
3229
4652
  configSchema: Gemini31ProPreviewConfigSchema,
4653
+ configKeys: core.toConfigKeys(Gemini31ProPreviewConfigSchema),
3230
4654
  configJsonSchema: core.toConfigJsonSchema(Gemini31ProPreviewConfigSchema),
3231
4655
  validateConfig: core.zodToStandardSchema(Gemini31ProPreviewConfigSchema)
3232
4656
  },
3233
4657
  {
3234
4658
  model: "gemini-3.8-flash",
3235
4659
  provider: "google",
4660
+ limits: GOOGLE_MODEL_LIMITS["gemini-3.8-flash"],
3236
4661
  pricingFamily: "gemini-3.8-flash",
3237
4662
  capabilities: {
4663
+ inputMimeTypes: GEMINI_INPUT_MIME_TYPES,
3238
4664
  reasoning: true,
3239
4665
  structuredOutput: true,
3240
4666
  nativeStructuredOutput: true,
3241
- vision: true,
3242
4667
  reasoningApi: "level",
3243
4668
  admittedReasoningEfforts: GEMINI_STANDARD_REASONING_EFFORTS,
3244
4669
  sampling: "fixed",
3245
4670
  caching: { explicit: true, minTokens: 1024 },
3246
4671
  grounding: true,
3247
4672
  functionCalling: true,
3248
- structuredOutputWithTools: true,
4673
+ structuredOutputWithTools: false,
4674
+ continuation: "history",
4675
+ providerState: true,
3249
4676
  serviceTiers: ["flex", "standard"]
3250
4677
  },
3251
4678
  configSchema: Gemini38FlashConfigSchema,
4679
+ configKeys: core.toConfigKeys(Gemini38FlashConfigSchema),
3252
4680
  configJsonSchema: core.toConfigJsonSchema(Gemini38FlashConfigSchema),
3253
4681
  validateConfig: core.zodToStandardSchema(Gemini38FlashConfigSchema)
3254
4682
  },
3255
4683
  {
3256
4684
  model: "gemini-3.7-flash",
3257
4685
  provider: "google",
4686
+ limits: GOOGLE_MODEL_LIMITS["gemini-3.7-flash"],
3258
4687
  pricingFamily: "gemini-3.7-flash",
3259
4688
  capabilities: {
4689
+ inputMimeTypes: GEMINI_INPUT_MIME_TYPES,
3260
4690
  reasoning: true,
3261
4691
  structuredOutput: true,
3262
4692
  nativeStructuredOutput: true,
3263
- vision: true,
3264
4693
  reasoningApi: "level",
3265
4694
  admittedReasoningEfforts: GEMINI_STANDARD_REASONING_EFFORTS,
3266
4695
  sampling: "fixed",
3267
4696
  caching: { explicit: true, minTokens: 1024 },
3268
4697
  grounding: true,
3269
4698
  functionCalling: true,
3270
- structuredOutputWithTools: true,
4699
+ structuredOutputWithTools: false,
4700
+ continuation: "history",
4701
+ providerState: true,
3271
4702
  serviceTiers: ["flex", "standard"]
3272
4703
  },
3273
4704
  configSchema: Gemini37FlashConfigSchema,
4705
+ configKeys: core.toConfigKeys(Gemini37FlashConfigSchema),
3274
4706
  configJsonSchema: core.toConfigJsonSchema(Gemini37FlashConfigSchema),
3275
4707
  validateConfig: core.zodToStandardSchema(Gemini37FlashConfigSchema)
3276
4708
  },
3277
4709
  {
3278
4710
  model: "gemini-3.6-flash",
3279
4711
  provider: "google",
4712
+ limits: GOOGLE_MODEL_LIMITS["gemini-3.6-flash"],
3280
4713
  pricingFamily: "gemini-3.6-flash",
3281
4714
  capabilities: {
4715
+ inputMimeTypes: GEMINI_INPUT_MIME_TYPES,
3282
4716
  reasoning: true,
3283
4717
  structuredOutput: true,
3284
4718
  nativeStructuredOutput: true,
3285
- vision: true,
3286
4719
  reasoningApi: "level",
3287
4720
  admittedReasoningEfforts: GEMINI_LEVEL_WITH_NONE_REASONING_EFFORTS,
3288
4721
  sampling: "fixed",
3289
4722
  caching: { explicit: true, minTokens: 1024 },
3290
4723
  grounding: true,
3291
4724
  functionCalling: true,
3292
- structuredOutputWithTools: true,
4725
+ structuredOutputWithTools: false,
4726
+ continuation: "history",
4727
+ providerState: true,
3293
4728
  serviceTiers: ["flex", "standard"]
3294
4729
  },
3295
4730
  configSchema: Gemini36FlashConfigSchema,
4731
+ configKeys: core.toConfigKeys(Gemini36FlashConfigSchema),
3296
4732
  configJsonSchema: core.toConfigJsonSchema(Gemini36FlashConfigSchema),
3297
4733
  validateConfig: core.zodToStandardSchema(Gemini36FlashConfigSchema)
3298
4734
  },
3299
4735
  {
3300
4736
  model: "gemini-3.5-flash-lite",
3301
4737
  provider: "google",
4738
+ limits: GOOGLE_MODEL_LIMITS["gemini-3.5-flash-lite"],
3302
4739
  pricingFamily: "gemini-3.5-flash-lite",
3303
4740
  capabilities: {
4741
+ inputMimeTypes: GEMINI_INPUT_MIME_TYPES,
3304
4742
  reasoning: true,
3305
4743
  structuredOutput: true,
3306
4744
  nativeStructuredOutput: true,
3307
- vision: true,
3308
4745
  reasoningApi: "level",
3309
4746
  admittedReasoningEfforts: GEMINI_LEVEL_WITH_NONE_REASONING_EFFORTS,
3310
4747
  sampling: "fixed",
3311
4748
  caching: { explicit: true, minTokens: 1024 },
3312
4749
  grounding: true,
3313
4750
  functionCalling: true,
3314
- structuredOutputWithTools: true,
4751
+ structuredOutputWithTools: false,
4752
+ continuation: "history",
4753
+ providerState: true,
3315
4754
  serviceTiers: ["flex", "standard"]
3316
4755
  },
3317
4756
  configSchema: Gemini35FlashLiteConfigSchema,
4757
+ configKeys: core.toConfigKeys(Gemini35FlashLiteConfigSchema),
3318
4758
  configJsonSchema: core.toConfigJsonSchema(Gemini35FlashLiteConfigSchema),
3319
4759
  validateConfig: core.zodToStandardSchema(Gemini35FlashLiteConfigSchema)
3320
4760
  }
@@ -3323,34 +4763,38 @@ var gemmaModelDescriptors = [
3323
4763
  {
3324
4764
  model: "gemma-4-31b-it",
3325
4765
  provider: "google",
4766
+ limits: GOOGLE_MODEL_LIMITS["gemma-4-31b-it"],
3326
4767
  capabilities: {
4768
+ inputMimeTypes: GEMMA_INPUT_MIME_TYPES,
3327
4769
  reasoning: true,
3328
4770
  reasoningApi: "level",
3329
4771
  admittedReasoningEfforts: GEMMA_REASONING_EFFORTS,
3330
4772
  structuredOutput: true,
3331
4773
  nativeStructuredOutput: true,
3332
4774
  grounding: true,
3333
- vision: true,
3334
4775
  sampling: "tunable"
3335
4776
  },
3336
4777
  configSchema: Gemma431bItConfigSchema,
4778
+ configKeys: core.toConfigKeys(Gemma431bItConfigSchema),
3337
4779
  configJsonSchema: core.toConfigJsonSchema(Gemma431bItConfigSchema),
3338
4780
  validateConfig: core.zodToStandardSchema(Gemma431bItConfigSchema)
3339
4781
  },
3340
4782
  {
3341
4783
  model: "gemma-4-26b-a4b-it",
3342
4784
  provider: "google",
4785
+ limits: GOOGLE_MODEL_LIMITS["gemma-4-26b-a4b-it"],
3343
4786
  capabilities: {
4787
+ inputMimeTypes: GEMMA_INPUT_MIME_TYPES,
3344
4788
  reasoning: true,
3345
4789
  reasoningApi: "level",
3346
4790
  admittedReasoningEfforts: GEMMA_REASONING_EFFORTS,
3347
4791
  structuredOutput: true,
3348
4792
  nativeStructuredOutput: true,
3349
4793
  grounding: true,
3350
- vision: true,
3351
4794
  sampling: "tunable"
3352
4795
  },
3353
4796
  configSchema: Gemma426bA4bItConfigSchema,
4797
+ configKeys: core.toConfigKeys(Gemma426bA4bItConfigSchema),
3354
4798
  configJsonSchema: core.toConfigJsonSchema(Gemma426bA4bItConfigSchema),
3355
4799
  validateConfig: core.zodToStandardSchema(Gemma426bA4bItConfigSchema)
3356
4800
  }
@@ -3370,16 +4814,32 @@ function googleProvider(opts) {
3370
4814
  }
3371
4815
  var DEFAULT_INTERVAL_MS = 3e3;
3372
4816
  var DEFAULT_TIMEOUT_MS = 3e5;
3373
- var realSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
4817
+ var waitOn = (scheduler) => (ms) => {
4818
+ let timer;
4819
+ const promise = new Promise((resolve) => {
4820
+ timer = scheduler.setTimeout(resolve, ms);
4821
+ });
4822
+ return {
4823
+ promise,
4824
+ cancel: () => {
4825
+ if (timer !== void 0) scheduler.clearTimeout(timer);
4826
+ }
4827
+ };
4828
+ };
4829
+ function pollTimeoutError(name) {
4830
+ return new core.LlmError(`Timed out waiting for uploaded file "${name}" to become ACTIVE`, {
4831
+ kind: "server",
4832
+ retryable: false,
4833
+ provider: "google"
4834
+ });
4835
+ }
3374
4836
  async function buildFilesClient(auth) {
3375
- const { GoogleGenAI } = await import('@google/genai');
3376
- const ai = new GoogleGenAI({ apiKey: requireApiKey(auth) });
4837
+ const ai = await newGoogleGenAI(auth);
3377
4838
  return {
3378
4839
  async upload(params) {
3379
- const fileArg = params.file instanceof Uint8Array ? new Blob(
3380
- [Uint8Array.from(params.file)],
3381
- params.config?.mimeType !== void 0 && params.config.mimeType.length > 0 ? { type: params.config.mimeType } : {}
3382
- ) : params.file;
4840
+ const fileArg = params.file instanceof Uint8Array ? new Blob([Uint8Array.from(params.file)], {
4841
+ ...params.config?.mimeType !== void 0 ? { type: params.config.mimeType } : {}
4842
+ }) : params.file;
3383
4843
  const result = await ai.files.upload({
3384
4844
  file: fileArg,
3385
4845
  ...params.config !== void 0 ? { config: params.config } : {}
@@ -3411,7 +4871,8 @@ var GoogleFileStore = class {
3411
4871
  logger;
3412
4872
  intervalMs;
3413
4873
  timeoutMs;
3414
- sleep;
4874
+ startWait;
4875
+ scheduler;
3415
4876
  now;
3416
4877
  /** Memoised client promise — built at most once per store instance. */
3417
4878
  clientPromise;
@@ -3422,19 +4883,22 @@ var GoogleFileStore = class {
3422
4883
  this.onDeleteError = opts.onDeleteError ?? ((name, err) => {
3423
4884
  if (this.logger !== void 0) {
3424
4885
  this.logger.error(
3425
- { name, error: core.redactSecrets(core.classifyError(err).message) },
4886
+ { name, error: core.redactSecrets(classifyGoogleError(err).message) },
3426
4887
  "gemini.file.delete.failed"
3427
4888
  );
3428
4889
  } else {
3429
4890
  console.error(
3430
4891
  `[GoogleFileStore] delete failed for "${name}":`,
3431
- core.redactSecrets(core.classifyError(err).message)
4892
+ core.redactSecrets(classifyGoogleError(err).message)
3432
4893
  );
3433
4894
  }
3434
4895
  });
3435
4896
  this.intervalMs = opts.poll?.intervalMs ?? DEFAULT_INTERVAL_MS;
3436
4897
  this.timeoutMs = opts.poll?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
3437
- this.sleep = opts.sleep ?? realSleep;
4898
+ this.scheduler = opts.scheduler ?? PLATFORM_SCHEDULER;
4899
+ const customSleep = opts.sleep;
4900
+ this.startWait = customSleep !== void 0 ? (ms) => ({ promise: customSleep(ms), cancel: () => {
4901
+ } }) : waitOn(this.scheduler);
3438
4902
  this.now = opts.now ?? (() => Date.now());
3439
4903
  }
3440
4904
  getClient() {
@@ -3448,23 +4912,41 @@ var GoogleFileStore = class {
3448
4912
  * Upload bytes to the Gemini File API and wait until the file is ACTIVE.
3449
4913
  *
3450
4914
  * @param source - Raw bytes or Blob.
3451
- * @param mimeType - IANA media type, e.g. `"image/png"`.
4915
+ * @param mimeType - IANA media type, e.g. `"image/png"`. It must pass the same
4916
+ * admission rule `generate` applies to a Gemini model's parts (one shared
4917
+ * function, so a file that uploads can be used): an empty or unadmitted type
4918
+ * is `bad_request` before any bytes are sent. The string is sent to Google
4919
+ * unchanged.
3452
4920
  * @param opts - Optional display name.
3453
4921
  */
3454
4922
  async upload(source, mimeType, opts) {
3455
4923
  const signal = opts?.signal;
4924
+ core.assertMediaTypeAdmitted(
4925
+ mimeType,
4926
+ GEMINI_INPUT_MIME_TYPES,
4927
+ "mimeType",
4928
+ "google",
4929
+ "a Google file upload"
4930
+ );
3456
4931
  const client = await this.getClient();
4932
+ if (signal?.aborted === true) {
4933
+ throw new core.LlmError("File upload aborted", { kind: "aborted", retryable: false });
4934
+ }
3457
4935
  let uploadResp;
3458
4936
  try {
3459
- uploadResp = await client.upload({
3460
- file: source,
3461
- config: {
3462
- mimeType,
3463
- ...opts?.displayName !== void 0 ? { displayName: opts.displayName } : {}
3464
- }
3465
- });
4937
+ uploadResp = await abortable(
4938
+ client.upload({
4939
+ file: source,
4940
+ config: {
4941
+ mimeType,
4942
+ ...opts?.displayName !== void 0 ? { displayName: opts.displayName } : {},
4943
+ ...signal !== void 0 ? { abortSignal: signal } : {}
4944
+ }
4945
+ }),
4946
+ signal
4947
+ );
3466
4948
  } catch (e) {
3467
- throw core.classifyError(e);
4949
+ throw classifyGoogleError(e);
3468
4950
  }
3469
4951
  const { name, uri } = uploadResp;
3470
4952
  if (name === void 0 || name.length === 0 || uri === void 0 || uri.length === 0) {
@@ -3479,10 +4961,7 @@ var GoogleFileStore = class {
3479
4961
  return makeHandle(uploadResp, fallback);
3480
4962
  }
3481
4963
  if (uploadResp.state === "FAILED") {
3482
- throw new core.LlmError("File processing failed immediately after upload", {
3483
- kind: "bad_request",
3484
- retryable: false
3485
- });
4964
+ throw failedFile("File processing failed immediately after upload", uploadResp);
3486
4965
  }
3487
4966
  const deadline = this.now() + this.timeoutMs;
3488
4967
  if (signal?.aborted === true) {
@@ -3491,49 +4970,102 @@ var GoogleFileStore = class {
3491
4970
  retryable: false
3492
4971
  });
3493
4972
  }
4973
+ let onAbort;
3494
4974
  const abortRacePromise = signal !== void 0 ? new Promise((_, reject) => {
3495
- signal.addEventListener(
3496
- "abort",
4975
+ onAbort = () => {
4976
+ reject(
4977
+ new core.LlmError("File upload polling aborted", {
4978
+ kind: "aborted",
4979
+ retryable: false
4980
+ })
4981
+ );
4982
+ };
4983
+ signal.addEventListener("abort", onAbort, { once: true });
4984
+ }) : void 0;
4985
+ abortRacePromise?.catch(() => {
4986
+ });
4987
+ try {
4988
+ return await this.pollUntilActive(
4989
+ client,
4990
+ name,
4991
+ fallback,
4992
+ deadline,
4993
+ signal,
4994
+ abortRacePromise
4995
+ );
4996
+ } finally {
4997
+ if (signal !== void 0 && onAbort !== void 0) {
4998
+ signal.removeEventListener("abort", onAbort);
4999
+ }
5000
+ }
5001
+ }
5002
+ /**
5003
+ * `work` raced against the abort promise and the time left until `deadline`
5004
+ * (the deadline is a non-retryable `server` error). `work` is observed, so a
5005
+ * late rejection after the race is lost is not unhandled; the deadline timer
5006
+ * is always cleared.
5007
+ */
5008
+ async raceDeadline(work, name, deadline, abortRacePromise) {
5009
+ work.catch(() => {
5010
+ });
5011
+ const remainingMs = deadline - this.now();
5012
+ let deadlineTimer;
5013
+ const deadlineRace = new Promise((_, reject) => {
5014
+ if (remainingMs > MAX_TIMER_MS) return;
5015
+ deadlineTimer = this.scheduler.setTimeout(
3497
5016
  () => {
3498
- reject(
3499
- new core.LlmError("File upload polling aborted", {
3500
- kind: "aborted",
3501
- retryable: false
3502
- })
3503
- );
5017
+ reject(pollTimeoutError(name));
3504
5018
  },
3505
- { once: true }
5019
+ Math.max(remainingMs, 0)
3506
5020
  );
3507
- }) : void 0;
5021
+ });
5022
+ deadlineRace.catch(() => {
5023
+ });
5024
+ try {
5025
+ return await Promise.race(
5026
+ abortRacePromise !== void 0 ? [work, deadlineRace, abortRacePromise] : [work, deadlineRace]
5027
+ );
5028
+ } finally {
5029
+ if (deadlineTimer !== void 0) this.scheduler.clearTimeout(deadlineTimer);
5030
+ }
5031
+ }
5032
+ /** Polls `name` until ACTIVE; see {@link GoogleFileStore.upload}. */
5033
+ async pollUntilActive(client, name, fallback, deadline, signal, abortRacePromise) {
3508
5034
  for (; ; ) {
3509
5035
  if (this.now() >= deadline) {
3510
- throw new core.LlmError("Timed out waiting for uploaded file to become ACTIVE", {
3511
- kind: "timeout",
3512
- retryable: true
3513
- });
5036
+ throw pollTimeoutError(name);
3514
5037
  }
3515
- const sleepCall = this.sleep(this.intervalMs);
3516
- await (abortRacePromise !== void 0 ? Promise.race([sleepCall, abortRacePromise]) : sleepCall);
3517
- let pollResp;
5038
+ const wait = this.startWait(this.intervalMs);
3518
5039
  try {
3519
- pollResp = await client.get({ name });
3520
- } catch (e) {
3521
- throw core.classifyError(e);
5040
+ await this.raceDeadline(wait.promise, name, deadline, abortRacePromise);
5041
+ } finally {
5042
+ wait.cancel();
5043
+ }
5044
+ if (this.now() >= deadline) {
5045
+ throw pollTimeoutError(name);
3522
5046
  }
5047
+ const pollCall = (async () => {
5048
+ try {
5049
+ return await client.get({ name });
5050
+ } catch (e) {
5051
+ throw classifyGoogleError(e);
5052
+ }
5053
+ })();
5054
+ const pollResp = await this.raceDeadline(pollCall, name, deadline, abortRacePromise);
3523
5055
  if (signal?.aborted === true) {
3524
5056
  throw new core.LlmError("File upload polling aborted", {
3525
5057
  kind: "aborted",
3526
5058
  retryable: false
3527
5059
  });
3528
5060
  }
5061
+ if (this.now() >= deadline) {
5062
+ throw pollTimeoutError(name);
5063
+ }
3529
5064
  if (pollResp.state === "ACTIVE") {
3530
5065
  return makeHandle(pollResp, fallback);
3531
5066
  }
3532
5067
  if (pollResp.state === "FAILED") {
3533
- throw new core.LlmError("File processing failed during polling", {
3534
- kind: "bad_request",
3535
- retryable: false
3536
- });
5068
+ throw failedFile("File processing failed during polling", pollResp);
3537
5069
  }
3538
5070
  }
3539
5071
  }
@@ -3570,15 +5102,7 @@ var GoogleFileStore = class {
3570
5102
  if (isGoogleNotFoundError(err)) {
3571
5103
  return;
3572
5104
  }
3573
- const classified = err instanceof core.LlmError ? err : core.classifyError(err);
3574
- const withProvider = classified.provider === void 0 ? new core.LlmError(classified.message, {
3575
- kind: classified.kind,
3576
- retryable: classified.retryable,
3577
- ...classified.httpStatus !== void 0 ? { httpStatus: classified.httpStatus } : {},
3578
- ...classified.retryAfterMs !== void 0 ? { retryAfterMs: classified.retryAfterMs } : {},
3579
- provider: "google",
3580
- cause: classified.cause ?? err
3581
- }) : classified;
5105
+ const withProvider = classifyGoogleError(err);
3582
5106
  if (failClosed) {
3583
5107
  throw withProvider;
3584
5108
  }
@@ -3600,31 +5124,58 @@ var GoogleFileStore = class {
3600
5124
  await Promise.allSettled(handles.map((h) => this.delete(h, opts)));
3601
5125
  }
3602
5126
  };
3603
- function isGoogleNotFoundError(err) {
3604
- if (err instanceof core.LlmError && err.httpStatus === 404) {
3605
- return true;
3606
- }
3607
- if (typeof err !== "object" || err === null) {
3608
- return false;
3609
- }
3610
- const obj = err;
3611
- if (obj["status"] === 404 || obj["httpStatus"] === 404 || obj["code"] === 404) {
3612
- return true;
3613
- }
3614
- if (obj["status"] === "NOT_FOUND" || obj["code"] === "NOT_FOUND") {
3615
- return true;
3616
- }
3617
- const msg = typeof obj["message"] === "string" ? obj["message"] : "";
3618
- if (/not\s*found|404/i.test(msg) && /file/i.test(msg)) {
3619
- return true;
5127
+ var TRANSIENT_FILE_STATUS_CODES = /* @__PURE__ */ new Set([4, 13, 14]);
5128
+ function failedFile(prefix, resp) {
5129
+ const providerMessage = resp.error?.message;
5130
+ const transient = typeof resp.error?.code === "number" && TRANSIENT_FILE_STATUS_CODES.has(resp.error.code);
5131
+ return new core.LlmError(
5132
+ providerMessage !== void 0 && providerMessage.length > 0 ? `${prefix}: ${providerMessage}` : prefix,
5133
+ {
5134
+ kind: transient ? "server" : "bad_request",
5135
+ retryable: transient,
5136
+ provider: "google",
5137
+ ...resp.error !== void 0 ? { cause: resp.error } : {}
5138
+ }
5139
+ );
5140
+ }
5141
+ async function abortable(promise, signal) {
5142
+ if (signal === void 0) return promise;
5143
+ promise.catch(() => {
5144
+ });
5145
+ let onAbort;
5146
+ const aborted = new Promise((_, reject) => {
5147
+ onAbort = () => {
5148
+ reject(new core.LlmError("File upload aborted", { kind: "aborted", retryable: false }));
5149
+ };
5150
+ signal.addEventListener("abort", onAbort, { once: true });
5151
+ if (signal.aborted) onAbort();
5152
+ });
5153
+ try {
5154
+ return await Promise.race([promise, aborted]);
5155
+ } finally {
5156
+ if (onAbort !== void 0) signal.removeEventListener("abort", onAbort);
3620
5157
  }
3621
- return false;
3622
5158
  }
3623
5159
  var DEFAULT_SKEW_SECONDS = 30;
3624
5160
  var DEFAULT_EXTENSION_SECONDS = 3600;
5161
+ function toolKindsOf(tools) {
5162
+ const kinds = /* @__PURE__ */ new Set();
5163
+ for (const tool of tools ?? []) {
5164
+ for (const [kind, value] of Object.entries(tool)) {
5165
+ if (value !== void 0) kinds.add(kind);
5166
+ }
5167
+ }
5168
+ return [...kinds];
5169
+ }
5170
+ function expiryOf(expireTime, fallbackMs) {
5171
+ if (expireTime !== void 0 && expireTime.length > 0) {
5172
+ const parsed = new Date(expireTime);
5173
+ if (!Number.isNaN(parsed.getTime())) return parsed;
5174
+ }
5175
+ return new Date(fallbackMs);
5176
+ }
3625
5177
  async function buildCachesClient(auth) {
3626
- const { GoogleGenAI } = await import('@google/genai');
3627
- const ai = new GoogleGenAI({ apiKey: requireApiKey(auth) });
5178
+ const ai = await newGoogleGenAI(auth);
3628
5179
  return {
3629
5180
  async create(params) {
3630
5181
  const result = await ai.caches.create(params);
@@ -3663,13 +5214,13 @@ var GoogleCacheStore = class {
3663
5214
  this.onDeleteError = opts.onDeleteError ?? ((cacheName, err) => {
3664
5215
  if (this.logger !== void 0) {
3665
5216
  this.logger.error(
3666
- { name: cacheName, error: core.redactSecrets(core.classifyError(err).message) },
5217
+ { name: cacheName, error: core.redactSecrets(classifyGoogleError(err).message) },
3667
5218
  "gemini.cache.delete.failed"
3668
5219
  );
3669
5220
  } else {
3670
5221
  console.error(
3671
5222
  `[GoogleCacheStore] delete failed for "${cacheName}":`,
3672
- core.redactSecrets(core.classifyError(err).message)
5223
+ core.redactSecrets(classifyGoogleError(err).message)
3673
5224
  );
3674
5225
  }
3675
5226
  });
@@ -3694,11 +5245,18 @@ var GoogleCacheStore = class {
3694
5245
  * `now + ttlSeconds * 1000`.
3695
5246
  */
3696
5247
  async create(input) {
5248
+ if (!Number.isInteger(input.ttlSeconds) || input.ttlSeconds <= 0) {
5249
+ throw new core.LlmError(
5250
+ `GoogleCacheStore.create: ttlSeconds must be a positive integer, got ${String(input.ttlSeconds)}.`,
5251
+ { kind: "bad_request", retryable: false, provider: "google" }
5252
+ );
5253
+ }
3697
5254
  if (this.preflight !== void 0) {
3698
5255
  const counted = await this.preflight.countTokens({
3699
5256
  model: input.model,
3700
5257
  ...input.contents !== void 0 ? { contents: input.contents } : {},
3701
- ...input.systemInstruction !== void 0 ? { systemInstruction: input.systemInstruction } : {}
5258
+ ...input.systemInstruction !== void 0 ? { systemInstruction: input.systemInstruction } : {},
5259
+ ...input.tools !== void 0 ? { tools: input.tools } : {}
3702
5260
  });
3703
5261
  if (counted < this.preflight.minTokens) {
3704
5262
  throw new core.LlmError(
@@ -3712,12 +5270,14 @@ var GoogleCacheStore = class {
3712
5270
  if (input.contents !== void 0) config.contents = input.contents;
3713
5271
  if (input.systemInstruction !== void 0)
3714
5272
  config.systemInstruction = input.systemInstruction;
5273
+ if (input.tools !== void 0) config.tools = input.tools;
5274
+ if (input.toolConfig !== void 0) config.toolConfig = input.toolConfig;
3715
5275
  if (input.displayName !== void 0) config.displayName = input.displayName;
3716
5276
  let resp;
3717
5277
  try {
3718
5278
  resp = await client.create({ model: input.model, config });
3719
5279
  } catch (e) {
3720
- throw core.classifyError(e);
5280
+ throw classifyGoogleError(e);
3721
5281
  }
3722
5282
  if (resp.name === void 0 || resp.name.length === 0) {
3723
5283
  throw new core.LlmError("Cache create response missing required field: name", {
@@ -3726,12 +5286,14 @@ var GoogleCacheStore = class {
3726
5286
  provider: "google"
3727
5287
  });
3728
5288
  }
3729
- const fallbackExpiry = new Date(this.now() + input.ttlSeconds * 1e3);
3730
- const expiresAt = resp.expireTime !== void 0 && resp.expireTime.length > 0 ? new Date(resp.expireTime) : fallbackExpiry;
5289
+ const expiresAt = expiryOf(resp.expireTime, this.now() + input.ttlSeconds * 1e3);
5290
+ const totalTokenCount = resp.usageMetadata?.totalTokenCount;
3731
5291
  return {
3732
5292
  cacheName: resp.name,
3733
5293
  model: resp.model ?? input.model,
3734
- expiresAt
5294
+ expiresAt,
5295
+ ...typeof totalTokenCount === "number" && Number.isFinite(totalTokenCount) ? { totalTokenCount } : {},
5296
+ toolKinds: toolKindsOf(input.tools)
3735
5297
  };
3736
5298
  }
3737
5299
  /**
@@ -3746,8 +5308,9 @@ var GoogleCacheStore = class {
3746
5308
  async getOrCreate(key, factory) {
3747
5309
  const mapKey = `${key.model}:${key.stableKey}`;
3748
5310
  const existing = this.entries.get(mapKey);
3749
- if (existing !== void 0 && this.isLive(existing.handle)) {
3750
- return existing.handle;
5311
+ if (existing !== void 0) {
5312
+ if (this.isLive(existing.handle)) return existing.handle;
5313
+ this.entries.delete(mapKey);
3751
5314
  }
3752
5315
  if (this.coalesce) {
3753
5316
  const inFlight = this.inflight.get(mapKey);
@@ -3761,7 +5324,9 @@ var GoogleCacheStore = class {
3761
5324
  model: key.model,
3762
5325
  ttlSeconds: factoryResult.ttlSeconds,
3763
5326
  ...factoryResult.contents !== void 0 ? { contents: factoryResult.contents } : {},
3764
- ...factoryResult.systemInstruction !== void 0 ? { systemInstruction: factoryResult.systemInstruction } : {}
5327
+ ...factoryResult.systemInstruction !== void 0 ? { systemInstruction: factoryResult.systemInstruction } : {},
5328
+ ...factoryResult.tools !== void 0 ? { tools: factoryResult.tools } : {},
5329
+ ...factoryResult.toolConfig !== void 0 ? { toolConfig: factoryResult.toolConfig } : {}
3765
5330
  });
3766
5331
  this.entries.set(mapKey, { handle, ttlSeconds: factoryResult.ttlSeconds });
3767
5332
  return handle;
@@ -3808,12 +5373,13 @@ var GoogleCacheStore = class {
3808
5373
  name: handle.cacheName,
3809
5374
  config: { ttl: `${extensionSeconds}s` }
3810
5375
  });
3811
- const fallbackExpiry = new Date(this.now() + extensionSeconds * 1e3);
3812
- const newExpiresAt = resp.expireTime !== void 0 && resp.expireTime.length > 0 ? new Date(resp.expireTime) : fallbackExpiry;
5376
+ const newExpiresAt = expiryOf(resp.expireTime, this.now() + extensionSeconds * 1e3);
3813
5377
  const newHandle = {
3814
5378
  cacheName: handle.cacheName,
3815
5379
  model: handle.model,
3816
- expiresAt: newExpiresAt
5380
+ expiresAt: newExpiresAt,
5381
+ ...handle.totalTokenCount !== void 0 ? { totalTokenCount: handle.totalTokenCount } : {},
5382
+ ...handle.toolKinds !== void 0 ? { toolKinds: handle.toolKinds } : {}
3817
5383
  };
3818
5384
  for (const [k, entry] of this.entries.entries()) {
3819
5385
  if (entry.handle.cacheName === handle.cacheName) {
@@ -3827,9 +5393,11 @@ var GoogleCacheStore = class {
3827
5393
  }
3828
5394
  }
3829
5395
  /**
3830
- * Delete a cached content resource.
5396
+ * Delete a cached content resource. Idempotent: a cache that is already gone
5397
+ * (HTTP 404, or the 403 `CachedContent not found` Google sends for an expired
5398
+ * one) is success, not an error.
3831
5399
  *
3832
- * Errors are forwarded to `onDeleteError` and NOT rethrown.
5400
+ * Any other error is forwarded to `onDeleteError` and NOT rethrown.
3833
5401
  * The handle is removed from the in-process entries map regardless.
3834
5402
  */
3835
5403
  async delete(handle) {
@@ -3843,6 +5411,7 @@ var GoogleCacheStore = class {
3843
5411
  const client = await this.getClient();
3844
5412
  await client.delete({ name: handle.cacheName });
3845
5413
  } catch (err) {
5414
+ if (isGoogleNotFoundError(err)) return;
3846
5415
  this.onDeleteError(handle.cacheName, err);
3847
5416
  }
3848
5417
  }
@@ -3884,7 +5453,7 @@ function convertMediaResolution(mediaResolution, location) {
3884
5453
  return mapMediaResolutionLevel(mediaResolution.level, location);
3885
5454
  }
3886
5455
  function convertPart(part, location, nameCounts, reservedIds) {
3887
- const keys = definedKeys(part);
5456
+ const keys = definedKeys(part).filter((key) => key !== "thoughtSignature");
3888
5457
  const baseKeys = keys.filter(
3889
5458
  (key) => key === "text" || key === "inlineData" || key === "fileData"
3890
5459
  );
@@ -3993,6 +5562,20 @@ function convertPart(part, location, nameCounts, reservedIds) {
3993
5562
  }
3994
5563
  throw badRequest(`${location}: Part has no recognized fields set.`);
3995
5564
  }
5565
+ function convertPartWithSignature(part, location, role, nameCounts, reservedIds) {
5566
+ const converted = convertPart(part, location, nameCounts, reservedIds);
5567
+ const signature = part.thoughtSignature;
5568
+ if (signature === void 0) return { part: converted };
5569
+ if (typeof signature !== "string" || signature.length === 0) {
5570
+ throw badRequest(`${location}: Part.thoughtSignature must be a non-empty string.`);
5571
+ }
5572
+ if (role !== "assistant" || converted.kind !== "text" && converted.kind !== "tool-call") {
5573
+ throw badRequest(
5574
+ `${location}: Part.thoughtSignature can only be imported from a model text or functionCall part.`
5575
+ );
5576
+ }
5577
+ return { part: converted, signature };
5578
+ }
3996
5579
  function convertRole(role, location) {
3997
5580
  if (role === "user") return "user";
3998
5581
  if (role === "model") return "assistant";
@@ -4038,6 +5621,7 @@ function geminiContentToMessages(input) {
4038
5621
  })
4039
5622
  )
4040
5623
  );
5624
+ const signatures = [];
4041
5625
  const messages = input.contents.map((content, contentIndex) => {
4042
5626
  const location = `contents[${contentIndex}]`;
4043
5627
  const envelopeExtraKeys = definedKeys(content).filter(
@@ -4049,15 +5633,45 @@ function geminiContentToMessages(input) {
4049
5633
  );
4050
5634
  }
4051
5635
  const role = convertRole(content.role, location);
4052
- const parts = (content.parts ?? []).map(
4053
- (part, partIndex) => convertPart(part, `${location}.parts[${partIndex}]`, nameCounts, reservedIds)
4054
- );
5636
+ const parts = (content.parts ?? []).map((part, partIndex) => {
5637
+ const partLocation = `${location}.parts[${partIndex}]`;
5638
+ const converted = convertPartWithSignature(
5639
+ part,
5640
+ partLocation,
5641
+ role,
5642
+ nameCounts,
5643
+ reservedIds
5644
+ );
5645
+ if (converted.signature !== void 0) {
5646
+ if (input.model === void 0) {
5647
+ throw badRequest(
5648
+ `${partLocation}: Part.thoughtSignature requires the \`model\` input; a signature is bound to the model that issued it.`
5649
+ );
5650
+ }
5651
+ signatures.push(
5652
+ signatureEntry(
5653
+ contentIndex,
5654
+ partIndex,
5655
+ input.model,
5656
+ converted.part,
5657
+ converted.signature
5658
+ )
5659
+ );
5660
+ }
5661
+ return converted.part;
5662
+ });
4055
5663
  return { role, parts };
4056
5664
  });
4057
- return { ...system !== void 0 ? { system } : {}, messages };
5665
+ return {
5666
+ ...system !== void 0 ? { system } : {},
5667
+ messages,
5668
+ ...signatures.length > 0 ? { transientProviderState: { google: { signatures } } } : {}
5669
+ };
4058
5670
  }
4059
5671
 
4060
5672
  exports.FLEX_DEFAULT_TIMEOUT_MS = FLEX_DEFAULT_TIMEOUT_MS;
5673
+ exports.GEMINI_GROUNDING_PRICING = GEMINI_GROUNDING_PRICING;
5674
+ exports.GEMINI_INPUT_MIME_TYPES = GEMINI_INPUT_MIME_TYPES;
4061
5675
  exports.GEMINI_PRICED_TIERS = GEMINI_PRICED_TIERS;
4062
5676
  exports.GEMINI_PRICING = GEMINI_PRICING;
4063
5677
  exports.GoogleCacheStore = GoogleCacheStore;
@@ -4065,7 +5679,9 @@ exports.GoogleFileStore = GoogleFileStore;
4065
5679
  exports.STANDARD_DEFAULT_TIMEOUT_MS = STANDARD_DEFAULT_TIMEOUT_MS;
4066
5680
  exports.TRANSPORT_TIMEOUT_BUFFER_MS = TRANSPORT_TIMEOUT_BUFFER_MS;
4067
5681
  exports.buildGoogleClient = buildGoogleClient;
5682
+ exports.classifyGoogleError = classifyGoogleError;
4068
5683
  exports.defaultGeminiRegistry = defaultGeminiRegistry;
5684
+ exports.dropMessagesFromSignatureState = dropMessagesFromSignatureState;
4069
5685
  exports.geminiAdapter = geminiAdapter;
4070
5686
  exports.geminiContentToMessages = geminiContentToMessages;
4071
5687
  exports.geminiModelDescriptors = geminiModelDescriptors;