@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 +1 -1
- package/README.md +30 -10
- package/dist/index.cjs +171 -209
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -23
- package/dist/index.d.ts +11 -23
- package/dist/index.js +171 -210
- package/dist/index.js.map +1 -1
- package/package.json +4 -6
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
|
|
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
|
|
10
|
-
|
|
|
11
|
-
| `geminiAdapter(opts?)`
|
|
12
|
-
| `GeminiAdapterOptions`
|
|
13
|
-
| `GeminiClientLike`
|
|
14
|
-
| `buildGoogleClient(auth)`
|
|
15
|
-
| `
|
|
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
|
|
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.
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
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
|
-
|
|
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]
|
|
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
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
-
|
|
315
|
-
if (
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
|
257
|
+
const offendingSampling = [];
|
|
342
258
|
if ("temperature" in config) {
|
|
343
|
-
|
|
344
|
-
droppedSampling.push("temperature");
|
|
259
|
+
offendingSampling.push("temperature");
|
|
345
260
|
}
|
|
346
261
|
if ("topP" in config) {
|
|
347
|
-
|
|
348
|
-
droppedSampling.push("topP");
|
|
262
|
+
offendingSampling.push("topP");
|
|
349
263
|
}
|
|
350
264
|
if ("topK" in config) {
|
|
351
|
-
|
|
352
|
-
droppedSampling.push("topK");
|
|
265
|
+
offendingSampling.push("topK");
|
|
353
266
|
}
|
|
354
|
-
if (
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
)}]
|
|
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 &&
|
|
284
|
+
if (groundingRequested && structuredOutputRequested) {
|
|
373
285
|
throw new core.LlmError(
|
|
374
|
-
"Grounding (googleSearch) cannot be combined with structured output (output.
|
|
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
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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
|
-
}
|
|
394
|
-
|
|
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 :
|
|
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
|
-
|
|
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
|
-
{
|
|
341
|
+
{
|
|
342
|
+
model,
|
|
343
|
+
configKeys: Object.keys(dispatchConfig),
|
|
344
|
+
serviceTier: dispatchConfig.serviceTier
|
|
345
|
+
},
|
|
416
346
|
"llm.adapter.dispatch"
|
|
417
347
|
);
|
|
418
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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.
|
|
878
|
+
exports.isGeminiCapacityError = isGeminiCapacityError;
|
|
917
879
|
//# sourceMappingURL=index.cjs.map
|
|
918
880
|
//# sourceMappingURL=index.cjs.map
|