import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.js";
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { a as asOptionalRecord } from "./record-coerce-DItp3I4t.js";
import { m as normalizeResolvedSecretInputString } from "./types.secrets-Bre8L6Ts.js";
import { r as normalizeProviderId } from "./provider-id-DMd-TDFp.js";
import { l as readResponseTextPrefix } from "./http-body-i9LqiJFi.js";
import { y as ssrfPolicyFromHttpBaseUrlAllowedHostname } from "./ssrf-BQa-LkaB.js";
import { i as fetchWithSsrFGuard } from "./fetch-guard-DX8FZzRa.js";
import { f as readProviderJsonArrayFieldResponse } from "./provider-http-errors-EtYOKZpQ.js";
//#region packages/memory-host-sdk/src/host/embedding-vectors.ts
/** Validate provider embeddings and restore their original request order. */
function readEmbeddingVectors(data, expectedCount, errorPrefix) {
	const malformedResponse = () => /* @__PURE__ */ new Error(`${errorPrefix}: malformed JSON response`);
	if (!Array.isArray(data) || expectedCount !== void 0 && data.length !== expectedCount) throw malformedResponse();
	const vectors = [];
	let indexed;
	for (let position = 0; position < data.length; position += 1) {
		const entry = asOptionalRecord(data[position]);
		const embedding = entry?.embedding;
		const usesIndex = entry?.index !== void 0;
		if (!entry || !Array.isArray(embedding) || embedding.length === 0 || indexed !== void 0 && indexed !== usesIndex) throw malformedResponse();
		for (const coordinate of embedding) if (typeof coordinate !== "number" || !Number.isFinite(coordinate)) throw malformedResponse();
		indexed = usesIndex;
		const index = usesIndex ? entry.index : position;
		if (typeof index !== "number" || !Number.isInteger(index) || index < 0 || index >= data.length || vectors[index] !== void 0) throw malformedResponse();
		vectors[index] = embedding;
	}
	return vectors;
}
/** Replace invalid coordinates and L2-normalize non-empty vectors. */
function sanitizeAndNormalizeEmbedding(vec) {
	const sanitized = vec.map((value) => Number.isFinite(value) ? value : 0);
	const magnitude = Math.sqrt(sanitized.reduce((sum, value) => sum + value * value, 0));
	if (magnitude < 1e-10) return sanitized;
	return sanitized.map((value) => value / magnitude);
}
//#endregion
//#region src/plugins/openai-compatible-embedding-provider.ts
/** Provider id for OpenAI-compatible remote embedding servers. */
const OPENAI_COMPATIBLE_EMBEDDING_PROVIDER_ID = "openai-compatible";
const OPENAI_COMPATIBLE_MODEL_APIS = /* @__PURE__ */ new Set(["openai-completions", "openai-responses"]);
const EMBEDDING_ERROR_BODY_MAX_BYTES = 8 * 1024;
const EMBEDDING_ERROR_BODY_MAX_CHARS = 1e3;
const EMBEDDING_ERROR_TRUNCATED_SUFFIX = "... [truncated]";
function normalizeBaseUrl(value) {
	const baseUrl = value?.trim();
	if (!baseUrl) throw new Error("openai-compatible embeddings: missing remote.baseUrl. Set it to your OpenAI-compatible embeddings server, for example http://127.0.0.1:11434/v1.");
	return baseUrl.replace(/\/+$/u, "");
}
function normalizeModel(value, providerId) {
	const model = value?.trim();
	if (!model) throw new Error("openai-compatible embeddings: missing model. Set it to the embedding model id your server expects.");
	const prefixes = new Set([
		providerId?.trim(),
		normalizeProviderId(providerId ?? ""),
		OPENAI_COMPATIBLE_EMBEDDING_PROVIDER_ID
	].filter((prefix) => Boolean(prefix)).map((prefix) => `${prefix}/`));
	for (const prefix of prefixes) if (model.startsWith(prefix)) return model.slice(prefix.length);
	return model;
}
function normalizeDimensions(value) {
	if (value === void 0) return;
	if (!Number.isInteger(value) || value <= 0) throw new Error("openai-compatible embeddings: dimensions must be a positive integer.");
	return value;
}
function normalizeOptionalInputType(value) {
	const inputType = value?.trim();
	return inputType ? inputType : void 0;
}
function resolveRequestInputType(client, kind) {
	if (kind === "query") return client.queryInputType ?? client.inputType;
	if (kind === "document") return client.documentInputType ?? client.inputType;
	return client.inputType;
}
function normalizeHeaderName(name) {
	return name.trim().toLowerCase();
}
function buildHeaders(params) {
	const headers = {
		accept: "application/json",
		"content-type": "application/json"
	};
	for (const [path, extra] of [["models.providers.*.headers", params.provider], ["memory.search.remote.headers", params.remote]]) for (const [name, rawValue] of Object.entries(extra ?? {})) {
		const normalizedName = normalizeHeaderName(name);
		if (!normalizedName) continue;
		const value = resolveSecretString({
			value: rawValue,
			path: `${path}.${normalizedName}`
		});
		if (value) headers[normalizedName] = value;
	}
	if (params.apiKey && !headers.authorization) headers.authorization = `Bearer ${params.apiKey}`;
	return headers;
}
function isSensitiveHeaderName(name) {
	return name === "authorization" || name === "proxy-authorization" || name.includes("api-key") || name.includes("token") || name.includes("secret");
}
function sanitizeCacheHeaders(headers) {
	const safeHeaders = Object.fromEntries(Object.entries(headers).filter(([name]) => !isSensitiveHeaderName(name)));
	return Object.keys(safeHeaders).length > 0 ? safeHeaders : void 0;
}
function resolveSecretString(params) {
	return normalizeResolvedSecretInputString({
		value: params.value,
		path: params.path
	});
}
function resolveRemoteApiKey(value) {
	return resolveSecretString({
		value,
		path: "memory.search.remote.apiKey"
	});
}
function isOpenAICompatibleProviderConfig(id, provider) {
	return normalizeProviderId(id) === OPENAI_COMPATIBLE_EMBEDDING_PROVIDER_ID || OPENAI_COMPATIBLE_MODEL_APIS.has(normalizeProviderId(provider.api ?? "")) || !provider.api && typeof provider.baseUrl === "string" && provider.baseUrl.trim().length > 0;
}
function resolveConfiguredProvider(options) {
	const providers = options.config.models?.providers;
	if (!providers) return;
	const providerId = options.provider?.trim() || OPENAI_COMPATIBLE_EMBEDDING_PROVIDER_ID;
	const normalizedProviderId = normalizeProviderId(providerId);
	const direct = providers[providerId];
	if (direct && isOpenAICompatibleProviderConfig(providerId, direct)) return {
		providerId,
		config: direct
	};
	const normalizedEntry = Object.entries(providers).find(([candidateId]) => normalizeProviderId(candidateId) === normalizedProviderId);
	if (!normalizedEntry) return;
	const [configuredProviderId, config] = normalizedEntry;
	return isOpenAICompatibleProviderConfig(configuredProviderId, config) ? {
		providerId: configuredProviderId,
		config
	} : void 0;
}
function embeddingInputToText(input) {
	if (typeof input === "string") return input;
	if (!input.parts || input.parts.length === 0) return input.text;
	const textParts = [];
	for (const part of input.parts) {
		if (part.type !== "text") throw new Error("openai-compatible embeddings only support text embedding inputs.");
		textParts.push(part.text);
	}
	return textParts.join("");
}
function malformedEmbeddingResponse() {
	return /* @__PURE__ */ new Error("openai-compatible embeddings failed: malformed JSON response");
}
async function readEmbeddingErrorBodySnippet(response) {
	if (!response.body || response.bodyUsed) return;
	const prefix = await readResponseTextPrefix(response, EMBEDDING_ERROR_BODY_MAX_BYTES).catch(() => void 0);
	if (!prefix?.text) return;
	const { text, truncated } = prefix;
	if (text.length > EMBEDDING_ERROR_BODY_MAX_CHARS) return `${truncateUtf16Safe(text, EMBEDDING_ERROR_BODY_MAX_CHARS)}${EMBEDDING_ERROR_TRUNCATED_SUFFIX}`;
	return truncated ? `${text}${EMBEDDING_ERROR_TRUNCATED_SUFFIX}` : text;
}
async function createEmbeddingHttpError(response) {
	const snippet = await readEmbeddingErrorBodySnippet(response);
	return /* @__PURE__ */ new Error(`openai-compatible embeddings failed: HTTP ${response.status}${snippet ? `: ${snippet}` : ""}`);
}
async function postEmbeddingRequest(params) {
	const { client, input } = params;
	const inputType = resolveRequestInputType(client, params.inputType);
	const body = {
		model: client.model,
		input,
		...typeof client.dimensions === "number" ? { dimensions: client.dimensions } : {},
		...inputType ? { input_type: inputType } : {}
	};
	const localServiceLease = client.localServiceTarget && client.acquireLocalService ? await client.acquireLocalService(client.localServiceTarget, params.signal) : void 0;
	try {
		const { response, release } = await fetchWithSsrFGuard({
			url: client.endpointUrl,
			init: {
				method: "POST",
				headers: client.headers,
				body: JSON.stringify(body)
			},
			signal: params.signal,
			policy: client.ssrfPolicy,
			auditContext: "embedding-provider:openai-compatible"
		});
		try {
			if (!response.ok) throw await createEmbeddingHttpError(response);
			return readEmbeddingVectors(await readProviderJsonArrayFieldResponse(response, "openai-compatible embeddings failed", "data"), input.length, "openai-compatible embeddings failed");
		} finally {
			await release();
		}
	} finally {
		localServiceLease?.release();
	}
}
/** Creates a normalized OpenAI-compatible embedding client from runtime config. */
async function createOpenAICompatibleEmbeddingClient(options) {
	const resolvedProvider = resolveConfiguredProvider(options);
	const configuredProvider = resolvedProvider?.config;
	const providerId = resolvedProvider?.providerId ?? options.provider?.trim() ?? OPENAI_COMPATIBLE_EMBEDDING_PROVIDER_ID;
	const remoteBaseUrl = normalizeOptionalString(options.remote?.baseUrl);
	const providerBaseUrl = normalizeOptionalString(configuredProvider?.baseUrl);
	const baseUrl = normalizeBaseUrl(remoteBaseUrl ?? providerBaseUrl);
	const { embeddingProviderOwnsDestination, resolveEmbeddingEndpointUrl } = await import("./plugin-sdk/memory-core-host-engine-embeddings.js");
	const providerOwnsDestination = providerBaseUrl !== void 0 && embeddingProviderOwnsDestination({
		baseUrl,
		providerBaseUrl
	});
	const model = normalizeModel(options.model, options.provider);
	const inputType = normalizeOptionalInputType(options.inputType);
	const queryInputType = normalizeOptionalInputType(options.queryInputType);
	const documentInputType = normalizeOptionalInputType(options.documentInputType);
	const headers = buildHeaders({
		apiKey: resolveRemoteApiKey(options.remote?.apiKey),
		provider: providerOwnsDestination ? configuredProvider?.headers : void 0,
		remote: options.remote?.headers
	});
	if (providerOwnsDestination && !headers.authorization) {
		const providerApiKey = resolveSecretString({
			value: configuredProvider?.apiKey,
			path: `models.providers.${providerId}.apiKey`
		});
		if (providerApiKey) headers.authorization = `Bearer ${providerApiKey}`;
	}
	const localServiceOptions = options;
	return {
		providerId,
		baseUrl,
		endpointUrl: resolveEmbeddingEndpointUrl(baseUrl, "embeddings"),
		headers,
		ssrfPolicy: ssrfPolicyFromHttpBaseUrlAllowedHostname(baseUrl),
		model,
		...configuredProvider?.localService && !remoteBaseUrl ? {
			localServiceTarget: {
				providerId,
				baseUrl,
				headers
			},
			acquireLocalService: localServiceOptions.acquireLocalService
		} : {},
		...options.dimensions !== void 0 ? { dimensions: normalizeDimensions(options.dimensions) } : {},
		...inputType ? { inputType } : {},
		...queryInputType ? { queryInputType } : {},
		...documentInputType ? { documentInputType } : {}
	};
}
/** Creates an OpenAI-compatible embedding provider and its backing client. */
async function createOpenAICompatibleEmbeddingProvider(options) {
	const client = await createOpenAICompatibleEmbeddingClient(options);
	const embedBatch = async (inputs, callOptions) => {
		if (inputs.length === 0) return [];
		return await postEmbeddingRequest({
			client,
			input: inputs.map(embeddingInputToText),
			signal: callOptions?.signal,
			inputType: callOptions?.inputType
		});
	};
	return {
		provider: {
			id: OPENAI_COMPATIBLE_EMBEDDING_PROVIDER_ID,
			model: client.model,
			...typeof client.dimensions === "number" ? { dimensions: client.dimensions } : {},
			embed: async (input, callOptions) => {
				const [embedding] = await embedBatch([input], callOptions);
				if (!embedding) throw malformedEmbeddingResponse();
				return embedding;
			},
			embedBatch
		},
		client
	};
}
//#endregion
//#region src/plugins/core-embedding-providers.ts
const CORE_EMBEDDING_PROVIDERS = [{
	adapter: {
		id: OPENAI_COMPATIBLE_EMBEDDING_PROVIDER_ID,
		transport: "remote",
		create: async (options) => {
			const { provider, client } = await createOpenAICompatibleEmbeddingProvider(options);
			const cacheHeaders = sanitizeCacheHeaders(client.headers);
			return {
				provider,
				runtime: {
					id: OPENAI_COMPATIBLE_EMBEDDING_PROVIDER_ID,
					inlineBatchTimeoutMs: 10 * 6e4,
					cacheKeyData: {
						provider: client.providerId,
						baseUrl: client.baseUrl,
						model: client.model,
						...typeof client.dimensions === "number" ? { dimensions: client.dimensions } : {},
						...client.inputType ? { inputType: client.inputType } : {},
						...client.queryInputType ? { queryInputType: client.queryInputType } : {},
						...client.documentInputType ? { documentInputType: client.documentInputType } : {},
						...cacheHeaders ? { headers: cacheHeaders } : {}
					}
				}
			};
		}
	},
	ownerPluginId: "core"
}];
function getCoreEmbeddingProvider(id) {
	return CORE_EMBEDDING_PROVIDERS.find((entry) => entry.adapter.id === id);
}
//#endregion
export { sanitizeAndNormalizeEmbedding as i, getCoreEmbeddingProvider as n, readEmbeddingVectors as r, CORE_EMBEDDING_PROVIDERS as t };
