UNPKG

@ai-sdk/google

Version:
9,018 lines • 327 kB
import { EXPERIMENTAL_EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER, WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE, combineHeaders, connectToWebSocket, convertAsyncIteratorToReadableStream, convertBase64ToUint8Array, convertInlineFileDataToUint8Array, convertJsonResponseToolStream, convertToBase64, convertUint8ArrayToBase64, createEventSourceResponseHandler, createJsonErrorResponseHandler, createJsonLinesResponseHandler, createJsonResponseHandler, createNullLanguageModelUsage, createProviderExecutedToolFactory, createProviderStreamError, createToolNameMapping, delay, detectMediaType, downloadBlob, encodePathSegment, generateId, getFromApi, getRuntimeEnvironmentUserAgent, getTopLevelMediaType, isAbortError, isCustomReasoning, isFullMediaType, isRecord, isSameOrigin, isUrlSupported, lazySchema, loadApiKey, mapReasoningToProviderBudget, mapReasoningToProviderEffort, normalizeBatchRequestCounts, parseProviderOptions, postJsonToApi, postToApi, removeUndefinedEntries, resolve, resolveFullMediaType, resolveProviderOptions, resolveProviderReference, resolveWith, safeParseJSON, safeValidateTypes, secureJsonParse, serializeModelOptions, stringifyWithBigIntAndCycles, waitForWebSocketBufferDrain, withUserAgentSuffix, withoutTrailingSlash, zodSchema } from "@ai-sdk/provider-utils";
import { Experimental_ExtractionLanguageModel } from "@ai-sdk/provider-utils/experimental-extraction";
import { AISDKError, Experimental_DecisionUnsupportedQuestionTypeError, InvalidArgumentError, InvalidResponseDataError, TooManyEmbeddingValuesForCallError, UnsupportedFunctionalityError } from "@ai-sdk/provider";
import { z } from "zod/v4";
//#region src/google-decision-api.ts
const googleDecisionResponseSchema = lazySchema(() => {
	const probability = z.number().min(0).max(1);
	return zodSchema(z.object({
		answers: z.record(z.string(), z.discriminatedUnion("type", [
			z.object({
				type: z.literal("noul"),
				noul: probability,
				confidence: probability.nullish()
			}),
			z.object({
				type: z.literal("choice"),
				choice: z.string(),
				probabilities: z.record(z.string(), probability),
				confidence: probability.nullish()
			}),
			z.object({
				type: z.literal("score"),
				score: z.number(),
				probabilities: z.record(z.string(), probability),
				confidence: probability.nullish(),
				legend: z.record(z.string(), z.string()).nullish()
			})
		])).nullish(),
		usage: z.object({
			total_input_tokens: z.number().nullish(),
			total_output_tokens: z.number().nullish(),
			total_tokens: z.number().nullish(),
			input_tokens_by_modality: z.array(z.object({
				modality: z.string(),
				tokens: z.number()
			})).nullish()
		}).nullish(),
		prompt_feedback: z.object({
			block_reason: z.string().nullish(),
			block_reason_message: z.string().nullish()
		}).nullish()
	}));
});
//#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/google-decision-model.ts
var DecisionGoogleModel = class DecisionGoogleModel {
	constructor(modelId, config) {
		this.modelId = modelId;
		this.config = config;
		this.specificationVersion = "v4";
		this.supportedQuestionTypes = [
			"choice",
			"score",
			"boolean"
		];
	}
	get provider() {
		return this.config.provider;
	}
	static [WORKFLOW_SERIALIZE](model) {
		return serializeModelOptions({
			modelId: model.modelId,
			config: model.config
		});
	}
	static [WORKFLOW_DESERIALIZE](options) {
		return new DecisionGoogleModel(options.modelId, options.config);
	}
	/** @deprecated Use `doDecide` instead. */
	doEvaluate(options) {
		return this.doDecide(options);
	}
	async doDecide({ state, questions, maxOutputTokens, reasoning, abortSignal, headers, providerOptions }) {
		abortSignal?.throwIfAborted();
		const entries = Object.entries(questions);
		if (entries.length === 0) throw new InvalidArgumentError({
			argument: "questions",
			message: "Google Decisions requires at least one question."
		});
		const requestQuestions = Object.fromEntries(entries.map(([id, question]) => {
			if (question.type === "box") throw new Experimental_DecisionUnsupportedQuestionTypeError({
				questionId: id,
				questionType: question.type,
				provider: this.provider,
				modelId: this.modelId
			});
			if (question.instructions == null) throw new InvalidArgumentError({
				argument: `questions.${id}.instructions`,
				message: "Google Decisions requires instructions for every question."
			});
			const instructions = toText(question.instructions);
			if (question.type === "boolean") return [id, {
				type: "noul",
				instructions,
				criteria: question.criteria == null ? void 0 : {
					true_description: question.criteria.true == null ? void 0 : toText(question.criteria.true),
					false_description: question.criteria.false == null ? void 0 : toText(question.criteria.false)
				}
			}];
			const count = Object.keys(question.criteria).length;
			if (count < 2 || count > 255) throw new InvalidArgumentError({
				argument: `questions.${id}.criteria`,
				message: "Google Decisions requires 2 to 255 options or score levels."
			});
			if (question.type === "score" && question.criteria.some((level) => level == null || level === "")) throw new InvalidArgumentError({
				argument: `questions.${id}.criteria`,
				message: "Google Decisions requires non-empty score level descriptions."
			});
			return [id, {
				type: question.type,
				instructions,
				criteria: question.type === "choice" ? Object.fromEntries(Object.entries(question.criteria).map(([key, value]) => [key, value == null ? null : toText(value)])) : question.criteria.map((level) => toText(level))
			}];
		}));
		const input = state.map((part) => {
			if (part.type === "text") return {
				type: "text",
				text: part.text
			};
			if (part.type === "json") return {
				type: "text",
				text: JSON.stringify(part.value)
			};
			const type = part.mediaType === "application/pdf" ? "document" : part.mediaType.split("/")[0];
			if (![
				"image",
				"audio",
				"video",
				"document"
			].includes(type)) throw new UnsupportedFunctionalityError({ functionality: `Google decision file media type: ${part.mediaType}` });
			if (part.data.type !== "data" && part.data.type !== "url") throw new UnsupportedFunctionalityError({ functionality: `Google decision file input: ${part.data.type}` });
			return {
				type,
				mime_type: part.mediaType,
				...part.data.type === "url" ? { uri: part.data.url.toString() } : { data: typeof part.data.data === "string" ? part.data.data : convertUint8ArrayToBase64(part.data.data) }
			};
		});
		const { value: response, rawValue, responseHeaders } = await postJsonToApi({
			url: `${this.config.baseURL}/decisions`,
			headers: combineHeaders(await resolve(this.config.headers), headers),
			body: {
				model: this.modelId,
				input,
				questions: requestQuestions
			},
			failedResponseHandler: googleFailedResponseHandler,
			successfulResponseHandler: createJsonResponseHandler(googleDecisionResponseSchema),
			abortSignal,
			fetch: this.config.fetch
		});
		abortSignal?.throwIfAborted();
		const blocked = response.prompt_feedback?.block_reason != null;
		const responseAnswers = response.answers;
		if (!blocked && (responseAnswers == null || Object.keys(responseAnswers).length !== entries.length || entries.some(([id]) => !Object.prototype.hasOwnProperty.call(responseAnswers, id)))) throw new InvalidResponseDataError({
			data: rawValue,
			message: "Google Decisions must return exactly one answer for every question."
		});
		return {
			answers: Object.fromEntries(entries.map(([id, question]) => {
				if (blocked) return [id, { type: "refusal" }];
				const answer = responseAnswers[id];
				if (question.type === "boolean" && answer.type === "noul") return [id, {
					type: "boolean",
					probability: answer.noul
				}];
				if (question.type === "choice" && answer.type === "choice" && Object.prototype.hasOwnProperty.call(question.criteria, answer.choice)) return [id, {
					type: "choice",
					choice: answer.choice,
					probabilities: answer.probabilities
				}];
				if (question.type === "score" && answer.type === "score" && answer.score >= 0 && answer.score <= question.criteria.length - 1) return [id, {
					type: "score",
					score: answer.score,
					probabilities: answer.probabilities
				}];
				throw new InvalidResponseDataError({
					data: rawValue,
					message: `Google Decisions returned an invalid answer for question "${id}".`
				});
			})),
			usage: response.usage == null ? void 0 : {
				inputTokens: response.usage.total_input_tokens ?? void 0,
				outputTokens: response.usage.total_output_tokens ?? void 0
			},
			warnings: [
				...maxOutputTokens != null ? [{
					type: "unsupported",
					feature: "maxOutputTokens"
				}] : [],
				...reasoning != null ? [{
					type: "unsupported",
					feature: "reasoning"
				}] : [],
				...Object.keys(providerOptions?.google ?? {}).map((option) => ({
					type: "unsupported",
					feature: `providerOptions.google.${option}`
				}))
			],
			providerMetadata: { google: {
				...response.usage == null ? {} : { usage: response.usage },
				...response.prompt_feedback == null ? {} : { promptFeedback: response.prompt_feedback },
				confidence: Object.fromEntries(Object.entries(responseAnswers ?? {}).flatMap(([id, answer]) => answer.confidence == null ? [] : [[id, answer.confidence]])),
				legend: Object.fromEntries(Object.entries(responseAnswers ?? {}).flatMap(([id, answer]) => answer.type === "score" && answer.legend != null ? [[id, answer.legend]] : []))
			} },
			response: {
				modelId: this.modelId,
				headers: responseHeaders,
				body: rawValue
			}
		};
	}
};
function toText(input) {
	return typeof input === "string" ? input : JSON.stringify(input);
}
//#endregion
//#region src/version.ts
const VERSION = "4.0.121";
//#endregion
//#region src/google-embedding-model-options.ts
const googleEmbeddingContentPartSchema = () => z.union([
	z.object({ text: z.string() }),
	z.object({ inlineData: z.object({
		mimeType: z.string(),
		data: z.string()
	}) }),
	z.object({ fileData: z.object({
		fileUri: z.string(),
		mimeType: z.string()
	}) })
]);
const googleEmbeddingModelOptions = lazySchema(() => zodSchema(z.object({
	/**
	* Optional. Optional reduced dimension for the output embedding.
	* If set, excessive values in the output embedding are truncated from the end.
	*/
	outputDimensionality: z.number().optional(),
	/**
	* Optional. Specifies the task type for generating embeddings.
	* Supported task types:
	* - SEMANTIC_SIMILARITY: Optimized for text similarity.
	* - CLASSIFICATION: Optimized for text classification.
	* - CLUSTERING: Optimized for clustering texts based on similarity.
	* - RETRIEVAL_DOCUMENT: Optimized for document retrieval.
	* - RETRIEVAL_QUERY: Optimized for query-based retrieval.
	* - QUESTION_ANSWERING: Optimized for answering questions.
	* - FACT_VERIFICATION: Optimized for verifying factual information.
	* - CODE_RETRIEVAL_QUERY: Optimized for retrieving code blocks based on natural language queries.
	*/
	taskType: z.enum([
		"SEMANTIC_SIMILARITY",
		"CLASSIFICATION",
		"CLUSTERING",
		"RETRIEVAL_DOCUMENT",
		"RETRIEVAL_QUERY",
		"QUESTION_ANSWERING",
		"FACT_VERIFICATION",
		"CODE_RETRIEVAL_QUERY"
	]).optional(),
	/**
	* Optional. Per-value multimodal content parts for embedding non-text
	* content (images, video, PDF, audio). Each entry corresponds to the
	* embedding value at the same index and its parts are merged with the
	* text value in the request. Use `null` for entries that are text-only.
	*
	* The array length must match the number of values being embedded. In
	* the case of a single embedding, the array length must be 1.
	*/
	content: z.array(z.array(googleEmbeddingContentPartSchema()).min(1).nullable()).optional()
})));
//#endregion
//#region src/google-embedding-model.ts
var GoogleEmbeddingModel = class GoogleEmbeddingModel {
	static [WORKFLOW_SERIALIZE](model) {
		return serializeModelOptions({
			modelId: model.modelId,
			config: model.config
		});
	}
	static [WORKFLOW_DESERIALIZE](options) {
		return new GoogleEmbeddingModel(options.modelId, options.config);
	}
	get provider() {
		return this.config.provider;
	}
	constructor(modelId, config) {
		this.specificationVersion = "v4";
		this.maxEmbeddingsPerCall = 100;
		this.supportsParallelCalls = true;
		this[EXPERIMENTAL_EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER] = ({ providerOptions, values, startIndex, endIndex }) => {
			const multimodalContent = providerOptions?.google?.content;
			if (!Array.isArray(multimodalContent)) return providerOptions;
			validateMultimodalContentLength({
				multimodalContent,
				values
			});
			return {
				...providerOptions,
				google: {
					...providerOptions?.google,
					content: multimodalContent.slice(startIndex, endIndex)
				}
			};
		};
		this.modelId = modelId;
		this.config = config;
	}
	async doEmbed({ values, dimensions, headers, abortSignal, providerOptions }) {
		const googleOptions = await parseProviderOptions({
			provider: "google",
			providerOptions,
			schema: googleEmbeddingModelOptions
		});
		if (values.length > this.maxEmbeddingsPerCall) throw new TooManyEmbeddingValuesForCallError({
			provider: this.provider,
			modelId: this.modelId,
			maxEmbeddingsPerCall: this.maxEmbeddingsPerCall,
			values
		});
		const mergedHeaders = combineHeaders(this.config.headers ? await resolve(this.config.headers) : void 0, headers);
		const multimodalContent = googleOptions?.content;
		validateMultimodalContentLength({
			multimodalContent,
			values
		});
		if (values.length === 1) {
			const valueParts = multimodalContent?.[0];
			const textPart = values[0] ? [{ text: values[0] }] : [];
			const parts = valueParts != null ? [...textPart, ...valueParts] : [{ text: values[0] }];
			const { responseHeaders, value: response, rawValue } = await postJsonToApi({
				url: `${this.config.baseURL}/models/${this.modelId}:embedContent`,
				headers: mergedHeaders,
				body: {
					model: `models/${this.modelId}`,
					content: { parts },
					outputDimensionality: googleOptions?.outputDimensionality ?? dimensions,
					taskType: googleOptions?.taskType
				},
				failedResponseHandler: googleFailedResponseHandler,
				successfulResponseHandler: createJsonResponseHandler(googleGenerativeAISingleEmbeddingResponseSchema),
				abortSignal,
				fetch: this.config.fetch
			});
			return {
				warnings: [],
				embeddings: [response.embedding.values],
				usage: void 0,
				response: {
					headers: responseHeaders,
					body: rawValue
				}
			};
		}
		const { responseHeaders, value: response, rawValue } = await postJsonToApi({
			url: `${this.config.baseURL}/models/${this.modelId}:batchEmbedContents`,
			headers: mergedHeaders,
			body: { requests: values.map((value, index) => {
				const valueParts = multimodalContent?.[index];
				const textPart = value ? [{ text: value }] : [];
				return {
					model: `models/${this.modelId}`,
					content: {
						role: "user",
						parts: valueParts != null ? [...textPart, ...valueParts] : [{ text: value }]
					},
					outputDimensionality: googleOptions?.outputDimensionality ?? dimensions,
					taskType: googleOptions?.taskType
				};
			}) },
			failedResponseHandler: googleFailedResponseHandler,
			successfulResponseHandler: createJsonResponseHandler(googleGenerativeAITextEmbeddingResponseSchema),
			abortSignal,
			fetch: this.config.fetch
		});
		return {
			warnings: [],
			embeddings: response.embeddings.map((item) => item.values),
			usage: void 0,
			response: {
				headers: responseHeaders,
				body: rawValue
			}
		};
	}
};
const googleGenerativeAITextEmbeddingResponseSchema = lazySchema(() => zodSchema(z.object({ embeddings: z.array(z.object({ values: z.array(z.number()) })) })));
const googleGenerativeAISingleEmbeddingResponseSchema = lazySchema(() => zodSchema(z.object({ embedding: z.object({ values: z.array(z.number()) }) })));
function validateMultimodalContentLength({ multimodalContent, values }) {
	if (multimodalContent != null && multimodalContent.length !== values.length) throw new Error(`The number of multimodal content entries (${multimodalContent.length}) must match the number of values (${values.length}).`);
}
//#endregion
//#region src/get-model-path.ts
function getModelPath(modelId) {
	return modelId.includes("/") ? modelId : `models/${modelId}`;
}
//#endregion
//#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/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/tool/google-search.ts
const googleSearchToolArgsBaseSchema = () => z.looseObject({
	searchTypes: z.object({
		webSearch: z.object({}).optional(),
		imageSearch: z.object({}).optional()
	}).optional(),
	timeRangeFilter: z.object({
		startTime: z.string(),
		endTime: z.string()
	}).optional()
});
const googleSearch = createProviderExecutedToolFactory({
	id: "google.google_search",
	inputSchema: lazySchema(() => zodSchema(z.object({}))),
	outputSchema: lazySchema(() => zodSchema(z.object({})))
});
//#endregion
//#region src/google-image-model-options.ts
const googleImageModelOptionsSchema = lazySchema(() => zodSchema(z.object({ 
/**
* Enable Google Search grounding for Gemini image models. The value is
* forwarded as the args of the `google.tools.googleSearch` provider
* tool on the underlying language-model call. Pass `{}` for defaults.
*
* `generateImage` does not accept a `tools` parameter, so this is the
* dedicated escape hatch for grounding image generation the same way
* `generateText` does.
*/
googleSearch: googleSearchToolArgsBaseSchema().optional() })));
//#endregion
//#region src/google-batch.ts
const googleBatchInputFileMaxBytes = 2147483648;
const googleBatchInlineCreationMaxBytes = 2e7;
const supportedGoogleBatchContentTypes = /* @__PURE__ */ new Set([
	"text",
	"reasoning",
	"source",
	"tool-call",
	"tool-result"
]);
function assertSupportedBatchRequests(requests) {
	for (const request of requests) {
		const requestType = request.type;
		if (requestType !== "text" && requestType !== "image") throw new UnsupportedFunctionalityError({
			functionality: `batch request type: ${requestType}`,
			message: `The Google Batch API does not support batch requests with type "${requestType}".`
		});
	}
}
const googleRpcStatusSchema = () => z.object({
	code: z.union([z.number(), z.string()]).nullish(),
	message: z.string().nullish(),
	status: z.string().nullish()
});
const googleBatchStatsSchema = () => z.object({
	requestCount: z.union([z.string(), z.number()]).nullish(),
	successfulRequestCount: z.union([z.string(), z.number()]).nullish(),
	failedRequestCount: z.union([z.string(), z.number()]).nullish(),
	pendingRequestCount: z.union([z.string(), z.number()]).nullish()
});
const googleBatchOutputSchema = () => z.object({
	responsesFile: z.string().nullish(),
	inlinedResponses: z.object({ inlinedResponses: z.array(z.object({
		metadata: z.object({ key: z.string() }),
		response: z.unknown().nullish(),
		error: googleRpcStatusSchema().nullish()
	})) }).nullish()
});
const googleBatchOperationZodSchema = () => z.object({
	name: z.string(),
	metadata: z.object({
		state: z.string().nullish(),
		createTime: z.string().nullish(),
		batchStats: googleBatchStatsSchema().nullish(),
		output: googleBatchOutputSchema().nullish()
	}).nullish(),
	done: z.boolean().nullish(),
	error: googleRpcStatusSchema().nullish(),
	response: googleBatchOutputSchema().nullish()
});
const googleBatchOperationSchema = lazySchema(() => zodSchema(googleBatchOperationZodSchema()));
const googleBatchListResponseSchema = lazySchema(() => zodSchema(z.object({
	operations: z.array(googleBatchOperationZodSchema()).nullish(),
	nextPageToken: z.string().nullish()
})));
const googleBatchCancelResponseSchema = lazySchema(() => zodSchema(z.object({})));
const googleFileUploadResponseSchema = lazySchema(() => zodSchema(z.object({ file: z.object({
	name: z.string(),
	expirationTime: z.string().nullish()
}) })));
const googleBatchResultLineSchema = lazySchema(() => zodSchema(z.object({
	key: z.string(),
	response: z.unknown().nullish(),
	error: googleRpcStatusSchema().nullish()
})));
const googleBatchResponsePreviewSchema = lazySchema(() => zodSchema(z.object({
	candidates: z.array(z.unknown()).nullish(),
	promptFeedback: z.object({ blockReason: z.string().nullish() }).nullish()
})));
var GoogleBatch = class {
	constructor(options) {
		this.specificationVersion = "v4";
		this.provider = options.provider;
		this.batchConfig = options.config;
		this.supportedUrls = options.supportedUrls;
		this.batchGenerateId = options.config.generateId ?? generateId;
		this.maxLineBytes = options.maxLineBytes;
	}
	async doStartBatch(options) {
		assertSupportedBatchRequests(options.requests);
		const modelId = getGoogleBatchModelId(options.requests);
		const warnings = [];
		const displayName = `ai-sdk-batch-${this.batchGenerateId()}`;
		const inlinedRequests = [];
		const inlineBatchBody = { batch: {
			displayName,
			...options.webhookUrl != null && { webhookConfig: { uris: [options.webhookUrl] } },
			inputConfig: { requests: { requests: inlinedRequests } }
		} };
		const textEncoder = new TextEncoder();
		let inlineInputBytes = textEncoder.encode(JSON.stringify(inlineBatchBody)).byteLength;
		let fileParts;
		for (const request of options.requests) {
			const preparedRequest = request.type === "text" ? await GoogleLanguageModel.prepareRequest({
				modelId: request.modelId,
				config: this.batchConfig,
				options: request.options
			}) : await this.prepareImageRequest(request);
			const inlinedRequest = {
				request: preparedRequest.args,
				metadata: { key: request.id }
			};
			if (fileParts == null) {
				const requestBytes = textEncoder.encode(JSON.stringify(inlinedRequest)).byteLength;
				const nextInlineInputBytes = inlineInputBytes + requestBytes + (inlinedRequests.length > 0 ? 1 : 0);
				if (nextInlineInputBytes < googleBatchInlineCreationMaxBytes) {
					inlinedRequests.push(inlinedRequest);
					inlineInputBytes = nextInlineInputBytes;
				} else {
					fileParts = [];
					for (const previousRequest of inlinedRequests) fileParts.push(JSON.stringify({
						key: previousRequest.metadata.key,
						request: previousRequest.request
					}), "\n");
					inlinedRequests.length = 0;
					fileParts.push(JSON.stringify({
						key: request.id,
						request: preparedRequest.args
					}), "\n");
				}
			} else fileParts.push(JSON.stringify({
				key: request.id,
				request: preparedRequest.args
			}), "\n");
			for (const warning of preparedRequest.warnings) warnings.push({
				requestId: request.id,
				warning
			});
		}
		const createUrl = `${this.batchConfig.baseURL}/${getModelPath(modelId)}:batchGenerateContent`;
		if (fileParts == null) {
			const { value: operation } = await postJsonToApi({
				url: createUrl,
				headers: await this.getHeaders(options.headers),
				body: inlineBatchBody,
				failedResponseHandler: googleFailedResponseHandler,
				successfulResponseHandler: createJsonResponseHandler(googleBatchOperationSchema),
				abortSignal: options.abortSignal,
				fetch: this.batchConfig.fetch
			});
			return {
				batchId: operation.name,
				...convertGoogleBatchStatus(operation),
				warnings
			};
		}
		const inputFile = new Blob(fileParts, { type: "application/jsonl" });
		fileParts.length = 0;
		if (inputFile.size > googleBatchInputFileMaxBytes) throw new InvalidArgumentError({
			argument: "requests",
			message: "Google batch input files must not exceed 2 GB."
		});
		const { value: uploadUrl } = await postJsonToApi({
			url: `${this.getBaseOrigin()}/upload/v1beta/files`,
			headers: combineHeaders(await this.getHeaders(options.headers), {
				"X-Goog-Upload-Protocol": "resumable",
				"X-Goog-Upload-Command": "start",
				"X-Goog-Upload-Header-Content-Length": String(inputFile.size),
				"X-Goog-Upload-Header-Content-Type": "application/jsonl"
			}),
			body: { file: { display_name: `${displayName}-input` } },
			failedResponseHandler: googleFailedResponseHandler,
			successfulResponseHandler: googleUploadUrlResponseHandler,
			abortSignal: options.abortSignal,
			fetch: this.batchConfig.fetch
		});
		const { value: uploadedFile } = await postToApi({
			url: uploadUrl,
			headers: {
				"X-Goog-Upload-Offset": "0",
				"X-Goog-Upload-Command": "upload, finalize",
				"Content-Type": "application/jsonl"
			},
			body: {
				content: inputFile,
				values: {
					byteLength: inputFile.size,
					mediaType: "application/jsonl"
				}
			},
			failedResponseHandler: googleFailedResponseHandler,
			successfulResponseHandler: createJsonResponseHandler(googleFileUploadResponseSchema),
			abortSignal: options.abortSignal,
			fetch: this.batchConfig.fetch
		});
		const { value: operation } = await postJsonToApi({
			url: createUrl,
			headers: await this.getHeaders(options.headers),
			body: { batch: {
				displayName,
				...options.webhookUrl != null && { webhookConfig: { uris: [options.webhookUrl] } },
				inputConfig: { fileName: uploadedFile.file.name }
			} },
			failedResponseHandler: googleFailedResponseHandler,
			successfulResponseHandler: createJsonResponseHandler(googleBatchOperationSchema),
			abortSignal: options.abortSignal,
			fetch: this.batchConfig.fetch
		});
		return {
			batchId: operation.name,
			...convertGoogleBatchStatus(operation),
			providerMetadata: { google: {
				inputFileId: uploadedFile.file.name,
				...uploadedFile.file.expirationTime != null ? { inputFileExpiresAt: uploadedFile.file.expirationTime } : {}
			} },
			warnings
		};
	}
	async doGetBatchStatus(options) {
		return convertGoogleBatchStatus(await this.retrieveBatch(options));
	}
	async doCancelBatch(options) {
		await postJsonToApi({
			url: `${this.batchConfig.baseURL}/${options.batchId}:cancel`,
			headers: await this.getHeaders(options.headers),
			body: {},
			failedResponseHandler: googleFailedResponseHandler,
			successfulResponseHandler: createJsonResponseHandler(googleBatchCancelResponseSchema),
			abortSignal: options.abortSignal,
			fetch: this.batchConfig.fetch
		});
		return {};
	}
	async doListBatches(options) {
		const url = new URL(`${this.batchConfig.baseURL}/batches`);
		if (options.limit != null) url.searchParams.set("pageSize", String(options.limit));
		if (options.cursor != null) url.searchParams.set("pageToken", options.cursor);
		const { value: page } = await getFromApi({
			url: url.toString(),
			headers: await this.getHeaders(options.headers),
			failedResponseHandler: googleFailedResponseHandler,
			successfulResponseHandler: createJsonResponseHandler(googleBatchListResponseSchema),
			abortSignal: options.abortSignal,
			fetch: this.batchConfig.fetch,
			validateUrl: false
		});
		return {
			batches: (page.operations ?? []).map((operation) => ({
				batchId: operation.name,
				...convertGoogleBatchStatus(operation)
			})),
			...page.nextPageToken != null ? { nextCursor: page.nextPageToken } : {}
		};
	}
	async doGetBatchResults(options) {
		const operation = await this.retrieveBatch(options);
		const batchStatus = convertGoogleBatchStatus(operation);
		if (batchStatus.status === "pending") throw new InvalidArgumentError({
			argument: "batchId",
			message: `Google batch "${options.batchId}" is not complete.`
		});
		const inlinedResponses = operation.metadata?.output?.inlinedResponses?.inlinedResponses ?? operation.response?.inlinedResponses?.inlinedResponses;
		if (inlinedResponses != null) return convertAsyncIteratorToReadableStream(this.iterateBatchResults(inlinedResponses.map((result) => ({
			key: result.metadata.key,
			response: result.response,
			error: result.error
		}))));
		const responsesFile = operation.metadata?.output?.responsesFile ?? operation.response?.responsesFile;
		if (responsesFile == null) {
			if (batchStatus.status === "completed") throw new InvalidResponseDataError({
				data: operation,
				message: `Google batch "${options.batchId}" completed without batch output.`
			});
			return new ReadableStream({ start(controller) {
				controller.close();
			} });
		}
		const encodedResponsesFile = responsesFile.split("/").map((segment) => encodeURIComponent(segment)).join("/");
		const { value: lines } = await getFromApi({
			url: `${this.getBaseOrigin()}/download/v1beta/${encodedResponsesFile}:download?alt=media`,
			headers: await this.getHeaders(options.headers),
			failedResponseHandler: googleFailedResponseHandler,
			successfulResponseHandler: createJsonLinesResponseHandler(googleBatchResultLineSchema, { maxLineBytes: this.maxLineBytes }),
			abortSignal: options.abortSignal,
			fetch: this.batchConfig.fetch,
			validateUrl: false
		});
		return convertAsyncIteratorToReadableStream(this.iterateBatchResults(lines));
	}
	async retrieveBatch(options) {
		const { value: operation } = await getFromApi({
			url: `${this.batchConfig.baseURL}/${options.batchId}`,
			headers: await this.getHeaders(options.headers),
			failedResponseHandler: googleFailedResponseHandler,
			successfulResponseHandler: createJsonResponseHandler(googleBatchOperationSchema),
			abortSignal: options.abortSignal,
			fetch: this.batchConfig.fetch,
			validateUrl: false
		});
		return operation;
	}
	async *iterateBatchResults(results) {
		for await (const line of results) {
			if (line.error != null) {
				const error = convertGoogleRpcError(line.error, "Google batch request failed.");
				const status = line.error.status === "CANCELLED" || String(line.error.code) === "1" ? "cancelled" : "failed";
				yield {
					type: "text",
					id: line.key,
					status,
					error
				};
				continue;
			}
			if (line.response == null) {
				yield {
					type: "text",
					id: line.key,
					status: "failed",
					error: {
						message: "Google returned a batch result without a response or error.",
						code: "invalid_batch_result"
					}
				};
				continue;
			}
			const preview = await safeValidateTypes({
				value: line.response,
				schema: googleBatchResponsePreviewSchema
			});
			if (preview.success && (preview.value.candidates == null || preview.value.candidates.length === 0)) {
				const promptFeedback = preview.value.promptFeedback ?? void 0;
				const blockReason = promptFeedback?.blockReason ?? void 0;
				yield {
					type: "text",
					id: line.key,
					status: "failed",
					error: {
						message: blockReason == null ? "Google returned a batch response without any candidates." : `Google blocked the batch request (${blockReason}).`,
						code: blockReason == null ? "invalid_response" : "prompt_blocked",
						...blockReason != null ? { type: blockReason } : {}
					},
					...promptFeedback != null ? { providerMetadata: { google: { promptFeedback: { blockReason: promptFeedback.blockReason ?? null } } } } : {}
				};
				continue;
			}
			const response = await safeValidateTypes({
				value: line.response,
				schema: responseSchema
			});
			if (!response.success) {
				yield {
					type: "text",
					id: line.key,
					status: "failed",
					error: {
						message: "Google returned an invalid GenerateContent batch result.",
						code: "invalid_response"
					}
				};
				continue;
			}
			const result = GoogleLanguageModel.convertGenerateContentResponse({
				config: this.batchConfig,
				response: response.value,
				warnings: [],
				providerOptionsNames: ["google"]
			});
			const imageResult = convertGoogleImageBatchResult(result);
			if (imageResult != null) {
				yield {
					type: "image",
					id: line.key,
					status: "succeeded",
					result: imageResult
				};
				continue;
			}
			const unsupportedPart = result.content.find((part) => !supportedGoogleBatchContentTypes.has(part.type));
			if (unsupportedPart != null) {
				yield {
					type: "text",
					id: line.key,
					status: "failed",
					error: {
						message: `Google returned a "${unsupportedPart.type}" content block, but that content is not supported in AI SDK text batches.`,
						code: "unsupported_content"
					}
				};
				continue;
			}
			yield {
				type: "text",
				id: line.key,
				status: "succeeded",
				result
			};
		}
	}
	async prepareImageRequest(request) {
		const { prompt, n, size, aspectRatio, seed, files, mask, providerOptions } = request.options;
		const warnings = [];
		if (mask != null) throw new UnsupportedFunctionalityError({ functionality: "mask-based image editing in Google batches" });
		if (n > 1) throw new UnsupportedFunctionalityError({ functionality: "multiple images per Google batch request" });
		if (size != null) warnings.push({
			type: "unsupported",
			feature: "size",
			details: "This model does not support the `size` option. Use `aspectRatio` instead."
		});
		const userContent = [];
		if (prompt != null) userContent.push({
			type: "text",
			text: prompt
		});
		for (const file of files ?? []) userContent.push(file.type === "url" ? {
			type: "file",
			data: {
				type: "url",
				url: new URL(file.url)
			},
			mediaType: "image/*"
		} : {
			type: "file",
			data: {
				type: "data",
				data: file.data
			},
			mediaType: file.mediaType
		});
		const googleImageOptions = await parseProviderOptions({
			provider: "google",
			providerOptions,
			schema: googleImageModelOptionsSchema
		});
		const { responseModalities: _responseModalities, imageConfig: userImageConfig, ...passthroughGoogleOptions } = await parseProviderOptions({
			provider: "google",
			providerOptions,
			schema: googleLanguageModelOptions
		}) ?? {};
		const preparedGoogleOptions = await parseProviderOptions({
			provider: "google",
			providerOptions: { google: {
				...passthroughGoogleOptions,
				responseModalities: ["IMAGE"],
				imageConfig: aspectRatio != null || userImageConfig != null ? {
					...userImageConfig,
					...aspectRatio != null ? { aspectRatio } : {}
				} : void 0
			} },
			schema: googleLanguageModelOptions
		});
		const prepared = await GoogleLanguageModel.prepareRequest({
			modelId: request.modelId,
			config: this.batchConfig,
			options: {
				prompt: [{
					role: "user",
					content: userContent
				}],
				seed,
				providerOptions: { google: preparedGoogleOptions ?? { responseModalities: ["IMAGE"] } },
				tools: googleImageOptions?.googleSearch != null ? [{
					type: "provider",
					id: "google.google_search",
					name: "google_search",
					args: googleImageOptions.googleSearch
				}] : void 0
			}
		});
		return {
			...prepared,
			warnings: [...warnings, ...prepared.warnings]
		};
	}
	async getHeaders(headers) {
		return combineHeaders(this.batchConfig.headers ? await resolve(this.batchConfig.headers) : void 0, headers);
	}
	getBaseOrigin() {
		return this.batchConfig.baseURL.replace(/\/v1beta$/, "");
	}
};
function convertGoogleBatchStatus(operation) {
	const rawStatus = operation.metadata?.state ?? void 0;
	const requestCounts = convertGoogleRequestCounts(operation.metadata?.batchStats);
	const createdAt = operation.metadata?.createTime ?? void 0;
	const error = operation.error != null ? convertGoogleRpcError(operation.error, "Google batch failed.") : void 0;
	return {
		status: mapGoogleBatchStatus({
			rawStatus,
			done: operation.done ?? void 0,
			hasError: error != null
		}),
		...rawStatus != null ? { rawStatus } : {},
		...requestCounts != null ? { requestCounts } : {},
		...error != null ? { error } : {},
		...createdAt != null ? { createdAt } : {}
	};
}
function mapGoogleBatchStatus({ rawStatus, done, hasError }) {
	if (hasError) return "failed";
	if (rawStatus == null) return done ? "completed" : "pending";
	switch (rawStatus.replace(/^(?:BATCH|JOB)_STATE_/, "")) {
		case "SUCCEEDED": return "completed";
		case "FAILED":
		case "CANCELLED":
		case "EXPIRED": return "failed";
		default: return "pending";
	}
}
function convertGoogleRequestCounts(counts) {
	const total = parseCount(counts?.requestCount);
	const completed = parseCount(counts?.successfulRequestCount ?? 0);
	const failed = parseCount(counts?.failedRequestCount ?? 0);
	const pending = parseCount(counts?.pendingRequestCount ?? 0);
	return normalizeBatchRequestCounts({
		total,
		pending,
		completed,
		failed
	});
}
function parseCount(value) {
	const count = typeof value === "string" && /^\d+$/.test(value) ? Number(value) : value;
	return typeof count === "number" && Number.isSafeInteger(count) && count >= 0 ? count : void 0;
}
function convertGoogleRpcError(error, fallbackMessage) {
	return {
		message: error.message ?? fallbackMessage,
		...error.status != null ? { type: error.status } : {},
		...error.code != null ? { code: String(error.code) } : {}
	};
}
const googleUploadUrlResponseHandler = async ({ response }) => {
	const uploadUrl = response.headers.get("x-goog-upload-url");
	if (uploadUrl == null) throw new InvalidResponseDataError({
		data: response.headers,
		message: "Google did not return a resumable upload URL."
	});
	return { value: uploadUrl };
};
function getGoogleBatchModelId(requests) {
	const modelId = requests[0]?.modelId;
	if (modelId == null) throw new InvalidArgumentError({
		argument: "requests",
		message: "Google batches require at least one request."
	});
	for (const request of requests) if (request.modelId !== modelId) throw new InvalidArgumentError({
		argument: "requests",
		message: "Google batches require every request to use the same model because the model is part of the batch endpoint."
	});
	return modelId;
}
function convertGoogleImageBatchResult(result) {
	const images = result.content.flatMap((part) => part.type === "file" && part.mediaType.startsWith("image/") && part.data.type === "data" ? [convertToBase64(part.data.data)] : []);
	if (images.length === 0) return void 0;
	const googleMetadata = result.providerMetadata?.google ?? {};
	return {
		images,
		warnings: result.warnings,
		providerMetadata: { google: {
			...googleMetadata,
			images: images.map(() => ({}))
		} },
		response: {
			timestamp: /* @__PURE__ */ new Date(),
			modelId: result.response?.modelId ?? "",
			headers: result.response?.headers
		},
		usage: {
			inputTokens: result.usage.inputTokens.total,
			outputTokens: result.usage.outputTokens.total,
			totalTokens: (result.usage.inputTokens.total ?? 0) + (result.usage.outputTokens.total ?? 0)
		}
	};
}
//#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/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,
	/**
	* 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: createProviderExecutedToolFactory({
		id: "google.google_maps",
		inputSchema: lazySchema(() => zodSchema(z.object({}))),
		outputSchema: lazySchema(() => zodSchema(z.object({})))
	}),
	/**
	* 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/google-image-model.ts
const googleImageModelsWithFileInputSupport = /* @__PURE__ */ new Set([
	"gemini-2.5-flash-image",
	"gemini-3-pro-image-preview",
	"gemini-3.1-flash-image-preview",
	"gemini-nano-banana-2.1"
]);
var GoogleImageModel = class GoogleImageModel {
	static [WORKFLOW_SERIALIZE](model) {
		return serializeModelOptions({
			modelId: model.modelId,
			config: model.config
		});
	}
	static [WORKFLOW_DESERIALIZE](options) {
		return new GoogleImageModel(options.modelId, {}, options.config);
	}
	get maxImagesPerCall() {
		if (this.settings.maxImagesPerCall != null) return this.settings.maxImagesPerCall;
		return 1;
	}
	get supportsFileInputs() {
		return googleImageModelsWithFileInputSupport.has(this.modelId) ? true : void 0;
	}
	get supportsMaskInputs() {
		return this.supportsFileInputs === true ? false : void 0;
	}
	get provider() {
		return this.config.provider;
	}
	constructor(modelId, settings, config) {
		this.modelId = modelId;
		this.settings = settings;
		this.config = config;
		this.specificationVersion = "v4";
	}
	async doGenerate(options) {
		if (!this.modelId.startsWith("gemini-")) throw new Error("Google image models other than Gemini are no longer supported. Use a model ID that starts with `gemini-`.");
		const { prompt, size, aspectRatio, seed, providerOptions, headers, abortSignal, files, mask } = options;
		const warnings = [];
		if (mask != null) throw new Error("Gemini image models do not support mask-based image editing.");
		if (size != null) warnings.push({
			type: "unsupported",
			feature: "size",
			details: "This model does not support the `size` option. Use `aspectRatio` instead."
		});
		const userContent = [];
		if (prompt != null) userContent.push({
			type: "text",
			text: prompt
		});
		if (files != null && files.length > 0) for (const file of files) if (file.type === "url") userContent.push({
			type: "file",
			data: {
				type: "url",
				url: new URL(file.url)
			},
			mediaType: "image/*"
		});
		else userContent.push({
			type: "file",
			data: {
				type: "data",
				data: typeof file.data === "string" ? file.data : new Uint8Array(file.data)
			},
			mediaType: file.mediaType
		});
		const languageModelPrompt = [{
			role: "user",
			content: userContent
		}];
		const googleImageOptions = await parseProviderOptions({
			provider: "google",
			providerOptions,
			schema: googleImageModelOptionsSchema
		});
		const { googleSearch: _strippedGoogleSearch, responseModalities: _strippedResponseModalities, imageConfig: userImageConfig, ...passthroughGoogleOptions } = providerOptions?.google ?? {};
		const result = await new GoogleLanguageModel(this.modelId, {
			provider: this.config.provider,
			baseURL: this.config.baseURL,
			headers: this.config.headers ?? {},
			fetch: this.config.fetch,
			generateId: this.config.generateId ?? generateId
		}).doGenerate({
			prompt: languageModelPrompt,
			seed,
			providerOptions: { google: {
				...passthroughGoogleOptions,
				responseModalities: ["IMAGE"],
				imageConfig: aspectRatio != null || userImageConfig != null ? {
					...userImageConfig,
					...aspectRatio != null ? { aspectRatio } : {}
				} : void 0
			} },
			tools: googleImageOptions?.googleSearch != null ? [{
				type: "provider",
				id: "google.google_search",
				name: "google_search",
				args: googleImageOptions.googleSearch
			}] : void 0,
			headers,
			abortSignal
		});
		const currentDate = this.config._internal?.currentDate?.() ?? /* @__PURE__ */ new Date();
		const images = [];
		for (const part of result.content) if (part.type === "file" && part.mediaType.startsWith("image/") && part.data.type === "data") images.push(convertToBase64(part.data.data));
		const languageModelGoogleMetadata = result.providerMetadata?.google ?? {};
		return {
			images,
			...result.finishReason.unified === "content-filter" ? { isRetryable: false } : {},
			warnings,
			providerMetadata: { google: {
				...languageModelGoogleMetadata,
				finishReason: result.finishReason.raw ?? null,
				images: images.map(() => ({}))
			} },
			response: {
				timestamp: currentDate,
				modelId: this.modelId,
				headers: result.response?.headers
			},
			usage: result.usage ? {
				inputTokens: result.usage.inputTokens.total,
				outputTokens: result.usage.outputTokens.total,
				totalTokens: (result.usage.inputTokens.total ?? 0) + (result.usage.outputTokens.total ?? 0)
			} : void 0
		};
	}
};
//#endregion
//#region src/google-files.ts
var GoogleFiles = class {
	get provider() {
		return this.config.provider;
	}
	constructor(config) {
		this.config = config;
		this.specificationVersion = "v4";
	}
	async uploadFile(options) {
		const googleOptions = await parseProviderOptions({
			provider: "google",
			providerOptions: options.providerOptions,
			schema: googleFilesUploadOptionsSchema
		});
		const getHeaders = async () => combineHeaders(await resolve(this.config.headers), options.headers);
		const fetchFn = this.config.fetch ?? globalThis.fetch;
		const warnings = [];
		if (options.filename != null) warnings.push({
			type: "unsupported",
			feature: "filename"
		});
		const fileBytes = convertInlineFileDataToUint8Array(options.data);
		const mediaType = options.mediaType;
		const displayName = googleOptions?.displayName;
		const initResponse = await fetchFn(`${this.config.baseURL.replace(/\/v1beta$/, "")}/upload/v1beta/files`, {
			method: "POST",
			headers: {
				...await getHeaders(),
				"X-Goog-Upload-Protocol": "resumable",
				"X-Goog-Upload-Command": "start",
				"X-Goog-Upload-Header-Content-Length": String(fileBytes.length),
				"X-Goog-Upload-Header-Content-Type": mediaType,
				"Content-Type": "application/json"
			},
			body: JSON.stringify({ file: { ...displayName != null ? { display_name: displayName } : {} } }),
			signal: options.abortSignal
		});
		if (!initResponse.ok) {
			const errorBody = await initResponse.text();
			throw new AISDKError({
				name: "GOOGLE_FILES_UPLOAD_ERROR",
				message: `Failed to initiate resumable upload: ${initResponse.status} ${errorBody}`
			});
		}
		const uploadUrl = initResponse.headers.get("x-goog-upload-url");
		if (!uploadUrl) throw new AISDKError({
			name: "GOOGLE_FILES_UPLOAD_ERROR",
			message: "No upload URL returned from initiation request"
		});
		const uploadResponse = await fetchFn(uploadUrl, {
			method: "POST",
			headers: {
				"X-Goog-Upload-Offset": "0",
				"X-Goog-Upload-Command": "upload, finalize"
			},
			body: ensureArrayBufferBacked(fileBytes),
			signal: options.abortSignal
		});
		if (!uploadResponse.ok) {
			const errorBody = await uploadResponse.text();
			throw new AISDKError({
				name: "GOOGLE_FILES_UPLOAD_ERROR",
				message: `Failed to upload file data: ${uploadResponse.status} ${errorBody}`
			});
		}
		let file = (await uploadResponse.json()).file;
		const pollIntervalMs = googleOptions?.pollIntervalMs ?? 2e3;
		const pollTimeoutMs = googleOptions?.pollTimeoutMs ?? 3e5;
		const startTime = Date.now();
		while (file.state === "PROCESSING") {
			if (Date.now() - startTime > pollTimeoutMs) throw new AISDKError({
				name: "GOOGLE_FILES_UPLOAD_TIMEOUT",
				message: `File processing timed out after ${pollTimeoutMs}ms`
			});
			await delay(pollIntervalMs, { abortSignal: options.abortSignal });
			const fileNameMatch = /^files\/([^/]+)$/.exec(file.name);
			const filePath = fileNameMatch != null ? `files/${encodePathSegment(fileNameMatch[1])}` : encodePathSegment(file.name);
			const { value: fileStatus } = await getFromApi({
				url: `${this.config.baseURL}/${filePath}`,
				validateUrl: false,
				headers: await getHeaders(),
				successfulResponseHandler: createJsonResponseHandler(googleFileResponseSchema),
				failedResponseHandler: googleFailedResponseHandler,
				abortSignal: options.abortSignal,
				fetch: this.config.fetch
			});
			file = fileStatus;
		}
		if (file.state === "FAILED") throw new AISDKError({
			name: "GOOGLE_FILES_UPLOAD_FAILED",
			message: `File processing failed for ${file.name}`
		});
		return {
			warnings,
			providerReference: { google: file.uri },
			mediaType: file.mimeType ?? options.mediaType,
			providerMetadata: { google: {
				name: file.name,
				displayName: file.displayName,
				mimeType: file.mimeType,
				sizeBytes: file.sizeBytes,
				state: file.state,
				uri: file.uri,
				...file.createTime != null ? { createTime: file.createTime } : {},
				...file.updateTime != null ? { updateTime: file.updateTime } : {},
				...file.expirationTime != null ? { expirationTime: file.expirationTime } : {},
				...file.sha256Hash != null ? { sha256Hash: file.sha256Hash } : {}
			} }
		};
	}
};
function ensureArrayBufferBacked(data) {
	if (data.buffer instanceof ArrayBuffer) return data;
	return new Uint8Array(data);
}
const googleFileResponseSchema = lazySchema(() => zodSchema(z.object({
	name: z.string(),
	displayName: z.string().nullish(),
	mimeType: z.string(),
	sizeBytes: z.string().nullish(),
	createTime: z.string().nullish(),
	updateTime: z.string().nullish(),
	expirationTime: z.string().nullish(),
	sha256Hash: z.string().nullish(),
	uri: z.string(),
	state: z.string()
})));
const googleFilesUploadOptionsSchema = lazySchema(() => zodSchema(z.looseObject({
	displayName: z.string().nullish(),
	pollIntervalMs: z.number().positive().nullish(),
	pollTimeoutMs: z.number().positive().nullish()
})));
//#endregion
//#region src/google-video-model-options.ts
const googleVideoModelOptionsSchema = lazySchema(() => zodSchema(z.looseObject({
	pollIntervalMs: z.number().positive().nullish(),
	pollTimeoutMs: z.number().positive().nullish(),
	personGeneration: z.enum([
		"dont_allow",
		"allow_adult",
		"allow_all"
	]).nullish(),
	negativePrompt: z.string().nullish(),
	referenceImages: z.array(z.object({
		bytesBase64Encoded: z.string().nullish(),
		gcsUri: z.string().nullish()
	})).nullish()
})));
//#endregion
//#region src/google-video-model.ts
function getFirstFrameImage(options) {
	return options.frameImages?.find((frame) => frame.frameType === "first_frame")?.image;
}
function resolveStartImage(options) {
	return getFirstFrameImage(options) ?? options.image;
}
function getLastFrameImage(options) {
	return options.frameImages?.find((frame) => frame.frameType === "last_frame")?.image;
}
function getInputReferences(options) {
	if (options.frameImages != null && options.frameImages.length > 0) return;
	return options.inputReferences != null && options.inputReferences.length > 0 ? options.inputReferences : void 0;
}
function convertFileToGoogleImage(file, warnings) {
	if (file.type === "url") {
		if (file.url.startsWith("gs://")) return {
			gcsUri: file.url,
			mimeType: "image/png"
		};
		warnings.push({
			type: "unsupported",
			feature: "URL-based image input",
			details: "Google Generative AI video models require base64-encoded images or GCS URIs. URL will be ignored."
		});
		return;
	}
	return {
		bytesBase64Encoded: typeof file.data === "string" ? file.data : convertUint8ArrayToBase64(file.data),
		mimeType: file.mediaType || "image/png"
	};
}
function convertProviderReferenceImage(refImg) {
	if (refImg.bytesBase64Encoded) return {
		image: {
			bytesBase64Encoded: refImg.bytesBase64Encoded,
			mimeType: "image/png"
		},
		referenceType: "asset"
	};
	if (refImg.gcsUri) return {
		image: {
			gcsUri: refImg.gcsUri,
			mimeType: "image/png"
		},
		referenceType: "asset"
	};
	return refImg;
}
function convertInputReferenceImage(file, warnings) {
	const image = convertFileToGoogleImage(file, warnings);
	return image != null ? {
		image,
		referenceType: "asset"
	} : void 0;
}
var GoogleVideoModel = class {
	get provider() {
		return this.config.provider;
	}
	get maxVideosPerCall() {
		return 4;
	}
	constructor(modelId, config) {
		this.modelId = modelId;
		this.config = config;
		this.specificationVersion = "v4";
	}
	async buildRequest(options) {
		const warnings = [];
		const googleOptions = await parseProviderOptions({
			provider: "google",
			providerOptions: options.providerOptions,
			schema: googleVideoModelOptionsSchema
		});
		const instances = [{}];
		const instance = instances[0];
		if (options.prompt != null) instance.prompt = options.prompt;
		const startImage = resolveStartImage(options);
		if (startImage != null) {
			const image = convertFileToGoogleImage(startImage, warnings);
			if (image != null) instance.image = image;
		}
		const lastFrameImage = getLastFrameImage(options);
		if (lastFrameImage != null) {
			const lastFrame = convertFileToGoogleImage(lastFrameImage, warnings);
			if (lastFrame != null) instance.lastFrame = lastFrame;
		}
		const inputReferences = getInputReferences(options);
		if (inputReferences != null) instance.referenceImages = inputReferences.flatMap((reference) => {
			const converted = convertInputReferenceImage(reference, warnings);
			return converted != null ? [converted] : [];
		});
		else if (googleOptions?.referenceImages != null) instance.referenceImages = googleOptions.referenceImages.map((refImg) => convertProviderReferenceImage(refImg));
		const parameters = { sampleCount: options.n };
		if (options.aspectRatio) parameters.aspectRatio = options.aspectRatio;
		if (options.resolution) parameters.resolution = {
			"1280x720": "720p",
			"1920x1080": "1080p",
			"3840x2160": "4k"
		}[options.resolution] || options.resolution;
		if (options.duration) parameters.durationSeconds = options.duration;
		if (options.seed != null) parameters.seed = options.seed;
		if (googleOptions != null) {
			const opts = googleOptions;
			if (opts.personGeneration !== void 0 && opts.personGeneration !== null) parameters.personGeneration = opts.personGeneration;
			if (opts.negativePrompt !== void 0 && opts.negativePrompt !== null) parameters.negativePrompt = opts.negativePrompt;
			for (const [key, value] of Object.entries(opts)) if (![
				"pollIntervalMs",
				"pollTimeoutMs",
				"personGeneration",
				"negativePrompt",
				"referenceImages"
			].includes(key)) parameters[key] = value;
		}
		return {
			instances,
			parameters,
			warnings,
			googleOptions
		};
	}
	async buildCompletedResult(finalOperation, responseHeaders, warnings, currentDate) {
		const response = finalOperation.response;
		if (!response?.generateVideoResponse?.generatedSamples || response.generateVideoResponse.generatedSamples.length === 0) throw new AISDKError({
			name: "GOOGLE_VIDEO_GENERATION_ERROR",
			message: `No videos in response. Response: ${JSON.stringify(finalOperation)}`
		});
		const videos = [];
		const videoMetadata = [];
		const apiKey = (await resolve(this.config.headers))?.["x-goog-api-key"];
		for (const generatedSample of response.generateVideoResponse.generatedSamples) if (generatedSample.video?.uri) {
			const urlWithAuth = apiKey && isSameOrigin(generatedSample.video.uri, this.config.baseURL) ? `${generatedSample.video.uri}${generatedSample.video.uri.includes("?") ? "&" : "?"}key=${apiKey}` : generatedSample.video.uri;
			videos.push({
				type: "url",
				url: urlWithAuth,
				mediaType: "video/mp4"
			});
			videoMetadata.push({ uri: generatedSample.video.uri });
		}
		if (videos.length === 0) throw new AISDKError({
			name: "GOOGLE_VIDEO_GENERATION_ERROR",
			message: "No valid videos in response"
		});
		return {
			status: "completed",
			videos,
			warnings,
			response: {
				timestamp: currentDate,
				modelId: this.modelId,
				headers: responseHeaders
			},
			providerMetadata: { google: { videos: videoMetadata } }
		};
	}
	async doStart(options) {
		const currentDate = this.config._internal?.currentDate?.() ?? /* @__PURE__ */ new Date();
		const { instances, parameters, warnings } = await this.buildRequest(options);
		const { value: operation, responseHeaders } = await postJsonToApi({
			url: `${this.config.baseURL}/models/${this.modelId}:predictLongRunning`,
			headers: combineHeaders(await resolve(this.config.headers), options.headers),
			body: {
				instances,
				parameters
			},
			successfulResponseHandler: createJsonResponseHandler(googleOperationSchema),
			failedResponseHandler: googleFailedResponseHandler,
			abortSignal: options.abortSignal,
			fetch: this.config.fetch
		});
		const operationName = operation.name;
		if (!operationName) throw new AISDKError({
			name: "GOOGLE_VIDEO_GENERATION_ERROR",
			message: "No operation name returned from API"
		});
		return {
			operation: { operationName },
			warnings,
			response: {
				timestamp: currentDate,
				modelId: this.modelId,
				headers: responseHeaders
			}
		};
	}
	async doStatus(options) {
		const currentDate = this.config._internal?.currentDate?.() ?? /* @__PURE__ */ new Date();
		const { operationName } = options.operation;
		const { value: statusOperation, responseHeaders } = await getFromApi({
			url: `${this.config.baseURL}/${operationName}`,
			validateUrl: false,
			headers: combineHeaders(await resolve(this.config.headers), options.headers),
			successfulResponseHandler: createJsonResponseHandler(googleOperationSchema),
			failedResponseHandler: googleFailedResponseHandler,
			abortSignal: options.abortSignal,
			fetch: this.config.fetch
		});
		if (!statusOperation.done) return {
			status: "pending",
			response: {
				timestamp: currentDate,
				modelId: this.modelId,
				headers: responseHeaders
			}
		};
		if (statusOperation.error) return {
			status: "error",
			error: `Video generation failed: ${statusOperation.error.message}`,
			response: {
				timestamp: currentDate,
				modelId: this.modelId,
				headers: responseHeaders
			}
		};
		return this.buildCompletedResult(statusOperation, responseHeaders, [], currentDate);
	}
};
const googleOperationSchema = lazySchema(() => zodSchema(z.object({
	name: z.string().nullish(),
	done: z.boolean().nullish(),
	error: z.object({
		code: z.number().nullish(),
		message: z.string(),
		status: z.string().nullish()
	}).nullish(),
	response: z.object({ generateVideoResponse: z.object({ generatedSamples: z.array(z.object({ video: z.object({ uri: z.string().nullish() }).nullish() })).nullish() }).nullish() }).nullish()
})));
//#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/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
//#region src/get-realtime-base-url.ts
/**
* Returns the base URL for Live API (WebSocket) endpoints, stripping a
* trailing `/v1beta` or `/v1alpha` API version segment: Live API paths carry
* their own version and are not nested under it.
*/
function getRealtimeBaseURL(baseURL) {
	const url = new URL(baseURL);
	const pathSegments = url.pathname.split("/");
	const version = pathSegments.at(-1);
	if (version === "v1beta" || version === "v1alpha") {
		pathSegments.pop();
		url.pathname = pathSegments.join("/") || "/";
	}
	return url;
}
/**
* Builds a `wss://` Live API WebSocket URL for the given bidi service path
* (e.g. `google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent`).
*/
function getRealtimeWebSocketURL(baseURL, webSocketPath) {
	const url = getRealtimeBaseURL(baseURL);
	url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
	url.pathname = `${url.pathname.replace(/\/$/, "")}/ws/${webSocketPath}`;
	return url;
}
//#endregion
//#region src/realtime/google-realtime-event-mapper.ts
/**
* Stateful event mapper for Google's Gemini Live API.
*
* Unlike OpenAI/xAI, Google's events don't have response/item IDs and
* a single message can contain multiple pieces of data. This class
* tracks turn state to generate consistent synthetic IDs.
*/
var GoogleRealtimeEventMapper = class {
	constructor() {
		this.turnCounter = 0;
		this.inputTranscriptionCounter = 0;
		this.inputTranscriptionBuffer = "";
		this.inputTranscriptionBoundary = "none";
		this.preserveInputTranscriptionAcrossTurnComplete = false;
		this.hasAudio = false;
		this.hasText = false;
		this.hasTranscript = false;
		this.turnClosed = false;
		this.inputAudioRate = 16e3;
	}
	get responseId() {
		return `google-resp-${this.turnCounter}`;
	}
	get itemId() {
		return `google-item-${this.turnCounter}`;
	}
	/**
	* Rolls over to the next turn lazily, once model content for the next turn
	* arrives. `turnComplete` merely marks the current turn closed.
	* This keeps an output transcript that arrives shortly after `turnComplete`
	* attached to the turn it belongs to, since Google delivers transcription
	* independently with no guaranteed ordering relative to `turnComplete`.
	*/
	beginTurnIfClosed() {
		if (!this.turnClosed) return;
		this.turnCounter++;
		this.hasAudio = false;
		this.hasText = false;
		this.hasTranscript = false;
		this.turnClosed = false;
		this.preserveInputTranscriptionAcrossTurnComplete = false;
	}
	accumulateInputTranscription({ text, finished }) {
		if (!text) {
			if (finished === true) this.finishInputTranscription();
			return;
		}
		const hasFinishedSignal = finished != null;
		if (this.inputTranscriptionBuffer !== "" && (this.inputTranscriptionBoundary === "interrupted" || this.inputTranscriptionBoundary === "turn-complete" && !hasFinishedSignal)) this.finishInputTranscription();
		this.inputTranscriptionBoundary = "none";
		this.inputTranscriptionBuffer += text;
		const result = {
			itemId: `google-input-${this.inputTranscriptionCounter}`,
			transcript: this.inputTranscriptionBuffer
		};
		if (finished === true) this.finishInputTranscription();
		return result;
	}
	finishInputTranscription() {
		if (this.inputTranscriptionBuffer === "") return;
		this.inputTranscriptionCounter++;
		this.inputTranscriptionBuffer = "";
	}
	parseServerEvent(raw) {
		const events = this.parseEvent(raw);
		const usage = mapGoogleUsage(raw.usageMetadata);
		if (usage == null) return events;
		const list = Array.isArray(events) ? events : [events];
		if (list.some((event) => event.type === "response-done")) {
			const mapped = list.map((event) => event.type === "response-done" ? {
				...event,
				usage
			} : event);
			return Array.isArray(events) ? mapped : mapped[0];
		}
		const update = {
			type: "custom",
			rawType: "usageMetadata",
			usage,
			raw
		};
		if (list.length === 1 && list[0].type === "custom" && list[0].rawType === "usageMetadata") return update;
		return [...list, update];
	}
	parseEvent(raw) {
		const data = raw;
		if (data.setupComplete != null) return {
			type: "session-created",
			raw
		};
		if (data.toolCall != null) {
			this.beginTurnIfClosed();
			return (data.toolCall.functionCalls ?? []).flatMap((functionCall) => {
				const args = JSON.stringify(functionCall.args ?? {});
				return [{
					type: "function-call-arguments-delta",
					responseId: this.responseId,
					itemId: this.itemId,
					callId: functionCall.id,
					delta: args,
					raw
				}, {
					type: "function-call-arguments-done",
					responseId: this.responseId,
					itemId: this.itemId,
					callId: functionCall.id,
					name: functionCall.name,
					arguments: args,
					raw
				}];
			});
		}
		if (data.toolCallCancellation != null) return {
			type: "custom",
			rawType: "toolCallCancellation",
			raw
		};
		if (data.goAway != null) return {
			type: "custom",
			rawType: "goAway",
			raw
		};
		if (data.sessionResumptionUpdate != null) return {
			type: "custom",
			rawType: "sessionResumptionUpdate",
			raw
		};
		if (data.serverContent != null) return this.parseServerContent(data.serverContent, raw);
		if (data.inputTranscription != null) {
			const transcription = this.accumulateInputTranscription(data.inputTranscription);
			if (transcription != null) return {
				type: "input-transcription-completed",
				...transcription,
				raw
			};
		}
		return {
			type: "custom",
			rawType: String(Object.keys(data)[0]),
			raw
		};
	}
	parseServerContent(serverContent, raw) {
		const events = [];
		if (serverContent.interrupted) {
			this.inputTranscriptionBoundary = "interrupted";
			this.preserveInputTranscriptionAcrossTurnComplete = true;
			this.turnClosed = true;
			events.push({
				type: "speech-started",
				raw
			});
		}
		if (serverContent.modelTurn?.parts) {
			this.beginTurnIfClosed();
			for (const part of serverContent.modelTurn.parts) {
				if (part.inlineData?.data) {
					this.hasAudio = true;
					events.push({
						type: "audio-delta",
						responseId: this.responseId,
						itemId: this.itemId,
						delta: part.inlineData.data,
						raw
					});
				}
				if (part.text) {
					this.hasText = true;
					events.push({
						type: "text-delta",
						responseId: this.responseId,
						itemId: this.itemId,
						delta: part.text,
						raw
					});
				}
			}
		}
		if (serverContent.outputTranscription?.text) {
			this.hasTranscript = true;
			events.push({
				type: "audio-transcript-delta",
				responseId: this.responseId,
				itemId: this.itemId,
				delta: serverContent.outputTranscription.text,
				raw
			});
		}
		if (serverContent.inputTranscription != null) {
			const transcription = this.accumulateInputTranscription(serverContent.inputTranscription);
			if (transcription != null) events.push({
				type: "input-transcription-completed",
				...transcription,
				raw
			});
		}
		if (serverContent.generationComplete) events.push({
			type: "custom",
			rawType: "generationComplete",
			raw
		});
		if (serverContent.interactionStatus != null) events.push({
			type: "custom",
			rawType: "interactionStatus",
			raw
		});
		if (serverContent.waitingForInput) events.push({
			type: "custom",
			rawType: "waitingForInput",
			raw
		});
		if (serverContent.turnComplete) {
			if (this.hasAudio) events.push({
				type: "audio-done",
				responseId: this.responseId,
				itemId: this.itemId,
				raw
			});
			if (this.hasText) events.push({
				type: "text-done",
				responseId: this.responseId,
				itemId: this.itemId,
				raw
			});
			if (this.hasTranscript) events.push({
				type: "audio-transcript-done",
				responseId: this.responseId,
				itemId: this.itemId,
				raw
			});
			events.push({
				type: "response-done",
				responseId: this.responseId,
				status: "completed",
				raw
			});
			this.turnClosed = true;
			if (this.preserveInputTranscriptionAcrossTurnComplete) this.preserveInputTranscriptionAcrossTurnComplete = false;
			else if (this.inputTranscriptionBoundary !== "interrupted") this.inputTranscriptionBoundary = "turn-complete";
		}
		if (events.length === 0) return {
			type: "custom",
			rawType: "serverContent",
			raw
		};
		return events.length === 1 ? events[0] : events;
	}
	serializeClientEvent(event, modelId) {
		switch (event.type) {
			case "session-update":
				if (event.config.inputAudioFormat?.rate != null) this.inputAudioRate = event.config.inputAudioFormat.rate;
				return { setup: buildGoogleSessionConfig(event.config, modelId) };
			case "input-audio-append": return { realtimeInput: { audio: {
				data: event.audio,
				mimeType: `audio/pcm;rate=${this.inputAudioRate}`
			} } };
			case "input-audio-commit": return { realtimeInput: { audioStreamEnd: true } };
			case "input-audio-clear":
			case "response-create":
			case "response-cancel":
			case "conversation-item-truncate": return null;
			case "conversation-item-create": {
				const item = event.item;
				switch (item.type) {
					case "text-message": return { realtimeInput: { text: item.text } };
					case "function-call-output": return serializeFunctionCallOutput(item);
					case "audio-message": return null;
				}
				break;
			}
		}
		return null;
	}
};
/**
* Maps Gemini Live `usageMetadata` to normalized usage. Google reports usage
* per modality in `promptTokensDetails` / `responseTokensDetails`.
*
* Field names are Live-API-specific: the Live `UsageMetadata` uses
* `responseTokenCount` / `responseTokensDetails` for output, NOT the
* `candidatesTokenCount` / `candidatesTokensDetails` of the regular
* `generateContent` response (see google-language-model.ts). Do not "align"
* these to `candidates*` — they are different wire shapes.
*
* Gross totals preserve provider counts even when modality details are absent
* or contain modalities without a dedicated normalized bucket. Never guess a
* text/audio split from those totals.
*/
function mapGoogleUsage(usageMetadata) {
	if (usageMetadata == null) return void 0;
	const input = sumByModality(usageMetadata.promptTokensDetails);
	const output = sumByModality(usageMetadata.responseTokensDetails);
	return compactUsage({
		inputTokens: usageMetadata.promptTokenCount != null ? usageMetadata.promptTokenCount + (usageMetadata.toolUsePromptTokenCount ?? 0) : void 0,
		outputTokens: usageMetadata.responseTokenCount != null ? usageMetadata.responseTokenCount + (usageMetadata.thoughtsTokenCount ?? 0) : void 0,
		cachedInputTokens: usageMetadata.cachedContentTokenCount,
		inputTextTokens: input.text,
		inputAudioTokens: input.audio,
		outputTextTokens: output.text,
		outputAudioTokens: output.audio
	});
}
/** Aggregate Gemini per-modality token details into text/audio buckets. */
function sumByModality(details) {
	if (details == null) return {};
	const totals = {};
	for (const detail of details) {
		const count = detail.tokenCount;
		if (count == null) continue;
		const modality = detail.modality?.toUpperCase();
		if (modality === "AUDIO") totals.audio = (totals.audio ?? 0) + count;
		else if (modality === "TEXT") totals.text = (totals.text ?? 0) + count;
	}
	return totals;
}
/** Drop `undefined` fields; return `undefined` when nothing is observable. */
function compactUsage(usage) {
	const result = {};
	for (const [key, value] of Object.entries(usage)) if (value != null) result[key] = value;
	return Object.keys(result).length > 0 ? result : void 0;
}
/**
* The value Gemini accepts for `functionResponse.response`.
*
* The field is typed `google.protobuf.Struct`, which is an object and nothing else, so
* a string, number, array or `null` on the wire is a protocol violation and the socket
* closes with 1007. `onToolCall` returns `unknown` and `addToolOutput` takes `unknown`,
* so those shapes reach here as perfectly valid JSON.
*
* The field's own docstring says what to do with one: "Use `output` key to specify
* function output ... If `output` and `error` keys are not specified, then whole
* `response` is treated as function output." An object is passed through unchanged and
* everything else is wrapped.
*/
function toFunctionResponseStruct(value) {
	return typeof value === "object" && value !== null && !Array.isArray(value) ? value : { output: value };
}
async function serializeFunctionCallOutput(item) {
	const parseResult = await safeParseJSON({ text: item.output });
	const response = parseResult.success ? toFunctionResponseStruct(parseResult.value) : { output: item.output };
	return { toolResponse: { functionResponses: [{
		id: item.callId,
		name: item.name,
		response
	}] } };
}
/**
* Live models that reason in the background (e.g. `gemini-3.8-live-extended-thinking`).
* Google requires exactly one of `thinkingLevel` / `thinkingBudget` in their setup and
* rejects `thinkingConfig` on every other Live model.
*/
function isThinkingLiveModel(modelId) {
	const modelName = modelId.split("/").at(-1)?.toLowerCase() ?? "";
	return /^gemini-\d+\.\d+-live\b.*thinking/.test(modelName);
}
/**
* Builds a Google-specific session configuration from a normalized config.
* Used to construct the `bidiGenerateContentSetup` payload for auth token creation.
*/
function buildGoogleSessionConfig(config, modelId) {
	const setup = { model: getModelPath(modelId) };
	const { google, ...restProviderOptions } = config?.providerOptions ?? {};
	const googleOptions = isRecord(google) ? google : void 0;
	const generationConfig = {};
	if (config?.outputModalities != null) generationConfig.responseModalities = config.outputModalities.map((m) => m.toUpperCase());
	else generationConfig.responseModalities = ["AUDIO"];
	if (config?.voice != null) generationConfig.speechConfig = { voiceConfig: { prebuiltVoiceConfig: { voiceName: config.voice } } };
	setup.generationConfig = generationConfig;
	if (config?.instructions != null) setup.systemInstruction = { parts: [{ text: config.instructions }] };
	if (config?.tools != null && config.tools.length > 0) setup.tools = [{ functionDeclarations: config.tools.map((tool) => ({
		name: tool.name,
		description: tool.description,
		parametersJsonSchema: tool.parameters,
		...googleOptions?.defaultToolBehavior != null ? { behavior: googleOptions.defaultToolBehavior } : {}
	})) }];
	if (config?.inputAudioTranscription != null) setup.inputAudioTranscription = {};
	if (config?.outputAudioTranscription != null) setup.outputAudioTranscription = {};
	const rawGenerationConfig = restProviderOptions.generationConfig;
	const explicitThinkingConfig = googleOptions?.thinkingConfig ?? (isRecord(rawGenerationConfig) && isRecord(rawGenerationConfig.thinkingConfig) ? rawGenerationConfig.thinkingConfig : void 0);
	const thinkingConfig = isThinkingLiveModel(modelId) && explicitThinkingConfig?.thinkingLevel == null && explicitThinkingConfig?.thinkingBudget == null ? {
		...explicitThinkingConfig,
		thinkingLevel: "low"
	} : googleOptions?.thinkingConfig;
	const applyThinkingConfig = () => {
		if (thinkingConfig == null) return;
		const target = isRecord(setup.generationConfig) ? setup.generationConfig : generationConfig;
		setup.generationConfig = {
			...target,
			thinkingConfig
		};
	};
	if (config?.providerOptions == null) {
		applyThinkingConfig();
		return setup;
	}
	Object.assign(setup, restProviderOptions);
	if (googleOptions?.translationConfig != null) setup.generationConfig = {
		...isRecord(setup.generationConfig) ? setup.generationConfig : generationConfig,
		translationConfig: googleOptions.translationConfig
	};
	applyThinkingConfig();
	return setup;
}
//#endregion
//#region src/realtime/google-realtime-model.ts
const realtimeWebSocketPath = "google.ai.generativelanguage.v1alpha.GenerativeService.BidiGenerateContentConstrained";
const realtimeServerWebSocketPath = "google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent";
function getAuthTokensURL(baseURL) {
	const url = getRealtimeBaseURL(baseURL);
	url.pathname = `${url.pathname.replace(/\/$/, "")}/v1alpha/auth_tokens`;
	return url.toString();
}
function getWebSocketURL(baseURL) {
	return getRealtimeWebSocketURL(baseURL, realtimeWebSocketPath).toString();
}
function getServerWebSocketURL(baseURL) {
	const url = getRealtimeBaseURL(baseURL);
	url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
	url.pathname = `${url.pathname.replace(/\/$/, "")}/ws/${realtimeServerWebSocketPath}`;
	return url.toString();
}
var GoogleRealtimeModel = class {
	constructor(modelId, config) {
		this.specificationVersion = "v4";
		this.capabilities = {
			conversation: "turn-based",
			transports: ["websocket"],
			connections: ["client-secret-websocket", "server-websocket"]
		};
		this.mapper = new GoogleRealtimeEventMapper();
		this.modelId = modelId;
		this.provider = config.provider;
		this.config = config;
	}
	async doCreateClientSecret(options) {
		const fetchFn = this.config.fetch ?? fetch;
		const apiKey = (await resolve(this.config.headers))["x-goog-api-key"];
		if (!apiKey) throw new Error("Google Generative AI API key is required for realtime token creation.");
		const now = Date.now();
		const openWindowMs = (options.expiresAfterSeconds ?? 60) * 1e3;
		const newSessionExpireTime = new Date(now + openWindowMs).toISOString();
		const expireTime = new Date(now + openWindowMs + 18e5).toISOString();
		const setupPayload = buildGoogleSessionConfig(options.sessionConfig, this.modelId);
		const response = await fetchFn(`${getAuthTokensURL(this.config.baseURL)}?key=${encodeURIComponent(apiKey)}`, {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify({
				uses: 0,
				expireTime,
				newSessionExpireTime,
				bidiGenerateContentSetup: setupPayload
			})
		});
		if (!response.ok) {
			const text = await response.text();
			throw new Error(`Google realtime auth token request failed: ${response.status} ${text}`);
		}
		const data = await response.json();
		return {
			token: data.name,
			url: getWebSocketURL(this.config.baseURL),
			expiresAt: data.expireTime ? Math.floor(new Date(data.expireTime).getTime() / 1e3) : void 0
		};
	}
	getWebSocketConfig(options) {
		return { url: `${options.url}?access_token=${encodeURIComponent(options.token)}` };
	}
	getServerWebSocketConfig(options) {
		this.assertConversationIntent(options);
		return resolveWith(this.config.headers, (headers) => ({
			url: getServerWebSocketURL(this.config.baseURL),
			headers: removeUndefinedEntries(headers)
		}));
	}
	assertConversationIntent(options) {
		if (options?.intent != null && options.intent !== "conversation") throw new Error(`Google realtime does not support the '${options.intent}' session intent.`);
	}
	parseServerEvent(raw) {
		return this.mapper.parseServerEvent(raw);
	}
	createServerEventParser() {
		const mapper = new GoogleRealtimeEventMapper();
		return (raw) => mapper.parseServerEvent(raw);
	}
	serializeClientEvent(event, options) {
		this.assertConversationIntent(options);
		return this.mapper.serializeClientEvent(event, this.modelId);
	}
	buildSessionConfig(config, options) {
		this.assertConversationIntent(options);
		return buildGoogleSessionConfig(config, this.modelId);
	}
};
//#endregion
//#region src/transcription/google-transcription-model-options.ts
/**
* Speech recognition options shared by unary (`gemini-3.5-transcribe`) and
* live (`gemini-3.5-transcribe-live`) transcription. Maps onto Google's
* `AudioTranscriptionConfig`.
*/
const googleTranscriptionModelOptions = lazySchema(() => zodSchema(z.object({
	/**
	* BCP-47 language codes providing hints about the languages present in the
	* audio. If omitted or empty, defaults to automatic language detection.
	*/
	languageCodes: z.array(z.string()).optional(),
	/**
	* Custom vocabulary phrases, which bias the speech recognition model
	* toward recognizing specific terms.
	*/
	customVocabulary: z.array(z.string()).optional(),
	/**
	* Enables word-level timestamp generation.
	*/
	wordTimestamp: z.boolean().optional(),
	/**
	* Enables speaker diarization.
	*/
	diarization: z.boolean().optional(),
	/**
	* Transcription output formatting mode.
	*
	* - `VERBATIM` (default): exact literal transcript preserving filler
	*   words, repetitions, and false starts.
	* - `SMART`: cleans up and structures the transcript in real time —
	*   disfluency removal, inline self-corrections, structured formatting
	*   (lists, numbers, dates, paragraph breaks), and grammar/casing polish.
	*/
	mode: z.enum(["SMART", "VERBATIM"]).optional()
})));
//#endregion
//#region src/transcription/google-transcription-model.ts
const liveWebSocketPath$1 = "google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent";
/**
* After the input audio has ended, finish when no terminal signal
* (`turnComplete` / idle `interactionStatus`) arrives within this window.
* Trailing transcripts reset the timer.
*/
const defaultFinishGraceMs$1 = 3e3;
function getLiveWebSocketURL$1(baseURL, apiKey) {
	const url = getRealtimeWebSocketURL(baseURL, liveWebSocketPath$1);
	url.searchParams.set("key", apiKey);
	return url;
}
/** Live transcription is only supported by `*-live` model variants. */
function isLiveTranscriptionModelId(modelId) {
	return modelId.includes("-live");
}
var GoogleTranscriptionModel = class GoogleTranscriptionModel {
	static [WORKFLOW_SERIALIZE](model) {
		return serializeModelOptions({
			modelId: model.modelId,
			config: model.config
		});
	}
	static [WORKFLOW_DESERIALIZE](options) {
		return new GoogleTranscriptionModel(options.modelId, options.config);
	}
	get provider() {
		return this.config.provider;
	}
	constructor(modelId, config) {
		this.modelId = modelId;
		this.config = config;
		this.specificationVersion = "v4";
	}
	async parseOptions(providerOptions) {
		return parseProviderOptions({
			provider: "google",
			providerOptions,
			schema: googleTranscriptionModelOptions
		});
	}
	async doGenerate(options) {
		if (isLiveTranscriptionModelId(this.modelId)) throw new InvalidArgumentError({
			argument: "modelId",
			message: `Model '${this.modelId}' only supports streaming transcription. Use experimental_streamTranscribe, or a unary model such as 'gemini-3.5-transcribe'.`
		});
		const currentDate = this.config._internal?.currentDate?.() ?? /* @__PURE__ */ new Date();
		const warnings = [];
		const transcriptionConfig = buildTranscriptionConfig(await this.parseOptions(options.providerOptions));
		const requestBody = {
			model: this.modelId,
			input: [{
				type: "audio",
				data: convertToBase64(options.audio),
				mime_type: options.mediaType
			}],
			...transcriptionConfig != null ? { generation_config: { transcription_config: transcriptionConfig } } : {}
		};
		const { value: response, responseHeaders, rawValue: rawResponse } = await postJsonToApi({
			url: `${this.config.baseURL}/interactions`,
			headers: combineHeaders(this.config.headers ? await resolve(this.config.headers) : void 0, options.headers),
			body: requestBody,
			failedResponseHandler: googleFailedResponseHandler,
			successfulResponseHandler: createJsonResponseHandler(googleInteractionsTranscriptionResponseSchema),
			abortSignal: options.abortSignal,
			fetch: this.config.fetch
		});
		let text = "";
		const segments = [];
		for (const step of response.steps ?? []) for (const content of step.content ?? []) {
			if (content.type !== "text" || content.text == null) continue;
			text += content.text;
			for (const annotation of content.annotations ?? []) {
				if (annotation.type !== "word_info") continue;
				const startSecond = parseOffsetSeconds(annotation.start_offset);
				const endSecond = parseOffsetSeconds(annotation.end_offset);
				if (annotation.text == null || startSecond == null || endSecond == null) continue;
				segments.push({
					text: annotation.text,
					startSecond,
					endSecond
				});
			}
		}
		return {
			text,
			segments,
			language: void 0,
			durationInSeconds: void 0,
			warnings,
			...response.usage != null && { usage: response.usage },
			response: {
				timestamp: currentDate,
				modelId: this.modelId,
				headers: responseHeaders,
				body: rawResponse
			},
			...response.usage != null ? { providerMetadata: { google: { usage: response.usage } } } : {}
		};
	}
	async doStream(options) {
		if (!isLiveTranscriptionModelId(this.modelId)) throw new InvalidArgumentError({
			argument: "modelId",
			message: `Model '${this.modelId}' does not support streaming transcription. Use a live model such as 'gemini-3.5-transcribe-live'.`
		});
		const currentDate = this.config._internal?.currentDate?.() ?? /* @__PURE__ */ new Date();
		const warnings = [];
		const googleOptions = await this.parseOptions(options.providerOptions);
		validateLiveInputAudioFormat(options.inputAudioFormat);
		const headers = combineHeaders(this.config.headers ? await resolve(this.config.headers) : void 0, options.headers);
		let apiKey;
		for (const [key, value] of Object.entries(headers)) if (key.toLowerCase() === "x-goog-api-key" && value != null) apiKey = value;
		if (apiKey == null) throw new Error("Google Generative AI API key is required for streaming transcription.");
		const webSocketHeaders = Object.fromEntries(Object.entries(headers).filter(([key]) => key.toLowerCase() !== "x-goog-api-key"));
		const setup = {
			model: getModelPath(this.modelId),
			inputAudioTranscription: buildAudioTranscriptionConfig(googleOptions) ?? {}
		};
		return {
			request: { body: setup },
			response: {
				timestamp: currentDate,
				modelId: this.modelId
			},
			stream: createGoogleLiveTranscriptionStream({
				webSocket: this.config.webSocket,
				url: getLiveWebSocketURL$1(this.config.baseURL, apiKey),
				headers: webSocketHeaders,
				setup,
				inputAudioRate: options.inputAudioFormat.rate ?? 16e3,
				finishGraceMs: this.config._internal?.finishGraceMs ?? defaultFinishGraceMs$1,
				warnings,
				audio: options.audio,
				abortSignal: options.abortSignal,
				includeRawChunks: options.includeRawChunks
			})
		};
	}
};
function createGoogleLiveTranscriptionStream({ webSocket, url, headers, setup, inputAudioRate, finishGraceMs, warnings, audio, abortSignal, includeRawChunks }) {
	let finished = false;
	let cleanup = () => {};
	return new ReadableStream({
		start: (controller) => {
			let audioReader;
			let connection;
			let resolveSetupComplete;
			const setupComplete = new Promise((resolve) => {
				resolveSetupComplete = resolve;
			});
			let segmentCounter = 0;
			let segmentBuffer = "";
			let fullText = "";
			let latestInterim = "";
			let language;
			let audioEnded = false;
			let usageMetadata;
			let finishTimer;
			const segmentId = () => `google-segment-${segmentCounter}`;
			const cancelPendingFinish = () => {
				if (finishTimer != null) {
					clearTimeout(finishTimer);
					finishTimer = void 0;
				}
			};
			const schedulePendingFinish = () => {
				if (finished || !audioEnded) return;
				cancelPendingFinish();
				finishTimer = setTimeout(() => {
					finishTimer = void 0;
					finish();
				}, finishGraceMs);
			};
			cleanup = (closeCode) => {
				cancelPendingFinish();
				if (audioReader != null) audioReader.cancel().catch(() => {});
				else audio.cancel().catch(() => {});
				connection?.close(closeCode);
			};
			const finishWithError = (error) => {
				if (finished) return;
				finished = true;
				cleanup();
				controller.error(error);
			};
			const completeSegment = () => {
				if (segmentBuffer === "") {
					if (latestInterim === "") return;
					segmentBuffer = latestInterim;
				}
				latestInterim = "";
				controller.enqueue({
					type: "transcript-final",
					id: segmentId(),
					text: segmentBuffer
				});
				fullText += fullText === "" ? segmentBuffer : ` ${segmentBuffer}`;
				segmentBuffer = "";
				segmentCounter++;
			};
			const finish = () => {
				if (finished) return;
				completeSegment();
				finished = true;
				controller.enqueue({
					type: "finish",
					text: fullText,
					segments: [],
					language,
					durationInSeconds: void 0,
					usage: usageMetadata,
					...usageMetadata != null ? { providerMetadata: { google: { usageMetadata } } } : {}
				});
				controller.close();
				cleanup(1e3);
			};
			const sendAudio = async (socket) => {
				audioReader = audio.getReader();
				try {
					while (true) {
						const { done, value } = await audioReader.read();
						if (done || finished) break;
						socket.send(JSON.stringify({ realtimeInput: { audio: {
							data: convertToBase64(value),
							mimeType: `audio/pcm;rate=${inputAudioRate}`
						} } }));
						await waitForWebSocketBufferDrain(socket);
					}
				} finally {
					audioReader.releaseLock();
					audioReader = void 0;
				}
				if (!finished) {
					socket.send(JSON.stringify({ realtimeInput: { audioStreamEnd: true } }));
					audioEnded = true;
					schedulePendingFinish();
				}
			};
			connection = connectToWebSocket({
				url,
				headers,
				webSocket,
				abortSignal,
				onAbort: finishWithError,
				onProcessingError: finishWithError,
				onOpen: (socket) => {
					controller.enqueue({
						type: "stream-start",
						warnings
					});
					socket.send(JSON.stringify({ setup }));
					setupComplete.then(() => finished ? void 0 : sendAudio(socket)).catch(finishWithError);
				},
				onMessageText: async (text) => {
					if (finished) return;
					const parsed = await safeParseJSON({ text });
					if (!parsed.success) return;
					const message = parsed.value;
					if (includeRawChunks) controller.enqueue({
						type: "raw",
						rawValue: message
					});
					if (message.setupComplete != null) resolveSetupComplete();
					if (message.usageMetadata != null) usageMetadata = message.usageMetadata;
					if (message.error != null) {
						finishWithError(new Error(message.error.message ?? "Google Live API error"));
						return;
					}
					const serverContent = message.serverContent;
					const interim = serverContent?.interimInputTranscription;
					if (interim?.text) {
						schedulePendingFinish();
						latestInterim = interim.text;
						controller.enqueue({
							type: "transcript-partial",
							id: segmentId(),
							text: interim.text
						});
					}
					const transcription = serverContent?.inputTranscription ?? message.inputTranscription;
					if (transcription != null) {
						if (transcription.languageCode != null) language = transcription.languageCode;
						if (transcription.text) {
							schedulePendingFinish();
							latestInterim = "";
							segmentBuffer += transcription.text;
							controller.enqueue({
								type: "transcript-delta",
								id: segmentId(),
								delta: transcription.text
							});
						}
						if (transcription.finished === true) completeSegment();
					}
					if (serverContent?.turnComplete) completeSegment();
					const interactionStatus = serverContent?.interactionStatus;
					if (audioEnded && (interactionStatus === "IDLE" || interactionStatus === "REQUIRES_ACTION" || serverContent?.turnComplete === true && interactionStatus == null)) finish();
				},
				onSocketError: () => {
					finishWithError(/* @__PURE__ */ new Error("Google Live transcription error"));
				},
				onClose: ({ code, reason }) => {
					if (finished) return;
					if (audioEnded) {
						finish();
						return;
					}
					finishWithError(/* @__PURE__ */ new Error(`Google Live transcription WebSocket closed unexpectedly before finishing (code ${code ?? "unknown"}${reason ? `, reason: ${reason}` : ""}).`));
				}
			});
		},
		cancel: () => {
			if (finished) return;
			finished = true;
			cleanup();
		}
	});
}
/**
* Builds Google's `AudioTranscriptionConfig` from provider options; returns
* undefined when no options are set.
*/
function buildAudioTranscriptionConfig(options) {
	if (options == null) return void 0;
	const config = {};
	if (options.languageCodes != null) config.languageCodes = options.languageCodes;
	if (options.customVocabulary != null) config.customVocabulary = options.customVocabulary;
	if (options.wordTimestamp != null) config.wordTimestamp = options.wordTimestamp;
	if (options.diarization != null) config.diarization = options.diarization;
	if (options.mode != null) config.mode = options.mode;
	return Object.keys(config).length > 0 ? config : void 0;
}
/**
* Builds the Interactions API `transcription_config` (snake_case wire) from
* provider options; returns undefined when no options are set. Diarization
* and word timestamps are expressed inside the `mode` object per
* https://ai.google.dev/gemini-api/docs/transcribe.
*/
function buildTranscriptionConfig(options) {
	if (options == null) return void 0;
	const config = {};
	if (options.languageCodes != null) config.language_codes = options.languageCodes;
	if (options.customVocabulary != null) config.custom_vocabulary = options.customVocabulary;
	if (options.mode != null || options.diarization === true || options.wordTimestamp === true) config.mode = {
		type: (options.mode ?? "VERBATIM").toLowerCase(),
		...options.diarization === true ? { diarization_mode: "speaker" } : {},
		...options.wordTimestamp === true ? { timestamp_granularities: ["word"] } : {}
	};
	return Object.keys(config).length > 0 ? config : void 0;
}
/** Parses a Google duration offset such as `"1s"` or `"9.400s"` to seconds. */
function parseOffsetSeconds(offset) {
	if (offset == null) return void 0;
	const parsed = Number.parseFloat(offset);
	return Number.isFinite(parsed) ? parsed : void 0;
}
function validateLiveInputAudioFormat(inputAudioFormat) {
	if (inputAudioFormat.type !== "audio/pcm" || inputAudioFormat.rate != null && inputAudioFormat.rate !== 16e3) throw new InvalidArgumentError({
		argument: "inputAudioFormat",
		message: "The Gemini Live transcription API only supports 16kHz 16-bit PCM input audio."
	});
}
const googleInteractionsWordAnnotationSchema = () => z.object({
	type: z.string().nullish(),
	text: z.string().nullish(),
	speaker: z.string().nullish(),
	start_offset: z.string().nullish(),
	end_offset: z.string().nullish()
});
const googleInteractionsTranscriptionResponseSchema = lazySchema(() => zodSchema(z.object({
	status: z.string().nullish(),
	steps: z.array(z.object({
		type: z.string().nullish(),
		content: z.array(z.object({
			type: z.string().nullish(),
			text: z.string().nullish(),
			annotations: z.array(googleInteractionsWordAnnotationSchema()).nullish()
		})).nullish()
	})).nullish(),
	usage: z.record(z.string(), z.json()).nullish()
})));
//#endregion
//#region src/speech-translation/google-speech-translation-model-options.ts
const googleSpeechTranslationModelOptions = lazySchema(() => zodSchema(z.object({ 
/**
* Whether input audio already in the target language should be echoed
* instead of producing silence.
*/
echoTargetLanguage: z.boolean().optional() })));
//#endregion
//#region src/speech-translation/google-speech-translation-model.ts
const liveWebSocketPath = "google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent";
/**
* After the input audio has ended, finish after this much trailing output
* silence. Live Translation is continuous and does not emit turnComplete.
*/
const defaultFinishGraceMs = 1e3;
const googleLiveOutputAudioRate = 24e3;
const pcm16SilenceAmplitudeThreshold = 128;
function getLiveWebSocketURL(baseURL, apiKey) {
	const url = getRealtimeWebSocketURL(baseURL, liveWebSocketPath);
	url.searchParams.set("key", apiKey);
	return url;
}
var GoogleSpeechTranslationModel = class GoogleSpeechTranslationModel {
	static [WORKFLOW_SERIALIZE](model) {
		return serializeModelOptions({
			modelId: model.modelId,
			config: model.config
		});
	}
	static [WORKFLOW_DESERIALIZE](options) {
		return new GoogleSpeechTranslationModel(options.modelId, options.config);
	}
	get provider() {
		return this.config.provider;
	}
	constructor(modelId, config) {
		this.specificationVersion = "v4";
		this.modelId = modelId;
		this.config = config;
	}
	async doStream(options) {
		if (options.targetLanguage == null) throw new InvalidArgumentError({
			argument: "targetLanguage",
			message: `targetLanguage is required for translation model '${this.modelId}'.`
		});
		const currentDate = this.config._internal?.currentDate?.() ?? /* @__PURE__ */ new Date();
		const googleOptions = await parseProviderOptions({
			provider: "google",
			providerOptions: options.providerOptions,
			schema: googleSpeechTranslationModelOptions
		});
		const warnings = [];
		validateGoogleSpeechTranslationInputAudioFormat(options.inputAudioFormat);
		if (options.sourceLanguage != null) warnings.push({
			type: "unsupported",
			feature: "sourceLanguage",
			details: "The Gemini Live translation API auto-detects the source language and does not accept a source language."
		});
		if (options.outputAudioFormat != null) warnings.push({
			type: "unsupported",
			feature: "outputAudioFormat",
			details: "The Gemini Live API always outputs 24kHz 16-bit PCM audio and does not accept an output audio format."
		});
		const headers = combineHeaders(await resolve(this.config.headers), options.headers);
		let apiKey;
		for (const [key, value] of Object.entries(headers)) if (key.toLowerCase() === "x-goog-api-key" && value != null) apiKey = value;
		if (apiKey == null) throw new Error("Google Generative AI API key is required for streaming translation.");
		const webSocketHeaders = Object.fromEntries(Object.entries(headers).filter(([key]) => key.toLowerCase() !== "x-goog-api-key"));
		const setup = buildGoogleLiveSpeechTranslationSetup({
			modelId: this.modelId,
			targetLanguage: options.targetLanguage,
			providerOptions: googleOptions
		});
		return {
			request: { body: setup },
			response: {
				timestamp: currentDate,
				modelId: this.modelId
			},
			stream: createGoogleLiveSpeechTranslationStream({
				webSocket: this.config.webSocket,
				url: getLiveWebSocketURL(this.config.baseURL, apiKey),
				headers: webSocketHeaders,
				setup,
				inputAudioRate: options.inputAudioFormat.rate ?? 16e3,
				finishGraceMs: this.config._internal?.finishGraceMs ?? defaultFinishGraceMs,
				warnings,
				audio: options.audio,
				abortSignal: options.abortSignal,
				includeRawChunks: options.includeRawChunks
			})
		};
	}
};
function createGoogleLiveSpeechTranslationStream({ webSocket, url, headers, setup, inputAudioRate, finishGraceMs, warnings, audio, abortSignal, includeRawChunks }) {
	let finished = false;
	let cleanup = () => {};
	return new ReadableStream({
		start: (controller) => {
			let audioReader;
			let connection;
			let resolveSetupComplete;
			const setupComplete = new Promise((resolve) => {
				resolveSetupComplete = resolve;
			});
			let turnCounter = 0;
			let sourceText = "";
			let sourceTurnBuffer = "";
			let translationText = "";
			let translationTurnBuffer = "";
			let audioEnded = false;
			let usage;
			let openTurn = false;
			let sawTurnComplete = false;
			let trailingSilenceMs = 0;
			let finishTimer;
			const itemId = () => `google-item-${turnCounter}`;
			const cancelPendingFinish = () => {
				if (finishTimer != null) {
					clearTimeout(finishTimer);
					finishTimer = void 0;
				}
			};
			const schedulePendingFinish = () => {
				if (finished || finishTimer != null) return;
				finishTimer = setTimeout(() => {
					finishTimer = void 0;
					finish();
				}, finishGraceMs);
			};
			const onTurnActivity = () => {
				openTurn = true;
				trailingSilenceMs = 0;
				cancelPendingFinish();
			};
			cleanup = (closeCode) => {
				cancelPendingFinish();
				if (audioReader != null) audioReader.cancel().catch(() => {});
				else audio.cancel().catch(() => {});
				connection?.close(closeCode);
			};
			const finishWithError = (error) => {
				if (finished) return;
				finished = true;
				cleanup();
				controller.error(error);
			};
			const finish = () => {
				if (finished) return;
				if (sourceTurnBuffer !== "" || translationTurnBuffer !== "") completeTurn();
				finished = true;
				controller.enqueue({
					type: "finish",
					sourceText,
					outputText: translationText,
					usage
				});
				controller.close();
				cleanup(1e3);
			};
			const completeTurn = () => {
				if (sourceTurnBuffer !== "") {
					controller.enqueue({
						type: "source-transcript-final",
						id: itemId(),
						text: sourceTurnBuffer
					});
					sourceText += sourceTurnBuffer;
					sourceTurnBuffer = "";
				}
				if (translationTurnBuffer !== "") {
					controller.enqueue({
						type: "output-text-final",
						id: itemId(),
						text: translationTurnBuffer
					});
					translationText += translationTurnBuffer;
					translationTurnBuffer = "";
				}
				turnCounter++;
			};
			const sendAudio = async (socket) => {
				audioReader = audio.getReader();
				try {
					while (true) {
						const { done, value } = await audioReader.read();
						if (done || finished) break;
						socket.send(JSON.stringify({ realtimeInput: { audio: {
							data: convertToBase64(value),
							mimeType: `audio/pcm;rate=${inputAudioRate}`
						} } }));
						await waitForWebSocketBufferDrain(socket);
					}
				} finally {
					audioReader.releaseLock();
					audioReader = void 0;
				}
				if (!finished) {
					socket.send(JSON.stringify({ realtimeInput: { audioStreamEnd: true } }));
					audioEnded = true;
					if (sawTurnComplete && !openTurn) schedulePendingFinish();
				}
			};
			connection = connectToWebSocket({
				url,
				headers,
				webSocket,
				abortSignal,
				onAbort: finishWithError,
				onProcessingError: finishWithError,
				onOpen: (socket) => {
					controller.enqueue({
						type: "stream-start",
						warnings
					});
					socket.send(JSON.stringify({ setup }));
					setupComplete.then(() => finished ? void 0 : sendAudio(socket)).catch(finishWithError);
				},
				onMessageText: async (text) => {
					if (finished) return;
					const parsed = await safeParseJSON({ text });
					if (!parsed.success) return;
					const message = parsed.value;
					if (includeRawChunks) controller.enqueue({
						type: "raw",
						rawValue: message
					});
					if (message.setupComplete != null) resolveSetupComplete();
					if (message.usageMetadata != null) usage = accumulateGoogleLiveUsage(usage, message.usageMetadata);
					if (message.error != null) {
						finishWithError(new Error(message.error.message ?? "Google Live API error"));
						return;
					}
					const inputTranscriptionText = message.serverContent?.inputTranscription?.text ?? message.inputTranscription?.text;
					if (inputTranscriptionText) {
						onTurnActivity();
						sourceTurnBuffer += inputTranscriptionText;
						controller.enqueue({
							type: "source-transcript-delta",
							id: itemId(),
							delta: inputTranscriptionText
						});
					}
					const serverContent = message.serverContent;
					if (serverContent == null) return;
					for (const part of serverContent.modelTurn?.parts ?? []) if (part.inlineData?.data) {
						controller.enqueue({
							type: "audio",
							id: itemId(),
							audio: part.inlineData.data
						});
						const silenceDurationMs = getPcm16SilenceDurationMs(part.inlineData.data);
						if (audioEnded && silenceDurationMs != null) {
							trailingSilenceMs += silenceDurationMs;
							if (trailingSilenceMs >= finishGraceMs) {
								finish();
								return;
							}
						} else onTurnActivity();
					}
					if (serverContent.outputTranscription?.text) {
						onTurnActivity();
						translationTurnBuffer += serverContent.outputTranscription.text;
						controller.enqueue({
							type: "output-text-delta",
							id: itemId(),
							delta: serverContent.outputTranscription.text
						});
					}
					if (serverContent.turnComplete) {
						completeTurn();
						openTurn = false;
						sawTurnComplete = true;
						if (audioEnded) schedulePendingFinish();
					}
				},
				onSocketError: () => {
					finishWithError(/* @__PURE__ */ new Error("Google Live translation error"));
				},
				onClose: ({ code, reason }) => {
					if (finished) return;
					if (finishTimer != null) {
						finish();
						return;
					}
					finishWithError(/* @__PURE__ */ new Error(`Google Live translation WebSocket closed unexpectedly before finishing (code ${code ?? "unknown"}${reason ? `, reason: ${reason}` : ""}).`));
				}
			});
		},
		cancel: () => {
			if (finished) return;
			finished = true;
			cleanup();
		}
	});
}
function accumulateGoogleLiveUsage(usage, usageMetadata) {
	let inputAudioTokens = usage?.inputAudioTokens;
	let outputAudioTokens = usage?.outputAudioTokens;
	for (const detail of usageMetadata.promptTokensDetails ?? []) if (detail.modality === "AUDIO" && detail.tokenCount != null) inputAudioTokens = (inputAudioTokens ?? 0) + detail.tokenCount;
	for (const detail of usageMetadata.responseTokensDetails ?? []) if (detail.modality === "AUDIO" && detail.tokenCount != null) outputAudioTokens = (outputAudioTokens ?? 0) + detail.tokenCount;
	if (inputAudioTokens == null && outputAudioTokens == null) return usage;
	return {
		...usage,
		...inputAudioTokens != null ? { inputAudioTokens } : {},
		...outputAudioTokens != null ? { outputAudioTokens } : {}
	};
}
function getPcm16SilenceDurationMs(audio) {
	let bytes;
	try {
		bytes = convertBase64ToUint8Array(audio);
	} catch {
		return;
	}
	if (bytes.byteLength < 2) return;
	const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
	const sampleCount = Math.floor(bytes.byteLength / 2);
	for (let i = 0; i < sampleCount; i++) if (Math.abs(view.getInt16(i * 2, true)) > pcm16SilenceAmplitudeThreshold) return;
	return sampleCount / googleLiveOutputAudioRate * 1e3;
}
function buildGoogleLiveSpeechTranslationSetup({ modelId, targetLanguage, providerOptions }) {
	return {
		model: getModelPath(modelId),
		generationConfig: {
			responseModalities: ["AUDIO"],
			translationConfig: {
				targetLanguageCode: targetLanguage,
				...providerOptions?.echoTargetLanguage != null ? { echoTargetLanguage: providerOptions.echoTargetLanguage } : {}
			}
		},
		inputAudioTranscription: {},
		outputAudioTranscription: {}
	};
}
function validateGoogleSpeechTranslationInputAudioFormat(inputAudioFormat) {
	if (inputAudioFormat.type !== "audio/pcm" || inputAudioFormat.rate != null && inputAudioFormat.rate !== 16e3) throw new InvalidArgumentError({
		argument: "inputAudioFormat",
		message: "The Gemini Live translation API only supports 16kHz 16-bit PCM input audio."
	});
}
//#endregion
//#region src/google-provider.ts
const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
const googleFilesUrlPattern = /^https:\/\/generativelanguage\.googleapis\.com\/v1beta\/files\/.*$/;
const supportedExternalUrlMediaTypes = [
	"text/html",
	"text/css",
	"text/plain",
	"text/xml",
	"text/csv",
	"text/rtf",
	"text/javascript",
	"application/json",
	"application/pdf",
	"image/bmp",
	"image/jpeg",
	"image/png",
	"image/webp",
	"video/mp4",
	"video/mpeg",
	"video/quicktime",
	"video/avi",
	"video/x-flv",
	"video/mpg",
	"video/webm",
	"video/wmv",
	"video/3gpp"
];
const externalHttpsUrlPattern = /^https:\/\/.*$/;
function supportsExternalFileUrls(modelId) {
	return /(^|\/)gemini-/.test(modelId) && !/(^|\/)gemini-2\.0/.test(modelId);
}
/**
* Create a Google provider instance.
*/
function createGoogle(options = {}) {
	const baseURL = withoutTrailingSlash(options.baseURL) ?? DEFAULT_BASE_URL;
	const providerName = options.name ?? "google.generative-ai";
	const getHeaders = () => resolveWith(options.apiKey, (apiKey) => withUserAgentSuffix({
		"x-goog-api-key": loadApiKey({
			apiKey,
			environmentVariableName: "GOOGLE_GENERATIVE_AI_API_KEY",
			description: "Google Generative AI"
		}),
		...options.headers
	}, `ai-sdk-google/${VERSION}`));
	const getSupportedUrls = (modelId, includeExternalUrls = modelId == null || supportsExternalFileUrls(modelId)) => ({
		"*": [
			googleFilesUrlPattern,
			new RegExp(`^${baseURL}/files/.*$`),
			new RegExp(`^https://(?:www\\.)?youtube\\.com/watch\\?v=[\\w-]+(?:&[\\w=&.-]*)?$`),
			new RegExp(`^https://youtu\\.be/[\\w-]+(?:\\?[\\w=&.-]*)?$`)
		],
		...includeExternalUrls ? Object.fromEntries(supportedExternalUrlMediaTypes.map((mediaType) => [mediaType, [externalHttpsUrlPattern]])) : {}
	});
	const languageModelConfig = {
		provider: providerName,
		baseURL,
		headers: getHeaders,
		generateId: options.generateId ?? generateId,
		fetch: options.fetch
	};
	const createChatModel = (modelId) => new GoogleLanguageModel(modelId, {
		...languageModelConfig,
		supportedUrls: () => getSupportedUrls(modelId)
	});
	const createBatch = () => new GoogleBatch({
		provider: `${providerName.replace(/\.generative-ai$/, "")}.batch`,
		maxLineBytes: options.batchResultDownloads?.maxLineBytes,
		config: languageModelConfig,
		supportedUrls: getSupportedUrls(void 0, false)
	});
	const createEmbeddingModel = (modelId) => new GoogleEmbeddingModel(modelId, {
		provider: providerName,
		baseURL,
		headers: getHeaders,
		fetch: options.fetch
	});
	const createImageModel = (modelId, settings = {}) => new GoogleImageModel(modelId, settings, {
		provider: providerName,
		baseURL,
		headers: getHeaders,
		fetch: options.fetch
	});
	const createFiles = () => new GoogleFiles({
		provider: providerName,
		baseURL,
		headers: getHeaders,
		fetch: options.fetch
	});
	const createVideoModel = (modelId) => new GoogleVideoModel(modelId, {
		provider: providerName,
		baseURL,
		headers: getHeaders,
		fetch: options.fetch,
		generateId: options.generateId ?? generateId
	});
	const createRealtimeModel = (modelId) => new GoogleRealtimeModel(modelId, {
		provider: `${providerName}.realtime`,
		baseURL,
		headers: getHeaders,
		fetch: options.fetch
	});
	const createSpeechTranslationModel = (modelId) => new GoogleSpeechTranslationModel(modelId, {
		provider: `${providerName}.speech-translation`,
		baseURL,
		headers: getHeaders,
		webSocket: options.webSocket
	});
	const createSpeechModel = (modelId) => new GoogleSpeechModel(modelId, {
		provider: `${providerName}.speech`,
		baseURL,
		headers: getHeaders,
		fetch: options.fetch
	});
	const createTranscriptionModel = (modelId) => new GoogleTranscriptionModel(modelId, {
		provider: `${providerName}.transcription`,
		baseURL,
		headers: getHeaders,
		fetch: options.fetch,
		webSocket: options.webSocket
	});
	const experimentalRealtimeFactory = Object.assign((modelId) => createRealtimeModel(modelId), { getToken: async (tokenOptions) => {
		const secret = await createRealtimeModel(tokenOptions.model).doCreateClientSecret({
			sessionConfig: tokenOptions.sessionConfig,
			expiresAfterSeconds: tokenOptions.expiresAfterSeconds
		});
		return {
			token: secret.token,
			url: secret.url,
			expiresAt: secret.expiresAt
		};
	} });
	const createInteractionsModel = (modelIdOrAgent) => new GoogleInteractionsLanguageModel(modelIdOrAgent, {
		provider: `${providerName}.interactions`,
		baseURL,
		headers: getHeaders,
		generateId: options.generateId ?? generateId,
		fetch: options.fetch
	});
	const provider = function(modelId) {
		if (new.target) throw new Error("The Google Generative AI model function cannot be called with the new keyword.");
		return createChatModel(modelId);
	};
	provider.specificationVersion = "v4";
	provider.languageModel = createChatModel;
	provider.chat = createChatModel;
	provider.generativeAI = createChatModel;
	provider.decisionModel = (modelId) => new DecisionGoogleModel(modelId, {
		provider: `${providerName.replace(/\.generative-ai$/, "")}.decision`,
		baseURL,
		headers: getHeaders,
		fetch: options.fetch
	});
	provider.evaluationModel = provider.decisionModel;
	provider.extractionModel = (modelId) => new Experimental_ExtractionLanguageModel({
		model: createChatModel(modelId),
		provider: `${providerName.replace(/\.generative-ai$/, "")}.extraction`
	});
	provider.experimental_batch = createBatch;
	provider.embedding = createEmbeddingModel;
	provider.embeddingModel = createEmbeddingModel;
	provider.textEmbedding = createEmbeddingModel;
	provider.textEmbeddingModel = createEmbeddingModel;
	provider.image = createImageModel;
	provider.imageModel = createImageModel;
	provider.video = createVideoModel;
	provider.videoModel = createVideoModel;
	provider.experimental_realtime = experimentalRealtimeFactory;
	provider.files = createFiles;
	provider.speech = createSpeechModel;
	provider.speechModel = createSpeechModel;
	provider.transcription = createTranscriptionModel;
	provider.transcriptionModel = createTranscriptionModel;
	provider.translation = createSpeechTranslationModel;
	provider.speechTranslationModel = createSpeechTranslationModel;
	provider.interactions = createInteractionsModel;
	provider.tools = googleTools;
	return provider;
}
/**
* Default Google Generative AI provider instance.
*/
const google = createGoogle();
//#endregion
export { GoogleRealtimeModel as Experimental_GoogleRealtimeModel, GoogleSpeechTranslationModel as Experimental_GoogleSpeechTranslationModel, GoogleSpeechTranslationModel as Experimental_GoogleTranslationModel, GoogleTranscriptionModel, VERSION, createGoogle, createGoogle as createGoogleGenerativeAI, google };

//# sourceMappingURL=index.js.map