@gullabs/google 0.2.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 +191 -0
- package/README.md +46 -0
- package/dist/index.cjs +918 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +591 -0
- package/dist/index.d.ts +591 -0
- package/dist/index.js +910 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,910 @@
|
|
|
1
|
+
import { assertNever, LlmError, classifyError, redactSecrets } from '@gullabs/core';
|
|
2
|
+
import { ZodOptional, ZodNullable, ZodDefault, ZodObject, ZodString, ZodNumber, ZodBoolean, ZodArray, ZodEnum, ZodLiteral } from 'zod';
|
|
3
|
+
|
|
4
|
+
// src/adapter.ts
|
|
5
|
+
|
|
6
|
+
// src/client.ts
|
|
7
|
+
var FLEX_DEFAULT_TIMEOUT_MS = 15e5;
|
|
8
|
+
var TRANSPORT_TIMEOUT_BUFFER_MS = 5e3;
|
|
9
|
+
async function buildGoogleClient(auth) {
|
|
10
|
+
const { GoogleGenAI } = await import('@google/genai');
|
|
11
|
+
const ai = new GoogleGenAI({ apiKey: auth.apiKey });
|
|
12
|
+
return {
|
|
13
|
+
models: {
|
|
14
|
+
async generateContent(params) {
|
|
15
|
+
const result = await ai.models.generateContent(params);
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function isOptionalField(schema) {
|
|
22
|
+
if (schema instanceof ZodOptional || schema instanceof ZodDefault) return true;
|
|
23
|
+
if (schema instanceof ZodNullable) {
|
|
24
|
+
const unwrapped = schema.unwrap();
|
|
25
|
+
return isOptionalField(unwrapped);
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
function zodToGeminiSchema(schema) {
|
|
30
|
+
return convertSchema(schema, false);
|
|
31
|
+
}
|
|
32
|
+
function convertSchema(schema, nullable) {
|
|
33
|
+
const desc = typeof schema.description === "string" ? schema.description : void 0;
|
|
34
|
+
if (schema instanceof ZodOptional) {
|
|
35
|
+
const unwrapped = schema.unwrap();
|
|
36
|
+
return convertSchema(unwrapped, nullable);
|
|
37
|
+
}
|
|
38
|
+
if (schema instanceof ZodNullable) {
|
|
39
|
+
const unwrapped = schema.unwrap();
|
|
40
|
+
return convertSchema(unwrapped, true);
|
|
41
|
+
}
|
|
42
|
+
if (schema instanceof ZodDefault) {
|
|
43
|
+
const innerType = schema._def.innerType;
|
|
44
|
+
return convertSchema(innerType, nullable);
|
|
45
|
+
}
|
|
46
|
+
if (schema instanceof ZodObject) {
|
|
47
|
+
const shape = schema.shape;
|
|
48
|
+
const properties = {};
|
|
49
|
+
const required = [];
|
|
50
|
+
for (const [key, fieldType] of Object.entries(shape)) {
|
|
51
|
+
const fieldSchema = convertSchema(fieldType, false);
|
|
52
|
+
if (fieldSchema === void 0) return void 0;
|
|
53
|
+
properties[key] = fieldSchema;
|
|
54
|
+
if (!isOptionalField(fieldType)) {
|
|
55
|
+
required.push(key);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const result = {
|
|
59
|
+
type: "object",
|
|
60
|
+
properties,
|
|
61
|
+
...required.length > 0 ? { required } : {},
|
|
62
|
+
...desc !== void 0 ? { description: desc } : {},
|
|
63
|
+
...nullable ? { nullable: true } : {}
|
|
64
|
+
};
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
if (schema instanceof ZodString) {
|
|
68
|
+
return {
|
|
69
|
+
type: "string",
|
|
70
|
+
...desc !== void 0 ? { description: desc } : {},
|
|
71
|
+
...nullable ? { nullable: true } : {}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (schema instanceof ZodNumber) {
|
|
75
|
+
const checks = schema._def.checks ?? [];
|
|
76
|
+
const isInt = checks.some((c) => c.kind === "int");
|
|
77
|
+
return {
|
|
78
|
+
type: isInt ? "integer" : "number",
|
|
79
|
+
...desc !== void 0 ? { description: desc } : {},
|
|
80
|
+
...nullable ? { nullable: true } : {}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (schema instanceof ZodBoolean) {
|
|
84
|
+
return {
|
|
85
|
+
type: "boolean",
|
|
86
|
+
...desc !== void 0 ? { description: desc } : {},
|
|
87
|
+
...nullable ? { nullable: true } : {}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (schema instanceof ZodArray) {
|
|
91
|
+
const element = schema.element;
|
|
92
|
+
const items = convertSchema(element, false);
|
|
93
|
+
if (items === void 0) return void 0;
|
|
94
|
+
return {
|
|
95
|
+
type: "array",
|
|
96
|
+
items,
|
|
97
|
+
...desc !== void 0 ? { description: desc } : {},
|
|
98
|
+
...nullable ? { nullable: true } : {}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (schema instanceof ZodEnum) {
|
|
102
|
+
return {
|
|
103
|
+
type: "string",
|
|
104
|
+
enum: schema.options,
|
|
105
|
+
...desc !== void 0 ? { description: desc } : {},
|
|
106
|
+
...nullable ? { nullable: true } : {}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
if (schema instanceof ZodLiteral) {
|
|
110
|
+
const val = schema.value;
|
|
111
|
+
if (typeof val === "string") {
|
|
112
|
+
return {
|
|
113
|
+
type: "string",
|
|
114
|
+
enum: [val],
|
|
115
|
+
...desc !== void 0 ? { description: desc } : {},
|
|
116
|
+
...nullable ? { nullable: true } : {}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
if (typeof val === "number") {
|
|
120
|
+
return {
|
|
121
|
+
type: "number",
|
|
122
|
+
...desc !== void 0 ? { description: desc } : {},
|
|
123
|
+
...nullable ? { nullable: true } : {}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (typeof val === "boolean") {
|
|
127
|
+
return {
|
|
128
|
+
type: "boolean",
|
|
129
|
+
...desc !== void 0 ? { description: desc } : {},
|
|
130
|
+
...nullable ? { nullable: true } : {}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return void 0;
|
|
134
|
+
}
|
|
135
|
+
return void 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// src/adapter.ts
|
|
139
|
+
var EFFORT_BUDGET = {
|
|
140
|
+
none: 0,
|
|
141
|
+
low: 1024,
|
|
142
|
+
medium: 8192,
|
|
143
|
+
high: 24576
|
|
144
|
+
};
|
|
145
|
+
function mapFinishReason(raw) {
|
|
146
|
+
if (raw === void 0) return void 0;
|
|
147
|
+
switch (raw) {
|
|
148
|
+
case "STOP":
|
|
149
|
+
return "stop";
|
|
150
|
+
case "MAX_TOKENS":
|
|
151
|
+
return "length";
|
|
152
|
+
case "SAFETY":
|
|
153
|
+
case "RECITATION":
|
|
154
|
+
case "BLOCKLIST":
|
|
155
|
+
case "PROHIBITED_CONTENT":
|
|
156
|
+
case "IMAGE_SAFETY":
|
|
157
|
+
return "content_filter";
|
|
158
|
+
default:
|
|
159
|
+
return "other";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function mapMediaResolution(res) {
|
|
163
|
+
switch (res) {
|
|
164
|
+
case "low":
|
|
165
|
+
return "MEDIA_RESOLUTION_LOW";
|
|
166
|
+
case "medium":
|
|
167
|
+
return "MEDIA_RESOLUTION_MEDIUM";
|
|
168
|
+
case "high":
|
|
169
|
+
return "MEDIA_RESOLUTION_HIGH";
|
|
170
|
+
default:
|
|
171
|
+
return assertNever(res);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function mapUsage(meta) {
|
|
175
|
+
const promptTokenCount = meta?.promptTokenCount ?? 0;
|
|
176
|
+
const candidatesTokenCount = meta?.candidatesTokenCount ?? 0;
|
|
177
|
+
const cachedContentTokenCount = meta?.cachedContentTokenCount;
|
|
178
|
+
const thoughtsTokenCount = meta?.thoughtsTokenCount;
|
|
179
|
+
const outputTokens = candidatesTokenCount + (thoughtsTokenCount ?? 0);
|
|
180
|
+
const inputTokens = promptTokenCount;
|
|
181
|
+
const totalTokens = meta?.totalTokenCount;
|
|
182
|
+
const details = {
|
|
183
|
+
input: inputTokens,
|
|
184
|
+
output: outputTokens,
|
|
185
|
+
...cachedContentTokenCount !== void 0 ? { cached: cachedContentTokenCount } : {},
|
|
186
|
+
...thoughtsTokenCount !== void 0 ? { thinking: thoughtsTokenCount } : {}
|
|
187
|
+
};
|
|
188
|
+
const raw = meta !== void 0 ? meta : null;
|
|
189
|
+
const usage = {
|
|
190
|
+
inputTokens,
|
|
191
|
+
outputTokens,
|
|
192
|
+
details,
|
|
193
|
+
raw,
|
|
194
|
+
...cachedContentTokenCount !== void 0 ? { cachedInputTokens: cachedContentTokenCount } : {},
|
|
195
|
+
...thoughtsTokenCount !== void 0 ? { thinkingTokens: thoughtsTokenCount } : {},
|
|
196
|
+
...totalTokens !== void 0 ? { totalTokens } : {}
|
|
197
|
+
};
|
|
198
|
+
return usage;
|
|
199
|
+
}
|
|
200
|
+
function geminiAdapter(opts) {
|
|
201
|
+
return {
|
|
202
|
+
id: "google",
|
|
203
|
+
async run(req, ctx) {
|
|
204
|
+
const warnings = [];
|
|
205
|
+
const model = req.model;
|
|
206
|
+
const mapPart = (p) => {
|
|
207
|
+
switch (p.kind) {
|
|
208
|
+
case "text":
|
|
209
|
+
return { text: p.text };
|
|
210
|
+
case "inline-media": {
|
|
211
|
+
return {
|
|
212
|
+
inlineData: {
|
|
213
|
+
mimeType: p.mimeType,
|
|
214
|
+
data: p.data
|
|
215
|
+
},
|
|
216
|
+
...p.mediaResolution !== void 0 ? { mediaResolution: { level: mapMediaResolution(p.mediaResolution) } } : {}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
case "file-uri": {
|
|
220
|
+
return {
|
|
221
|
+
fileData: {
|
|
222
|
+
mimeType: p.mimeType,
|
|
223
|
+
fileUri: p.uri
|
|
224
|
+
},
|
|
225
|
+
...p.mediaResolution !== void 0 ? { mediaResolution: { level: mapMediaResolution(p.mediaResolution) } } : {}
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
default:
|
|
229
|
+
return assertNever(p);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
const contents = req.messages.map((msg) => ({
|
|
233
|
+
role: msg.role === "assistant" ? "model" : "user",
|
|
234
|
+
parts: msg.parts.map(mapPart)
|
|
235
|
+
}));
|
|
236
|
+
const genConfig = req.config;
|
|
237
|
+
const config = {};
|
|
238
|
+
if (req.system !== void 0) {
|
|
239
|
+
config.systemInstruction = { parts: [{ text: req.system }] };
|
|
240
|
+
}
|
|
241
|
+
if (genConfig.temperature !== void 0) {
|
|
242
|
+
config.temperature = genConfig.temperature;
|
|
243
|
+
}
|
|
244
|
+
if (genConfig.topP !== void 0) {
|
|
245
|
+
config.topP = genConfig.topP;
|
|
246
|
+
}
|
|
247
|
+
if (genConfig.topK !== void 0) {
|
|
248
|
+
config.topK = genConfig.topK;
|
|
249
|
+
}
|
|
250
|
+
if (genConfig.maxOutputTokens !== void 0) {
|
|
251
|
+
config.maxOutputTokens = genConfig.maxOutputTokens;
|
|
252
|
+
}
|
|
253
|
+
if (genConfig.stopSequences !== void 0) {
|
|
254
|
+
config.stopSequences = genConfig.stopSequences;
|
|
255
|
+
}
|
|
256
|
+
config.serviceTier = genConfig.serviceTier === "standard" ? "standard" : "flex";
|
|
257
|
+
const reasoning = genConfig.reasoning;
|
|
258
|
+
if (reasoning !== void 0) {
|
|
259
|
+
const reasoningApi = req.modelDescriptor?.capabilities?.reasoningApi;
|
|
260
|
+
if (reasoningApi === "budget") {
|
|
261
|
+
const budget = reasoning.budgetTokens !== void 0 ? reasoning.budgetTokens : reasoning.effort !== void 0 ? EFFORT_BUDGET[reasoning.effort] ?? 0 : void 0;
|
|
262
|
+
config.thinkingConfig = {
|
|
263
|
+
...budget !== void 0 ? { thinkingBudget: budget } : {},
|
|
264
|
+
...reasoning.includeThoughts === true ? { includeThoughts: true } : {}
|
|
265
|
+
};
|
|
266
|
+
if (reasoning.effort !== void 0 && reasoning.budgetTokens !== void 0) {
|
|
267
|
+
warnings.push({
|
|
268
|
+
type: "reasoning-mapping",
|
|
269
|
+
quality: "approximate",
|
|
270
|
+
details: "budgetTokens takes precedence over effort for thinkingBudget"
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
} else if (reasoningApi === "level") {
|
|
274
|
+
let thinkingLevel;
|
|
275
|
+
if (reasoning.effort !== void 0) {
|
|
276
|
+
switch (reasoning.effort) {
|
|
277
|
+
case "none":
|
|
278
|
+
thinkingLevel = "MINIMAL";
|
|
279
|
+
break;
|
|
280
|
+
case "low":
|
|
281
|
+
thinkingLevel = "LOW";
|
|
282
|
+
break;
|
|
283
|
+
case "medium":
|
|
284
|
+
thinkingLevel = "MEDIUM";
|
|
285
|
+
break;
|
|
286
|
+
case "high":
|
|
287
|
+
thinkingLevel = "HIGH";
|
|
288
|
+
break;
|
|
289
|
+
default:
|
|
290
|
+
assertNever(reasoning.effort);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (reasoning.budgetTokens !== void 0) {
|
|
294
|
+
warnings.push({
|
|
295
|
+
type: "reasoning-mapping",
|
|
296
|
+
quality: "approximate",
|
|
297
|
+
details: "budgetTokens is not supported for gemini-3.x models; mapping effort to thinkingLevel instead"
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
config.thinkingConfig = {
|
|
301
|
+
...thinkingLevel !== void 0 ? { thinkingLevel } : {},
|
|
302
|
+
...reasoning.includeThoughts === true ? { includeThoughts: true } : {}
|
|
303
|
+
};
|
|
304
|
+
} else {
|
|
305
|
+
warnings.push({
|
|
306
|
+
type: "reasoning-mapping",
|
|
307
|
+
quality: "unsupported",
|
|
308
|
+
details: `thinkingConfig not mapped for model "${model}"; unknown generation`
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
let outputSchemaRequested = false;
|
|
313
|
+
if (req.outputSchema !== void 0) {
|
|
314
|
+
outputSchemaRequested = true;
|
|
315
|
+
config.responseMimeType = "application/json";
|
|
316
|
+
if (req.outputSchema["~standard"].vendor === "zod") {
|
|
317
|
+
const geminiSchema = zodToGeminiSchema(req.outputSchema);
|
|
318
|
+
if (geminiSchema !== void 0) {
|
|
319
|
+
config.responseSchema = geminiSchema;
|
|
320
|
+
} else {
|
|
321
|
+
warnings.push({
|
|
322
|
+
type: "unsupported-setting",
|
|
323
|
+
setting: "output.schema",
|
|
324
|
+
details: "Could not convert Zod schema to Gemini responseSchema; proceeding with responseMimeType only. Engine will still validate."
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
} else {
|
|
328
|
+
warnings.push({
|
|
329
|
+
type: "other",
|
|
330
|
+
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.`
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
const googleOpts = genConfig.providerOptions?.["google"];
|
|
335
|
+
if (googleOpts !== void 0 && typeof googleOpts === "object" && googleOpts !== null) {
|
|
336
|
+
Object.assign(config, googleOpts);
|
|
337
|
+
}
|
|
338
|
+
if (req.modelDescriptor?.capabilities?.sampling === "fixed") {
|
|
339
|
+
const droppedSampling = [];
|
|
340
|
+
if ("temperature" in config) {
|
|
341
|
+
delete config.temperature;
|
|
342
|
+
droppedSampling.push("temperature");
|
|
343
|
+
}
|
|
344
|
+
if ("topP" in config) {
|
|
345
|
+
delete config.topP;
|
|
346
|
+
droppedSampling.push("topP");
|
|
347
|
+
}
|
|
348
|
+
if ("topK" in config) {
|
|
349
|
+
delete config.topK;
|
|
350
|
+
droppedSampling.push("topK");
|
|
351
|
+
}
|
|
352
|
+
if (droppedSampling.length > 0) {
|
|
353
|
+
warnings.push({
|
|
354
|
+
type: "unsupported-setting",
|
|
355
|
+
setting: droppedSampling.join(", "),
|
|
356
|
+
details: `Sampling parameter(s) [${droppedSampling.join(
|
|
357
|
+
", "
|
|
358
|
+
)}] from providerOptions.google were stripped: this model has fixed sampling (Gemini 3.x) and the API rejects these fields.`
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
const configAsAny = config;
|
|
363
|
+
const groundingRequested = Array.isArray(configAsAny.tools) && configAsAny.tools.some((t) => {
|
|
364
|
+
if (t !== null && typeof t === "object") {
|
|
365
|
+
const tool = t;
|
|
366
|
+
return "googleSearch" in tool || "googleSearchRetrieval" in tool;
|
|
367
|
+
}
|
|
368
|
+
return false;
|
|
369
|
+
});
|
|
370
|
+
if (groundingRequested && req.outputSchema !== void 0) {
|
|
371
|
+
throw new LlmError(
|
|
372
|
+
"Grounding (googleSearch) cannot be combined with structured output (output.schema) on Gemini; choose one.",
|
|
373
|
+
{ kind: "bad_request", retryable: false }
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
let _flexTimeoutHandle;
|
|
377
|
+
if (config.serviceTier === "flex" && genConfig.timeoutMs === void 0) {
|
|
378
|
+
const flexController = new AbortController();
|
|
379
|
+
const timeoutReason = new DOMException(
|
|
380
|
+
`Flex timeout: call exceeded ${FLEX_DEFAULT_TIMEOUT_MS}ms client-side ceiling (@google/genai #1277 belt-and-suspenders)`,
|
|
381
|
+
"TimeoutError"
|
|
382
|
+
);
|
|
383
|
+
_flexTimeoutHandle = setTimeout(() => {
|
|
384
|
+
flexController.abort(timeoutReason);
|
|
385
|
+
}, FLEX_DEFAULT_TIMEOUT_MS);
|
|
386
|
+
if (ctx.signal !== void 0) {
|
|
387
|
+
config.abortSignal = AbortSignal.any([flexController.signal, ctx.signal]);
|
|
388
|
+
} else {
|
|
389
|
+
config.abortSignal = flexController.signal;
|
|
390
|
+
}
|
|
391
|
+
} else if (ctx.signal !== void 0) {
|
|
392
|
+
config.abortSignal = ctx.signal;
|
|
393
|
+
}
|
|
394
|
+
const callerHttpOptions = config.httpOptions;
|
|
395
|
+
const computedTimeoutMs = genConfig.timeoutMs !== void 0 ? genConfig.timeoutMs + TRANSPORT_TIMEOUT_BUFFER_MS : genConfig.serviceTier === "flex" ? FLEX_DEFAULT_TIMEOUT_MS : void 0;
|
|
396
|
+
const mergedHttpOptions = {
|
|
397
|
+
...computedTimeoutMs !== void 0 ? { timeout: computedTimeoutMs } : {},
|
|
398
|
+
...callerHttpOptions
|
|
399
|
+
};
|
|
400
|
+
if (Object.keys(mergedHttpOptions).length > 0) {
|
|
401
|
+
config.httpOptions = mergedHttpOptions;
|
|
402
|
+
}
|
|
403
|
+
const params = {
|
|
404
|
+
model,
|
|
405
|
+
contents,
|
|
406
|
+
config
|
|
407
|
+
};
|
|
408
|
+
let response;
|
|
409
|
+
try {
|
|
410
|
+
const buildClient = opts?._clientFactory ?? buildGoogleClient;
|
|
411
|
+
const client = opts?.client !== void 0 ? opts.client : await buildClient(ctx.auth);
|
|
412
|
+
ctx.logger.debug(
|
|
413
|
+
{ model, configKeys: Object.keys(config) },
|
|
414
|
+
"llm.adapter.dispatch"
|
|
415
|
+
);
|
|
416
|
+
response = await client.models.generateContent(params);
|
|
417
|
+
} catch (rawErr) {
|
|
418
|
+
const classified = classifyError(rawErr);
|
|
419
|
+
throw new LlmError(classified.message, {
|
|
420
|
+
kind: classified.kind,
|
|
421
|
+
retryable: classified.retryable,
|
|
422
|
+
...classified.httpStatus !== void 0 ? { httpStatus: classified.httpStatus } : {},
|
|
423
|
+
...classified.retryAfterMs !== void 0 ? { retryAfterMs: classified.retryAfterMs } : {},
|
|
424
|
+
provider: "google",
|
|
425
|
+
cause: classified.cause ?? rawErr
|
|
426
|
+
});
|
|
427
|
+
} finally {
|
|
428
|
+
if (_flexTimeoutHandle !== void 0) clearTimeout(_flexTimeoutHandle);
|
|
429
|
+
}
|
|
430
|
+
const hasBlockReason = response.promptFeedback?.blockReason !== void 0;
|
|
431
|
+
const hasCandidates = response.candidates !== void 0 && response.candidates.length > 0;
|
|
432
|
+
if (hasBlockReason || !hasCandidates) {
|
|
433
|
+
const reason = response.promptFeedback?.blockReason ?? "NO_CANDIDATES";
|
|
434
|
+
throw new LlmError(`Gemini response blocked: ${reason}`, {
|
|
435
|
+
kind: "content_filter",
|
|
436
|
+
retryable: false,
|
|
437
|
+
provider: "google"
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
const candidates = response.candidates;
|
|
441
|
+
if (candidates === void 0 || candidates.length === 0) {
|
|
442
|
+
throw new LlmError("Gemini response blocked: NO_CANDIDATES", {
|
|
443
|
+
kind: "content_filter",
|
|
444
|
+
retryable: false,
|
|
445
|
+
provider: "google"
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
const candidate = candidates[0];
|
|
449
|
+
if (candidate === void 0) {
|
|
450
|
+
throw new LlmError("Gemini response blocked: NO_CANDIDATES", {
|
|
451
|
+
kind: "content_filter",
|
|
452
|
+
retryable: false,
|
|
453
|
+
provider: "google"
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
const parts = candidate.content?.parts ?? [];
|
|
457
|
+
const textParts = [];
|
|
458
|
+
const thoughtParts = [];
|
|
459
|
+
for (const part of parts) {
|
|
460
|
+
if (part.text !== void 0) {
|
|
461
|
+
if (part.thought === true) {
|
|
462
|
+
thoughtParts.push(part.text);
|
|
463
|
+
} else {
|
|
464
|
+
textParts.push(part.text);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
const text = textParts.join("");
|
|
469
|
+
const reasoningText = thoughtParts.length > 0 ? thoughtParts.join("") : void 0;
|
|
470
|
+
let rawStructured;
|
|
471
|
+
if (outputSchemaRequested && text.length > 0) {
|
|
472
|
+
try {
|
|
473
|
+
rawStructured = JSON.parse(text);
|
|
474
|
+
} catch {
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const usage = mapUsage(response.usageMetadata);
|
|
478
|
+
const finishReason = mapFinishReason(candidate.finishReason);
|
|
479
|
+
const result = {
|
|
480
|
+
model,
|
|
481
|
+
usage,
|
|
482
|
+
warnings,
|
|
483
|
+
...text.length > 0 ? { text } : {},
|
|
484
|
+
...reasoningText !== void 0 ? { reasoningText } : {},
|
|
485
|
+
...rawStructured !== void 0 ? { rawStructured } : {},
|
|
486
|
+
...finishReason !== void 0 ? { finishReason } : {},
|
|
487
|
+
...response.modelVersion !== void 0 ? { modelVersion: response.modelVersion } : {},
|
|
488
|
+
...response.responseId !== void 0 ? { responseId: response.responseId } : {},
|
|
489
|
+
// Build providerMetadata — merge promptFeedback + groundingMetadata when present.
|
|
490
|
+
...(() => {
|
|
491
|
+
const pf = response.promptFeedback;
|
|
492
|
+
const gm = candidate.groundingMetadata;
|
|
493
|
+
if (pf === void 0 && gm === void 0) return {};
|
|
494
|
+
const meta = {};
|
|
495
|
+
if (pf !== void 0) {
|
|
496
|
+
meta["promptFeedback"] = pf;
|
|
497
|
+
}
|
|
498
|
+
if (gm !== void 0) {
|
|
499
|
+
meta["groundingMetadata"] = gm;
|
|
500
|
+
}
|
|
501
|
+
return { providerMetadata: meta };
|
|
502
|
+
})()
|
|
503
|
+
};
|
|
504
|
+
return result;
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
var DEFAULT_INTERVAL_MS = 3e3;
|
|
509
|
+
var DEFAULT_TIMEOUT_MS = 3e5;
|
|
510
|
+
var realSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
511
|
+
async function buildFilesClient(auth) {
|
|
512
|
+
const { GoogleGenAI } = await import('@google/genai');
|
|
513
|
+
const ai = new GoogleGenAI({ apiKey: auth.apiKey });
|
|
514
|
+
return {
|
|
515
|
+
async upload(params) {
|
|
516
|
+
const fileArg = params.file instanceof Uint8Array ? new Blob(
|
|
517
|
+
[params.file],
|
|
518
|
+
params.config?.mimeType !== void 0 && params.config.mimeType.length > 0 ? { type: params.config.mimeType } : {}
|
|
519
|
+
) : params.file;
|
|
520
|
+
const result = await ai.files.upload({
|
|
521
|
+
file: fileArg,
|
|
522
|
+
...params.config !== void 0 ? { config: params.config } : {}
|
|
523
|
+
});
|
|
524
|
+
return result;
|
|
525
|
+
},
|
|
526
|
+
async get(params) {
|
|
527
|
+
const result = await ai.files.get(params);
|
|
528
|
+
return result;
|
|
529
|
+
},
|
|
530
|
+
async delete(params) {
|
|
531
|
+
await ai.files.delete(params);
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
function makeHandle(resp, fallback) {
|
|
536
|
+
const et = resp.expirationTime;
|
|
537
|
+
return {
|
|
538
|
+
name: resp.name ?? fallback.name,
|
|
539
|
+
uri: resp.uri ?? fallback.uri,
|
|
540
|
+
mimeType: resp.mimeType ?? fallback.mimeType,
|
|
541
|
+
...et !== void 0 && et.length > 0 ? { expiresAt: new Date(et) } : {}
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
var GoogleFileStore = class {
|
|
545
|
+
auth;
|
|
546
|
+
clientOverride;
|
|
547
|
+
onDeleteError;
|
|
548
|
+
logger;
|
|
549
|
+
intervalMs;
|
|
550
|
+
timeoutMs;
|
|
551
|
+
sleep;
|
|
552
|
+
now;
|
|
553
|
+
/** Memoised client promise — built at most once per store instance. */
|
|
554
|
+
clientPromise;
|
|
555
|
+
constructor(opts) {
|
|
556
|
+
this.auth = opts.auth;
|
|
557
|
+
this.clientOverride = opts.client;
|
|
558
|
+
this.logger = opts.logger;
|
|
559
|
+
this.onDeleteError = opts.onDeleteError ?? ((name, err) => {
|
|
560
|
+
if (this.logger !== void 0) {
|
|
561
|
+
this.logger.error(
|
|
562
|
+
{ name, error: redactSecrets(classifyError(err).message) },
|
|
563
|
+
"gemini.file.delete.failed"
|
|
564
|
+
);
|
|
565
|
+
} else {
|
|
566
|
+
console.error(
|
|
567
|
+
`[GoogleFileStore] delete failed for "${name}":`,
|
|
568
|
+
redactSecrets(classifyError(err).message)
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
this.intervalMs = opts.poll?.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
573
|
+
this.timeoutMs = opts.poll?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
574
|
+
this.sleep = opts.sleep ?? realSleep;
|
|
575
|
+
this.now = opts.now ?? (() => Date.now());
|
|
576
|
+
}
|
|
577
|
+
getClient() {
|
|
578
|
+
if (this.clientOverride !== void 0) return Promise.resolve(this.clientOverride);
|
|
579
|
+
if (this.clientPromise === void 0) {
|
|
580
|
+
this.clientPromise = buildFilesClient(this.auth);
|
|
581
|
+
}
|
|
582
|
+
return this.clientPromise;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Upload bytes to the Gemini File API and wait until the file is ACTIVE.
|
|
586
|
+
*
|
|
587
|
+
* @param source - Raw bytes or Blob.
|
|
588
|
+
* @param mimeType - IANA media type, e.g. `"image/png"`.
|
|
589
|
+
* @param opts - Optional display name.
|
|
590
|
+
*/
|
|
591
|
+
async upload(source, mimeType, opts) {
|
|
592
|
+
const signal = opts?.signal;
|
|
593
|
+
const client = await this.getClient();
|
|
594
|
+
let uploadResp;
|
|
595
|
+
try {
|
|
596
|
+
uploadResp = await client.upload({
|
|
597
|
+
file: source,
|
|
598
|
+
config: {
|
|
599
|
+
mimeType,
|
|
600
|
+
...opts?.displayName !== void 0 ? { displayName: opts.displayName } : {}
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
} catch (e) {
|
|
604
|
+
throw classifyError(e);
|
|
605
|
+
}
|
|
606
|
+
const { name, uri } = uploadResp;
|
|
607
|
+
if (name === void 0 || name.length === 0 || uri === void 0 || uri.length === 0) {
|
|
608
|
+
throw new LlmError("File upload response missing required fields (name or uri)", {
|
|
609
|
+
kind: "bad_request",
|
|
610
|
+
retryable: false
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
const fallback = { name, uri, mimeType };
|
|
614
|
+
if (uploadResp.state === "ACTIVE") {
|
|
615
|
+
return makeHandle(uploadResp, fallback);
|
|
616
|
+
}
|
|
617
|
+
if (uploadResp.state === "FAILED") {
|
|
618
|
+
throw new LlmError("File processing failed immediately after upload", {
|
|
619
|
+
kind: "bad_request",
|
|
620
|
+
retryable: false
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
const deadline = this.now() + this.timeoutMs;
|
|
624
|
+
if (signal?.aborted === true) {
|
|
625
|
+
throw new LlmError("File upload polling aborted", {
|
|
626
|
+
kind: "aborted",
|
|
627
|
+
retryable: false
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
const abortRacePromise = signal !== void 0 ? new Promise((_, reject) => {
|
|
631
|
+
signal.addEventListener(
|
|
632
|
+
"abort",
|
|
633
|
+
() => {
|
|
634
|
+
reject(
|
|
635
|
+
new LlmError("File upload polling aborted", {
|
|
636
|
+
kind: "aborted",
|
|
637
|
+
retryable: false
|
|
638
|
+
})
|
|
639
|
+
);
|
|
640
|
+
},
|
|
641
|
+
{ once: true }
|
|
642
|
+
);
|
|
643
|
+
}) : void 0;
|
|
644
|
+
for (; ; ) {
|
|
645
|
+
if (this.now() >= deadline) {
|
|
646
|
+
throw new LlmError("Timed out waiting for uploaded file to become ACTIVE", {
|
|
647
|
+
kind: "timeout",
|
|
648
|
+
retryable: true
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
const sleepCall = this.sleep(this.intervalMs);
|
|
652
|
+
await (abortRacePromise !== void 0 ? Promise.race([sleepCall, abortRacePromise]) : sleepCall);
|
|
653
|
+
let pollResp;
|
|
654
|
+
try {
|
|
655
|
+
pollResp = await client.get({ name });
|
|
656
|
+
} catch (e) {
|
|
657
|
+
throw classifyError(e);
|
|
658
|
+
}
|
|
659
|
+
if (signal?.aborted === true) {
|
|
660
|
+
throw new LlmError("File upload polling aborted", {
|
|
661
|
+
kind: "aborted",
|
|
662
|
+
retryable: false
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
if (pollResp.state === "ACTIVE") {
|
|
666
|
+
return makeHandle(pollResp, fallback);
|
|
667
|
+
}
|
|
668
|
+
if (pollResp.state === "FAILED") {
|
|
669
|
+
throw new LlmError("File processing failed during polling", {
|
|
670
|
+
kind: "bad_request",
|
|
671
|
+
retryable: false
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Delete a single uploaded file.
|
|
678
|
+
* Errors are forwarded to `onDeleteError` and NOT rethrown.
|
|
679
|
+
*/
|
|
680
|
+
async delete(handle) {
|
|
681
|
+
try {
|
|
682
|
+
const client = await this.getClient();
|
|
683
|
+
await client.delete({ name: handle.name });
|
|
684
|
+
} catch (err) {
|
|
685
|
+
this.onDeleteError(handle.name, err);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Delete multiple files.
|
|
690
|
+
* Each failure is individually forwarded to `onDeleteError`; none are thrown.
|
|
691
|
+
*/
|
|
692
|
+
async deleteAll(handles) {
|
|
693
|
+
await Promise.allSettled(handles.map((h) => this.delete(h)));
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
var DEFAULT_SKEW_SECONDS = 30;
|
|
697
|
+
var DEFAULT_EXTENSION_SECONDS = 3600;
|
|
698
|
+
async function buildCachesClient(auth) {
|
|
699
|
+
const { GoogleGenAI } = await import('@google/genai');
|
|
700
|
+
const ai = new GoogleGenAI({ apiKey: auth.apiKey });
|
|
701
|
+
return {
|
|
702
|
+
async create(params) {
|
|
703
|
+
const result = await ai.caches.create(params);
|
|
704
|
+
return result;
|
|
705
|
+
},
|
|
706
|
+
async update(params) {
|
|
707
|
+
const result = await ai.caches.update(params);
|
|
708
|
+
return result;
|
|
709
|
+
},
|
|
710
|
+
async delete(params) {
|
|
711
|
+
await ai.caches.delete(params);
|
|
712
|
+
}
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
var GoogleCacheStore = class {
|
|
716
|
+
auth;
|
|
717
|
+
clientOverride;
|
|
718
|
+
coalesce;
|
|
719
|
+
skewMs;
|
|
720
|
+
onDeleteError;
|
|
721
|
+
logger;
|
|
722
|
+
now;
|
|
723
|
+
/** Memoised client promise — built at most once per store instance. */
|
|
724
|
+
clientPromise;
|
|
725
|
+
/** In-process cache of live handles, keyed by `${model}:${stableKey}`. */
|
|
726
|
+
entries = /* @__PURE__ */ new Map();
|
|
727
|
+
/** In-flight create promises when coalescing is enabled. */
|
|
728
|
+
inflight = /* @__PURE__ */ new Map();
|
|
729
|
+
constructor(opts) {
|
|
730
|
+
this.auth = opts.auth;
|
|
731
|
+
this.clientOverride = opts.client;
|
|
732
|
+
this.coalesce = opts.coalesce ?? false;
|
|
733
|
+
this.skewMs = (opts.expirySkewSeconds ?? DEFAULT_SKEW_SECONDS) * 1e3;
|
|
734
|
+
this.logger = opts.logger;
|
|
735
|
+
this.onDeleteError = opts.onDeleteError ?? ((cacheName, err) => {
|
|
736
|
+
if (this.logger !== void 0) {
|
|
737
|
+
this.logger.error(
|
|
738
|
+
{ name: cacheName, error: redactSecrets(classifyError(err).message) },
|
|
739
|
+
"gemini.cache.delete.failed"
|
|
740
|
+
);
|
|
741
|
+
} else {
|
|
742
|
+
console.error(
|
|
743
|
+
`[GoogleCacheStore] delete failed for "${cacheName}":`,
|
|
744
|
+
redactSecrets(classifyError(err).message)
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
this.now = opts.now ?? (() => Date.now());
|
|
749
|
+
}
|
|
750
|
+
getClient() {
|
|
751
|
+
if (this.clientOverride !== void 0) return Promise.resolve(this.clientOverride);
|
|
752
|
+
if (this.clientPromise === void 0) {
|
|
753
|
+
this.clientPromise = buildCachesClient(this.auth);
|
|
754
|
+
}
|
|
755
|
+
return this.clientPromise;
|
|
756
|
+
}
|
|
757
|
+
isLive(handle) {
|
|
758
|
+
return handle.expiresAt.getTime() - this.skewMs > this.now();
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Create a new cached content resource.
|
|
762
|
+
*
|
|
763
|
+
* The `expiresAt` on the returned handle is computed from the server's
|
|
764
|
+
* `expireTime` when available, with a local-clock fallback of
|
|
765
|
+
* `now + ttlSeconds * 1000`.
|
|
766
|
+
*/
|
|
767
|
+
async create(input) {
|
|
768
|
+
const client = await this.getClient();
|
|
769
|
+
const config = { ttl: `${input.ttlSeconds}s` };
|
|
770
|
+
if (input.contents !== void 0) config.contents = input.contents;
|
|
771
|
+
if (input.systemInstruction !== void 0)
|
|
772
|
+
config.systemInstruction = input.systemInstruction;
|
|
773
|
+
if (input.displayName !== void 0) config.displayName = input.displayName;
|
|
774
|
+
let resp;
|
|
775
|
+
try {
|
|
776
|
+
resp = await client.create({ model: input.model, config });
|
|
777
|
+
} catch (e) {
|
|
778
|
+
throw classifyError(e);
|
|
779
|
+
}
|
|
780
|
+
if (resp.name === void 0 || resp.name.length === 0) {
|
|
781
|
+
throw new LlmError("Cache create response missing required field: name", {
|
|
782
|
+
kind: "bad_request",
|
|
783
|
+
retryable: false
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
const fallbackExpiry = new Date(this.now() + input.ttlSeconds * 1e3);
|
|
787
|
+
const expiresAt = resp.expireTime !== void 0 && resp.expireTime.length > 0 ? new Date(resp.expireTime) : fallbackExpiry;
|
|
788
|
+
return {
|
|
789
|
+
cacheName: resp.name,
|
|
790
|
+
model: resp.model ?? input.model,
|
|
791
|
+
expiresAt
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Return a live cached-content handle, creating one if none exists or the
|
|
796
|
+
* cached entry has expired (accounting for skew).
|
|
797
|
+
*
|
|
798
|
+
* Reuse is PROCESS-SCOPED only — this store instance's in-memory map.
|
|
799
|
+
*
|
|
800
|
+
* When `coalesce` is enabled, concurrent calls for the same key share one
|
|
801
|
+
* in-flight create.
|
|
802
|
+
*/
|
|
803
|
+
async getOrCreate(key, factory) {
|
|
804
|
+
const mapKey = `${key.model}:${key.stableKey}`;
|
|
805
|
+
const existing = this.entries.get(mapKey);
|
|
806
|
+
if (existing !== void 0 && this.isLive(existing.handle)) {
|
|
807
|
+
return existing.handle;
|
|
808
|
+
}
|
|
809
|
+
if (this.coalesce) {
|
|
810
|
+
const inFlight = this.inflight.get(mapKey);
|
|
811
|
+
if (inFlight !== void 0) {
|
|
812
|
+
return inFlight;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
const doCreate = async () => {
|
|
816
|
+
const factoryResult = await factory();
|
|
817
|
+
const handle = await this.create({
|
|
818
|
+
model: key.model,
|
|
819
|
+
ttlSeconds: factoryResult.ttlSeconds,
|
|
820
|
+
...factoryResult.contents !== void 0 ? { contents: factoryResult.contents } : {},
|
|
821
|
+
...factoryResult.systemInstruction !== void 0 ? { systemInstruction: factoryResult.systemInstruction } : {}
|
|
822
|
+
});
|
|
823
|
+
this.entries.set(mapKey, { handle, ttlSeconds: factoryResult.ttlSeconds });
|
|
824
|
+
return handle;
|
|
825
|
+
};
|
|
826
|
+
if (this.coalesce) {
|
|
827
|
+
const promise = doCreate().finally(() => {
|
|
828
|
+
this.inflight.delete(mapKey);
|
|
829
|
+
});
|
|
830
|
+
this.inflight.set(mapKey, promise);
|
|
831
|
+
return promise;
|
|
832
|
+
}
|
|
833
|
+
return doCreate();
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Extend the cache TTL if it will expire within `thresholdSeconds`.
|
|
837
|
+
*
|
|
838
|
+
* Fail-open: if the update call throws, the original handle is returned
|
|
839
|
+
* unchanged. This method NEVER throws.
|
|
840
|
+
*
|
|
841
|
+
* @param handle - Handle to potentially refresh.
|
|
842
|
+
* @param opts.thresholdSeconds - Extend if expiry is within this many seconds.
|
|
843
|
+
* Default 300.
|
|
844
|
+
* @param opts.extensionSeconds - New TTL to set. Default: original TTL from
|
|
845
|
+
* entries map, or 3600 s when the handle was not created via `getOrCreate`.
|
|
846
|
+
*/
|
|
847
|
+
async refreshIfExpiringSoon(handle, opts) {
|
|
848
|
+
const thresholdMs = (opts?.thresholdSeconds ?? 300) * 1e3;
|
|
849
|
+
if (handle.expiresAt.getTime() - this.now() > thresholdMs) {
|
|
850
|
+
return handle;
|
|
851
|
+
}
|
|
852
|
+
let extensionSeconds = opts?.extensionSeconds;
|
|
853
|
+
if (extensionSeconds === void 0) {
|
|
854
|
+
for (const entry of this.entries.values()) {
|
|
855
|
+
if (entry.handle.cacheName === handle.cacheName) {
|
|
856
|
+
extensionSeconds = entry.ttlSeconds;
|
|
857
|
+
break;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
extensionSeconds = extensionSeconds ?? DEFAULT_EXTENSION_SECONDS;
|
|
861
|
+
}
|
|
862
|
+
try {
|
|
863
|
+
const client = await this.getClient();
|
|
864
|
+
const resp = await client.update({
|
|
865
|
+
name: handle.cacheName,
|
|
866
|
+
config: { ttl: `${extensionSeconds}s` }
|
|
867
|
+
});
|
|
868
|
+
const fallbackExpiry = new Date(this.now() + extensionSeconds * 1e3);
|
|
869
|
+
const newExpiresAt = resp.expireTime !== void 0 && resp.expireTime.length > 0 ? new Date(resp.expireTime) : fallbackExpiry;
|
|
870
|
+
const newHandle = {
|
|
871
|
+
cacheName: handle.cacheName,
|
|
872
|
+
model: handle.model,
|
|
873
|
+
expiresAt: newExpiresAt
|
|
874
|
+
};
|
|
875
|
+
for (const [k, entry] of this.entries.entries()) {
|
|
876
|
+
if (entry.handle.cacheName === handle.cacheName) {
|
|
877
|
+
this.entries.set(k, { handle: newHandle, ttlSeconds: extensionSeconds });
|
|
878
|
+
break;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
return newHandle;
|
|
882
|
+
} catch {
|
|
883
|
+
return handle;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Delete a cached content resource.
|
|
888
|
+
*
|
|
889
|
+
* Errors are forwarded to `onDeleteError` and NOT rethrown.
|
|
890
|
+
* The handle is removed from the in-process entries map regardless.
|
|
891
|
+
*/
|
|
892
|
+
async delete(handle) {
|
|
893
|
+
for (const [k, entry] of this.entries.entries()) {
|
|
894
|
+
if (entry.handle.cacheName === handle.cacheName) {
|
|
895
|
+
this.entries.delete(k);
|
|
896
|
+
break;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
try {
|
|
900
|
+
const client = await this.getClient();
|
|
901
|
+
await client.delete({ name: handle.cacheName });
|
|
902
|
+
} catch (err) {
|
|
903
|
+
this.onDeleteError(handle.cacheName, err);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
|
|
908
|
+
export { FLEX_DEFAULT_TIMEOUT_MS, GoogleCacheStore, GoogleFileStore, TRANSPORT_TIMEOUT_BUFFER_MS, buildGoogleClient, geminiAdapter, zodToGeminiSchema };
|
|
909
|
+
//# sourceMappingURL=index.js.map
|
|
910
|
+
//# sourceMappingURL=index.js.map
|