import { createHash } from "crypto"; export type EmbeddingTaskType = | "SEMANTIC_SIMILARITY" | "CLASSIFICATION" | "CLUSTERING" | "RETRIEVAL_DOCUMENT" | "RETRIEVAL_QUERY" | "CODE_RETRIEVAL_QUERY" | "QUESTION_ANSWERING" | "FACT_VERIFICATION"; export interface TextEmbeddingInput { type: "text"; text: string; } export interface ImageEmbeddingInput { type: "image"; data: Buffer; mimeType: string; // "image/png" | "image/jpeg" } export interface AudioEmbeddingInput { type: "audio"; data: Buffer; mimeType: string; // "audio/mp3" | "audio/wav" } export interface VideoEmbeddingInput { type: "video"; data: Buffer; mimeType: string; // "video/mp4" | "video/mov" } export type MultimodalEmbeddingInput = | TextEmbeddingInput | ImageEmbeddingInput | AudioEmbeddingInput | VideoEmbeddingInput; /** Accepts raw strings as shorthand for text inputs. */ export type EmbeddingInput = string | MultimodalEmbeddingInput; export function normalizeEmbeddingInput( input: EmbeddingInput, ): MultimodalEmbeddingInput { if (typeof input === "string") { return { type: "text", text: input }; } return input; } /** Sparse vector representation: parallel arrays of term indices and weights. */ export interface SparseEmbedding { indices: number[]; values: number[]; } export function embeddingInputContentHash(input: EmbeddingInput): string { const normalized = normalizeEmbeddingInput(input); const hash = createHash("sha256"); hash.update(normalized.type); if (normalized.type === "text") { hash.update(normalized.text); } else { hash.update(normalized.mimeType); hash.update(normalized.data); } return hash.digest("hex"); } // --------------------------------------------------------------------------- // Backend interface types (extracted from embedding-backend.ts to break // circular imports between the factory and provider implementations) // --------------------------------------------------------------------------- export type EmbeddingProviderName = "local" | "openai" | "gemini" | "ollama"; export interface EmbeddingRequestOptions { signal?: AbortSignal; } export interface EmbeddingBackend { readonly provider: EmbeddingProviderName; readonly model: string; embed( inputs: EmbeddingInput[], options?: EmbeddingRequestOptions, ): Promise; dispose?(): void; /** * Deterministic teardown for daemon shutdown: release any OS resources the * backend owns (e.g. a worker subprocess) and resolve only once they are * confirmed gone. Unlike {@link dispose}, which may defer while embeds are * in flight, this must not leave a child process behind. */ shutdown?(): Promise; /** * Release owned OS resources synchronously, for a process that must exit this * tick. Unlike {@link shutdown} it cannot wait for confirmation, so it uses * the uncatchable signal rather than the graceful one. */ terminateNow?(): void; /** * Reap any owned child still attributable to this process, without needing a * handle for it. Backstop for a teardown that could not resolve its handle in * time. */ sweepOwnedWorkers?(): Promise; } /** * Fixed text embedded to measure a backend's output dimension. Shared so the * startup reconcile probe and the per-query availability check agree on the * input (and therefore the in-memory vector cache identity). */ export const EMBEDDING_DIMENSION_PROBE_TEXT = "embedding dimension probe";