UNPKG

@ai-sdk/google

Version:
6,064 lines • 220 kB
import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE, combineHeaders, convertBase64ToUint8Array, convertJsonResponseToolStream, convertToBase64, createEventSourceResponseHandler, createJsonErrorResponseHandler, createJsonResponseHandler, createNullLanguageModelUsage, createProviderExecutedToolFactory, createProviderStreamError, createToolNameMapping, delay, detectMediaType, downloadBlob, generateId, getFromApi, getRuntimeEnvironmentUserAgent, getTopLevelMediaType, isAbortError, isCustomReasoning, isFullMediaType, isUrlSupported, lazySchema, mapReasoningToProviderBudget, mapReasoningToProviderEffort, parseProviderOptions, postJsonToApi, resolve, resolveFullMediaType, resolveProviderOptions, resolveProviderReference, secureJsonParse, serializeModelOptions, stringifyWithBigIntAndCycles, withUserAgentSuffix, zodSchema } from "@ai-sdk/provider-utils";
import { z } from "zod/v4";
import { InvalidArgumentError, UnsupportedFunctionalityError } from "@ai-sdk/provider";
//#region src/continue-google-stream.ts
/**
* Concatenates the event streams of a long decode response that spans several
* requests (Gemini 4 long decode).
*
* Events of the current stream are passed through unchanged. When an event
* signals that the per-request cap was reached while budget remains
* (`getContinuationToken` returns a token), the next request is sent with that
* token once the current stream ends, and its events are appended after a
* `continuation-start` marker. The marker orders per-response state changes
* with buffered events, including when the new stream is empty. If the
* continuation request fails, the error is emitted as a failed parse result
* and the stream ends.
*/
function continueGoogleStream({ stream, getContinuationToken, continueStream }) {
	let reader = stream.getReader();
	let pendingContinuationToken;
	return new ReadableStream({
		async pull(controller) {
			while (true) {
				const { done, value } = await reader.read();
				if (!done) {
					if (value.success) {
						const token = getContinuationToken(value.value);
						if (token != null) pendingContinuationToken = token;
					}
					controller.enqueue(value);
					return;
				}
				if (pendingContinuationToken == null) {
					controller.close();
					return;
				}
				const continuationToken = pendingContinuationToken;
				pendingContinuationToken = void 0;
				try {
					reader = (await continueStream(continuationToken)).getReader();
					controller.enqueue({ type: "continuation-start" });
					return;
				} catch (error) {
					controller.enqueue({
						success: false,
						error,
						rawValue: void 0
					});
					controller.close();
					return;
				}
			}
		},
		async cancel(reason) {
			await reader.cancel(reason);
		}
	});
}
//#endregion
//#region src/convert-google-usage.ts
function convertGoogleUsage(usage) {
	if (usage == null) return createNullLanguageModelUsage();
	const promptTokens = usage.promptTokenCount ?? 0;
	const candidatesTokens = usage.candidatesTokenCount ?? 0;
	const toolUsePromptTokens = usage.toolUsePromptTokenCount ?? 0;
	const cachedContentTokens = usage.cachedContentTokenCount ?? 0;
	const thoughtsTokens = usage.thoughtsTokenCount ?? 0;
	const inputTokens = promptTokens + toolUsePromptTokens;
	return {
		inputTokens: {
			total: inputTokens,
			noCache: inputTokens - cachedContentTokens,
			cacheRead: cachedContentTokens,
			cacheWrite: void 0
		},
		outputTokens: {
			total: candidatesTokens + thoughtsTokens,
			text: candidatesTokens,
			reasoning: thoughtsTokens
		},
		raw: usage
	};
}
/**
* Sums the usage metadata of two requests, e.g. the hops of a long decode
* response that was continued automatically. Non-numeric fields are taken
* from the later request.
*/
function addGoogleUsageMetadata(a, b) {
	if (a == null || b == null) return b ?? a;
	return {
		...a,
		...b,
		promptTokenCount: addCounts(a.promptTokenCount, b.promptTokenCount),
		candidatesTokenCount: addCounts(a.candidatesTokenCount, b.candidatesTokenCount),
		toolUsePromptTokenCount: addCounts(a.toolUsePromptTokenCount, b.toolUsePromptTokenCount),
		totalTokenCount: addCounts(a.totalTokenCount, b.totalTokenCount),
		cachedContentTokenCount: addCounts(a.cachedContentTokenCount, b.cachedContentTokenCount),
		thoughtsTokenCount: addCounts(a.thoughtsTokenCount, b.thoughtsTokenCount),
		promptTokensDetails: addTokenDetails(a.promptTokensDetails, b.promptTokensDetails),
		cacheTokensDetails: addTokenDetails(a.cacheTokensDetails, b.cacheTokensDetails),
		candidatesTokensDetails: addTokenDetails(a.candidatesTokensDetails, b.candidatesTokensDetails),
		toolUsePromptTokensDetails: addTokenDetails(a.toolUsePromptTokensDetails, b.toolUsePromptTokensDetails)
	};
}
function addCounts(a, b) {
	return a == null && b == null ? b ?? a : (a ?? 0) + (b ?? 0);
}
function addTokenDetails(a, b) {
	if (a == null || b == null) return b ?? a;
	const tokenCounts = /* @__PURE__ */ new Map();
	for (const { modality, tokenCount } of [...a, ...b]) tokenCounts.set(modality, (tokenCounts.get(modality) ?? 0) + tokenCount);
	return Array.from(tokenCounts, ([modality, tokenCount]) => ({
		modality,
		tokenCount
	}));
}
//#endregion
//#region src/google-prompt.ts
const googleVideoMetadataSchema = z.object({
	/** Start offset as a duration in seconds, e.g. `'3.5s'`. */
	startOffset: z.string().optional(),
	/** End offset as a duration in seconds, e.g. `'10s'`. */
	endOffset: z.string().optional(),
	/** Frames per second in the range (0, 24]. Defaults to 1. */
	fps: z.number().gt(0).max(24).optional()
});
//#endregion
//#region src/tool/code-execution.ts
const codeExecutionInputSchema = z.object({
	language: z.string().describe("The programming language of the code."),
	code: z.string().describe("The code to be executed.")
});
const codeExecutionOutputSchema = z.object({
	outcome: z.string().describe("The outcome of the execution (e.g., \"OUTCOME_OK\")."),
	output: z.string().describe("The output from the code execution.")
});
/**
* A tool that enables the model to generate and run Python code.
*
* @note Ensure the selected model supports Code Execution.
* Multi-tool usage with the code execution tool is typically compatible with Gemini >=2 models.
*
* @see https://ai.google.dev/gemini-api/docs/code-execution (Google AI)
* @see https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/code-execution-api (Vertex AI)
*/
const codeExecution = createProviderExecutedToolFactory({
	id: "google.code_execution",
	inputSchema: codeExecutionInputSchema,
	outputSchema: codeExecutionOutputSchema
});
//#endregion
//#region src/convert-to-google-messages.ts
/**
* Sentinel value Google documents for replaying functionCall parts whose
* original thoughtSignature is not available to the client.
*
* See https://ai.google.dev/gemini-api/docs/thought-signatures.
*/
const SKIP_THOUGHT_SIGNATURE_VALIDATOR = "skip_thought_signature_validator";
const dataUrlRegex = /^data:([^;,]+);base64,(.+)$/s;
function parseBase64DataUrl(value) {
	const match = dataUrlRegex.exec(value);
	if (match == null) return;
	return {
		mediaType: match[1],
		data: match[2]
	};
}
function convertUrlToolResultPart(url) {
	const parsedDataUrl = parseBase64DataUrl(url);
	if (parsedDataUrl == null) return;
	return { inlineData: {
		mimeType: parsedDataUrl.mediaType,
		data: parsedDataUrl.data
	} };
}
function containsJSONSchemaReference(value, seen = /* @__PURE__ */ new WeakSet()) {
	if (value === null || typeof value !== "object") return false;
	if (seen.has(value)) return false;
	seen.add(value);
	if (Array.isArray(value)) return value.some((item) => containsJSONSchemaReference(item, seen));
	return Object.entries(value).some(([key, nestedValue]) => key === "$ref" || containsJSONSchemaReference(nestedValue, seen));
}
function serializeFunctionResponseContent(value) {
	if (containsJSONSchemaReference(value)) return stringifyWithBigIntAndCycles(value);
	return containsNonJsonValue(value) ? secureJsonParse(stringifyWithBigIntAndCycles(value)) : value;
}
function containsNonJsonValue(value, ancestors = /* @__PURE__ */ new Set()) {
	if (typeof value === "bigint") return true;
	if (typeof value !== "object" || value === null) return false;
	if (typeof value.toJSON === "function") return true;
	if (ancestors.has(value)) return true;
	ancestors.add(value);
	try {
		return Object.values(value).some((item) => containsNonJsonValue(item, ancestors));
	} finally {
		ancestors.delete(value);
	}
}
function appendToolResultParts(parts, toolName, outputValue, toolCallId, includeFunctionCallIds = true, supportedUrls = {}) {
	const functionResponseParts = [];
	const responseTextParts = [];
	for (const contentPart of outputValue) switch (contentPart.type) {
		case "text":
			responseTextParts.push(contentPart.text);
			break;
		case "file":
			if (contentPart.data.type === "data") functionResponseParts.push({ inlineData: {
				mimeType: resolveFullMediaType({ part: contentPart }),
				data: convertToBase64(contentPart.data.data)
			} });
			else if (contentPart.data.type === "url") {
				const url = contentPart.data.url.toString();
				const convertedUrlPart = convertUrlToolResultPart(url);
				const supportedUrl = contentPart.data.url.protocol === "gs:" && contentPart.data.originalUrl != null ? contentPart.data.originalUrl : url;
				if (convertedUrlPart != null) functionResponseParts.push(convertedUrlPart);
				else if (isFullMediaType(contentPart.mediaType) && isUrlSupported({
					url: supportedUrl,
					mediaType: contentPart.mediaType,
					supportedUrls
				})) functionResponseParts.push({ fileData: {
					mimeType: contentPart.mediaType,
					fileUri: supportedUrl
				} });
				else responseTextParts.push(JSON.stringify(contentPart));
			} else responseTextParts.push(JSON.stringify(contentPart));
			break;
		default: responseTextParts.push(JSON.stringify(contentPart));
	}
	parts.push({ functionResponse: {
		...includeFunctionCallIds && toolCallId != null ? { id: toolCallId } : {},
		name: toolName,
		response: {
			name: toolName,
			content: responseTextParts.length > 0 ? responseTextParts.join("\n") : "Tool executed successfully."
		},
		...functionResponseParts.length > 0 ? { parts: functionResponseParts } : {}
	} });
}
function appendLegacyToolResultParts(parts, toolName, outputValue, toolCallId, includeFunctionCallIds = true) {
	for (const contentPart of outputValue) switch (contentPart.type) {
		case "text":
			parts.push({ functionResponse: {
				...includeFunctionCallIds && toolCallId != null ? { id: toolCallId } : {},
				name: toolName,
				response: {
					name: toolName,
					content: contentPart.text
				}
			} });
			break;
		case "file":
			if (contentPart.data.type === "data") {
				const topLevelMediaType = getTopLevelMediaType(contentPart.mediaType);
				parts.push({ inlineData: {
					mimeType: resolveFullMediaType({ part: contentPart }),
					data: convertToBase64(contentPart.data.data)
				} }, { text: `Tool executed successfully and returned this ${topLevelMediaType === "image" ? "image" : "file"} as a response` });
			} else parts.push({ text: JSON.stringify(contentPart) });
			break;
		default: parts.push({ text: JSON.stringify(contentPart) });
	}
}
function convertToGoogleMessages(prompt, options) {
	const systemInstructionParts = [];
	const contents = [];
	let systemMessagesAllowed = true;
	const isGemmaModel = options?.isGemmaModel ?? false;
	const isGemini3Model = options?.isGemini3Model ?? false;
	const onWarning = options?.onWarning;
	const providerOptionsNames = options?.providerOptionsNames ?? ["google"];
	const isVertexLike = !providerOptionsNames.includes("google");
	const supportsFunctionResponseParts = options?.supportsFunctionResponseParts ?? true;
	const includeFunctionCallIds = options?.includeFunctionCallIds ?? true;
	const supportedFunctionResponseUrls = options?.supportedFunctionResponseUrls ?? {};
	let sentinelInjected = false;
	const missingSignatureToolNames = [];
	const injectSkipSignature = (toolName) => {
		missingSignatureToolNames.push(toolName);
		sentinelInjected = true;
		return SKIP_THOUGHT_SIGNATURE_VALIDATOR;
	};
	const readProviderOpts = (part) => {
		for (const name of providerOptionsNames) {
			const v = part.providerOptions?.[name];
			if (v != null) return v;
		}
		if (isVertexLike) return part.providerOptions?.google;
		return part.providerOptions?.googleVertex ?? part.providerOptions?.vertex;
	};
	for (const { role, content } of prompt) switch (role) {
		case "system":
			if (!systemMessagesAllowed) throw new UnsupportedFunctionalityError({ functionality: "system messages are only supported at the beginning of the conversation" });
			systemInstructionParts.push({ text: content });
			break;
		case "user": {
			systemMessagesAllowed = false;
			const parts = [];
			for (const part of content) switch (part.type) {
				case "text":
					parts.push({ text: part.text });
					break;
				case "file": {
					const rawVideoMetadata = readProviderOpts(part)?.videoMetadata;
					const videoMetadata = rawVideoMetadata == null ? void 0 : googleVideoMetadataSchema.parse(rawVideoMetadata);
					switch (part.data.type) {
						case "url":
							parts.push({
								fileData: {
									mimeType: resolveFullMediaType({ part }),
									fileUri: part.data.url.protocol === "gs:" && part.data.originalUrl != null ? part.data.originalUrl : part.data.url.toString()
								},
								...videoMetadata != null ? { videoMetadata } : {}
							});
							break;
						case "reference":
							if (isVertexLike) throw new UnsupportedFunctionalityError({ functionality: "file parts with provider references" });
							parts.push({
								fileData: {
									mimeType: resolveFullMediaType({ part }),
									fileUri: resolveProviderReference({
										reference: part.data.reference,
										provider: "google"
									})
								},
								...videoMetadata != null ? { videoMetadata } : {}
							});
							break;
						case "text":
							parts.push({
								inlineData: {
									mimeType: isFullMediaType(part.mediaType) ? part.mediaType : "text/plain",
									data: convertToBase64(new TextEncoder().encode(part.data.text))
								},
								...videoMetadata != null ? { videoMetadata } : {}
							});
							break;
						case "data": parts.push({
							inlineData: {
								mimeType: resolveFullMediaType({ part }),
								data: convertToBase64(part.data.data)
							},
							...videoMetadata != null ? { videoMetadata } : {}
						});
					}
					break;
				}
			}
			contents.push({
				role: "user",
				parts
			});
			break;
		}
		case "assistant": {
			systemMessagesAllowed = false;
			let modelResponseHasSignedFunctionCall = false;
			contents.push({
				role: "model",
				parts: content.map((part) => {
					const providerOpts = readProviderOpts(part);
					const thoughtSignature = providerOpts?.thoughtSignature != null ? String(providerOpts.thoughtSignature) : void 0;
					switch (part.type) {
						case "text": return part.text.length === 0 && thoughtSignature == null ? void 0 : {
							text: part.text,
							thoughtSignature
						};
						case "reasoning": return part.text.length === 0 && thoughtSignature == null ? void 0 : {
							text: part.text,
							thought: true,
							thoughtSignature
						};
						case "reasoning-file":
							switch (part.data.type) {
								case "url": throw new UnsupportedFunctionalityError({ functionality: "File data URLs in assistant messages are not supported" });
								case "data": return {
									inlineData: {
										mimeType: part.mediaType,
										data: convertToBase64(part.data.data)
									},
									thought: true,
									thoughtSignature
								};
							}
							break;
						case "file":
							switch (part.data.type) {
								case "url": throw new UnsupportedFunctionalityError({ functionality: "File data URLs in assistant messages are not supported" });
								case "reference":
									if (isVertexLike) throw new UnsupportedFunctionalityError({ functionality: "file parts with provider references" });
									return {
										fileData: {
											mimeType: part.mediaType,
											fileUri: resolveProviderReference({
												reference: part.data.reference,
												provider: "google"
											})
										},
										...providerOpts?.thought === true ? { thought: true } : {},
										thoughtSignature
									};
								case "text": return {
									inlineData: {
										mimeType: isFullMediaType(part.mediaType) ? part.mediaType : "text/plain",
										data: convertToBase64(new TextEncoder().encode(part.data.text))
									},
									...providerOpts?.thought === true ? { thought: true } : {},
									thoughtSignature
								};
								case "data": return {
									inlineData: {
										mimeType: part.mediaType,
										data: convertToBase64(part.data.data)
									},
									...providerOpts?.thought === true ? { thought: true } : {},
									thoughtSignature
								};
							}
							break;
						case "tool-call": {
							if (part.providerExecuted === true && part.toolName === "code_execution") return { executableCode: codeExecutionInputSchema.parse(typeof part.input === "string" ? secureJsonParse(part.input) : part.input) };
							const serverToolCallId = providerOpts?.serverToolCallId != null ? String(providerOpts.serverToolCallId) : void 0;
							const serverToolType = providerOpts?.serverToolType != null ? String(providerOpts.serverToolType) : void 0;
							const isServerToolCall = serverToolCallId != null && serverToolType != null;
							const effectiveThoughtSignature = thoughtSignature ?? (isGemini3Model && !(!isServerToolCall && thoughtSignature == null && modelResponseHasSignedFunctionCall) ? injectSkipSignature(part.toolName) : void 0);
							if (!isServerToolCall && thoughtSignature != null) modelResponseHasSignedFunctionCall = true;
							if (isServerToolCall) return {
								toolCall: {
									toolType: serverToolType,
									args: typeof part.input === "string" ? secureJsonParse(part.input) : part.input,
									id: serverToolCallId
								},
								thoughtSignature: effectiveThoughtSignature
							};
							return {
								functionCall: {
									...includeFunctionCallIds && part.toolCallId != null ? { id: part.toolCallId } : {},
									name: part.toolName,
									args: parseToolCallInput(part.input)
								},
								thoughtSignature: effectiveThoughtSignature
							};
						}
						case "tool-result": {
							if (part.toolName === "code_execution" && part.output.type === "json") return { codeExecutionResult: codeExecutionOutputSchema.parse(part.output.value) };
							const serverToolCallId = providerOpts?.serverToolCallId != null ? String(providerOpts.serverToolCallId) : void 0;
							const serverToolType = providerOpts?.serverToolType != null ? String(providerOpts.serverToolType) : void 0;
							if (serverToolCallId && serverToolType) return {
								toolResponse: {
									toolType: serverToolType,
									response: part.output.type === "json" ? part.output.value : {},
									id: serverToolCallId
								},
								thoughtSignature
							};
							return;
						}
					}
				}).filter((part) => part !== void 0)
			});
			break;
		}
		case "tool": {
			systemMessagesAllowed = false;
			const parts = [];
			for (const part of content) {
				if (part.type === "tool-approval-response") continue;
				const partProviderOpts = readProviderOpts(part);
				const serverToolCallId = partProviderOpts?.serverToolCallId != null ? String(partProviderOpts.serverToolCallId) : void 0;
				const serverToolType = partProviderOpts?.serverToolType != null ? String(partProviderOpts.serverToolType) : void 0;
				if (serverToolCallId && serverToolType) {
					const serverThoughtSignature = partProviderOpts?.thoughtSignature != null ? String(partProviderOpts.thoughtSignature) : void 0;
					if (contents.length > 0) {
						const lastContent = contents[contents.length - 1];
						if (lastContent.role === "model") {
							lastContent.parts.push({
								toolResponse: {
									toolType: serverToolType,
									response: part.output.type === "json" ? part.output.value : {},
									id: serverToolCallId
								},
								thoughtSignature: serverThoughtSignature
							});
							continue;
						}
					}
				}
				const output = part.output;
				if (output.type === "content") {
					if (supportsFunctionResponseParts) appendToolResultParts(parts, part.toolName, output.value, part.toolCallId, includeFunctionCallIds, supportedFunctionResponseUrls);
					else appendLegacyToolResultParts(parts, part.toolName, output.value, part.toolCallId, includeFunctionCallIds);
				} else {
					const isError = output.type === "error-text" || output.type === "error-json" || output.type === "execution-denied";
					parts.push({ functionResponse: {
						...includeFunctionCallIds && part.toolCallId != null ? { id: part.toolCallId } : {},
						name: part.toolName,
						response: {
							name: part.toolName,
							...isError ? { error: output.type === "execution-denied" ? output.reason ?? "Tool call execution denied." : serializeFunctionResponseContent(output.value) } : { content: serializeFunctionResponseContent(output.value) }
						}
					} });
				}
			}
			contents.push({
				role: "user",
				parts
			});
			break;
		}
	}
	if (isGemmaModel && systemInstructionParts.length > 0 && contents.length > 0 && contents[0].role === "user") {
		const systemText = systemInstructionParts.map((part) => part.text).join("\n\n");
		contents[0].parts.unshift({ text: systemText + "\n\n" });
	}
	if (sentinelInjected && onWarning != null) {
		const uniqueToolNames = Array.from(new Set(missingSignatureToolNames));
		onWarning({
			type: "other",
			message: `Replayed ${missingSignatureToolNames.length} \`functionCall\` part(s) for a Gemini 3 model without a \`thoughtSignature\` (tools: ${uniqueToolNames.map((name) => `\`${name}\``).join(", ")}). Injected the documented \`skip_thought_signature_validator\` sentinel to keep the request from failing with HTTP 400. The likely cause is application code that drops \`providerOptions.google.thoughtSignature\` when persisting or serializing assistant tool-call messages. See https://ai.google.dev/gemini-api/docs/thought-signatures.`
		});
	}
	return {
		systemInstruction: systemInstructionParts.length > 0 && !isGemmaModel ? { parts: systemInstructionParts } : void 0,
		contents
	};
}
function parseToolCallInput(input) {
	if (typeof input !== "string") return input;
	try {
		return secureJsonParse(input);
	} catch {
		return input;
	}
}
//#endregion
//#region src/download-tool-result-files.ts
/**
* Download tool result file URLs that Vertex cannot reference directly before
* converting the prompt to the Google request format.
*/
async function downloadToolResultFiles(prompt, { abortSignal, fetch, maxBytes, supportedUrls = {} }) {
	const result = [];
	for (const message of prompt) {
		if (message.role === "assistant") {
			const content = [];
			for (const part of message.content) content.push(part.type === "tool-result" ? {
				...part,
				output: await downloadToolResultOutput(part.output, {
					abortSignal,
					fetch,
					maxBytes,
					supportedUrls
				})
			} : part);
			result.push({
				...message,
				content
			});
			continue;
		}
		if (message.role === "tool") {
			const content = [];
			for (const part of message.content) {
				if (part.type !== "tool-result") {
					content.push(part);
					continue;
				}
				content.push({
					...part,
					output: await downloadToolResultOutput(part.output, {
						abortSignal,
						fetch,
						maxBytes,
						supportedUrls
					})
				});
			}
			result.push({
				...message,
				content
			});
			continue;
		}
		result.push(message);
	}
	return result;
}
async function downloadToolResultOutput(output, { abortSignal, fetch, maxBytes, supportedUrls }) {
	if (output.type !== "content") return output;
	const value = [];
	for (const part of output.value) {
		if (part.type !== "file" || part.data.type !== "url") {
			value.push(part);
			continue;
		}
		if (isUrlSupported({
			url: part.data.url.toString(),
			mediaType: part.mediaType,
			supportedUrls
		})) {
			value.push(part);
			continue;
		}
		const blob = await downloadBlob(part.data.url.toString(), {
			abortSignal,
			fetch,
			maxBytes
		});
		const data = new Uint8Array(await blob.arrayBuffer());
		const detectedMediaType = detectMediaType({
			data,
			topLevelType: "image"
		});
		value.push({
			...part,
			data: {
				type: "data",
				data
			},
			mediaType: detectedMediaType ?? (blob.type && !isFullMediaType(part.mediaType) ? blob.type : part.mediaType)
		});
	}
	return {
		...output,
		value
	};
}
//#endregion
//#region src/get-model-path.ts
function getModelPath(modelId) {
	return modelId.includes("/") ? modelId : `models/${modelId}`;
}
//#endregion
//#region src/google-error.ts
const getGoogleErrorSchema = () => z.object({
	code: z.number().nullable(),
	message: z.string(),
	status: z.string(),
	details: z.array(z.unknown()).nullish()
});
const googleErrorDataSchema = lazySchema(() => zodSchema(z.object({ error: getGoogleErrorSchema() })));
const googleFailedResponseHandler = createJsonErrorResponseHandler({
	errorSchema: googleErrorDataSchema,
	errorToMessage: (data) => data.error.message
});
function mapGoogleErrorStatusToHttpStatusCode(status) {
	switch (status) {
		case "CANCELLED": return 499;
		case "INVALID_ARGUMENT":
		case "FAILED_PRECONDITION":
		case "OUT_OF_RANGE": return 400;
		case "DEADLINE_EXCEEDED": return 504;
		case "NOT_FOUND": return 404;
		case "ALREADY_EXISTS":
		case "ABORTED": return 409;
		case "PERMISSION_DENIED": return 403;
		case "UNAUTHENTICATED": return 401;
		case "RESOURCE_EXHAUSTED": return 429;
		case "UNIMPLEMENTED": return 501;
		case "UNKNOWN":
		case "INTERNAL":
		case "DATA_LOSS": return 500;
		case "UNAVAILABLE": return 503;
		default: return;
	}
}
//#endregion
//#region src/sanitize-response-json-schema.ts
/**
* Recursively replaces `const` with a single-value `enum` in the JSON Schema
* locations supported by Google because `responseJsonSchema` does not support
* `const`. All other schema properties are preserved.
*/
function sanitizeResponseJsonSchema(schema) {
	const { const: constValue, properties, items, additionalProperties, anyOf, oneOf, ...result } = schema;
	return {
		...result,
		...constValue !== void 0 ? { enum: [constValue] } : {},
		...properties != null ? { properties: sanitizeDefinitions(properties) } : {},
		...items != null ? { items: Array.isArray(items) ? items.map(sanitizeDefinition) : sanitizeDefinition(items) } : {},
		...additionalProperties != null ? { additionalProperties: typeof additionalProperties === "boolean" ? additionalProperties : sanitizeDefinition(additionalProperties) } : {},
		...anyOf != null ? { anyOf: anyOf.map(sanitizeDefinition) } : {},
		...oneOf != null ? { oneOf: oneOf.map(sanitizeDefinition) } : {},
		...result.$defs != null ? { $defs: sanitizeDefinitions(result.$defs) } : {}
	};
}
function sanitizeDefinitions(definitions) {
	return Object.fromEntries(Object.entries(definitions).map(([name, definition]) => [name, sanitizeDefinition(definition)]));
}
function sanitizeDefinition(definition) {
	return typeof definition === "boolean" ? definition : sanitizeResponseJsonSchema(definition);
}
//#endregion
//#region src/google-language-model-options.ts
const googleLanguageModelOptions = lazySchema(() => zodSchema(z.object({
	responseModalities: z.array(z.enum([
		"TEXT",
		"IMAGE",
		"AUDIO"
	])).optional(),
	thinkingConfig: z.object({
		thinkingBudget: z.number().optional(),
		includeThoughts: z.boolean().optional(),
		thinkingLevel: z.enum([
			"minimal",
			"low",
			"medium",
			"high",
			"xhigh"
		]).optional()
	}).optional(),
	/**
	* Optional.
	* The name of the cached content used as context to serve the prediction.
	* Format: cachedContents/{cachedContent}
	*/
	cachedContent: z.string().optional(),
	/**
	* Optional. Enable structured output. Default is true.
	*
	* This is useful when the JSON Schema contains elements that are
	* not supported by the OpenAPI schema version that
	* Google uses. You can use this to disable
	* structured outputs if you need to.
	*/
	structuredOutputs: z.boolean().optional(),
	/**
	* Optional. A list of unique safety settings for blocking unsafe content.
	*/
	safetySettings: z.array(z.object({
		category: z.enum([
			"HARM_CATEGORY_UNSPECIFIED",
			"HARM_CATEGORY_HATE_SPEECH",
			"HARM_CATEGORY_DANGEROUS_CONTENT",
			"HARM_CATEGORY_HARASSMENT",
			"HARM_CATEGORY_SEXUALLY_EXPLICIT",
			"HARM_CATEGORY_CIVIC_INTEGRITY"
		]),
		threshold: z.enum([
			"HARM_BLOCK_THRESHOLD_UNSPECIFIED",
			"BLOCK_LOW_AND_ABOVE",
			"BLOCK_MEDIUM_AND_ABOVE",
			"BLOCK_ONLY_HIGH",
			"BLOCK_NONE",
			"OFF"
		])
	})).optional(),
	threshold: z.enum([
		"HARM_BLOCK_THRESHOLD_UNSPECIFIED",
		"BLOCK_LOW_AND_ABOVE",
		"BLOCK_MEDIUM_AND_ABOVE",
		"BLOCK_ONLY_HIGH",
		"BLOCK_NONE",
		"OFF"
	]).optional(),
	/**
	* Optional. Enables timestamp understanding for audio-only files.
	*
	* https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/audio-understanding
	*/
	audioTimestamp: z.boolean().optional(),
	/**
	* Optional. Defines labels used in billing reports. Available on Vertex AI only.
	*
	* https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/add-labels-to-api-calls
	*/
	labels: z.record(z.string(), z.string()).optional(),
	/**
	* Optional. If specified, the media resolution specified will be used.
	*
	* https://ai.google.dev/api/generate-content#MediaResolution
	*/
	mediaResolution: z.enum([
		"MEDIA_RESOLUTION_UNSPECIFIED",
		"MEDIA_RESOLUTION_LOW",
		"MEDIA_RESOLUTION_MEDIUM",
		"MEDIA_RESOLUTION_HIGH"
	]).optional(),
	/**
	* Optional. Configures the image generation aspect ratio for Gemini models.
	*
	* https://ai.google.dev/gemini-api/docs/image-generation#aspect_ratios
	*/
	imageConfig: z.object({
		aspectRatio: z.enum([
			"1:1",
			"2:3",
			"3:2",
			"3:4",
			"4:3",
			"4:5",
			"5:4",
			"9:16",
			"16:9",
			"21:9",
			"1:8",
			"8:1",
			"1:4",
			"4:1"
		]).optional(),
		imageSize: z.enum([
			"1K",
			"2K",
			"4K",
			"512"
		]).optional(),
		/**
		* Optional. Controls the generation of people in images.
		* Vertex AI only.
		*/
		personGeneration: z.enum([
			"PERSON_GENERATION_UNSPECIFIED",
			"ALLOW_ALL",
			"ALLOW_ADULT",
			"ALLOW_NONE"
		]).optional(),
		/**
		* Optional. Controls whether generation of prominent people
		* (celebrities) is allowed. When set together with
		* `personGeneration`, `personGeneration` takes precedence.
		* Vertex AI only.
		*
		* https://docs.cloud.google.com/vertex-ai/generative-ai/docs/reference/rest/v1/GenerationConfig
		*/
		prominentPeople: z.enum([
			"PROMINENT_PEOPLE_UNSPECIFIED",
			"ALLOW_PROMINENT_PEOPLE",
			"BLOCK_PROMINENT_PEOPLE"
		]).optional(),
		/**
		* Optional. The image output format for generated images.
		* Vertex AI only.
		*/
		imageOutputOptions: z.object({
			mimeType: z.enum(["image/jpeg", "image/png"]).optional(),
			compressionQuality: z.number().optional()
		}).optional()
	}).optional(),
	/**
	* Optional. Configuration for grounding retrieval.
	* Used to provide location context for Google Maps and Google Search grounding.
	*
	* https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/grounding-with-google-maps
	*/
	retrievalConfig: z.object({ latLng: z.object({
		latitude: z.number(),
		longitude: z.number()
	}).optional() }).optional(),
	/**
	* Optional. When set to true, function call arguments will be streamed
	* incrementally via partialArgs in streaming responses. Only supported
	* on the Vertex AI API (not the Gemini API) and only for Gemini 3+
	* models.
	*
	* @default false
	*
	* https://docs.cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling#streaming-fc
	*/
	streamFunctionCallArguments: z.boolean().optional(),
	/**
	* Optional. The service tier to use for the request. Sent as the
	* `serviceTier` body field. Gemini API only.
	*/
	serviceTier: z.enum([
		"standard",
		"flex",
		"priority"
	]).optional(),
	/**
	* Optional. Resumes a response that stopped with `MAX_TOKENS` because it
	* used up its `maxOutputTokens` budget (Gemini 4 long decode). Pass the
	* `continuationToken` from the previous response's provider metadata
	* together with the original prompt and a higher `maxOutputTokens` (the
	* budget is cumulative across requests). Requests that reach the
	* per-request cap while budget remains (`CONTINUATION`) are continued
	* automatically. Sent as the `continuationToken` body field.
	*/
	continuationToken: z.string().optional(),
	/**
	* Optional. Vertex AI only. Sent as the
	* `X-Vertex-AI-LLM-Shared-Request-Type` request header to select a
	* shared (PayGo) tier. With Provisioned Throughput allocated and
	* `requestType` unset, the request falls back to this tier only if
	* PT capacity is exhausted.
	*
	* https://docs.cloud.google.com/vertex-ai/generative-ai/docs/priority-paygo
	* https://docs.cloud.google.com/vertex-ai/generative-ai/docs/flex-paygo
	*/
	sharedRequestType: z.enum([
		"priority",
		"flex",
		"standard"
	]).optional(),
	/**
	* Optional. Vertex AI only. Sent as the `X-Vertex-AI-LLM-Request-Type`
	* request header. Set to `'shared'` together with `sharedRequestType`
	* to bypass Provisioned Throughput entirely.
	*
	* https://docs.cloud.google.com/vertex-ai/generative-ai/docs/priority-paygo
	*/
	requestType: z.enum(["shared"]).optional()
})));
//#endregion
//#region src/google-model-catalog.ts
const gemini1ModelPattern = /(^|[/.])gemini-1(?:[.-]|$)/i;
const gemini2ModelPattern = /(^|[/.])gemini-2(?:[.-]|$)/i;
const gemini25ModelPattern = /(^|[/.])gemini-2\.5(?:[.-]|$)/i;
const geminiModelPattern = /(^|[/.])gemini-/i;
/** Options accepted under providerOptions.google (or googleVertex on Vertex). */
const googleProviderOptions = { language: {
	responseModalities: { description: "Response modalities to generate." },
	thinkingConfig: { description: "Thinking budget, level, and thought output." },
	cachedContent: { description: "Cached content to use as request context." },
	structuredOutputs: { description: "Whether to send the JSON response schema." },
	safetySettings: { description: "Safety settings for the request." },
	threshold: { description: "Default threshold for configurable safety categories." },
	audioTimestamp: { description: "Enable timestamp understanding for audio." },
	labels: { description: "Billing labels. Vertex AI only." },
	mediaResolution: { description: "Resolution used to process media inputs." },
	imageConfig: { description: "Image generation configuration." },
	retrievalConfig: { description: "Retrieval configuration for tools." },
	streamFunctionCallArguments: { description: "Stream function arguments. Vertex AI only." },
	serviceTier: { description: "Service tier. Gemini API only." },
	continuationToken: { description: "Resume a response using its continuation token." },
	sharedRequestType: { description: "Shared PayGo tier. Vertex AI only." },
	requestType: { description: "Request routing type. Vertex AI only." }
} };
const standard = {
	image: true,
	objectGeneration: true,
	tools: true,
	streaming: true,
	googleSearch: true,
	urlContext: true
};
const googleModelCatalog = Object.fromEntries(Object.entries({
	"gemini-nano-banana-2.1": {
		image: true,
		objectGeneration: false,
		tools: false,
		streaming: false,
		googleSearch: true,
		urlContext: false
	},
	"gemini-4-argon": standard,
	"gemini-4-carbon": standard,
	"gemini-4-hydrogen": standard,
	"gemini-3.8-flash": standard,
	"gemini-3.7-flash": standard,
	"gemini-3.6-flash": standard,
	"gemini-3.5-flash": standard,
	"gemini-3.5-flash-lite": standard,
	"gemini-3.1-pro-preview": standard,
	"gemini-3.1-flash-image-preview": standard,
	"gemini-3.1-flash-lite-preview": standard,
	"gemini-3-pro-preview": standard,
	"gemini-3-pro-image-preview": standard,
	"gemini-3-flash-preview": standard,
	"gemini-2.5-pro": standard,
	"gemini-2.5-flash": standard,
	"gemini-2.5-flash-lite": standard,
	"gemini-2.5-flash-lite-preview-06-17": standard,
	"gemini-2.0-flash": standard,
	aqa: void 0,
	"deep-research-max-preview-04-2026": void 0,
	"deep-research-preview-04-2026": void 0,
	"deep-research-pro-preview-12-2025": void 0,
	"gemini-2.0-flash-001": void 0,
	"gemini-2.0-flash-lite": void 0,
	"gemini-2.0-flash-lite-001": void 0,
	"gemini-2.5-computer-use-preview-10-2025": void 0,
	"gemini-2.5-flash-image": void 0,
	"gemini-2.5-flash-native-audio-latest": void 0,
	"gemini-2.5-flash-native-audio-preview-09-2025": void 0,
	"gemini-2.5-flash-native-audio-preview-12-2025": void 0,
	"gemini-2.5-flash-preview-tts": void 0,
	"gemini-2.5-pro-preview-tts": void 0,
	"gemini-3.1-flash-tts-preview": void 0,
	"gemini-3.1-pro-preview-customtools": void 0,
	"gemini-flash-latest": void 0,
	"gemini-flash-lite-latest": void 0,
	"gemini-pro-latest": void 0,
	"gemini-robotics-er-1.5-preview": void 0,
	"gemma-3-12b-it": void 0,
	"gemma-3-1b-it": void 0,
	"gemma-3-27b-it": void 0,
	"gemma-3-4b-it": void 0,
	"gemma-3n-e2b-it": void 0,
	"gemma-3n-e4b-it": void 0,
	"nano-banana-pro-preview": void 0
}).filter(([, definition]) => definition != null).map(([modelId, documented]) => [modelId, createGoogleLanguageModelEntry(modelId, documented)]));
function getGoogleLanguageModelEntry(modelId) {
	return Object.prototype.hasOwnProperty.call(googleModelCatalog, modelId) ? googleModelCatalog[modelId] : createGoogleLanguageModelEntry(modelId);
}
function createGoogleLanguageModelEntry(modelId, documented) {
	const runtime = resolveRuntimeDefinition(modelId);
	const declared = documented != null;
	const id = modelId.toLowerCase();
	const thinkingFormat = runtime.usesGemini3Features && !modelId.includes("gemini-3-pro-image") ? "level" : "budget";
	const isHydrogen = /(^|[/.])gemini-4-hydrogen(?:-|$)/i.test(modelId);
	const minLevel = isHydrogen ? "low" : getMinimumThinkingLevelForGemini3Model(modelId);
	return {
		capabilities: { language: {
			inputs: {
				image: documented?.image ?? "unknown",
				audio: "unknown"
			},
			objectGeneration: documented?.objectGeneration ?? "unknown",
			tools: {
				usage: documented?.tools ?? "unknown",
				streaming: documented?.streaming ?? "unknown",
				gemini2: runtime.supportsGemini2Tools,
				googleSearch: documented?.googleSearch ?? "unknown",
				urlContext: documented?.urlContext ?? "unknown",
				fileSearch: runtime.supportsFileSearch,
				mixed: runtime.usesGemini3Features,
				functionResponseParts: runtime.usesGemini3Features
			},
			reasoning: {
				support: "unknown",
				efforts: thinkingFormat === "level" ? [
					...minLevel === "minimal" ? ["minimal"] : [],
					"low",
					"medium",
					"high",
					...isHydrogen ? ["xhigh"] : []
				] : void 0,
				minBudget: id.includes("2.5-pro") ? 128 : 0,
				maxBudget: id.includes("2.5-pro") || id.includes("gemini-3-pro-image") ? 32768 : 24576
			},
			sampling: {
				temperature: "unknown",
				topP: "unknown",
				topK: "unknown",
				logprobs: false,
				penalties: gemini25ModelPattern.test(modelId) ? { condition: "Only supported on the Vertex AI API." } : true
			},
			structuredOutput: { withFunctionTools: runtime.usesGemini3Features }
		} },
		providerOptions: { language: declareProviderOptions(declared ? true : "unknown", {
			labels: { condition: "Only supported on the Vertex AI API." },
			sharedRequestType: { condition: "Only supported on the Vertex AI API." },
			requestType: { condition: "Only supported on the Vertex AI API." },
			serviceTier: { condition: "Only supported on the Gemini API." },
			streamFunctionCallArguments: { condition: "Only supported for streaming requests on the Vertex AI API with Gemini 3 or newer." }
		}) },
		requestConfig: { language: {
			systemInstruction: id.startsWith("gemma-") ? "user" : "system",
			thinkingFormat,
			reasoningMaxOutputTokens: 65536,
			jsonResponseTool: /(^|\/)gemini-/i.test(modelId)
		} }
	};
}
function declareProviderOptions(support, exceptions) {
	return Object.fromEntries(Object.keys(googleProviderOptions.language).map((option) => [option, exceptions[option] ?? support]));
}
function isKnownPreGemini2Model(modelId) {
	return gemini1ModelPattern.test(modelId) || /(^|[/.])gemini-pro(?:-vision)?$/i.test(modelId) || /(^|[/.])gemini-robotics-er-1\.5(?:[.-]|$)/i.test(modelId);
}
/**
* Classifies Gemini capabilities by excluding known older generations.
*
* Google model IDs are open-ended, so unrecognized Gemini IDs and aliases
* intentionally inherit the newest supported behavior. Add exceptions here
* only when a model is known to require a legacy request shape.
*/
function resolveRuntimeDefinition(modelId) {
	const isGeminiModel = geminiModelPattern.test(modelId);
	const isGemini2Model = gemini2ModelPattern.test(modelId);
	const isKnownPreGemini2 = isKnownPreGemini2Model(modelId);
	const usesGemini3Features = isGeminiModel && !(isKnownPreGemini2 || isGemini2Model);
	return {
		supportsGemini2Tools: isGeminiModel && !isKnownPreGemini2 || modelId.toLowerCase().includes("nano-banana"),
		supportsFileSearch: gemini25ModelPattern.test(modelId) || usesGemini3Features,
		usesGemini3Features
	};
}
function getMinimumThinkingLevelForGemini3Model(modelId) {
	const modelName = /(?:^|[/.])(gemini-[^/]*)$/i.exec(modelId)?.[1].toLowerCase();
	if (modelName === "gemini-flash-latest") return "low";
	const proVersionMatch = /^gemini-(\d+)(?:\.\d+)?-pro(?:$|-)/.exec(modelName ?? "");
	if (Number(proVersionMatch?.[1]) >= 3) return "low";
	const versionMatch = /^gemini-(\d+)\.(\d+)-flash(?:$|-(?!lite(?:-|$)))/.exec(modelName ?? "");
	if (versionMatch == null) return "minimal";
	const majorVersion = Number(versionMatch[1]);
	const minorVersion = Number(versionMatch[2]);
	return majorVersion > 3 || majorVersion === 3 && minorVersion >= 7 ? "low" : "minimal";
}
//#endregion
//#region src/google-model-capabilities.ts
/** Compatibility projection of the resolved catalog for existing request code. */
function getGoogleModelCapabilities(modelId) {
	const { tools } = getGoogleLanguageModelEntry(modelId).capabilities.language;
	return {
		supportsGemini2Tools: tools.gemini2,
		supportsFileSearch: tools.fileSearch,
		usesGemini3Features: tools.mixed
	};
}
//#endregion
//#region src/google-prepare-tools.ts
function prepareTools({ tools, toolChoice, modelId, isVertexProvider = false }) {
	if (toolChoice?.type === "none") tools = tools?.filter((tool) => tool.type === "function");
	tools = tools?.length ? tools : void 0;
	const toolWarnings = [];
	const { supportsGemini2Tools, supportsFileSearch, usesGemini3Features } = getGoogleModelCapabilities(modelId);
	if (tools == null) return {
		tools: void 0,
		toolConfig: void 0,
		toolWarnings
	};
	const hasFunctionTools = tools.some((tool) => tool.type === "function");
	const hasProviderTools = tools.some((tool) => tool.type === "provider");
	if (hasFunctionTools && hasProviderTools && !usesGemini3Features) toolWarnings.push({
		type: "unsupported",
		feature: `combination of function and provider-defined tools`
	});
	if (hasProviderTools) {
		const googleTools = [];
		tools.filter((tool) => tool.type === "provider").forEach((tool) => {
			switch (tool.id) {
				case "google.google_search":
					if (supportsGemini2Tools) googleTools.push({ googleSearch: { ...tool.args } });
					else toolWarnings.push({
						type: "unsupported",
						feature: `provider-defined tool ${tool.id}`,
						details: "Google Search requires Gemini 2.0 or newer."
					});
					break;
				case "google.enterprise_web_search":
					if (supportsGemini2Tools) googleTools.push({ enterpriseWebSearch: {} });
					else toolWarnings.push({
						type: "unsupported",
						feature: `provider-defined tool ${tool.id}`,
						details: "Enterprise Web Search requires Gemini 2.0 or newer."
					});
					break;
				case "google.url_context":
					if (supportsGemini2Tools) googleTools.push({ urlContext: {} });
					else toolWarnings.push({
						type: "unsupported",
						feature: `provider-defined tool ${tool.id}`,
						details: "The URL context tool is not supported with other Gemini models than Gemini 2."
					});
					break;
				case "google.code_execution":
					if (supportsGemini2Tools) googleTools.push({ codeExecution: {} });
					else toolWarnings.push({
						type: "unsupported",
						feature: `provider-defined tool ${tool.id}`,
						details: "The code execution tool is not supported with other Gemini models than Gemini 2."
					});
					break;
				case "google.file_search":
					if (supportsFileSearch) googleTools.push({ fileSearch: { ...tool.args } });
					else toolWarnings.push({
						type: "unsupported",
						feature: `provider-defined tool ${tool.id}`,
						details: "The file search tool is only supported with Gemini 2.5 models and Gemini 3 models."
					});
					break;
				case "google.vertex_rag_store":
					if (supportsGemini2Tools) googleTools.push({ retrieval: { vertex_rag_store: {
						rag_resources: { rag_corpus: tool.args.ragCorpus },
						similarity_top_k: tool.args.topK
					} } });
					else toolWarnings.push({
						type: "unsupported",
						feature: `provider-defined tool ${tool.id}`,
						details: "The RAG store tool is not supported with other Gemini models than Gemini 2."
					});
					break;
				case "google.google_maps":
					if (supportsGemini2Tools) googleTools.push({ googleMaps: {} });
					else toolWarnings.push({
						type: "unsupported",
						feature: `provider-defined tool ${tool.id}`,
						details: "The Google Maps grounding tool is not supported with Gemini models other than Gemini 2 or newer."
					});
					break;
				default: toolWarnings.push({
					type: "unsupported",
					feature: `provider-defined tool ${tool.id}`
				});
			}
		});
		if (hasFunctionTools && usesGemini3Features && googleTools.length > 0) {
			const functionDeclarations = [];
			for (const tool of tools) if (tool.type === "function") functionDeclarations.push(prepareFunctionDeclaration(tool));
			const combinedToolConfig = {
				functionCallingConfig: { mode: "VALIDATED" },
				...!isVertexProvider && { includeServerSideToolInvocations: true }
			};
			if (toolChoice != null) switch (toolChoice.type) {
				case "auto": break;
				case "none":
					combinedToolConfig.functionCallingConfig = { mode: "NONE" };
					break;
				case "required":
					combinedToolConfig.functionCallingConfig = { mode: "ANY" };
					break;
				case "tool": combinedToolConfig.functionCallingConfig = {
					mode: "ANY",
					allowedFunctionNames: [toolChoice.toolName]
				};
			}
			return {
				tools: [...googleTools, { functionDeclarations }],
				toolConfig: combinedToolConfig,
				toolWarnings
			};
		}
		return {
			tools: googleTools.length > 0 ? googleTools : void 0,
			toolConfig: void 0,
			toolWarnings
		};
	}
	const functionDeclarations = [];
	let hasStrictTools = false;
	for (const tool of tools) switch (tool.type) {
		case "function":
			functionDeclarations.push(prepareFunctionDeclaration(tool));
			if (tool.strict === true) hasStrictTools = true;
			break;
		default: toolWarnings.push({
			type: "unsupported",
			feature: `function tool ${tool.name}`
		});
	}
	if (toolChoice == null) return {
		tools: [{ functionDeclarations }],
		toolConfig: hasStrictTools ? { functionCallingConfig: { mode: "VALIDATED" } } : void 0,
		toolWarnings
	};
	const type = toolChoice.type;
	switch (type) {
		case "auto": return {
			tools: [{ functionDeclarations }],
			toolConfig: { functionCallingConfig: { mode: hasStrictTools ? "VALIDATED" : "AUTO" } },
			toolWarnings
		};
		case "none": return {
			tools: [{ functionDeclarations }],
			toolConfig: { functionCallingConfig: { mode: "NONE" } },
			toolWarnings
		};
		case "required": return {
			tools: [{ functionDeclarations }],
			toolConfig: { functionCallingConfig: { mode: "ANY" } },
			toolWarnings
		};
		case "tool": return {
			tools: [{ functionDeclarations }],
			toolConfig: { functionCallingConfig: {
				mode: "ANY",
				allowedFunctionNames: [toolChoice.toolName]
			} },
			toolWarnings
		};
		default: throw new UnsupportedFunctionalityError({ functionality: `tool choice type: ${type}` });
	}
}
function prepareFunctionDeclaration(tool) {
	return {
		name: tool.name,
		description: tool.description ?? "",
		parametersJsonSchema: tool.inputSchema
	};
}
//#endregion
//#region src/google-json-accumulator.ts
/**
* Incrementally builds a JSON object from Google's streaming `partialArgs`
* chunks emitted during tool-call function calling. Tracks both the structured
* object and a running JSON text representation so callers can emit text deltas
* that, when concatenated, form valid nested JSON matching JSON.stringify output.
*
* Input: [{jsonPath:"$.location",stringValue:"Boston"}]
* Output: '{"location":"Boston"', then finalize() → closingDelta='}'
*/
var GoogleJSONAccumulator = class {
	constructor() {
		this.accumulatedArgs = {};
		this.jsonText = "";
		this.pathStack = [];
		this.stringOpen = false;
	}
	/**
	* Input: [{jsonPath:"$.brightness",numberValue:50}]
	* Output: { currentJSON:{brightness:50}, textDelta:'{"brightness":50' }
	*/
	processPartialArgs(partialArgs) {
		let delta = "";
		for (const arg of partialArgs) {
			const isArrayTerminator = arg.jsonPath.endsWith("[]");
			const rawPath = (isArrayTerminator ? arg.jsonPath.slice(0, -2) : arg.jsonPath).replace(/^\$\./, "");
			if (!rawPath || rawPath === "$") continue;
			const segments = parsePath(rawPath);
			if (!normalizeAndCheckIndexes(this.accumulatedArgs, segments)) continue;
			const existingValue = getNestedValue(this.accumulatedArgs, segments);
			if (arg.stringValue != null && existingValue !== void 0) {
				const escaped = JSON.stringify(arg.stringValue).slice(1, -1);
				setNestedValue(this.accumulatedArgs, segments, existingValue + arg.stringValue);
				delta += escaped;
				if (arg.willContinue !== true) delta += this.closeOpenString();
				continue;
			}
			const resolved = resolvePartialArgValue(arg);
			if (resolved == null) {
				if (existingValue !== void 0) {
					if (typeof existingValue === "string" && arg.willContinue !== true) delta += this.closeOpenString();
					continue;
				}
				if (arg.willContinue === true) continue;
				setNestedValue(this.accumulatedArgs, segments, isArrayTerminator ? [] : {});
				delta += this.emitContainer(segments, isArrayTerminator);
				continue;
			}
			setNestedValue(this.accumulatedArgs, segments, resolved.value);
			delta += this.emitNavigationTo(segments, arg, resolved.json);
		}
		this.jsonText += delta;
		return {
			currentJSON: this.accumulatedArgs,
			textDelta: delta
		};
	}
	/**
	* Input: jsonText='{"brightness":50', accumulatedArgs={brightness:50}
	* Output: { finalJSON:'{"brightness":50}', closingDelta:'}' }
	*/
	finalize() {
		const finalArgs = JSON.stringify(this.accumulatedArgs);
		return {
			finalJSON: finalArgs,
			closingDelta: finalArgs.slice(this.jsonText.length)
		};
	}
	/**
	* Input: stringOpen=true
	* Output: '"' (and stringOpen=false), or '' when no string is open
	*/
	closeOpenString() {
		if (!this.stringOpen) return "";
		this.stringOpen = false;
		return "\"";
	}
	/**
	* Input: pathStack=[] (first call) or pathStack=[root,...] (subsequent calls)
	* Output: '{' (first call) or '' (subsequent calls)
	*/
	ensureRoot() {
		if (this.pathStack.length === 0) {
			this.pathStack.push({
				segment: "",
				isArray: false,
				childCount: 0
			});
			return "{";
		}
		return "";
	}
	/**
	* Emits the JSON text fragment needed to navigate from the current open
	* path to the new leaf at `targetSegments`, then writes the value.
	*
	* Input: targetSegments=["recipe","name"], arg={jsonPath:"$.recipe.name",stringValue:"Lasagna"}, valueJson='"Lasagna"'
	* Output: '{"recipe":{"name":"Lasagna"'
	*/
	emitNavigationTo(targetSegments, arg, valueJson) {
		let fragment = this.closeOpenString();
		fragment += this.ensureRoot();
		const targetContainerSegments = targetSegments.slice(0, -1);
		const leafSegment = targetSegments[targetSegments.length - 1];
		const commonDepth = this.findCommonStackDepth(targetContainerSegments);
		fragment += this.closeDownTo(commonDepth);
		fragment += this.openDownTo(targetContainerSegments, leafSegment);
		fragment += this.emitLeaf(leafSegment, arg, valueJson);
		return fragment;
	}
	/**
	* Navigates to `targetSegments` and opens a container there. The container
	* stays open so later entries for its children continue inside it; it is
	* closed by subsequent navigation or by `finalize()`.
	*
	* Input: targetSegments=["nested","tags"], isArray=true
	* Output: '{"nested":{"tags":['
	*/
	emitContainer(targetSegments, isArray) {
		let fragment = this.closeOpenString();
		fragment += this.ensureRoot();
		const commonDepth = this.findCommonStackDepth(targetSegments);
		fragment += this.closeDownTo(commonDepth);
		fragment += this.openDownTo(targetSegments, isArray ? 0 : "");
		return fragment;
	}
	/**
	* Returns the stack depth to preserve when navigating to a new target
	* container path. Always >= 1 (the root is never popped).
	*
	* Input: stack=[root,"recipe","ingredients",0], target=["recipe","ingredients",1]
	* Output: 3 (keep root+"recipe"+"ingredients")
	*/
	findCommonStackDepth(targetContainer) {
		const maxDepth = Math.min(this.pathStack.length - 1, targetContainer.length);
		let common = 0;
		for (let i = 0; i < maxDepth; i++) if (this.pathStack[i + 1].segment === targetContainer[i]) common++;
		else break;
		return common + 1;
	}
	/**
	* Closes containers from the current stack depth back down to `targetDepth`.
	*
	* Input: this.pathStack=[root,"recipe","ingredients",0], targetDepth=3
	* Output: '}'
	*/
	closeDownTo(targetDepth) {
		let fragment = "";
		while (this.pathStack.length > targetDepth) {
			const entry = this.pathStack.pop();
			fragment += entry.isArray ? "]" : "}";
		}
		return fragment;
	}
	/**
	* Opens containers from the current stack depth down to the full target
	* container path, emitting opening `{`, `[`, keys, and commas as needed.
	* `leafSegment` is used to determine if the innermost container is an array.
	*
	* Input: this.pathStack=[root], targetContainer=["recipe","ingredients"], leafSegment=0
	* Output: '"recipe":{"ingredients":['
	*/
	openDownTo(targetContainer, leafSegment) {
		let fragment = "";
		const startIdx = this.pathStack.length - 1;
		for (let i = startIdx; i < targetContainer.length; i++) {
			const pathSegment = targetContainer[i];
			const parentEntry = this.pathStack[this.pathStack.length - 1];
			if (parentEntry.childCount > 0) fragment += ",";
			parentEntry.childCount++;
			if (typeof pathSegment === "string") fragment += `${JSON.stringify(pathSegment)}:`;
			const isArray = typeof (i + 1 < targetContainer.length ? targetContainer[i + 1] : leafSegment) === "number";
			fragment += isArray ? "[" : "{";
			this.pathStack.push({
				segment: pathSegment,
				isArray,
				childCount: 0
			});
		}
		return fragment;
	}
	/**
	* Emits the comma, key, and value for a leaf entry in the current container.
	*
	* Input: leafSegment="name", arg={stringValue:"Lasagna"}, valueJson='"Lasagna"'
	* Output: '"name":"Lasagna"' (or ',"name":"Lasagna"' if container.childCount > 0)
	*/
	emitLeaf(leafSegment, arg, valueJson) {
		let fragment = "";
		const container = this.pathStack[this.pathStack.length - 1];
		if (container.childCount > 0) fragment += ",";
		container.childCount++;
		if (typeof leafSegment === "string") fragment += `${JSON.stringify(leafSegment)}:`;
		if (arg.stringValue != null && arg.willContinue) {
			fragment += valueJson.slice(0, -1);
			this.stringOpen = true;
		} else fragment += valueJson;
		return fragment;
	}
};
/**
* Splits a dotted/bracketed JSON path like `recipe.ingredients[0].name` into segments.
*
* Input: "recipe.ingredients[0].name"
* Output: ["recipe", "ingredients", 0, "name"]
*/
function parsePath(rawPath) {
	const segments = [];
	for (const part of rawPath.split(".")) {
		const bracketIdx = part.indexOf("[");
		if (bracketIdx === -1) segments.push(part);
		else {
			if (bracketIdx > 0) segments.push(part.slice(0, bracketIdx));
			for (const m of part.matchAll(/\[(\d+)\]/g)) segments.push(parseInt(m[1], 10));
		}
	}
	return segments;
}
const hasOwn = Object.prototype.hasOwnProperty;
/**
* Checks only direct properties so path traversal never follows the prototype chain.
*/
function hasOwnProperty(obj, key) {
	return hasOwn.call(obj, key);
}
/**
* Defines path values as own data properties so special keys like `__proto__`
* cannot invoke prototype setters while accumulating streamed arguments.
*/
function defineOwnProperty(obj, key, value) {
	Object.defineProperty(obj, key, {
		value,
		enumerable: true,
		configurable: true,
		writable: true
	});
}
/**
* Traverses a nested object along the given path segments and returns the leaf value.
*
* Input: ({recipe:{name:"Lasagna"}}, ["recipe","name"])
* Output: "Lasagna"
*/
function getNestedValue(obj, segments) {
	let current = obj;
	for (const pathSegment of segments) {
		if (current == null || typeof current !== "object") return void 0;
		const currentRecord = current;
		if (!hasOwnProperty(currentRecord, pathSegment)) return void 0;
		current = currentRecord[pathSegment];
	}
	return current;
}
/**
* Normalizes dotted array indexes in place and checks that every array index
* is an existing element or the next one. Arrays are streamed in order, and a
* sparse index would make `finalize()` serialize one `null` per hole, so a tiny
* response could allocate without bound.
* A missing or `null` parent is replaced with a new array, so it starts at 0.
*
* Input: ({items:["a"]}, ["items",1]) or ({items:["a"]}, ["items",5])
* Output: true or false
*/
function normalizeAndCheckIndexes(obj, segments) {
	let current = obj;
	for (let i = 0; i < segments.length; i++) {
		let pathSegment = segments[i];
		if (Array.isArray(current) && typeof pathSegment === "string") {
			const index = Number(pathSegment);
			if (Number.isInteger(index) && index >= 0 && index < 2 ** 32 - 1 && String(index) === pathSegment) {
				pathSegment = index;
				segments[i] = index;
			}
		}
		if (typeof pathSegment === "number") {
			const length = Array.isArray(current) ? current.length : current == null ? 0 : void 0;
			if (length !== void 0 && pathSegment > length) return false;
		}
		current = current != null && typeof current === "object" && hasOwnProperty(current, pathSegment) ? current[pathSegment] : void 0;
	}
	return true;
}
/**
* Sets a value at a nested path, creating intermediate objects or arrays as needed.
*
* Input: obj={}, segments=["recipe","ingredients",0,"name"], value="Noodles"
* Output: {recipe:{ingredients:[{name:"Noodles"}]}}
*/
function setNestedValue(obj, segments, value) {
	let current = obj;
	for (let i = 0; i < segments.length - 1; i++) {
		const pathSegment = segments[i];
		const nextSeg = segments[i + 1];
		if (!hasOwnProperty(current, pathSegment) || current[pathSegment] == null) defineOwnProperty(current, pathSegment, typeof nextSeg === "number" ? [] : {});
		current = current[pathSegment];
	}
	defineOwnProperty(current, segments[segments.length - 1], value);
}
/**
* Extracts the first non-null typed value from a partial arg and returns it with its JSON representation.
*
* Input: arg={stringValue:"Boston"} or arg={numberValue:50}
* Output: {value:"Boston", json:'"Boston"'} or {value:50, json:'50'}
*/
function resolvePartialArgValue(arg) {
	const value = arg.stringValue ?? arg.numberValue ?? arg.boolValue;
	if (value != null) return {
		value,
		json: JSON.stringify(value)
	};
	if ("nullValue" in arg) return {
		value: null,
		json: "null"
	};
}
//#endregion
//#region src/map-google-finish-reason.ts
function mapGoogleFinishReason({ finishReason, hasToolCalls }) {
	switch (finishReason) {
		case "STOP": return hasToolCalls ? "tool-calls" : "stop";
		case "MAX_TOKENS":
		case "CONTINUATION": return "length";
		case "IMAGE_SAFETY":
		case "RECITATION":
		case "SAFETY":
		case "BLOCKLIST":
		case "PROHIBITED_CONTENT":
		case "SPII": return "content-filter";
		case "MALFORMED_FUNCTION_CALL": return "error";
		default: return "other";
	}
}
//#endregion
//#region src/google-language-model.ts
const configurableSafetySettingCategories = [
	"HARM_CATEGORY_HATE_SPEECH",
	"HARM_CATEGORY_DANGEROUS_CONTENT",
	"HARM_CATEGORY_HARASSMENT",
	"HARM_CATEGORY_SEXUALLY_EXPLICIT"
];
const googleCloudStorageFunctionResponseUrls = {
	"image/png": [/^gs:\/\/.*$/],
	"image/jpeg": [/^gs:\/\/.*$/],
	"image/webp": [/^gs:\/\/.*$/],
	"application/pdf": [/^gs:\/\/.*$/],
	"text/plain": [/^gs:\/\/.*$/]
};
var GoogleLanguageModel = class GoogleLanguageModel {
	static [WORKFLOW_SERIALIZE](model) {
		return serializeModelOptions({
			modelId: model.modelId,
			config: model.config
		});
	}
	static [WORKFLOW_DESERIALIZE](options) {
		return new GoogleLanguageModel(options.modelId, options.config);
	}
	constructor(modelId, config) {
		this.specificationVersion = "v4";
		this.modelId = modelId;
		this.config = config;
		this.generateId = config.generateId ?? generateId;
	}
	get provider() {
		return this.config.provider;
	}
	get supportedUrls() {
		return this.config.supportedUrls?.() ?? {};
	}
	static async prepareRequest({ modelId, config, options: { prompt, maxOutputTokens, temperature, topP, topK, frequencyPenalty, presencePenalty, stopSequences, responseFormat, seed, tools, toolChoice, reasoning, providerOptions, abortSignal }, isStreaming = false }) {
		const warnings = [];
		const providerOptionsNames = config.provider.includes("vertex") ? ["googleVertex", "vertex"] : ["google"];
		const { options: googleOptions } = await resolveProviderOptions({
			providerOptions,
			namespaces: [...providerOptionsNames, "google"],
			resolution: "first",
			schema: googleLanguageModelOptions
		});
		const isVertexProvider = config.provider.startsWith("google.vertex.");
		if (tools?.some((tool) => tool.type === "provider" && tool.id === "google.vertex_rag_store") && !isVertexProvider) warnings.push({
			type: "other",
			message: `The 'vertex_rag_store' tool is only supported with the Google Vertex provider and might not be supported or could behave unexpectedly with the current Google provider (${config.provider}).`
		});
		if (googleOptions?.streamFunctionCallArguments && !isVertexProvider) warnings.push({
			type: "other",
			message: `'streamFunctionCallArguments' is only supported on the Vertex AI API and will be ignored with the current Google provider (${config.provider}). See https://docs.cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling#streaming-fc`
		});
		if (googleOptions?.serviceTier && isVertexProvider) warnings.push({
			type: "other",
			message: "'serviceTier' is a Gemini API option and is not supported on Vertex AI. Use 'sharedRequestType' (and optionally 'requestType') instead. See https://docs.cloud.google.com/vertex-ai/generative-ai/docs/priority-paygo"
		});
		if ((googleOptions?.sharedRequestType || googleOptions?.requestType) && !isVertexProvider) warnings.push({
			type: "other",
			message: `'sharedRequestType' and 'requestType' are Vertex AI options and are ignored with the current Google provider (${config.provider}).`
		});
		const vertexPaygoHeaders = isVertexProvider && (googleOptions?.sharedRequestType || googleOptions?.requestType) ? {
			...googleOptions.sharedRequestType && { "X-Vertex-AI-LLM-Shared-Request-Type": googleOptions.sharedRequestType },
			...googleOptions.requestType && { "X-Vertex-AI-LLM-Request-Type": googleOptions.requestType }
		} : void 0;
		const bodyServiceTier = isVertexProvider ? void 0 : googleOptions?.serviceTier;
		let imageConfig = googleOptions?.imageConfig;
		if (imageConfig != null && !isVertexProvider) {
			const { personGeneration, prominentPeople, imageOutputOptions, ...geminiApiImageConfig } = imageConfig;
			const droppedImageConfigFields = Object.entries({
				personGeneration,
				prominentPeople,
				imageOutputOptions
			}).filter(([, value]) => value != null).map(([key]) => `'imageConfig.${key}'`);
			if (droppedImageConfigFields.length > 0) {
				warnings.push({
					type: "other",
					message: `${droppedImageConfigFields.join(", ")} ${droppedImageConfigFields.length === 1 ? "is a Vertex AI option and is" : "are Vertex AI options and are"} ignored with the current Google provider (${config.provider}).`
				});
				imageConfig = geminiApiImageConfig;
			}
		}
		const entry = getGoogleLanguageModelEntry(modelId);
		const isGemmaModel = entry.requestConfig.language.systemInstruction === "user";
		const isGemini25DeveloperApiModel = !isVertexProvider && entry.capabilities.language.sampling.penalties !== true;
		if (isGemini25DeveloperApiModel && frequencyPenalty != null) warnings.push({
			type: "unsupported",
			feature: "frequencyPenalty"
		});
		if (isGemini25DeveloperApiModel && presencePenalty != null) warnings.push({
			type: "unsupported",
			feature: "presencePenalty"
		});
		const usesGemini3Features = entry.capabilities.language.tools.functionResponseParts;
		const supportedFunctionResponseUrls = usesGemini3Features && config.downloadToolResultFiles?.supportsGoogleCloudStorageUrls ? googleCloudStorageFunctionResponseUrls : void 0;
		const { contents, systemInstruction } = convertToGoogleMessages(config.downloadToolResultFiles ? await downloadToolResultFiles(prompt, {
			abortSignal,
			maxBytes: config.downloadToolResultFiles.maxBytes,
			fetch: config.downloadToolResultFiles.fetch,
			supportedUrls: supportedFunctionResponseUrls
		}) : prompt, {
			isGemmaModel,
			isGemini3Model: usesGemini3Features,
			onWarning: (warning) => warnings.push(warning),
			providerOptionsNames,
			supportsFunctionResponseParts: usesGemini3Features,
			includeFunctionCallIds: !isVertexProvider,
			supportedFunctionResponseUrls
		});
		const { tools: googleTools, toolConfig: googleToolConfig, toolWarnings } = prepareTools({
			tools,
			toolChoice,
			modelId,
			isVertexProvider
		});
		const toolNameMapping = createToolNameMapping({
			tools,
			providerToolNames: { "google.code_execution": "code_execution" }
		});
		const resolvedThinking = googleOptions?.thinkingConfig?.thinkingBudget != null || googleOptions?.thinkingConfig?.thinkingLevel != null ? void 0 : resolveThinkingConfig({
			reasoning,
			modelId,
			warnings
		});
		const thinkingConfig = googleOptions?.thinkingConfig || resolvedThinking ? {
			...resolvedThinking,
			...googleOptions?.thinkingConfig
		} : void 0;
		const streamFunctionCallArguments = isStreaming && isVertexProvider ? googleOptions?.streamFunctionCallArguments ?? false : void 0;
		const safetyThreshold = googleOptions?.threshold;
		const safetySettings = googleOptions?.safetySettings ?? (safetyThreshold != null ? configurableSafetySettingCategories.map((category) => ({
			category,
			threshold: safetyThreshold
		})) : void 0);
		const toolConfig = googleToolConfig || streamFunctionCallArguments || googleOptions?.retrievalConfig ? {
			...googleToolConfig,
			...streamFunctionCallArguments && { functionCallingConfig: {
				...googleToolConfig?.functionCallingConfig,
				streamFunctionCallArguments: true
			} },
			...googleOptions?.retrievalConfig && { retrievalConfig: googleOptions.retrievalConfig }
		} : void 0;
		return {
			args: {
				generationConfig: {
					maxOutputTokens,
					temperature,
					topK,
					topP,
					frequencyPenalty: isGemini25DeveloperApiModel ? void 0 : frequencyPenalty,
					presencePenalty: isGemini25DeveloperApiModel ? void 0 : presencePenalty,
					stopSequences,
					seed,
					responseMimeType: responseFormat?.type === "json" ? "application/json" : void 0,
					responseJsonSchema: responseFormat?.type === "json" && responseFormat.schema != null && (googleOptions?.structuredOutputs ?? true) ? sanitizeResponseJsonSchema(responseFormat.schema) : void 0,
					...googleOptions?.audioTimestamp && { audioTimestamp: googleOptions.audioTimestamp },
					responseModalities: googleOptions?.responseModalities,
					thinkingConfig,
					...googleOptions?.mediaResolution && { mediaResolution: googleOptions.mediaResolution },
					...imageConfig && { imageConfig }
				},
				contents,
				systemInstruction: isGemmaModel ? void 0 : systemInstruction,
				safetySettings,
				tools: googleTools,
				toolConfig,
				cachedContent: googleOptions?.cachedContent,
				labels: googleOptions?.labels,
				serviceTier: bodyServiceTier,
				...googleOptions?.continuationToken != null && { continuationToken: googleOptions.continuationToken }
			},
			warnings: [...warnings, ...toolWarnings],
			providerOptionsNames,
			extraHeaders: vertexPaygoHeaders,
			toolNameMapping
		};
	}
	async getArgs(options, { isStreaming = false } = {}) {
		const { responseFormat, toolChoice } = options;
		const tools = toolChoice?.type === "none" ? options.tools?.filter((tool) => tool.type === "function") : options.tools;
		const entry = getGoogleLanguageModelEntry(this.modelId);
		const { structuredOutput } = entry.capabilities.language;
		let jsonResponseTool;
		if (responseFormat?.type === "json" && responseFormat.schema != null && tools?.some((tool) => tool.type === "function") && entry.requestConfig.language.jsonResponseTool && (!structuredOutput.withFunctionTools || toolChoice?.type === "required" || toolChoice?.type === "tool")) {
			let name = "json";
			for (let suffix = 1; tools.some((tool) => tool.name === name); suffix++) name = `json_${suffix}`;
			jsonResponseTool = {
				type: "function",
				name,
				description: responseFormat.description ?? "Respond with a JSON object.",
				inputSchema: responseFormat.schema
			};
		}
		return {
			...await GoogleLanguageModel.prepareRequest({
				modelId: this.modelId,
				config: this.config,
				options: jsonResponseTool == null ? options : {
					...options,
					responseFormat: void 0,
					tools: [...tools ?? [], jsonResponseTool],
					toolChoice: toolChoice?.type === "tool" ? toolChoice : toolChoice?.type === "none" ? {
						type: "tool",
						toolName: jsonResponseTool.name
					} : { type: "required" }
				},
				isStreaming
			}),
			jsonResponseToolName: jsonResponseTool?.name
		};
	}
	static convertGenerateContentResponse({ config, response, responseHeaders, warnings, providerOptionsNames, toolNameMapping, jsonResponseToolName, initialPromptTokenCount, groundingMetadataPerRequest }) {
		const wrapProviderMetadata = (payload) => Object.fromEntries(providerOptionsNames.map((name) => [name, payload]));
		const candidate = response.candidates?.[0];
		const promptBlockReason = response.promptFeedback?.blockReason;
		const confirmedPromptBlockReason = isConfirmedPromptBlockReason(promptBlockReason) ? promptBlockReason : void 0;
		const isPromptBlocked = candidate?.finishReason == null && confirmedPromptBlockReason != null;
		const rawFinishReason = candidate?.finishReason ?? confirmedPromptBlockReason;
		const content = [];
		const parts = candidate?.content?.parts ?? [];
		const usageMetadata = response.usageMetadata;
		let lastCodeExecutionToolCallId;
		let lastServerToolCallId;
		for (const part of parts) if ("executableCode" in part && part.executableCode?.code) {
			const toolCallId = config.generateId();
			lastCodeExecutionToolCallId = toolCallId;
			content.push({
				type: "tool-call",
				toolCallId,
				toolName: toolNameMapping?.toCustomToolName("code_execution") ?? "code_execution",
				input: JSON.stringify(part.executableCode),
				providerExecuted: true
			});
		} else if ("codeExecutionResult" in part && part.codeExecutionResult) content.push({
			type: "tool-result",
			toolCallId: lastCodeExecutionToolCallId,
			toolName: toolNameMapping?.toCustomToolName("code_execution") ?? "code_execution",
			result: {
				outcome: part.codeExecutionResult.outcome,
				output: part.codeExecutionResult.output ?? ""
			}
		});
		else if ("text" in part && part.text != null) {
			if (jsonResponseToolName != null && part.thought !== true) continue;
			const thoughtSignatureMetadata = part.thoughtSignature ? wrapProviderMetadata({ thoughtSignature: part.thoughtSignature }) : void 0;
			if (part.text.length === 0) {
				if (thoughtSignatureMetadata != null && content.length > 0) {
					const lastContent = content[content.length - 1];
					lastContent.providerMetadata = thoughtSignatureMetadata;
				}
			} else content.push({
				type: part.thought === true ? "reasoning" : "text",
				text: part.text,
				providerMetadata: thoughtSignatureMetadata
			});
		} else if ("functionCall" in part && part.functionCall.name != null) {
			if (part.functionCall.name === jsonResponseToolName) {
				content.push({
					type: "text",
					text: typeof part.functionCall.args === "string" ? part.functionCall.args : JSON.stringify(part.functionCall.args ?? {}),
					...part.thoughtSignature ? { providerMetadata: wrapProviderMetadata({ thoughtSignature: part.thoughtSignature }) } : {}
				});
				continue;
			}
			content.push({
				type: "tool-call",
				toolCallId: part.functionCall.id || config.generateId(),
				toolName: part.functionCall.name,
				input: JSON.stringify(part.functionCall.args ?? {}),
				providerMetadata: part.thoughtSignature ? wrapProviderMetadata({ thoughtSignature: part.thoughtSignature }) : void 0
			});
		} else if ("inlineData" in part) {
			const hasThought = part.thought === true;
			const hasThoughtSignature = !!part.thoughtSignature;
			content.push({
				type: hasThought ? "reasoning-file" : "file",
				data: {
					type: "data",
					data: part.inlineData.data
				},
				mediaType: part.inlineData.mimeType,
				providerMetadata: hasThoughtSignature ? wrapProviderMetadata({ thoughtSignature: part.thoughtSignature }) : void 0
			});
		} else if ("toolCall" in part && part.toolCall) {
			const toolCallId = part.toolCall.id || config.generateId();
			lastServerToolCallId = toolCallId;
			content.push({
				type: "tool-call",
				toolCallId,
				toolName: `server:${part.toolCall.toolType}`,
				input: JSON.stringify(part.toolCall.args ?? {}),
				providerExecuted: true,
				dynamic: true,
				providerMetadata: part.thoughtSignature ? wrapProviderMetadata({
					thoughtSignature: part.thoughtSignature,
					serverToolCallId: toolCallId,
					serverToolType: part.toolCall.toolType
				}) : wrapProviderMetadata({
					serverToolCallId: toolCallId,
					serverToolType: part.toolCall.toolType
				})
			});
		} else if ("toolResponse" in part && part.toolResponse) {
			const responseToolCallId = lastServerToolCallId || part.toolResponse.id || config.generateId();
			content.push({
				type: "tool-result",
				toolCallId: responseToolCallId,
				toolName: `server:${part.toolResponse.toolType}`,
				result: part.toolResponse.response ?? {},
				providerMetadata: part.thoughtSignature ? wrapProviderMetadata({
					thoughtSignature: part.thoughtSignature,
					serverToolCallId: responseToolCallId,
					serverToolType: part.toolResponse.toolType
				}) : wrapProviderMetadata({
					serverToolCallId: responseToolCallId,
					serverToolType: part.toolResponse.toolType
				})
			});
			lastServerToolCallId = void 0;
		}
		const sources = extractSources({
			groundingMetadata: candidate?.groundingMetadata,
			generateId: config.generateId
		}) ?? [];
		for (const source of sources) content.push(source);
		return {
			content,
			finishReason: {
				unified: isPromptBlocked ? "content-filter" : mapGoogleFinishReason({
					finishReason: rawFinishReason,
					hasToolCalls: content.some((part) => part.type === "tool-call" && !part.providerExecuted)
				}),
				raw: rawFinishReason
			},
			usage: convertGoogleUsage(usageMetadata),
			warnings,
			providerMetadata: wrapProviderMetadata({
				promptFeedback: response.promptFeedback ?? null,
				groundingMetadata: candidate?.groundingMetadata ?? null,
				...groundingMetadataPerRequest != null ? { groundingMetadataPerRequest } : {},
				urlContextMetadata: candidate?.urlContextMetadata ?? null,
				safetyRatings: candidate?.safetyRatings ?? null,
				usageMetadata: usageMetadata ?? null,
				finishMessage: candidate?.finishMessage ?? null,
				serviceTier: usageMetadata?.serviceTier ?? responseHeaders?.["x-gemini-service-tier"] ?? null,
				...candidate?.continuationToken != null ? { continuationToken: candidate.continuationToken } : {},
				...initialPromptTokenCount != null ? { initialPromptTokenCount } : {}
			}),
			response: { id: response.responseId ?? void 0 }
		};
	}
	async doGenerate(options) {
		const { args, warnings, providerOptionsNames, extraHeaders, toolNameMapping, jsonResponseToolName } = await this.getArgs(options);
		const generateContent = async (body) => postJsonToApi({
			url: `${this.config.baseURL}/${getModelPath(this.modelId)}:generateContent`,
			headers: combineHeaders(await resolve(this.config.headers), options.headers, extraHeaders),
			body,
			failedResponseHandler: googleFailedResponseHandler,
			successfulResponseHandler: createJsonResponseHandler(responseSchema),
			abortSignal: options.abortSignal,
			fetch: this.config.fetch
		});
		let { responseHeaders, value: response, rawValue: rawResponse } = await generateContent(args);
		const content = [];
		const groundingMetadataPerRequest = [];
		let usageMetadata;
		const firstPromptTokenCount = response.usageMetadata?.promptTokenCount;
		let isContinued = false;
		while (true) {
			const candidate = response.candidates?.[0];
			groundingMetadataPerRequest.push(candidate?.groundingMetadata ?? null);
			const continuationToken = candidate?.finishReason === "CONTINUATION" ? candidate.continuationToken : void 0;
			usageMetadata = addGoogleUsageMetadata(usageMetadata, response.usageMetadata ?? void 0);
			if (continuationToken == null) break;
			content.push(...GoogleLanguageModel.convertGenerateContentResponse({
				config: this.config,
				response,
				warnings,
				providerOptionsNames,
				toolNameMapping,
				jsonResponseToolName
			}).content);
			isContinued = true;
			({responseHeaders, value: response, rawValue: rawResponse} = await generateContent({
				...args,
				continuationToken
			}));
		}
		const lastResult = GoogleLanguageModel.convertGenerateContentResponse({
			config: this.config,
			response: {
				...response,
				usageMetadata
			},
			responseHeaders,
			warnings,
			providerOptionsNames,
			toolNameMapping,
			jsonResponseToolName,
			initialPromptTokenCount: isContinued ? firstPromptTokenCount : void 0,
			groundingMetadataPerRequest: isContinued ? groundingMetadataPerRequest : void 0
		});
		const result = {
			...lastResult,
			content: [...content, ...lastResult.content]
		};
		return {
			...result,
			request: { body: args },
			response: {
				...result.response,
				headers: responseHeaders,
				body: rawResponse
			}
		};
	}
	async doStream(options) {
		const { args, warnings, providerOptionsNames, extraHeaders, toolNameMapping, jsonResponseToolName } = await this.getArgs(options, { isStreaming: true });
		const wrapProviderMetadata = (payload) => Object.fromEntries(providerOptionsNames.map((name) => [name, payload]));
		const streamGenerateContent = async (body) => postJsonToApi({
			url: `${this.config.baseURL}/${getModelPath(this.modelId)}:streamGenerateContent?alt=sse`,
			headers: combineHeaders(await resolve(this.config.headers), options.headers, extraHeaders),
			body,
			failedResponseHandler: googleFailedResponseHandler,
			successfulResponseHandler: createEventSourceResponseHandler(chunkSchema),
			abortSignal: options.abortSignal,
			fetch: this.config.fetch
		});
		const { responseHeaders, value: firstResponse } = await streamGenerateContent(args);
		const groundingMetadataPerRequest = [];
		let requestGroundingMetadata = null;
		const response = continueGoogleStream({
			stream: firstResponse,
			getContinuationToken: (chunk) => {
				const candidate = chunk.candidates?.[0];
				if (candidate?.groundingMetadata != null) requestGroundingMetadata = candidate.groundingMetadata;
				return candidate?.finishReason === "CONTINUATION" ? candidate.continuationToken ?? void 0 : void 0;
			},
			continueStream: async (continuationToken) => {
				const { value } = await streamGenerateContent({
					...args,
					continuationToken
				});
				groundingMetadataPerRequest.push(requestGroundingMetadata);
				requestGroundingMetadata = null;
				return value;
			}
		});
		let finishReason = {
			unified: "interrupted",
			raw: void 0
		};
		let usage = void 0;
		let continuedUsage = void 0;
		let initialPromptTokenCount = void 0;
		let promptFeedback = null;
		let lastGroundingMetadata = null;
		let lastUrlContextMetadata = null;
		let lastSafetyRatings = null;
		let lastFinishMessage = null;
		let lastContinuationToken = null;
		let confirmedPromptBlockReason;
		const generateId = this.config.generateId;
		let hasToolCalls = false;
		let hasEmittedResponseMetadata = false;
		let currentTextBlockId = null;
		let currentReasoningBlockId = null;
		let blockCounter = 0;
		const emittedSourceKeys = /* @__PURE__ */ new Set();
		let lastCodeExecutionToolCallId;
		let lastServerToolCallId;
		const activeStreamingToolCalls = [];
		const finishActiveStreamingToolCall = (controller) => {
			const active = activeStreamingToolCalls.pop();
			if (active == null) return;
			if (currentTextBlockId !== null) {
				controller.enqueue({
					type: "text-end",
					id: currentTextBlockId
				});
				currentTextBlockId = null;
			}
			if (currentReasoningBlockId !== null) {
				controller.enqueue({
					type: "reasoning-end",
					id: currentReasoningBlockId
				});
				currentReasoningBlockId = null;
			}
			const { finalJSON, closingDelta } = active.accumulator.finalize();
			if (closingDelta.length > 0) controller.enqueue({
				type: "tool-input-delta",
				id: active.toolCallId,
				delta: closingDelta,
				providerMetadata: active.providerMetadata
			});
			controller.enqueue({
				type: "tool-input-end",
				id: active.toolCallId,
				providerMetadata: active.providerMetadata
			});
			controller.enqueue({
				type: "tool-call",
				toolCallId: active.toolCallId,
				toolName: active.toolName,
				input: finalJSON,
				providerMetadata: active.providerMetadata
			});
			hasToolCalls = true;
		};
		const result = {
			stream: response.pipeThrough(new TransformStream({
				start(controller) {
					controller.enqueue({
						type: "stream-start",
						warnings
					});
				},
				transform(chunk, controller) {
					if ("type" in chunk) {
						finishReason = {
							unified: "interrupted",
							raw: void 0
						};
						return;
					}
					if (options.includeRawChunks) controller.enqueue({
						type: "raw",
						rawValue: chunk.rawValue
					});
					if (!chunk.success) {
						if (finishReason.unified === "interrupted") finishReason = {
							unified: "error",
							raw: void 0
						};
						controller.enqueue({
							type: "error",
							error: chunk.error
						});
						return;
					}
					const value = chunk.value;
					if (value.error != null) {
						finishReason = {
							unified: "error",
							raw: void 0
						};
						const error = value.error;
						const statusCode = error.code ?? mapGoogleErrorStatusToHttpStatusCode(error.status);
						controller.enqueue({
							type: "error",
							error: createProviderStreamError({
								message: error.message,
								type: error.status,
								code: error.code ?? void 0,
								statusCode,
								isRetryable: statusCode != null && (statusCode === 408 || statusCode === 409 || statusCode === 429 || statusCode >= 500),
								data: error
							})
						});
						return;
					}
					if (!hasEmittedResponseMetadata && value.responseId != null) {
						hasEmittedResponseMetadata = true;
						controller.enqueue({
							type: "response-metadata",
							id: value.responseId
						});
					}
					const usageMetadata = value.usageMetadata;
					if (usageMetadata != null) usage = usageMetadata;
					if (value.promptFeedback != null && confirmedPromptBlockReason == null) {
						promptFeedback = value.promptFeedback;
						if (isConfirmedPromptBlockReason(value.promptFeedback.blockReason)) {
							confirmedPromptBlockReason = value.promptFeedback.blockReason;
							finishReason = {
								unified: "content-filter",
								raw: confirmedPromptBlockReason
							};
						}
					}
					const candidate = value.candidates?.[0];
					if (candidate != null) {
						if (candidate.groundingMetadata != null) lastGroundingMetadata = candidate.groundingMetadata;
						if (candidate.urlContextMetadata != null) lastUrlContextMetadata = candidate.urlContextMetadata;
						if (candidate.safetyRatings != null) lastSafetyRatings = candidate.safetyRatings;
						if (candidate.finishMessage != null) lastFinishMessage = candidate.finishMessage;
						if (candidate.continuationToken != null) lastContinuationToken = candidate.continuationToken;
					}
					if (confirmedPromptBlockReason != null || candidate == null) return;
					const content = candidate.content;
					const sources = extractSources({
						groundingMetadata: candidate.groundingMetadata,
						generateId,
						emittedSourceKeys
					});
					if (sources != null) for (const source of sources) controller.enqueue(source);
					if (content != null) {
						const parts = content.parts ?? [];
						for (const part of parts) if ("executableCode" in part && part.executableCode?.code) {
							const toolCallId = generateId();
							lastCodeExecutionToolCallId = toolCallId;
							controller.enqueue({
								type: "tool-call",
								toolCallId,
								toolName: toolNameMapping.toCustomToolName("code_execution"),
								input: JSON.stringify(part.executableCode),
								providerExecuted: true
							});
						} else if ("codeExecutionResult" in part && part.codeExecutionResult) {
							const toolCallId = lastCodeExecutionToolCallId;
							if (toolCallId) controller.enqueue({
								type: "tool-result",
								toolCallId,
								toolName: toolNameMapping.toCustomToolName("code_execution"),
								result: {
									outcome: part.codeExecutionResult.outcome,
									output: part.codeExecutionResult.output ?? ""
								}
							});
						} else if ("text" in part && part.text != null) {
							const thoughtSignatureMetadata = part.thoughtSignature ? wrapProviderMetadata({ thoughtSignature: part.thoughtSignature }) : void 0;
							if (part.text.length === 0) {
								if (thoughtSignatureMetadata != null) {
									if (currentTextBlockId !== null) controller.enqueue({
										type: "text-delta",
										id: currentTextBlockId,
										delta: "",
										providerMetadata: thoughtSignatureMetadata
									});
									else {
										if (currentReasoningBlockId !== null) {
											controller.enqueue({
												type: "reasoning-end",
												id: currentReasoningBlockId
											});
											currentReasoningBlockId = null;
										}
										const id = String(blockCounter++);
										const type = part.thought === true ? "reasoning" : "text";
										controller.enqueue({
											type: `${type}-start`,
											id,
											providerMetadata: thoughtSignatureMetadata
										});
										controller.enqueue({
											type: `${type}-delta`,
											id,
											delta: "",
											providerMetadata: thoughtSignatureMetadata
										});
										controller.enqueue({
											type: `${type}-end`,
											id
										});
									}
								}
							} else if (part.thought === true) {
								if (currentTextBlockId !== null) {
									controller.enqueue({
										type: "text-end",
										id: currentTextBlockId
									});
									currentTextBlockId = null;
								}
								if (currentReasoningBlockId === null) {
									currentReasoningBlockId = String(blockCounter++);
									controller.enqueue({
										type: "reasoning-start",
										id: currentReasoningBlockId,
										providerMetadata: thoughtSignatureMetadata
									});
								}
								controller.enqueue({
									type: "reasoning-delta",
									id: currentReasoningBlockId,
									delta: part.text,
									providerMetadata: thoughtSignatureMetadata
								});
							} else {
								if (currentReasoningBlockId !== null) {
									controller.enqueue({
										type: "reasoning-end",
										id: currentReasoningBlockId
									});
									currentReasoningBlockId = null;
								}
								if (currentTextBlockId === null) {
									currentTextBlockId = String(blockCounter++);
									controller.enqueue({
										type: "text-start",
										id: currentTextBlockId,
										providerMetadata: thoughtSignatureMetadata
									});
								}
								controller.enqueue({
									type: "text-delta",
									id: currentTextBlockId,
									delta: part.text,
									providerMetadata: thoughtSignatureMetadata
								});
							}
						} else if ("inlineData" in part) {
							if (currentTextBlockId !== null) {
								controller.enqueue({
									type: "text-end",
									id: currentTextBlockId
								});
								currentTextBlockId = null;
							}
							if (currentReasoningBlockId !== null) {
								controller.enqueue({
									type: "reasoning-end",
									id: currentReasoningBlockId
								});
								currentReasoningBlockId = null;
							}
							const hasThought = part.thought === true;
							const fileMeta = !!part.thoughtSignature ? wrapProviderMetadata({ thoughtSignature: part.thoughtSignature }) : void 0;
							controller.enqueue({
								type: hasThought ? "reasoning-file" : "file",
								mediaType: part.inlineData.mimeType,
								data: {
									type: "data",
									data: part.inlineData.data
								},
								providerMetadata: fileMeta
							});
						} else if ("toolCall" in part && part.toolCall) {
							const toolCallId = part.toolCall.id || generateId();
							lastServerToolCallId = toolCallId;
							const serverMeta = wrapProviderMetadata({
								...part.thoughtSignature ? { thoughtSignature: part.thoughtSignature } : {},
								serverToolCallId: toolCallId,
								serverToolType: part.toolCall.toolType
							});
							controller.enqueue({
								type: "tool-call",
								toolCallId,
								toolName: `server:${part.toolCall.toolType}`,
								input: JSON.stringify(part.toolCall.args ?? {}),
								providerExecuted: true,
								dynamic: true,
								providerMetadata: serverMeta
							});
						} else if ("toolResponse" in part && part.toolResponse) {
							const responseToolCallId = lastServerToolCallId || part.toolResponse.id || generateId();
							const serverMeta = wrapProviderMetadata({
								...part.thoughtSignature ? { thoughtSignature: part.thoughtSignature } : {},
								serverToolCallId: responseToolCallId,
								serverToolType: part.toolResponse.toolType
							});
							controller.enqueue({
								type: "tool-result",
								toolCallId: responseToolCallId,
								toolName: `server:${part.toolResponse.toolType}`,
								result: part.toolResponse.response ?? {},
								providerMetadata: serverMeta
							});
							lastServerToolCallId = void 0;
						} else if ("functionCall" in part) {
							const providerMeta = part.thoughtSignature ? wrapProviderMetadata({ thoughtSignature: part.thoughtSignature }) : void 0;
							const isStreamingChunk = part.functionCall.partialArgs != null || part.functionCall.name != null && part.functionCall.willContinue === true;
							const isTerminalChunk = part.functionCall.name == null && part.functionCall.args == null && part.functionCall.partialArgs == null && part.functionCall.willContinue == null;
							const isCompleteCall = part.functionCall.name != null && part.functionCall.args != null && part.functionCall.partialArgs == null;
							const isNoArgsCompleteCall = part.functionCall.name != null && part.functionCall.args == null && part.functionCall.partialArgs == null && part.functionCall.willContinue !== true;
							if (!isStreamingChunk && (isCompleteCall || isNoArgsCompleteCall)) {
								if (currentTextBlockId !== null) {
									controller.enqueue({
										type: "text-end",
										id: currentTextBlockId
									});
									currentTextBlockId = null;
								}
								if (currentReasoningBlockId !== null) {
									controller.enqueue({
										type: "reasoning-end",
										id: currentReasoningBlockId
									});
									currentReasoningBlockId = null;
								}
							}
							if (isStreamingChunk) {
								if (part.functionCall.name != null) {
									const toolCallId = part.functionCall.id || generateId();
									const accumulator = new GoogleJSONAccumulator();
									activeStreamingToolCalls.push({
										toolCallId,
										toolName: part.functionCall.name,
										accumulator,
										providerMetadata: providerMeta
									});
									controller.enqueue({
										type: "tool-input-start",
										id: toolCallId,
										toolName: part.functionCall.name,
										providerMetadata: providerMeta
									});
									if (part.functionCall.partialArgs != null) {
										const partialArgs = part.functionCall.partialArgs;
										const { textDelta } = accumulator.processPartialArgs(partialArgs);
										if (textDelta.length > 0) controller.enqueue({
											type: "tool-input-delta",
											id: toolCallId,
											delta: textDelta,
											providerMetadata: providerMeta
										});
										if (part.functionCall.willContinue !== true && partialArgs.every((arg) => arg.willContinue !== true)) finishActiveStreamingToolCall(controller);
									}
								} else if (part.functionCall.partialArgs != null && activeStreamingToolCalls.length > 0) {
									const active = activeStreamingToolCalls[activeStreamingToolCalls.length - 1];
									const partialArgs = part.functionCall.partialArgs;
									const { textDelta } = active.accumulator.processPartialArgs(partialArgs);
									if (textDelta.length > 0) controller.enqueue({
										type: "tool-input-delta",
										id: active.toolCallId,
										delta: textDelta,
										providerMetadata: providerMeta
									});
									if (part.functionCall.willContinue !== true && partialArgs.every((arg) => arg.willContinue !== true)) finishActiveStreamingToolCall(controller);
								}
							} else if (isTerminalChunk && activeStreamingToolCalls.length > 0) finishActiveStreamingToolCall(controller);
							else if (isCompleteCall) {
								const toolCallId = part.functionCall.id || generateId();
								const toolName = part.functionCall.name;
								const args = typeof part.functionCall.args === "string" ? part.functionCall.args : JSON.stringify(part.functionCall.args ?? {});
								controller.enqueue({
									type: "tool-input-start",
									id: toolCallId,
									toolName,
									providerMetadata: providerMeta
								});
								controller.enqueue({
									type: "tool-input-delta",
									id: toolCallId,
									delta: args,
									providerMetadata: providerMeta
								});
								controller.enqueue({
									type: "tool-input-end",
									id: toolCallId,
									providerMetadata: providerMeta
								});
								controller.enqueue({
									type: "tool-call",
									toolCallId,
									toolName,
									input: args,
									providerMetadata: providerMeta
								});
								hasToolCalls = true;
							} else if (isNoArgsCompleteCall) {
								const toolCallId = part.functionCall.id || generateId();
								const toolName = part.functionCall.name;
								controller.enqueue({
									type: "tool-input-start",
									id: toolCallId,
									toolName,
									providerMetadata: providerMeta
								});
								controller.enqueue({
									type: "tool-input-end",
									id: toolCallId,
									providerMetadata: providerMeta
								});
								controller.enqueue({
									type: "tool-call",
									toolCallId,
									toolName,
									input: "{}",
									providerMetadata: providerMeta
								});
								hasToolCalls = true;
							}
						}
					}
					if (candidate.finishReason === "CONTINUATION") {
						if (continuedUsage == null) initialPromptTokenCount = usage?.promptTokenCount;
						continuedUsage = addGoogleUsageMetadata(continuedUsage, usage);
						usage = void 0;
					}
					if (candidate.finishReason != null) finishReason = {
						unified: mapGoogleFinishReason({
							finishReason: candidate.finishReason,
							hasToolCalls
						}),
						raw: candidate.finishReason
					};
				},
				flush(controller) {
					usage = addGoogleUsageMetadata(continuedUsage, usage);
					if (currentTextBlockId !== null) controller.enqueue({
						type: "text-end",
						id: currentTextBlockId
					});
					if (currentReasoningBlockId !== null) controller.enqueue({
						type: "reasoning-end",
						id: currentReasoningBlockId
					});
					controller.enqueue({
						type: "finish",
						finishReason,
						usage: convertGoogleUsage(usage),
						providerMetadata: wrapProviderMetadata({
							promptFeedback,
							groundingMetadata: lastGroundingMetadata,
							...groundingMetadataPerRequest.length > 0 ? { groundingMetadataPerRequest: [...groundingMetadataPerRequest, requestGroundingMetadata] } : {},
							urlContextMetadata: lastUrlContextMetadata,
							safetyRatings: lastSafetyRatings,
							usageMetadata: usage ?? null,
							finishMessage: lastFinishMessage,
							serviceTier: usage?.serviceTier ?? responseHeaders?.["x-gemini-service-tier"] ?? null,
							...lastContinuationToken != null && (finishReason.raw === "MAX_TOKENS" || finishReason.raw === "CONTINUATION") ? { continuationToken: lastContinuationToken } : {},
							...initialPromptTokenCount != null ? { initialPromptTokenCount } : {}
						})
					});
				}
			})),
			response: { headers: responseHeaders },
			request: { body: args }
		};
		return {
			...result,
			stream: jsonResponseToolName == null ? result.stream : result.stream.pipeThrough(convertJsonResponseToolStream(jsonResponseToolName))
		};
	}
};
function resolveThinkingConfig({ reasoning, modelId, warnings }) {
	if (!isCustomReasoning(reasoning)) return;
	if (getGoogleLanguageModelEntry(modelId).requestConfig.language.thinkingFormat === "level") return resolveGemini3ThinkingConfig({
		reasoning,
		modelId,
		warnings
	});
	return resolveGemini25ThinkingConfig({
		reasoning,
		modelId,
		warnings
	});
}
function resolveGemini3ThinkingConfig({ reasoning, modelId, warnings }) {
	const thinkingLevels = getGoogleLanguageModelEntry(modelId).capabilities.language.reasoning.efforts;
	const thinkingLevel = mapReasoningToProviderEffort({
		reasoning,
		effortMap: Object.fromEntries((thinkingLevels ?? []).map((level) => [level, level])),
		warnings
	});
	if (thinkingLevel == null) return;
	return { thinkingLevel };
}
function resolveGemini25ThinkingConfig({ reasoning, modelId, warnings }) {
	const entry = getGoogleLanguageModelEntry(modelId);
	const minThinkingBudget = entry.capabilities.language.reasoning.minBudget;
	if (reasoning === "none" && minThinkingBudget === 0) return { thinkingBudget: 0 };
	return { thinkingBudget: mapReasoningToProviderBudget({
		reasoning,
		maxOutputTokens: entry.requestConfig.language.reasoningMaxOutputTokens,
		maxReasoningBudget: entry.capabilities.language.reasoning.maxBudget,
		minReasoningBudget: minThinkingBudget,
		warnings
	}) };
}
function extractSources({ groundingMetadata, generateId, emittedSourceKeys }) {
	if (!groundingMetadata?.groundingChunks) return;
	const sources = [];
	const addSource = (source, key) => {
		if (emittedSourceKeys?.has(key)) return;
		emittedSourceKeys?.add(key);
		sources.push(source);
	};
	for (const chunk of groundingMetadata.groundingChunks) if (chunk.web != null) addSource({
		type: "source",
		sourceType: "url",
		id: generateId(),
		url: chunk.web.uri,
		title: chunk.web.title ?? void 0
	}, JSON.stringify(["url", chunk.web.uri]));
	else if (chunk.image != null) addSource({
		type: "source",
		sourceType: "url",
		id: generateId(),
		url: chunk.image.sourceUri,
		title: chunk.image.title ?? void 0
	}, JSON.stringify(["url", chunk.image.sourceUri]));
	else if (chunk.retrievedContext != null) {
		const uri = chunk.retrievedContext.uri;
		const fileSearchStore = chunk.retrievedContext.fileSearchStore;
		if (uri && (uri.startsWith("http://") || uri.startsWith("https://"))) addSource({
			type: "source",
			sourceType: "url",
			id: generateId(),
			url: uri,
			title: chunk.retrievedContext.title ?? void 0
		}, JSON.stringify(["url", uri]));
		else if (uri) {
			const title = chunk.retrievedContext.title ?? "Unknown Document";
			let mediaType = "application/octet-stream";
			let filename = void 0;
			if (uri.endsWith(".pdf")) {
				mediaType = "application/pdf";
				filename = uri.split("/").pop();
			} else if (uri.endsWith(".txt")) {
				mediaType = "text/plain";
				filename = uri.split("/").pop();
			} else if (uri.endsWith(".docx")) {
				mediaType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
				filename = uri.split("/").pop();
			} else if (uri.endsWith(".doc")) {
				mediaType = "application/msword";
				filename = uri.split("/").pop();
			} else if (uri.match(/\.(md|markdown)$/)) {
				mediaType = "text/markdown";
				filename = uri.split("/").pop();
			} else filename = uri.split("/").pop();
			addSource({
				type: "source",
				sourceType: "document",
				id: generateId(),
				mediaType,
				title,
				filename
			}, JSON.stringify(["document", uri]));
		} else if (fileSearchStore) {
			const title = chunk.retrievedContext.title ?? "Unknown Document";
			addSource({
				type: "source",
				sourceType: "document",
				id: generateId(),
				mediaType: "application/octet-stream",
				title,
				filename: fileSearchStore.split("/").pop()
			}, JSON.stringify([
				"file-search",
				fileSearchStore,
				title
			]));
		}
	} else if (chunk.maps != null) {
		if (chunk.maps.uri) addSource({
			type: "source",
			sourceType: "url",
			id: generateId(),
			url: chunk.maps.uri,
			title: chunk.maps.title ?? void 0
		}, JSON.stringify(["url", chunk.maps.uri]));
	}
	return sources.length > 0 ? sources : void 0;
}
const getGroundingMetadataSchema = () => z.object({
	webSearchQueries: z.array(z.string()).nullish(),
	imageSearchQueries: z.array(z.string()).nullish(),
	retrievalQueries: z.array(z.string()).nullish(),
	searchEntryPoint: z.object({ renderedContent: z.string() }).nullish(),
	groundingChunks: z.array(z.object({
		web: z.object({
			uri: z.string(),
			title: z.string().nullish()
		}).nullish(),
		image: z.object({
			sourceUri: z.string(),
			imageUri: z.string(),
			title: z.string().nullish(),
			domain: z.string().nullish()
		}).nullish(),
		retrievedContext: z.object({
			uri: z.string().nullish(),
			title: z.string().nullish(),
			text: z.string().nullish(),
			fileSearchStore: z.string().nullish(),
			customMetadata: z.array(z.object({
				key: z.string(),
				stringValue: z.string().nullish(),
				numericValue: z.number().nullish(),
				stringListValue: z.object({ values: z.array(z.string()).nullish() }).nullish()
			})).nullish()
		}).nullish(),
		maps: z.object({
			uri: z.string().nullish(),
			title: z.string().nullish(),
			text: z.string().nullish(),
			placeId: z.string().nullish()
		}).nullish()
	})).nullish(),
	groundingSupports: z.array(z.object({
		segment: z.object({
			startIndex: z.number().nullish(),
			endIndex: z.number().nullish(),
			text: z.string().nullish()
		}).nullish(),
		segment_text: z.string().nullish(),
		groundingChunkIndices: z.array(z.number()).nullish(),
		supportChunkIndices: z.array(z.number()).nullish(),
		confidenceScores: z.array(z.number()).nullish(),
		confidenceScore: z.array(z.number()).nullish()
	})).nullish(),
	retrievalMetadata: z.union([z.object({ webDynamicRetrievalScore: z.number() }), z.object({})]).nullish()
});
const partialArgSchema = () => z.object({
	jsonPath: z.string(),
	stringValue: z.string().nullish(),
	numberValue: z.number().nullish(),
	boolValue: z.boolean().nullish(),
	nullValue: z.unknown().nullish(),
	willContinue: z.boolean().nullish()
});
const getContentSchema = () => z.object({ parts: z.array(z.union([
	z.object({
		functionCall: z.object({
			id: z.string().nullish(),
			name: z.string().nullish(),
			args: z.unknown().nullish(),
			partialArgs: z.array(partialArgSchema()).nullish(),
			willContinue: z.boolean().nullish()
		}),
		thoughtSignature: z.string().nullish()
	}),
	z.object({
		inlineData: z.object({
			mimeType: z.string(),
			data: z.string()
		}),
		thought: z.boolean().nullish(),
		thoughtSignature: z.string().nullish()
	}),
	z.object({
		toolCall: z.object({
			toolType: z.string(),
			args: z.unknown().nullish(),
			id: z.string()
		}),
		thoughtSignature: z.string().nullish()
	}),
	z.object({
		toolResponse: z.object({
			toolType: z.string(),
			response: z.unknown().nullish(),
			id: z.string()
		}),
		thoughtSignature: z.string().nullish()
	}),
	z.object({
		executableCode: z.object({
			language: z.string(),
			code: z.string()
		}).nullish(),
		codeExecutionResult: z.object({
			outcome: z.string(),
			output: z.string().nullish()
		}).nullish(),
		text: z.string().nullish(),
		thought: z.boolean().nullish(),
		thoughtSignature: z.string().nullish()
	})
])).nullish() });
const getSafetyRatingSchema = () => z.object({
	category: z.string().nullish(),
	probability: z.string().nullish(),
	probabilityScore: z.number().nullish(),
	severity: z.string().nullish(),
	severityScore: z.number().nullish(),
	blocked: z.boolean().nullish()
});
const tokenDetailsSchema = () => z.array(z.object({
	modality: z.string(),
	tokenCount: z.number()
}).loose()).nullish();
const usageSchema$1 = () => z.object({
	cachedContentTokenCount: z.number().nullish(),
	thoughtsTokenCount: z.number().nullish(),
	promptTokenCount: z.number().nullish(),
	candidatesTokenCount: z.number().nullish(),
	toolUsePromptTokenCount: z.number().nullish(),
	totalTokenCount: z.number().nullish(),
	trafficType: z.string().nullish(),
	serviceTier: z.string().nullish(),
	promptTokensDetails: tokenDetailsSchema(),
	cacheTokensDetails: tokenDetailsSchema(),
	candidatesTokensDetails: tokenDetailsSchema(),
	toolUsePromptTokensDetails: tokenDetailsSchema()
}).loose();
const getUrlContextMetadataSchema = () => z.object({ urlMetadata: z.array(z.object({
	retrievedUrl: z.string(),
	urlRetrievalStatus: z.string()
})).nullish() });
const responseSchema = lazySchema(() => zodSchema(z.object({
	responseId: z.string().nullish(),
	candidates: z.array(z.object({
		content: getContentSchema().nullish().or(z.object({}).strict()),
		finishReason: z.string().nullish(),
		finishMessage: z.string().nullish(),
		continuationToken: z.string().nullish(),
		safetyRatings: z.array(getSafetyRatingSchema()).nullish(),
		groundingMetadata: getGroundingMetadataSchema().nullish(),
		urlContextMetadata: getUrlContextMetadataSchema().nullish()
	})).nullish(),
	usageMetadata: usageSchema$1().nullish(),
	promptFeedback: z.object({
		blockReason: z.string().nullish(),
		safetyRatings: z.array(getSafetyRatingSchema()).nullish()
	}).nullish()
})));
const chunkSchema = lazySchema(() => zodSchema(z.object({
	error: getGoogleErrorSchema().nullish(),
	responseId: z.string().nullish(),
	candidates: z.array(z.object({
		content: getContentSchema().nullish(),
		finishReason: z.string().nullish(),
		finishMessage: z.string().nullish(),
		continuationToken: z.string().nullish(),
		safetyRatings: z.array(getSafetyRatingSchema()).nullish(),
		groundingMetadata: getGroundingMetadataSchema().nullish(),
		urlContextMetadata: getUrlContextMetadataSchema().nullish()
	})).nullish(),
	usageMetadata: usageSchema$1().nullish(),
	promptFeedback: z.object({
		blockReason: z.string().nullish(),
		safetyRatings: z.array(getSafetyRatingSchema()).nullish()
	}).nullish()
})));
function isConfirmedPromptBlockReason(blockReason) {
	return blockReason != null && blockReason !== "" && blockReason !== "BLOCK_REASON_UNSPECIFIED" && blockReason !== "BLOCKED_REASON_UNSPECIFIED";
}
//#endregion
//#region src/google-speech-api.ts
/**
* Response schema for the Gemini `:generateContent` endpoint when called with
* `responseModalities: ['AUDIO']`. The generated audio is returned as base64
* encoded audio in the first inline-data part (WAV by default on Gemini 3.8,
* raw PCM on older models).
*/
const googleSpeechResponseSchema = lazySchema(() => zodSchema(z.object({
	candidates: z.array(z.object({ content: z.object({ parts: z.array(z.object({ inlineData: z.object({
		mimeType: z.string().nullish(),
		data: z.string().nullish()
	}).nullish() })).nullish() }).nullish() })).nullish(),
	usageMetadata: z.record(z.string(), z.json()).nullish()
})));
//#endregion
//#region src/google-speech-input.ts
/**
* Inspects speech input without replacing provider option validation. This is
* also used by intermediaries that need the transcript or voice kind before
* invoking a model. Model support for structured turns is validated separately.
*/
function getGoogleSpeechInput({ text, voice, providerOptions }) {
	const google = providerOptions?.google;
	const options = google != null && typeof google === "object" ? google : void 0;
	const turns = options && "turns" in options ? options.turns : void 0;
	const turnTexts = [];
	if (Array.isArray(turns) && turns.length > 0) {
		for (const turn of turns) {
			if (turn == null || typeof turn !== "object" || !("text" in turn) || typeof turn.text !== "string") break;
			turnTexts.push(turn.text);
		}
		if (turnTexts.length === turns.length) text = turnTexts.join("");
	}
	const config = options && "multiSpeakerVoiceConfig" in options ? options.multiSpeakerVoiceConfig : void 0;
	const speakers = config != null && typeof config === "object" && "speakerVoiceConfigs" in config && Array.isArray(config.speakerVoiceConfigs) ? config.speakerVoiceConfigs : [];
	return {
		text,
		usesCustomVoice: voice?.startsWith("voice_") === true || voice?.startsWith("voicekey_") === true || speakers.some((speaker) => speaker != null && typeof speaker === "object" && "voiceConfig" in speaker && speaker.voiceConfig != null && typeof speaker.voiceConfig === "object" && "voice" in speaker.voiceConfig)
	};
}
//#endregion
//#region src/google-speech-model-options.ts
const prebuiltVoiceConfigSchema = () => z.object({ voiceName: z.string() });
const voiceConfigSchema = () => z.object({
	prebuiltVoiceConfig: prebuiltVoiceConfigSchema(),
	voice: z.never().optional()
});
const speechMetadataSchema = () => z.object({
	speaker: z.string().min(1).optional(),
	style: z.string().optional()
});
const googleSpeechProviderOptionsSchema = lazySchema(() => zodSchema(z.object({
	/** Turn-level directions for the top-level text, for Gemini 3.8 TTS. */
	speechMetadata: speechMetadataSchema().optional(),
	/**
	* Structured transcript for Gemini 3.8 TTS. Replaces the top-level text;
	* pass text: '' when using turns. Each multi-speaker turn must name a
	* configured speaker in speechMetadata. Per-turn styles override instructions.
	*/
	turns: z.array(z.object({
		text: z.string(),
		speechMetadata: speechMetadataSchema().optional()
	})).min(1).optional(),
	/**
	* Multi-speaker configuration for dialogue audio. When provided, this
	* overrides the top-level `voice`. The Gemini TTS API supports up to two
	* speakers. For Gemini 3.8, each turn's speechMetadata.speaker must match
	* a configured speaker; older models use speaker labels in the text.
	*
	* https://ai.google.dev/gemini-api/docs/speech-generation#multi-speaker
	*/
	multiSpeakerVoiceConfig: z.object({ speakerVoiceConfigs: z.array(z.object({
		speaker: z.string(),
		voiceConfig: voiceConfigSchema()
	})) }).optional()
})));
//#endregion
//#region src/google-speech-model.ts
const DEFAULT_VOICE = "Kore";
const DEFAULT_SAMPLE_RATE = 24e3;
var GoogleSpeechModel = class GoogleSpeechModel {
	static [WORKFLOW_SERIALIZE](model) {
		return serializeModelOptions({
			modelId: model.modelId,
			config: model.config
		});
	}
	static [WORKFLOW_DESERIALIZE](options) {
		return new GoogleSpeechModel(options.modelId, options.config);
	}
	get provider() {
		return this.config.provider;
	}
	constructor(modelId, config) {
		this.modelId = modelId;
		this.config = config;
		this.specificationVersion = "v4";
	}
	async getArgs({ text, voice = DEFAULT_VOICE, outputFormat, instructions, speed, language, providerOptions }) {
		const warnings = [];
		const providerOptionsNames = this.config.provider.includes("vertex") ? ["googleVertex", "vertex"] : ["google"];
		let googleOptions;
		for (const name of providerOptionsNames) {
			googleOptions = await parseProviderOptions({
				provider: name,
				providerOptions,
				schema: googleSpeechProviderOptionsSchema
			});
			if (googleOptions != null) break;
		}
		if (googleOptions == null && !providerOptionsNames.includes("google")) googleOptions = await parseProviderOptions({
			provider: "google",
			providerOptions,
			schema: googleSpeechProviderOptionsSchema
		});
		const usesStructuredSpeech = !this.modelId.startsWith("gemini-2.5-") && !this.modelId.startsWith("gemini-3.1-");
		const input = getGoogleSpeechInput({
			text,
			voice,
			providerOptions: { google: googleOptions }
		});
		if (input.usesCustomVoice) throw new InvalidArgumentError({
			argument: "voice",
			message: "Custom voices are not supported. Use a prebuilt voice instead."
		});
		const multiSpeakerVoiceConfig = googleOptions?.multiSpeakerVoiceConfig;
		const languageCode = language === "auto" ? void 0 : language;
		const speechConfig = multiSpeakerVoiceConfig ? {
			multiSpeakerVoiceConfig,
			...languageCode != null && { languageCode }
		} : {
			voiceConfig: { prebuiltVoiceConfig: { voiceName: voice } },
			...languageCode != null && { languageCode }
		};
		let promptText = text;
		if (instructions != null && !usesStructuredSpeech) {
			if (multiSpeakerVoiceConfig) warnings.push({
				type: "unsupported",
				feature: "instructions",
				details: "Google Gemini TTS ignores `instructions` when `multiSpeakerVoiceConfig` is set, because prepending them would break multi-speaker transcript parsing."
			});
			else promptText = `${instructions}: ${text}`;
		}
		let parts = [{ text: promptText }];
		if (usesStructuredSpeech) {
			if (googleOptions?.turns && googleOptions.speechMetadata) throw new InvalidArgumentError({
				argument: "providerOptions",
				message: "Set speechMetadata on each turn when using turns."
			});
			if (googleOptions?.turns && text !== "") warnings.push({
				type: "unsupported",
				feature: "text",
				details: "Google TTS turns replace the top-level text."
			});
			parts = (googleOptions?.turns ?? [{
				text,
				speechMetadata: googleOptions?.speechMetadata
			}]).map((part) => {
				const style = part.speechMetadata?.style ?? instructions;
				const speaker = part.speechMetadata?.speaker;
				if (multiSpeakerVoiceConfig && !multiSpeakerVoiceConfig.speakerVoiceConfigs.some((config) => config.speaker === speaker)) throw new InvalidArgumentError({
					argument: "speechMetadata.speaker",
					message: "Every multi-speaker turn must specify a speechMetadata.speaker matching a configured speaker."
				});
				return {
					text: part.text,
					...style != null || speaker != null ? { speechMetadata: {
						style,
						speaker
					} } : {}
				};
			});
		} else if (googleOptions?.turns || googleOptions?.speechMetadata) throw new InvalidArgumentError({
			argument: "providerOptions",
			message: "Structured speech metadata and turns require Gemini 3.8 TTS."
		});
		if (input.text.length === 0) throw new InvalidArgumentError({
			argument: "text",
			message: "Speech input must contain a non-empty transcript."
		});
		if (speed != null) warnings.push({
			type: "unsupported",
			feature: "speed",
			details: "Google Gemini TTS models do not support the `speed` option. It was ignored."
		});
		const formats = usesStructuredSpeech ? {
			wav: "AUDIO_WAV",
			"audio/wav": "AUDIO_WAV",
			pcm: "AUDIO_L16",
			"audio/l16": "AUDIO_L16",
			mulaw: "AUDIO_MULAW",
			"audio/mulaw": "AUDIO_MULAW",
			alaw: "AUDIO_ALAW",
			"audio/alaw": "AUDIO_ALAW"
		} : {
			wav: "AUDIO_WAV",
			pcm: "AUDIO_L16"
		};
		let resolvedOutputFormat = "wav";
		if (outputFormat != null && Object.prototype.hasOwnProperty.call(formats, outputFormat)) resolvedOutputFormat = outputFormat;
		else if (outputFormat != null) warnings.push({
			type: "unsupported",
			feature: "outputFormat",
			details: `Unsupported output format: ${outputFormat}. Using wav instead.`
		});
		return {
			requestBody: {
				contents: [{
					role: "user",
					parts
				}],
				generationConfig: {
					responseModalities: ["AUDIO"],
					speechConfig,
					...usesStructuredSpeech && outputFormat != null ? { responseFormat: { audio: { mimeType: formats[resolvedOutputFormat] } } } : {}
				}
			},
			warnings,
			outputFormat: formats[resolvedOutputFormat],
			usesStructuredSpeech
		};
	}
	async doGenerate(options) {
		const currentDate = this.config._internal?.currentDate?.() ?? /* @__PURE__ */ new Date();
		const { requestBody, warnings, outputFormat, usesStructuredSpeech } = await this.getArgs(options);
		const { value: response, responseHeaders, rawValue: rawResponse } = await postJsonToApi({
			url: `${this.config.baseURL}/models/${this.modelId}:generateContent`,
			headers: combineHeaders(this.config.headers ? await resolve(this.config.headers) : void 0, options.headers),
			body: requestBody,
			failedResponseHandler: googleFailedResponseHandler,
			successfulResponseHandler: createJsonResponseHandler(googleSpeechResponseSchema),
			abortSignal: options.abortSignal,
			fetch: this.config.fetch
		});
		let base64Audio;
		let mimeType;
		for (const candidate of response.candidates ?? []) {
			for (const part of candidate.content?.parts ?? []) if (part.inlineData?.data) {
				base64Audio = part.inlineData.data;
				mimeType = part.inlineData.mimeType ?? void 0;
				break;
			}
			if (base64Audio != null) break;
		}
		const sampleRate = parseSampleRate(mimeType) ?? DEFAULT_SAMPLE_RATE;
		const bytes = base64Audio != null ? convertBase64ToUint8Array(base64Audio) : /* @__PURE__ */ new Uint8Array(0);
		const isPcm = /^audio\/(?:l16|pcm)(?:;|$)/i.test(mimeType ?? "") || mimeType == null && !usesStructuredSpeech;
		const audio = outputFormat === "AUDIO_WAV" && isPcm && bytes.length > 0 ? addWavHeader(bytes, sampleRate) : bytes;
		if (outputFormat === "AUDIO_L16" && bytes.length > 0 && !usesStructuredSpeech) warnings.push({
			type: "unsupported",
			feature: "outputFormat",
			details: `Returning raw PCM audio (signed 16-bit little-endian, mono, ${sampleRate} Hz). These bytes have no container header and are not directly playable; see providerMetadata.google for the sample rate and mime type.`
		});
		return {
			audio,
			warnings,
			...response.usageMetadata != null && { usage: response.usageMetadata },
			request: { body: JSON.stringify(requestBody) },
			response: {
				timestamp: currentDate,
				modelId: this.modelId,
				headers: responseHeaders,
				body: rawResponse
			},
			providerMetadata: { google: {
				sampleRate,
				mimeType: mimeType ?? null
			} }
		};
	}
};
/**
* Parses the sample rate from a PCM mime type such as `audio/L16;rate=24000`.
*/
function parseSampleRate(mimeType) {
	if (mimeType == null) return;
	const match = /rate=(\d+)/.exec(mimeType);
	return match ? Number.parseInt(match[1], 10) : void 0;
}
/**
* Wraps raw signed 16-bit little-endian mono PCM in a minimal 44-byte WAV
* (RIFF/WAVE) container so the output is playable and detectable as `audio/wav`.
*/
function addWavHeader(pcm, sampleRate) {
	const numChannels = 1;
	const bitsPerSample = 16;
	const blockAlign = 2;
	const byteRate = sampleRate * blockAlign;
	const dataSize = pcm.length;
	const buffer = new ArrayBuffer(44 + dataSize);
	const view = new DataView(buffer);
	writeAscii(view, 0, "RIFF");
	view.setUint32(4, 36 + dataSize, true);
	writeAscii(view, 8, "WAVE");
	writeAscii(view, 12, "fmt ");
	view.setUint32(16, 16, true);
	view.setUint16(20, 1, true);
	view.setUint16(22, numChannels, true);
	view.setUint32(24, sampleRate, true);
	view.setUint32(28, byteRate, true);
	view.setUint16(32, blockAlign, true);
	view.setUint16(34, bitsPerSample, true);
	writeAscii(view, 36, "data");
	view.setUint32(40, dataSize, true);
	const out = new Uint8Array(buffer);
	out.set(pcm, 44);
	return out;
}
function writeAscii(view, offset, text) {
	for (let i = 0; i < text.length; i++) view.setUint8(offset + i, text.charCodeAt(i));
}
//#endregion
//#region src/tool/enterprise-web-search.ts
const enterpriseWebSearch = createProviderExecutedToolFactory({
	id: "google.enterprise_web_search",
	inputSchema: lazySchema(() => zodSchema(z.object({}))),
	outputSchema: lazySchema(() => zodSchema(z.object({})))
});
//#endregion
//#region src/tool/file-search.ts
lazySchema(() => zodSchema(z.looseObject({
	/** The names of the file_search_stores to retrieve from.
	*  Example: `fileSearchStores/my-file-search-store-123`
	*/
	fileSearchStoreNames: z.array(z.string()).describe("The names of the file_search_stores to retrieve from. Example: `fileSearchStores/my-file-search-store-123`"),
	/** The number of file search retrieval chunks to retrieve. */
	topK: z.number().int().positive().describe("The number of file search retrieval chunks to retrieve.").optional(),
	/** Metadata filter to apply to the file search retrieval documents.
	*  See https://google.aip.dev/160 for the syntax of the filter expression.
	*/
	metadataFilter: z.string().describe("Metadata filter to apply to the file search retrieval documents. See https://google.aip.dev/160 for the syntax of the filter expression.").optional()
})));
const fileSearch = createProviderExecutedToolFactory({
	id: "google.file_search",
	inputSchema: lazySchema(() => zodSchema(z.object({}))),
	outputSchema: lazySchema(() => zodSchema(z.object({})))
});
//#endregion
//#region src/tool/google-maps.ts
const googleMaps = createProviderExecutedToolFactory({
	id: "google.google_maps",
	inputSchema: lazySchema(() => zodSchema(z.object({}))),
	outputSchema: lazySchema(() => zodSchema(z.object({})))
});
//#endregion
//#region src/google-tools.ts
const googleTools = {
	/**
	* Creates a Google search tool that gives Google direct access to real-time web content.
	* Must have name "google_search".
	*/
	googleSearch: createProviderExecutedToolFactory({
		id: "google.google_search",
		inputSchema: lazySchema(() => zodSchema(z.object({}))),
		outputSchema: lazySchema(() => zodSchema(z.object({})))
	}),
	/**
	* Creates an Enterprise Web Search tool for grounding responses using a compliance-focused web index.
	* Designed for highly-regulated industries (finance, healthcare, public sector).
	* Does not log customer data and supports VPC service controls.
	* Must have name "enterprise_web_search".
	*
	* @note Only available on Vertex AI. Requires Gemini 2.0 or newer.
	*
	* @see https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/web-grounding-enterprise
	*/
	enterpriseWebSearch,
	/**
	* Creates a Google Maps grounding tool that gives the model access to Google Maps data.
	* Must have name "google_maps".
	*
	* @see https://ai.google.dev/gemini-api/docs/maps-grounding
	* @see https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/grounding-with-google-maps
	*/
	googleMaps,
	/**
	* Creates a URL context tool that gives Google direct access to real-time web content.
	* Must have name "url_context".
	*/
	urlContext: createProviderExecutedToolFactory({
		id: "google.url_context",
		inputSchema: lazySchema(() => zodSchema(z.object({}))),
		outputSchema: lazySchema(() => zodSchema(z.object({})))
	}),
	/**
	* Enables Retrieval Augmented Generation (RAG) via the Gemini File Search tool.
	* Must have name "file_search".
	*
	* @param fileSearchStoreNames - Fully-qualified File Search store resource names.
	* @param metadataFilter - Optional filter expression to restrict the files that can be retrieved.
	* @param topK - Optional result limit for the number of chunks returned from File Search.
	*
	* @see https://ai.google.dev/gemini-api/docs/file-search
	*/
	fileSearch,
	/**
	* A tool that enables the model to generate and run Python code.
	* Must have name "code_execution".
	*
	* @note Ensure the selected model supports Code Execution.
	* Multi-tool usage with the code execution tool is typically compatible with Gemini >=2 models.
	*
	* @see https://ai.google.dev/gemini-api/docs/code-execution (Google AI)
	* @see https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/code-execution-api (Vertex AI)
	*/
	codeExecution,
	/**
	* Creates a Vertex RAG Store tool that enables the model to perform RAG searches against a Vertex RAG Store.
	* Must have name "vertex_rag_store".
	*/
	vertexRagStore: createProviderExecutedToolFactory({
		id: "google.vertex_rag_store",
		inputSchema: lazySchema(() => zodSchema(z.object({}))),
		outputSchema: lazySchema(() => zodSchema(z.object({})))
	})
};
//#endregion
//#region src/interactions/convert-google-interactions-usage.ts
function convertGoogleInteractionsUsage(usage) {
	if (usage == null) return createNullLanguageModelUsage();
	const totalInput = usage.total_input_tokens ?? 0;
	const totalOutput = usage.total_output_tokens ?? 0;
	const totalThought = usage.total_thought_tokens ?? 0;
	const totalCached = usage.total_cached_tokens ?? 0;
	return {
		inputTokens: {
			total: usage.total_input_tokens ?? void 0,
			noCache: usage.total_input_tokens == null ? void 0 : totalInput - totalCached,
			cacheRead: usage.total_cached_tokens ?? void 0,
			cacheWrite: void 0
		},
		outputTokens: {
			total: usage.total_output_tokens == null && usage.total_thought_tokens == null ? void 0 : totalOutput + totalThought,
			text: usage.total_output_tokens ?? void 0,
			reasoning: usage.total_thought_tokens ?? void 0
		},
		raw: usage
	};
}
/**
* Extracts the per-modality output token breakdown from an Interactions usage
* record (e.g. `{ video: 57920, text: 12 }`).
*/
function getGoogleInteractionsOutputTokensByModality(usage) {
	const byModality = usage?.output_tokens_by_modality;
	if (byModality == null) return;
	const result = {};
	for (const entry of byModality) if (entry?.modality != null && entry.tokens != null) result[entry.modality] = entry.tokens;
	return Object.keys(result).length > 0 ? result : void 0;
}
//#endregion
//#region src/interactions/extract-google-interactions-sources.ts
const KNOWN_DOC_EXTENSIONS = {
	pdf: "application/pdf",
	txt: "text/plain",
	md: "text/markdown",
	markdown: "text/markdown",
	doc: "application/msword",
	docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
};
function inferDocMediaType(uriOrName) {
	const lower = uriOrName.toLowerCase();
	for (const [ext, media] of Object.entries(KNOWN_DOC_EXTENSIONS)) if (lower.endsWith(`.${ext}`)) return media;
	return "application/octet-stream";
}
function basename(uriOrName) {
	const parts = uriOrName.split("/");
	const last = parts[parts.length - 1];
	return last && last.length > 0 ? last : void 0;
}
/**
* Maps a single text-block annotation (`url_citation` / `file_citation` /
* `place_citation`) onto a `LanguageModelV4Source`. Returns `undefined` when
* the annotation lacks the minimum payload to form a source (e.g. a URL
* citation without a `url`).
*/
function annotationToSource({ annotation, generateId }) {
	switch (annotation.type) {
		case "url_citation": {
			const urlCitation = annotation;
			if (urlCitation.url == null || urlCitation.url.length === 0) return;
			return {
				type: "source",
				sourceType: "url",
				id: generateId(),
				url: urlCitation.url,
				...urlCitation.title != null ? { title: urlCitation.title } : {}
			};
		}
		case "file_citation": {
			const fileCitation = annotation;
			const uri = fileCitation.url ?? fileCitation.document_uri ?? fileCitation.file_name;
			if (uri == null || uri.length === 0) return void 0;
			if (uri.startsWith("http://") || uri.startsWith("https://")) return {
				type: "source",
				sourceType: "url",
				id: generateId(),
				url: uri,
				...fileCitation.file_name != null ? { title: fileCitation.file_name } : {}
			};
			const filename = fileCitation.file_name ?? basename(uri);
			const mediaType = inferDocMediaType(uri);
			return {
				type: "source",
				sourceType: "document",
				id: generateId(),
				mediaType,
				title: fileCitation.file_name ?? filename ?? uri,
				...filename != null ? { filename } : {}
			};
		}
		case "place_citation": {
			const placeCitation = annotation;
			if (placeCitation.url == null || placeCitation.url.length === 0) return;
			return {
				type: "source",
				sourceType: "url",
				id: generateId(),
				url: placeCitation.url,
				...placeCitation.name != null ? { title: placeCitation.name } : {}
			};
		}
		default: return;
	}
}
/**
* Maps a built-in tool *result* content block to zero or more
* `LanguageModelV4Source` parts. The Interactions API exposes grounding
* sources both inline (via `text_annotation` deltas) and via tool-result
* content blocks; the latter is what this function consumes.
*
* Supported result kinds:
* - `url_context_result`   -> URL sources for each fetched URL with `status: 'success'`
* - `google_search_result` -> URL sources (when `url` is present), search-suggestion
*                              entries are skipped (they are HTML widgets, not citations)
* - `google_maps_result`   -> URL sources for each place with a `url`
* - `file_search_result`   -> document sources (best-effort -- `result[]` is loosely typed)
*/
function builtinToolResultToSources({ block, generateId }) {
	const sources = [];
	switch (block.type) {
		case "url_context_result": {
			const result = block.result ?? [];
			for (const entry of result) {
				if (entry?.url == null || entry.url.length === 0) continue;
				if (entry.status != null && entry.status !== "success") continue;
				sources.push({
					type: "source",
					sourceType: "url",
					id: generateId(),
					url: entry.url
				});
			}
			break;
		}
		case "google_search_result": {
			const result = block.result ?? [];
			for (const entry of result) {
				const url = entry?.url;
				if (url == null || url.length === 0) continue;
				sources.push({
					type: "source",
					sourceType: "url",
					id: generateId(),
					url,
					...entry.title != null ? { title: entry.title } : {}
				});
			}
			break;
		}
		case "google_maps_result": {
			const result = block.result ?? [];
			for (const entry of result) for (const place of entry.places ?? []) {
				if (place.url == null || place.url.length === 0) continue;
				sources.push({
					type: "source",
					sourceType: "url",
					id: generateId(),
					url: place.url,
					...place.name != null ? { title: place.name } : {}
				});
			}
			break;
		}
		case "file_search_result": {
			const result = block.result ?? [];
			for (const raw of result) {
				if (raw == null || typeof raw !== "object") continue;
				const entry = raw;
				const uri = entry.url ?? entry.document_uri ?? entry.file_name;
				if (uri == null || uri.length === 0) continue;
				if (uri.startsWith("http://") || uri.startsWith("https://")) {
					sources.push({
						type: "source",
						sourceType: "url",
						id: generateId(),
						url: uri,
						...entry.title != null ? { title: entry.title } : {}
					});
					continue;
				}
				const filename = entry.file_name ?? basename(uri);
				const mediaType = inferDocMediaType(uri);
				sources.push({
					type: "source",
					sourceType: "document",
					id: generateId(),
					mediaType,
					title: entry.title ?? entry.file_name ?? filename ?? uri,
					...filename != null ? { filename } : {}
				});
			}
			break;
		}
	}
	return sources;
}
/**
* Given a list of annotations attached to a single `text` content block,
* returns the corresponding `LanguageModelV4Source` parts (de-duplicated by
* URL/filename to avoid double-counting when the same citation reappears
* across deltas).
*/
function annotationsToSources({ annotations, generateId }) {
	if (annotations == null) return [];
	const seen = /* @__PURE__ */ new Set();
	const sources = [];
	for (const annotation of annotations) {
		const source = annotationToSource({
			annotation,
			generateId
		});
		if (source == null) continue;
		const key = source.sourceType === "url" ? `url:${source.url}` : `doc:${source.filename ?? source.title}`;
		if (seen.has(key)) continue;
		seen.add(key);
		sources.push(source);
	}
	return sources;
}
//#endregion
//#region src/interactions/map-google-interactions-finish-reason.ts
function mapGoogleInteractionsFinishReason({ status, hasFunctionCall }) {
	switch (status) {
		case "completed": return hasFunctionCall ? "tool-calls" : "stop";
		case "requires_action": return "tool-calls";
		case "failed": return "error";
		case "incomplete": return "length";
		case "cancelled": return "other";
		default: return "other";
	}
}
//#endregion
//#region src/interactions/build-google-interactions-stream-transform.ts
const BUILTIN_TOOL_CALL_TYPES$1 = /* @__PURE__ */ new Set([
	"google_search_call",
	"code_execution_call",
	"url_context_call",
	"file_search_call",
	"google_maps_call",
	"mcp_server_tool_call"
]);
const BUILTIN_TOOL_RESULT_TYPES$1 = /* @__PURE__ */ new Set([
	"google_search_result",
	"code_execution_result",
	"url_context_result",
	"file_search_result",
	"google_maps_result",
	"mcp_server_tool_result"
]);
function builtinToolNameFromCallType$1(type) {
	return type.replace(/_call$/, "");
}
function builtinToolNameFromResultType$1(type) {
	return type.replace(/_result$/, "");
}
/**
* Builds a `TransformStream<ParseResult<GoogleInteractionsEvent>, LanguageModelV4StreamPart>`
* over the Interactions API SSE event stream.
*
* Surfaces text + thought (reasoning), function_call, image, built-in tool
* call/result steps, and `text_annotation` -> `source` parts.
*/
function buildGoogleInteractionsStreamTransform({ warnings, generateId, includeRawChunks, serviceTier: headerServiceTier }) {
	let interactionId;
	let usage;
	let serviceTier = headerServiceTier;
	let finishStatus;
	let hasFunctionCall = false;
	let hasCompletionEvent = false;
	const openBlocks = /* @__PURE__ */ new Map();
	const emittedSourceKeys = /* @__PURE__ */ new Set();
	function sourceKey(source) {
		return source.sourceType === "url" ? `url:${source.url}` : `doc:${source.filename ?? source.title}`;
	}
	return new TransformStream({
		start(controller) {
			controller.enqueue({
				type: "stream-start",
				warnings
			});
		},
		transform(chunk, controller) {
			if (includeRawChunks) controller.enqueue({
				type: "raw",
				rawValue: chunk.rawValue
			});
			if (!chunk.success) {
				finishStatus = "failed";
				controller.enqueue({
					type: "error",
					error: chunk.error
				});
				return;
			}
			const value = chunk.value;
			const eventType = value.event_type;
			switch (eventType) {
				case "interaction.created": {
					const interaction = value.interaction;
					interactionId = interaction?.id != null && interaction.id.length > 0 ? interaction.id : void 0;
					const created = interaction?.created;
					let timestamp;
					if (typeof created === "string") {
						const parsed = new Date(created);
						if (!Number.isNaN(parsed.getTime())) timestamp = parsed;
					}
					controller.enqueue({
						type: "response-metadata",
						...interactionId != null ? { id: interactionId } : {},
						modelId: interaction?.model,
						...timestamp ? { timestamp } : {}
					});
					break;
				}
				case "step.start": {
					const event = value;
					const step = event.step;
					const index = event.index;
					const blockId = `${interactionId ?? "interaction"}:${index}`;
					const stepType = step?.type;
					if (stepType === "model_output") {
						const initial = step?.content?.[0];
						if (initial?.type === "text") {
							openBlocks.set(index, {
								kind: "text",
								id: blockId,
								emittedSourceKeys: /* @__PURE__ */ new Set()
							});
							controller.enqueue({
								type: "text-start",
								id: blockId
							});
							const initialSources = annotationsToSources({
								annotations: initial.annotations,
								generateId
							});
							for (const source of initialSources) {
								const key = sourceKey(source);
								if (emittedSourceKeys.has(key)) continue;
								emittedSourceKeys.add(key);
								controller.enqueue(source);
							}
						} else if (initial?.type === "image") openBlocks.set(index, {
							kind: "image",
							id: blockId,
							...initial.data != null ? { data: initial.data } : {},
							...initial.mime_type != null ? { mimeType: initial.mime_type } : {},
							...initial.uri != null ? { uri: initial.uri } : {}
						});
						else openBlocks.set(index, {
							kind: "pending_model_output",
							id: blockId
						});
					} else if (stepType === "thought") {
						const signature = step?.signature;
						openBlocks.set(index, {
							kind: "reasoning",
							id: blockId,
							...signature != null ? { signature } : {}
						});
						controller.enqueue({
							type: "reasoning-start",
							id: blockId
						});
						if (Array.isArray(step?.summary)) {
							for (const item of step.summary) if (item?.type === "text" && typeof item.text === "string") controller.enqueue({
								type: "reasoning-delta",
								id: blockId,
								delta: item.text
							});
						}
					} else if (stepType === "processing_call" || stepType === "processing_result") {
						const google = {};
						if (step?.signature != null) google.signature = step.signature;
						if (interactionId != null) google.interactionId = interactionId;
						if (stepType === "processing_call") google.processingId = step?.id || blockId;
						else google.processingCallId = step?.call_id || blockId;
						openBlocks.set(index, {
							kind: "custom",
							id: blockId,
							customKind: `google.${stepType}`,
							google
						});
					} else if (stepType === "function_call") {
						const toolCallId = step?.id || blockId;
						const toolName = step?.name ?? "unknown";
						hasFunctionCall = true;
						const state = {
							kind: "function_call",
							id: blockId,
							toolCallId,
							toolName,
							argumentsAccum: "",
							...step?.signature != null ? { signature: step.signature } : {}
						};
						openBlocks.set(index, state);
						controller.enqueue({
							type: "tool-input-start",
							id: toolCallId,
							toolName
						});
					} else if (stepType != null && BUILTIN_TOOL_CALL_TYPES$1.has(stepType)) {
						const toolName = stepType === "mcp_server_tool_call" ? step?.name ?? "mcp_server_tool" : builtinToolNameFromCallType$1(stepType);
						const state = {
							kind: "builtin_tool_call",
							id: blockId,
							blockType: stepType,
							toolCallId: step?.id || blockId,
							toolName,
							arguments: step?.arguments ?? {},
							callEmitted: false
						};
						openBlocks.set(index, state);
					} else if (stepType != null && BUILTIN_TOOL_RESULT_TYPES$1.has(stepType)) {
						const toolName = stepType === "mcp_server_tool_result" ? step?.name ?? "mcp_server_tool" : builtinToolNameFromResultType$1(stepType);
						const state = {
							kind: "builtin_tool_result",
							id: blockId,
							blockType: stepType,
							callId: step?.call_id || blockId,
							toolName,
							result: step?.result ?? null,
							...step?.is_error != null ? { isError: step.is_error } : {},
							resultEmitted: false
						};
						openBlocks.set(index, state);
					} else openBlocks.set(index, {
						kind: "unknown",
						id: blockId
					});
					break;
				}
				case "step.delta": {
					const event = value;
					let open = openBlocks.get(event.index);
					if (open == null) break;
					const dtype = event.delta?.type;
					if (open.kind === "pending_model_output") {
						if (dtype === "text" || dtype === "text_annotation" || dtype === "text_annotation_delta") {
							const promoted = {
								kind: "text",
								id: open.id,
								emittedSourceKeys: /* @__PURE__ */ new Set()
							};
							openBlocks.set(event.index, promoted);
							open = promoted;
							controller.enqueue({
								type: "text-start",
								id: promoted.id
							});
						}
					}
					if (dtype === "image" && (open.kind === "pending_model_output" || open.kind === "text" || open.kind === "image")) {
						const imageDelta = event.delta;
						const google = {};
						if (interactionId != null) google.interactionId = interactionId;
						const providerMetadata = Object.keys(google).length > 0 ? { google } : void 0;
						if (imageDelta?.data != null && imageDelta.data.length > 0) controller.enqueue({
							type: "file",
							mediaType: imageDelta.mime_type ?? "image/png",
							data: {
								type: "data",
								data: imageDelta.data
							},
							...providerMetadata ? { providerMetadata } : {}
						});
						else if (imageDelta?.uri != null && imageDelta.uri.length > 0) controller.enqueue({
							type: "file",
							mediaType: imageDelta.mime_type ?? "image/png",
							data: {
								type: "url",
								url: new URL(imageDelta.uri)
							},
							...providerMetadata ? { providerMetadata } : {}
						});
						if (open.kind === "image") {
							open.data = void 0;
							open.uri = void 0;
						}
						break;
					}
					if (dtype === "video" && (open.kind === "pending_model_output" || open.kind === "text")) {
						const videoDelta = event.delta;
						const google = {};
						if (interactionId != null) google.interactionId = interactionId;
						const providerMetadata = Object.keys(google).length > 0 ? { google } : void 0;
						if (videoDelta?.data != null && videoDelta.data.length > 0) controller.enqueue({
							type: "file",
							mediaType: videoDelta.mime_type ?? "video/mp4",
							data: {
								type: "data",
								data: videoDelta.data
							},
							...providerMetadata ? { providerMetadata } : {}
						});
						else if (videoDelta?.uri != null && videoDelta.uri.length > 0) controller.enqueue({
							type: "file",
							mediaType: videoDelta.mime_type ?? "video/mp4",
							data: {
								type: "url",
								url: new URL(videoDelta.uri)
							},
							...providerMetadata ? { providerMetadata } : {}
						});
						break;
					}
					const delta = event.delta;
					if (open.kind === "custom" && (delta?.type === "processing_call" || delta?.type === "processing_result")) {
						if (delta.signature != null) open.google.signature = delta.signature;
						if (delta.type === "processing_call" && delta.id != null && delta.id.length > 0) open.google.processingId = delta.id;
						if (delta.type === "processing_result" && delta.call_id != null && delta.call_id.length > 0) open.google.processingCallId = delta.call_id;
					} else if (open.kind === "text" && delta?.type === "text") {
						const text = delta.text ?? "";
						if (text.length > 0) controller.enqueue({
							type: "text-delta",
							id: open.id,
							delta: text
						});
					} else if (open.kind === "text" && (delta?.type === "text_annotation" || delta?.type === "text_annotation_delta")) {
						const sources = annotationsToSources({
							annotations: delta.annotations,
							generateId
						});
						for (const source of sources) {
							const key = sourceKey(source);
							if (emittedSourceKeys.has(key)) continue;
							emittedSourceKeys.add(key);
							open.emittedSourceKeys.add(key);
							controller.enqueue(source);
						}
					} else if (open.kind === "image" && delta?.type === "image") {
						if (delta.data != null) open.data = delta.data;
						if (delta.mime_type != null) open.mimeType = delta.mime_type;
						if (delta.uri != null) open.uri = delta.uri;
					} else if (open.kind === "reasoning") {
						if (delta?.type === "thought_summary") {
							const item = delta.content;
							if (item?.type === "text" && typeof item.text === "string") controller.enqueue({
								type: "reasoning-delta",
								id: open.id,
								delta: item.text
							});
						} else if (delta?.type === "thought_signature") {
							const signature = delta.signature;
							if (signature != null) open.signature = signature;
						}
					} else if (open.kind === "function_call" && delta?.type === "arguments_delta") {
						const slice = typeof delta.arguments === "string" ? delta.arguments : "";
						if (slice.length > 0) {
							open.argumentsAccum += slice;
							controller.enqueue({
								type: "tool-input-delta",
								id: open.toolCallId,
								delta: slice
							});
						}
						if (delta.id != null && delta.id.length > 0) open.toolCallId = delta.id;
						if (delta.signature != null) open.signature = delta.signature;
						hasFunctionCall = true;
					} else if (open.kind === "builtin_tool_call" && delta?.type === open.blockType) {
						if (delta.id != null && delta.id.length > 0) open.toolCallId = delta.id;
						if (delta.arguments != null && typeof delta.arguments === "object") open.arguments = delta.arguments;
						if (delta.name != null && open.blockType === "mcp_server_tool_call") open.toolName = delta.name;
					} else if (open.kind === "builtin_tool_result" && delta?.type === open.blockType) {
						if (delta.call_id != null && delta.call_id.length > 0) open.callId = delta.call_id;
						if (delta.result !== void 0) open.result = delta.result;
						if (delta.is_error != null) open.isError = delta.is_error;
						if (delta.name != null && open.blockType === "mcp_server_tool_result") open.toolName = delta.name;
					}
					break;
				}
				case "step.stop": {
					const event = value;
					const open = openBlocks.get(event.index);
					if (open == null) break;
					if (open.kind === "text") {
						const textProviderMetadata = interactionId != null ? { google: { interactionId } } : void 0;
						controller.enqueue({
							type: "text-end",
							id: open.id,
							...textProviderMetadata ? { providerMetadata: textProviderMetadata } : {}
						});
					} else if (open.kind === "reasoning") {
						const google = {};
						if (open.signature != null) google.signature = open.signature;
						if (interactionId != null) google.interactionId = interactionId;
						const providerMetadata = Object.keys(google).length > 0 ? { google } : void 0;
						controller.enqueue({
							type: "reasoning-end",
							id: open.id,
							...providerMetadata ? { providerMetadata } : {}
						});
					} else if (open.kind === "image") {
						const google = {};
						if (interactionId != null) google.interactionId = interactionId;
						const providerMetadata = Object.keys(google).length > 0 ? { google } : void 0;
						if (open.data != null && open.data.length > 0) controller.enqueue({
							type: "file",
							mediaType: open.mimeType ?? "image/png",
							data: {
								type: "data",
								data: open.data
							},
							...providerMetadata ? { providerMetadata } : {}
						});
						else if (open.uri != null && open.uri.length > 0) controller.enqueue({
							type: "file",
							mediaType: open.mimeType ?? "image/png",
							data: {
								type: "url",
								url: new URL(open.uri)
							},
							...providerMetadata ? { providerMetadata } : {}
						});
					} else if (open.kind === "function_call") {
						const accumulated = open.argumentsAccum.length > 0 ? open.argumentsAccum : "{}";
						controller.enqueue({
							type: "tool-input-end",
							id: open.toolCallId
						});
						const google = {};
						if (open.signature != null) google.signature = open.signature;
						if (interactionId != null) google.interactionId = interactionId;
						const providerMetadata = Object.keys(google).length > 0 ? { google } : void 0;
						controller.enqueue({
							type: "tool-call",
							toolCallId: open.toolCallId,
							toolName: open.toolName,
							input: accumulated,
							...providerMetadata ? { providerMetadata } : {}
						});
					} else if (open.kind === "custom") controller.enqueue({
						type: "custom",
						kind: open.customKind,
						providerMetadata: { google: open.google }
					});
					else if (open.kind === "builtin_tool_call" && !open.callEmitted) {
						controller.enqueue({
							type: "tool-call",
							toolCallId: open.toolCallId,
							toolName: open.toolName,
							input: JSON.stringify(open.arguments ?? {}),
							providerExecuted: true
						});
						open.callEmitted = true;
					} else if (open.kind === "builtin_tool_result" && !open.resultEmitted) {
						controller.enqueue({
							type: "tool-result",
							toolCallId: open.callId,
							toolName: open.toolName,
							result: open.result ?? null
						});
						open.resultEmitted = true;
						const sources = builtinToolResultToSources({
							block: {
								type: open.blockType,
								call_id: open.callId,
								result: open.result
							},
							generateId
						});
						for (const source of sources) {
							const key = sourceKey(source);
							if (emittedSourceKeys.has(key)) continue;
							emittedSourceKeys.add(key);
							controller.enqueue(source);
						}
					}
					openBlocks.delete(event.index);
					break;
				}
				case "interaction.status_update":
				case "interaction.in_progress":
				case "interaction.requires_action": {
					const event = value;
					if (event.status != null) finishStatus = event.status;
					else if (eventType === "interaction.requires_action") finishStatus = "requires_action";
					else finishStatus = "in_progress";
					break;
				}
				case "interaction.completed": {
					hasCompletionEvent = true;
					const interaction = value.interaction;
					if (interaction?.id != null && interaction.id.length > 0) interactionId = interaction.id;
					if (interaction?.status != null) finishStatus = interaction.status;
					if (interaction?.usage != null) usage = interaction.usage;
					if (interaction?.service_tier != null) serviceTier = interaction.service_tier;
					break;
				}
				case "error": {
					const event = value;
					finishStatus = "failed";
					controller.enqueue({
						type: "error",
						error: createProviderStreamError({
							message: event.error?.message ?? "Unknown interaction error",
							type: event.event_type,
							code: event.error?.code ?? void 0,
							data: event
						})
					});
					break;
				}
			}
		},
		flush(controller) {
			const isInterrupted = !hasCompletionEvent && (finishStatus == null || finishStatus === "in_progress");
			const finishReason = {
				unified: isInterrupted ? "interrupted" : mapGoogleInteractionsFinishReason({
					status: finishStatus,
					hasFunctionCall
				}),
				raw: isInterrupted ? void 0 : finishStatus
			};
			const outputTokensByModality = getGoogleInteractionsOutputTokensByModality(usage);
			const providerMetadata = { google: {
				...interactionId != null ? { interactionId } : {},
				...serviceTier != null ? { serviceTier } : {},
				...outputTokensByModality != null ? { outputTokensByModality } : {}
			} };
			controller.enqueue({
				type: "finish",
				finishReason,
				usage: convertGoogleInteractionsUsage(usage),
				providerMetadata
			});
		}
	});
}
//#endregion
//#region src/interactions/convert-to-google-interactions-input.ts
/**
* Converts an AI SDK `LanguageModelV4Prompt` into the Gemini Interactions
* request shape (`{ input: Array<Step>, system_instruction }`).
*
* Prior assistant content round-trips as discrete steps:
*   - text / image content → `model_output` step with a single `content` array
*   - reasoning → `thought` step (`signature` + `summary`)
*   - tool-call → `function_call` step
* User turns (and tool-result turns from the previous round) are sent as
* `user_input` steps whose `content[]` holds the user's parts (text, files,
* and — for tool-result turns — `function_result` blocks).
*
* Handles text parts, file parts (image / audio / document / video, all four
* `data.type` shapes), tool-call/tool-result round-tripping, per-step
* `signature` round-tripping, and statefulness compaction (drop assistant/tool
* turns whose `providerOptions.google.interactionId === previousInteractionId`).
*/
function convertToGoogleInteractionsInput({ prompt, previousInteractionId, store, mediaResolution }) {
	const warnings = [];
	const incoherentCombo = previousInteractionId != null && store === false;
	const shouldCompact = previousInteractionId != null && store !== false;
	if (incoherentCombo) warnings.push({
		type: "other",
		message: "google.interactions: providerOptions.google.previousInteractionId was set together with store: false. These are incoherent (the prior interaction cannot be referenced when nothing was stored on the server); the full history will be sent and previous_interaction_id will still be emitted."
	});
	const compactedPrompt = shouldCompact ? compactPromptForPreviousInteraction({
		prompt,
		previousInteractionId
	}) : prompt;
	const systemTexts = [];
	const steps = [];
	for (const message of compactedPrompt) switch (message.role) {
		case "system":
			systemTexts.push(message.content);
			break;
		case "user": {
			const content = [];
			for (const part of message.content) if (part.type === "text") content.push({
				type: "text",
				text: part.text
			});
			else if (part.type === "file") {
				const fileBlock = convertFilePartToContent({
					part,
					warnings,
					mediaResolution
				});
				if (fileBlock != null) content.push(fileBlock);
			}
			const merged = mergeAdjacentTextContent(content);
			if (merged.length > 0) steps.push({
				type: "user_input",
				content: merged
			});
			break;
		}
		case "assistant": {
			let pendingModelOutput = [];
			const flushModelOutput = () => {
				if (pendingModelOutput.length > 0) {
					steps.push({
						type: "model_output",
						content: pendingModelOutput
					});
					pendingModelOutput = [];
				}
			};
			for (const part of message.content) if (part.type === "text") pendingModelOutput.push({
				type: "text",
				text: part.text
			});
			else if (part.type === "reasoning") {
				flushModelOutput();
				const signature = part.providerOptions?.google?.signature;
				steps.push({
					type: "thought",
					...signature != null ? { signature } : {},
					summary: part.text.length > 0 ? [{
						type: "text",
						text: part.text
					}] : void 0
				});
			} else if (part.type === "file") {
				const fileBlock = convertFilePartToContent({
					part,
					warnings,
					mediaResolution
				});
				if (fileBlock != null) pendingModelOutput.push(fileBlock);
			} else if (part.type === "custom") {
				flushModelOutput();
				const google = part.providerOptions?.google;
				const signature = typeof google?.signature === "string" ? google.signature : void 0;
				if (part.kind === "google.processing_call" && typeof google?.processingId === "string") steps.push({
					type: "processing_call",
					id: google.processingId,
					...signature != null ? { signature } : {}
				});
				else if (part.kind === "google.processing_result" && typeof google?.processingCallId === "string") steps.push({
					type: "processing_result",
					call_id: google.processingCallId,
					...signature != null ? { signature } : {}
				});
				else warnings.push({
					type: "other",
					message: `google.interactions: unsupported or invalid custom assistant content part "${part.kind}"; part dropped.`
				});
			} else if (part.type === "tool-call") {
				flushModelOutput();
				const signature = part.providerOptions?.google?.signature;
				const args = typeof part.input === "string" ? safeParseToolArgs(part.input) : part.input ?? {};
				steps.push({
					type: "function_call",
					id: part.toolCallId,
					name: part.toolName,
					arguments: args,
					...signature != null ? { signature } : {}
				});
			} else warnings.push({
				type: "other",
				message: `google.interactions: unsupported assistant content part type "${part.type}"; part dropped.`
			});
			flushModelOutput();
			break;
		}
		case "tool": {
			const content = [];
			for (const part of message.content) {
				if (part.type !== "tool-result") {
					warnings.push({
						type: "other",
						message: `google.interactions: unsupported tool message part type "${part.type}"; part dropped.`
					});
					continue;
				}
				const block = convertToolResultPart({
					toolCallId: part.toolCallId,
					toolName: part.toolName,
					output: part.output,
					signature: part.providerOptions?.google?.signature,
					warnings
				});
				content.push(block);
			}
			if (content.length > 0) steps.push({
				type: "user_input",
				content
			});
			break;
		}
	}
	return {
		input: steps,
		systemInstruction: systemTexts.length > 0 ? systemTexts.join("\n\n") : void 0,
		warnings
	};
}
/**
* Maps a single AI SDK `LanguageModelV4FilePart` to a Gemini Interactions
* content block (`image` / `audio` / `document` / `video`).
*/
function convertFilePartToContent({ part, warnings, mediaResolution }) {
	if (part.data.type === "text") return {
		type: "text",
		text: part.data.text
	};
	const topLevel = getTopLevelMediaType(part.mediaType);
	let kind;
	switch (topLevel) {
		case "image":
			kind = "image";
			break;
		case "audio":
			kind = "audio";
			break;
		case "video":
			kind = "video";
			break;
		case "application":
		case "text":
			kind = "document";
			break;
		default: kind = void 0;
	}
	if (kind == null) {
		warnings.push({
			type: "other",
			message: `google.interactions: unsupported file media type "${part.mediaType}"; part dropped.`
		});
		return;
	}
	const resolutionField = mediaResolution != null && (kind === "image" || kind === "video") ? { resolution: mediaResolution } : {};
	const processingField = kind === "video" ? getVideoProcessingField({
		part,
		warnings
	}) : {};
	switch (part.data.type) {
		case "data": {
			const mimeType = resolveFullMediaType({ part });
			return {
				type: kind,
				data: convertToBase64(part.data.data),
				mime_type: mimeType,
				...resolutionField,
				...processingField
			};
		}
		case "url": return {
			type: kind,
			uri: part.data.url.toString(),
			...isFullMediaType(part.mediaType) ? { mime_type: part.mediaType } : {},
			...resolutionField,
			...processingField
		};
		case "reference": {
			const uri = resolveProviderReference({
				reference: part.data.reference,
				provider: "google"
			});
			return {
				type: kind,
				uri,
				...isFullMediaType(part.mediaType) ? { mime_type: part.mediaType } : {},
				...resolutionField,
				...processingField
			};
		}
	}
}
function getVideoProcessingField({ part, warnings }) {
	const processing = (part.providerOptions?.google)?.processing;
	if (processing == null) return {};
	if (processing === "agentic" || processing === "static") return { processing };
	if (typeof processing === "object" && !Array.isArray(processing) && processing.type === "static") {
		const config = processing;
		return { processing: {
			type: "static",
			...typeof config.startOffset === "number" ? { start_offset: `${config.startOffset}s` } : {},
			...typeof config.endOffset === "number" ? { end_offset: `${config.endOffset}s` } : {},
			...typeof config.fps === "number" ? { fps: config.fps } : {}
		} };
	}
	warnings.push({
		type: "other",
		message: "google.interactions: invalid providerOptions.google.processing on video file part; expected \"agentic\", \"static\", or a static processing configuration. Option dropped."
	});
	return {};
}
function compactPromptForPreviousInteraction({ prompt, previousInteractionId }) {
	const out = [];
	const droppedToolCallIds = /* @__PURE__ */ new Set();
	for (const message of prompt) {
		if (message.role === "assistant") {
			if (message.content.some((part) => {
				return part.providerOptions?.google?.interactionId === previousInteractionId;
			})) {
				for (const part of message.content) if (part.type === "tool-call") droppedToolCallIds.add(part.toolCallId);
				continue;
			}
			out.push(message);
			continue;
		}
		if (message.role === "tool") {
			const remaining = message.content.filter((part) => {
				if (part.type !== "tool-result") return true;
				return !droppedToolCallIds.has(part.toolCallId);
			});
			if (remaining.length === 0) continue;
			out.push({
				...message,
				content: remaining
			});
			continue;
		}
		out.push(message);
	}
	return out;
}
function safeParseToolArgs(input) {
	try {
		const parsed = secureJsonParse(input);
		if (parsed != null && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
		return { value: parsed };
	} catch {
		return { value: input };
	}
}
function convertToolResultPart({ toolCallId, toolName, output, signature, warnings }) {
	const base = {
		type: "function_result",
		call_id: toolCallId,
		name: toolName,
		...signature != null ? { signature } : {}
	};
	switch (output.type) {
		case "text": return {
			...base,
			result: output.value
		};
		case "json": return {
			...base,
			result: JSON.stringify(output.value)
		};
		case "error-text": return {
			...base,
			is_error: true,
			result: output.value
		};
		case "error-json": return {
			...base,
			is_error: true,
			result: JSON.stringify(output.value)
		};
		case "execution-denied": return {
			...base,
			is_error: true,
			result: output.reason ?? "Tool execution denied by user."
		};
		case "content": {
			const blocks = [];
			for (const item of output.value) if (item.type === "text") blocks.push({
				type: "text",
				text: item.text
			});
			else if (item.type === "file") {
				if (getTopLevelMediaType(item.mediaType) !== "image") {
					warnings.push({
						type: "other",
						message: `google.interactions: tool-result file with mediaType "${item.mediaType}" is not supported (Interactions \`function_result.result\` accepts only text and image content); part dropped.`
					});
					continue;
				}
				const imageBlock = filePartToImageBlock({
					part: item,
					warnings
				});
				if (imageBlock != null) blocks.push(imageBlock);
			} else warnings.push({
				type: "other",
				message: `google.interactions: tool-result content part type "${item.type}" is not supported; part dropped.`
			});
			return {
				...base,
				result: blocks
			};
		}
	}
}
function filePartToImageBlock({ part, warnings }) {
	switch (part.data.type) {
		case "data": {
			const mimeType = isFullMediaType(part.mediaType) ? part.mediaType : resolveFullMediaType({ part: {
				type: "file",
				mediaType: part.mediaType,
				data: part.data
			} });
			return {
				type: "image",
				data: convertToBase64(part.data.data),
				mime_type: mimeType
			};
		}
		case "url": return {
			type: "image",
			uri: part.data.url.toString(),
			...isFullMediaType(part.mediaType) ? { mime_type: part.mediaType } : {}
		};
		case "reference": return {
			type: "image",
			uri: resolveProviderReference({
				reference: part.data.reference,
				provider: "google"
			}),
			...isFullMediaType(part.mediaType) ? { mime_type: part.mediaType } : {}
		};
		case "text":
			warnings.push({
				type: "other",
				message: "google.interactions: tool-result image part with `data.type === \"text\"` is not representable as an image; part dropped."
			});
			return;
	}
}
function mergeAdjacentTextContent(content) {
	if (content.length < 2) return content;
	const result = [];
	for (const block of content) {
		const last = result[result.length - 1];
		if (block.type === "text" && last != null && last.type === "text" && last.annotations == null && block.annotations == null) {
			const merged = {
				type: "text",
				text: `${last.text}\n\n${block.text}`
			};
			result[result.length - 1] = merged;
			continue;
		}
		result.push(block);
	}
	return result;
}
//#endregion
//#region src/interactions/google-interactions-api.ts
const tokenByModalitySchema = () => z.object({
	modality: z.string().nullish(),
	tokens: z.number().nullish()
}).loose();
const usageSchema = () => z.object({
	total_input_tokens: z.number().nullish(),
	total_output_tokens: z.number().nullish(),
	total_thought_tokens: z.number().nullish(),
	total_cached_tokens: z.number().nullish(),
	total_tool_use_tokens: z.number().nullish(),
	total_tokens: z.number().nullish(),
	input_tokens_by_modality: z.array(tokenByModalitySchema()).nullish(),
	output_tokens_by_modality: z.array(tokenByModalitySchema()).nullish(),
	cached_tokens_by_modality: z.array(tokenByModalitySchema()).nullish(),
	tool_use_tokens_by_modality: z.array(tokenByModalitySchema()).nullish(),
	grounding_tool_count: z.array(z.object({
		type: z.string().nullish(),
		count: z.number().nullish()
	}).loose()).nullish()
}).loose();
const interactionStatusSchema = () => z.enum([
	"in_progress",
	"requires_action",
	"completed",
	"failed",
	"cancelled",
	"incomplete"
]);
const annotationSchema = () => {
	const urlCitation = z.object({
		type: z.literal("url_citation"),
		url: z.string().nullish(),
		title: z.string().nullish(),
		start_index: z.number().nullish(),
		end_index: z.number().nullish()
	}).loose();
	const fileCitation = z.object({
		type: z.literal("file_citation"),
		file_name: z.string().nullish(),
		document_uri: z.string().nullish(),
		url: z.string().nullish(),
		page_number: z.number().nullish(),
		media_id: z.string().nullish(),
		start_index: z.number().nullish(),
		end_index: z.number().nullish(),
		custom_metadata: z.record(z.string(), z.unknown()).nullish()
	}).loose();
	const placeCitation = z.object({
		type: z.literal("place_citation"),
		name: z.string().nullish(),
		url: z.string().nullish(),
		place_id: z.string().nullish(),
		start_index: z.number().nullish(),
		end_index: z.number().nullish()
	}).loose();
	return z.union([
		urlCitation,
		fileCitation,
		placeCitation,
		z.object({ type: z.string() }).loose()
	]);
};
const thoughtSummaryItemSchema = () => z.object({
	type: z.string(),
	text: z.string().nullish(),
	data: z.string().nullish(),
	mime_type: z.string().nullish()
}).loose();
const contentBlockSchema = () => {
	const textContent = z.object({
		type: z.literal("text"),
		text: z.string(),
		annotations: z.array(annotationSchema()).nullish()
	}).loose();
	const imageContent = z.object({
		type: z.literal("image"),
		data: z.string().nullish(),
		mime_type: z.string().nullish(),
		resolution: z.enum([
			"low",
			"medium",
			"high",
			"ultra_high"
		]).nullish(),
		uri: z.string().nullish()
	}).loose();
	const videoContent = z.object({
		type: z.literal("video"),
		data: z.string().nullish(),
		mime_type: z.string().nullish(),
		uri: z.string().nullish()
	}).loose();
	return z.union([
		textContent,
		imageContent,
		videoContent,
		z.object({ type: z.string() }).loose()
	]);
};
const BUILTIN_TOOL_CALL_STEP_TYPES = [
	"google_search_call",
	"code_execution_call",
	"url_context_call",
	"file_search_call",
	"google_maps_call",
	"mcp_server_tool_call"
];
const BUILTIN_TOOL_RESULT_STEP_TYPES = [
	"google_search_result",
	"code_execution_result",
	"url_context_result",
	"file_search_result",
	"google_maps_result",
	"mcp_server_tool_result"
];
const stepSchema = () => {
	const userInputStep = z.object({
		type: z.literal("user_input"),
		content: z.array(contentBlockSchema()).nullish()
	}).loose();
	const modelOutputStep = z.object({
		type: z.literal("model_output"),
		content: z.array(contentBlockSchema()).nullish()
	}).loose();
	const functionCallStep = z.object({
		type: z.literal("function_call"),
		id: z.string(),
		name: z.string(),
		arguments: z.record(z.string(), z.unknown()).nullish(),
		signature: z.string().nullish()
	}).loose();
	const thoughtStep = z.object({
		type: z.literal("thought"),
		signature: z.string().nullish(),
		summary: z.array(thoughtSummaryItemSchema()).nullish()
	}).loose();
	const processingCallStep = z.object({
		type: z.literal("processing_call"),
		id: z.string(),
		signature: z.string().nullish()
	}).loose();
	const processingResultStep = z.object({
		type: z.literal("processing_result"),
		call_id: z.string(),
		signature: z.string().nullish()
	}).loose();
	const builtinToolCallStep = z.object({
		type: z.enum(BUILTIN_TOOL_CALL_STEP_TYPES),
		id: z.string(),
		arguments: z.record(z.string(), z.unknown()).nullish(),
		name: z.string().nullish(),
		server_name: z.string().nullish(),
		search_type: z.string().nullish(),
		signature: z.string().nullish()
	}).loose();
	const builtinToolResultStep = z.object({
		type: z.enum(BUILTIN_TOOL_RESULT_STEP_TYPES),
		call_id: z.string(),
		result: z.unknown().nullish(),
		is_error: z.boolean().nullish(),
		name: z.string().nullish(),
		server_name: z.string().nullish(),
		signature: z.string().nullish()
	}).loose();
	return z.union([
		userInputStep,
		modelOutputStep,
		functionCallStep,
		thoughtStep,
		processingCallStep,
		processingResultStep,
		builtinToolCallStep,
		builtinToolResultStep,
		z.object({ type: z.string() }).loose()
	]);
};
const googleInteractionsResponseSchema = lazySchema(() => zodSchema(z.object({
	id: z.string().nullish(),
	created: z.string().nullish(),
	updated: z.string().nullish(),
	status: interactionStatusSchema(),
	model: z.string().nullish(),
	agent: z.string().nullish(),
	steps: z.array(stepSchema()).nullish(),
	usage: usageSchema().nullish(),
	service_tier: z.string().nullish(),
	previous_interaction_id: z.string().nullish(),
	response_modalities: z.array(z.string()).nullish()
}).loose()));
const googleInteractionsEventSchema = lazySchema(() => zodSchema((() => {
	const status = interactionStatusSchema();
	const annotation = annotationSchema();
	const thoughtSummaryItem = thoughtSummaryItemSchema();
	const interactionCreatedEvent = z.object({
		event_type: z.literal("interaction.created"),
		event_id: z.string().nullish(),
		interaction: z.object({
			id: z.string().nullish(),
			created: z.string().nullish(),
			model: z.string().nullish(),
			agent: z.string().nullish(),
			status: status.nullish()
		}).loose()
	}).loose();
	const stepStartEvent = z.object({
		event_type: z.literal("step.start"),
		event_id: z.string().nullish(),
		index: z.number(),
		step: stepSchema()
	}).loose();
	const stepDeltaText = z.object({
		type: z.literal("text"),
		text: z.string()
	}).loose();
	const stepDeltaThoughtSummary = z.object({
		type: z.literal("thought_summary"),
		content: thoughtSummaryItem.nullish()
	}).loose();
	const stepDeltaThoughtSignature = z.object({
		type: z.literal("thought_signature"),
		signature: z.string().nullish()
	}).loose();
	const stepDeltaArgumentsDelta = z.object({
		type: z.literal("arguments_delta"),
		arguments: z.string().nullish(),
		id: z.string().nullish(),
		signature: z.string().nullish()
	}).loose();
	const stepDeltaTextAnnotation = z.object({
		type: z.enum(["text_annotation_delta", "text_annotation"]),
		annotations: z.array(annotation).nullish()
	}).loose();
	const stepDeltaImage = z.object({
		type: z.literal("image"),
		data: z.string().nullish(),
		mime_type: z.string().nullish(),
		resolution: z.enum([
			"low",
			"medium",
			"high",
			"ultra_high"
		]).nullish(),
		uri: z.string().nullish()
	}).loose();
	const stepDeltaVideo = z.object({
		type: z.literal("video"),
		data: z.string().nullish(),
		mime_type: z.string().nullish(),
		uri: z.string().nullish()
	}).loose();
	const stepDeltaBuiltinToolCall = z.object({
		type: z.enum(BUILTIN_TOOL_CALL_STEP_TYPES),
		id: z.string().nullish(),
		arguments: z.record(z.string(), z.unknown()).nullish(),
		name: z.string().nullish(),
		server_name: z.string().nullish(),
		search_type: z.string().nullish(),
		signature: z.string().nullish()
	}).loose();
	const stepDeltaBuiltinToolResult = z.object({
		type: z.enum(BUILTIN_TOOL_RESULT_STEP_TYPES),
		call_id: z.string().nullish(),
		result: z.unknown().nullish(),
		is_error: z.boolean().nullish(),
		name: z.string().nullish(),
		server_name: z.string().nullish(),
		signature: z.string().nullish()
	}).loose();
	const stepDeltaUnknown = z.object({ type: z.string() }).loose();
	const stepDeltaUnion = z.union([
		stepDeltaText,
		stepDeltaImage,
		stepDeltaVideo,
		stepDeltaThoughtSummary,
		stepDeltaThoughtSignature,
		stepDeltaArgumentsDelta,
		stepDeltaTextAnnotation,
		stepDeltaBuiltinToolCall,
		stepDeltaBuiltinToolResult,
		stepDeltaUnknown
	]);
	const stepDeltaEvent = z.object({
		event_type: z.literal("step.delta"),
		event_id: z.string().nullish(),
		index: z.number(),
		delta: stepDeltaUnion
	}).loose();
	const stepStopEvent = z.object({
		event_type: z.literal("step.stop"),
		event_id: z.string().nullish(),
		index: z.number()
	}).loose();
	const interactionStatusUpdateEvent = z.object({
		event_type: z.literal("interaction.status_update"),
		event_id: z.string().nullish(),
		interaction_id: z.string().nullish(),
		status: status.nullish()
	}).loose();
	const interactionInProgressEvent = z.object({
		event_type: z.literal("interaction.in_progress"),
		event_id: z.string().nullish(),
		interaction_id: z.string().nullish(),
		status: status.nullish()
	}).loose();
	const interactionRequiresActionEvent = z.object({
		event_type: z.literal("interaction.requires_action"),
		event_id: z.string().nullish(),
		interaction_id: z.string().nullish(),
		status: status.nullish()
	}).loose();
	const interactionCompletedEvent = z.object({
		event_type: z.literal("interaction.completed"),
		event_id: z.string().nullish(),
		interaction: z.object({
			id: z.string().nullish(),
			status: status.nullish(),
			usage: usageSchema().nullish(),
			service_tier: z.string().nullish()
		}).loose()
	}).loose();
	const errorEvent = z.object({
		event_type: z.literal("error"),
		event_id: z.string().nullish(),
		error: z.object({
			code: z.string().nullish(),
			message: z.string().nullish()
		}).loose().nullish()
	}).loose();
	const unknownEvent = z.object({ event_type: z.string() }).loose();
	return z.union([
		interactionCreatedEvent,
		stepStartEvent,
		stepDeltaEvent,
		stepStopEvent,
		interactionStatusUpdateEvent,
		interactionInProgressEvent,
		interactionRequiresActionEvent,
		interactionCompletedEvent,
		errorEvent,
		unknownEvent
	]);
})()));
//#endregion
//#region src/interactions/google-interactions-language-model-options.ts
/**
* Provider-options schema for `google.interactions(...)` calls. Read from the
* shared `providerOptions.google.*` namespace (per PRD); per-call options that
* the AI SDK doesn't natively expose live here.
*
* All fields are `.nullish()` per the existing google provider convention.
*/
const googleInteractionsLanguageModelOptions = lazySchema(() => zodSchema(z.object({
	/** User-defined request metadata, including `safety_identifier`. */
	labels: z.record(z.string(), z.string()).nullish(),
	previousInteractionId: z.string().nullish(),
	store: z.boolean().nullish(),
	agent: z.string().nullish(),
	agentConfig: z.union([z.object({ type: z.literal("dynamic") }).loose(), z.object({
		type: z.literal("deep-research"),
		thinkingSummaries: z.enum(["auto", "none"]).nullish(),
		visualization: z.enum(["off", "auto"]).nullish(),
		collaborativePlanning: z.boolean().nullish()
	})]).nullish(),
	thinkingLevel: z.enum([
		"minimal",
		"low",
		"medium",
		"high",
		"xhigh"
	]).nullish(),
	thinkingSummaries: z.enum(["auto", "none"]).nullish(),
	/**
	* Output-format entries that map directly to the API's `response_format`
	* array. Use this to request image, audio, video, or non-JSON text
	* outputs with modality-specific controls.
	*
	* Entries are sent in order. The AI SDK call-level `responseFormat: {
	* type: 'json', schema }` still drives JSON-mode and adds a matching
	* text entry automatically; entries listed here are appended.
	*/
	responseFormat: z.array(z.union([
		z.object({
			type: z.literal("text"),
			mimeType: z.string().nullish(),
			schema: z.unknown().nullish()
		}).loose(),
		z.object({
			type: z.literal("image"),
			mimeType: z.string().nullish(),
			aspectRatio: z.enum([
				"1:1",
				"2:3",
				"3:2",
				"3:4",
				"4:3",
				"4:5",
				"5:4",
				"9:16",
				"16:9",
				"21:9",
				"1:8",
				"8:1",
				"1:4",
				"4:1"
			]).nullish(),
			imageSize: z.enum([
				"1K",
				"2K",
				"4K",
				"512"
			]).nullish()
		}).loose(),
		z.object({
			type: z.literal("audio"),
			mimeType: z.string().nullish()
		}).loose(),
		z.object({
			type: z.literal("video"),
			aspectRatio: z.enum(["16:9", "9:16"]).nullish(),
			resolution: z.enum([
				"360p",
				"720p",
				"1080p",
				"4k"
			]).nullish(),
			duration: z.string().nullish(),
			delivery: z.enum(["inline", "uri"]).nullish(),
			gcsUri: z.string().nullish()
		}).loose()
	])).nullish(),
	/**
	* @deprecated Use `responseFormat` with a `{ type: 'image', ... }`
	* entry instead. Retained for backwards compatibility; the SDK
	* translates it into a matching `response_format` image entry and
	* emits a warning when set.
	*/
	imageConfig: z.object({
		aspectRatio: z.enum([
			"1:1",
			"2:3",
			"3:2",
			"3:4",
			"4:3",
			"4:5",
			"5:4",
			"9:16",
			"16:9",
			"21:9",
			"1:8",
			"8:1",
			"1:4",
			"4:1"
		]).nullish(),
		imageSize: z.enum([
			"1K",
			"2K",
			"4K",
			"512"
		]).nullish()
	}).nullish(),
	mediaResolution: z.enum([
		"low",
		"medium",
		"high",
		"ultra_high"
	]).nullish(),
	responseModalities: z.array(z.enum([
		"text",
		"image",
		"audio",
		"video",
		"document"
	])).nullish(),
	serviceTier: z.enum([
		"flex",
		"standard",
		"priority"
	]).nullish(),
	/**
	* Alternative to AI SDK `system` message. If both are set, the AI SDK
	* `system` message wins and a warning is emitted.
	*/
	systemInstruction: z.string().nullish(),
	/**
	* Per-block signature for round-tripping `thought.signature` and
	* `function_call.signature` blocks. Set by the SDK on output reasoning /
	* tool-call parts; passed back unchanged on input parts so the API
	* accepts the prior turn.
	*/
	signature: z.string().nullish(),
	/**
	* Set by the SDK on output assistant messages. The converter uses it to
	* decide which messages to drop when compacting under
	* `previousInteractionId`.
	*/
	interactionId: z.string().nullish(),
	/**
	* Maximum time, in milliseconds, to poll a background interaction (agent
	* call) before giving up. Defaults to 30 minutes. Long-running agents
	* such as deep research can take tens of minutes — increase if needed.
	*/
	pollingTimeoutMs: z.number().int().positive().nullish(),
	/**
	* Run the interaction in the background. Required for agents whose
	* server-side workflow cannot complete within a single request/response.
	* When `true`, the POST returns with a non-terminal status and the SDK
	* polls `GET /interactions/{id}` until the work completes. Some agents
	* reject `true`; see the agent's documentation for which mode it
	* requires.
	*/
	background: z.boolean().nullish(),
	/**
	* Environment configuration for the agent sandbox. Only applies to agent
	* calls (`google.interactions({ agent })`); ignored on model-id calls.
	*
	*   - `"remote"`: provision a fresh sandbox for this call.
	*   - any other string: an existing `environment_id` to reuse.
	*   - object: provision a fresh sandbox and optionally preload `sources`
	*     and/or constrain outbound traffic via `network`.
	*/
	environment: z.union([z.string(), z.object({
		type: z.literal("remote"),
		sources: z.array(z.union([
			z.object({
				type: z.literal("gcs"),
				source: z.string(),
				target: z.string().nullish()
			}),
			z.object({
				type: z.literal("repository"),
				source: z.string(),
				target: z.string().nullish()
			}),
			z.object({
				type: z.literal("inline"),
				content: z.string(),
				target: z.string()
			})
		])).nullish(),
		network: z.union([z.literal("disabled"), z.object({ allowlist: z.array(z.object({
			domain: z.string(),
			transform: z.array(z.record(z.string(), z.string())).nullish()
		})) })]).nullish()
	})]).nullish()
})));
//#endregion
//#region src/interactions/parse-google-interactions-outputs.ts
function googleProviderMetadata({ signature, interactionId, processingId, processingCallId }) {
	const google = {};
	if (signature != null) google.signature = signature;
	if (interactionId != null) google.interactionId = interactionId;
	if (processingId != null) google.processingId = processingId;
	if (processingCallId != null) google.processingCallId = processingCallId;
	return Object.keys(google).length > 0 ? { providerMetadata: { google } } : {};
}
const BUILTIN_TOOL_CALL_TYPES = /* @__PURE__ */ new Set([
	"google_search_call",
	"code_execution_call",
	"url_context_call",
	"file_search_call",
	"google_maps_call",
	"mcp_server_tool_call"
]);
const BUILTIN_TOOL_RESULT_TYPES = /* @__PURE__ */ new Set([
	"google_search_result",
	"code_execution_result",
	"url_context_result",
	"file_search_result",
	"google_maps_result",
	"mcp_server_tool_result"
]);
function builtinToolNameFromCallType(type) {
	return type.replace(/_call$/, "");
}
function builtinToolNameFromResultType(type) {
	return type.replace(/_result$/, "");
}
/**
* Walks the `steps[]` array of an Interactions response and emits AI SDK
* `LanguageModelV4Content[]`. Surfaces:
*
* - `model_output` steps: iterates `step.content[]` for `text` (with
*   annotations → source parts) and `image` content blocks.
* - `thought` steps: emits a single `reasoning` part from `summary[*]`.
* - `function_call` steps: emits a `tool-call` part directly.
* - Built-in tool `*_call` / `*_result` steps (Google Search, Code Execution,
*   URL Context, File Search, Google Maps, MCP Server): emits
*   `tool-call`/`tool-result` parts with `providerExecuted: true`.
* - `user_input` steps are skipped (they echo the client's input).
*/
function parseGoogleInteractionsOutputs({ steps, generateId, interactionId }) {
	const content = [];
	let hasFunctionCall = false;
	if (steps == null) return {
		content,
		hasFunctionCall
	};
	for (const step of steps) {
		if (step == null || typeof step !== "object") continue;
		const type = step.type;
		if (typeof type !== "string") continue;
		switch (type) {
			case "user_input": break;
			case "model_output": {
				const blocks = step.content ?? [];
				for (const block of blocks) {
					if (block == null || typeof block !== "object") continue;
					const blockType = block.type;
					if (blockType === "text") {
						const text = block.text ?? "";
						const annotations = block.annotations;
						content.push({
							type: "text",
							text,
							...googleProviderMetadata({ interactionId })
						});
						const sources = annotationsToSources({
							annotations,
							generateId
						});
						for (const source of sources) content.push(source);
					} else if (blockType === "image") {
						const image = block;
						if (image.data != null && image.data.length > 0) content.push({
							type: "file",
							mediaType: image.mime_type ?? "image/png",
							data: {
								type: "data",
								data: image.data
							},
							...googleProviderMetadata({ interactionId })
						});
						else if (image.uri != null && image.uri.length > 0) content.push({
							type: "file",
							mediaType: image.mime_type ?? "image/png",
							data: {
								type: "url",
								url: new URL(image.uri)
							},
							...googleProviderMetadata({ interactionId })
						});
					} else if (blockType === "video") {
						const video = block;
						if (video.data != null && video.data.length > 0) content.push({
							type: "file",
							mediaType: video.mime_type ?? "video/mp4",
							data: {
								type: "data",
								data: video.data
							},
							...googleProviderMetadata({ interactionId })
						});
						else if (video.uri != null && video.uri.length > 0) content.push({
							type: "file",
							mediaType: video.mime_type ?? "video/mp4",
							data: {
								type: "url",
								url: new URL(video.uri)
							},
							...googleProviderMetadata({ interactionId })
						});
					}
				}
				break;
			}
			case "thought": {
				const thought = step;
				const text = (Array.isArray(thought.summary) ? thought.summary : []).filter((item) => item?.type === "text" && typeof item.text === "string").map((item) => item.text).join("\n");
				content.push({
					type: "reasoning",
					text,
					...googleProviderMetadata({
						signature: thought.signature,
						interactionId
					})
				});
				break;
			}
			case "processing_call": {
				const call = step;
				const processingId = call.id || generateId();
				content.push({
					type: "custom",
					kind: "google.processing_call",
					...googleProviderMetadata({
						signature: call.signature,
						interactionId,
						processingId
					})
				});
				break;
			}
			case "processing_result": {
				const result = step;
				const processingCallId = result.call_id || generateId();
				content.push({
					type: "custom",
					kind: "google.processing_result",
					...googleProviderMetadata({
						signature: result.signature,
						interactionId,
						processingCallId
					})
				});
				break;
			}
			case "function_call": {
				hasFunctionCall = true;
				const call = step;
				content.push({
					type: "tool-call",
					toolCallId: call.id,
					toolName: call.name,
					input: JSON.stringify(call.arguments ?? {}),
					...googleProviderMetadata({
						signature: call.signature,
						interactionId
					})
				});
				break;
			}
			default: if (BUILTIN_TOOL_CALL_TYPES.has(type)) {
				const call = step;
				const toolName = type === "mcp_server_tool_call" ? call.name ?? "mcp_server_tool" : builtinToolNameFromCallType(type);
				const input = JSON.stringify(call.arguments ?? {});
				content.push({
					type: "tool-call",
					toolCallId: call.id || generateId(),
					toolName,
					input,
					providerExecuted: true
				});
			} else if (BUILTIN_TOOL_RESULT_TYPES.has(type)) {
				const result = step;
				const toolName = type === "mcp_server_tool_result" ? result.name ?? "mcp_server_tool" : builtinToolNameFromResultType(type);
				content.push({
					type: "tool-result",
					toolCallId: result.call_id || generateId(),
					toolName,
					result: result.result ?? null
				});
				const sources = builtinToolResultToSources({
					block: step,
					generateId
				});
				for (const source of sources) content.push(source);
			}
		}
	}
	return {
		content,
		hasFunctionCall
	};
}
//#endregion
//#region src/interactions/cancel-google-interaction.ts
const getOriginalFetch = () => globalThis.fetch;
/**
* Best-effort `POST /interactions/{id}/cancel` to stop a background interaction
* on Google's side after the caller has aborted locally. Errors and non-2xx
* responses are swallowed so a cancel failure cannot mask the original abort.
*
* Skips the request entirely if `interactionId` is missing/empty -- e.g. when
* the interaction was created with `store: false` and the API did not return an
* id.
*/
async function cancelGoogleInteraction({ baseURL, interactionId, headers, fetch = getOriginalFetch() }) {
	if (interactionId == null || interactionId.length === 0) return;
	const url = `${baseURL}/interactions/${encodeURIComponent(interactionId)}/cancel`;
	try {
		const response = await fetch(url, {
			method: "POST",
			headers: withUserAgentSuffix(combineHeaders({ "Content-Type": "application/json" }, await resolve(headers)), getRuntimeEnvironmentUserAgent()),
			body: "{}"
		});
		try {
			await response.text();
		} catch {}
	} catch {}
}
//#endregion
//#region src/interactions/poll-google-interactions.ts
const TERMINAL_STATUSES = /* @__PURE__ */ new Set([
	"completed",
	"failed",
	"cancelled",
	"incomplete"
]);
function isTerminalStatus(status) {
	return status != null && TERMINAL_STATUSES.has(status);
}
const DEFAULT_INITIAL_DELAY_MS = 1e3;
const DEFAULT_MAX_DELAY_MS = 1e4;
const DEFAULT_TIMEOUT_MS = 18e5;
/**
* Polls `GET {baseURL}/interactions/{id}` until the response status is
* terminal (`completed` / `failed` / `cancelled` / `incomplete`). Throws if
* the polling loop exceeds `timeoutMs`, the response has no `id` to poll on,
* or the abort signal fires.
*/
async function pollGoogleInteractionUntilTerminal({ baseURL, interactionId, headers, fetch, abortSignal, initialDelayMs = DEFAULT_INITIAL_DELAY_MS, maxDelayMs = DEFAULT_MAX_DELAY_MS, timeoutMs = DEFAULT_TIMEOUT_MS }) {
	if (interactionId == null || interactionId.length === 0) throw new Error("google.interactions: cannot poll a background interaction without an id. The POST response did not include an interaction id.");
	const startedAt = Date.now();
	let nextDelayMs = initialDelayMs;
	const url = `${baseURL}/interactions/${encodeURIComponent(interactionId)}`;
	const cancelOnServer = () => cancelGoogleInteraction({
		baseURL,
		interactionId,
		headers,
		fetch
	});
	try {
		while (true) {
			if (abortSignal?.aborted) {
				await cancelOnServer();
				throw new DOMException("Polling was aborted", "AbortError");
			}
			if (Date.now() - startedAt > timeoutMs) throw new Error(`google.interactions: timed out polling interaction ${interactionId} after ${timeoutMs}ms.`);
			await delay(nextDelayMs, { abortSignal });
			const { value: response, rawValue: rawResponse, responseHeaders } = await getFromApi({
				url,
				validateUrl: false,
				headers: await resolve(headers),
				failedResponseHandler: googleFailedResponseHandler,
				successfulResponseHandler: createJsonResponseHandler(googleInteractionsResponseSchema),
				abortSignal,
				fetch
			});
			if (isTerminalStatus(response.status)) return {
				response,
				rawResponse,
				responseHeaders
			};
			nextDelayMs = Math.min(nextDelayMs * 2, maxDelayMs);
		}
	} catch (error) {
		if (isAbortError(error)) await cancelOnServer();
		throw error;
	}
}
//#endregion
//#region src/interactions/prepare-google-interactions-tools.ts
/**
* Maps AI SDK tool definitions and `toolChoice` onto the Gemini Interactions
* `tools[]` and `tool_choice` request fields.
*
* AI SDK function tools (`{ type: 'function', name, description, inputSchema }`)
* map to Interactions `{ type: 'function', name, description, parameters }`,
* with `parameters` passed through as plain JSON Schema (per
* `googleapis/js-genai` `samples/interactions_tool_call_with_functions.ts` and
* `src/interactions/resources/interactions.ts` `Function.parameters: unknown`).
*
* Provider-defined tools (`{ type: 'provider', id: 'google.<name>', args }`)
* map to the discriminated `Tool` union. The full set of
* provider-defined tool ids supported here:
*
* - `google.google_search`     -> `{ type: 'google_search', search_types? }`
* - `google.code_execution`    -> `{ type: 'code_execution' }`
* - `google.url_context`       -> `{ type: 'url_context' }`
* - `google.file_search`       -> `{ type: 'file_search', file_search_store_names?, top_k?, metadata_filter? }`
* - `google.google_maps`       -> `{ type: 'google_maps', latitude?, longitude?, enable_widget? }`
* - `google.computer_use`      -> `{ type: 'computer_use', environment?, excludedPredefinedFunctions? }`
* - `google.mcp_server`        -> `{ type: 'mcp_server', name?, url?, headers?, allowed_tools? }`
* - `google.retrieval`         -> `{ type: 'retrieval', retrieval_types?, vertex_ai_search_config? }`
*
* `toolChoice` shapes:
* - `'auto'`     -> `'auto'`
* - `'required'` -> `'any'`
* - `'none'`     -> omit all tools and `tool_choice`
* - `{ type: 'tool', toolName }` -> `{ allowed_tools: { mode: 'validated', tools: [name] } }`
*   (Interactions `AllowedTools.tools` is an `Array<string>` of function
*   names, not tool descriptors -- see `src/interactions/resources/interactions.ts`
*   line ~151).
*/
function prepareGoogleInteractionsTools({ tools, toolChoice }) {
	const toolWarnings = [];
	if (toolChoice?.type === "none") return {
		tools: void 0,
		toolChoice: void 0,
		toolWarnings
	};
	const normalized = tools?.length ? tools : void 0;
	if (normalized == null) return {
		tools: void 0,
		toolChoice: void 0,
		toolWarnings
	};
	const interactionsTools = [];
	for (const tool of normalized) {
		if (tool.type === "function") {
			interactionsTools.push({
				type: "function",
				name: tool.name,
				description: tool.description ?? "",
				parameters: tool.inputSchema
			});
			continue;
		}
		if (tool.type === "provider") {
			const args = tool.args ?? {};
			switch (tool.id) {
				case "google.google_search": {
					const searchTypesArg = args.searchTypes;
					let search_types;
					if (searchTypesArg != null && typeof searchTypesArg === "object") {
						const list = [];
						if (searchTypesArg.webSearch != null) list.push("web_search");
						if (searchTypesArg.imageSearch != null) list.push("image_search");
						if (list.length > 0) search_types = list;
					}
					interactionsTools.push({
						type: "google_search",
						...search_types != null ? { search_types } : {}
					});
					break;
				}
				case "google.code_execution":
					interactionsTools.push({ type: "code_execution" });
					break;
				case "google.url_context":
					interactionsTools.push({ type: "url_context" });
					break;
				case "google.file_search":
					interactionsTools.push({
						type: "file_search",
						...args.fileSearchStoreNames != null ? { file_search_store_names: args.fileSearchStoreNames } : {},
						...args.topK != null ? { top_k: args.topK } : {},
						...args.metadataFilter != null ? { metadata_filter: args.metadataFilter } : {}
					});
					break;
				case "google.google_maps":
					interactionsTools.push({
						type: "google_maps",
						...args.latitude != null ? { latitude: args.latitude } : {},
						...args.longitude != null ? { longitude: args.longitude } : {},
						...args.enableWidget != null ? { enable_widget: args.enableWidget } : {}
					});
					break;
				case "google.computer_use":
					interactionsTools.push({
						type: "computer_use",
						environment: args.environment ?? "browser",
						...args.excludedPredefinedFunctions != null ? { excludedPredefinedFunctions: args.excludedPredefinedFunctions } : {}
					});
					break;
				case "google.mcp_server":
					interactionsTools.push({
						type: "mcp_server",
						...args.name != null ? { name: args.name } : {},
						...args.url != null ? { url: args.url } : {},
						...args.headers != null ? { headers: args.headers } : {},
						...args.allowedTools != null ? { allowed_tools: args.allowedTools } : {}
					});
					break;
				case "google.retrieval": {
					const vertexAiSearchConfig = args.vertexAiSearchConfig ?? void 0;
					interactionsTools.push({
						type: "retrieval",
						...args.retrievalTypes != null ? { retrieval_types: args.retrievalTypes } : { retrieval_types: ["vertex_ai_search"] },
						...vertexAiSearchConfig != null ? { vertex_ai_search_config: vertexAiSearchConfig } : {}
					});
					break;
				}
				default: toolWarnings.push({
					type: "unsupported",
					feature: `provider-defined tool ${tool.id}`,
					details: `provider-defined tool ${tool.id} is not supported by google.interactions; tool dropped.`
				});
			}
			continue;
		}
		toolWarnings.push({
			type: "unsupported",
			feature: `tool of type ${tool.type}`,
			details: "Only function tools and google.* provider-defined tools are supported by google.interactions; tool dropped."
		});
	}
	const hasFunctionTool = interactionsTools.some((t) => t.type === "function");
	let mappedToolChoice;
	if (toolChoice != null && hasFunctionTool) switch (toolChoice.type) {
		case "auto":
			mappedToolChoice = "auto";
			break;
		case "required":
			mappedToolChoice = "any";
			break;
		case "tool": mappedToolChoice = { allowed_tools: {
			mode: "validated",
			tools: [toolChoice.toolName]
		} };
	}
	return {
		tools: interactionsTools.length > 0 ? interactionsTools : void 0,
		toolChoice: mappedToolChoice,
		toolWarnings
	};
}
//#endregion
//#region src/interactions/stream-google-interactions.ts
const DEFAULT_MAX_RETRIES = 3;
const DEFAULT_RETRY_DELAY_MS = 500;
/**
* Connects to `GET {baseURL}/interactions/{id}?stream=true` and surfaces the
* server-sent events as a `ReadableStream<ParseResult<GoogleInteractionsEvent>>`
* so the existing `buildGoogleInteractionsStreamTransform` can consume them
* unchanged.
*
* The connection can drop mid-run: long-running agents idle for long
* stretches between SSE events and undici's default body timeout terminates
* the request with `UND_ERR_BODY_TIMEOUT`. We track the last seen `event_id`
* and reconnect with `?last_event_id=<id>` on any unexpected end. After
* `maxRetries` consecutive failures the stream errors out so the caller can
* decide whether to fall back to polling.
*
* The stream completes cleanly when an `interaction.complete` event with a
* terminal status arrives, or when an `error` event arrives.
*/
function streamGoogleInteractionEvents({ baseURL, interactionId, headers, fetch, abortSignal, maxRetries = DEFAULT_MAX_RETRIES, retryDelayMs = DEFAULT_RETRY_DELAY_MS }) {
	if (interactionId.length === 0) throw new Error("google.interactions: cannot stream a background interaction without an id.");
	let lastEventId;
	let complete = false;
	let attempt = 0;
	let receivedAnyEventThisAttempt = false;
	let currentReader;
	const internalAbort = new AbortController();
	const upstreamAbortHandler = () => internalAbort.abort();
	if (abortSignal != null) {
		if (abortSignal.aborted) internalAbort.abort();
		else abortSignal.addEventListener("abort", upstreamAbortHandler, { once: true });
	}
	const effectiveSignal = internalAbort.signal;
	function buildUrl() {
		const base = `${baseURL}/interactions/${encodeURIComponent(interactionId)}`;
		const params = new URLSearchParams({ stream: "true" });
		if (lastEventId != null) params.set("last_event_id", lastEventId);
		return `${base}?${params.toString()}`;
	}
	async function openReader() {
		const { value: stream } = await getFromApi({
			url: buildUrl(),
			validateUrl: false,
			headers: {
				...await resolve(headers),
				accept: "text/event-stream"
			},
			failedResponseHandler: googleFailedResponseHandler,
			successfulResponseHandler: createEventSourceResponseHandler(googleInteractionsEventSchema),
			abortSignal: effectiveSignal,
			fetch
		});
		return stream.getReader();
	}
	return new ReadableStream({
		async start(controller) {
			try {
				while (!complete && !effectiveSignal.aborted) {
					if (currentReader == null) try {
						currentReader = await openReader();
						receivedAnyEventThisAttempt = false;
					} catch (error) {
						if (isAbortError(error) || effectiveSignal.aborted) {
							controller.error(error);
							return;
						}
						attempt++;
						if (attempt >= maxRetries) {
							controller.error(error);
							return;
						}
						await delay(retryDelayMs * attempt, { abortSignal: effectiveSignal });
						continue;
					}
					try {
						const { done, value } = await currentReader.read();
						if (done) {
							currentReader = void 0;
							if (complete) break;
							if (!receivedAnyEventThisAttempt) {
								attempt++;
								if (attempt >= maxRetries) {
									controller.error(/* @__PURE__ */ new Error("google.interactions: SSE stream closed without producing any events."));
									return;
								}
								await delay(retryDelayMs * attempt, { abortSignal: effectiveSignal });
							} else attempt = 0;
							continue;
						}
						receivedAnyEventThisAttempt = true;
						if (value.success) {
							const streamEvent = value.value;
							if (typeof streamEvent.event_id === "string" && streamEvent.event_id.length > 0) lastEventId = streamEvent.event_id;
							if (streamEvent.event_type === "interaction.completed" || streamEvent.event_type === "error") complete = true;
						}
						controller.enqueue(value);
					} catch (error) {
						if (isAbortError(error) || effectiveSignal.aborted) {
							controller.error(error);
							return;
						}
						currentReader = void 0;
						attempt++;
						if (attempt >= maxRetries) {
							controller.error(error);
							return;
						}
						await delay(retryDelayMs * attempt, { abortSignal: effectiveSignal });
					}
				}
				controller.close();
			} catch (error) {
				controller.error(error);
			} finally {
				if (abortSignal != null) abortSignal.removeEventListener("abort", upstreamAbortHandler);
				currentReader?.cancel().catch(() => {});
				currentReader = void 0;
				if (effectiveSignal.aborted && !complete) await cancelGoogleInteraction({
					baseURL,
					interactionId,
					headers,
					fetch
				});
			}
		},
		cancel() {
			internalAbort.abort();
			currentReader?.cancel().catch(() => {});
			currentReader = void 0;
		}
	});
}
//#endregion
//#region src/interactions/synthesize-google-interactions-agent-stream.ts
/**
* Synthesizes a `LanguageModelV4StreamPart` stream from a fully-resolved
* Interaction response (i.e. the `response` returned after polling a
* `background: true` agent call to a terminal status).
*
* Agent calls cannot use SSE (`stream: true` is incompatible with
* `background: true`), so we deterministically replay the polled outputs as a
* stream sequence in the same order/shape `buildGoogleInteractionsStreamTransform`
* would produce. Each text/reasoning block is emitted as a single delta — the
* server has already produced the whole block by the time we synthesize.
*/
function synthesizeGoogleInteractionsAgentStream({ response, warnings, generateId, includeRawChunks, headerServiceTier }) {
	return new ReadableStream({ start(controller) {
		controller.enqueue({
			type: "stream-start",
			warnings
		});
		const interactionId = typeof response.id === "string" && response.id.length > 0 ? response.id : void 0;
		let timestamp;
		const created = response.created;
		if (typeof created === "string") {
			const parsed = new Date(created);
			if (!Number.isNaN(parsed.getTime())) timestamp = parsed;
		}
		controller.enqueue({
			type: "response-metadata",
			...interactionId != null ? { id: interactionId } : {},
			modelId: response.model ?? void 0,
			...timestamp ? { timestamp } : {}
		});
		if (includeRawChunks) controller.enqueue({
			type: "raw",
			rawValue: response
		});
		const { content, hasFunctionCall } = parseGoogleInteractionsOutputs({
			steps: response.steps ?? null,
			generateId,
			interactionId
		});
		let blockCounter = 0;
		const nextBlockId = () => `${interactionId ?? "agent"}:${blockCounter++}`;
		for (const part of content) switch (part.type) {
			case "text": {
				const id = nextBlockId();
				const providerMetadata = part.providerMetadata;
				controller.enqueue({
					type: "text-start",
					id
				});
				if (part.text.length > 0) controller.enqueue({
					type: "text-delta",
					id,
					delta: part.text
				});
				controller.enqueue({
					type: "text-end",
					id,
					...providerMetadata ? { providerMetadata } : {}
				});
				break;
			}
			case "reasoning": {
				const id = nextBlockId();
				const providerMetadata = part.providerMetadata;
				controller.enqueue({
					type: "reasoning-start",
					id
				});
				if (part.text.length > 0) controller.enqueue({
					type: "reasoning-delta",
					id,
					delta: part.text
				});
				controller.enqueue({
					type: "reasoning-end",
					id,
					...providerMetadata ? { providerMetadata } : {}
				});
				break;
			}
			case "tool-call": {
				const providerMetadata = part.providerMetadata;
				controller.enqueue({
					type: "tool-input-start",
					id: part.toolCallId,
					toolName: part.toolName,
					...part.providerExecuted ? { providerExecuted: part.providerExecuted } : {}
				});
				controller.enqueue({
					type: "tool-input-delta",
					id: part.toolCallId,
					delta: part.input
				});
				controller.enqueue({
					type: "tool-input-end",
					id: part.toolCallId
				});
				controller.enqueue({
					type: "tool-call",
					toolCallId: part.toolCallId,
					toolName: part.toolName,
					input: part.input,
					...part.providerExecuted ? { providerExecuted: part.providerExecuted } : {},
					...providerMetadata ? { providerMetadata } : {}
				});
				break;
			}
			case "tool-result":
				controller.enqueue({
					type: "tool-result",
					toolCallId: part.toolCallId,
					toolName: part.toolName,
					result: part.result
				});
				break;
			case "source":
			case "file": controller.enqueue(part);
		}
		const serviceTier = response.service_tier ?? headerServiceTier;
		const finishReason = {
			unified: mapGoogleInteractionsFinishReason({
				status: response.status,
				hasFunctionCall
			}),
			raw: response.status
		};
		const providerMetadata = { google: {
			...interactionId != null ? { interactionId } : {},
			...serviceTier != null ? { serviceTier } : {}
		} };
		controller.enqueue({
			type: "finish",
			finishReason,
			usage: convertGoogleInteractionsUsage(response.usage),
			providerMetadata
		});
		controller.close();
	} });
}
//#endregion
//#region src/interactions/google-interactions-language-model.ts
var GoogleInteractionsLanguageModel = class GoogleInteractionsLanguageModel {
	static [WORKFLOW_SERIALIZE](model) {
		return {
			...serializeModelOptions({
				modelId: model.modelId,
				config: model.config
			}),
			agent: model.agent
		};
	}
	static [WORKFLOW_DESERIALIZE](options) {
		return new GoogleInteractionsLanguageModel(options.agent != null ? { agent: options.agent } : options.modelId, options.config);
	}
	constructor(modelOrAgent, config) {
		this.specificationVersion = "v4";
		if (typeof modelOrAgent === "string") {
			this.modelId = modelOrAgent;
			this.agent = void 0;
		} else if ("managedAgent" in modelOrAgent) {
			this.modelId = modelOrAgent.managedAgent;
			this.agent = modelOrAgent.managedAgent;
		} else {
			this.modelId = modelOrAgent.agent;
			this.agent = modelOrAgent.agent;
		}
		this.config = config;
	}
	get provider() {
		return this.config.provider;
	}
	get supportedUrls() {
		if (this.config.supportedUrls) return this.config.supportedUrls();
		return {
			"image/*": [/^https?:\/\/.+/],
			"application/pdf": [/^https?:\/\/.+/],
			"audio/*": [/^https?:\/\/.+/],
			"video/*": [
				/^https?:\/\/(www\.)?youtube\.com\/watch\?v=.+/,
				/^https?:\/\/youtu\.be\/.+/,
				/^gs:\/\/.+/
			]
		};
	}
	async getArgs(options) {
		const warnings = [];
		const googleOptions = await parseProviderOptions({
			provider: "google",
			providerOptions: options.providerOptions,
			schema: googleInteractionsLanguageModelOptions
		});
		const isAgent = this.agent != null;
		if (!isAgent) {
			if (options.frequencyPenalty != null) warnings.push({
				type: "unsupported",
				feature: "frequencyPenalty"
			});
			if (options.presencePenalty != null) warnings.push({
				type: "unsupported",
				feature: "presencePenalty"
			});
		}
		const hasTools = options.tools != null && options.tools.length > 0;
		let toolsForBody;
		let toolChoiceForBody;
		if (hasTools) {
			const prepared = prepareGoogleInteractionsTools({
				tools: options.tools,
				toolChoice: options.toolChoice
			});
			toolsForBody = prepared.tools;
			toolChoiceForBody = prepared.toolChoice;
			warnings.push(...prepared.toolWarnings);
		}
		const responseFormatEntries = [];
		if (options.responseFormat?.type === "json") {
			if (isAgent) warnings.push({
				type: "other",
				message: "google.interactions: structured output (responseFormat) is not supported when an agent is set; responseFormat will be ignored."
			});
			else {
				const entry = {
					type: "text",
					mime_type: "application/json",
					...options.responseFormat.schema != null ? { schema: options.responseFormat.schema } : {}
				};
				responseFormatEntries.push(entry);
			}
		}
		if (googleOptions?.responseFormat != null) {
			for (const entry of googleOptions.responseFormat) if (entry.type === "text") responseFormatEntries.push(pruneUndefined({
				type: "text",
				mime_type: entry.mimeType ?? void 0,
				schema: entry.schema ?? void 0
			}));
			else if (entry.type === "image") responseFormatEntries.push(pruneUndefined({
				type: "image",
				mime_type: entry.mimeType ?? void 0,
				aspect_ratio: entry.aspectRatio ?? void 0,
				image_size: entry.imageSize ?? void 0
			}));
			else if (entry.type === "audio") responseFormatEntries.push(pruneUndefined({
				type: "audio",
				mime_type: entry.mimeType ?? void 0
			}));
			else if (entry.type === "video") responseFormatEntries.push(pruneUndefined({
				type: "video",
				aspect_ratio: entry.aspectRatio ?? void 0,
				resolution: entry.resolution ?? void 0,
				duration: entry.duration ?? void 0,
				delivery: entry.delivery ?? void 0,
				gcs_uri: entry.gcsUri ?? void 0
			}));
		}
		const { input, systemInstruction: convertedSystemInstruction, warnings: convWarnings } = convertToGoogleInteractionsInput({
			prompt: options.prompt,
			previousInteractionId: googleOptions?.previousInteractionId ?? void 0,
			store: googleOptions?.store ?? void 0,
			mediaResolution: googleOptions?.mediaResolution ?? void 0
		});
		warnings.push(...convWarnings);
		let systemInstruction = convertedSystemInstruction;
		const optionSystemInstruction = googleOptions?.systemInstruction ?? void 0;
		if (systemInstruction != null && optionSystemInstruction != null) warnings.push({
			type: "other",
			message: "google.interactions: both AI SDK system message and providerOptions.google.systemInstruction were set; using the AI SDK system message."
		});
		else if (systemInstruction == null && optionSystemInstruction != null) systemInstruction = optionSystemInstruction;
		let generationConfig;
		if (isAgent) {
			const droppedFields = [];
			if (options.temperature != null) droppedFields.push("temperature");
			if (options.topP != null) droppedFields.push("topP");
			if (options.topK != null) droppedFields.push("topK");
			if (options.frequencyPenalty != null) droppedFields.push("frequencyPenalty");
			if (options.presencePenalty != null) droppedFields.push("presencePenalty");
			if (options.seed != null) droppedFields.push("seed");
			if (options.stopSequences != null && options.stopSequences.length > 0) droppedFields.push("stopSequences");
			if (options.maxOutputTokens != null) droppedFields.push("maxOutputTokens");
			if (isCustomReasoning(options.reasoning)) droppedFields.push("reasoning");
			if (googleOptions?.thinkingLevel != null) droppedFields.push("thinkingLevel");
			if (googleOptions?.thinkingSummaries != null) droppedFields.push("thinkingSummaries");
			if (googleOptions?.imageConfig != null) droppedFields.push("imageConfig");
			if (droppedFields.length > 0) warnings.push({
				type: "other",
				message: `google.interactions: ${droppedFields.join(", ")} ${droppedFields.length === 1 ? "is" : "are"} not supported when an agent is set; use providerOptions.google.agentConfig instead. Dropped from the request body.`
			});
			generationConfig = void 0;
		} else {
			generationConfig = pruneUndefined({
				temperature: options.temperature ?? void 0,
				top_p: options.topP ?? void 0,
				top_k: options.topK ?? void 0,
				seed: options.seed ?? void 0,
				stop_sequences: options.stopSequences != null && options.stopSequences.length > 0 ? options.stopSequences : void 0,
				max_output_tokens: options.maxOutputTokens ?? void 0,
				thinking_level: mapReasoningToProviderEffort({
					reasoning: options.reasoning,
					providerOptionsEffort: googleOptions?.thinkingLevel,
					effortMap: Object.fromEntries((getGoogleLanguageModelEntry(this.modelId).capabilities.language.reasoning.efforts ?? []).map((level) => [level, level])),
					warnings
				}),
				thinking_summaries: googleOptions?.thinkingSummaries ?? void 0,
				tool_choice: toolChoiceForBody
			});
			if (googleOptions?.imageConfig != null) {
				const alreadyHasImageEntry = responseFormatEntries.some((entry) => entry.type === "image");
				warnings.push({
					type: "other",
					message: alreadyHasImageEntry ? "google.interactions: providerOptions.google.imageConfig is deprecated and was ignored because providerOptions.google.responseFormat already supplies an image entry. Use responseFormat exclusively." : "google.interactions: providerOptions.google.imageConfig is deprecated. Use providerOptions.google.responseFormat with a { type: \"image\", ... } entry instead."
				});
				if (!alreadyHasImageEntry) responseFormatEntries.push({
					type: "image",
					mime_type: "image/png",
					...googleOptions.imageConfig.aspectRatio != null ? { aspect_ratio: googleOptions.imageConfig.aspectRatio } : {},
					...googleOptions.imageConfig.imageSize != null ? { image_size: googleOptions.imageConfig.imageSize } : {}
				});
			}
		}
		let agentConfig;
		if (isAgent && googleOptions?.agentConfig != null) {
			const agentConfigOptions = googleOptions.agentConfig;
			if (agentConfigOptions.type === "deep-research") agentConfig = pruneUndefined({
				type: "deep-research",
				thinking_summaries: agentConfigOptions.thinkingSummaries ?? void 0,
				visualization: agentConfigOptions.visualization ?? void 0,
				collaborative_planning: agentConfigOptions.collaborativePlanning ?? void 0
			});
			else if (agentConfigOptions.type === "dynamic") agentConfig = { type: "dynamic" };
		}
		let environment;
		if (googleOptions?.environment != null) {
			if (!isAgent) warnings.push({
				type: "other",
				message: "google.interactions: environment is only supported when an agent is set; environment will be omitted from the request body."
			});
			else if (typeof googleOptions.environment === "string") environment = googleOptions.environment;
			else {
				const environmentOptions = googleOptions.environment;
				const sources = environmentOptions.sources?.map((source) => {
					if (source.type === "inline") return {
						type: "inline",
						content: source.content,
						target: source.target
					};
					return pruneUndefined({
						type: source.type,
						source: source.source,
						target: source.target ?? void 0
					});
				});
				let network;
				if (environmentOptions.network === "disabled") network = "disabled";
				else if (environmentOptions.network != null) network = { allowlist: environmentOptions.network.allowlist.map((entry) => pruneUndefined({
					domain: entry.domain,
					transform: entry.transform ?? void 0
				})) };
				environment = pruneUndefined({
					type: "remote",
					sources: sources != null && sources.length > 0 ? sources : void 0,
					network
				});
			}
		}
		return {
			args: pruneUndefined({
				...isAgent ? { agent: this.agent } : { model: this.modelId },
				input,
				labels: googleOptions?.labels ?? void 0,
				system_instruction: systemInstruction,
				tools: toolsForBody,
				response_format: responseFormatEntries.length > 0 ? responseFormatEntries : void 0,
				response_modalities: googleOptions?.responseModalities != null ? googleOptions.responseModalities : void 0,
				previous_interaction_id: googleOptions?.previousInteractionId ?? void 0,
				service_tier: googleOptions?.serviceTier ?? void 0,
				store: googleOptions?.store ?? void 0,
				generation_config: generationConfig != null && Object.keys(generationConfig).length > 0 ? generationConfig : void 0,
				agent_config: agentConfig,
				environment,
				background: googleOptions?.background ?? void 0
			}),
			warnings,
			isAgent,
			isBackground: googleOptions?.background === true,
			pollingTimeoutMs: googleOptions?.pollingTimeoutMs ?? void 0
		};
	}
	async doGenerate(options) {
		const { args, warnings, isAgent, isBackground, pollingTimeoutMs } = await this.getArgs(options);
		const url = `${this.config.baseURL}/interactions`;
		const mergedHeaders = async () => combineHeaders(await resolve(this.config.headers), options.headers);
		let { responseHeaders, value: response, rawValue: rawResponse } = await postJsonToApi({
			url,
			headers: await resolve(mergedHeaders),
			body: args,
			failedResponseHandler: googleFailedResponseHandler,
			successfulResponseHandler: createJsonResponseHandler(googleInteractionsResponseSchema),
			abortSignal: options.abortSignal,
			fetch: this.config.fetch
		});
		if ((isAgent || isBackground) && !isTerminalStatus(response.status)) {
			const polled = await pollGoogleInteractionUntilTerminal({
				baseURL: this.config.baseURL,
				interactionId: response.id,
				headers: mergedHeaders,
				fetch: this.config.fetch,
				abortSignal: options.abortSignal,
				timeoutMs: pollingTimeoutMs
			});
			response = polled.response;
			rawResponse = polled.rawResponse;
			responseHeaders = polled.responseHeaders ?? responseHeaders;
		}
		const interactionId = typeof response.id === "string" && response.id.length > 0 ? response.id : void 0;
		const { content, hasFunctionCall } = parseGoogleInteractionsOutputs({
			steps: response.steps ?? null,
			generateId: this.config.generateId ?? generateId,
			interactionId
		});
		const finishReason = {
			unified: mapGoogleInteractionsFinishReason({
				status: response.status,
				hasFunctionCall
			}),
			raw: response.status
		};
		const serviceTier = response.service_tier ?? responseHeaders?.["x-gemini-service-tier"] ?? void 0;
		const outputTokensByModality = getGoogleInteractionsOutputTokensByModality(response.usage);
		const providerMetadata = { google: {
			...interactionId != null ? { interactionId } : {},
			...serviceTier != null ? { serviceTier } : {},
			...outputTokensByModality != null ? { outputTokensByModality } : {}
		} };
		let timestamp;
		if (typeof response.created === "string") {
			const parsed = new Date(response.created);
			if (!Number.isNaN(parsed.getTime())) timestamp = parsed;
		}
		return {
			content,
			finishReason,
			usage: convertGoogleInteractionsUsage(response.usage),
			warnings,
			providerMetadata,
			request: { body: args },
			response: {
				headers: responseHeaders,
				body: rawResponse,
				...interactionId != null ? { id: interactionId } : {},
				...timestamp ? { timestamp } : {},
				modelId: response.model ?? void 0
			}
		};
	}
	async doStream(options) {
		const { args, warnings, isBackground, pollingTimeoutMs } = await this.getArgs(options);
		const url = `${this.config.baseURL}/interactions`;
		const mergedHeaders = async () => combineHeaders(await resolve(this.config.headers), options.headers);
		if (isBackground) return this.doStreamBackground({
			args,
			warnings,
			url,
			mergedHeaders,
			options,
			pollingTimeoutMs
		});
		const body = {
			...args,
			stream: true
		};
		const { responseHeaders, value: response } = await postJsonToApi({
			url,
			headers: await resolve(mergedHeaders),
			body,
			failedResponseHandler: googleFailedResponseHandler,
			successfulResponseHandler: createEventSourceResponseHandler(googleInteractionsEventSchema),
			abortSignal: options.abortSignal,
			fetch: this.config.fetch
		});
		const headerServiceTier = responseHeaders?.["x-gemini-service-tier"];
		const transform = buildGoogleInteractionsStreamTransform({
			warnings,
			generateId: this.config.generateId ?? generateId,
			includeRawChunks: options.includeRawChunks,
			serviceTier: headerServiceTier
		});
		return {
			stream: response.pipeThrough(transform),
			request: { body },
			response: { headers: responseHeaders }
		};
	}
	async doStreamBackground({ args, warnings, url, mergedHeaders, options, pollingTimeoutMs }) {
		const { responseHeaders: postHeaders, value: postResponse } = await postJsonToApi({
			url,
			headers: await resolve(mergedHeaders),
			body: args,
			failedResponseHandler: googleFailedResponseHandler,
			successfulResponseHandler: createJsonResponseHandler(googleInteractionsResponseSchema),
			abortSignal: options.abortSignal,
			fetch: this.config.fetch
		});
		const interactionId = postResponse.id;
		if (interactionId == null || interactionId.length === 0) throw new Error("google.interactions: background POST response did not include an interaction id; cannot stream the result.");
		const headerServiceTier = postHeaders?.["x-gemini-service-tier"];
		if (isTerminalStatus(postResponse.status)) return {
			stream: synthesizeGoogleInteractionsAgentStream({
				response: postResponse,
				warnings,
				generateId: this.config.generateId ?? generateId,
				includeRawChunks: options.includeRawChunks,
				headerServiceTier
			}),
			request: { body: args },
			response: { headers: postHeaders }
		};
		const events = streamGoogleInteractionEvents({
			baseURL: this.config.baseURL,
			interactionId,
			headers: mergedHeaders,
			fetch: this.config.fetch,
			abortSignal: options.abortSignal
		});
		const transform = buildGoogleInteractionsStreamTransform({
			warnings,
			generateId: this.config.generateId ?? generateId,
			includeRawChunks: options.includeRawChunks,
			serviceTier: headerServiceTier
		});
		return {
			stream: events.pipeThrough(transform),
			request: { body: args },
			response: { headers: postHeaders }
		};
	}
};
function pruneUndefined(obj) {
	const result = {};
	for (const [key, value] of Object.entries(obj)) {
		if (value === void 0) continue;
		result[key] = value;
	}
	return result;
}
//#endregion
export { GoogleInteractionsLanguageModel, GoogleLanguageModel, GoogleSpeechModel, getGoogleLanguageModelEntry, getGoogleSpeechInput, getGroundingMetadataSchema, getUrlContextMetadataSchema, googleProviderOptions, googleTools, responseSchema };

//# sourceMappingURL=index.js.map