import { i as sanitizeSurrogates, t as transformMessages } from "./transform-messages-CDzZFJ8Z.js";
import { FinishReason, FunctionCallingConfigMode } from "@google/genai";
//#region node_modules/@mariozechner/pi-ai/dist/providers/google-shared.js
/**
* Shared utilities for Google Generative AI and Google Vertex providers.
*/
/**
* Determines whether a streamed Gemini `Part` should be treated as "thinking".
*
* Protocol note (Gemini / Vertex AI thought signatures):
* - `thought: true` is the definitive marker for thinking content (thought summaries).
* - `thoughtSignature` is an encrypted representation of the model's internal thought process
*   used to preserve reasoning context across multi-turn interactions.
* - `thoughtSignature` can appear on ANY part type (text, functionCall, etc.) - it does NOT
*   indicate the part itself is thinking content.
* - For non-functionCall responses, the signature appears on the last part for context replay.
* - When persisting/replaying model outputs, signature-bearing parts must be preserved as-is;
*   do not merge/move signatures across parts.
*
* See: https://ai.google.dev/gemini-api/docs/thought-signatures
*/
function isThinkingPart(part) {
	return part.thought === true;
}
/**
* Retain thought signatures during streaming.
*
* Some backends only send `thoughtSignature` on the first delta for a given part/block; later deltas may omit it.
* This helper preserves the last non-empty signature for the current block.
*
* Note: this does NOT merge or move signatures across distinct response parts. It only prevents
* a signature from being overwritten with `undefined` within the same streamed block.
*/
function retainThoughtSignature(existing, incoming) {
	if (typeof incoming === "string" && incoming.length > 0) return incoming;
	return existing;
}
const base64SignaturePattern = /^[A-Za-z0-9+/]+={0,2}$/;
function isValidThoughtSignature(signature) {
	if (!signature) return false;
	if (signature.length % 4 !== 0) return false;
	return base64SignaturePattern.test(signature);
}
/**
* Only keep signatures from the same provider/model and with valid base64.
*/
function resolveThoughtSignature(isSameProviderAndModel, signature) {
	return isSameProviderAndModel && isValidThoughtSignature(signature) ? signature : void 0;
}
/**
* Models via Google APIs that require explicit tool call IDs in function calls/responses.
*/
function requiresToolCallId(modelId) {
	return modelId.startsWith("claude-") || modelId.startsWith("gpt-oss-");
}
function getGeminiMajorVersion(modelId) {
	const match = modelId.toLowerCase().match(/^gemini(?:-live)?-(\d+)/);
	if (!match) return void 0;
	return Number.parseInt(match[1], 10);
}
function supportsMultimodalFunctionResponse(modelId) {
	const geminiMajorVersion = getGeminiMajorVersion(modelId);
	if (geminiMajorVersion !== void 0) return geminiMajorVersion >= 3;
	return true;
}
/**
* Convert internal messages to Gemini Content[] format.
*/
function convertMessages(model, context) {
	const contents = [];
	const normalizeToolCallId = (id) => {
		if (!requiresToolCallId(model.id)) return id;
		return id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64);
	};
	const transformedMessages = transformMessages(context.messages, model, normalizeToolCallId);
	for (const msg of transformedMessages) if (msg.role === "user") if (typeof msg.content === "string") contents.push({
		role: "user",
		parts: [{ text: sanitizeSurrogates(msg.content) }]
	});
	else {
		const parts = msg.content.map((item) => {
			if (item.type === "text") return { text: sanitizeSurrogates(item.text) };
			else return { inlineData: {
				mimeType: item.mimeType,
				data: item.data
			} };
		});
		if (parts.length === 0) continue;
		contents.push({
			role: "user",
			parts
		});
	}
	else if (msg.role === "assistant") {
		const parts = [];
		const isSameProviderAndModel = msg.provider === model.provider && msg.model === model.id;
		for (const block of msg.content) if (block.type === "text") {
			if (!block.text || block.text.trim() === "") continue;
			const thoughtSignature = resolveThoughtSignature(isSameProviderAndModel, block.textSignature);
			parts.push({
				text: sanitizeSurrogates(block.text),
				...thoughtSignature && { thoughtSignature }
			});
		} else if (block.type === "thinking") {
			if (!block.thinking || block.thinking.trim() === "") continue;
			if (isSameProviderAndModel) {
				const thoughtSignature = resolveThoughtSignature(isSameProviderAndModel, block.thinkingSignature);
				parts.push({
					thought: true,
					text: sanitizeSurrogates(block.thinking),
					...thoughtSignature && { thoughtSignature }
				});
			} else parts.push({ text: sanitizeSurrogates(block.thinking) });
		} else if (block.type === "toolCall") {
			const thoughtSignature = resolveThoughtSignature(isSameProviderAndModel, block.thoughtSignature);
			const part = {
				functionCall: {
					name: block.name,
					args: block.arguments ?? {},
					...requiresToolCallId(model.id) ? { id: block.id } : {}
				},
				...thoughtSignature && { thoughtSignature }
			};
			parts.push(part);
		}
		if (parts.length === 0) continue;
		contents.push({
			role: "model",
			parts
		});
	} else if (msg.role === "toolResult") {
		const textResult = msg.content.filter((c) => c.type === "text").map((c) => c.text).join("\n");
		const imageContent = model.input.includes("image") ? msg.content.filter((c) => c.type === "image") : [];
		const hasText = textResult.length > 0;
		const hasImages = imageContent.length > 0;
		const modelSupportsMultimodalFunctionResponse = supportsMultimodalFunctionResponse(model.id);
		const responseValue = hasText ? sanitizeSurrogates(textResult) : hasImages ? "(see attached image)" : "";
		const imageParts = imageContent.map((imageBlock) => ({ inlineData: {
			mimeType: imageBlock.mimeType,
			data: imageBlock.data
		} }));
		const includeId = requiresToolCallId(model.id);
		const functionResponsePart = { functionResponse: {
			name: msg.toolName,
			response: msg.isError ? { error: responseValue } : { output: responseValue },
			...hasImages && modelSupportsMultimodalFunctionResponse && { parts: imageParts },
			...includeId ? { id: msg.toolCallId } : {}
		} };
		const lastContent = contents[contents.length - 1];
		if (lastContent?.role === "user" && lastContent.parts?.some((p) => p.functionResponse)) lastContent.parts.push(functionResponsePart);
		else contents.push({
			role: "user",
			parts: [functionResponsePart]
		});
		if (hasImages && !modelSupportsMultimodalFunctionResponse) contents.push({
			role: "user",
			parts: [{ text: "Tool result image:" }, ...imageParts]
		});
	}
	return contents;
}
const JSON_SCHEMA_META_DECLARATIONS = new Set([
	"$schema",
	"$id",
	"$anchor",
	"$dynamicAnchor",
	"$vocabulary",
	"$comment",
	"$defs",
	"definitions"
]);
/**
* Strip meta-declarations from a schema obj
*/
function sanitizeForOpenApi(schema) {
	if (typeof schema !== "object" || schema === null || Array.isArray(schema)) return schema;
	const result = {};
	for (const [key, value] of Object.entries(schema)) {
		if (JSON_SCHEMA_META_DECLARATIONS.has(key)) continue;
		result[key] = sanitizeForOpenApi(value);
	}
	return result;
}
/**
* Convert tools to Gemini function declarations format.
*
* By default uses `parametersJsonSchema` which supports full JSON Schema (including
* anyOf, oneOf, const, etc.). Set `useParameters` to true to use the legacy `parameters`
* field instead (OpenAPI 3.03 Schema). This is needed for Cloud Code Assist with Claude
* models, where the API translates `parameters` into Anthropic's `input_schema`.
*/
function convertTools(tools, useParameters = false) {
	if (tools.length === 0) return void 0;
	return [{ functionDeclarations: tools.map((tool) => ({
		name: tool.name,
		description: tool.description,
		...useParameters ? { parameters: sanitizeForOpenApi(tool.parameters) } : { parametersJsonSchema: tool.parameters }
	})) }];
}
/**
* Map tool choice string to Gemini FunctionCallingConfigMode.
*/
function mapToolChoice(choice) {
	switch (choice) {
		case "auto": return FunctionCallingConfigMode.AUTO;
		case "none": return FunctionCallingConfigMode.NONE;
		case "any": return FunctionCallingConfigMode.ANY;
		default: return FunctionCallingConfigMode.AUTO;
	}
}
/**
* Map Gemini FinishReason to our StopReason.
*/
function mapStopReason(reason) {
	switch (reason) {
		case FinishReason.STOP: return "stop";
		case FinishReason.MAX_TOKENS: return "length";
		case FinishReason.BLOCKLIST:
		case FinishReason.PROHIBITED_CONTENT:
		case FinishReason.SPII:
		case FinishReason.SAFETY:
		case FinishReason.IMAGE_SAFETY:
		case FinishReason.IMAGE_PROHIBITED_CONTENT:
		case FinishReason.IMAGE_RECITATION:
		case FinishReason.IMAGE_OTHER:
		case FinishReason.RECITATION:
		case FinishReason.FINISH_REASON_UNSPECIFIED:
		case FinishReason.OTHER:
		case FinishReason.LANGUAGE:
		case FinishReason.MALFORMED_FUNCTION_CALL:
		case FinishReason.UNEXPECTED_TOOL_CALL:
		case FinishReason.NO_IMAGE: return "error";
		default: throw new Error(`Unhandled stop reason: ${reason}`);
	}
}
//#endregion
export { mapToolChoice as a, mapStopReason as i, convertTools as n, retainThoughtSignature as o, isThinkingPart as r, convertMessages as t };
