@gullabs/google 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +48 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -1
- package/dist/index.d.ts +25 -1
- package/dist/index.js +49 -9
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -51,12 +51,6 @@ function isGeminiCapacityError(err) {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
// src/adapter.ts
|
|
54
|
-
var EFFORT_BUDGET = {
|
|
55
|
-
none: 0,
|
|
56
|
-
low: 1024,
|
|
57
|
-
medium: 8192,
|
|
58
|
-
high: 24576
|
|
59
|
-
};
|
|
60
54
|
function mapFinishReason(raw) {
|
|
61
55
|
if (raw === void 0) return void 0;
|
|
62
56
|
switch (raw) {
|
|
@@ -195,7 +189,7 @@ function geminiAdapter(opts) {
|
|
|
195
189
|
);
|
|
196
190
|
}
|
|
197
191
|
if (reasoningApi === "budget") {
|
|
198
|
-
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;
|
|
199
193
|
config.thinkingConfig = {
|
|
200
194
|
...budget !== void 0 ? { thinkingBudget: budget } : {},
|
|
201
195
|
...reasoning.includeThoughts === true ? { includeThoughts: true } : {}
|
|
@@ -249,6 +243,16 @@ function geminiAdapter(opts) {
|
|
|
249
243
|
if (googleOpts !== void 0 && typeof googleOpts === "object" && googleOpts !== null) {
|
|
250
244
|
Object.assign(config, googleOpts);
|
|
251
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
|
+
}
|
|
252
256
|
if (req.modelDescriptor?.capabilities?.sampling === "fixed") {
|
|
253
257
|
const offendingSampling = [];
|
|
254
258
|
if ("temperature" in config) {
|
|
@@ -864,6 +868,42 @@ var GoogleCacheStore = class {
|
|
|
864
868
|
}
|
|
865
869
|
};
|
|
866
870
|
|
|
871
|
+
// src/grounding.ts
|
|
872
|
+
function hostnameFrom(url) {
|
|
873
|
+
return url.hostname.startsWith("www.") ? url.hostname.slice(4) : url.hostname;
|
|
874
|
+
}
|
|
875
|
+
function toCitation(chunk) {
|
|
876
|
+
if (chunk === null || typeof chunk !== "object") return void 0;
|
|
877
|
+
const web = chunk["web"];
|
|
878
|
+
if (web === null || typeof web !== "object") return void 0;
|
|
879
|
+
const uri = web["uri"];
|
|
880
|
+
if (typeof uri !== "string" || uri.length === 0) return void 0;
|
|
881
|
+
let parsed;
|
|
882
|
+
try {
|
|
883
|
+
parsed = new URL(uri);
|
|
884
|
+
} catch {
|
|
885
|
+
return void 0;
|
|
886
|
+
}
|
|
887
|
+
const title = web["title"];
|
|
888
|
+
const sourceName = typeof title === "string" && title.length > 0 ? title : hostnameFrom(parsed);
|
|
889
|
+
return { url: uri, sourceName };
|
|
890
|
+
}
|
|
891
|
+
function normalizeGroundingCitations(groundingMetadata) {
|
|
892
|
+
if (groundingMetadata === null || typeof groundingMetadata !== "object") return [];
|
|
893
|
+
const chunks = groundingMetadata["groundingChunks"];
|
|
894
|
+
if (!Array.isArray(chunks)) return [];
|
|
895
|
+
const seen = /* @__PURE__ */ new Set();
|
|
896
|
+
const citations = [];
|
|
897
|
+
for (const chunk of chunks) {
|
|
898
|
+
const citation = toCitation(chunk);
|
|
899
|
+
if (citation === void 0) continue;
|
|
900
|
+
if (seen.has(citation.url)) continue;
|
|
901
|
+
seen.add(citation.url);
|
|
902
|
+
citations.push(citation);
|
|
903
|
+
}
|
|
904
|
+
return citations;
|
|
905
|
+
}
|
|
906
|
+
|
|
867
907
|
exports.FLEX_DEFAULT_TIMEOUT_MS = FLEX_DEFAULT_TIMEOUT_MS;
|
|
868
908
|
exports.GoogleCacheStore = GoogleCacheStore;
|
|
869
909
|
exports.GoogleFileStore = GoogleFileStore;
|
|
@@ -872,5 +912,6 @@ exports.TRANSPORT_TIMEOUT_BUFFER_MS = TRANSPORT_TIMEOUT_BUFFER_MS;
|
|
|
872
912
|
exports.buildGoogleClient = buildGoogleClient;
|
|
873
913
|
exports.geminiAdapter = geminiAdapter;
|
|
874
914
|
exports.isGeminiCapacityError = isGeminiCapacityError;
|
|
915
|
+
exports.normalizeGroundingCitations = normalizeGroundingCitations;
|
|
875
916
|
//# sourceMappingURL=index.cjs.map
|
|
876
917
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/flex-fallback.ts","../src/adapter.ts","../src/file-store.ts","../src/cache-store.ts"],"names":["assertNever","LlmError","classifyError","redactSecrets"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Structural GeminiClientLike interface + buildGoogleClient factory.\n *\n * This module defines the structural interface the adapter depends on.\n * The real @google/genai SDK is imported ONLY in buildGoogleClient so tests\n * can inject a fake without pulling in the real SDK.\n *\n * @module\n */\n\nimport type { AuthMaterial } from '@gullabs/core'\n\n// ---------------------------------------------------------------------------\n// Transport timeout defaults\n// ---------------------------------------------------------------------------\n\n/**\n * Default HTTP transport timeout for Gemini Flex service-tier calls (ms).\n *\n * The default per-attempt transport (httpOptions) timeout applied ONLY when a\n * flex call provides no explicit `timeoutMs`. Set to 25 minutes (1_500_000 ms)\n * to cover the ~20-minute flex workload ceiling with margin. The @google/genai\n * SDK defaults to 1 minute, which would terminate a long flex call prematurely.\n * Callers SHOULD still set `timeoutMs` for a precise per-attempt ceiling; this\n * value is only the backstop for callers that do not.\n */\nexport const FLEX_DEFAULT_TIMEOUT_MS = 1_500_000\n\n/**\n * Default HTTP transport timeout for Gemini standard-tier calls (ms).\n *\n * Used when a standard-tier request has no explicit `timeoutMs`. The adapter\n * also backs this with an AbortController so the limit is a real client-side\n * ceiling, not only an SDK transport hint.\n */\nexport const STANDARD_DEFAULT_TIMEOUT_MS = 300_000\n\n/**\n * Buffer added above `timeoutMs` when setting the SDK transport timeout.\n *\n * When `timeoutMs` is set, the engine arms an AbortSignal at exactly\n * `timeoutMs`. If the SDK transport timer fired at the same instant it\n * could preempt the signal and produce a raw SDK error instead of the\n * engine's clean `kind:'timeout'`. By setting the transport timeout to\n * `timeoutMs + TRANSPORT_TIMEOUT_BUFFER_MS` we ensure the engine's\n * AbortSignal always fires first.\n */\nexport const TRANSPORT_TIMEOUT_BUFFER_MS = 5_000\n\n// ---------------------------------------------------------------------------\n// Response shape — mirrors the @google/genai surface we actually consume\n// ---------------------------------------------------------------------------\n\n/** A single text/thought part in a Gemini candidate content. */\nexport interface GeminiPartShape {\n text?: string\n /**\n * Present and `true` on thought-summary parts.\n * Real field name in @google/genai Candidate.content.parts: `thought`.\n */\n thought?: boolean\n}\n\n/** A candidate returned by Gemini generateContent. */\nexport interface GeminiCandidateShape {\n content?: {\n parts?: GeminiPartShape[]\n }\n /**\n * Why the model stopped.\n * Real SDK enum (FinishReason): \"STOP\", \"MAX_TOKENS\", \"SAFETY\",\n * \"RECITATION\", \"BLOCKLIST\", \"PROHIBITED_CONTENT\", etc.\n */\n finishReason?: string\n /**\n * Grounding metadata returned when Google Search grounding is active.\n * Real SDK type: GroundingMetadata. Kept as `unknown` to avoid a hard\n * coupling to @google/genai types; cast to JsonValue at the adapter boundary.\n */\n groundingMetadata?: unknown\n}\n\n/**\n * Token usage metadata returned alongside a Gemini response.\n *\n * Real type: GenerateContentResponseUsageMetadata.\n * NOTE: thoughtsTokenCount is SEPARATE from candidatesTokenCount.\n * The adapter must add them to get GROSS outputTokens.\n */\nexport interface GeminiUsageMetadataShape {\n promptTokenCount?: number\n candidatesTokenCount?: number\n cachedContentTokenCount?: number\n thoughtsTokenCount?: number\n totalTokenCount?: number\n}\n\n/**\n * Structural equivalent of @google/genai's GenerateContentResponse.\n * Only the fields the adapter reads are represented here.\n */\nexport interface GeminiResponseShape {\n candidates?: GeminiCandidateShape[]\n usageMetadata?: GeminiUsageMetadataShape\n /** Real field: GenerateContentResponse.modelVersion */\n modelVersion?: string\n /** Real field: GenerateContentResponse.responseId */\n responseId?: string\n /**\n * Safety-block metadata.\n * Real type: GenerateContentResponsePromptFeedback.\n * Present when the prompt (not output) was blocked by safety filters.\n */\n promptFeedback?: {\n /** Real type: BlockedReason (string enum). e.g. \"SAFETY\", \"OTHER\". */\n blockReason?: string\n blockReasonMessage?: string\n safetyRatings?: unknown[]\n }\n}\n\n// ---------------------------------------------------------------------------\n// Request / config shape — what the adapter sends to generateContent\n// ---------------------------------------------------------------------------\n\n/**\n * Per-part media-resolution hint emitted on inline/file parts.\n * Real type: @google/genai `PartMediaResolution`; `level` values come from\n * the `PartMediaResolutionLevel` string enum (we only emit the LOW/MEDIUM/HIGH\n * subset our normalized `mediaResolution` maps to).\n */\nexport interface GeminiPartMediaResolution {\n level?: 'MEDIA_RESOLUTION_LOW' | 'MEDIA_RESOLUTION_MEDIUM' | 'MEDIA_RESOLUTION_HIGH'\n}\n\n/** A text part in a content object we construct. */\nexport interface GeminiTextContentPart {\n text: string\n}\n\n/**\n * An inline binary media part in a content object we construct.\n * `data` must be raw base64 — no `data:…;base64,` prefix.\n */\nexport interface GeminiInlineDataContentPart {\n inlineData: {\n /** IANA media type, e.g. `\"image/png\"`. */\n mimeType: string\n /** Raw base64-encoded bytes (no data-URL prefix). */\n data: string\n }\n /** Optional per-part media-resolution hint (real field: `Part.mediaResolution`). */\n mediaResolution?: GeminiPartMediaResolution\n}\n\n/**\n * A provider-hosted file reference part in a content object we construct.\n * The Gemini service dereferences `fileUri` server-side.\n */\nexport interface GeminiFileDataContentPart {\n fileData: {\n /** IANA media type of the referenced file. */\n mimeType: string\n /** Provider-assigned file URI, e.g. from the Gemini File API. */\n fileUri: string\n }\n /** Optional per-part media-resolution hint (real field: `Part.mediaResolution`). */\n mediaResolution?: GeminiPartMediaResolution\n}\n\n/**\n * Union of all part shapes the adapter may produce for `GeminiContent.parts`.\n * Each member (including the optional per-part `mediaResolution`) is a\n * structural subset of the real `@google/genai` `Part` type for the fields we use.\n */\nexport type GeminiContentPart =\n | GeminiTextContentPart\n | GeminiInlineDataContentPart\n | GeminiFileDataContentPart\n\n/** A content object (message) we construct. */\nexport interface GeminiContent {\n role: string\n parts: GeminiContentPart[]\n}\n\n/**\n * Schema shape we pass as responseSchema.\n * Structurally compatible with @google/genai Schema.\n */\nexport interface GeminiSchema {\n type?: string\n description?: string\n properties?: Record<string, GeminiSchema>\n required?: string[]\n items?: GeminiSchema\n enum?: string[]\n nullable?: boolean\n format?: string\n}\n\n/**\n * Thinking configuration.\n * Real type: ThinkingConfig in @google/genai.\n * - thinkingBudget: 0 = DISABLED, -1 = AUTOMATIC\n * - thinkingLevel: ThinkingLevel enum (\"LOW\", \"MEDIUM\", \"HIGH\", \"MINIMAL\")\n */\nexport interface GeminiThinkingConfig {\n includeThoughts?: boolean\n thinkingBudget?: number\n /**\n * Real type: ThinkingLevel enum.\n * Values: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MINIMAL\" | \"THINKING_LEVEL_UNSPECIFIED\"\n */\n thinkingLevel?: string\n}\n\n/**\n * Config object passed in GenerateContentParameters.config.\n * Real type: GenerateContentConfig.\n * abortSignal is inside config, NOT in the top-level params.\n */\nexport interface GeminiGenerateConfig {\n systemInstruction?: { parts: GeminiContentPart[] }\n temperature?: number\n topP?: number\n topK?: number\n maxOutputTokens?: number\n stopSequences?: string[]\n responseMimeType?: string\n responseSchema?: GeminiSchema\n thinkingConfig?: GeminiThinkingConfig\n /** Real type: ServiceTier enum. Values: \"flex\" | \"standard\". */\n serviceTier?: string\n /** Real field: GenerateContentConfig.abortSignal (NOT in top-level params). */\n abortSignal?: AbortSignal\n /**\n * Per-request HTTP options forwarded to the @google/genai transport.\n * We use this to set a transport-level timeout that is >= the AbortSignal\n * deadline so the SDK fetch does not preempt the abort.\n *\n * Real field: GenerateContentConfig.httpOptions.timeout (milliseconds).\n * Real field: GenerateContentConfig.httpOptions.headers (Record<string,string>).\n * Used by FIX A-1 to inject Vertex flex routing headers.\n */\n httpOptions?: { timeout?: number; headers?: Record<string, string> }\n}\n\n/**\n * Parameters for models.generateContent.\n * Real type: GenerateContentParameters.\n */\nexport interface GeminiGenerateParams {\n model: string\n contents: GeminiContent[]\n config?: GeminiGenerateConfig\n}\n\n// ---------------------------------------------------------------------------\n// GeminiClientLike — structural interface (no @google/genai dependency)\n// ---------------------------------------------------------------------------\n\n/**\n * Structural interface for the @google/genai client surface the adapter uses.\n *\n * Satisfied by:\n * - The real GoogleGenAI client (via buildGoogleClient wrapper).\n * - FakeGeminiClient from @gullabs/testing (its generateContent accepts unknown).\n */\nexport interface GeminiClientLike {\n models: {\n generateContent(params: GeminiGenerateParams): Promise<GeminiResponseShape>\n }\n}\n\n// ---------------------------------------------------------------------------\n// buildGoogleClient — imports the real @google/genai SDK\n// ---------------------------------------------------------------------------\n\n/**\n * Build a real @google/genai client from AuthMaterial.\n *\n * Returns a GeminiClientLike wrapper around the real GoogleGenAI client.\n * Only API-key authentication is supported; Vertex AI is not.\n *\n * @param auth - API key credentials ({ apiKey }).\n */\nexport async function buildGoogleClient(auth: AuthMaterial): Promise<GeminiClientLike> {\n // Import the real SDK — only called at runtime when no client is injected.\n // The cast is safe: GeminiGenerateParams is a structural subset of\n // GenerateContentParameters; GeminiResponseShape is a subset of\n // GenerateContentResponse.\n const { GoogleGenAI } = await import('@google/genai')\n\n const ai = new GoogleGenAI({ apiKey: auth.apiKey })\n\n return {\n models: {\n async generateContent(params: GeminiGenerateParams): Promise<GeminiResponseShape> {\n // Cast needed: our structural types are subsets of the real SDK types.\n const result = await (\n ai.models.generateContent as (params: unknown) => Promise<GeminiResponseShape>\n )(params)\n return result\n },\n },\n }\n}\n","import type { LlmError } from '@gullabs/core'\n\nconst CAPACITY_PATTERNS = [\n /capacity/i,\n /overload/i,\n /overloaded/i,\n /unavailable/i,\n /no\\s+capacity/i,\n /temporar(?:y|ily)/i,\n /try\\s+again/i,\n]\n\nconst QUOTA_PATTERNS = [\n /quota/i,\n /billing/i,\n /billable/i,\n /payment/i,\n /rate\\s+limit/i,\n /exceeded/i,\n /insufficient/i,\n]\n\nexport function isGeminiCapacityError(err: LlmError): boolean {\n if (err.kind === 'server') return err.httpStatus === 503\n if (err.kind !== 'rate_limited') return false\n\n const message = err.message\n if (QUOTA_PATTERNS.some((pattern) => pattern.test(message))) {\n return false\n }\n return CAPACITY_PATTERNS.some((pattern) => pattern.test(message))\n}\n","/**\n * geminiAdapter — @gullabs/google Gemini provider adapter.\n *\n * Pure request⇄response mapping over @google/genai (via GeminiClientLike).\n * Never persists, never computes cost, never loops.\n *\n * @module\n */\n\nimport { LlmError, classifyError, assertNever } from '@gullabs/core'\nimport type {\n ProviderAdapter,\n ResolvedRequest,\n AdapterCtx,\n AdapterResult,\n Usage,\n Warning,\n FinishReason,\n JsonValue,\n AuthMaterial,\n Part,\n} from '@gullabs/core'\nimport {\n buildGoogleClient,\n FLEX_DEFAULT_TIMEOUT_MS,\n STANDARD_DEFAULT_TIMEOUT_MS,\n TRANSPORT_TIMEOUT_BUFFER_MS,\n} from './client.js'\nimport type {\n GeminiClientLike,\n GeminiGenerateConfig,\n GeminiContent,\n GeminiContentPart,\n GeminiResponseShape,\n GeminiUsageMetadataShape,\n} from './client.js'\nimport { isGeminiCapacityError } from './flex-fallback.js'\n\n// ---------------------------------------------------------------------------\n// Exported types for consumers that inject a custom client\n// ---------------------------------------------------------------------------\nexport type { GeminiClientLike }\n\n// ---------------------------------------------------------------------------\n// Reasoning effort → thinkingBudget mapping (gemini-2.5* models)\n// ---------------------------------------------------------------------------\n\nconst EFFORT_BUDGET: Record<string, number> = {\n none: 0,\n low: 1024,\n medium: 8192,\n high: 24576,\n}\n\n// ---------------------------------------------------------------------------\n// FinishReason mapping (Gemini SDK enum → our FinishReason)\n// ---------------------------------------------------------------------------\n\nfunction mapFinishReason(raw: string | undefined): FinishReason | undefined {\n if (raw === undefined) return undefined\n switch (raw) {\n case 'STOP':\n return 'stop'\n case 'MAX_TOKENS':\n return 'length'\n case 'SAFETY':\n case 'RECITATION':\n case 'BLOCKLIST':\n case 'PROHIBITED_CONTENT':\n case 'IMAGE_SAFETY':\n return 'content_filter'\n default:\n return 'other'\n }\n}\n\n// ---------------------------------------------------------------------------\n// mediaResolution mapping (normalized hint → Gemini PartMediaResolutionLevel)\n// ---------------------------------------------------------------------------\n\n/**\n * Map our normalized cross-provider `mediaResolution` hint to the Gemini\n * `PartMediaResolutionLevel` string-enum value emitted on `Part.mediaResolution`.\n */\nfunction mapMediaResolution(\n res: 'low' | 'medium' | 'high',\n): 'MEDIA_RESOLUTION_LOW' | 'MEDIA_RESOLUTION_MEDIUM' | 'MEDIA_RESOLUTION_HIGH' {\n switch (res) {\n case 'low':\n return 'MEDIA_RESOLUTION_LOW'\n case 'medium':\n return 'MEDIA_RESOLUTION_MEDIUM'\n case 'high':\n return 'MEDIA_RESOLUTION_HIGH'\n default:\n return assertNever(res)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Usage mapping — #1 correctness rule\n// ---------------------------------------------------------------------------\n\n/**\n * Map Gemini usageMetadata to our Usage type.\n *\n * **GROSS convention enforced here:**\n * - outputTokens = candidatesTokenCount + (thoughtsTokenCount ?? 0)\n * → thinkingTokens ⊆ outputTokens so cost math doesn't double-count.\n * - inputTokens = promptTokenCount (cachedContentTokenCount is already a\n * subset of promptTokenCount → cachedInputTokens = cachedContentTokenCount).\n */\nfunction mapUsage(meta: GeminiUsageMetadataShape | undefined): Usage {\n const promptTokenCount = meta?.promptTokenCount ?? 0\n const candidatesTokenCount = meta?.candidatesTokenCount ?? 0\n const cachedContentTokenCount = meta?.cachedContentTokenCount\n const thoughtsTokenCount = meta?.thoughtsTokenCount\n\n // #1 RULE: outputTokens = candidates + thoughts (GROSS; thinking ⊆ output)\n const outputTokens = candidatesTokenCount + (thoughtsTokenCount ?? 0)\n const inputTokens = promptTokenCount\n const totalTokens = meta?.totalTokenCount\n\n // Canonical details keys: input, cached, output.\n const details: Record<string, number> = {\n input: inputTokens,\n output: outputTokens,\n ...(cachedContentTokenCount !== undefined ? { cached: cachedContentTokenCount } : {}),\n ...(thoughtsTokenCount !== undefined ? { thinking: thoughtsTokenCount } : {}),\n }\n\n // Raw: the full usageMetadata object verbatim (as JsonValue).\n const raw: JsonValue =\n meta !== undefined ? (meta as unknown as { [k: string]: JsonValue }) : null\n\n const usage: Usage = {\n inputTokens,\n outputTokens,\n details,\n raw,\n ...(cachedContentTokenCount !== undefined\n ? { cachedInputTokens: cachedContentTokenCount }\n : {}),\n ...(thoughtsTokenCount !== undefined ? { thinkingTokens: thoughtsTokenCount } : {}),\n ...(totalTokens !== undefined ? { totalTokens } : {}),\n }\n\n return usage\n}\n\n// ---------------------------------------------------------------------------\n// Adapter options\n// ---------------------------------------------------------------------------\n\nexport interface GeminiAdapterOptions {\n /**\n * Inject a pre-built client (real or fake).\n * When omitted, `buildGoogleClient` is called with `ctx.auth` at call time,\n * inside the classified try/catch so any construction failure is wrapped\n * as a typed `LlmError`.\n */\n client?: GeminiClientLike\n /**\n * @internal Testing-only.\n *\n * Override the default `buildGoogleClient` factory. Allows unit tests to\n * simulate construction failures (e.g. bad credentials) without importing\n * the real `@google/genai` SDK. Never set this in production code.\n */\n _clientFactory?: (auth: AuthMaterial) => GeminiClientLike | Promise<GeminiClientLike>\n}\n\n// ---------------------------------------------------------------------------\n// geminiAdapter factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a Gemini provider adapter.\n *\n * @param opts.client - Optional pre-built client (e.g. for testing).\n */\nexport function geminiAdapter(opts?: GeminiAdapterOptions): ProviderAdapter {\n return {\n id: 'google',\n\n async run(req: ResolvedRequest, ctx: AdapterCtx): Promise<AdapterResult> {\n const warnings: Warning[] = []\n const model = req.model\n\n // ------------------------------------------------------------------\n // 1. Map messages → contents\n // ------------------------------------------------------------------\n\n /**\n * Map a single {@link Part} to its Gemini SDK equivalent.\n *\n * - `text` → `{ text }`\n * - `inline-media` → `{ inlineData: { mimeType, data } }` + optional `mediaResolution`\n * - `file-uri` → `{ fileData: { mimeType, fileUri } }` + optional `mediaResolution`\n *\n * `mediaResolution` IS supported as a per-part field by the Gemini SDK\n * (`Part.mediaResolution`). The normalised value is mapped to the\n * `PartMediaResolutionLevel` string enum before emission.\n */\n const mapPart = (p: Part): GeminiContentPart => {\n switch (p.kind) {\n case 'text':\n return { text: p.text }\n\n case 'inline-media': {\n return {\n inlineData: {\n mimeType: p.mimeType,\n data: p.data,\n },\n ...(p.mediaResolution !== undefined\n ? { mediaResolution: { level: mapMediaResolution(p.mediaResolution) } }\n : {}),\n }\n }\n\n case 'file-uri': {\n return {\n fileData: {\n mimeType: p.mimeType,\n fileUri: p.uri,\n },\n ...(p.mediaResolution !== undefined\n ? { mediaResolution: { level: mapMediaResolution(p.mediaResolution) } }\n : {}),\n }\n }\n\n default:\n return assertNever(p)\n }\n }\n\n const contents: GeminiContent[] = req.messages.map((msg) => ({\n role: msg.role === 'assistant' ? 'model' : 'user',\n parts: msg.parts.map(mapPart),\n }))\n\n // ------------------------------------------------------------------\n // 2. Build GenerateContentConfig\n // ------------------------------------------------------------------\n const genConfig = req.config\n const config: GeminiGenerateConfig = {}\n\n // System instruction\n if (req.system !== undefined) {\n config.systemInstruction = { parts: [{ text: req.system }] }\n }\n\n // Basic generation parameters (only include when defined)\n if (genConfig.temperature !== undefined) {\n config.temperature = genConfig.temperature\n }\n if (genConfig.topP !== undefined) {\n config.topP = genConfig.topP\n }\n if (genConfig.topK !== undefined) {\n config.topK = genConfig.topK\n }\n if (genConfig.maxOutputTokens !== undefined) {\n config.maxOutputTokens = genConfig.maxOutputTokens\n }\n if (genConfig.stopSequences !== undefined) {\n config.stopSequences = genConfig.stopSequences\n }\n\n // Service tier (FLEX or STANDARD)\n // Real SDK: GenerateContentConfig.serviceTier = ServiceTier enum (\"flex\"|\"standard\")\n const explicit = (genConfig as { serviceTier?: 'flex' | 'standard' }).serviceTier\n const supported = req.modelDescriptor?.capabilities?.serviceTiers\n if (explicit !== undefined) {\n // caller explicitly chose a tier — reject if the model can't honour it\n if (\n req.modelDescriptor !== undefined &&\n (supported === undefined || !supported.includes(explicit))\n ) {\n throw new LlmError(\n `serviceTier \"${explicit}\" is not supported for model \"${model}\".`,\n { kind: 'bad_request', retryable: false },\n )\n }\n config.serviceTier = explicit\n } else {\n // no explicit choice — default to flex only when the model supports it\n if (req.modelDescriptor === undefined) {\n config.serviceTier = 'flex'\n } else if (supported?.includes('flex') === true) {\n config.serviceTier = 'flex'\n }\n // otherwise omit serviceTier entirely\n }\n\n // ------------------------------------------------------------------\n // 3. Reasoning → thinkingConfig\n // ------------------------------------------------------------------\n const reasoning = genConfig.reasoning\n if (reasoning !== undefined) {\n const reasoningApi = req.modelDescriptor?.capabilities?.reasoningApi\n\n if (reasoning.effort !== undefined && reasoning.budgetTokens !== undefined) {\n throw new LlmError(\n `Provide either reasoning.effort or reasoning.budgetTokens, not both, for model \"${model}\".`,\n { kind: 'bad_request', retryable: false },\n )\n }\n\n if (reasoningApi === 'budget') {\n // gemini-2.5* → thinkingBudget\n const budget =\n reasoning.budgetTokens !== undefined\n ? reasoning.budgetTokens\n : reasoning.effort !== undefined\n ? EFFORT_BUDGET[reasoning.effort] ?? 0\n : undefined\n\n config.thinkingConfig = {\n ...(budget !== undefined ? { thinkingBudget: budget } : {}),\n ...(reasoning.includeThoughts === true ? { includeThoughts: true } : {}),\n }\n } else if (reasoningApi === 'level') {\n // gemini-3.* → thinkingLevel\n if (reasoning.budgetTokens !== undefined) {\n throw new LlmError(\n `reasoning.budgetTokens is not supported for model \"${model}\" (it uses thinkingLevel, not thinkingBudget); use reasoning.effort instead.`,\n { kind: 'bad_request', retryable: false },\n )\n }\n\n // Real SDK ThinkingLevel enum: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MINIMAL\"\n let thinkingLevel: string | undefined\n if (reasoning.effort !== undefined) {\n switch (reasoning.effort) {\n case 'none':\n thinkingLevel = 'MINIMAL'\n break\n case 'low':\n thinkingLevel = 'LOW'\n break\n case 'medium':\n thinkingLevel = 'MEDIUM'\n break\n case 'high':\n thinkingLevel = 'HIGH'\n break\n default:\n assertNever(reasoning.effort)\n }\n }\n\n config.thinkingConfig = {\n ...(thinkingLevel !== undefined ? { thinkingLevel } : {}),\n ...(reasoning.includeThoughts === true ? { includeThoughts: true } : {}),\n }\n } else {\n throw new LlmError(\n `Model \"${model}\" does not support reasoning/thinkingConfig.`,\n { kind: 'bad_request', retryable: false },\n )\n }\n }\n\n // ------------------------------------------------------------------\n // 4. Structured output → responseMimeType + responseSchema\n // ------------------------------------------------------------------\n const structuredOutputRequested = req.outputJsonSchema !== undefined\n if (structuredOutputRequested) {\n const nativeStructuredOutput =\n req.modelDescriptor?.capabilities?.nativeStructuredOutput !== false\n\n if (nativeStructuredOutput) {\n config.responseMimeType = 'application/json'\n config.responseSchema = req.outputJsonSchema as NonNullable<\n GeminiGenerateConfig['responseSchema']\n >\n }\n }\n\n // ------------------------------------------------------------------\n // 5. providerOptions.google → spread verbatim (last, caller wins)\n // ------------------------------------------------------------------\n const googleOpts = genConfig.providerOptions?.['google']\n if (\n googleOpts !== undefined &&\n typeof googleOpts === 'object' &&\n googleOpts !== null\n ) {\n Object.assign(config, googleOpts)\n }\n\n // ------------------------------------------------------------------\n // 5c. Re-assert fixed-sampling invariant AFTER providerOptions merge.\n // providerOptions.google is a last-write-wins escape hatch, but on\n // Gemini 3.x (sampling: 'fixed') the API rejects temperature/topP/topK\n // unconditionally. Throw so the caller knows they sent bad config.\n // ------------------------------------------------------------------\n if (req.modelDescriptor?.capabilities?.sampling === 'fixed') {\n const offendingSampling: string[] = []\n if ('temperature' in config) {\n offendingSampling.push('temperature')\n }\n if ('topP' in config) {\n offendingSampling.push('topP')\n }\n if ('topK' in config) {\n offendingSampling.push('topK')\n }\n if (offendingSampling.length > 0) {\n throw new LlmError(\n `Sampling parameters [${offendingSampling.join(\n ', ',\n )}] are not supported for model \"${model}\" (fixed sampling); they were supplied via providerOptions.google.`,\n { kind: 'bad_request', retryable: false },\n )\n }\n }\n\n // ------------------------------------------------------------------\n // 5b. Grounding ↔ structured-output conflict guard\n // Tools arrive via providerOptions.google; check after the merge.\n // ------------------------------------------------------------------\n const configAsAny = config as { tools?: unknown[] }\n const groundingRequested =\n Array.isArray(configAsAny.tools) &&\n configAsAny.tools.some((t): boolean => {\n if (t !== null && typeof t === 'object') {\n const tool = t as Record<string, unknown>\n return 'googleSearch' in tool || 'googleSearchRetrieval' in tool\n }\n return false\n })\n\n if (groundingRequested && structuredOutputRequested) {\n throw new LlmError(\n 'Grounding (googleSearch) cannot be combined with structured output (output.jsonSchema) on Gemini; choose one.',\n { kind: 'bad_request', retryable: false },\n )\n }\n\n // ------------------------------------------------------------------\n // 6. AbortSignal passthrough + FIX A-2: client-side flex ceiling\n //\n // FIX A-2 belt-and-suspenders: @google/genai issue #1277 — on some SDK\n // versions httpOptions.timeout is a no-op for generateContent. On the\n // flex-default path (flex tier, no explicit timeoutMs) the engine arms NO\n // AbortSignal; relying solely on httpOptions.timeout risks a silent hang.\n // We arm our own AbortController here and combine it with any incoming\n // signal so WE enforce the ceiling regardless of the SDK bug.\n //\n // Flex and standard default paths need this extra timer when timeoutMs is\n // absent. When timeoutMs IS set the engine already arms a hard AbortSignal\n // at exactly timeoutMs.\n //\n // Abort reason uses DOMException with name 'TimeoutError' so classifyError\n // maps the resulting LlmError to kind:'timeout' (retryable:true), matching\n // how the rest of the codebase surfaces timeout errors.\n //\n // AbortSignal.any requires Node ≥ 20.3; our engine floor is Node ≥ 20,\n // so this is always available in supported environments.\n //\n // Real SDK: GenerateContentConfig.abortSignal (in config, NOT in params)\n // ------------------------------------------------------------------\n let tierTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n const clearTierTimeout = (): void => {\n if (tierTimeoutHandle !== undefined) {\n clearTimeout(tierTimeoutHandle)\n tierTimeoutHandle = undefined\n }\n }\n\n const applyTierTimeout = (tier: string | undefined): void => {\n clearTierTimeout()\n delete config.abortSignal\n\n const defaultTimeoutMs =\n genConfig.timeoutMs === undefined\n ? tier === 'flex'\n ? FLEX_DEFAULT_TIMEOUT_MS\n : tier === 'standard'\n ? STANDARD_DEFAULT_TIMEOUT_MS\n : undefined\n : undefined\n\n if (defaultTimeoutMs !== undefined) {\n const tierController = new AbortController()\n const tierLabel = tier === 'standard' ? 'Standard' : 'Flex'\n const timeoutReason = new DOMException(\n `${tierLabel} timeout: call exceeded ${defaultTimeoutMs}ms client-side ceiling` +\n ' (@google/genai #1277 belt-and-suspenders)',\n 'TimeoutError',\n )\n tierTimeoutHandle = setTimeout(() => {\n tierController.abort(timeoutReason)\n }, defaultTimeoutMs)\n config.abortSignal =\n ctx.signal !== undefined\n ? AbortSignal.any([tierController.signal, ctx.signal])\n : tierController.signal\n } else if (ctx.signal !== undefined) {\n config.abortSignal = ctx.signal\n }\n }\n\n applyTierTimeout(config.serviceTier)\n\n // ------------------------------------------------------------------\n // 7. Transport timeout — set httpOptions.timeout so the @google/genai\n // HTTP transport does NOT preempt the AbortSignal hard ceiling.\n //\n // Policy (precedence, highest first):\n // 1. Caller-supplied httpOptions via providerOptions.google.httpOptions\n // WIN unconditionally; any extra fields are also preserved.\n // 2. timeoutMs is set → computed transport timeout = timeoutMs +\n // TRANSPORT_TIMEOUT_BUFFER_MS so the engine's AbortSignal (hard\n // ceiling at timeoutMs) always fires before the SDK transport timer.\n // 3. serviceTier 'flex', no timeoutMs → FLEX_DEFAULT_TIMEOUT_MS (no\n // buffer; there is no engine AbortSignal deadline in this case).\n // 4. serviceTier 'standard', no timeoutMs → STANDARD_DEFAULT_TIMEOUT_MS.\n //\n // Merge order: computed timeout first, caller's httpOptions spread on\n // top so caller values always win. Only assign config.httpOptions when\n // the merged object is non-empty (exactOptionalPropertyTypes-safe).\n // ------------------------------------------------------------------\n\n // Capture caller-supplied httpOptions BEFORE we overwrite.\n // These arrived via Object.assign(config, googleOpts) above and may\n // include a caller timeout and/or other httpOptions fields.\n const callerHttpOptions = config.httpOptions as Record<string, unknown> | undefined\n\n const computedTimeoutMs: number | undefined =\n genConfig.timeoutMs !== undefined\n ? genConfig.timeoutMs + TRANSPORT_TIMEOUT_BUFFER_MS\n : config.serviceTier === 'flex'\n ? FLEX_DEFAULT_TIMEOUT_MS\n : config.serviceTier === 'standard'\n ? STANDARD_DEFAULT_TIMEOUT_MS\n : undefined\n\n // Merge: our computed timeout is the base; caller wins on top.\n const mergedHttpOptions: Record<string, unknown> = {\n ...(computedTimeoutMs !== undefined ? { timeout: computedTimeoutMs } : {}),\n ...callerHttpOptions,\n }\n\n if (Object.keys(mergedHttpOptions).length > 0) {\n config.httpOptions = mergedHttpOptions\n }\n\n // ------------------------------------------------------------------\n // 8b. Client construction + SDK call — both inside the classifier\n // so that ANY failure in run() (including a bad auth constructor)\n // is rethrown as a typed LlmError(provider:'google').\n // ------------------------------------------------------------------\n let response: GeminiResponseShape\n let servedServiceTier = config.serviceTier\n const dispatch = async (): Promise<GeminiResponseShape> => {\n const dispatchConfig: GeminiGenerateConfig = {\n ...config,\n ...(config.httpOptions !== undefined\n ? { httpOptions: { ...config.httpOptions } }\n : {}),\n }\n const params = {\n model,\n contents,\n config: dispatchConfig,\n }\n const buildClient = opts?._clientFactory ?? buildGoogleClient\n const client: GeminiClientLike =\n opts?.client !== undefined ? opts.client : await buildClient(ctx.auth)\n ctx.logger.debug(\n {\n model,\n configKeys: Object.keys(dispatchConfig),\n serviceTier: dispatchConfig.serviceTier,\n },\n 'llm.adapter.dispatch',\n )\n return client.models.generateContent(params)\n }\n try {\n response = await dispatch()\n } catch (rawErr) {\n // Classify SDK errors → LlmError\n const classified = classifyError(rawErr)\n const typed = new LlmError(classified.message, {\n kind: classified.kind,\n retryable: classified.retryable,\n ...(classified.httpStatus !== undefined\n ? { httpStatus: classified.httpStatus }\n : {}),\n ...(classified.retryAfterMs !== undefined\n ? { retryAfterMs: classified.retryAfterMs }\n : {}),\n provider: 'google',\n cause: classified.cause ?? rawErr,\n ...(servedServiceTier !== undefined ? { servedServiceTier } : {}),\n })\n\n if (\n config.serviceTier === 'flex' &&\n genConfig.flexFallback !== false &&\n isGeminiCapacityError(typed)\n ) {\n config.serviceTier = 'standard'\n servedServiceTier = 'standard'\n const fallbackTimeout =\n genConfig.timeoutMs !== undefined\n ? genConfig.timeoutMs + TRANSPORT_TIMEOUT_BUFFER_MS\n : STANDARD_DEFAULT_TIMEOUT_MS\n config.httpOptions = {\n timeout: fallbackTimeout,\n ...callerHttpOptions,\n }\n applyTierTimeout('standard')\n try {\n response = await dispatch()\n } catch (fallbackRawErr) {\n const fallbackClassified = classifyError(fallbackRawErr)\n throw new LlmError(fallbackClassified.message, {\n kind: fallbackClassified.kind,\n retryable: fallbackClassified.retryable,\n ...(fallbackClassified.httpStatus !== undefined\n ? { httpStatus: fallbackClassified.httpStatus }\n : {}),\n ...(fallbackClassified.retryAfterMs !== undefined\n ? { retryAfterMs: fallbackClassified.retryAfterMs }\n : {}),\n provider: 'google',\n cause: fallbackClassified.cause ?? fallbackRawErr,\n servedServiceTier: 'standard',\n })\n }\n } else {\n throw typed\n }\n } finally {\n // FIX A-2: always clear the tier timeout timer so it never leaks,\n // regardless of whether the call succeeded, threw, or was aborted.\n clearTierTimeout()\n }\n\n // ------------------------------------------------------------------\n // 8. Blocked response check\n // promptFeedback.blockReason set OR no candidates → content_filter\n // ------------------------------------------------------------------\n const hasBlockReason = response.promptFeedback?.blockReason !== undefined\n const hasCandidates =\n response.candidates !== undefined && response.candidates.length > 0\n\n if (hasBlockReason || !hasCandidates) {\n const reason = response.promptFeedback?.blockReason ?? 'NO_CANDIDATES'\n throw new LlmError(`Gemini response blocked: ${reason}`, {\n kind: 'content_filter',\n retryable: false,\n provider: 'google',\n })\n }\n\n // ------------------------------------------------------------------\n // 9. Map response\n // ------------------------------------------------------------------\n const candidates = response.candidates\n if (candidates === undefined || candidates.length === 0) {\n throw new LlmError('Gemini response blocked: NO_CANDIDATES', {\n kind: 'content_filter',\n retryable: false,\n provider: 'google',\n })\n }\n const candidate = candidates[0]\n if (candidate === undefined) {\n throw new LlmError('Gemini response blocked: NO_CANDIDATES', {\n kind: 'content_filter',\n retryable: false,\n provider: 'google',\n })\n }\n const parts = candidate.content?.parts ?? []\n\n // Separate thought parts from text parts.\n const textParts: string[] = []\n const thoughtParts: string[] = []\n\n for (const part of parts) {\n if (part.text !== undefined) {\n if (part.thought === true) {\n thoughtParts.push(part.text)\n } else {\n textParts.push(part.text)\n }\n }\n }\n\n const text = textParts.join('')\n const reasoningText = thoughtParts.length > 0 ? thoughtParts.join('') : undefined\n\n // Parse structured output (JSON text → rawStructured).\n let rawStructured: unknown\n if (structuredOutputRequested && text.length > 0) {\n try {\n rawStructured = JSON.parse(text)\n } catch {\n // Core reports outputParsed:false; callers own validation/retry policy.\n }\n }\n\n // ------------------------------------------------------------------\n // 10. Build AdapterResult\n // ------------------------------------------------------------------\n const usage = mapUsage(response.usageMetadata)\n const finishReason = mapFinishReason(candidate.finishReason)\n\n const result: AdapterResult = {\n model,\n usage,\n warnings,\n ...(servedServiceTier !== undefined ? { servedServiceTier } : {}),\n ...(text.length > 0 ? { text } : {}),\n ...(reasoningText !== undefined ? { reasoningText } : {}),\n ...(rawStructured !== undefined ? { rawStructured } : {}),\n ...(finishReason !== undefined ? { finishReason } : {}),\n ...(response.modelVersion !== undefined\n ? { modelVersion: response.modelVersion }\n : {}),\n ...(response.responseId !== undefined ? { responseId: response.responseId } : {}),\n // Build providerMetadata — merge promptFeedback + groundingMetadata when present.\n ...((): { providerMetadata: JsonValue } | Record<string, never> => {\n const pf = response.promptFeedback\n const gm = candidate.groundingMetadata\n if (pf === undefined && gm === undefined) return {}\n const meta: { [k: string]: JsonValue } = {}\n if (pf !== undefined) {\n meta['promptFeedback'] = pf as unknown as JsonValue\n }\n if (gm !== undefined) {\n meta['groundingMetadata'] = gm as unknown as JsonValue\n }\n return { providerMetadata: meta as JsonValue }\n })(),\n }\n\n return result\n },\n }\n}\n","/**\n * GoogleFileStore — thin wrapper over the Gemini File API.\n *\n * Handles upload + polling until ACTIVE, and tracked deletion.\n * Injectable sleep and client for tests (no network required).\n *\n * @module\n */\n\nimport type { AuthMaterial, Logger } from '@gullabs/core'\nimport { LlmError, classifyError, redactSecrets } from '@gullabs/core'\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** A handle to a file stored in the Gemini File API. */\nexport interface GoogleFileHandle {\n /** Resource name, e.g. \"files/abc123\". */\n name: string\n /** URI to pass as FileUriPart.uri in an LlmRequest. */\n uri: string\n mimeType: string\n /** Provider auto-deletes ~48 h after upload. Absent when not returned. */\n expiresAt?: Date\n}\n\n/**\n * Minimal structural interface for the Gemini Files client surface we use.\n * Satisfied by the real ai.files object or a test fake.\n */\nexport interface GeminiFilesClientLike {\n upload(params: {\n file: Uint8Array | Blob\n config?: { mimeType?: string; displayName?: string }\n }): Promise<{\n name?: string\n uri?: string\n mimeType?: string\n state?: string\n expirationTime?: string\n }>\n get(params: { name: string }): Promise<{\n name?: string\n uri?: string\n mimeType?: string\n state?: string\n expirationTime?: string\n }>\n delete(params: { name: string }): Promise<void>\n}\n\nexport interface GoogleFileStoreOptions {\n auth: AuthMaterial\n /** Injectable client for tests; skips SDK import when provided. */\n client?: GeminiFilesClientLike\n /** Called on delete failures instead of rethrowing. Default: console.error. */\n onDeleteError?: (name: string, err: unknown) => void\n /** Optional structured logger. When provided, routes delete failures to logger.error. */\n logger?: Logger\n poll?: {\n /** Delay between state polls. Default: 3000 ms. */\n intervalMs?: number\n /** Max time to wait for ACTIVE. Default: 300 000 ms (5 min). */\n timeoutMs?: number\n }\n /** Injectable sleep for tests. Default: real setTimeout. */\n sleep?: (ms: number) => Promise<void>\n /** Injectable clock for deterministic tests. Default: `Date.now`. */\n now?: () => number\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_INTERVAL_MS = 3_000\nconst DEFAULT_TIMEOUT_MS = 300_000\n\nconst realSleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms))\n\nasync function buildFilesClient(auth: AuthMaterial): Promise<GeminiFilesClientLike> {\n const { GoogleGenAI } = await import('@google/genai')\n\n const ai = new GoogleGenAI({ apiKey: auth.apiKey })\n\n return {\n async upload(params) {\n // Real SDK accepts string | Blob; convert Uint8Array → Blob.\n const fileArg: Blob =\n params.file instanceof Uint8Array\n ? new Blob(\n [params.file],\n params.config?.mimeType !== undefined && params.config.mimeType.length > 0\n ? { type: params.config.mimeType }\n : {},\n )\n : params.file\n\n const result = await (\n ai.files.upload as (p: unknown) => Promise<{\n name?: string\n uri?: string\n mimeType?: string\n state?: string\n expirationTime?: string\n }>\n )({\n file: fileArg,\n ...(params.config !== undefined ? { config: params.config } : {}),\n })\n return result\n },\n\n async get(params) {\n const result = await (\n ai.files.get as (p: unknown) => Promise<{\n name?: string\n uri?: string\n mimeType?: string\n state?: string\n expirationTime?: string\n }>\n )(params)\n return result\n },\n\n async delete(params) {\n await (ai.files.delete as (p: unknown) => Promise<unknown>)(params)\n },\n }\n}\n\ntype FileResp = {\n name?: string\n uri?: string\n mimeType?: string\n state?: string\n expirationTime?: string\n}\n\nfunction makeHandle(\n resp: FileResp,\n fallback: { name: string; uri: string; mimeType: string },\n): GoogleFileHandle {\n const et = resp.expirationTime\n return {\n name: resp.name ?? fallback.name,\n uri: resp.uri ?? fallback.uri,\n mimeType: resp.mimeType ?? fallback.mimeType,\n ...(et !== undefined && et.length > 0 ? { expiresAt: new Date(et) } : {}),\n }\n}\n\n// ---------------------------------------------------------------------------\n// GoogleFileStore\n// ---------------------------------------------------------------------------\n\n/**\n * **Auth snapshot note:** this store captures the `AuthMaterial` at construction\n * time and memoizes a single SDK client from it (`clientPromise`). This is\n * correct and sufficient for static API keys. If refreshable credentials\n * (short-lived OAuth/STS tokens) are added in the future, this memoization is\n * the seam that will need rework: the cached client would hold stale credentials\n * for the lifetime of a long-lived store instance. At that point, the store\n * will need to either rebuild the client on each operation or accept a\n * credential-resolver callback rather than a plain `AuthMaterial` value.\n * See ADR-020 in DECISIONS.md.\n */\nexport class GoogleFileStore {\n private readonly auth: AuthMaterial\n private readonly clientOverride: GeminiFilesClientLike | undefined\n private readonly onDeleteError: (name: string, err: unknown) => void\n private readonly logger: Logger | undefined\n private readonly intervalMs: number\n private readonly timeoutMs: number\n private readonly sleep: (ms: number) => Promise<void>\n private readonly now: () => number\n /** Memoised client promise — built at most once per store instance. */\n private clientPromise: Promise<GeminiFilesClientLike> | undefined\n\n constructor(opts: GoogleFileStoreOptions) {\n this.auth = opts.auth\n this.clientOverride = opts.client\n this.logger = opts.logger\n this.onDeleteError =\n opts.onDeleteError ??\n ((name, err) => {\n if (this.logger !== undefined) {\n this.logger.error(\n { name, error: redactSecrets(classifyError(err).message) },\n 'gemini.file.delete.failed',\n )\n } else {\n console.error(\n `[GoogleFileStore] delete failed for \"${name}\":`,\n redactSecrets(classifyError(err).message),\n )\n }\n })\n this.intervalMs = opts.poll?.intervalMs ?? DEFAULT_INTERVAL_MS\n this.timeoutMs = opts.poll?.timeoutMs ?? DEFAULT_TIMEOUT_MS\n this.sleep = opts.sleep ?? realSleep\n this.now = opts.now ?? (() => Date.now())\n }\n\n private getClient(): Promise<GeminiFilesClientLike> {\n if (this.clientOverride !== undefined) return Promise.resolve(this.clientOverride)\n if (this.clientPromise === undefined) {\n this.clientPromise = buildFilesClient(this.auth)\n }\n return this.clientPromise\n }\n\n /**\n * Upload bytes to the Gemini File API and wait until the file is ACTIVE.\n *\n * @param source - Raw bytes or Blob.\n * @param mimeType - IANA media type, e.g. `\"image/png\"`.\n * @param opts - Optional display name.\n */\n async upload(\n source: Uint8Array | Blob,\n mimeType: string,\n opts?: { displayName?: string; signal?: AbortSignal },\n ): Promise<GoogleFileHandle> {\n const signal = opts?.signal\n const client = await this.getClient()\n\n let uploadResp: {\n name?: string\n uri?: string\n mimeType?: string\n state?: string\n expirationTime?: string\n }\n try {\n uploadResp = await client.upload({\n file: source,\n config: {\n mimeType,\n ...(opts?.displayName !== undefined ? { displayName: opts.displayName } : {}),\n },\n })\n } catch (e) {\n throw classifyError(e)\n }\n\n const { name, uri } = uploadResp\n\n if (\n name === undefined ||\n name.length === 0 ||\n uri === undefined ||\n uri.length === 0\n ) {\n throw new LlmError('File upload response missing required fields (name or uri)', {\n kind: 'bad_request',\n retryable: false,\n })\n }\n\n const fallback = { name, uri, mimeType }\n\n if (uploadResp.state === 'ACTIVE') {\n return makeHandle(uploadResp, fallback)\n }\n\n if (uploadResp.state === 'FAILED') {\n throw new LlmError('File processing failed immediately after upload', {\n kind: 'bad_request',\n retryable: false,\n })\n }\n\n // PROCESSING (or unknown) — poll until ACTIVE or timeout.\n const deadline = this.now() + this.timeoutMs\n\n // Pre-flight: if the signal is already aborted, throw before creating any\n // promise so we never produce an unhandled rejection.\n if (signal?.aborted === true) {\n throw new LlmError('File upload polling aborted', {\n kind: 'aborted',\n retryable: false,\n })\n }\n\n // Build an abort-race promise so future aborts wake up the sleep race\n // immediately rather than waiting the full interval. Created only when\n // the signal is NOT already aborted (guard above handles that case).\n const abortRacePromise: Promise<never> | undefined =\n signal !== undefined\n ? new Promise<never>((_, reject) => {\n signal.addEventListener(\n 'abort',\n () => {\n reject(\n new LlmError('File upload polling aborted', {\n kind: 'aborted',\n retryable: false,\n }),\n )\n },\n { once: true },\n )\n })\n : undefined\n\n for (;;) {\n if (this.now() >= deadline) {\n throw new LlmError('Timed out waiting for uploaded file to become ACTIVE', {\n kind: 'timeout',\n retryable: true,\n })\n }\n\n // Sleep — race against the abort promise so we wake up immediately\n // when the signal fires rather than waiting the full interval.\n const sleepCall = this.sleep(this.intervalMs)\n await (abortRacePromise !== undefined\n ? Promise.race([sleepCall, abortRacePromise])\n : sleepCall)\n\n let pollResp: {\n name?: string\n uri?: string\n mimeType?: string\n state?: string\n expirationTime?: string\n }\n try {\n pollResp = await client.get({ name })\n } catch (e) {\n throw classifyError(e)\n }\n\n // Also guard here: the signal may have fired during client.get()\n // before we looped back to the sleep race.\n // Cast needed: TS 5.6 persists readonly-property narrowing across awaits,\n // making it think `aborted` is still `false | undefined` after the preflight.\n if ((signal?.aborted as boolean | undefined) === true) {\n throw new LlmError('File upload polling aborted', {\n kind: 'aborted',\n retryable: false,\n })\n }\n\n if (pollResp.state === 'ACTIVE') {\n return makeHandle(pollResp, fallback)\n }\n\n if (pollResp.state === 'FAILED') {\n throw new LlmError('File processing failed during polling', {\n kind: 'bad_request',\n retryable: false,\n })\n }\n // PROCESSING — continue loop\n }\n }\n\n /**\n * Delete a single uploaded file.\n * Errors are forwarded to `onDeleteError` and NOT rethrown.\n */\n async delete(handle: GoogleFileHandle): Promise<void> {\n try {\n const client = await this.getClient()\n await client.delete({ name: handle.name })\n } catch (err) {\n this.onDeleteError(handle.name, err)\n }\n }\n\n /**\n * Delete multiple files.\n * Each failure is individually forwarded to `onDeleteError`; none are thrown.\n */\n async deleteAll(handles: GoogleFileHandle[]): Promise<void> {\n await Promise.allSettled(handles.map((h) => this.delete(h)))\n }\n}\n","/**\n * GoogleCacheStore — thin wrapper over the Gemini Context Cache API.\n *\n * Manages create / get-or-create / refresh / delete lifecycle for cached\n * contents. In-memory cache entries are PROCESS-SCOPED; they are not shared\n * across processes, workers, or restarts.\n *\n * Injectable client and `now` function keep tests free of network and clock.\n *\n * @module\n */\n\nimport type { AuthMaterial, Logger } from '@gullabs/core'\nimport { LlmError, classifyError, redactSecrets } from '@gullabs/core'\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * A handle to a cached content resource in the Gemini Context Cache API.\n *\n * Pass `cacheName` as `providerOptions.google.cachedContent` in LlmRequest.\n */\nexport interface GoogleCacheHandle {\n /** Resource name, e.g. \"cachedContents/xyz\". */\n cacheName: string\n /**\n * Local view of when the cache expires (server-authoritative; computed from\n * ttl or parsed from the server response `expireTime`).\n */\n expiresAt: Date\n /** Caches are model-bound; never use a handle with a different model. */\n model: string\n}\n\n/** Key used to look up or create an entry in the in-process cache map. */\nexport interface CacheKey {\n model: string\n /** Caller-chosen stable identifier, e.g. a hash of the cached content. */\n stableKey: string\n}\n\n/**\n * Minimal structural interface for the Gemini Caches client surface we use.\n * Satisfied by the real `ai.caches` object or a test fake.\n */\nexport interface GeminiCachesClientLike {\n create(params: {\n model: string\n config: {\n contents?: unknown\n systemInstruction?: unknown\n ttl?: string\n displayName?: string\n }\n }): Promise<{ name?: string; model?: string; expireTime?: string }>\n\n update(params: {\n name: string\n config: { ttl?: string; expireTime?: string }\n }): Promise<{ name?: string; expireTime?: string }>\n\n delete(params: { name: string }): Promise<unknown>\n}\n\nexport interface GoogleCacheStoreOptions {\n auth: AuthMaterial\n /** Injectable client for tests; skips SDK import when provided. */\n client?: GeminiCachesClientLike\n /**\n * When true, concurrent `getOrCreate` calls for the same key share one\n * in-flight create. Default false.\n */\n coalesce?: boolean\n /**\n * Subtracted from the local expiry so we stop using a cache slightly before\n * the server evicts it. Default 30 s.\n */\n expirySkewSeconds?: number\n /** Called on delete failures instead of rethrowing. */\n onDeleteError?: (cacheName: string, err: unknown) => void\n /** Optional structured logger. When provided, routes delete failures to logger.error. */\n logger?: Logger\n /** Injectable clock for deterministic tests. Default: `Date.now`. */\n now?: () => number\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_SKEW_SECONDS = 30\nconst DEFAULT_EXTENSION_SECONDS = 3600\n\nasync function buildCachesClient(auth: AuthMaterial): Promise<GeminiCachesClientLike> {\n const { GoogleGenAI } = await import('@google/genai')\n\n const ai = new GoogleGenAI({ apiKey: auth.apiKey })\n\n return {\n async create(params) {\n const result = await (\n ai.caches.create as (p: unknown) => Promise<{\n name?: string\n model?: string\n expireTime?: string\n }>\n )(params)\n return result\n },\n\n async update(params) {\n const result = await (\n ai.caches.update as (p: unknown) => Promise<{\n name?: string\n expireTime?: string\n }>\n )(params)\n return result\n },\n\n async delete(params) {\n await (ai.caches.delete as (p: unknown) => Promise<unknown>)(params)\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// GoogleCacheStore\n// ---------------------------------------------------------------------------\n\ninterface EntryRecord {\n handle: GoogleCacheHandle\n /** Original ttlSeconds used to create this entry; used as extension default. */\n ttlSeconds: number\n}\n\n/**\n * Process-scoped helper for the Gemini Context Cache API.\n *\n * NOTE: `getOrCreate` reuse is PROCESS-SCOPED only. Entries survive only for\n * the lifetime of this `GoogleCacheStore` instance. Across restarts, new\n * caches will be created (and old ones will be server-evicted after their TTL).\n *\n * **Auth snapshot note:** this store captures the `AuthMaterial` at construction\n * time and memoizes a single SDK client from it (`clientPromise`). This is\n * correct and sufficient for static API keys. If refreshable credentials\n * (short-lived OAuth/STS tokens) are added in the future, this memoization is\n * the seam that will need rework: the cached client would hold stale credentials\n * for the lifetime of a long-lived store instance. At that point, the store\n * will need to either rebuild the client on each operation or accept a\n * credential-resolver callback rather than a plain `AuthMaterial` value.\n * See ADR-020 in DECISIONS.md.\n */\nexport class GoogleCacheStore {\n private readonly auth: AuthMaterial\n private readonly clientOverride: GeminiCachesClientLike | undefined\n private readonly coalesce: boolean\n private readonly skewMs: number\n private readonly onDeleteError: (cacheName: string, err: unknown) => void\n private readonly logger: Logger | undefined\n private readonly now: () => number\n\n /** Memoised client promise — built at most once per store instance. */\n private clientPromise: Promise<GeminiCachesClientLike> | undefined\n\n /** In-process cache of live handles, keyed by `${model}:${stableKey}`. */\n private readonly entries: Map<string, EntryRecord> = new Map()\n\n /** In-flight create promises when coalescing is enabled. */\n private readonly inflight: Map<string, Promise<GoogleCacheHandle>> = new Map()\n\n constructor(opts: GoogleCacheStoreOptions) {\n this.auth = opts.auth\n this.clientOverride = opts.client\n this.coalesce = opts.coalesce ?? false\n this.skewMs = (opts.expirySkewSeconds ?? DEFAULT_SKEW_SECONDS) * 1000\n this.logger = opts.logger\n this.onDeleteError =\n opts.onDeleteError ??\n ((cacheName, err) => {\n if (this.logger !== undefined) {\n this.logger.error(\n { name: cacheName, error: redactSecrets(classifyError(err).message) },\n 'gemini.cache.delete.failed',\n )\n } else {\n console.error(\n `[GoogleCacheStore] delete failed for \"${cacheName}\":`,\n redactSecrets(classifyError(err).message),\n )\n }\n })\n this.now = opts.now ?? (() => Date.now())\n }\n\n private getClient(): Promise<GeminiCachesClientLike> {\n if (this.clientOverride !== undefined) return Promise.resolve(this.clientOverride)\n if (this.clientPromise === undefined) {\n this.clientPromise = buildCachesClient(this.auth)\n }\n return this.clientPromise\n }\n\n private isLive(handle: GoogleCacheHandle): boolean {\n return handle.expiresAt.getTime() - this.skewMs > this.now()\n }\n\n /**\n * Create a new cached content resource.\n *\n * The `expiresAt` on the returned handle is computed from the server's\n * `expireTime` when available, with a local-clock fallback of\n * `now + ttlSeconds * 1000`.\n */\n async create(input: {\n model: string\n ttlSeconds: number\n contents?: unknown\n systemInstruction?: unknown\n displayName?: string\n }): Promise<GoogleCacheHandle> {\n const client = await this.getClient()\n\n const config: {\n ttl: string\n contents?: unknown\n systemInstruction?: unknown\n displayName?: string\n } = { ttl: `${input.ttlSeconds}s` }\n\n if (input.contents !== undefined) config.contents = input.contents\n if (input.systemInstruction !== undefined)\n config.systemInstruction = input.systemInstruction\n if (input.displayName !== undefined) config.displayName = input.displayName\n\n let resp: { name?: string; model?: string; expireTime?: string }\n try {\n resp = await client.create({ model: input.model, config })\n } catch (e) {\n throw classifyError(e)\n }\n\n if (resp.name === undefined || resp.name.length === 0) {\n throw new LlmError('Cache create response missing required field: name', {\n kind: 'bad_request',\n retryable: false,\n })\n }\n\n const fallbackExpiry = new Date(this.now() + input.ttlSeconds * 1000)\n const expiresAt =\n resp.expireTime !== undefined && resp.expireTime.length > 0\n ? new Date(resp.expireTime)\n : fallbackExpiry\n\n return {\n cacheName: resp.name,\n model: resp.model ?? input.model,\n expiresAt,\n }\n }\n\n /**\n * Return a live cached-content handle, creating one if none exists or the\n * cached entry has expired (accounting for skew).\n *\n * Reuse is PROCESS-SCOPED only — this store instance's in-memory map.\n *\n * When `coalesce` is enabled, concurrent calls for the same key share one\n * in-flight create.\n */\n async getOrCreate(\n key: CacheKey,\n factory: () => Promise<{\n ttlSeconds: number\n contents?: unknown\n systemInstruction?: unknown\n }>,\n ): Promise<GoogleCacheHandle> {\n const mapKey = `${key.model}:${key.stableKey}`\n\n // Return existing live entry if available.\n const existing = this.entries.get(mapKey)\n if (existing !== undefined && this.isLive(existing.handle)) {\n return existing.handle\n }\n\n // If coalescing, piggyback on an in-flight create for this key.\n if (this.coalesce) {\n const inFlight = this.inflight.get(mapKey)\n if (inFlight !== undefined) {\n return inFlight\n }\n }\n\n const doCreate = async (): Promise<GoogleCacheHandle> => {\n const factoryResult = await factory()\n const handle = await this.create({\n model: key.model,\n ttlSeconds: factoryResult.ttlSeconds,\n ...(factoryResult.contents !== undefined\n ? { contents: factoryResult.contents }\n : {}),\n ...(factoryResult.systemInstruction !== undefined\n ? { systemInstruction: factoryResult.systemInstruction }\n : {}),\n })\n this.entries.set(mapKey, { handle, ttlSeconds: factoryResult.ttlSeconds })\n return handle\n }\n\n if (this.coalesce) {\n const promise = doCreate().finally(() => {\n this.inflight.delete(mapKey)\n })\n this.inflight.set(mapKey, promise)\n return promise\n }\n\n return doCreate()\n }\n\n /**\n * Extend the cache TTL if it will expire within `thresholdSeconds`.\n *\n * Fail-open: if the update call throws, the original handle is returned\n * unchanged. This method NEVER throws.\n *\n * @param handle - Handle to potentially refresh.\n * @param opts.thresholdSeconds - Extend if expiry is within this many seconds.\n * Default 300.\n * @param opts.extensionSeconds - New TTL to set. Default: original TTL from\n * entries map, or 3600 s when the handle was not created via `getOrCreate`.\n */\n async refreshIfExpiringSoon(\n handle: GoogleCacheHandle,\n opts?: { thresholdSeconds?: number; extensionSeconds?: number },\n ): Promise<GoogleCacheHandle> {\n const thresholdMs = (opts?.thresholdSeconds ?? 300) * 1000\n\n if (handle.expiresAt.getTime() - this.now() > thresholdMs) {\n // Not near expiry — return unchanged.\n return handle\n }\n\n // Find the original ttl from the entries map for this handle.\n let extensionSeconds = opts?.extensionSeconds\n if (extensionSeconds === undefined) {\n for (const entry of this.entries.values()) {\n if (entry.handle.cacheName === handle.cacheName) {\n extensionSeconds = entry.ttlSeconds\n break\n }\n }\n extensionSeconds = extensionSeconds ?? DEFAULT_EXTENSION_SECONDS\n }\n\n try {\n const client = await this.getClient()\n const resp = await client.update({\n name: handle.cacheName,\n config: { ttl: `${extensionSeconds}s` },\n })\n\n const fallbackExpiry = new Date(this.now() + extensionSeconds * 1000)\n const newExpiresAt =\n resp.expireTime !== undefined && resp.expireTime.length > 0\n ? new Date(resp.expireTime)\n : fallbackExpiry\n\n const newHandle: GoogleCacheHandle = {\n cacheName: handle.cacheName,\n model: handle.model,\n expiresAt: newExpiresAt,\n }\n\n // Update the entries map entry if this handle is tracked.\n for (const [k, entry] of this.entries.entries()) {\n if (entry.handle.cacheName === handle.cacheName) {\n this.entries.set(k, { handle: newHandle, ttlSeconds: extensionSeconds })\n break\n }\n }\n\n return newHandle\n } catch {\n // Fail-open: return original handle unchanged.\n return handle\n }\n }\n\n /**\n * Delete a cached content resource.\n *\n * Errors are forwarded to `onDeleteError` and NOT rethrown.\n * The handle is removed from the in-process entries map regardless.\n */\n async delete(handle: GoogleCacheHandle): Promise<void> {\n // Always remove from local map, even if delete fails.\n for (const [k, entry] of this.entries.entries()) {\n if (entry.handle.cacheName === handle.cacheName) {\n this.entries.delete(k)\n break\n }\n }\n\n try {\n const client = await this.getClient()\n await client.delete({ name: handle.cacheName })\n } catch (err) {\n this.onDeleteError(handle.cacheName, err)\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/flex-fallback.ts","../src/adapter.ts","../src/file-store.ts","../src/cache-store.ts","../src/grounding.ts"],"names":["assertNever","LlmError","EFFORT_BUDGET","classifyError","redactSecrets"],"mappings":";;;;;;;AA0BO,IAAM,uBAAA,GAA0B;AAShC,IAAM,2BAAA,GAA8B;AAYpC,IAAM,2BAAA,GAA8B;AAgP3C,eAAsB,kBAAkB,IAAA,EAA+C;AAKrF,EAAA,MAAM,EAAE,WAAA,EAAY,GAAI,MAAM,OAAO,eAAe,CAAA;AAEpD,EAAA,MAAM,KAAK,IAAI,WAAA,CAAY,EAAE,MAAA,EAAQ,IAAA,CAAK,QAAQ,CAAA;AAElD,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ;AAAA,MACN,MAAM,gBAAgB,MAAA,EAA4D;AAEhF,QAAA,MAAM,MAAA,GAAS,MACb,EAAA,CAAG,MAAA,CAAO,gBACV,MAAM,CAAA;AACR,QAAA,OAAO,MAAA;AAAA,MACT;AAAA;AACF,GACF;AACF;;;ACjTA,IAAM,iBAAA,GAAoB;AAAA,EACxB,WAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA;AAAA,EACA,cAAA;AAAA,EACA,gBAAA;AAAA,EACA,oBAAA;AAAA,EACA;AACF,CAAA;AAEA,IAAM,cAAA,GAAiB;AAAA,EACrB,QAAA;AAAA,EACA,UAAA;AAAA,EACA,WAAA;AAAA,EACA,UAAA;AAAA,EACA,eAAA;AAAA,EACA,WAAA;AAAA,EACA;AACF,CAAA;AAEO,SAAS,sBAAsB,GAAA,EAAwB;AAC5D,EAAA,IAAI,GAAA,CAAI,IAAA,KAAS,QAAA,EAAU,OAAO,IAAI,UAAA,KAAe,GAAA;AACrD,EAAA,IAAI,GAAA,CAAI,IAAA,KAAS,cAAA,EAAgB,OAAO,KAAA;AAExC,EAAA,MAAM,UAAU,GAAA,CAAI,OAAA;AACpB,EAAA,IAAI,cAAA,CAAe,KAAK,CAAC,OAAA,KAAY,QAAQ,IAAA,CAAK,OAAO,CAAC,CAAA,EAAG;AAC3D,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,OAAO,kBAAkB,IAAA,CAAK,CAAC,YAAY,OAAA,CAAQ,IAAA,CAAK,OAAO,CAAC,CAAA;AAClE;;;ACgBA,SAAS,gBAAgB,GAAA,EAAmD;AAC1E,EAAA,IAAI,GAAA,KAAQ,QAAW,OAAO,MAAA;AAC9B,EAAA,QAAQ,GAAA;AAAK,IACX,KAAK,MAAA;AACH,MAAA,OAAO,MAAA;AAAA,IACT,KAAK,YAAA;AACH,MAAA,OAAO,QAAA;AAAA,IACT,KAAK,QAAA;AAAA,IACL,KAAK,YAAA;AAAA,IACL,KAAK,WAAA;AAAA,IACL,KAAK,oBAAA;AAAA,IACL,KAAK,cAAA;AACH,MAAA,OAAO,gBAAA;AAAA,IACT;AACE,MAAA,OAAO,OAAA;AAAA;AAEb;AAUA,SAAS,mBACP,GAAA,EAC8E;AAC9E,EAAA,QAAQ,GAAA;AAAK,IACX,KAAK,KAAA;AACH,MAAA,OAAO,sBAAA;AAAA,IACT,KAAK,QAAA;AACH,MAAA,OAAO,yBAAA;AAAA,IACT,KAAK,MAAA;AACH,MAAA,OAAO,uBAAA;AAAA,IACT;AACE,MAAA,OAAOA,iBAAY,GAAG,CAAA;AAAA;AAE5B;AAeA,SAAS,SAAS,IAAA,EAAmD;AACnE,EAAA,MAAM,gBAAA,GAAmB,MAAM,gBAAA,IAAoB,CAAA;AACnD,EAAA,MAAM,oBAAA,GAAuB,MAAM,oBAAA,IAAwB,CAAA;AAC3D,EAAA,MAAM,0BAA0B,IAAA,EAAM,uBAAA;AACtC,EAAA,MAAM,qBAAqB,IAAA,EAAM,kBAAA;AAGjC,EAAA,MAAM,YAAA,GAAe,wBAAwB,kBAAA,IAAsB,CAAA,CAAA;AACnE,EAAA,MAAM,WAAA,GAAc,gBAAA;AACpB,EAAA,MAAM,cAAc,IAAA,EAAM,eAAA;AAG1B,EAAA,MAAM,OAAA,GAAkC;AAAA,IACtC,KAAA,EAAO,WAAA;AAAA,IACP,MAAA,EAAQ,YAAA;AAAA,IACR,GAAI,uBAAA,KAA4B,MAAA,GAAY,EAAE,MAAA,EAAQ,uBAAA,KAA4B,EAAC;AAAA,IACnF,GAAI,kBAAA,KAAuB,MAAA,GAAY,EAAE,QAAA,EAAU,kBAAA,KAAuB;AAAC,GAC7E;AAGA,EAAA,MAAM,GAAA,GACJ,IAAA,KAAS,MAAA,GAAa,IAAA,GAAiD,IAAA;AAEzE,EAAA,MAAM,KAAA,GAAe;AAAA,IACnB,WAAA;AAAA,IACA,YAAA;AAAA,IACA,OAAA;AAAA,IACA,GAAA;AAAA,IACA,GAAI,uBAAA,KAA4B,MAAA,GAC5B,EAAE,iBAAA,EAAmB,uBAAA,KACrB,EAAC;AAAA,IACL,GAAI,kBAAA,KAAuB,MAAA,GAAY,EAAE,cAAA,EAAgB,kBAAA,KAAuB,EAAC;AAAA,IACjF,GAAI,WAAA,KAAgB,MAAA,GAAY,EAAE,WAAA,KAAgB;AAAC,GACrD;AAEA,EAAA,OAAO,KAAA;AACT;AAiCO,SAAS,cAAc,IAAA,EAA8C;AAC1E,EAAA,OAAO;AAAA,IACL,EAAA,EAAI,QAAA;AAAA,IAEJ,MAAM,GAAA,CAAI,GAAA,EAAsB,GAAA,EAAyC;AACvE,MAAA,MAAM,WAAsB,EAAC;AAC7B,MAAA,MAAM,QAAQ,GAAA,CAAI,KAAA;AAiBlB,MAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KAA+B;AAC9C,QAAA,QAAQ,EAAE,IAAA;AAAM,UACd,KAAK,MAAA;AACH,YAAA,OAAO,EAAE,IAAA,EAAM,CAAA,CAAE,IAAA,EAAK;AAAA,UAExB,KAAK,cAAA,EAAgB;AACnB,YAAA,OAAO;AAAA,cACL,UAAA,EAAY;AAAA,gBACV,UAAU,CAAA,CAAE,QAAA;AAAA,gBACZ,MAAM,CAAA,CAAE;AAAA,eACV;AAAA,cACA,GAAI,CAAA,CAAE,eAAA,KAAoB,MAAA,GACtB,EAAE,eAAA,EAAiB,EAAE,KAAA,EAAO,kBAAA,CAAmB,CAAA,CAAE,eAAe,CAAA,EAAE,KAClE;AAAC,aACP;AAAA,UACF;AAAA,UAEA,KAAK,UAAA,EAAY;AACf,YAAA,OAAO;AAAA,cACL,QAAA,EAAU;AAAA,gBACR,UAAU,CAAA,CAAE,QAAA;AAAA,gBACZ,SAAS,CAAA,CAAE;AAAA,eACb;AAAA,cACA,GAAI,CAAA,CAAE,eAAA,KAAoB,MAAA,GACtB,EAAE,eAAA,EAAiB,EAAE,KAAA,EAAO,kBAAA,CAAmB,CAAA,CAAE,eAAe,CAAA,EAAE,KAClE;AAAC,aACP;AAAA,UACF;AAAA,UAEA;AACE,YAAA,OAAOA,iBAAY,CAAC,CAAA;AAAA;AACxB,MACF,CAAA;AAEA,MAAA,MAAM,QAAA,GAA4B,GAAA,CAAI,QAAA,CAAS,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,QAC3D,IAAA,EAAM,GAAA,CAAI,IAAA,KAAS,WAAA,GAAc,OAAA,GAAU,MAAA;AAAA,QAC3C,KAAA,EAAO,GAAA,CAAI,KAAA,CAAM,GAAA,CAAI,OAAO;AAAA,OAC9B,CAAE,CAAA;AAKF,MAAA,MAAM,YAAY,GAAA,CAAI,MAAA;AACtB,MAAA,MAAM,SAA+B,EAAC;AAGtC,MAAA,IAAI,GAAA,CAAI,WAAW,MAAA,EAAW;AAC5B,QAAA,MAAA,CAAO,iBAAA,GAAoB,EAAE,KAAA,EAAO,CAAC,EAAE,IAAA,EAAM,GAAA,CAAI,MAAA,EAAQ,CAAA,EAAE;AAAA,MAC7D;AAGA,MAAA,IAAI,SAAA,CAAU,gBAAgB,MAAA,EAAW;AACvC,QAAA,MAAA,CAAO,cAAc,SAAA,CAAU,WAAA;AAAA,MACjC;AACA,MAAA,IAAI,SAAA,CAAU,SAAS,MAAA,EAAW;AAChC,QAAA,MAAA,CAAO,OAAO,SAAA,CAAU,IAAA;AAAA,MAC1B;AACA,MAAA,IAAI,SAAA,CAAU,SAAS,MAAA,EAAW;AAChC,QAAA,MAAA,CAAO,OAAO,SAAA,CAAU,IAAA;AAAA,MAC1B;AACA,MAAA,IAAI,SAAA,CAAU,oBAAoB,MAAA,EAAW;AAC3C,QAAA,MAAA,CAAO,kBAAkB,SAAA,CAAU,eAAA;AAAA,MACrC;AACA,MAAA,IAAI,SAAA,CAAU,kBAAkB,MAAA,EAAW;AACzC,QAAA,MAAA,CAAO,gBAAgB,SAAA,CAAU,aAAA;AAAA,MACnC;AAIA,MAAA,MAAM,WAAY,SAAA,CAAoD,WAAA;AACtE,MAAA,MAAM,SAAA,GAAY,GAAA,CAAI,eAAA,EAAiB,YAAA,EAAc,YAAA;AACrD,MAAA,IAAI,aAAa,MAAA,EAAW;AAE1B,QAAA,IACE,GAAA,CAAI,oBAAoB,MAAA,KACvB,SAAA,KAAc,UAAa,CAAC,SAAA,CAAU,QAAA,CAAS,QAAQ,CAAA,CAAA,EACxD;AACA,UAAA,MAAM,IAAIC,aAAA;AAAA,YACR,CAAA,aAAA,EAAgB,QAAQ,CAAA,8BAAA,EAAiC,KAAK,CAAA,EAAA,CAAA;AAAA,YAC9D,EAAE,IAAA,EAAM,aAAA,EAAe,SAAA,EAAW,KAAA;AAAM,WAC1C;AAAA,QACF;AACA,QAAA,MAAA,CAAO,WAAA,GAAc,QAAA;AAAA,MACvB,CAAA,MAAO;AAEL,QAAA,IAAI,GAAA,CAAI,oBAAoB,MAAA,EAAW;AACrC,UAAA,MAAA,CAAO,WAAA,GAAc,MAAA;AAAA,QACvB,CAAA,MAAA,IAAW,SAAA,EAAW,QAAA,CAAS,MAAM,MAAM,IAAA,EAAM;AAC/C,UAAA,MAAA,CAAO,WAAA,GAAc,MAAA;AAAA,QACvB;AAAA,MAEF;AAKA,MAAA,MAAM,YAAY,SAAA,CAAU,SAAA;AAC5B,MAAA,IAAI,cAAc,MAAA,EAAW;AAC3B,QAAA,MAAM,YAAA,GAAe,GAAA,CAAI,eAAA,EAAiB,YAAA,EAAc,YAAA;AAExD,QAAA,IAAI,SAAA,CAAU,MAAA,KAAW,MAAA,IAAa,SAAA,CAAU,iBAAiB,MAAA,EAAW;AAC1E,UAAA,MAAM,IAAIA,aAAA;AAAA,YACR,mFAAmF,KAAK,CAAA,EAAA,CAAA;AAAA,YACxF,EAAE,IAAA,EAAM,aAAA,EAAe,SAAA,EAAW,KAAA;AAAM,WAC1C;AAAA,QACF;AAEA,QAAA,IAAI,iBAAiB,QAAA,EAAU;AAE7B,UAAA,MAAM,MAAA,GACJ,SAAA,CAAU,YAAA,KAAiB,MAAA,GACvB,SAAA,CAAU,YAAA,GACV,SAAA,CAAU,MAAA,KAAW,MAAA,GACrBC,kBAAA,CAAc,SAAA,CAAU,MAAM,CAAA,GAC9B,MAAA;AAEN,UAAA,MAAA,CAAO,cAAA,GAAiB;AAAA,YACtB,GAAI,MAAA,KAAW,MAAA,GAAY,EAAE,cAAA,EAAgB,MAAA,KAAW,EAAC;AAAA,YACzD,GAAI,UAAU,eAAA,KAAoB,IAAA,GAAO,EAAE,eAAA,EAAiB,IAAA,KAAS;AAAC,WACxE;AAAA,QACF,CAAA,MAAA,IAAW,iBAAiB,OAAA,EAAS;AAEnC,UAAA,IAAI,SAAA,CAAU,iBAAiB,MAAA,EAAW;AACxC,YAAA,MAAM,IAAID,aAAA;AAAA,cACR,sDAAsD,KAAK,CAAA,4EAAA,CAAA;AAAA,cAC3D,EAAE,IAAA,EAAM,aAAA,EAAe,SAAA,EAAW,KAAA;AAAM,aAC1C;AAAA,UACF;AAGA,UAAA,IAAI,aAAA;AACJ,UAAA,IAAI,SAAA,CAAU,WAAW,MAAA,EAAW;AAClC,YAAA,QAAQ,UAAU,MAAA;AAAQ,cACxB,KAAK,MAAA;AACH,gBAAA,aAAA,GAAgB,SAAA;AAChB,gBAAA;AAAA,cACF,KAAK,KAAA;AACH,gBAAA,aAAA,GAAgB,KAAA;AAChB,gBAAA;AAAA,cACF,KAAK,QAAA;AACH,gBAAA,aAAA,GAAgB,QAAA;AAChB,gBAAA;AAAA,cACF,KAAK,MAAA;AACH,gBAAA,aAAA,GAAgB,MAAA;AAChB,gBAAA;AAAA,cACF;AACE,gBAAAD,gBAAA,CAAY,UAAU,MAAM,CAAA;AAAA;AAChC,UACF;AAEA,UAAA,MAAA,CAAO,cAAA,GAAiB;AAAA,YACtB,GAAI,aAAA,KAAkB,MAAA,GAAY,EAAE,aAAA,KAAkB,EAAC;AAAA,YACvD,GAAI,UAAU,eAAA,KAAoB,IAAA,GAAO,EAAE,eAAA,EAAiB,IAAA,KAAS;AAAC,WACxE;AAAA,QACF,CAAA,MAAO;AACL,UAAA,MAAM,IAAIC,aAAA;AAAA,YACR,UAAU,KAAK,CAAA,4CAAA,CAAA;AAAA,YACf,EAAE,IAAA,EAAM,aAAA,EAAe,SAAA,EAAW,KAAA;AAAM,WAC1C;AAAA,QACF;AAAA,MACF;AAKA,MAAA,MAAM,yBAAA,GAA4B,IAAI,gBAAA,KAAqB,MAAA;AAC3D,MAAA,IAAI,yBAAA,EAA2B;AAC7B,QAAA,MAAM,sBAAA,GACJ,GAAA,CAAI,eAAA,EAAiB,YAAA,EAAc,sBAAA,KAA2B,KAAA;AAEhE,QAAA,IAAI,sBAAA,EAAwB;AAC1B,UAAA,MAAA,CAAO,gBAAA,GAAmB,kBAAA;AAC1B,UAAA,MAAA,CAAO,iBAAiB,GAAA,CAAI,gBAAA;AAAA,QAG9B;AAAA,MACF;AAKA,MAAA,MAAM,UAAA,GAAa,SAAA,CAAU,eAAA,GAAkB,QAAQ,CAAA;AACvD,MAAA,IACE,eAAe,MAAA,IACf,OAAO,UAAA,KAAe,QAAA,IACtB,eAAe,IAAA,EACf;AACA,QAAA,MAAA,CAAO,MAAA,CAAO,QAAQ,UAAU,CAAA;AAAA,MAClC;AAQA,MAAA,MAAM,oBAAqB,MAAA,CAAoC,WAAA;AAC/D,MAAA,IAAI,iBAAA,KAAsB,MAAA,IAAa,GAAA,CAAI,eAAA,KAAoB,MAAA,EAAW;AACxE,QAAA,MAAM,mBAAA,GAAsB,GAAA,CAAI,eAAA,CAAgB,YAAA,EAAc,YAAA;AAC9D,QAAA,IACE,wBAAwB,MAAA,IACxB,CAAC,mBAAA,CAAoB,QAAA,CAAS,iBAAwC,CAAA,EACtE;AACA,UAAA,MAAM,IAAIA,aAAA;AAAA,YACR,CAAA,aAAA,EAAgB,iBAAiB,CAAA,8BAAA,EAAiC,KAAK,CAAA,EAAA,CAAA;AAAA,YACvE,EAAE,IAAA,EAAM,aAAA,EAAe,SAAA,EAAW,KAAA;AAAM,WAC1C;AAAA,QACF;AAAA,MACF;AAQA,MAAA,IAAI,GAAA,CAAI,eAAA,EAAiB,YAAA,EAAc,QAAA,KAAa,OAAA,EAAS;AAC3D,QAAA,MAAM,oBAA8B,EAAC;AACrC,QAAA,IAAI,iBAAiB,MAAA,EAAQ;AAC3B,UAAA,iBAAA,CAAkB,KAAK,aAAa,CAAA;AAAA,QACtC;AACA,QAAA,IAAI,UAAU,MAAA,EAAQ;AACpB,UAAA,iBAAA,CAAkB,KAAK,MAAM,CAAA;AAAA,QAC/B;AACA,QAAA,IAAI,UAAU,MAAA,EAAQ;AACpB,UAAA,iBAAA,CAAkB,KAAK,MAAM,CAAA;AAAA,QAC/B;AACA,QAAA,IAAI,iBAAA,CAAkB,SAAS,CAAA,EAAG;AAChC,UAAA,MAAM,IAAIA,aAAA;AAAA,YACR,wBAAwB,iBAAA,CAAkB,IAAA;AAAA,cACxC;AAAA,aACD,kCAAkC,KAAK,CAAA,kEAAA,CAAA;AAAA,YACxC,EAAE,IAAA,EAAM,aAAA,EAAe,SAAA,EAAW,KAAA;AAAM,WAC1C;AAAA,QACF;AAAA,MACF;AAMA,MAAA,MAAM,WAAA,GAAc,MAAA;AACpB,MAAA,MAAM,kBAAA,GACJ,KAAA,CAAM,OAAA,CAAQ,WAAA,CAAY,KAAK,KAC/B,WAAA,CAAY,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAe;AACrC,QAAA,IAAI,CAAA,KAAM,IAAA,IAAQ,OAAO,CAAA,KAAM,QAAA,EAAU;AACvC,UAAA,MAAM,IAAA,GAAO,CAAA;AACb,UAAA,OAAO,cAAA,IAAkB,QAAQ,uBAAA,IAA2B,IAAA;AAAA,QAC9D;AACA,QAAA,OAAO,KAAA;AAAA,MACT,CAAC,CAAA;AAEH,MAAA,IAAI,sBAAsB,yBAAA,EAA2B;AACnD,QAAA,MAAM,IAAIA,aAAA;AAAA,UACR,+GAAA;AAAA,UACA,EAAE,IAAA,EAAM,aAAA,EAAe,SAAA,EAAW,KAAA;AAAM,SAC1C;AAAA,MACF;AAyBA,MAAA,IAAI,iBAAA;AAEJ,MAAA,MAAM,mBAAmB,MAAY;AACnC,QAAA,IAAI,sBAAsB,MAAA,EAAW;AACnC,UAAA,YAAA,CAAa,iBAAiB,CAAA;AAC9B,UAAA,iBAAA,GAAoB,MAAA;AAAA,QACtB;AAAA,MACF,CAAA;AAEA,MAAA,MAAM,gBAAA,GAAmB,CAAC,IAAA,KAAmC;AAC3D,QAAA,gBAAA,EAAiB;AACjB,QAAA,OAAO,MAAA,CAAO,WAAA;AAEd,QAAA,MAAM,gBAAA,GACJ,SAAA,CAAU,SAAA,KAAc,MAAA,GACpB,IAAA,KAAS,SACP,uBAAA,GACA,IAAA,KAAS,UAAA,GACT,2BAAA,GACA,MAAA,GACF,MAAA;AAEN,QAAA,IAAI,qBAAqB,MAAA,EAAW;AAClC,UAAA,MAAM,cAAA,GAAiB,IAAI,eAAA,EAAgB;AAC3C,UAAA,MAAM,SAAA,GAAY,IAAA,KAAS,UAAA,GAAa,UAAA,GAAa,MAAA;AACrD,UAAA,MAAM,gBAAgB,IAAI,YAAA;AAAA,YACxB,CAAA,EAAG,SAAS,CAAA,wBAAA,EAA2B,gBAAgB,CAAA,gEAAA,CAAA;AAAA,YAEvD;AAAA,WACF;AACA,UAAA,iBAAA,GAAoB,WAAW,MAAM;AACnC,YAAA,cAAA,CAAe,MAAM,aAAa,CAAA;AAAA,UACpC,GAAG,gBAAgB,CAAA;AACnB,UAAA,MAAA,CAAO,WAAA,GACL,GAAA,CAAI,MAAA,KAAW,MAAA,GACX,WAAA,CAAY,GAAA,CAAI,CAAC,cAAA,CAAe,MAAA,EAAQ,GAAA,CAAI,MAAM,CAAC,IACnD,cAAA,CAAe,MAAA;AAAA,QACvB,CAAA,MAAA,IAAW,GAAA,CAAI,MAAA,KAAW,MAAA,EAAW;AACnC,UAAA,MAAA,CAAO,cAAc,GAAA,CAAI,MAAA;AAAA,QAC3B;AAAA,MACF,CAAA;AAEA,MAAA,gBAAA,CAAiB,OAAO,WAAW,CAAA;AAwBnC,MAAA,MAAM,oBAAoB,MAAA,CAAO,WAAA;AAEjC,MAAA,MAAM,iBAAA,GACJ,SAAA,CAAU,SAAA,KAAc,MAAA,GACpB,UAAU,SAAA,GAAY,2BAAA,GACtB,MAAA,CAAO,WAAA,KAAgB,MAAA,GACvB,uBAAA,GACA,MAAA,CAAO,WAAA,KAAgB,aACvB,2BAAA,GACA,MAAA;AAGN,MAAA,MAAM,iBAAA,GAA6C;AAAA,QACjD,GAAI,iBAAA,KAAsB,MAAA,GAAY,EAAE,OAAA,EAAS,iBAAA,KAAsB,EAAC;AAAA,QACxE,GAAG;AAAA,OACL;AAEA,MAAA,IAAI,MAAA,CAAO,IAAA,CAAK,iBAAiB,CAAA,CAAE,SAAS,CAAA,EAAG;AAC7C,QAAA,MAAA,CAAO,WAAA,GAAc,iBAAA;AAAA,MACvB;AAOA,MAAA,IAAI,QAAA;AACJ,MAAA,IAAI,oBAAoB,MAAA,CAAO,WAAA;AAC/B,MAAA,MAAM,WAAW,YAA0C;AACzD,QAAA,MAAM,cAAA,GAAuC;AAAA,UAC3C,GAAG,MAAA;AAAA,UACH,GAAI,MAAA,CAAO,WAAA,KAAgB,MAAA,GACvB,EAAE,WAAA,EAAa,EAAE,GAAG,MAAA,CAAO,WAAA,EAAY,EAAE,GACzC;AAAC,SACP;AACA,QAAA,MAAM,MAAA,GAAS;AAAA,UACb,KAAA;AAAA,UACA,QAAA;AAAA,UACA,MAAA,EAAQ;AAAA,SACV;AACA,QAAA,MAAM,WAAA,GAAc,MAAM,cAAA,IAAkB,iBAAA;AAC5C,QAAA,MAAM,MAAA,GACJ,MAAM,MAAA,KAAW,MAAA,GAAY,KAAK,MAAA,GAAS,MAAM,WAAA,CAAY,GAAA,CAAI,IAAI,CAAA;AACvE,QAAA,GAAA,CAAI,MAAA,CAAO,KAAA;AAAA,UACT;AAAA,YACE,KAAA;AAAA,YACA,UAAA,EAAY,MAAA,CAAO,IAAA,CAAK,cAAc,CAAA;AAAA,YACtC,aAAa,cAAA,CAAe;AAAA,WAC9B;AAAA,UACA;AAAA,SACF;AACA,QAAA,OAAO,MAAA,CAAO,MAAA,CAAO,eAAA,CAAgB,MAAM,CAAA;AAAA,MAC7C,CAAA;AACA,MAAA,IAAI;AACF,QAAA,QAAA,GAAW,MAAM,QAAA,EAAS;AAAA,MAC5B,SAAS,MAAA,EAAQ;AAEf,QAAA,MAAM,UAAA,GAAaE,mBAAc,MAAM,CAAA;AACvC,QAAA,MAAM,KAAA,GAAQ,IAAIF,aAAA,CAAS,UAAA,CAAW,OAAA,EAAS;AAAA,UAC7C,MAAM,UAAA,CAAW,IAAA;AAAA,UACjB,WAAW,UAAA,CAAW,SAAA;AAAA,UACtB,GAAI,WAAW,UAAA,KAAe,MAAA,GAC1B,EAAE,UAAA,EAAY,UAAA,CAAW,UAAA,EAAW,GACpC,EAAC;AAAA,UACL,GAAI,WAAW,YAAA,KAAiB,MAAA,GAC5B,EAAE,YAAA,EAAc,UAAA,CAAW,YAAA,EAAa,GACxC,EAAC;AAAA,UACL,QAAA,EAAU,QAAA;AAAA,UACV,KAAA,EAAO,WAAW,KAAA,IAAS,MAAA;AAAA,UAC3B,GAAI,iBAAA,KAAsB,MAAA,GAAY,EAAE,iBAAA,KAAsB;AAAC,SAChE,CAAA;AAED,QAAA,IACE,MAAA,CAAO,gBAAgB,MAAA,IACvB,SAAA,CAAU,iBAAiB,KAAA,IAC3B,qBAAA,CAAsB,KAAK,CAAA,EAC3B;AACA,UAAA,MAAA,CAAO,WAAA,GAAc,UAAA;AACrB,UAAA,iBAAA,GAAoB,UAAA;AACpB,UAAA,MAAM,kBACJ,SAAA,CAAU,SAAA,KAAc,MAAA,GACpB,SAAA,CAAU,YAAY,2BAAA,GACtB,2BAAA;AACN,UAAA,MAAA,CAAO,WAAA,GAAc;AAAA,YACnB,OAAA,EAAS,eAAA;AAAA,YACT,GAAG;AAAA,WACL;AACA,UAAA,gBAAA,CAAiB,UAAU,CAAA;AAC3B,UAAA,IAAI;AACF,YAAA,QAAA,GAAW,MAAM,QAAA,EAAS;AAAA,UAC5B,SAAS,cAAA,EAAgB;AACvB,YAAA,MAAM,kBAAA,GAAqBE,mBAAc,cAAc,CAAA;AACvD,YAAA,MAAM,IAAIF,aAAA,CAAS,kBAAA,CAAmB,OAAA,EAAS;AAAA,cAC7C,MAAM,kBAAA,CAAmB,IAAA;AAAA,cACzB,WAAW,kBAAA,CAAmB,SAAA;AAAA,cAC9B,GAAI,mBAAmB,UAAA,KAAe,MAAA,GAClC,EAAE,UAAA,EAAY,kBAAA,CAAmB,UAAA,EAAW,GAC5C,EAAC;AAAA,cACL,GAAI,mBAAmB,YAAA,KAAiB,MAAA,GACpC,EAAE,YAAA,EAAc,kBAAA,CAAmB,YAAA,EAAa,GAChD,EAAC;AAAA,cACL,QAAA,EAAU,QAAA;AAAA,cACV,KAAA,EAAO,mBAAmB,KAAA,IAAS,cAAA;AAAA,cACnC,iBAAA,EAAmB;AAAA,aACpB,CAAA;AAAA,UACH;AAAA,QACF,CAAA,MAAO;AACL,UAAA,MAAM,KAAA;AAAA,QACR;AAAA,MACF,CAAA,SAAE;AAGA,QAAA,gBAAA,EAAiB;AAAA,MACnB;AAMA,MAAA,MAAM,cAAA,GAAiB,QAAA,CAAS,cAAA,EAAgB,WAAA,KAAgB,MAAA;AAChE,MAAA,MAAM,gBACJ,QAAA,CAAS,UAAA,KAAe,MAAA,IAAa,QAAA,CAAS,WAAW,MAAA,GAAS,CAAA;AAEpE,MAAA,IAAI,cAAA,IAAkB,CAAC,aAAA,EAAe;AACpC,QAAA,MAAM,MAAA,GAAS,QAAA,CAAS,cAAA,EAAgB,WAAA,IAAe,eAAA;AACvD,QAAA,MAAM,IAAIA,aAAA,CAAS,CAAA,yBAAA,EAA4B,MAAM,CAAA,CAAA,EAAI;AAAA,UACvD,IAAA,EAAM,gBAAA;AAAA,UACN,SAAA,EAAW,KAAA;AAAA,UACX,QAAA,EAAU;AAAA,SACX,CAAA;AAAA,MACH;AAKA,MAAA,MAAM,aAAa,QAAA,CAAS,UAAA;AAC5B,MAAA,IAAI,UAAA,KAAe,MAAA,IAAa,UAAA,CAAW,MAAA,KAAW,CAAA,EAAG;AACvD,QAAA,MAAM,IAAIA,cAAS,wCAAA,EAA0C;AAAA,UAC3D,IAAA,EAAM,gBAAA;AAAA,UACN,SAAA,EAAW,KAAA;AAAA,UACX,QAAA,EAAU;AAAA,SACX,CAAA;AAAA,MACH;AACA,MAAA,MAAM,SAAA,GAAY,WAAW,CAAC,CAAA;AAC9B,MAAA,IAAI,cAAc,MAAA,EAAW;AAC3B,QAAA,MAAM,IAAIA,cAAS,wCAAA,EAA0C;AAAA,UAC3D,IAAA,EAAM,gBAAA;AAAA,UACN,SAAA,EAAW,KAAA;AAAA,UACX,QAAA,EAAU;AAAA,SACX,CAAA;AAAA,MACH;AACA,MAAA,MAAM,KAAA,GAAQ,SAAA,CAAU,OAAA,EAAS,KAAA,IAAS,EAAC;AAG3C,MAAA,MAAM,YAAsB,EAAC;AAC7B,MAAA,MAAM,eAAyB,EAAC;AAEhC,MAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,QAAA,IAAI,IAAA,CAAK,SAAS,MAAA,EAAW;AAC3B,UAAA,IAAI,IAAA,CAAK,YAAY,IAAA,EAAM;AACzB,YAAA,YAAA,CAAa,IAAA,CAAK,KAAK,IAAI,CAAA;AAAA,UAC7B,CAAA,MAAO;AACL,YAAA,SAAA,CAAU,IAAA,CAAK,KAAK,IAAI,CAAA;AAAA,UAC1B;AAAA,QACF;AAAA,MACF;AAEA,MAAA,MAAM,IAAA,GAAO,SAAA,CAAU,IAAA,CAAK,EAAE,CAAA;AAC9B,MAAA,MAAM,gBAAgB,YAAA,CAAa,MAAA,GAAS,IAAI,YAAA,CAAa,IAAA,CAAK,EAAE,CAAA,GAAI,MAAA;AAGxE,MAAA,IAAI,aAAA;AACJ,MAAA,IAAI,yBAAA,IAA6B,IAAA,CAAK,MAAA,GAAS,CAAA,EAAG;AAChD,QAAA,IAAI;AACF,UAAA,aAAA,GAAgB,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,QACjC,CAAA,CAAA,MAAQ;AAAA,QAER;AAAA,MACF;AAKA,MAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,QAAA,CAAS,aAAa,CAAA;AAC7C,MAAA,MAAM,YAAA,GAAe,eAAA,CAAgB,SAAA,CAAU,YAAY,CAAA;AAE3D,MAAA,MAAM,MAAA,GAAwB;AAAA,QAC5B,KAAA;AAAA,QACA,KAAA;AAAA,QACA,QAAA;AAAA,QACA,GAAI,iBAAA,KAAsB,MAAA,GAAY,EAAE,iBAAA,KAAsB,EAAC;AAAA,QAC/D,GAAI,IAAA,CAAK,MAAA,GAAS,IAAI,EAAE,IAAA,KAAS,EAAC;AAAA,QAClC,GAAI,aAAA,KAAkB,MAAA,GAAY,EAAE,aAAA,KAAkB,EAAC;AAAA,QACvD,GAAI,aAAA,KAAkB,MAAA,GAAY,EAAE,aAAA,KAAkB,EAAC;AAAA,QACvD,GAAI,YAAA,KAAiB,MAAA,GAAY,EAAE,YAAA,KAAiB,EAAC;AAAA,QACrD,GAAI,SAAS,YAAA,KAAiB,MAAA,GAC1B,EAAE,YAAA,EAAc,QAAA,CAAS,YAAA,EAAa,GACtC,EAAC;AAAA,QACL,GAAI,SAAS,UAAA,KAAe,MAAA,GAAY,EAAE,UAAA,EAAY,QAAA,CAAS,UAAA,EAAW,GAAI,EAAC;AAAA;AAAA,QAE/E,IAAI,MAA+D;AACjE,UAAA,MAAM,KAAK,QAAA,CAAS,cAAA;AACpB,UAAA,MAAM,KAAK,SAAA,CAAU,iBAAA;AACrB,UAAA,IAAI,EAAA,KAAO,MAAA,IAAa,EAAA,KAAO,MAAA,SAAkB,EAAC;AAClD,UAAA,MAAM,OAAmC,EAAC;AAC1C,UAAA,IAAI,OAAO,MAAA,EAAW;AACpB,YAAA,IAAA,CAAK,gBAAgB,CAAA,GAAI,EAAA;AAAA,UAC3B;AACA,UAAA,IAAI,OAAO,MAAA,EAAW;AACpB,YAAA,IAAA,CAAK,mBAAmB,CAAA,GAAI,EAAA;AAAA,UAC9B;AACA,UAAA,OAAO,EAAE,kBAAkB,IAAA,EAAkB;AAAA,QAC/C,CAAA;AAAG,OACL;AAEA,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,GACF;AACF;AC3qBA,IAAM,mBAAA,GAAsB,GAAA;AAC5B,IAAM,kBAAA,GAAqB,GAAA;AAE3B,IAAM,SAAA,GAAY,CAAC,EAAA,KACjB,IAAI,OAAA,CAAQ,CAAC,OAAA,KAAY,UAAA,CAAW,OAAA,EAAS,EAAE,CAAC,CAAA;AAElD,eAAe,iBAAiB,IAAA,EAAoD;AAClF,EAAA,MAAM,EAAE,WAAA,EAAY,GAAI,MAAM,OAAO,eAAe,CAAA;AAEpD,EAAA,MAAM,KAAK,IAAI,WAAA,CAAY,EAAE,MAAA,EAAQ,IAAA,CAAK,QAAQ,CAAA;AAElD,EAAA,OAAO;AAAA,IACL,MAAM,OAAO,MAAA,EAAQ;AAEnB,MAAA,MAAM,OAAA,GACJ,MAAA,CAAO,IAAA,YAAgB,UAAA,GACnB,IAAI,IAAA;AAAA,QACF,CAAC,OAAO,IAAI,CAAA;AAAA,QACZ,MAAA,CAAO,MAAA,EAAQ,QAAA,KAAa,MAAA,IAAa,OAAO,MAAA,CAAO,QAAA,CAAS,MAAA,GAAS,CAAA,GACrE,EAAE,IAAA,EAAM,MAAA,CAAO,MAAA,CAAO,QAAA,KACtB;AAAC,UAEP,MAAA,CAAO,IAAA;AAEb,MAAA,MAAM,MAAA,GAAS,MACb,EAAA,CAAG,KAAA,CAAM,MAAA,CAOT;AAAA,QACA,IAAA,EAAM,OAAA;AAAA,QACN,GAAI,OAAO,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,MAAA,CAAO,MAAA,EAAO,GAAI;AAAC,OAChE,CAAA;AACD,MAAA,OAAO,MAAA;AAAA,IACT,CAAA;AAAA,IAEA,MAAM,IAAI,MAAA,EAAQ;AAChB,MAAA,MAAM,MAAA,GAAS,MACb,EAAA,CAAG,KAAA,CAAM,IAOT,MAAM,CAAA;AACR,MAAA,OAAO,MAAA;AAAA,IACT,CAAA;AAAA,IAEA,MAAM,OAAO,MAAA,EAAQ;AACnB,MAAA,MAAO,EAAA,CAAG,KAAA,CAAM,MAAA,CAA4C,MAAM,CAAA;AAAA,IACpE;AAAA,GACF;AACF;AAUA,SAAS,UAAA,CACP,MACA,QAAA,EACkB;AAClB,EAAA,MAAM,KAAK,IAAA,CAAK,cAAA;AAChB,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,IAAA,CAAK,IAAA,IAAQ,QAAA,CAAS,IAAA;AAAA,IAC5B,GAAA,EAAK,IAAA,CAAK,GAAA,IAAO,QAAA,CAAS,GAAA;AAAA,IAC1B,QAAA,EAAU,IAAA,CAAK,QAAA,IAAY,QAAA,CAAS,QAAA;AAAA,IACpC,GAAI,EAAA,KAAO,MAAA,IAAa,EAAA,CAAG,MAAA,GAAS,CAAA,GAAI,EAAE,SAAA,EAAW,IAAI,IAAA,CAAK,EAAE,CAAA,KAAM;AAAC,GACzE;AACF;AAiBO,IAAM,kBAAN,MAAsB;AAAA,EACV,IAAA;AAAA,EACA,cAAA;AAAA,EACA,aAAA;AAAA,EACA,MAAA;AAAA,EACA,UAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA;AAAA,EACA,GAAA;AAAA;AAAA,EAET,aAAA;AAAA,EAER,YAAY,IAAA,EAA8B;AACxC,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AACjB,IAAA,IAAA,CAAK,iBAAiB,IAAA,CAAK,MAAA;AAC3B,IAAA,IAAA,CAAK,SAAS,IAAA,CAAK,MAAA;AACnB,IAAA,IAAA,CAAK,aAAA,GACH,IAAA,CAAK,aAAA,KACJ,CAAC,MAAM,GAAA,KAAQ;AACd,MAAA,IAAI,IAAA,CAAK,WAAW,MAAA,EAAW;AAC7B,QAAA,IAAA,CAAK,MAAA,CAAO,KAAA;AAAA,UACV,EAAE,MAAM,KAAA,EAAOG,kBAAA,CAAcD,mBAAc,GAAG,CAAA,CAAE,OAAO,CAAA,EAAE;AAAA,UACzD;AAAA,SACF;AAAA,MACF,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,KAAA;AAAA,UACN,wCAAwC,IAAI,CAAA,EAAA,CAAA;AAAA,UAC5CC,kBAAA,CAAcD,kBAAAA,CAAc,GAAG,CAAA,CAAE,OAAO;AAAA,SAC1C;AAAA,MACF;AAAA,IACF,CAAA,CAAA;AACF,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA,CAAK,IAAA,EAAM,UAAA,IAAc,mBAAA;AAC3C,IAAA,IAAA,CAAK,SAAA,GAAY,IAAA,CAAK,IAAA,EAAM,SAAA,IAAa,kBAAA;AACzC,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAK,KAAA,IAAS,SAAA;AAC3B,IAAA,IAAA,CAAK,GAAA,GAAM,IAAA,CAAK,GAAA,KAAQ,MAAM,KAAK,GAAA,EAAI,CAAA;AAAA,EACzC;AAAA,EAEQ,SAAA,GAA4C;AAClD,IAAA,IAAI,KAAK,cAAA,KAAmB,MAAA,SAAkB,OAAA,CAAQ,OAAA,CAAQ,KAAK,cAAc,CAAA;AACjF,IAAA,IAAI,IAAA,CAAK,kBAAkB,MAAA,EAAW;AACpC,MAAA,IAAA,CAAK,aAAA,GAAgB,gBAAA,CAAiB,IAAA,CAAK,IAAI,CAAA;AAAA,IACjD;AACA,IAAA,OAAO,IAAA,CAAK,aAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,MAAA,CACJ,MAAA,EACA,QAAA,EACA,IAAA,EAC2B;AAC3B,IAAA,MAAM,SAAS,IAAA,EAAM,MAAA;AACrB,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,SAAA,EAAU;AAEpC,IAAA,IAAI,UAAA;AAOJ,IAAA,IAAI;AACF,MAAA,UAAA,GAAa,MAAM,OAAO,MAAA,CAAO;AAAA,QAC/B,IAAA,EAAM,MAAA;AAAA,QACN,MAAA,EAAQ;AAAA,UACN,QAAA;AAAA,UACA,GAAI,MAAM,WAAA,KAAgB,KAAA,CAAA,GAAY,EAAE,WAAA,EAAa,IAAA,CAAK,WAAA,EAAY,GAAI;AAAC;AAC7E,OACD,CAAA;AAAA,IACH,SAAS,CAAA,EAAG;AACV,MAAA,MAAMA,mBAAc,CAAC,CAAA;AAAA,IACvB;AAEA,IAAA,MAAM,EAAE,IAAA,EAAM,GAAA,EAAI,GAAI,UAAA;AAEtB,IAAA,IACE,IAAA,KAAS,UACT,IAAA,CAAK,MAAA,KAAW,KAChB,GAAA,KAAQ,MAAA,IACR,GAAA,CAAI,MAAA,KAAW,CAAA,EACf;AACA,MAAA,MAAM,IAAIF,cAAS,4DAAA,EAA8D;AAAA,QAC/E,IAAA,EAAM,aAAA;AAAA,QACN,SAAA,EAAW;AAAA,OACZ,CAAA;AAAA,IACH;AAEA,IAAA,MAAM,QAAA,GAAW,EAAE,IAAA,EAAM,GAAA,EAAK,QAAA,EAAS;AAEvC,IAAA,IAAI,UAAA,CAAW,UAAU,QAAA,EAAU;AACjC,MAAA,OAAO,UAAA,CAAW,YAAY,QAAQ,CAAA;AAAA,IACxC;AAEA,IAAA,IAAI,UAAA,CAAW,UAAU,QAAA,EAAU;AACjC,MAAA,MAAM,IAAIA,cAAS,iDAAA,EAAmD;AAAA,QACpE,IAAA,EAAM,aAAA;AAAA,QACN,SAAA,EAAW;AAAA,OACZ,CAAA;AAAA,IACH;AAGA,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,EAAI,GAAI,IAAA,CAAK,SAAA;AAInC,IAAA,IAAI,MAAA,EAAQ,YAAY,IAAA,EAAM;AAC5B,MAAA,MAAM,IAAIA,cAAS,6BAAA,EAA+B;AAAA,QAChD,IAAA,EAAM,SAAA;AAAA,QACN,SAAA,EAAW;AAAA,OACZ,CAAA;AAAA,IACH;AAKA,IAAA,MAAM,mBACJ,MAAA,KAAW,MAAA,GACP,IAAI,OAAA,CAAe,CAAC,GAAG,MAAA,KAAW;AAChC,MAAA,MAAA,CAAO,gBAAA;AAAA,QACL,OAAA;AAAA,QACA,MAAM;AACJ,UAAA,MAAA;AAAA,YACE,IAAIA,cAAS,6BAAA,EAA+B;AAAA,cAC1C,IAAA,EAAM,SAAA;AAAA,cACN,SAAA,EAAW;AAAA,aACZ;AAAA,WACH;AAAA,QACF,CAAA;AAAA,QACA,EAAE,MAAM,IAAA;AAAK,OACf;AAAA,IACF,CAAC,CAAA,GACD,MAAA;AAEN,IAAA,WAAS;AACP,MAAA,IAAI,IAAA,CAAK,GAAA,EAAI,IAAK,QAAA,EAAU;AAC1B,QAAA,MAAM,IAAIA,cAAS,sDAAA,EAAwD;AAAA,UACzE,IAAA,EAAM,SAAA;AAAA,UACN,SAAA,EAAW;AAAA,SACZ,CAAA;AAAA,MACH;AAIA,MAAA,MAAM,SAAA,GAAY,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,UAAU,CAAA;AAC5C,MAAA,OAAO,gBAAA,KAAqB,SACxB,OAAA,CAAQ,IAAA,CAAK,CAAC,SAAA,EAAW,gBAAgB,CAAC,CAAA,GAC1C,SAAA,CAAA;AAEJ,MAAA,IAAI,QAAA;AAOJ,MAAA,IAAI;AACF,QAAA,QAAA,GAAW,MAAM,MAAA,CAAO,GAAA,CAAI,EAAE,MAAM,CAAA;AAAA,MACtC,SAAS,CAAA,EAAG;AACV,QAAA,MAAME,mBAAc,CAAC,CAAA;AAAA,MACvB;AAMA,MAAA,IAAK,MAAA,EAAQ,YAAoC,IAAA,EAAM;AACrD,QAAA,MAAM,IAAIF,cAAS,6BAAA,EAA+B;AAAA,UAChD,IAAA,EAAM,SAAA;AAAA,UACN,SAAA,EAAW;AAAA,SACZ,CAAA;AAAA,MACH;AAEA,MAAA,IAAI,QAAA,CAAS,UAAU,QAAA,EAAU;AAC/B,QAAA,OAAO,UAAA,CAAW,UAAU,QAAQ,CAAA;AAAA,MACtC;AAEA,MAAA,IAAI,QAAA,CAAS,UAAU,QAAA,EAAU;AAC/B,QAAA,MAAM,IAAIA,cAAS,uCAAA,EAAyC;AAAA,UAC1D,IAAA,EAAM,aAAA;AAAA,UACN,SAAA,EAAW;AAAA,SACZ,CAAA;AAAA,MACH;AAAA,IAEF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO,MAAA,EAAyC;AACpD,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,SAAA,EAAU;AACpC,MAAA,MAAM,OAAO,MAAA,CAAO,EAAE,IAAA,EAAM,MAAA,CAAO,MAAM,CAAA;AAAA,IAC3C,SAAS,GAAA,EAAK;AACZ,MAAA,IAAA,CAAK,aAAA,CAAc,MAAA,CAAO,IAAA,EAAM,GAAG,CAAA;AAAA,IACrC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UAAU,OAAA,EAA4C;AAC1D,IAAA,MAAM,OAAA,CAAQ,UAAA,CAAW,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAM,IAAA,CAAK,MAAA,CAAO,CAAC,CAAC,CAAC,CAAA;AAAA,EAC7D;AACF;AClSA,IAAM,oBAAA,GAAuB,EAAA;AAC7B,IAAM,yBAAA,GAA4B,IAAA;AAElC,eAAe,kBAAkB,IAAA,EAAqD;AACpF,EAAA,MAAM,EAAE,WAAA,EAAY,GAAI,MAAM,OAAO,eAAe,CAAA;AAEpD,EAAA,MAAM,KAAK,IAAI,WAAA,CAAY,EAAE,MAAA,EAAQ,IAAA,CAAK,QAAQ,CAAA;AAElD,EAAA,OAAO;AAAA,IACL,MAAM,OAAO,MAAA,EAAQ;AACnB,MAAA,MAAM,MAAA,GAAS,MACb,EAAA,CAAG,MAAA,CAAO,OAKV,MAAM,CAAA;AACR,MAAA,OAAO,MAAA;AAAA,IACT,CAAA;AAAA,IAEA,MAAM,OAAO,MAAA,EAAQ;AACnB,MAAA,MAAM,MAAA,GAAS,MACb,EAAA,CAAG,MAAA,CAAO,OAIV,MAAM,CAAA;AACR,MAAA,OAAO,MAAA;AAAA,IACT,CAAA;AAAA,IAEA,MAAM,OAAO,MAAA,EAAQ;AACnB,MAAA,MAAO,EAAA,CAAG,MAAA,CAAO,MAAA,CAA4C,MAAM,CAAA;AAAA,IACrE;AAAA,GACF;AACF;AA6BO,IAAM,mBAAN,MAAuB;AAAA,EACX,IAAA;AAAA,EACA,cAAA;AAAA,EACA,QAAA;AAAA,EACA,MAAA;AAAA,EACA,aAAA;AAAA,EACA,MAAA;AAAA,EACA,GAAA;AAAA;AAAA,EAGT,aAAA;AAAA;AAAA,EAGS,OAAA,uBAAwC,GAAA,EAAI;AAAA;AAAA,EAG5C,QAAA,uBAAwD,GAAA,EAAI;AAAA,EAE7E,YAAY,IAAA,EAA+B;AACzC,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AACjB,IAAA,IAAA,CAAK,iBAAiB,IAAA,CAAK,MAAA;AAC3B,IAAA,IAAA,CAAK,QAAA,GAAW,KAAK,QAAA,IAAY,KAAA;AACjC,IAAA,IAAA,CAAK,MAAA,GAAA,CAAU,IAAA,CAAK,iBAAA,IAAqB,oBAAA,IAAwB,GAAA;AACjE,IAAA,IAAA,CAAK,SAAS,IAAA,CAAK,MAAA;AACnB,IAAA,IAAA,CAAK,aAAA,GACH,IAAA,CAAK,aAAA,KACJ,CAAC,WAAW,GAAA,KAAQ;AACnB,MAAA,IAAI,IAAA,CAAK,WAAW,MAAA,EAAW;AAC7B,QAAA,IAAA,CAAK,MAAA,CAAO,KAAA;AAAA,UACV,EAAE,MAAM,SAAA,EAAW,KAAA,EAAOG,mBAAcD,kBAAAA,CAAc,GAAG,CAAA,CAAE,OAAO,CAAA,EAAE;AAAA,UACpE;AAAA,SACF;AAAA,MACF,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,KAAA;AAAA,UACN,yCAAyC,SAAS,CAAA,EAAA,CAAA;AAAA,UAClDC,kBAAAA,CAAcD,kBAAAA,CAAc,GAAG,CAAA,CAAE,OAAO;AAAA,SAC1C;AAAA,MACF;AAAA,IACF,CAAA,CAAA;AACF,IAAA,IAAA,CAAK,GAAA,GAAM,IAAA,CAAK,GAAA,KAAQ,MAAM,KAAK,GAAA,EAAI,CAAA;AAAA,EACzC;AAAA,EAEQ,SAAA,GAA6C;AACnD,IAAA,IAAI,KAAK,cAAA,KAAmB,MAAA,SAAkB,OAAA,CAAQ,OAAA,CAAQ,KAAK,cAAc,CAAA;AACjF,IAAA,IAAI,IAAA,CAAK,kBAAkB,MAAA,EAAW;AACpC,MAAA,IAAA,CAAK,aAAA,GAAgB,iBAAA,CAAkB,IAAA,CAAK,IAAI,CAAA;AAAA,IAClD;AACA,IAAA,OAAO,IAAA,CAAK,aAAA;AAAA,EACd;AAAA,EAEQ,OAAO,MAAA,EAAoC;AACjD,IAAA,OAAO,OAAO,SAAA,CAAU,OAAA,KAAY,IAAA,CAAK,MAAA,GAAS,KAAK,GAAA,EAAI;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,OAAO,KAAA,EAMkB;AAC7B,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,SAAA,EAAU;AAEpC,IAAA,MAAM,SAKF,EAAE,GAAA,EAAK,CAAA,EAAG,KAAA,CAAM,UAAU,CAAA,CAAA,CAAA,EAAI;AAElC,IAAA,IAAI,KAAA,CAAM,QAAA,KAAa,MAAA,EAAW,MAAA,CAAO,WAAW,KAAA,CAAM,QAAA;AAC1D,IAAA,IAAI,MAAM,iBAAA,KAAsB,MAAA;AAC9B,MAAA,MAAA,CAAO,oBAAoB,KAAA,CAAM,iBAAA;AACnC,IAAA,IAAI,KAAA,CAAM,WAAA,KAAgB,MAAA,EAAW,MAAA,CAAO,cAAc,KAAA,CAAM,WAAA;AAEhE,IAAA,IAAI,IAAA;AACJ,IAAA,IAAI;AACF,MAAA,IAAA,GAAO,MAAM,OAAO,MAAA,CAAO,EAAE,OAAO,KAAA,CAAM,KAAA,EAAO,QAAQ,CAAA;AAAA,IAC3D,SAAS,CAAA,EAAG;AACV,MAAA,MAAMA,mBAAc,CAAC,CAAA;AAAA,IACvB;AAEA,IAAA,IAAI,KAAK,IAAA,KAAS,MAAA,IAAa,IAAA,CAAK,IAAA,CAAK,WAAW,CAAA,EAAG;AACrD,MAAA,MAAM,IAAIF,cAAS,oDAAA,EAAsD;AAAA,QACvE,IAAA,EAAM,aAAA;AAAA,QACN,SAAA,EAAW;AAAA,OACZ,CAAA;AAAA,IACH;AAEA,IAAA,MAAM,cAAA,GAAiB,IAAI,IAAA,CAAK,IAAA,CAAK,KAAI,GAAI,KAAA,CAAM,aAAa,GAAI,CAAA;AACpE,IAAA,MAAM,SAAA,GACJ,IAAA,CAAK,UAAA,KAAe,MAAA,IAAa,IAAA,CAAK,UAAA,CAAW,MAAA,GAAS,CAAA,GACtD,IAAI,IAAA,CAAK,IAAA,CAAK,UAAU,CAAA,GACxB,cAAA;AAEN,IAAA,OAAO;AAAA,MACL,WAAW,IAAA,CAAK,IAAA;AAAA,MAChB,KAAA,EAAO,IAAA,CAAK,KAAA,IAAS,KAAA,CAAM,KAAA;AAAA,MAC3B;AAAA,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,WAAA,CACJ,GAAA,EACA,OAAA,EAK4B;AAC5B,IAAA,MAAM,SAAS,CAAA,EAAG,GAAA,CAAI,KAAK,CAAA,CAAA,EAAI,IAAI,SAAS,CAAA,CAAA;AAG5C,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAA;AACxC,IAAA,IAAI,aAAa,MAAA,IAAa,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,MAAM,CAAA,EAAG;AAC1D,MAAA,OAAO,QAAA,CAAS,MAAA;AAAA,IAClB;AAGA,IAAA,IAAI,KAAK,QAAA,EAAU;AACjB,MAAA,MAAM,QAAA,GAAW,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA;AACzC,MAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,QAAA,OAAO,QAAA;AAAA,MACT;AAAA,IACF;AAEA,IAAA,MAAM,WAAW,YAAwC;AACvD,MAAA,MAAM,aAAA,GAAgB,MAAM,OAAA,EAAQ;AACpC,MAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,MAAA,CAAO;AAAA,QAC/B,OAAO,GAAA,CAAI,KAAA;AAAA,QACX,YAAY,aAAA,CAAc,UAAA;AAAA,QAC1B,GAAI,cAAc,QAAA,KAAa,MAAA,GAC3B,EAAE,QAAA,EAAU,aAAA,CAAc,QAAA,EAAS,GACnC,EAAC;AAAA,QACL,GAAI,cAAc,iBAAA,KAAsB,MAAA,GACpC,EAAE,iBAAA,EAAmB,aAAA,CAAc,iBAAA,EAAkB,GACrD;AAAC,OACN,CAAA;AACD,MAAA,IAAA,CAAK,OAAA,CAAQ,IAAI,MAAA,EAAQ,EAAE,QAAQ,UAAA,EAAY,aAAA,CAAc,YAAY,CAAA;AACzE,MAAA,OAAO,MAAA;AAAA,IACT,CAAA;AAEA,IAAA,IAAI,KAAK,QAAA,EAAU;AACjB,MAAA,MAAM,OAAA,GAAU,QAAA,EAAS,CAAE,OAAA,CAAQ,MAAM;AACvC,QAAA,IAAA,CAAK,QAAA,CAAS,OAAO,MAAM,CAAA;AAAA,MAC7B,CAAC,CAAA;AACD,MAAA,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,MAAA,EAAQ,OAAO,CAAA;AACjC,MAAA,OAAO,OAAA;AAAA,IACT;AAEA,IAAA,OAAO,QAAA,EAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,qBAAA,CACJ,MAAA,EACA,IAAA,EAC4B;AAC5B,IAAA,MAAM,WAAA,GAAA,CAAe,IAAA,EAAM,gBAAA,IAAoB,GAAA,IAAO,GAAA;AAEtD,IAAA,IAAI,OAAO,SAAA,CAAU,OAAA,KAAY,IAAA,CAAK,GAAA,KAAQ,WAAA,EAAa;AAEzD,MAAA,OAAO,MAAA;AAAA,IACT;AAGA,IAAA,IAAI,mBAAmB,IAAA,EAAM,gBAAA;AAC7B,IAAA,IAAI,qBAAqB,MAAA,EAAW;AAClC,MAAA,KAAA,MAAW,KAAA,IAAS,IAAA,CAAK,OAAA,CAAQ,MAAA,EAAO,EAAG;AACzC,QAAA,IAAI,KAAA,CAAM,MAAA,CAAO,SAAA,KAAc,MAAA,CAAO,SAAA,EAAW;AAC/C,UAAA,gBAAA,GAAmB,KAAA,CAAM,UAAA;AACzB,UAAA;AAAA,QACF;AAAA,MACF;AACA,MAAA,gBAAA,GAAmB,gBAAA,IAAoB,yBAAA;AAAA,IACzC;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,SAAA,EAAU;AACpC,MAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,MAAA,CAAO;AAAA,QAC/B,MAAM,MAAA,CAAO,SAAA;AAAA,QACb,MAAA,EAAQ,EAAE,GAAA,EAAK,CAAA,EAAG,gBAAgB,CAAA,CAAA,CAAA;AAAI,OACvC,CAAA;AAED,MAAA,MAAM,iBAAiB,IAAI,IAAA,CAAK,KAAK,GAAA,EAAI,GAAI,mBAAmB,GAAI,CAAA;AACpE,MAAA,MAAM,YAAA,GACJ,IAAA,CAAK,UAAA,KAAe,KAAA,CAAA,IAAa,IAAA,CAAK,UAAA,CAAW,MAAA,GAAS,CAAA,GACtD,IAAI,IAAA,CAAK,IAAA,CAAK,UAAU,CAAA,GACxB,cAAA;AAEN,MAAA,MAAM,SAAA,GAA+B;AAAA,QACnC,WAAW,MAAA,CAAO,SAAA;AAAA,QAClB,OAAO,MAAA,CAAO,KAAA;AAAA,QACd,SAAA,EAAW;AAAA,OACb;AAGA,MAAA,KAAA,MAAW,CAAC,CAAA,EAAG,KAAK,KAAK,IAAA,CAAK,OAAA,CAAQ,SAAQ,EAAG;AAC/C,QAAA,IAAI,KAAA,CAAM,MAAA,CAAO,SAAA,KAAc,MAAA,CAAO,SAAA,EAAW;AAC/C,UAAA,IAAA,CAAK,OAAA,CAAQ,IAAI,CAAA,EAAG,EAAE,QAAQ,SAAA,EAAW,UAAA,EAAY,kBAAkB,CAAA;AACvE,UAAA;AAAA,QACF;AAAA,MACF;AAEA,MAAA,OAAO,SAAA;AAAA,IACT,CAAA,CAAA,MAAQ;AAEN,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAO,MAAA,EAA0C;AAErD,IAAA,KAAA,MAAW,CAAC,CAAA,EAAG,KAAK,KAAK,IAAA,CAAK,OAAA,CAAQ,SAAQ,EAAG;AAC/C,MAAA,IAAI,KAAA,CAAM,MAAA,CAAO,SAAA,KAAc,MAAA,CAAO,SAAA,EAAW;AAC/C,QAAA,IAAA,CAAK,OAAA,CAAQ,OAAO,CAAC,CAAA;AACrB,QAAA;AAAA,MACF;AAAA,IACF;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,SAAA,EAAU;AACpC,MAAA,MAAM,OAAO,MAAA,CAAO,EAAE,IAAA,EAAM,MAAA,CAAO,WAAW,CAAA;AAAA,IAChD,SAAS,GAAA,EAAK;AACZ,MAAA,IAAA,CAAK,aAAA,CAAc,MAAA,CAAO,SAAA,EAAW,GAAG,CAAA;AAAA,IAC1C;AAAA,EACF;AACF;;;AC5YA,SAAS,aAAa,GAAA,EAAkB;AACtC,EAAA,OAAO,GAAA,CAAI,QAAA,CAAS,UAAA,CAAW,MAAM,CAAA,GAAI,IAAI,QAAA,CAAS,KAAA,CAAM,CAAC,CAAA,GAAI,GAAA,CAAI,QAAA;AACvE;AAWA,SAAS,WAAW,KAAA,EAAsC;AACxD,EAAA,IAAI,KAAA,KAAU,IAAA,IAAQ,OAAO,KAAA,KAAU,UAAU,OAAO,MAAA;AAExD,EAAA,MAAM,GAAA,GAAO,MAAkC,KAAK,CAAA;AACpD,EAAA,IAAI,GAAA,KAAQ,IAAA,IAAQ,OAAO,GAAA,KAAQ,UAAU,OAAO,MAAA;AAEpD,EAAA,MAAM,GAAA,GAAO,IAAgC,KAAK,CAAA;AAClD,EAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,IAAY,GAAA,CAAI,MAAA,KAAW,GAAG,OAAO,MAAA;AAExD,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI;AACF,IAAA,MAAA,GAAS,IAAI,IAAI,GAAG,CAAA;AAAA,EACtB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,KAAA,GAAS,IAAgC,OAAO,CAAA;AACtD,EAAA,MAAM,UAAA,GACJ,OAAO,KAAA,KAAU,QAAA,IAAY,MAAM,MAAA,GAAS,CAAA,GAAI,KAAA,GAAQ,YAAA,CAAa,MAAM,CAAA;AAE7E,EAAA,OAAO,EAAE,GAAA,EAAK,GAAA,EAAK,UAAA,EAAW;AAChC;AAWO,SAAS,4BAA4B,iBAAA,EAAwC;AAClF,EAAA,IAAI,sBAAsB,IAAA,IAAQ,OAAO,iBAAA,KAAsB,QAAA,SAAiB,EAAC;AAEjF,EAAA,MAAM,MAAA,GAAU,kBAA8C,iBAAiB,CAAA;AAC/E,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,SAAU,EAAC;AAEpC,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAC7B,EAAA,MAAM,YAAwB,EAAC;AAE/B,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,MAAM,QAAA,GAAW,WAAW,KAAK,CAAA;AACjC,IAAA,IAAI,aAAa,MAAA,EAAW;AAC5B,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,QAAA,CAAS,GAAG,CAAA,EAAG;AAC5B,IAAA,IAAA,CAAK,GAAA,CAAI,SAAS,GAAG,CAAA;AACrB,IAAA,SAAA,CAAU,KAAK,QAAQ,CAAA;AAAA,EACzB;AAEA,EAAA,OAAO,SAAA;AACT","file":"index.cjs","sourcesContent":["/**\n * Structural GeminiClientLike interface + buildGoogleClient factory.\n *\n * This module defines the structural interface the adapter depends on.\n * The real @google/genai SDK is imported ONLY in buildGoogleClient so tests\n * can inject a fake without pulling in the real SDK.\n *\n * @module\n */\n\nimport type { AuthMaterial } from '@gullabs/core'\n\n// ---------------------------------------------------------------------------\n// Transport timeout defaults\n// ---------------------------------------------------------------------------\n\n/**\n * Default HTTP transport timeout for Gemini Flex service-tier calls (ms).\n *\n * The default per-attempt transport (httpOptions) timeout applied ONLY when a\n * flex call provides no explicit `timeoutMs`. Set to 25 minutes (1_500_000 ms)\n * to cover the ~20-minute flex workload ceiling with margin. The @google/genai\n * SDK defaults to 1 minute, which would terminate a long flex call prematurely.\n * Callers SHOULD still set `timeoutMs` for a precise per-attempt ceiling; this\n * value is only the backstop for callers that do not.\n */\nexport const FLEX_DEFAULT_TIMEOUT_MS = 1_500_000\n\n/**\n * Default HTTP transport timeout for Gemini standard-tier calls (ms).\n *\n * Used when a standard-tier request has no explicit `timeoutMs`. The adapter\n * also backs this with an AbortController so the limit is a real client-side\n * ceiling, not only an SDK transport hint.\n */\nexport const STANDARD_DEFAULT_TIMEOUT_MS = 300_000\n\n/**\n * Buffer added above `timeoutMs` when setting the SDK transport timeout.\n *\n * When `timeoutMs` is set, the engine arms an AbortSignal at exactly\n * `timeoutMs`. If the SDK transport timer fired at the same instant it\n * could preempt the signal and produce a raw SDK error instead of the\n * engine's clean `kind:'timeout'`. By setting the transport timeout to\n * `timeoutMs + TRANSPORT_TIMEOUT_BUFFER_MS` we ensure the engine's\n * AbortSignal always fires first.\n */\nexport const TRANSPORT_TIMEOUT_BUFFER_MS = 5_000\n\n// ---------------------------------------------------------------------------\n// Response shape — mirrors the @google/genai surface we actually consume\n// ---------------------------------------------------------------------------\n\n/** A single text/thought part in a Gemini candidate content. */\nexport interface GeminiPartShape {\n text?: string\n /**\n * Present and `true` on thought-summary parts.\n * Real field name in @google/genai Candidate.content.parts: `thought`.\n */\n thought?: boolean\n}\n\n/** A candidate returned by Gemini generateContent. */\nexport interface GeminiCandidateShape {\n content?: {\n parts?: GeminiPartShape[]\n }\n /**\n * Why the model stopped.\n * Real SDK enum (FinishReason): \"STOP\", \"MAX_TOKENS\", \"SAFETY\",\n * \"RECITATION\", \"BLOCKLIST\", \"PROHIBITED_CONTENT\", etc.\n */\n finishReason?: string\n /**\n * Grounding metadata returned when Google Search grounding is active.\n * Real SDK type: GroundingMetadata. Kept as `unknown` to avoid a hard\n * coupling to @google/genai types; cast to JsonValue at the adapter boundary.\n */\n groundingMetadata?: unknown\n}\n\n/**\n * Token usage metadata returned alongside a Gemini response.\n *\n * Real type: GenerateContentResponseUsageMetadata.\n * NOTE: thoughtsTokenCount is SEPARATE from candidatesTokenCount.\n * The adapter must add them to get GROSS outputTokens.\n */\nexport interface GeminiUsageMetadataShape {\n promptTokenCount?: number\n candidatesTokenCount?: number\n cachedContentTokenCount?: number\n thoughtsTokenCount?: number\n totalTokenCount?: number\n}\n\n/**\n * Structural equivalent of @google/genai's GenerateContentResponse.\n * Only the fields the adapter reads are represented here.\n */\nexport interface GeminiResponseShape {\n candidates?: GeminiCandidateShape[]\n usageMetadata?: GeminiUsageMetadataShape\n /** Real field: GenerateContentResponse.modelVersion */\n modelVersion?: string\n /** Real field: GenerateContentResponse.responseId */\n responseId?: string\n /**\n * Safety-block metadata.\n * Real type: GenerateContentResponsePromptFeedback.\n * Present when the prompt (not output) was blocked by safety filters.\n */\n promptFeedback?: {\n /** Real type: BlockedReason (string enum). e.g. \"SAFETY\", \"OTHER\". */\n blockReason?: string\n blockReasonMessage?: string\n safetyRatings?: unknown[]\n }\n}\n\n// ---------------------------------------------------------------------------\n// Request / config shape — what the adapter sends to generateContent\n// ---------------------------------------------------------------------------\n\n/**\n * Per-part media-resolution hint emitted on inline/file parts.\n * Real type: @google/genai `PartMediaResolution`; `level` values come from\n * the `PartMediaResolutionLevel` string enum (we only emit the LOW/MEDIUM/HIGH\n * subset our normalized `mediaResolution` maps to).\n */\nexport interface GeminiPartMediaResolution {\n level?: 'MEDIA_RESOLUTION_LOW' | 'MEDIA_RESOLUTION_MEDIUM' | 'MEDIA_RESOLUTION_HIGH'\n}\n\n/** A text part in a content object we construct. */\nexport interface GeminiTextContentPart {\n text: string\n}\n\n/**\n * An inline binary media part in a content object we construct.\n * `data` must be raw base64 — no `data:…;base64,` prefix.\n */\nexport interface GeminiInlineDataContentPart {\n inlineData: {\n /** IANA media type, e.g. `\"image/png\"`. */\n mimeType: string\n /** Raw base64-encoded bytes (no data-URL prefix). */\n data: string\n }\n /** Optional per-part media-resolution hint (real field: `Part.mediaResolution`). */\n mediaResolution?: GeminiPartMediaResolution\n}\n\n/**\n * A provider-hosted file reference part in a content object we construct.\n * The Gemini service dereferences `fileUri` server-side.\n */\nexport interface GeminiFileDataContentPart {\n fileData: {\n /** IANA media type of the referenced file. */\n mimeType: string\n /** Provider-assigned file URI, e.g. from the Gemini File API. */\n fileUri: string\n }\n /** Optional per-part media-resolution hint (real field: `Part.mediaResolution`). */\n mediaResolution?: GeminiPartMediaResolution\n}\n\n/**\n * Union of all part shapes the adapter may produce for `GeminiContent.parts`.\n * Each member (including the optional per-part `mediaResolution`) is a\n * structural subset of the real `@google/genai` `Part` type for the fields we use.\n */\nexport type GeminiContentPart =\n | GeminiTextContentPart\n | GeminiInlineDataContentPart\n | GeminiFileDataContentPart\n\n/** A content object (message) we construct. */\nexport interface GeminiContent {\n role: string\n parts: GeminiContentPart[]\n}\n\n/**\n * Schema shape we pass as responseSchema.\n * Structurally compatible with @google/genai Schema.\n */\nexport interface GeminiSchema {\n type?: string\n description?: string\n properties?: Record<string, GeminiSchema>\n required?: string[]\n items?: GeminiSchema\n enum?: string[]\n nullable?: boolean\n format?: string\n}\n\n/**\n * Thinking configuration.\n * Real type: ThinkingConfig in @google/genai.\n * - thinkingBudget: 0 = DISABLED, -1 = AUTOMATIC\n * - thinkingLevel: ThinkingLevel enum (\"LOW\", \"MEDIUM\", \"HIGH\", \"MINIMAL\")\n */\nexport interface GeminiThinkingConfig {\n includeThoughts?: boolean\n thinkingBudget?: number\n /**\n * Real type: ThinkingLevel enum.\n * Values: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MINIMAL\" | \"THINKING_LEVEL_UNSPECIFIED\"\n */\n thinkingLevel?: string\n}\n\n/**\n * Config object passed in GenerateContentParameters.config.\n * Real type: GenerateContentConfig.\n * abortSignal is inside config, NOT in the top-level params.\n */\nexport interface GeminiGenerateConfig {\n systemInstruction?: { parts: GeminiContentPart[] }\n temperature?: number\n topP?: number\n topK?: number\n maxOutputTokens?: number\n stopSequences?: string[]\n responseMimeType?: string\n responseSchema?: GeminiSchema\n thinkingConfig?: GeminiThinkingConfig\n /** Real type: ServiceTier enum. Values: \"flex\" | \"standard\". */\n serviceTier?: string\n /** Real field: GenerateContentConfig.abortSignal (NOT in top-level params). */\n abortSignal?: AbortSignal\n /**\n * Per-request HTTP options forwarded to the @google/genai transport.\n * We use this to set a transport-level timeout that is >= the AbortSignal\n * deadline so the SDK fetch does not preempt the abort.\n *\n * Real field: GenerateContentConfig.httpOptions.timeout (milliseconds).\n * Real field: GenerateContentConfig.httpOptions.headers (Record<string,string>).\n * Used by FIX A-1 to inject Vertex flex routing headers.\n */\n httpOptions?: { timeout?: number; headers?: Record<string, string> }\n}\n\n/**\n * Parameters for models.generateContent.\n * Real type: GenerateContentParameters.\n */\nexport interface GeminiGenerateParams {\n model: string\n contents: GeminiContent[]\n config?: GeminiGenerateConfig\n}\n\n// ---------------------------------------------------------------------------\n// GeminiClientLike — structural interface (no @google/genai dependency)\n// ---------------------------------------------------------------------------\n\n/**\n * Structural interface for the @google/genai client surface the adapter uses.\n *\n * Satisfied by:\n * - The real GoogleGenAI client (via buildGoogleClient wrapper).\n * - FakeGeminiClient from @gullabs/testing (its generateContent accepts unknown).\n */\nexport interface GeminiClientLike {\n models: {\n generateContent(params: GeminiGenerateParams): Promise<GeminiResponseShape>\n }\n}\n\n// ---------------------------------------------------------------------------\n// buildGoogleClient — imports the real @google/genai SDK\n// ---------------------------------------------------------------------------\n\n/**\n * Build a real @google/genai client from AuthMaterial.\n *\n * Returns a GeminiClientLike wrapper around the real GoogleGenAI client.\n * Only API-key authentication is supported; Vertex AI is not.\n *\n * @param auth - API key credentials ({ apiKey }).\n */\nexport async function buildGoogleClient(auth: AuthMaterial): Promise<GeminiClientLike> {\n // Import the real SDK — only called at runtime when no client is injected.\n // The cast is safe: GeminiGenerateParams is a structural subset of\n // GenerateContentParameters; GeminiResponseShape is a subset of\n // GenerateContentResponse.\n const { GoogleGenAI } = await import('@google/genai')\n\n const ai = new GoogleGenAI({ apiKey: auth.apiKey })\n\n return {\n models: {\n async generateContent(params: GeminiGenerateParams): Promise<GeminiResponseShape> {\n // Cast needed: our structural types are subsets of the real SDK types.\n const result = await (\n ai.models.generateContent as (params: unknown) => Promise<GeminiResponseShape>\n )(params)\n return result\n },\n },\n }\n}\n","import type { LlmError } from '@gullabs/core'\n\nconst CAPACITY_PATTERNS = [\n /capacity/i,\n /overload/i,\n /overloaded/i,\n /unavailable/i,\n /no\\s+capacity/i,\n /temporar(?:y|ily)/i,\n /try\\s+again/i,\n]\n\nconst QUOTA_PATTERNS = [\n /quota/i,\n /billing/i,\n /billable/i,\n /payment/i,\n /rate\\s+limit/i,\n /exceeded/i,\n /insufficient/i,\n]\n\nexport function isGeminiCapacityError(err: LlmError): boolean {\n if (err.kind === 'server') return err.httpStatus === 503\n if (err.kind !== 'rate_limited') return false\n\n const message = err.message\n if (QUOTA_PATTERNS.some((pattern) => pattern.test(message))) {\n return false\n }\n return CAPACITY_PATTERNS.some((pattern) => pattern.test(message))\n}\n","/**\n * geminiAdapter — @gullabs/google Gemini provider adapter.\n *\n * Pure request⇄response mapping over @google/genai (via GeminiClientLike).\n * Never persists, never computes cost, never loops.\n *\n * @module\n */\n\nimport { LlmError, classifyError, assertNever, EFFORT_BUDGET } from '@gullabs/core'\nimport type {\n ProviderAdapter,\n ResolvedRequest,\n AdapterCtx,\n AdapterResult,\n Usage,\n Warning,\n FinishReason,\n JsonValue,\n AuthMaterial,\n Part,\n} from '@gullabs/core'\nimport {\n buildGoogleClient,\n FLEX_DEFAULT_TIMEOUT_MS,\n STANDARD_DEFAULT_TIMEOUT_MS,\n TRANSPORT_TIMEOUT_BUFFER_MS,\n} from './client.js'\nimport type {\n GeminiClientLike,\n GeminiGenerateConfig,\n GeminiContent,\n GeminiContentPart,\n GeminiResponseShape,\n GeminiUsageMetadataShape,\n} from './client.js'\nimport { isGeminiCapacityError } from './flex-fallback.js'\n\n// ---------------------------------------------------------------------------\n// Exported types for consumers that inject a custom client\n// ---------------------------------------------------------------------------\nexport type { GeminiClientLike }\n\n// ---------------------------------------------------------------------------\n// FinishReason mapping (Gemini SDK enum → our FinishReason)\n// ---------------------------------------------------------------------------\n\nfunction mapFinishReason(raw: string | undefined): FinishReason | undefined {\n if (raw === undefined) return undefined\n switch (raw) {\n case 'STOP':\n return 'stop'\n case 'MAX_TOKENS':\n return 'length'\n case 'SAFETY':\n case 'RECITATION':\n case 'BLOCKLIST':\n case 'PROHIBITED_CONTENT':\n case 'IMAGE_SAFETY':\n return 'content_filter'\n default:\n return 'other'\n }\n}\n\n// ---------------------------------------------------------------------------\n// mediaResolution mapping (normalized hint → Gemini PartMediaResolutionLevel)\n// ---------------------------------------------------------------------------\n\n/**\n * Map our normalized cross-provider `mediaResolution` hint to the Gemini\n * `PartMediaResolutionLevel` string-enum value emitted on `Part.mediaResolution`.\n */\nfunction mapMediaResolution(\n res: 'low' | 'medium' | 'high',\n): 'MEDIA_RESOLUTION_LOW' | 'MEDIA_RESOLUTION_MEDIUM' | 'MEDIA_RESOLUTION_HIGH' {\n switch (res) {\n case 'low':\n return 'MEDIA_RESOLUTION_LOW'\n case 'medium':\n return 'MEDIA_RESOLUTION_MEDIUM'\n case 'high':\n return 'MEDIA_RESOLUTION_HIGH'\n default:\n return assertNever(res)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Usage mapping — #1 correctness rule\n// ---------------------------------------------------------------------------\n\n/**\n * Map Gemini usageMetadata to our Usage type.\n *\n * **GROSS convention enforced here:**\n * - outputTokens = candidatesTokenCount + (thoughtsTokenCount ?? 0)\n * → thinkingTokens ⊆ outputTokens so cost math doesn't double-count.\n * - inputTokens = promptTokenCount (cachedContentTokenCount is already a\n * subset of promptTokenCount → cachedInputTokens = cachedContentTokenCount).\n */\nfunction mapUsage(meta: GeminiUsageMetadataShape | undefined): Usage {\n const promptTokenCount = meta?.promptTokenCount ?? 0\n const candidatesTokenCount = meta?.candidatesTokenCount ?? 0\n const cachedContentTokenCount = meta?.cachedContentTokenCount\n const thoughtsTokenCount = meta?.thoughtsTokenCount\n\n // #1 RULE: outputTokens = candidates + thoughts (GROSS; thinking ⊆ output)\n const outputTokens = candidatesTokenCount + (thoughtsTokenCount ?? 0)\n const inputTokens = promptTokenCount\n const totalTokens = meta?.totalTokenCount\n\n // Canonical details keys: input, cached, output.\n const details: Record<string, number> = {\n input: inputTokens,\n output: outputTokens,\n ...(cachedContentTokenCount !== undefined ? { cached: cachedContentTokenCount } : {}),\n ...(thoughtsTokenCount !== undefined ? { thinking: thoughtsTokenCount } : {}),\n }\n\n // Raw: the full usageMetadata object verbatim (as JsonValue).\n const raw: JsonValue =\n meta !== undefined ? (meta as unknown as { [k: string]: JsonValue }) : null\n\n const usage: Usage = {\n inputTokens,\n outputTokens,\n details,\n raw,\n ...(cachedContentTokenCount !== undefined\n ? { cachedInputTokens: cachedContentTokenCount }\n : {}),\n ...(thoughtsTokenCount !== undefined ? { thinkingTokens: thoughtsTokenCount } : {}),\n ...(totalTokens !== undefined ? { totalTokens } : {}),\n }\n\n return usage\n}\n\n// ---------------------------------------------------------------------------\n// Adapter options\n// ---------------------------------------------------------------------------\n\nexport interface GeminiAdapterOptions {\n /**\n * Inject a pre-built client (real or fake).\n * When omitted, `buildGoogleClient` is called with `ctx.auth` at call time,\n * inside the classified try/catch so any construction failure is wrapped\n * as a typed `LlmError`.\n */\n client?: GeminiClientLike\n /**\n * @internal Testing-only.\n *\n * Override the default `buildGoogleClient` factory. Allows unit tests to\n * simulate construction failures (e.g. bad credentials) without importing\n * the real `@google/genai` SDK. Never set this in production code.\n */\n _clientFactory?: (auth: AuthMaterial) => GeminiClientLike | Promise<GeminiClientLike>\n}\n\n// ---------------------------------------------------------------------------\n// geminiAdapter factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a Gemini provider adapter.\n *\n * @param opts.client - Optional pre-built client (e.g. for testing).\n */\nexport function geminiAdapter(opts?: GeminiAdapterOptions): ProviderAdapter {\n return {\n id: 'google',\n\n async run(req: ResolvedRequest, ctx: AdapterCtx): Promise<AdapterResult> {\n const warnings: Warning[] = []\n const model = req.model\n\n // ------------------------------------------------------------------\n // 1. Map messages → contents\n // ------------------------------------------------------------------\n\n /**\n * Map a single {@link Part} to its Gemini SDK equivalent.\n *\n * - `text` → `{ text }`\n * - `inline-media` → `{ inlineData: { mimeType, data } }` + optional `mediaResolution`\n * - `file-uri` → `{ fileData: { mimeType, fileUri } }` + optional `mediaResolution`\n *\n * `mediaResolution` IS supported as a per-part field by the Gemini SDK\n * (`Part.mediaResolution`). The normalised value is mapped to the\n * `PartMediaResolutionLevel` string enum before emission.\n */\n const mapPart = (p: Part): GeminiContentPart => {\n switch (p.kind) {\n case 'text':\n return { text: p.text }\n\n case 'inline-media': {\n return {\n inlineData: {\n mimeType: p.mimeType,\n data: p.data,\n },\n ...(p.mediaResolution !== undefined\n ? { mediaResolution: { level: mapMediaResolution(p.mediaResolution) } }\n : {}),\n }\n }\n\n case 'file-uri': {\n return {\n fileData: {\n mimeType: p.mimeType,\n fileUri: p.uri,\n },\n ...(p.mediaResolution !== undefined\n ? { mediaResolution: { level: mapMediaResolution(p.mediaResolution) } }\n : {}),\n }\n }\n\n default:\n return assertNever(p)\n }\n }\n\n const contents: GeminiContent[] = req.messages.map((msg) => ({\n role: msg.role === 'assistant' ? 'model' : 'user',\n parts: msg.parts.map(mapPart),\n }))\n\n // ------------------------------------------------------------------\n // 2. Build GenerateContentConfig\n // ------------------------------------------------------------------\n const genConfig = req.config\n const config: GeminiGenerateConfig = {}\n\n // System instruction\n if (req.system !== undefined) {\n config.systemInstruction = { parts: [{ text: req.system }] }\n }\n\n // Basic generation parameters (only include when defined)\n if (genConfig.temperature !== undefined) {\n config.temperature = genConfig.temperature\n }\n if (genConfig.topP !== undefined) {\n config.topP = genConfig.topP\n }\n if (genConfig.topK !== undefined) {\n config.topK = genConfig.topK\n }\n if (genConfig.maxOutputTokens !== undefined) {\n config.maxOutputTokens = genConfig.maxOutputTokens\n }\n if (genConfig.stopSequences !== undefined) {\n config.stopSequences = genConfig.stopSequences\n }\n\n // Service tier (FLEX or STANDARD)\n // Real SDK: GenerateContentConfig.serviceTier = ServiceTier enum (\"flex\"|\"standard\")\n const explicit = (genConfig as { serviceTier?: 'flex' | 'standard' }).serviceTier\n const supported = req.modelDescriptor?.capabilities?.serviceTiers\n if (explicit !== undefined) {\n // caller explicitly chose a tier — reject if the model can't honour it\n if (\n req.modelDescriptor !== undefined &&\n (supported === undefined || !supported.includes(explicit))\n ) {\n throw new LlmError(\n `serviceTier \"${explicit}\" is not supported for model \"${model}\".`,\n { kind: 'bad_request', retryable: false },\n )\n }\n config.serviceTier = explicit\n } else {\n // no explicit choice — default to flex only when the model supports it\n if (req.modelDescriptor === undefined) {\n config.serviceTier = 'flex'\n } else if (supported?.includes('flex') === true) {\n config.serviceTier = 'flex'\n }\n // otherwise omit serviceTier entirely\n }\n\n // ------------------------------------------------------------------\n // 3. Reasoning → thinkingConfig\n // ------------------------------------------------------------------\n const reasoning = genConfig.reasoning\n if (reasoning !== undefined) {\n const reasoningApi = req.modelDescriptor?.capabilities?.reasoningApi\n\n if (reasoning.effort !== undefined && reasoning.budgetTokens !== undefined) {\n throw new LlmError(\n `Provide either reasoning.effort or reasoning.budgetTokens, not both, for model \"${model}\".`,\n { kind: 'bad_request', retryable: false },\n )\n }\n\n if (reasoningApi === 'budget') {\n // gemini-2.5* → thinkingBudget\n const budget =\n reasoning.budgetTokens !== undefined\n ? reasoning.budgetTokens\n : reasoning.effort !== undefined\n ? EFFORT_BUDGET[reasoning.effort]\n : undefined\n\n config.thinkingConfig = {\n ...(budget !== undefined ? { thinkingBudget: budget } : {}),\n ...(reasoning.includeThoughts === true ? { includeThoughts: true } : {}),\n }\n } else if (reasoningApi === 'level') {\n // gemini-3.* → thinkingLevel\n if (reasoning.budgetTokens !== undefined) {\n throw new LlmError(\n `reasoning.budgetTokens is not supported for model \"${model}\" (it uses thinkingLevel, not thinkingBudget); use reasoning.effort instead.`,\n { kind: 'bad_request', retryable: false },\n )\n }\n\n // Real SDK ThinkingLevel enum: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MINIMAL\"\n let thinkingLevel: string | undefined\n if (reasoning.effort !== undefined) {\n switch (reasoning.effort) {\n case 'none':\n thinkingLevel = 'MINIMAL'\n break\n case 'low':\n thinkingLevel = 'LOW'\n break\n case 'medium':\n thinkingLevel = 'MEDIUM'\n break\n case 'high':\n thinkingLevel = 'HIGH'\n break\n default:\n assertNever(reasoning.effort)\n }\n }\n\n config.thinkingConfig = {\n ...(thinkingLevel !== undefined ? { thinkingLevel } : {}),\n ...(reasoning.includeThoughts === true ? { includeThoughts: true } : {}),\n }\n } else {\n throw new LlmError(\n `Model \"${model}\" does not support reasoning/thinkingConfig.`,\n { kind: 'bad_request', retryable: false },\n )\n }\n }\n\n // ------------------------------------------------------------------\n // 4. Structured output → responseMimeType + responseSchema\n // ------------------------------------------------------------------\n const structuredOutputRequested = req.outputJsonSchema !== undefined\n if (structuredOutputRequested) {\n const nativeStructuredOutput =\n req.modelDescriptor?.capabilities?.nativeStructuredOutput !== false\n\n if (nativeStructuredOutput) {\n config.responseMimeType = 'application/json'\n config.responseSchema = req.outputJsonSchema as NonNullable<\n GeminiGenerateConfig['responseSchema']\n >\n }\n }\n\n // ------------------------------------------------------------------\n // 5. providerOptions.google → spread verbatim (last, caller wins)\n // ------------------------------------------------------------------\n const googleOpts = genConfig.providerOptions?.['google']\n if (\n googleOpts !== undefined &&\n typeof googleOpts === 'object' &&\n googleOpts !== null\n ) {\n Object.assign(config, googleOpts)\n }\n\n // ------------------------------------------------------------------\n // 5a. Re-assert serviceTier validity AFTER providerOptions merge.\n // providerOptions.google is a last-write-wins escape hatch; without\n // this re-check a caller can inject an arbitrary serviceTier string\n // that bypasses the earlier validation.\n // ------------------------------------------------------------------\n const mergedServiceTier = (config as { serviceTier?: string }).serviceTier\n if (mergedServiceTier !== undefined && req.modelDescriptor !== undefined) {\n const supportedAfterMerge = req.modelDescriptor.capabilities?.serviceTiers\n if (\n supportedAfterMerge === undefined ||\n !supportedAfterMerge.includes(mergedServiceTier as 'flex' | 'standard')\n ) {\n throw new LlmError(\n `serviceTier \"${mergedServiceTier}\" is not supported for model \"${model}\".`,\n { kind: 'bad_request', retryable: false },\n )\n }\n }\n\n // ------------------------------------------------------------------\n // 5c. Re-assert fixed-sampling invariant AFTER providerOptions merge.\n // providerOptions.google is a last-write-wins escape hatch, but on\n // Gemini 3.x (sampling: 'fixed') the API rejects temperature/topP/topK\n // unconditionally. Throw so the caller knows they sent bad config.\n // ------------------------------------------------------------------\n if (req.modelDescriptor?.capabilities?.sampling === 'fixed') {\n const offendingSampling: string[] = []\n if ('temperature' in config) {\n offendingSampling.push('temperature')\n }\n if ('topP' in config) {\n offendingSampling.push('topP')\n }\n if ('topK' in config) {\n offendingSampling.push('topK')\n }\n if (offendingSampling.length > 0) {\n throw new LlmError(\n `Sampling parameters [${offendingSampling.join(\n ', ',\n )}] are not supported for model \"${model}\" (fixed sampling); they were supplied via providerOptions.google.`,\n { kind: 'bad_request', retryable: false },\n )\n }\n }\n\n // ------------------------------------------------------------------\n // 5b. Grounding ↔ structured-output conflict guard\n // Tools arrive via providerOptions.google; check after the merge.\n // ------------------------------------------------------------------\n const configAsAny = config as { tools?: unknown[] }\n const groundingRequested =\n Array.isArray(configAsAny.tools) &&\n configAsAny.tools.some((t): boolean => {\n if (t !== null && typeof t === 'object') {\n const tool = t as Record<string, unknown>\n return 'googleSearch' in tool || 'googleSearchRetrieval' in tool\n }\n return false\n })\n\n if (groundingRequested && structuredOutputRequested) {\n throw new LlmError(\n 'Grounding (googleSearch) cannot be combined with structured output (output.jsonSchema) on Gemini; choose one.',\n { kind: 'bad_request', retryable: false },\n )\n }\n\n // ------------------------------------------------------------------\n // 6. AbortSignal passthrough + FIX A-2: client-side flex ceiling\n //\n // FIX A-2 belt-and-suspenders: @google/genai issue #1277 — on some SDK\n // versions httpOptions.timeout is a no-op for generateContent. On the\n // flex-default path (flex tier, no explicit timeoutMs) the engine arms NO\n // AbortSignal; relying solely on httpOptions.timeout risks a silent hang.\n // We arm our own AbortController here and combine it with any incoming\n // signal so WE enforce the ceiling regardless of the SDK bug.\n //\n // Flex and standard default paths need this extra timer when timeoutMs is\n // absent. When timeoutMs IS set the engine already arms a hard AbortSignal\n // at exactly timeoutMs.\n //\n // Abort reason uses DOMException with name 'TimeoutError' so classifyError\n // maps the resulting LlmError to kind:'timeout' (retryable:true), matching\n // how the rest of the codebase surfaces timeout errors.\n //\n // AbortSignal.any requires Node ≥ 20.3; our engine floor is Node ≥ 20,\n // so this is always available in supported environments.\n //\n // Real SDK: GenerateContentConfig.abortSignal (in config, NOT in params)\n // ------------------------------------------------------------------\n let tierTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n const clearTierTimeout = (): void => {\n if (tierTimeoutHandle !== undefined) {\n clearTimeout(tierTimeoutHandle)\n tierTimeoutHandle = undefined\n }\n }\n\n const applyTierTimeout = (tier: string | undefined): void => {\n clearTierTimeout()\n delete config.abortSignal\n\n const defaultTimeoutMs =\n genConfig.timeoutMs === undefined\n ? tier === 'flex'\n ? FLEX_DEFAULT_TIMEOUT_MS\n : tier === 'standard'\n ? STANDARD_DEFAULT_TIMEOUT_MS\n : undefined\n : undefined\n\n if (defaultTimeoutMs !== undefined) {\n const tierController = new AbortController()\n const tierLabel = tier === 'standard' ? 'Standard' : 'Flex'\n const timeoutReason = new DOMException(\n `${tierLabel} timeout: call exceeded ${defaultTimeoutMs}ms client-side ceiling` +\n ' (@google/genai #1277 belt-and-suspenders)',\n 'TimeoutError',\n )\n tierTimeoutHandle = setTimeout(() => {\n tierController.abort(timeoutReason)\n }, defaultTimeoutMs)\n config.abortSignal =\n ctx.signal !== undefined\n ? AbortSignal.any([tierController.signal, ctx.signal])\n : tierController.signal\n } else if (ctx.signal !== undefined) {\n config.abortSignal = ctx.signal\n }\n }\n\n applyTierTimeout(config.serviceTier)\n\n // ------------------------------------------------------------------\n // 7. Transport timeout — set httpOptions.timeout so the @google/genai\n // HTTP transport does NOT preempt the AbortSignal hard ceiling.\n //\n // Policy (precedence, highest first):\n // 1. Caller-supplied httpOptions via providerOptions.google.httpOptions\n // WIN unconditionally; any extra fields are also preserved.\n // 2. timeoutMs is set → computed transport timeout = timeoutMs +\n // TRANSPORT_TIMEOUT_BUFFER_MS so the engine's AbortSignal (hard\n // ceiling at timeoutMs) always fires before the SDK transport timer.\n // 3. serviceTier 'flex', no timeoutMs → FLEX_DEFAULT_TIMEOUT_MS (no\n // buffer; there is no engine AbortSignal deadline in this case).\n // 4. serviceTier 'standard', no timeoutMs → STANDARD_DEFAULT_TIMEOUT_MS.\n //\n // Merge order: computed timeout first, caller's httpOptions spread on\n // top so caller values always win. Only assign config.httpOptions when\n // the merged object is non-empty (exactOptionalPropertyTypes-safe).\n // ------------------------------------------------------------------\n\n // Capture caller-supplied httpOptions BEFORE we overwrite.\n // These arrived via Object.assign(config, googleOpts) above and may\n // include a caller timeout and/or other httpOptions fields.\n const callerHttpOptions = config.httpOptions as Record<string, unknown> | undefined\n\n const computedTimeoutMs: number | undefined =\n genConfig.timeoutMs !== undefined\n ? genConfig.timeoutMs + TRANSPORT_TIMEOUT_BUFFER_MS\n : config.serviceTier === 'flex'\n ? FLEX_DEFAULT_TIMEOUT_MS\n : config.serviceTier === 'standard'\n ? STANDARD_DEFAULT_TIMEOUT_MS\n : undefined\n\n // Merge: our computed timeout is the base; caller wins on top.\n const mergedHttpOptions: Record<string, unknown> = {\n ...(computedTimeoutMs !== undefined ? { timeout: computedTimeoutMs } : {}),\n ...callerHttpOptions,\n }\n\n if (Object.keys(mergedHttpOptions).length > 0) {\n config.httpOptions = mergedHttpOptions\n }\n\n // ------------------------------------------------------------------\n // 8b. Client construction + SDK call — both inside the classifier\n // so that ANY failure in run() (including a bad auth constructor)\n // is rethrown as a typed LlmError(provider:'google').\n // ------------------------------------------------------------------\n let response: GeminiResponseShape\n let servedServiceTier = config.serviceTier\n const dispatch = async (): Promise<GeminiResponseShape> => {\n const dispatchConfig: GeminiGenerateConfig = {\n ...config,\n ...(config.httpOptions !== undefined\n ? { httpOptions: { ...config.httpOptions } }\n : {}),\n }\n const params = {\n model,\n contents,\n config: dispatchConfig,\n }\n const buildClient = opts?._clientFactory ?? buildGoogleClient\n const client: GeminiClientLike =\n opts?.client !== undefined ? opts.client : await buildClient(ctx.auth)\n ctx.logger.debug(\n {\n model,\n configKeys: Object.keys(dispatchConfig),\n serviceTier: dispatchConfig.serviceTier,\n },\n 'llm.adapter.dispatch',\n )\n return client.models.generateContent(params)\n }\n try {\n response = await dispatch()\n } catch (rawErr) {\n // Classify SDK errors → LlmError\n const classified = classifyError(rawErr)\n const typed = new LlmError(classified.message, {\n kind: classified.kind,\n retryable: classified.retryable,\n ...(classified.httpStatus !== undefined\n ? { httpStatus: classified.httpStatus }\n : {}),\n ...(classified.retryAfterMs !== undefined\n ? { retryAfterMs: classified.retryAfterMs }\n : {}),\n provider: 'google',\n cause: classified.cause ?? rawErr,\n ...(servedServiceTier !== undefined ? { servedServiceTier } : {}),\n })\n\n if (\n config.serviceTier === 'flex' &&\n genConfig.flexFallback !== false &&\n isGeminiCapacityError(typed)\n ) {\n config.serviceTier = 'standard'\n servedServiceTier = 'standard'\n const fallbackTimeout =\n genConfig.timeoutMs !== undefined\n ? genConfig.timeoutMs + TRANSPORT_TIMEOUT_BUFFER_MS\n : STANDARD_DEFAULT_TIMEOUT_MS\n config.httpOptions = {\n timeout: fallbackTimeout,\n ...callerHttpOptions,\n }\n applyTierTimeout('standard')\n try {\n response = await dispatch()\n } catch (fallbackRawErr) {\n const fallbackClassified = classifyError(fallbackRawErr)\n throw new LlmError(fallbackClassified.message, {\n kind: fallbackClassified.kind,\n retryable: fallbackClassified.retryable,\n ...(fallbackClassified.httpStatus !== undefined\n ? { httpStatus: fallbackClassified.httpStatus }\n : {}),\n ...(fallbackClassified.retryAfterMs !== undefined\n ? { retryAfterMs: fallbackClassified.retryAfterMs }\n : {}),\n provider: 'google',\n cause: fallbackClassified.cause ?? fallbackRawErr,\n servedServiceTier: 'standard',\n })\n }\n } else {\n throw typed\n }\n } finally {\n // FIX A-2: always clear the tier timeout timer so it never leaks,\n // regardless of whether the call succeeded, threw, or was aborted.\n clearTierTimeout()\n }\n\n // ------------------------------------------------------------------\n // 8. Blocked response check\n // promptFeedback.blockReason set OR no candidates → content_filter\n // ------------------------------------------------------------------\n const hasBlockReason = response.promptFeedback?.blockReason !== undefined\n const hasCandidates =\n response.candidates !== undefined && response.candidates.length > 0\n\n if (hasBlockReason || !hasCandidates) {\n const reason = response.promptFeedback?.blockReason ?? 'NO_CANDIDATES'\n throw new LlmError(`Gemini response blocked: ${reason}`, {\n kind: 'content_filter',\n retryable: false,\n provider: 'google',\n })\n }\n\n // ------------------------------------------------------------------\n // 9. Map response\n // ------------------------------------------------------------------\n const candidates = response.candidates\n if (candidates === undefined || candidates.length === 0) {\n throw new LlmError('Gemini response blocked: NO_CANDIDATES', {\n kind: 'content_filter',\n retryable: false,\n provider: 'google',\n })\n }\n const candidate = candidates[0]\n if (candidate === undefined) {\n throw new LlmError('Gemini response blocked: NO_CANDIDATES', {\n kind: 'content_filter',\n retryable: false,\n provider: 'google',\n })\n }\n const parts = candidate.content?.parts ?? []\n\n // Separate thought parts from text parts.\n const textParts: string[] = []\n const thoughtParts: string[] = []\n\n for (const part of parts) {\n if (part.text !== undefined) {\n if (part.thought === true) {\n thoughtParts.push(part.text)\n } else {\n textParts.push(part.text)\n }\n }\n }\n\n const text = textParts.join('')\n const reasoningText = thoughtParts.length > 0 ? thoughtParts.join('') : undefined\n\n // Parse structured output (JSON text → rawStructured).\n let rawStructured: unknown\n if (structuredOutputRequested && text.length > 0) {\n try {\n rawStructured = JSON.parse(text)\n } catch {\n // Core reports outputParsed:false; callers own validation/retry policy.\n }\n }\n\n // ------------------------------------------------------------------\n // 10. Build AdapterResult\n // ------------------------------------------------------------------\n const usage = mapUsage(response.usageMetadata)\n const finishReason = mapFinishReason(candidate.finishReason)\n\n const result: AdapterResult = {\n model,\n usage,\n warnings,\n ...(servedServiceTier !== undefined ? { servedServiceTier } : {}),\n ...(text.length > 0 ? { text } : {}),\n ...(reasoningText !== undefined ? { reasoningText } : {}),\n ...(rawStructured !== undefined ? { rawStructured } : {}),\n ...(finishReason !== undefined ? { finishReason } : {}),\n ...(response.modelVersion !== undefined\n ? { modelVersion: response.modelVersion }\n : {}),\n ...(response.responseId !== undefined ? { responseId: response.responseId } : {}),\n // Build providerMetadata — merge promptFeedback + groundingMetadata when present.\n ...((): { providerMetadata: JsonValue } | Record<string, never> => {\n const pf = response.promptFeedback\n const gm = candidate.groundingMetadata\n if (pf === undefined && gm === undefined) return {}\n const meta: { [k: string]: JsonValue } = {}\n if (pf !== undefined) {\n meta['promptFeedback'] = pf as unknown as JsonValue\n }\n if (gm !== undefined) {\n meta['groundingMetadata'] = gm as unknown as JsonValue\n }\n return { providerMetadata: meta as JsonValue }\n })(),\n }\n\n return result\n },\n }\n}\n","/**\n * GoogleFileStore — thin wrapper over the Gemini File API.\n *\n * Handles upload + polling until ACTIVE, and tracked deletion.\n * Injectable sleep and client for tests (no network required).\n *\n * @module\n */\n\nimport type { AuthMaterial, Logger } from '@gullabs/core'\nimport { LlmError, classifyError, redactSecrets } from '@gullabs/core'\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** A handle to a file stored in the Gemini File API. */\nexport interface GoogleFileHandle {\n /** Resource name, e.g. \"files/abc123\". */\n name: string\n /** URI to pass as FileUriPart.uri in an LlmRequest. */\n uri: string\n mimeType: string\n /** Provider auto-deletes ~48 h after upload. Absent when not returned. */\n expiresAt?: Date\n}\n\n/**\n * Minimal structural interface for the Gemini Files client surface we use.\n * Satisfied by the real ai.files object or a test fake.\n */\nexport interface GeminiFilesClientLike {\n upload(params: {\n file: Uint8Array | Blob\n config?: { mimeType?: string; displayName?: string }\n }): Promise<{\n name?: string\n uri?: string\n mimeType?: string\n state?: string\n expirationTime?: string\n }>\n get(params: { name: string }): Promise<{\n name?: string\n uri?: string\n mimeType?: string\n state?: string\n expirationTime?: string\n }>\n delete(params: { name: string }): Promise<void>\n}\n\nexport interface GoogleFileStoreOptions {\n auth: AuthMaterial\n /** Injectable client for tests; skips SDK import when provided. */\n client?: GeminiFilesClientLike\n /** Called on delete failures instead of rethrowing. Default: console.error. */\n onDeleteError?: (name: string, err: unknown) => void\n /** Optional structured logger. When provided, routes delete failures to logger.error. */\n logger?: Logger\n poll?: {\n /** Delay between state polls. Default: 3000 ms. */\n intervalMs?: number\n /** Max time to wait for ACTIVE. Default: 300 000 ms (5 min). */\n timeoutMs?: number\n }\n /** Injectable sleep for tests. Default: real setTimeout. */\n sleep?: (ms: number) => Promise<void>\n /** Injectable clock for deterministic tests. Default: `Date.now`. */\n now?: () => number\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_INTERVAL_MS = 3_000\nconst DEFAULT_TIMEOUT_MS = 300_000\n\nconst realSleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms))\n\nasync function buildFilesClient(auth: AuthMaterial): Promise<GeminiFilesClientLike> {\n const { GoogleGenAI } = await import('@google/genai')\n\n const ai = new GoogleGenAI({ apiKey: auth.apiKey })\n\n return {\n async upload(params) {\n // Real SDK accepts string | Blob; convert Uint8Array → Blob.\n const fileArg: Blob =\n params.file instanceof Uint8Array\n ? new Blob(\n [params.file],\n params.config?.mimeType !== undefined && params.config.mimeType.length > 0\n ? { type: params.config.mimeType }\n : {},\n )\n : params.file\n\n const result = await (\n ai.files.upload as (p: unknown) => Promise<{\n name?: string\n uri?: string\n mimeType?: string\n state?: string\n expirationTime?: string\n }>\n )({\n file: fileArg,\n ...(params.config !== undefined ? { config: params.config } : {}),\n })\n return result\n },\n\n async get(params) {\n const result = await (\n ai.files.get as (p: unknown) => Promise<{\n name?: string\n uri?: string\n mimeType?: string\n state?: string\n expirationTime?: string\n }>\n )(params)\n return result\n },\n\n async delete(params) {\n await (ai.files.delete as (p: unknown) => Promise<unknown>)(params)\n },\n }\n}\n\ntype FileResp = {\n name?: string\n uri?: string\n mimeType?: string\n state?: string\n expirationTime?: string\n}\n\nfunction makeHandle(\n resp: FileResp,\n fallback: { name: string; uri: string; mimeType: string },\n): GoogleFileHandle {\n const et = resp.expirationTime\n return {\n name: resp.name ?? fallback.name,\n uri: resp.uri ?? fallback.uri,\n mimeType: resp.mimeType ?? fallback.mimeType,\n ...(et !== undefined && et.length > 0 ? { expiresAt: new Date(et) } : {}),\n }\n}\n\n// ---------------------------------------------------------------------------\n// GoogleFileStore\n// ---------------------------------------------------------------------------\n\n/**\n * **Auth snapshot note:** this store captures the `AuthMaterial` at construction\n * time and memoizes a single SDK client from it (`clientPromise`). This is\n * correct and sufficient for static API keys. If refreshable credentials\n * (short-lived OAuth/STS tokens) are added in the future, this memoization is\n * the seam that will need rework: the cached client would hold stale credentials\n * for the lifetime of a long-lived store instance. At that point, the store\n * will need to either rebuild the client on each operation or accept a\n * credential-resolver callback rather than a plain `AuthMaterial` value.\n * See ADR-020 in DECISIONS.md.\n */\nexport class GoogleFileStore {\n private readonly auth: AuthMaterial\n private readonly clientOverride: GeminiFilesClientLike | undefined\n private readonly onDeleteError: (name: string, err: unknown) => void\n private readonly logger: Logger | undefined\n private readonly intervalMs: number\n private readonly timeoutMs: number\n private readonly sleep: (ms: number) => Promise<void>\n private readonly now: () => number\n /** Memoised client promise — built at most once per store instance. */\n private clientPromise: Promise<GeminiFilesClientLike> | undefined\n\n constructor(opts: GoogleFileStoreOptions) {\n this.auth = opts.auth\n this.clientOverride = opts.client\n this.logger = opts.logger\n this.onDeleteError =\n opts.onDeleteError ??\n ((name, err) => {\n if (this.logger !== undefined) {\n this.logger.error(\n { name, error: redactSecrets(classifyError(err).message) },\n 'gemini.file.delete.failed',\n )\n } else {\n console.error(\n `[GoogleFileStore] delete failed for \"${name}\":`,\n redactSecrets(classifyError(err).message),\n )\n }\n })\n this.intervalMs = opts.poll?.intervalMs ?? DEFAULT_INTERVAL_MS\n this.timeoutMs = opts.poll?.timeoutMs ?? DEFAULT_TIMEOUT_MS\n this.sleep = opts.sleep ?? realSleep\n this.now = opts.now ?? (() => Date.now())\n }\n\n private getClient(): Promise<GeminiFilesClientLike> {\n if (this.clientOverride !== undefined) return Promise.resolve(this.clientOverride)\n if (this.clientPromise === undefined) {\n this.clientPromise = buildFilesClient(this.auth)\n }\n return this.clientPromise\n }\n\n /**\n * Upload bytes to the Gemini File API and wait until the file is ACTIVE.\n *\n * @param source - Raw bytes or Blob.\n * @param mimeType - IANA media type, e.g. `\"image/png\"`.\n * @param opts - Optional display name.\n */\n async upload(\n source: Uint8Array | Blob,\n mimeType: string,\n opts?: { displayName?: string; signal?: AbortSignal },\n ): Promise<GoogleFileHandle> {\n const signal = opts?.signal\n const client = await this.getClient()\n\n let uploadResp: {\n name?: string\n uri?: string\n mimeType?: string\n state?: string\n expirationTime?: string\n }\n try {\n uploadResp = await client.upload({\n file: source,\n config: {\n mimeType,\n ...(opts?.displayName !== undefined ? { displayName: opts.displayName } : {}),\n },\n })\n } catch (e) {\n throw classifyError(e)\n }\n\n const { name, uri } = uploadResp\n\n if (\n name === undefined ||\n name.length === 0 ||\n uri === undefined ||\n uri.length === 0\n ) {\n throw new LlmError('File upload response missing required fields (name or uri)', {\n kind: 'bad_request',\n retryable: false,\n })\n }\n\n const fallback = { name, uri, mimeType }\n\n if (uploadResp.state === 'ACTIVE') {\n return makeHandle(uploadResp, fallback)\n }\n\n if (uploadResp.state === 'FAILED') {\n throw new LlmError('File processing failed immediately after upload', {\n kind: 'bad_request',\n retryable: false,\n })\n }\n\n // PROCESSING (or unknown) — poll until ACTIVE or timeout.\n const deadline = this.now() + this.timeoutMs\n\n // Pre-flight: if the signal is already aborted, throw before creating any\n // promise so we never produce an unhandled rejection.\n if (signal?.aborted === true) {\n throw new LlmError('File upload polling aborted', {\n kind: 'aborted',\n retryable: false,\n })\n }\n\n // Build an abort-race promise so future aborts wake up the sleep race\n // immediately rather than waiting the full interval. Created only when\n // the signal is NOT already aborted (guard above handles that case).\n const abortRacePromise: Promise<never> | undefined =\n signal !== undefined\n ? new Promise<never>((_, reject) => {\n signal.addEventListener(\n 'abort',\n () => {\n reject(\n new LlmError('File upload polling aborted', {\n kind: 'aborted',\n retryable: false,\n }),\n )\n },\n { once: true },\n )\n })\n : undefined\n\n for (;;) {\n if (this.now() >= deadline) {\n throw new LlmError('Timed out waiting for uploaded file to become ACTIVE', {\n kind: 'timeout',\n retryable: true,\n })\n }\n\n // Sleep — race against the abort promise so we wake up immediately\n // when the signal fires rather than waiting the full interval.\n const sleepCall = this.sleep(this.intervalMs)\n await (abortRacePromise !== undefined\n ? Promise.race([sleepCall, abortRacePromise])\n : sleepCall)\n\n let pollResp: {\n name?: string\n uri?: string\n mimeType?: string\n state?: string\n expirationTime?: string\n }\n try {\n pollResp = await client.get({ name })\n } catch (e) {\n throw classifyError(e)\n }\n\n // Also guard here: the signal may have fired during client.get()\n // before we looped back to the sleep race.\n // Cast needed: TS 5.6 persists readonly-property narrowing across awaits,\n // making it think `aborted` is still `false | undefined` after the preflight.\n if ((signal?.aborted as boolean | undefined) === true) {\n throw new LlmError('File upload polling aborted', {\n kind: 'aborted',\n retryable: false,\n })\n }\n\n if (pollResp.state === 'ACTIVE') {\n return makeHandle(pollResp, fallback)\n }\n\n if (pollResp.state === 'FAILED') {\n throw new LlmError('File processing failed during polling', {\n kind: 'bad_request',\n retryable: false,\n })\n }\n // PROCESSING — continue loop\n }\n }\n\n /**\n * Delete a single uploaded file.\n * Errors are forwarded to `onDeleteError` and NOT rethrown.\n */\n async delete(handle: GoogleFileHandle): Promise<void> {\n try {\n const client = await this.getClient()\n await client.delete({ name: handle.name })\n } catch (err) {\n this.onDeleteError(handle.name, err)\n }\n }\n\n /**\n * Delete multiple files.\n * Each failure is individually forwarded to `onDeleteError`; none are thrown.\n */\n async deleteAll(handles: GoogleFileHandle[]): Promise<void> {\n await Promise.allSettled(handles.map((h) => this.delete(h)))\n }\n}\n","/**\n * GoogleCacheStore — thin wrapper over the Gemini Context Cache API.\n *\n * Manages create / get-or-create / refresh / delete lifecycle for cached\n * contents. In-memory cache entries are PROCESS-SCOPED; they are not shared\n * across processes, workers, or restarts.\n *\n * Injectable client and `now` function keep tests free of network and clock.\n *\n * @module\n */\n\nimport type { AuthMaterial, Logger } from '@gullabs/core'\nimport { LlmError, classifyError, redactSecrets } from '@gullabs/core'\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * A handle to a cached content resource in the Gemini Context Cache API.\n *\n * Pass `cacheName` as `providerOptions.google.cachedContent` in LlmRequest.\n */\nexport interface GoogleCacheHandle {\n /** Resource name, e.g. \"cachedContents/xyz\". */\n cacheName: string\n /**\n * Local view of when the cache expires (server-authoritative; computed from\n * ttl or parsed from the server response `expireTime`).\n */\n expiresAt: Date\n /** Caches are model-bound; never use a handle with a different model. */\n model: string\n}\n\n/** Key used to look up or create an entry in the in-process cache map. */\nexport interface CacheKey {\n model: string\n /** Caller-chosen stable identifier, e.g. a hash of the cached content. */\n stableKey: string\n}\n\n/**\n * Minimal structural interface for the Gemini Caches client surface we use.\n * Satisfied by the real `ai.caches` object or a test fake.\n */\nexport interface GeminiCachesClientLike {\n create(params: {\n model: string\n config: {\n contents?: unknown\n systemInstruction?: unknown\n ttl?: string\n displayName?: string\n }\n }): Promise<{ name?: string; model?: string; expireTime?: string }>\n\n update(params: {\n name: string\n config: { ttl?: string; expireTime?: string }\n }): Promise<{ name?: string; expireTime?: string }>\n\n delete(params: { name: string }): Promise<unknown>\n}\n\nexport interface GoogleCacheStoreOptions {\n auth: AuthMaterial\n /** Injectable client for tests; skips SDK import when provided. */\n client?: GeminiCachesClientLike\n /**\n * When true, concurrent `getOrCreate` calls for the same key share one\n * in-flight create. Default false.\n */\n coalesce?: boolean\n /**\n * Subtracted from the local expiry so we stop using a cache slightly before\n * the server evicts it. Default 30 s.\n */\n expirySkewSeconds?: number\n /** Called on delete failures instead of rethrowing. */\n onDeleteError?: (cacheName: string, err: unknown) => void\n /** Optional structured logger. When provided, routes delete failures to logger.error. */\n logger?: Logger\n /** Injectable clock for deterministic tests. Default: `Date.now`. */\n now?: () => number\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_SKEW_SECONDS = 30\nconst DEFAULT_EXTENSION_SECONDS = 3600\n\nasync function buildCachesClient(auth: AuthMaterial): Promise<GeminiCachesClientLike> {\n const { GoogleGenAI } = await import('@google/genai')\n\n const ai = new GoogleGenAI({ apiKey: auth.apiKey })\n\n return {\n async create(params) {\n const result = await (\n ai.caches.create as (p: unknown) => Promise<{\n name?: string\n model?: string\n expireTime?: string\n }>\n )(params)\n return result\n },\n\n async update(params) {\n const result = await (\n ai.caches.update as (p: unknown) => Promise<{\n name?: string\n expireTime?: string\n }>\n )(params)\n return result\n },\n\n async delete(params) {\n await (ai.caches.delete as (p: unknown) => Promise<unknown>)(params)\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// GoogleCacheStore\n// ---------------------------------------------------------------------------\n\ninterface EntryRecord {\n handle: GoogleCacheHandle\n /** Original ttlSeconds used to create this entry; used as extension default. */\n ttlSeconds: number\n}\n\n/**\n * Process-scoped helper for the Gemini Context Cache API.\n *\n * NOTE: `getOrCreate` reuse is PROCESS-SCOPED only. Entries survive only for\n * the lifetime of this `GoogleCacheStore` instance. Across restarts, new\n * caches will be created (and old ones will be server-evicted after their TTL).\n *\n * **Auth snapshot note:** this store captures the `AuthMaterial` at construction\n * time and memoizes a single SDK client from it (`clientPromise`). This is\n * correct and sufficient for static API keys. If refreshable credentials\n * (short-lived OAuth/STS tokens) are added in the future, this memoization is\n * the seam that will need rework: the cached client would hold stale credentials\n * for the lifetime of a long-lived store instance. At that point, the store\n * will need to either rebuild the client on each operation or accept a\n * credential-resolver callback rather than a plain `AuthMaterial` value.\n * See ADR-020 in DECISIONS.md.\n */\nexport class GoogleCacheStore {\n private readonly auth: AuthMaterial\n private readonly clientOverride: GeminiCachesClientLike | undefined\n private readonly coalesce: boolean\n private readonly skewMs: number\n private readonly onDeleteError: (cacheName: string, err: unknown) => void\n private readonly logger: Logger | undefined\n private readonly now: () => number\n\n /** Memoised client promise — built at most once per store instance. */\n private clientPromise: Promise<GeminiCachesClientLike> | undefined\n\n /** In-process cache of live handles, keyed by `${model}:${stableKey}`. */\n private readonly entries: Map<string, EntryRecord> = new Map()\n\n /** In-flight create promises when coalescing is enabled. */\n private readonly inflight: Map<string, Promise<GoogleCacheHandle>> = new Map()\n\n constructor(opts: GoogleCacheStoreOptions) {\n this.auth = opts.auth\n this.clientOverride = opts.client\n this.coalesce = opts.coalesce ?? false\n this.skewMs = (opts.expirySkewSeconds ?? DEFAULT_SKEW_SECONDS) * 1000\n this.logger = opts.logger\n this.onDeleteError =\n opts.onDeleteError ??\n ((cacheName, err) => {\n if (this.logger !== undefined) {\n this.logger.error(\n { name: cacheName, error: redactSecrets(classifyError(err).message) },\n 'gemini.cache.delete.failed',\n )\n } else {\n console.error(\n `[GoogleCacheStore] delete failed for \"${cacheName}\":`,\n redactSecrets(classifyError(err).message),\n )\n }\n })\n this.now = opts.now ?? (() => Date.now())\n }\n\n private getClient(): Promise<GeminiCachesClientLike> {\n if (this.clientOverride !== undefined) return Promise.resolve(this.clientOverride)\n if (this.clientPromise === undefined) {\n this.clientPromise = buildCachesClient(this.auth)\n }\n return this.clientPromise\n }\n\n private isLive(handle: GoogleCacheHandle): boolean {\n return handle.expiresAt.getTime() - this.skewMs > this.now()\n }\n\n /**\n * Create a new cached content resource.\n *\n * The `expiresAt` on the returned handle is computed from the server's\n * `expireTime` when available, with a local-clock fallback of\n * `now + ttlSeconds * 1000`.\n */\n async create(input: {\n model: string\n ttlSeconds: number\n contents?: unknown\n systemInstruction?: unknown\n displayName?: string\n }): Promise<GoogleCacheHandle> {\n const client = await this.getClient()\n\n const config: {\n ttl: string\n contents?: unknown\n systemInstruction?: unknown\n displayName?: string\n } = { ttl: `${input.ttlSeconds}s` }\n\n if (input.contents !== undefined) config.contents = input.contents\n if (input.systemInstruction !== undefined)\n config.systemInstruction = input.systemInstruction\n if (input.displayName !== undefined) config.displayName = input.displayName\n\n let resp: { name?: string; model?: string; expireTime?: string }\n try {\n resp = await client.create({ model: input.model, config })\n } catch (e) {\n throw classifyError(e)\n }\n\n if (resp.name === undefined || resp.name.length === 0) {\n throw new LlmError('Cache create response missing required field: name', {\n kind: 'bad_request',\n retryable: false,\n })\n }\n\n const fallbackExpiry = new Date(this.now() + input.ttlSeconds * 1000)\n const expiresAt =\n resp.expireTime !== undefined && resp.expireTime.length > 0\n ? new Date(resp.expireTime)\n : fallbackExpiry\n\n return {\n cacheName: resp.name,\n model: resp.model ?? input.model,\n expiresAt,\n }\n }\n\n /**\n * Return a live cached-content handle, creating one if none exists or the\n * cached entry has expired (accounting for skew).\n *\n * Reuse is PROCESS-SCOPED only — this store instance's in-memory map.\n *\n * When `coalesce` is enabled, concurrent calls for the same key share one\n * in-flight create.\n */\n async getOrCreate(\n key: CacheKey,\n factory: () => Promise<{\n ttlSeconds: number\n contents?: unknown\n systemInstruction?: unknown\n }>,\n ): Promise<GoogleCacheHandle> {\n const mapKey = `${key.model}:${key.stableKey}`\n\n // Return existing live entry if available.\n const existing = this.entries.get(mapKey)\n if (existing !== undefined && this.isLive(existing.handle)) {\n return existing.handle\n }\n\n // If coalescing, piggyback on an in-flight create for this key.\n if (this.coalesce) {\n const inFlight = this.inflight.get(mapKey)\n if (inFlight !== undefined) {\n return inFlight\n }\n }\n\n const doCreate = async (): Promise<GoogleCacheHandle> => {\n const factoryResult = await factory()\n const handle = await this.create({\n model: key.model,\n ttlSeconds: factoryResult.ttlSeconds,\n ...(factoryResult.contents !== undefined\n ? { contents: factoryResult.contents }\n : {}),\n ...(factoryResult.systemInstruction !== undefined\n ? { systemInstruction: factoryResult.systemInstruction }\n : {}),\n })\n this.entries.set(mapKey, { handle, ttlSeconds: factoryResult.ttlSeconds })\n return handle\n }\n\n if (this.coalesce) {\n const promise = doCreate().finally(() => {\n this.inflight.delete(mapKey)\n })\n this.inflight.set(mapKey, promise)\n return promise\n }\n\n return doCreate()\n }\n\n /**\n * Extend the cache TTL if it will expire within `thresholdSeconds`.\n *\n * Fail-open: if the update call throws, the original handle is returned\n * unchanged. This method NEVER throws.\n *\n * @param handle - Handle to potentially refresh.\n * @param opts.thresholdSeconds - Extend if expiry is within this many seconds.\n * Default 300.\n * @param opts.extensionSeconds - New TTL to set. Default: original TTL from\n * entries map, or 3600 s when the handle was not created via `getOrCreate`.\n */\n async refreshIfExpiringSoon(\n handle: GoogleCacheHandle,\n opts?: { thresholdSeconds?: number; extensionSeconds?: number },\n ): Promise<GoogleCacheHandle> {\n const thresholdMs = (opts?.thresholdSeconds ?? 300) * 1000\n\n if (handle.expiresAt.getTime() - this.now() > thresholdMs) {\n // Not near expiry — return unchanged.\n return handle\n }\n\n // Find the original ttl from the entries map for this handle.\n let extensionSeconds = opts?.extensionSeconds\n if (extensionSeconds === undefined) {\n for (const entry of this.entries.values()) {\n if (entry.handle.cacheName === handle.cacheName) {\n extensionSeconds = entry.ttlSeconds\n break\n }\n }\n extensionSeconds = extensionSeconds ?? DEFAULT_EXTENSION_SECONDS\n }\n\n try {\n const client = await this.getClient()\n const resp = await client.update({\n name: handle.cacheName,\n config: { ttl: `${extensionSeconds}s` },\n })\n\n const fallbackExpiry = new Date(this.now() + extensionSeconds * 1000)\n const newExpiresAt =\n resp.expireTime !== undefined && resp.expireTime.length > 0\n ? new Date(resp.expireTime)\n : fallbackExpiry\n\n const newHandle: GoogleCacheHandle = {\n cacheName: handle.cacheName,\n model: handle.model,\n expiresAt: newExpiresAt,\n }\n\n // Update the entries map entry if this handle is tracked.\n for (const [k, entry] of this.entries.entries()) {\n if (entry.handle.cacheName === handle.cacheName) {\n this.entries.set(k, { handle: newHandle, ttlSeconds: extensionSeconds })\n break\n }\n }\n\n return newHandle\n } catch {\n // Fail-open: return original handle unchanged.\n return handle\n }\n }\n\n /**\n * Delete a cached content resource.\n *\n * Errors are forwarded to `onDeleteError` and NOT rethrown.\n * The handle is removed from the in-process entries map regardless.\n */\n async delete(handle: GoogleCacheHandle): Promise<void> {\n // Always remove from local map, even if delete fails.\n for (const [k, entry] of this.entries.entries()) {\n if (entry.handle.cacheName === handle.cacheName) {\n this.entries.delete(k)\n break\n }\n }\n\n try {\n const client = await this.getClient()\n await client.delete({ name: handle.cacheName })\n } catch (err) {\n this.onDeleteError(handle.cacheName, err)\n }\n }\n}\n","/**\n * normalizeGroundingCitations — shape Gemini's raw groundingMetadata.groundingChunks\n * into a clean, deduplicated citation list.\n *\n * This is a caller-owned, optional post-processing convenience helper — NOT\n * request validation. It NEVER throws: any missing/malformed input yields `[]`.\n *\n * @module\n */\n\nexport interface Citation {\n url: string\n sourceName: string\n}\n\n/**\n * Derive a human-readable source name for a hostname, stripping a leading\n * `www.` label (e.g. `www.example.com` → `example.com`).\n */\nfunction hostnameFrom(url: URL): string {\n return url.hostname.startsWith('www.') ? url.hostname.slice(4) : url.hostname\n}\n\n/**\n * Shape a single grounding chunk into a `Citation`, or `undefined` if the\n * chunk is malformed (missing/invalid `web.uri`).\n *\n * Real SDK shape (`@google/genai` `GroundingChunk.web`): `{ uri, title, domain }`.\n * `title`, when present, is the human-readable page title and is preferred\n * over the hostname as `sourceName`; the hostname (parsed from `uri`) is the\n * robust fallback when `title` is absent.\n */\nfunction toCitation(chunk: unknown): Citation | undefined {\n if (chunk === null || typeof chunk !== 'object') return undefined\n\n const web = (chunk as Record<string, unknown>)['web']\n if (web === null || typeof web !== 'object') return undefined\n\n const uri = (web as Record<string, unknown>)['uri']\n if (typeof uri !== 'string' || uri.length === 0) return undefined\n\n let parsed: URL\n try {\n parsed = new URL(uri)\n } catch {\n return undefined\n }\n\n const title = (web as Record<string, unknown>)['title']\n const sourceName =\n typeof title === 'string' && title.length > 0 ? title : hostnameFrom(parsed)\n\n return { url: uri, sourceName }\n}\n\n/**\n * Shape Gemini's raw `groundingMetadata.groundingChunks` into a clean,\n * deduplicated citation list (deduplicated by URL, first-seen order).\n *\n * Accepts `unknown` since `groundingMetadata` arrives as raw JSON on\n * `providerMetadata` (see `docs/grounded-structured.md`). Never throws —\n * any missing/malformed top-level shape returns `[]`; a malformed individual\n * chunk is skipped rather than failing the whole array.\n */\nexport function normalizeGroundingCitations(groundingMetadata: unknown): Citation[] {\n if (groundingMetadata === null || typeof groundingMetadata !== 'object') return []\n\n const chunks = (groundingMetadata as Record<string, unknown>)['groundingChunks']\n if (!Array.isArray(chunks)) return []\n\n const seen = new Set<string>()\n const citations: Citation[] = []\n\n for (const chunk of chunks) {\n const citation = toCitation(chunk)\n if (citation === undefined) continue\n if (seen.has(citation.url)) continue\n seen.add(citation.url)\n citations.push(citation)\n }\n\n return citations\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -576,4 +576,28 @@ declare class GoogleCacheStore {
|
|
|
576
576
|
delete(handle: GoogleCacheHandle): Promise<void>;
|
|
577
577
|
}
|
|
578
578
|
|
|
579
|
-
|
|
579
|
+
/**
|
|
580
|
+
* normalizeGroundingCitations — shape Gemini's raw groundingMetadata.groundingChunks
|
|
581
|
+
* into a clean, deduplicated citation list.
|
|
582
|
+
*
|
|
583
|
+
* This is a caller-owned, optional post-processing convenience helper — NOT
|
|
584
|
+
* request validation. It NEVER throws: any missing/malformed input yields `[]`.
|
|
585
|
+
*
|
|
586
|
+
* @module
|
|
587
|
+
*/
|
|
588
|
+
interface Citation {
|
|
589
|
+
url: string;
|
|
590
|
+
sourceName: string;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Shape Gemini's raw `groundingMetadata.groundingChunks` into a clean,
|
|
594
|
+
* deduplicated citation list (deduplicated by URL, first-seen order).
|
|
595
|
+
*
|
|
596
|
+
* Accepts `unknown` since `groundingMetadata` arrives as raw JSON on
|
|
597
|
+
* `providerMetadata` (see `docs/grounded-structured.md`). Never throws —
|
|
598
|
+
* any missing/malformed top-level shape returns `[]`; a malformed individual
|
|
599
|
+
* chunk is skipped rather than failing the whole array.
|
|
600
|
+
*/
|
|
601
|
+
declare function normalizeGroundingCitations(groundingMetadata: unknown): Citation[];
|
|
602
|
+
|
|
603
|
+
export { type CacheKey, type Citation, FLEX_DEFAULT_TIMEOUT_MS, type GeminiAdapterOptions, type GeminiCachesClientLike, type GeminiClientLike, type GeminiFilesClientLike, type GoogleCacheHandle, GoogleCacheStore, type GoogleCacheStoreOptions, type GoogleFileHandle, GoogleFileStore, type GoogleFileStoreOptions, STANDARD_DEFAULT_TIMEOUT_MS, TRANSPORT_TIMEOUT_BUFFER_MS, buildGoogleClient, geminiAdapter, isGeminiCapacityError, normalizeGroundingCitations };
|