{"version":3,"file":"embedding-gate-Cc5UQcO7.mjs","names":[],"sources":["../src/memory/embedding-binding.ts","../src/memory/backends/transformers-cpu.ts","../src/memory/backends/ollama.ts","../src/memory/backends/ollama-provision.ts","../src/memory/backends/index.ts","../src/memory/embedding-gate.ts"],"sourcesContent":["/**\n * Index ↔ backend binding rules. Dependency-free so the storage layer can\n * import it.\n */\n\nimport type { EmbeddingBinding } from \"../storage/interface.js\";\n\n/**\n * What an index holding vectors but no recorded binding is: it predates\n * binding, so its vectors are transformers-cpu-q8. Model and dims are unknown\n * (\"\" / 0 = wildcard) and are filled in on the first embed that adopts it.\n */\nexport const LEGACY_EMBEDDING_BINDING: EmbeddingBinding = {\n  backend: \"transformers-cpu-q8\",\n  model: \"\",\n  dims: 0,\n};\n\nexport function isInferredBinding(b: EmbeddingBinding): boolean {\n  return b.model === \"\";\n}\n\nexport function bindingMatches(b: EmbeddingBinding, cur: { id: string; model: string; dims: number }): boolean {\n  return (\n    b.backend === cur.id &&\n    (b.model === \"\" || b.model === cur.model) &&\n    (b.dims === 0 || b.dims === cur.dims)\n  );\n}\n\nexport function describeBinding(b: { backend: string; model: string }): string {\n  return b.model ? `${b.backend} (${b.model})` : b.backend;\n}\n\n/** The hint shown wherever a binding mismatch pauses embedding (CLI, gate, dashboard). */\nexport function mismatchReason(b: EmbeddingBinding, cur: { id: string; model: string }): string {\n  return `index embedded with ${describeBinding(b)}, configured ${describeBinding({ backend: cur.id, model: cur.model })}: run \\`pai memory reembed\\` to switch`;\n}\n","/** In-process transformers.js, q8, CPU. The original backend; vectors unchanged. */\n\nimport type { EmbeddingBackend } from \"./types.js\";\nimport { EMBEDDING_DIM, configureEmbeddingModel, generateEmbeddings } from \"../embeddings.js\";\n\nexport const TRANSFORMERS_ID = \"transformers-cpu-q8\";\n\nexport function createTransformersBackend(model: string): EmbeddingBackend {\n  return {\n    id: TRANSFORMERS_ID,\n    model,\n    dims: EMBEDDING_DIM,\n    maxTokens: 512,\n    async available() {\n      return { ok: true, reason: \"in-process CPU (q8), no server needed\" };\n    },\n    async embed(texts) {\n      configureEmbeddingModel(model);\n      return generateEmbeddings(texts);\n    },\n  };\n}\n","/**\n * Ollama backend: POST {baseUrl}/api/embed, Metal GPU. Measured ~51 chunks/s\n * with an F16 GGUF of arctic-embed-m-v1.5 (vs ~8 for CPU q8), cosine 0.99999\n * against fp32 for inputs up to 510 tokens.\n *\n * Ollama cuts inputs over 510 real tokens differently from the model's own\n * truncation (cos ~0.45 vs fp32), so every text is cut to 510 tokens here\n * with the model's tokenizer before it is sent. Chunk boundaries in the\n * database are not touched.\n */\n\nimport type { EmbeddingBackend } from \"./types.js\";\n\nexport const OLLAMA_ID = \"ollama-f16\";\nexport const OLLAMA_DEFAULT_MODEL = \"arctic-embed-m-v1.5-f16\";\nexport const OLLAMA_DEFAULT_URL = \"http://127.0.0.1:11434\";\nexport const TOKENIZER_MODEL = \"Snowflake/snowflake-arctic-embed-m-v1.5\";\n\nconst BATCH = 64;\nconst MAX_CONTENT_TOKENS = 510; // 512 minus [CLS] and [SEP]\n\nexport interface Tokenizer {\n  encode(text: string): number[];\n  decode(ids: number[]): string;\n}\n\nexport interface OllamaDeps {\n  fetch?: typeof fetch;\n  tokenizer?: () => Promise<Tokenizer>;\n  log?: (msg: string) => void;\n}\n\nasync function loadTokenizer(): Promise<Tokenizer> {\n  const { AutoTokenizer } = await import(\"@huggingface/transformers\");\n  const tok = await AutoTokenizer.from_pretrained(TOKENIZER_MODEL);\n  return {\n    encode: (t) => tok.encode(t) as number[],\n    decode: (ids) => tok.decode(ids, { skip_special_tokens: true }),\n  };\n}\n\n/**\n * Cut `text` to at most 510 content tokens (512 with specials). Returns the\n * text unchanged when it already fits.\n */\nexport function truncateToTokens(text: string, tok: Tokenizer): { text: string; truncated: boolean } {\n  // A token is at least one character, so a short text cannot exceed the cap.\n  if (text.length <= MAX_CONTENT_TOKENS) return { text, truncated: false };\n  const ids = tok.encode(text);\n  if (ids.length <= MAX_CONTENT_TOKENS + 2) return { text, truncated: false };\n  let keep = MAX_CONTENT_TOKENS;\n  let cut: string;\n  // decode+re-encode can shift the count by a few tokens; shrink until it fits.\n  do {\n    cut = tok.decode(ids.slice(1, 1 + keep));\n    keep -= 5;\n  } while (tok.encode(cut).length > MAX_CONTENT_TOKENS + 2 && keep > 0);\n  return { text: cut, truncated: true };\n}\n\nexport type OllamaState = \"ready\" | \"model-missing\" | \"server-down\";\n\nexport async function probeOllama(\n  baseUrl: string,\n  model: string,\n  f: typeof fetch = fetch,\n): Promise<{ state: OllamaState; detail: string }> {\n  let names: string[];\n  try {\n    const r = await f(`${baseUrl}/api/tags`, { signal: AbortSignal.timeout(2000) });\n    if (!r.ok) return { state: \"server-down\", detail: `${baseUrl} answered HTTP ${r.status}` };\n    names = ((await r.json()) as { models?: Array<{ name: string }> }).models?.map((m) => m.name) ?? [];\n  } catch {\n    return { state: \"server-down\", detail: `no Ollama server at ${baseUrl}` };\n  }\n  const present = names.some((n) => n === model || n === `${model}:latest`);\n  return present\n    ? { state: \"ready\", detail: `server at ${baseUrl}, model ${model} present` }\n    : { state: \"model-missing\", detail: `server at ${baseUrl} reachable, model ${model} not present` };\n}\n\nexport function createOllamaBackend(\n  opts: { baseUrl?: string; model?: string } = {},\n  deps: OllamaDeps = {},\n): EmbeddingBackend {\n  const baseUrl = (opts.baseUrl ?? OLLAMA_DEFAULT_URL).replace(/\\/+$/, \"\");\n  const model = opts.model ?? OLLAMA_DEFAULT_MODEL;\n  const f = deps.fetch ?? fetch;\n  const log = deps.log ?? ((m: string) => process.stderr.write(`${m}\\n`));\n  const getTok = deps.tokenizer ?? loadTokenizer;\n  let tok: Tokenizer | undefined;\n  const dims = 768;\n\n  async function post(input: string[]): Promise<number[][]> {\n    let r: Response;\n    try {\n      r = await f(`${baseUrl}/api/embed`, {\n        method: \"POST\",\n        headers: { \"content-type\": \"application/json\" },\n        body: JSON.stringify({ model, input, keep_alive: \"10m\" }),\n      });\n    } catch (e) {\n      // Surface the socket code so retryTransient treats a down server as transient.\n      const code = (e as { cause?: { code?: string } }).cause?.code ?? \"ECONNREFUSED\";\n      throw Object.assign(new Error(`Ollama unreachable at ${baseUrl} (${code})`), { code });\n    }\n    if (!r.ok) throw new Error(`Ollama /api/embed HTTP ${r.status}: ${(await r.text()).slice(0, 200)}`);\n    const body = (await r.json()) as { embeddings?: number[][] };\n    if (!body.embeddings || body.embeddings.length !== input.length) {\n      throw new Error(`Ollama returned ${body.embeddings?.length ?? 0} embeddings for ${input.length} inputs`);\n    }\n    return body.embeddings;\n  }\n\n  return {\n    id: OLLAMA_ID,\n    model,\n    dims,\n    maxTokens: 512,\n    async available() {\n      const p = await probeOllama(baseUrl, model, f);\n      return { ok: p.state === \"ready\", reason: p.detail };\n    },\n    async embed(texts) {\n      if (texts.length === 0) return [];\n      tok ??= await getTok();\n      let cutCount = 0;\n      const prepared = texts.map((t) => {\n        const r = truncateToTokens(t, tok!);\n        if (r.truncated) cutCount++;\n        return r.text;\n      });\n      if (cutCount > 0) log(`[pai-embed] ollama: truncated ${cutCount}/${texts.length} texts to ${MAX_CONTENT_TOKENS} tokens`);\n\n      const out: Float32Array[] = [];\n      for (let i = 0; i < prepared.length; i += BATCH) {\n        for (const row of await post(prepared.slice(i, i + BATCH))) {\n          if (row.length !== dims) throw new Error(`Ollama model ${model} returned ${row.length}-dim vectors, expected ${dims}`);\n          const v = Float32Array.from(row);\n          let n = 0;\n          for (let k = 0; k < v.length; k++) n += v[k] * v[k];\n          if (Math.abs(Math.sqrt(n) - 1) > 1e-3) throw new Error(`Ollama model ${model} returned non-normalized vectors (|v|=${Math.sqrt(n).toFixed(4)})`);\n          out.push(v);\n        }\n      }\n      return out;\n    },\n  };\n}\n","/**\n * Provision the F16 GGUF of arctic-embed-m-v1.5 into a local Ollama.\n * `ollama pull hf.co/...` fails on a Xet redirect, so the file is fetched\n * directly and imported with `ollama create`. No sudo; idempotent.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { createReadStream, createWriteStream, existsSync, mkdirSync, statSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport { homedir } from \"node:os\";\nimport { probeOllama } from \"./ollama.js\";\n\nexport const GGUF_URL =\n  \"https://huggingface.co/yixuan-chia/snowflake-arctic-embed-m-v1.5-GGUF/resolve/main/snowflake-arctic-embed-m-v1.5-F16.gguf\";\nexport const GGUF_SIZE = 219454752;\nexport const GGUF_SHA256 = \"ece140d563b01d1e0cdbf836d5ac002c0a1e5bb254540177f4f4c4ae0793853a\";\n\nexport function ollamaBinaryPath(): string | null {\n  for (const dir of (process.env.PATH ?? \"\").split(\":\").concat(\"/opt/homebrew/bin\", \"/usr/local/bin\")) {\n    if (dir && existsSync(join(dir, \"ollama\"))) return join(dir, \"ollama\");\n  }\n  return null;\n}\n\nasync function sha256File(path: string): Promise<string> {\n  const h = createHash(\"sha256\");\n  await pipeline(createReadStream(path), h);\n  return h.digest(\"hex\");\n}\n\nexport async function provisionOllama(opts: {\n  baseUrl: string;\n  model: string;\n  /** Scratch dir for the download; default ~/.cache/pai/ollama. */\n  workDir?: string;\n  log?: (m: string) => void;\n}): Promise<{ created: boolean }> {\n  const log = opts.log ?? ((m) => console.log(m));\n  const bin = ollamaBinaryPath();\n  if (!bin) throw new Error(\"ollama is not installed (brew install ollama)\");\n\n  const probe = await probeOllama(opts.baseUrl, opts.model);\n  if (probe.state === \"ready\") {\n    log(`Model ${opts.model} already present; nothing to do.`);\n    return { created: false };\n  }\n  if (probe.state === \"server-down\") throw new Error(`${probe.detail}; start it with \\`ollama serve\\``);\n\n  const dir = opts.workDir ?? join(homedir(), \".cache\", \"pai\", \"ollama\");\n  mkdirSync(dir, { recursive: true });\n  const gguf = join(dir, \"snowflake-arctic-embed-m-v1.5-F16.gguf\");\n\n  if (!(existsSync(gguf) && statSync(gguf).size === GGUF_SIZE)) {\n    log(`Downloading ${GGUF_URL} (${Math.round(GGUF_SIZE / 1e6)} MB)...`);\n    const res = await fetch(GGUF_URL, { redirect: \"follow\" });\n    if (!res.ok || !res.body) throw new Error(`Download failed: HTTP ${res.status}`);\n    await pipeline(Readable.fromWeb(res.body as never), createWriteStream(gguf));\n  }\n  const size = statSync(gguf).size;\n  if (size !== GGUF_SIZE) throw new Error(`Downloaded ${size} bytes, expected ${GGUF_SIZE}; delete ${gguf} and retry`);\n  const sha = await sha256File(gguf);\n  if (sha !== GGUF_SHA256) throw new Error(`Checksum mismatch for ${gguf}: ${sha}`);\n\n  const modelfile = join(dir, \"Modelfile\");\n  writeFileSync(modelfile, `FROM ${gguf}\\n`);\n  log(`Importing as ${opts.model} (ollama create)...`);\n  execFileSync(bin, [\"create\", opts.model, \"-f\", modelfile], { stdio: \"inherit\", env: { ...process.env, OLLAMA_HOST: opts.baseUrl } });\n  return { created: true };\n}\n","/**\n * Backend registry: config → backend, plus discovery.\n *\n * Detection order is speed order: ollama (Metal GPU) first, then\n * transformers-cpu. Add an \"http\" backend (e.g. MLX server) by implementing\n * EmbeddingBackend and adding a case to createBackend() and a probe to\n * detectBackends().\n */\n\nimport { loadConfig, type PaiDaemonConfig } from \"../../daemon/config.js\";\nimport type { EmbeddingBackend } from \"./types.js\";\nimport { TRANSFORMERS_ID, createTransformersBackend } from \"./transformers-cpu.js\";\nimport { OLLAMA_ID, OLLAMA_DEFAULT_MODEL, OLLAMA_DEFAULT_URL, createOllamaBackend, probeOllama } from \"./ollama.js\";\nimport { ollamaBinaryPath } from \"./ollama-provision.js\";\n\nexport const BACKEND_IDS = [OLLAMA_ID, TRANSFORMERS_ID] as const;\nexport { OLLAMA_ID, TRANSFORMERS_ID, OLLAMA_DEFAULT_MODEL, OLLAMA_DEFAULT_URL };\n\nexport function createBackend(\n  id: string,\n  cfg: Pick<PaiDaemonConfig, \"embedding\" | \"embeddingModel\"> = loadConfig(),\n  modelOverride?: string,\n): EmbeddingBackend {\n  switch (id) {\n    case OLLAMA_ID:\n      return createOllamaBackend({\n        baseUrl: cfg.embedding?.ollama?.baseUrl,\n        model: modelOverride ?? cfg.embedding?.model ?? OLLAMA_DEFAULT_MODEL,\n      });\n    case TRANSFORMERS_ID:\n      return createTransformersBackend(cfg.embeddingModel || \"Snowflake/snowflake-arctic-embed-m-v1.5\");\n    default:\n      throw new Error(`Unknown embedding backend \"${id}\". Known: ${BACKEND_IDS.join(\", \")}`);\n  }\n}\n\nexport function getConfiguredBackend(cfg: PaiDaemonConfig = loadConfig()): EmbeddingBackend {\n  return createBackend(cfg.embedding?.backend || TRANSFORMERS_ID, cfg);\n}\n\nexport interface Detection {\n  id: string;\n  ok: boolean;\n  reason: string;\n  /** Model missing on a reachable server: `pai memory backend provision ollama` fixes it. */\n  provisionable?: boolean;\n}\n\nexport async function detectBackends(\n  cfg: Pick<PaiDaemonConfig, \"embedding\" | \"embeddingModel\"> = loadConfig(),\n  deps: { fetch?: typeof fetch; ollamaBinary?: () => string | null } = {},\n): Promise<{ results: Detection[]; recommended: string | null }> {\n  const ollama = createBackend(OLLAMA_ID, cfg) as ReturnType<typeof createOllamaBackend>;\n  const baseUrl = cfg.embedding?.ollama?.baseUrl ?? OLLAMA_DEFAULT_URL;\n  const p = await probeOllama(baseUrl, ollama.model, deps.fetch);\n  const results: Detection[] = [];\n  if (p.state === \"ready\") results.push({ id: OLLAMA_ID, ok: true, reason: p.detail });\n  else if (p.state === \"model-missing\") {\n    results.push({ id: OLLAMA_ID, ok: false, provisionable: true, reason: `${p.detail}; run \\`pai memory backend provision ollama\\`` });\n  } else if ((deps.ollamaBinary ?? ollamaBinaryPath)()) {\n    results.push({ id: OLLAMA_ID, ok: false, reason: `${p.detail}; ollama is installed, start it with \\`ollama serve\\`` });\n  } else {\n    results.push({ id: OLLAMA_ID, ok: false, reason: `${p.detail}; ollama is not installed` });\n  }\n\n  const t = await createBackend(TRANSFORMERS_ID, cfg).available();\n  results.push({ id: TRANSFORMERS_ID, ...t });\n\n  return { results, recommended: results.find((r) => r.ok)?.id ?? null };\n}\n","/**\n * The single entry point for embedding against an index: the configured\n * backend, checked against the backend the index's vectors came from.\n *\n * Mismatch or an unavailable backend never falls back to another backend:\n * embedding pauses (chunks stay NULL) and queries go keyword-only with a note.\n */\n\nimport type { StorageBackend } from \"../storage/interface.js\";\nimport type { EmbeddingBackend } from \"./backends/types.js\";\nimport { getConfiguredBackend } from \"./backends/index.js\";\nimport { bindingMatches, isInferredBinding, mismatchReason } from \"./embedding-binding.js\";\n\nexport const QUERY_PREFIX = \"Represent this sentence for searching relevant passages: \";\n\nexport type Gate =\n  | { ok: true; backend: EmbeddingBackend }\n  | { ok: false; kind: \"mismatch\" | \"unavailable\"; reason: string };\n\n/**\n * @param record  Writing vectors: record (or complete) the index binding. Pass\n *                false for queries, which must not touch the binding.\n */\nexport async function gateBackend(\n  storage: StorageBackend,\n  opts: { record: boolean; backend?: EmbeddingBackend },\n): Promise<Gate> {\n  const backend = opts.backend ?? getConfiguredBackend();\n  const binding = await storage.getEmbeddingBinding();\n\n  if (binding && !bindingMatches(binding, backend)) {\n    return {\n      ok: false,\n      kind: \"mismatch\",\n      reason: mismatchReason(binding, backend),\n    };\n  }\n  const avail = await backend.available();\n  if (!avail.ok) {\n    return { ok: false, kind: \"unavailable\", reason: `embedding backend ${backend.id} unavailable: ${avail.reason}` };\n  }\n  if (opts.record && (!binding || isInferredBinding(binding))) {\n    await storage.setEmbeddingBinding({ backend: backend.id, model: backend.model, dims: backend.dims });\n  }\n  return { ok: true, backend };\n}\n\n/** Query embedding, or null plus a note to show next to the keyword-only results. */\nexport async function embedQuery(\n  storage: StorageBackend,\n  text: string,\n  backend?: EmbeddingBackend,\n): Promise<{ vec: Float32Array; note?: undefined } | { vec: null; note: string }> {\n  const gate = await gateBackend(storage, { record: false, backend });\n  if (!gate.ok) return { vec: null, note: `${gate.reason}. Results are keyword-only.` };\n  try {\n    const [vec] = await gate.backend.embed([QUERY_PREFIX + text]);\n    return { vec };\n  } catch (e) {\n    return { vec: null, note: `query embedding failed (${e instanceof Error ? e.message : String(e)}). Results are keyword-only.` };\n  }\n}\n\n/** For callers with no keyword fallback (zettelkasten tools): throws the note. */\nexport async function requireQueryEmbedding(storage: StorageBackend, text: string): Promise<Float32Array> {\n  const r = await embedQuery(storage, text);\n  if (!r.vec) throw new Error(r.note);\n  return r.vec;\n}\n\n/** Cheap throughput probe for ETA: embeds `texts` and returns chunks/s. */\nexport async function measureThroughput(backend: EmbeddingBackend, texts: string[]): Promise<number> {\n  if (texts.length === 0) return 0;\n  await backend.embed(texts.slice(0, 1)); // warm: model load\n  const t0 = performance.now();\n  await backend.embed(texts);\n  return texts.length / ((performance.now() - t0) / 1000);\n}\n"],"mappings":"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"}