import { l as normalizeStringEntries } from "./string-normalization-WNUDCpXX.js";
import { o as createProviderHttpError } from "./provider-http-errors-iTH78wTf.js";
import "./string-coerce-runtime-CmKEt2-v.js";
import "./provider-http-cqr3AnCX.js";
import { r as sanitizeAndNormalizeEmbedding } from "./embeddings-Molb7Y3n.js";
import { C as withRemoteHttpResponse, _ as buildBatchHeaders, g as runEmbeddingBatchGroups, h as buildEmbeddingBatchGroupOptions, o as debugEmbeddingsLog, v as normalizeBatchBaseUrl } from "./memory-core-host-engine-embeddings-CbWyG7iu.js";
import crypto from "node:crypto";
//#region extensions/google/embedding-batch.ts
const GEMINI_BATCH_MAX_REQUESTS = 5e4;
function hashText(text) {
	return crypto.createHash("sha256").update(text).digest("hex");
}
function getGeminiUploadUrl(baseUrl) {
	if (baseUrl.includes("/v1beta")) return baseUrl.replace(/\/v1beta\/?$/, "/upload/v1beta");
	return `${baseUrl.replace(/\/$/, "")}/upload`;
}
function buildGeminiUploadBody(params) {
	const boundary = `openclaw-${hashText(params.displayName)}`;
	const jsonPart = JSON.stringify({ file: {
		displayName: params.displayName,
		mimeType: "application/jsonl"
	} });
	const delimiter = `--${boundary}\r\n`;
	const closeDelimiter = `--${boundary}--\r\n`;
	const parts = [
		`${delimiter}Content-Type: application/json; charset=UTF-8\r\n\r\n${jsonPart}\r\n`,
		`${delimiter}Content-Type: application/jsonl; charset=UTF-8\r\n\r\n${params.jsonl}\r\n`,
		closeDelimiter
	];
	return {
		body: new Blob([parts.join("")], { type: "multipart/related" }),
		contentType: `multipart/related; boundary=${boundary}`
	};
}
async function submitGeminiBatch(params) {
	const baseUrl = normalizeBatchBaseUrl(params.gemini);
	const uploadPayload = buildGeminiUploadBody({
		jsonl: params.requests.map((request) => JSON.stringify({
			key: request.custom_id,
			request: request.request
		})).join("\n"),
		displayName: `memory-embeddings-${hashText(String(Date.now()))}`
	});
	const uploadUrl = `${getGeminiUploadUrl(baseUrl)}/files?uploadType=multipart`;
	debugEmbeddingsLog("memory embeddings: gemini batch upload", {
		uploadUrl,
		baseUrl,
		requests: params.requests.length
	});
	const filePayload = await withRemoteHttpResponse({
		url: uploadUrl,
		ssrfPolicy: params.gemini.ssrfPolicy,
		init: {
			method: "POST",
			headers: {
				...buildBatchHeaders(params.gemini, { json: false }),
				"Content-Type": uploadPayload.contentType
			},
			body: uploadPayload.body
		},
		onResponse: async (fileRes) => {
			if (!fileRes.ok) {
				const text = await fileRes.text();
				throw new Error(`gemini batch file upload failed: ${fileRes.status} ${text}`);
			}
			return await fileRes.json();
		}
	});
	const fileId = filePayload.name ?? filePayload.file?.name;
	if (!fileId) throw new Error("gemini batch file upload failed: missing file id");
	const batchBody = { batch: {
		displayName: `memory-embeddings-${params.agentId}`,
		inputConfig: { file_name: fileId }
	} };
	const batchEndpoint = `${baseUrl}/${params.gemini.modelPath}:asyncBatchEmbedContent`;
	debugEmbeddingsLog("memory embeddings: gemini batch create", {
		batchEndpoint,
		fileId
	});
	return await withRemoteHttpResponse({
		url: batchEndpoint,
		ssrfPolicy: params.gemini.ssrfPolicy,
		init: {
			method: "POST",
			headers: buildBatchHeaders(params.gemini, { json: true }),
			body: JSON.stringify(batchBody)
		},
		onResponse: async (batchRes) => {
			if (batchRes.ok) return await batchRes.json();
			const text = await batchRes.text();
			if (batchRes.status === 404) throw new Error("gemini batch create failed: 404 (asyncBatchEmbedContent not available for this model/baseUrl). Disable remote.batch.enabled or switch providers.");
			throw new Error(`gemini batch create failed: ${batchRes.status} ${text}`);
		}
	});
}
async function fetchGeminiBatchStatus(params) {
	const statusUrl = `${normalizeBatchBaseUrl(params.gemini)}/${params.batchName.startsWith("batches/") ? params.batchName : `batches/${params.batchName}`}`;
	debugEmbeddingsLog("memory embeddings: gemini batch status", { statusUrl });
	return await withRemoteHttpResponse({
		url: statusUrl,
		ssrfPolicy: params.gemini.ssrfPolicy,
		init: { headers: buildBatchHeaders(params.gemini, { json: true }) },
		onResponse: async (res) => {
			if (!res.ok) throw await createProviderHttpError(res, "gemini batch status failed");
			return await res.json();
		}
	});
}
async function fetchGeminiFileContent(params) {
	const downloadUrl = `${normalizeBatchBaseUrl(params.gemini)}/${params.fileId.startsWith("files/") ? params.fileId : `files/${params.fileId}`}:download`;
	debugEmbeddingsLog("memory embeddings: gemini batch download", { downloadUrl });
	return await withRemoteHttpResponse({
		url: downloadUrl,
		ssrfPolicy: params.gemini.ssrfPolicy,
		init: { headers: buildBatchHeaders(params.gemini, { json: true }) },
		onResponse: async (res) => {
			if (!res.ok) throw await createProviderHttpError(res, "gemini batch file content failed");
			return await res.text();
		}
	});
}
function parseGeminiBatchOutput(text) {
	if (!text.trim()) return [];
	return normalizeStringEntries(text.split("\n")).map((line) => JSON.parse(line));
}
async function waitForGeminiBatch(params) {
	const start = Date.now();
	let current = params.initial;
	while (true) {
		const status = current ?? await fetchGeminiBatchStatus({
			gemini: params.gemini,
			batchName: params.batchName
		});
		const state = status.state ?? "UNKNOWN";
		if ([
			"SUCCEEDED",
			"COMPLETED",
			"DONE"
		].includes(state)) {
			const outputFileId = status.outputConfig?.file ?? status.outputConfig?.fileId ?? status.metadata?.output?.responsesFile;
			if (!outputFileId) throw new Error(`gemini batch ${params.batchName} completed without output file`);
			return { outputFileId };
		}
		if ([
			"FAILED",
			"CANCELLED",
			"CANCELED",
			"EXPIRED"
		].includes(state)) {
			const message = status.error?.message ?? "unknown error";
			throw new Error(`gemini batch ${params.batchName} ${state}: ${message}`);
		}
		if (!params.wait) throw new Error(`gemini batch ${params.batchName} still ${state}; wait disabled`);
		if (Date.now() - start > params.timeoutMs) throw new Error(`gemini batch ${params.batchName} timed out after ${params.timeoutMs}ms`);
		params.debug?.(`gemini batch ${params.batchName} ${state}; waiting ${params.pollIntervalMs}ms`);
		await new Promise((resolve) => {
			setTimeout(resolve, params.pollIntervalMs);
		});
		current = void 0;
	}
}
async function runGeminiEmbeddingBatches(params) {
	return await runEmbeddingBatchGroups({
		...buildEmbeddingBatchGroupOptions(params, {
			maxRequests: GEMINI_BATCH_MAX_REQUESTS,
			debugLabel: "memory embeddings: gemini batch submit"
		}),
		runGroup: async ({ group, groupIndex, groups, byCustomId, pollIntervalMs, timeoutMs }) => {
			const batchInfo = await submitGeminiBatch({
				gemini: params.gemini,
				requests: group,
				agentId: params.agentId
			});
			const batchName = batchInfo.name ?? "";
			if (!batchName) throw new Error("gemini batch create failed: missing batch name");
			params.debug?.("memory embeddings: gemini batch created", {
				batchName,
				state: batchInfo.state,
				group: groupIndex + 1,
				groups,
				requests: group.length
			});
			if (!params.wait && batchInfo.state && ![
				"SUCCEEDED",
				"COMPLETED",
				"DONE"
			].includes(batchInfo.state)) throw new Error(`gemini batch ${batchName} submitted; enable remote.batch.wait to await completion`);
			const completed = batchInfo.state && [
				"SUCCEEDED",
				"COMPLETED",
				"DONE"
			].includes(batchInfo.state) ? { outputFileId: batchInfo.outputConfig?.file ?? batchInfo.outputConfig?.fileId ?? batchInfo.metadata?.output?.responsesFile ?? "" } : await waitForGeminiBatch({
				gemini: params.gemini,
				batchName,
				wait: params.wait,
				pollIntervalMs,
				timeoutMs,
				debug: params.debug,
				initial: batchInfo
			});
			if (!completed.outputFileId) throw new Error(`gemini batch ${batchName} completed without output file`);
			const outputLines = parseGeminiBatchOutput(await fetchGeminiFileContent({
				gemini: params.gemini,
				fileId: completed.outputFileId
			}));
			const errors = [];
			const remaining = new Set(group.map((request) => request.custom_id));
			for (const line of outputLines) {
				const customId = line.key ?? line.custom_id ?? line.request_id;
				if (!customId) continue;
				remaining.delete(customId);
				if (line.error?.message) {
					errors.push(`${customId}: ${line.error.message}`);
					continue;
				}
				if (line.response?.error?.message) {
					errors.push(`${customId}: ${line.response.error.message}`);
					continue;
				}
				const embedding = sanitizeAndNormalizeEmbedding(line.embedding?.values ?? line.response?.embedding?.values ?? []);
				if (embedding.length === 0) {
					errors.push(`${customId}: empty embedding`);
					continue;
				}
				byCustomId.set(customId, embedding);
			}
			if (errors.length > 0) throw new Error(`gemini batch ${batchName} failed: ${errors.join("; ")}`);
			if (remaining.size > 0) throw new Error(`gemini batch ${batchName} missing ${remaining.size} embedding responses`);
		}
	});
}
//#endregion
export { runGeminiEmbeddingBatches as t };
