@gullabs/google 0.2.0 → 0.3.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 +160 -202
- 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 +160 -203
- 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,121 +20,34 @@ 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
|
|
@@ -255,24 +168,45 @@ function geminiAdapter(opts) {
|
|
|
255
168
|
if (genConfig.stopSequences !== void 0) {
|
|
256
169
|
config.stopSequences = genConfig.stopSequences;
|
|
257
170
|
}
|
|
258
|
-
|
|
171
|
+
const explicit = genConfig.serviceTier;
|
|
172
|
+
const supported = req.modelDescriptor?.capabilities?.serviceTiers;
|
|
173
|
+
if (explicit !== void 0) {
|
|
174
|
+
if (req.modelDescriptor !== void 0 && (supported === void 0 || !supported.includes(explicit))) {
|
|
175
|
+
throw new core.LlmError(
|
|
176
|
+
`serviceTier "${explicit}" is not supported for model "${model}".`,
|
|
177
|
+
{ kind: "bad_request", retryable: false }
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
config.serviceTier = explicit;
|
|
181
|
+
} else {
|
|
182
|
+
if (req.modelDescriptor === void 0) {
|
|
183
|
+
config.serviceTier = "flex";
|
|
184
|
+
} else if (supported?.includes("flex") === true) {
|
|
185
|
+
config.serviceTier = "flex";
|
|
186
|
+
}
|
|
187
|
+
}
|
|
259
188
|
const reasoning = genConfig.reasoning;
|
|
260
189
|
if (reasoning !== void 0) {
|
|
261
190
|
const reasoningApi = req.modelDescriptor?.capabilities?.reasoningApi;
|
|
191
|
+
if (reasoning.effort !== void 0 && reasoning.budgetTokens !== void 0) {
|
|
192
|
+
throw new core.LlmError(
|
|
193
|
+
`Provide either reasoning.effort or reasoning.budgetTokens, not both, for model "${model}".`,
|
|
194
|
+
{ kind: "bad_request", retryable: false }
|
|
195
|
+
);
|
|
196
|
+
}
|
|
262
197
|
if (reasoningApi === "budget") {
|
|
263
198
|
const budget = reasoning.budgetTokens !== void 0 ? reasoning.budgetTokens : reasoning.effort !== void 0 ? EFFORT_BUDGET[reasoning.effort] ?? 0 : void 0;
|
|
264
199
|
config.thinkingConfig = {
|
|
265
200
|
...budget !== void 0 ? { thinkingBudget: budget } : {},
|
|
266
201
|
...reasoning.includeThoughts === true ? { includeThoughts: true } : {}
|
|
267
202
|
};
|
|
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
203
|
} else if (reasoningApi === "level") {
|
|
204
|
+
if (reasoning.budgetTokens !== void 0) {
|
|
205
|
+
throw new core.LlmError(
|
|
206
|
+
`reasoning.budgetTokens is not supported for model "${model}" (it uses thinkingLevel, not thinkingBudget); use reasoning.effort instead.`,
|
|
207
|
+
{ kind: "bad_request", retryable: false }
|
|
208
|
+
);
|
|
209
|
+
}
|
|
276
210
|
let thinkingLevel;
|
|
277
211
|
if (reasoning.effort !== void 0) {
|
|
278
212
|
switch (reasoning.effort) {
|
|
@@ -292,45 +226,23 @@ function geminiAdapter(opts) {
|
|
|
292
226
|
core.assertNever(reasoning.effort);
|
|
293
227
|
}
|
|
294
228
|
}
|
|
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
229
|
config.thinkingConfig = {
|
|
303
230
|
...thinkingLevel !== void 0 ? { thinkingLevel } : {},
|
|
304
231
|
...reasoning.includeThoughts === true ? { includeThoughts: true } : {}
|
|
305
232
|
};
|
|
306
233
|
} else {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
});
|
|
234
|
+
throw new core.LlmError(
|
|
235
|
+
`Model "${model}" does not support reasoning/thinkingConfig.`,
|
|
236
|
+
{ kind: "bad_request", retryable: false }
|
|
237
|
+
);
|
|
312
238
|
}
|
|
313
239
|
}
|
|
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
|
-
});
|
|
240
|
+
const structuredOutputRequested = req.outputJsonSchema !== void 0;
|
|
241
|
+
if (structuredOutputRequested) {
|
|
242
|
+
const nativeStructuredOutput = req.modelDescriptor?.capabilities?.nativeStructuredOutput !== false;
|
|
243
|
+
if (nativeStructuredOutput) {
|
|
244
|
+
config.responseMimeType = "application/json";
|
|
245
|
+
config.responseSchema = req.outputJsonSchema;
|
|
334
246
|
}
|
|
335
247
|
}
|
|
336
248
|
const googleOpts = genConfig.providerOptions?.["google"];
|
|
@@ -338,27 +250,23 @@ function geminiAdapter(opts) {
|
|
|
338
250
|
Object.assign(config, googleOpts);
|
|
339
251
|
}
|
|
340
252
|
if (req.modelDescriptor?.capabilities?.sampling === "fixed") {
|
|
341
|
-
const
|
|
253
|
+
const offendingSampling = [];
|
|
342
254
|
if ("temperature" in config) {
|
|
343
|
-
|
|
344
|
-
droppedSampling.push("temperature");
|
|
255
|
+
offendingSampling.push("temperature");
|
|
345
256
|
}
|
|
346
257
|
if ("topP" in config) {
|
|
347
|
-
|
|
348
|
-
droppedSampling.push("topP");
|
|
258
|
+
offendingSampling.push("topP");
|
|
349
259
|
}
|
|
350
260
|
if ("topK" in config) {
|
|
351
|
-
|
|
352
|
-
droppedSampling.push("topK");
|
|
261
|
+
offendingSampling.push("topK");
|
|
353
262
|
}
|
|
354
|
-
if (
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
setting: droppedSampling.join(", "),
|
|
358
|
-
details: `Sampling parameter(s) [${droppedSampling.join(
|
|
263
|
+
if (offendingSampling.length > 0) {
|
|
264
|
+
throw new core.LlmError(
|
|
265
|
+
`Sampling parameters [${offendingSampling.join(
|
|
359
266
|
", "
|
|
360
|
-
)}]
|
|
361
|
-
|
|
267
|
+
)}] are not supported for model "${model}" (fixed sampling); they were supplied via providerOptions.google.`,
|
|
268
|
+
{ kind: "bad_request", retryable: false }
|
|
269
|
+
);
|
|
362
270
|
}
|
|
363
271
|
}
|
|
364
272
|
const configAsAny = config;
|
|
@@ -369,32 +277,41 @@ function geminiAdapter(opts) {
|
|
|
369
277
|
}
|
|
370
278
|
return false;
|
|
371
279
|
});
|
|
372
|
-
if (groundingRequested &&
|
|
280
|
+
if (groundingRequested && structuredOutputRequested) {
|
|
373
281
|
throw new core.LlmError(
|
|
374
|
-
"Grounding (googleSearch) cannot be combined with structured output (output.
|
|
282
|
+
"Grounding (googleSearch) cannot be combined with structured output (output.jsonSchema) on Gemini; choose one.",
|
|
375
283
|
{ kind: "bad_request", retryable: false }
|
|
376
284
|
);
|
|
377
285
|
}
|
|
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;
|
|
286
|
+
let tierTimeoutHandle;
|
|
287
|
+
const clearTierTimeout = () => {
|
|
288
|
+
if (tierTimeoutHandle !== void 0) {
|
|
289
|
+
clearTimeout(tierTimeoutHandle);
|
|
290
|
+
tierTimeoutHandle = void 0;
|
|
392
291
|
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
|
|
292
|
+
};
|
|
293
|
+
const applyTierTimeout = (tier) => {
|
|
294
|
+
clearTierTimeout();
|
|
295
|
+
delete config.abortSignal;
|
|
296
|
+
const defaultTimeoutMs = genConfig.timeoutMs === void 0 ? tier === "flex" ? FLEX_DEFAULT_TIMEOUT_MS : tier === "standard" ? STANDARD_DEFAULT_TIMEOUT_MS : void 0 : void 0;
|
|
297
|
+
if (defaultTimeoutMs !== void 0) {
|
|
298
|
+
const tierController = new AbortController();
|
|
299
|
+
const tierLabel = tier === "standard" ? "Standard" : "Flex";
|
|
300
|
+
const timeoutReason = new DOMException(
|
|
301
|
+
`${tierLabel} timeout: call exceeded ${defaultTimeoutMs}ms client-side ceiling (@google/genai #1277 belt-and-suspenders)`,
|
|
302
|
+
"TimeoutError"
|
|
303
|
+
);
|
|
304
|
+
tierTimeoutHandle = setTimeout(() => {
|
|
305
|
+
tierController.abort(timeoutReason);
|
|
306
|
+
}, defaultTimeoutMs);
|
|
307
|
+
config.abortSignal = ctx.signal !== void 0 ? AbortSignal.any([tierController.signal, ctx.signal]) : tierController.signal;
|
|
308
|
+
} else if (ctx.signal !== void 0) {
|
|
309
|
+
config.abortSignal = ctx.signal;
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
applyTierTimeout(config.serviceTier);
|
|
396
313
|
const callerHttpOptions = config.httpOptions;
|
|
397
|
-
const computedTimeoutMs = genConfig.timeoutMs !== void 0 ? genConfig.timeoutMs + TRANSPORT_TIMEOUT_BUFFER_MS :
|
|
314
|
+
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
315
|
const mergedHttpOptions = {
|
|
399
316
|
...computedTimeoutMs !== void 0 ? { timeout: computedTimeoutMs } : {},
|
|
400
317
|
...callerHttpOptions
|
|
@@ -402,32 +319,71 @@ function geminiAdapter(opts) {
|
|
|
402
319
|
if (Object.keys(mergedHttpOptions).length > 0) {
|
|
403
320
|
config.httpOptions = mergedHttpOptions;
|
|
404
321
|
}
|
|
405
|
-
const params = {
|
|
406
|
-
model,
|
|
407
|
-
contents,
|
|
408
|
-
config
|
|
409
|
-
};
|
|
410
322
|
let response;
|
|
411
|
-
|
|
323
|
+
let servedServiceTier = config.serviceTier;
|
|
324
|
+
const dispatch = async () => {
|
|
325
|
+
const dispatchConfig = {
|
|
326
|
+
...config,
|
|
327
|
+
...config.httpOptions !== void 0 ? { httpOptions: { ...config.httpOptions } } : {}
|
|
328
|
+
};
|
|
329
|
+
const params = {
|
|
330
|
+
model,
|
|
331
|
+
contents,
|
|
332
|
+
config: dispatchConfig
|
|
333
|
+
};
|
|
412
334
|
const buildClient = opts?._clientFactory ?? buildGoogleClient;
|
|
413
335
|
const client = opts?.client !== void 0 ? opts.client : await buildClient(ctx.auth);
|
|
414
336
|
ctx.logger.debug(
|
|
415
|
-
{
|
|
337
|
+
{
|
|
338
|
+
model,
|
|
339
|
+
configKeys: Object.keys(dispatchConfig),
|
|
340
|
+
serviceTier: dispatchConfig.serviceTier
|
|
341
|
+
},
|
|
416
342
|
"llm.adapter.dispatch"
|
|
417
343
|
);
|
|
418
|
-
|
|
344
|
+
return client.models.generateContent(params);
|
|
345
|
+
};
|
|
346
|
+
try {
|
|
347
|
+
response = await dispatch();
|
|
419
348
|
} catch (rawErr) {
|
|
420
349
|
const classified = core.classifyError(rawErr);
|
|
421
|
-
|
|
350
|
+
const typed = new core.LlmError(classified.message, {
|
|
422
351
|
kind: classified.kind,
|
|
423
352
|
retryable: classified.retryable,
|
|
424
353
|
...classified.httpStatus !== void 0 ? { httpStatus: classified.httpStatus } : {},
|
|
425
354
|
...classified.retryAfterMs !== void 0 ? { retryAfterMs: classified.retryAfterMs } : {},
|
|
426
355
|
provider: "google",
|
|
427
|
-
cause: classified.cause ?? rawErr
|
|
356
|
+
cause: classified.cause ?? rawErr,
|
|
357
|
+
...servedServiceTier !== void 0 ? { servedServiceTier } : {}
|
|
428
358
|
});
|
|
359
|
+
if (config.serviceTier === "flex" && genConfig.flexFallback !== false && isGeminiCapacityError(typed)) {
|
|
360
|
+
config.serviceTier = "standard";
|
|
361
|
+
servedServiceTier = "standard";
|
|
362
|
+
const fallbackTimeout = genConfig.timeoutMs !== void 0 ? genConfig.timeoutMs + TRANSPORT_TIMEOUT_BUFFER_MS : STANDARD_DEFAULT_TIMEOUT_MS;
|
|
363
|
+
config.httpOptions = {
|
|
364
|
+
timeout: fallbackTimeout,
|
|
365
|
+
...callerHttpOptions
|
|
366
|
+
};
|
|
367
|
+
applyTierTimeout("standard");
|
|
368
|
+
try {
|
|
369
|
+
response = await dispatch();
|
|
370
|
+
} catch (fallbackRawErr) {
|
|
371
|
+
const fallbackClassified = core.classifyError(fallbackRawErr);
|
|
372
|
+
throw new core.LlmError(fallbackClassified.message, {
|
|
373
|
+
kind: fallbackClassified.kind,
|
|
374
|
+
retryable: fallbackClassified.retryable,
|
|
375
|
+
...fallbackClassified.httpStatus !== void 0 ? { httpStatus: fallbackClassified.httpStatus } : {},
|
|
376
|
+
...fallbackClassified.retryAfterMs !== void 0 ? { retryAfterMs: fallbackClassified.retryAfterMs } : {},
|
|
377
|
+
provider: "google",
|
|
378
|
+
cause: fallbackClassified.cause ?? fallbackRawErr,
|
|
379
|
+
servedServiceTier: "standard"
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
} else {
|
|
383
|
+
throw typed;
|
|
384
|
+
}
|
|
429
385
|
} finally {
|
|
430
|
-
|
|
386
|
+
clearTierTimeout();
|
|
431
387
|
}
|
|
432
388
|
const hasBlockReason = response.promptFeedback?.blockReason !== void 0;
|
|
433
389
|
const hasCandidates = response.candidates !== void 0 && response.candidates.length > 0;
|
|
@@ -470,7 +426,7 @@ function geminiAdapter(opts) {
|
|
|
470
426
|
const text = textParts.join("");
|
|
471
427
|
const reasoningText = thoughtParts.length > 0 ? thoughtParts.join("") : void 0;
|
|
472
428
|
let rawStructured;
|
|
473
|
-
if (
|
|
429
|
+
if (structuredOutputRequested && text.length > 0) {
|
|
474
430
|
try {
|
|
475
431
|
rawStructured = JSON.parse(text);
|
|
476
432
|
} catch {
|
|
@@ -482,6 +438,7 @@ function geminiAdapter(opts) {
|
|
|
482
438
|
model,
|
|
483
439
|
usage,
|
|
484
440
|
warnings,
|
|
441
|
+
...servedServiceTier !== void 0 ? { servedServiceTier } : {},
|
|
485
442
|
...text.length > 0 ? { text } : {},
|
|
486
443
|
...reasoningText !== void 0 ? { reasoningText } : {},
|
|
487
444
|
...rawStructured !== void 0 ? { rawStructured } : {},
|
|
@@ -910,9 +867,10 @@ var GoogleCacheStore = class {
|
|
|
910
867
|
exports.FLEX_DEFAULT_TIMEOUT_MS = FLEX_DEFAULT_TIMEOUT_MS;
|
|
911
868
|
exports.GoogleCacheStore = GoogleCacheStore;
|
|
912
869
|
exports.GoogleFileStore = GoogleFileStore;
|
|
870
|
+
exports.STANDARD_DEFAULT_TIMEOUT_MS = STANDARD_DEFAULT_TIMEOUT_MS;
|
|
913
871
|
exports.TRANSPORT_TIMEOUT_BUFFER_MS = TRANSPORT_TIMEOUT_BUFFER_MS;
|
|
914
872
|
exports.buildGoogleClient = buildGoogleClient;
|
|
915
873
|
exports.geminiAdapter = geminiAdapter;
|
|
916
|
-
exports.
|
|
874
|
+
exports.isGeminiCapacityError = isGeminiCapacityError;
|
|
917
875
|
//# sourceMappingURL=index.cjs.map
|
|
918
876
|
//# sourceMappingURL=index.cjs.map
|