@gullabs/google 0.2.0 → 0.4.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/LICENSE CHANGED
@@ -176,7 +176,7 @@
176
176
  comment syntax for the file format in please. Also attach a copy of
177
177
  this License to your work.
178
178
 
179
- Copyright 2026 Atif Gul
179
+ Copyright 2026 Gul Labs
180
180
 
181
181
  Licensed under the Apache License, Version 2.0 (the "License");
182
182
  you may not use this file except in compliance with the License.
package/README.md CHANGED
@@ -6,13 +6,13 @@ Gemini provider adapter for any-llm. A thin mapping layer over `@google/genai` t
6
6
 
7
7
  ## Key exports
8
8
 
9
- | Export | What it is |
10
- | --------------------------- | ----------------------------------------------------------------------------- |
11
- | `geminiAdapter(opts?)` | Creates the `ProviderAdapter` for Gemini |
12
- | `GeminiAdapterOptions` | `{ client?: GeminiClientLike }` — inject a pre-built or fake client |
13
- | `GeminiClientLike` | Structural interface the adapter depends on (satisfied by real SDK and fakes) |
14
- | `buildGoogleClient(auth)` | Builds the real `@google/genai` client from `AuthMaterial` |
15
- | `zodToGeminiSchema(schema)` | Converts a Zod schema to a Gemini `responseSchema` object |
9
+ | Export | What it is |
10
+ | ---------------------------- | ----------------------------------------------------------------------------- |
11
+ | `geminiAdapter(opts?)` | Creates the `ProviderAdapter` for Gemini |
12
+ | `GeminiAdapterOptions` | `{ client?: GeminiClientLike }` — inject a pre-built or fake client |
13
+ | `GeminiClientLike` | Structural interface the adapter depends on (satisfied by real SDK and fakes) |
14
+ | `buildGoogleClient(auth)` | Builds the real `@google/genai` client from `AuthMaterial` |
15
+ | `isGeminiCapacityError(err)` | Detects Gemini Flex shared-capacity errors for built-in fallback |
16
16
 
17
17
  ## Quick example
18
18
 
@@ -37,10 +37,30 @@ const result = await client.generate(
37
37
 
38
38
  ## What it maps
39
39
 
40
- - `serviceTier: 'flex'` → Gemini Flex service tier (default)
40
+ - `serviceTier: 'flex'` → Gemini Flex service tier when the model descriptor supports it
41
41
  - `reasoning.includeThoughts` → `thinkingConfig.includeThoughts`; thought parts become `reasoningText`
42
42
  - `reasoning.effort` → `thinkingBudget` (gemini-2.5) or `thinkingLevel` (gemini-3.x) with a warning when lossy
43
- - `output.schema` → `responseMimeType: 'application/json'` + `responseSchema` (Zod-converted)
44
- - `providerOptions.google.*` → forwarded verbatim to the SDK config
43
+ - `output.jsonSchema` → `responseMimeType: 'application/json'` + verbatim `responseSchema` when native structured output is enabled; the engine returns parsed output and `outputParsed` without validating shape
44
+ - `providerOptions.google.*` → forwarded verbatim to the SDK config, including Gemini `safetySettings`
45
45
  - Usage: `promptTokenCount`→`inputTokens`, `candidatesTokenCount`+`thoughtsTokenCount`→`outputTokens` (GROSS)
46
46
  - Errors: `401/403`→`invalid_auth`, `429`→`rate_limited`, `5xx`→`server`, timeouts, safety blocks
47
+
48
+ ## Gemma 4
49
+
50
+ The default registry includes two API-verified Gemma 4 models: `gemma-4-31b-it`
51
+ and `gemma-4-26b-a4b-it`. Both route through this adapter and support:
52
+
53
+ - **Native structured output** — `responseMimeType` + verbatim `responseSchema` are sent
54
+ automatically when `output.jsonSchema` is set.
55
+ - **Grounding** — `tools:[{googleSearch:{}}]` via `providerOptions.google`.
56
+ - **Vision** — `inline-media` and `file-uri` multimodal message parts.
57
+ - **Thinking** — `reasoning.effort` maps to `thinkingLevel` (`reasoningApi: 'level'`).
58
+ Gemma 4 thinking is binary: only `effort: 'none'` (MINIMAL) and `effort: 'high'`
59
+ (HIGH) are accepted. Passing `effort: 'low'` or `effort: 'medium'` is rejected at
60
+ validation time with a `bad_request` error because the model only supports MINIMAL
61
+ and HIGH `thinkingLevel` values. Note: `thinkingBudget` is **not** supported
62
+ (rejected by the API with HTTP 400).
63
+ - **Tunable sampling** — `temperature`, `topP`, `topK` are accepted.
64
+
65
+ Gemma 4 models do **not** support Gemini Flex service tier (`serviceTiers` is absent from their
66
+ descriptors) and are unpriced (`cost.microUsd` will be `null`).
package/dist/index.cjs CHANGED
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  var core = require('@gullabs/core');
4
- var zod = require('zod');
5
4
 
6
5
  // src/adapter.ts
7
6
 
8
7
  // src/client.ts
9
8
  var FLEX_DEFAULT_TIMEOUT_MS = 15e5;
9
+ var STANDARD_DEFAULT_TIMEOUT_MS = 3e5;
10
10
  var TRANSPORT_TIMEOUT_BUFFER_MS = 5e3;
11
11
  async function buildGoogleClient(auth) {
12
12
  const { GoogleGenAI } = await import('@google/genai');
@@ -20,130 +20,37 @@ async function buildGoogleClient(auth) {
20
20
  }
21
21
  };
22
22
  }
23
- function isOptionalField(schema) {
24
- if (schema instanceof zod.ZodOptional || schema instanceof zod.ZodDefault) return true;
25
- if (schema instanceof zod.ZodNullable) {
26
- const unwrapped = schema.unwrap();
27
- return isOptionalField(unwrapped);
28
- }
29
- return false;
30
- }
31
- function zodToGeminiSchema(schema) {
32
- return convertSchema(schema, false);
33
- }
34
- function convertSchema(schema, nullable) {
35
- const desc = typeof schema.description === "string" ? schema.description : void 0;
36
- if (schema instanceof zod.ZodOptional) {
37
- const unwrapped = schema.unwrap();
38
- return convertSchema(unwrapped, nullable);
39
- }
40
- if (schema instanceof zod.ZodNullable) {
41
- const unwrapped = schema.unwrap();
42
- return convertSchema(unwrapped, true);
43
- }
44
- if (schema instanceof zod.ZodDefault) {
45
- const innerType = schema._def.innerType;
46
- return convertSchema(innerType, nullable);
47
- }
48
- if (schema instanceof zod.ZodObject) {
49
- const shape = schema.shape;
50
- const properties = {};
51
- const required = [];
52
- for (const [key, fieldType] of Object.entries(shape)) {
53
- const fieldSchema = convertSchema(fieldType, false);
54
- if (fieldSchema === void 0) return void 0;
55
- properties[key] = fieldSchema;
56
- if (!isOptionalField(fieldType)) {
57
- required.push(key);
58
- }
59
- }
60
- const result = {
61
- type: "object",
62
- properties,
63
- ...required.length > 0 ? { required } : {},
64
- ...desc !== void 0 ? { description: desc } : {},
65
- ...nullable ? { nullable: true } : {}
66
- };
67
- return result;
68
- }
69
- if (schema instanceof zod.ZodString) {
70
- return {
71
- type: "string",
72
- ...desc !== void 0 ? { description: desc } : {},
73
- ...nullable ? { nullable: true } : {}
74
- };
75
- }
76
- if (schema instanceof zod.ZodNumber) {
77
- const checks = schema._def.checks ?? [];
78
- const isInt = checks.some((c) => c.kind === "int");
79
- return {
80
- type: isInt ? "integer" : "number",
81
- ...desc !== void 0 ? { description: desc } : {},
82
- ...nullable ? { nullable: true } : {}
83
- };
84
- }
85
- if (schema instanceof zod.ZodBoolean) {
86
- return {
87
- type: "boolean",
88
- ...desc !== void 0 ? { description: desc } : {},
89
- ...nullable ? { nullable: true } : {}
90
- };
91
- }
92
- if (schema instanceof zod.ZodArray) {
93
- const element = schema.element;
94
- const items = convertSchema(element, false);
95
- if (items === void 0) return void 0;
96
- return {
97
- type: "array",
98
- items,
99
- ...desc !== void 0 ? { description: desc } : {},
100
- ...nullable ? { nullable: true } : {}
101
- };
102
- }
103
- if (schema instanceof zod.ZodEnum) {
104
- return {
105
- type: "string",
106
- enum: schema.options,
107
- ...desc !== void 0 ? { description: desc } : {},
108
- ...nullable ? { nullable: true } : {}
109
- };
110
- }
111
- if (schema instanceof zod.ZodLiteral) {
112
- const val = schema.value;
113
- if (typeof val === "string") {
114
- return {
115
- type: "string",
116
- enum: [val],
117
- ...desc !== void 0 ? { description: desc } : {},
118
- ...nullable ? { nullable: true } : {}
119
- };
120
- }
121
- if (typeof val === "number") {
122
- return {
123
- type: "number",
124
- ...desc !== void 0 ? { description: desc } : {},
125
- ...nullable ? { nullable: true } : {}
126
- };
127
- }
128
- if (typeof val === "boolean") {
129
- return {
130
- type: "boolean",
131
- ...desc !== void 0 ? { description: desc } : {},
132
- ...nullable ? { nullable: true } : {}
133
- };
134
- }
135
- return void 0;
23
+
24
+ // src/flex-fallback.ts
25
+ var CAPACITY_PATTERNS = [
26
+ /capacity/i,
27
+ /overload/i,
28
+ /overloaded/i,
29
+ /unavailable/i,
30
+ /no\s+capacity/i,
31
+ /temporar(?:y|ily)/i,
32
+ /try\s+again/i
33
+ ];
34
+ var QUOTA_PATTERNS = [
35
+ /quota/i,
36
+ /billing/i,
37
+ /billable/i,
38
+ /payment/i,
39
+ /rate\s+limit/i,
40
+ /exceeded/i,
41
+ /insufficient/i
42
+ ];
43
+ function isGeminiCapacityError(err) {
44
+ if (err.kind === "server") return err.httpStatus === 503;
45
+ if (err.kind !== "rate_limited") return false;
46
+ const message = err.message;
47
+ if (QUOTA_PATTERNS.some((pattern) => pattern.test(message))) {
48
+ return false;
136
49
  }
137
- return void 0;
50
+ return CAPACITY_PATTERNS.some((pattern) => pattern.test(message));
138
51
  }
139
52
 
140
53
  // src/adapter.ts
141
- var EFFORT_BUDGET = {
142
- none: 0,
143
- low: 1024,
144
- medium: 8192,
145
- high: 24576
146
- };
147
54
  function mapFinishReason(raw) {
148
55
  if (raw === void 0) return void 0;
149
56
  switch (raw) {
@@ -255,24 +162,45 @@ function geminiAdapter(opts) {
255
162
  if (genConfig.stopSequences !== void 0) {
256
163
  config.stopSequences = genConfig.stopSequences;
257
164
  }
258
- config.serviceTier = genConfig.serviceTier === "standard" ? "standard" : "flex";
165
+ const explicit = genConfig.serviceTier;
166
+ const supported = req.modelDescriptor?.capabilities?.serviceTiers;
167
+ if (explicit !== void 0) {
168
+ if (req.modelDescriptor !== void 0 && (supported === void 0 || !supported.includes(explicit))) {
169
+ throw new core.LlmError(
170
+ `serviceTier "${explicit}" is not supported for model "${model}".`,
171
+ { kind: "bad_request", retryable: false }
172
+ );
173
+ }
174
+ config.serviceTier = explicit;
175
+ } else {
176
+ if (req.modelDescriptor === void 0) {
177
+ config.serviceTier = "flex";
178
+ } else if (supported?.includes("flex") === true) {
179
+ config.serviceTier = "flex";
180
+ }
181
+ }
259
182
  const reasoning = genConfig.reasoning;
260
183
  if (reasoning !== void 0) {
261
184
  const reasoningApi = req.modelDescriptor?.capabilities?.reasoningApi;
185
+ if (reasoning.effort !== void 0 && reasoning.budgetTokens !== void 0) {
186
+ throw new core.LlmError(
187
+ `Provide either reasoning.effort or reasoning.budgetTokens, not both, for model "${model}".`,
188
+ { kind: "bad_request", retryable: false }
189
+ );
190
+ }
262
191
  if (reasoningApi === "budget") {
263
- const budget = reasoning.budgetTokens !== void 0 ? reasoning.budgetTokens : reasoning.effort !== void 0 ? EFFORT_BUDGET[reasoning.effort] ?? 0 : void 0;
192
+ const budget = reasoning.budgetTokens !== void 0 ? reasoning.budgetTokens : reasoning.effort !== void 0 ? core.EFFORT_BUDGET[reasoning.effort] : void 0;
264
193
  config.thinkingConfig = {
265
194
  ...budget !== void 0 ? { thinkingBudget: budget } : {},
266
195
  ...reasoning.includeThoughts === true ? { includeThoughts: true } : {}
267
196
  };
268
- if (reasoning.effort !== void 0 && reasoning.budgetTokens !== void 0) {
269
- warnings.push({
270
- type: "reasoning-mapping",
271
- quality: "approximate",
272
- details: "budgetTokens takes precedence over effort for thinkingBudget"
273
- });
274
- }
275
197
  } else if (reasoningApi === "level") {
198
+ if (reasoning.budgetTokens !== void 0) {
199
+ throw new core.LlmError(
200
+ `reasoning.budgetTokens is not supported for model "${model}" (it uses thinkingLevel, not thinkingBudget); use reasoning.effort instead.`,
201
+ { kind: "bad_request", retryable: false }
202
+ );
203
+ }
276
204
  let thinkingLevel;
277
205
  if (reasoning.effort !== void 0) {
278
206
  switch (reasoning.effort) {
@@ -292,73 +220,57 @@ function geminiAdapter(opts) {
292
220
  core.assertNever(reasoning.effort);
293
221
  }
294
222
  }
295
- if (reasoning.budgetTokens !== void 0) {
296
- warnings.push({
297
- type: "reasoning-mapping",
298
- quality: "approximate",
299
- details: "budgetTokens is not supported for gemini-3.x models; mapping effort to thinkingLevel instead"
300
- });
301
- }
302
223
  config.thinkingConfig = {
303
224
  ...thinkingLevel !== void 0 ? { thinkingLevel } : {},
304
225
  ...reasoning.includeThoughts === true ? { includeThoughts: true } : {}
305
226
  };
306
227
  } else {
307
- warnings.push({
308
- type: "reasoning-mapping",
309
- quality: "unsupported",
310
- details: `thinkingConfig not mapped for model "${model}"; unknown generation`
311
- });
228
+ throw new core.LlmError(
229
+ `Model "${model}" does not support reasoning/thinkingConfig.`,
230
+ { kind: "bad_request", retryable: false }
231
+ );
312
232
  }
313
233
  }
314
- let outputSchemaRequested = false;
315
- if (req.outputSchema !== void 0) {
316
- outputSchemaRequested = true;
317
- config.responseMimeType = "application/json";
318
- if (req.outputSchema["~standard"].vendor === "zod") {
319
- const geminiSchema = zodToGeminiSchema(req.outputSchema);
320
- if (geminiSchema !== void 0) {
321
- config.responseSchema = geminiSchema;
322
- } else {
323
- warnings.push({
324
- type: "unsupported-setting",
325
- setting: "output.schema",
326
- details: "Could not convert Zod schema to Gemini responseSchema; proceeding with responseMimeType only. Engine will still validate."
327
- });
328
- }
329
- } else {
330
- warnings.push({
331
- type: "other",
332
- message: `Native Gemini responseSchema conversion is not available for vendor "${req.outputSchema["~standard"].vendor}"; proceeding with responseMimeType only. Engine will validate output client-side via Standard Schema.`
333
- });
234
+ const structuredOutputRequested = req.outputJsonSchema !== void 0;
235
+ if (structuredOutputRequested) {
236
+ const nativeStructuredOutput = req.modelDescriptor?.capabilities?.nativeStructuredOutput !== false;
237
+ if (nativeStructuredOutput) {
238
+ config.responseMimeType = "application/json";
239
+ config.responseSchema = req.outputJsonSchema;
334
240
  }
335
241
  }
336
242
  const googleOpts = genConfig.providerOptions?.["google"];
337
243
  if (googleOpts !== void 0 && typeof googleOpts === "object" && googleOpts !== null) {
338
244
  Object.assign(config, googleOpts);
339
245
  }
246
+ const mergedServiceTier = config.serviceTier;
247
+ if (mergedServiceTier !== void 0 && req.modelDescriptor !== void 0) {
248
+ const supportedAfterMerge = req.modelDescriptor.capabilities?.serviceTiers;
249
+ if (supportedAfterMerge === void 0 || !supportedAfterMerge.includes(mergedServiceTier)) {
250
+ throw new core.LlmError(
251
+ `serviceTier "${mergedServiceTier}" is not supported for model "${model}".`,
252
+ { kind: "bad_request", retryable: false }
253
+ );
254
+ }
255
+ }
340
256
  if (req.modelDescriptor?.capabilities?.sampling === "fixed") {
341
- const droppedSampling = [];
257
+ const offendingSampling = [];
342
258
  if ("temperature" in config) {
343
- delete config.temperature;
344
- droppedSampling.push("temperature");
259
+ offendingSampling.push("temperature");
345
260
  }
346
261
  if ("topP" in config) {
347
- delete config.topP;
348
- droppedSampling.push("topP");
262
+ offendingSampling.push("topP");
349
263
  }
350
264
  if ("topK" in config) {
351
- delete config.topK;
352
- droppedSampling.push("topK");
265
+ offendingSampling.push("topK");
353
266
  }
354
- if (droppedSampling.length > 0) {
355
- warnings.push({
356
- type: "unsupported-setting",
357
- setting: droppedSampling.join(", "),
358
- details: `Sampling parameter(s) [${droppedSampling.join(
267
+ if (offendingSampling.length > 0) {
268
+ throw new core.LlmError(
269
+ `Sampling parameters [${offendingSampling.join(
359
270
  ", "
360
- )}] from providerOptions.google were stripped: this model has fixed sampling (Gemini 3.x) and the API rejects these fields.`
361
- });
271
+ )}] are not supported for model "${model}" (fixed sampling); they were supplied via providerOptions.google.`,
272
+ { kind: "bad_request", retryable: false }
273
+ );
362
274
  }
363
275
  }
364
276
  const configAsAny = config;
@@ -369,32 +281,41 @@ function geminiAdapter(opts) {
369
281
  }
370
282
  return false;
371
283
  });
372
- if (groundingRequested && req.outputSchema !== void 0) {
284
+ if (groundingRequested && structuredOutputRequested) {
373
285
  throw new core.LlmError(
374
- "Grounding (googleSearch) cannot be combined with structured output (output.schema) on Gemini; choose one.",
286
+ "Grounding (googleSearch) cannot be combined with structured output (output.jsonSchema) on Gemini; choose one.",
375
287
  { kind: "bad_request", retryable: false }
376
288
  );
377
289
  }
378
- let _flexTimeoutHandle;
379
- if (config.serviceTier === "flex" && genConfig.timeoutMs === void 0) {
380
- const flexController = new AbortController();
381
- const timeoutReason = new DOMException(
382
- `Flex timeout: call exceeded ${FLEX_DEFAULT_TIMEOUT_MS}ms client-side ceiling (@google/genai #1277 belt-and-suspenders)`,
383
- "TimeoutError"
384
- );
385
- _flexTimeoutHandle = setTimeout(() => {
386
- flexController.abort(timeoutReason);
387
- }, FLEX_DEFAULT_TIMEOUT_MS);
388
- if (ctx.signal !== void 0) {
389
- config.abortSignal = AbortSignal.any([flexController.signal, ctx.signal]);
390
- } else {
391
- config.abortSignal = flexController.signal;
290
+ let tierTimeoutHandle;
291
+ const clearTierTimeout = () => {
292
+ if (tierTimeoutHandle !== void 0) {
293
+ clearTimeout(tierTimeoutHandle);
294
+ tierTimeoutHandle = void 0;
392
295
  }
393
- } else if (ctx.signal !== void 0) {
394
- config.abortSignal = ctx.signal;
395
- }
296
+ };
297
+ const applyTierTimeout = (tier) => {
298
+ clearTierTimeout();
299
+ delete config.abortSignal;
300
+ const defaultTimeoutMs = genConfig.timeoutMs === void 0 ? tier === "flex" ? FLEX_DEFAULT_TIMEOUT_MS : tier === "standard" ? STANDARD_DEFAULT_TIMEOUT_MS : void 0 : void 0;
301
+ if (defaultTimeoutMs !== void 0) {
302
+ const tierController = new AbortController();
303
+ const tierLabel = tier === "standard" ? "Standard" : "Flex";
304
+ const timeoutReason = new DOMException(
305
+ `${tierLabel} timeout: call exceeded ${defaultTimeoutMs}ms client-side ceiling (@google/genai #1277 belt-and-suspenders)`,
306
+ "TimeoutError"
307
+ );
308
+ tierTimeoutHandle = setTimeout(() => {
309
+ tierController.abort(timeoutReason);
310
+ }, defaultTimeoutMs);
311
+ config.abortSignal = ctx.signal !== void 0 ? AbortSignal.any([tierController.signal, ctx.signal]) : tierController.signal;
312
+ } else if (ctx.signal !== void 0) {
313
+ config.abortSignal = ctx.signal;
314
+ }
315
+ };
316
+ applyTierTimeout(config.serviceTier);
396
317
  const callerHttpOptions = config.httpOptions;
397
- const computedTimeoutMs = genConfig.timeoutMs !== void 0 ? genConfig.timeoutMs + TRANSPORT_TIMEOUT_BUFFER_MS : genConfig.serviceTier === "flex" ? FLEX_DEFAULT_TIMEOUT_MS : void 0;
318
+ const computedTimeoutMs = genConfig.timeoutMs !== void 0 ? genConfig.timeoutMs + TRANSPORT_TIMEOUT_BUFFER_MS : config.serviceTier === "flex" ? FLEX_DEFAULT_TIMEOUT_MS : config.serviceTier === "standard" ? STANDARD_DEFAULT_TIMEOUT_MS : void 0;
398
319
  const mergedHttpOptions = {
399
320
  ...computedTimeoutMs !== void 0 ? { timeout: computedTimeoutMs } : {},
400
321
  ...callerHttpOptions
@@ -402,32 +323,71 @@ function geminiAdapter(opts) {
402
323
  if (Object.keys(mergedHttpOptions).length > 0) {
403
324
  config.httpOptions = mergedHttpOptions;
404
325
  }
405
- const params = {
406
- model,
407
- contents,
408
- config
409
- };
410
326
  let response;
411
- try {
327
+ let servedServiceTier = config.serviceTier;
328
+ const dispatch = async () => {
329
+ const dispatchConfig = {
330
+ ...config,
331
+ ...config.httpOptions !== void 0 ? { httpOptions: { ...config.httpOptions } } : {}
332
+ };
333
+ const params = {
334
+ model,
335
+ contents,
336
+ config: dispatchConfig
337
+ };
412
338
  const buildClient = opts?._clientFactory ?? buildGoogleClient;
413
339
  const client = opts?.client !== void 0 ? opts.client : await buildClient(ctx.auth);
414
340
  ctx.logger.debug(
415
- { model, configKeys: Object.keys(config) },
341
+ {
342
+ model,
343
+ configKeys: Object.keys(dispatchConfig),
344
+ serviceTier: dispatchConfig.serviceTier
345
+ },
416
346
  "llm.adapter.dispatch"
417
347
  );
418
- response = await client.models.generateContent(params);
348
+ return client.models.generateContent(params);
349
+ };
350
+ try {
351
+ response = await dispatch();
419
352
  } catch (rawErr) {
420
353
  const classified = core.classifyError(rawErr);
421
- throw new core.LlmError(classified.message, {
354
+ const typed = new core.LlmError(classified.message, {
422
355
  kind: classified.kind,
423
356
  retryable: classified.retryable,
424
357
  ...classified.httpStatus !== void 0 ? { httpStatus: classified.httpStatus } : {},
425
358
  ...classified.retryAfterMs !== void 0 ? { retryAfterMs: classified.retryAfterMs } : {},
426
359
  provider: "google",
427
- cause: classified.cause ?? rawErr
360
+ cause: classified.cause ?? rawErr,
361
+ ...servedServiceTier !== void 0 ? { servedServiceTier } : {}
428
362
  });
363
+ if (config.serviceTier === "flex" && genConfig.flexFallback !== false && isGeminiCapacityError(typed)) {
364
+ config.serviceTier = "standard";
365
+ servedServiceTier = "standard";
366
+ const fallbackTimeout = genConfig.timeoutMs !== void 0 ? genConfig.timeoutMs + TRANSPORT_TIMEOUT_BUFFER_MS : STANDARD_DEFAULT_TIMEOUT_MS;
367
+ config.httpOptions = {
368
+ timeout: fallbackTimeout,
369
+ ...callerHttpOptions
370
+ };
371
+ applyTierTimeout("standard");
372
+ try {
373
+ response = await dispatch();
374
+ } catch (fallbackRawErr) {
375
+ const fallbackClassified = core.classifyError(fallbackRawErr);
376
+ throw new core.LlmError(fallbackClassified.message, {
377
+ kind: fallbackClassified.kind,
378
+ retryable: fallbackClassified.retryable,
379
+ ...fallbackClassified.httpStatus !== void 0 ? { httpStatus: fallbackClassified.httpStatus } : {},
380
+ ...fallbackClassified.retryAfterMs !== void 0 ? { retryAfterMs: fallbackClassified.retryAfterMs } : {},
381
+ provider: "google",
382
+ cause: fallbackClassified.cause ?? fallbackRawErr,
383
+ servedServiceTier: "standard"
384
+ });
385
+ }
386
+ } else {
387
+ throw typed;
388
+ }
429
389
  } finally {
430
- if (_flexTimeoutHandle !== void 0) clearTimeout(_flexTimeoutHandle);
390
+ clearTierTimeout();
431
391
  }
432
392
  const hasBlockReason = response.promptFeedback?.blockReason !== void 0;
433
393
  const hasCandidates = response.candidates !== void 0 && response.candidates.length > 0;
@@ -470,7 +430,7 @@ function geminiAdapter(opts) {
470
430
  const text = textParts.join("");
471
431
  const reasoningText = thoughtParts.length > 0 ? thoughtParts.join("") : void 0;
472
432
  let rawStructured;
473
- if (outputSchemaRequested && text.length > 0) {
433
+ if (structuredOutputRequested && text.length > 0) {
474
434
  try {
475
435
  rawStructured = JSON.parse(text);
476
436
  } catch {
@@ -482,6 +442,7 @@ function geminiAdapter(opts) {
482
442
  model,
483
443
  usage,
484
444
  warnings,
445
+ ...servedServiceTier !== void 0 ? { servedServiceTier } : {},
485
446
  ...text.length > 0 ? { text } : {},
486
447
  ...reasoningText !== void 0 ? { reasoningText } : {},
487
448
  ...rawStructured !== void 0 ? { rawStructured } : {},
@@ -910,9 +871,10 @@ var GoogleCacheStore = class {
910
871
  exports.FLEX_DEFAULT_TIMEOUT_MS = FLEX_DEFAULT_TIMEOUT_MS;
911
872
  exports.GoogleCacheStore = GoogleCacheStore;
912
873
  exports.GoogleFileStore = GoogleFileStore;
874
+ exports.STANDARD_DEFAULT_TIMEOUT_MS = STANDARD_DEFAULT_TIMEOUT_MS;
913
875
  exports.TRANSPORT_TIMEOUT_BUFFER_MS = TRANSPORT_TIMEOUT_BUFFER_MS;
914
876
  exports.buildGoogleClient = buildGoogleClient;
915
877
  exports.geminiAdapter = geminiAdapter;
916
- exports.zodToGeminiSchema = zodToGeminiSchema;
878
+ exports.isGeminiCapacityError = isGeminiCapacityError;
917
879
  //# sourceMappingURL=index.cjs.map
918
880
  //# sourceMappingURL=index.cjs.map