@tekmidian/pai 0.66.7 → 0.66.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{aibroker-client-BevXY1Zz.mjs → aibroker-client-BfnVwGZl.mjs} +3 -3
- package/dist/{aibroker-client-BevXY1Zz.mjs.map → aibroker-client-BfnVwGZl.mjs.map} +1 -1
- package/dist/{aibroker-client-CxTpL_u7.mjs → aibroker-client-C-O3Fflh.mjs} +1 -1
- package/dist/{auto-route-BVeAp0fl.mjs → auto-route-D8TBDNqb.mjs} +3 -3
- package/dist/{auto-route-BVeAp0fl.mjs.map → auto-route-D8TBDNqb.mjs.map} +1 -1
- package/dist/auto-route-DCxTKz19.mjs +7 -0
- package/dist/{chain-8hFKr23C.mjs → chain-BL4khGgy.mjs} +4 -3
- package/dist/{chain-8hFKr23C.mjs.map → chain-BL4khGgy.mjs.map} +1 -1
- package/dist/cli/index.mjs +20 -17
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/program.mjs +19 -16
- package/dist/{config-sfaArCsl.mjs → config-DjZELvoA.mjs} +2 -2
- package/dist/{config-sfaArCsl.mjs.map → config-DjZELvoA.mjs.map} +1 -1
- package/dist/{run-env-CkAmx3lb.mjs → config-Dpe2JlbY.mjs} +5 -114
- package/dist/config-Dpe2JlbY.mjs.map +1 -0
- package/dist/{config-UWtzLpXi.mjs → config-yomayAwe.mjs} +1 -1
- package/dist/{context-handover-cache-Bb-H_mTa.mjs → context-handover-cache-DFqxNh-H.mjs} +27 -12
- package/dist/context-handover-cache-DFqxNh-H.mjs.map +1 -0
- package/dist/daemon/index.mjs +20 -16
- package/dist/daemon/index.mjs.map +1 -1
- package/dist/{daemon-CkbX-hV7.mjs → daemon-CsR1S_Qr.mjs} +20 -19
- package/dist/{daemon-CkbX-hV7.mjs.map → daemon-CsR1S_Qr.mjs.map} +1 -1
- package/dist/daemon-WrncnGua.mjs +23 -0
- package/dist/daemon-mcp/index.mjs +15 -13
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/{embedding-gate-BVrr0BgS.mjs → embedding-gate-DqbKvn56.mjs} +4 -4
- package/dist/{embedding-gate-BVrr0BgS.mjs.map → embedding-gate-DqbKvn56.mjs.map} +1 -1
- package/dist/{embedding-gate-Crj8awk7.mjs → embedding-gate-SJMCPwxG.mjs} +3 -3
- package/dist/{embeddings-DFl88_y-.mjs → embeddings-BUjZMD9F.mjs} +1 -1
- package/dist/{embeddings-DFl88_y-.mjs.map → embeddings-BUjZMD9F.mjs.map} +1 -1
- package/dist/{embeddings-BuA7v4lk.mjs → embeddings-DrJ-ewfi.mjs} +1 -1
- package/dist/{env-DqTjeKoA.mjs → env-CPCNHz3i.mjs} +1 -1
- package/dist/{env-DqTjeKoA.mjs.map → env-CPCNHz3i.mjs.map} +1 -1
- package/dist/factory-DjtXElOI.mjs +9 -0
- package/dist/{factory-mNBitkL_.mjs → factory-yElzJc6f.mjs} +10 -10
- package/dist/{factory-mNBitkL_.mjs.map → factory-yElzJc6f.mjs.map} +1 -1
- package/dist/{fallback-DdC1_s2d.mjs → fallback-2yWDDYiT.mjs} +5 -4
- package/dist/{fallback-DdC1_s2d.mjs.map → fallback-2yWDDYiT.mjs.map} +1 -1
- package/dist/{helpers-CZsi_49C.mjs → helpers-B9LHRJ8R.mjs} +1 -1
- package/dist/{helpers-CZsi_49C.mjs.map → helpers-B9LHRJ8R.mjs.map} +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +7 -4
- package/dist/{ipc-client-BgSzY9QJ.mjs → ipc-client-DVZ_EGnu.mjs} +2 -2
- package/dist/{ipc-client-BgSzY9QJ.mjs.map → ipc-client-DVZ_EGnu.mjs.map} +1 -1
- package/dist/main-resolver-DjQsexge.mjs +8 -0
- package/dist/{main-resolver-DEXky_Km.mjs → main-resolver-Dxvn4Mv8.mjs} +7 -6
- package/dist/{main-resolver-DEXky_Km.mjs.map → main-resolver-Dxvn4Mv8.mjs.map} +1 -1
- package/dist/{pai-marker-DXVpFsYz.mjs → pai-marker-1B7iC7oq.mjs} +121 -3
- package/dist/pai-marker-1B7iC7oq.mjs.map +1 -0
- package/dist/{planner-DBoB5GtC.mjs → planner-CspP59ya.mjs} +13 -11
- package/dist/{planner-DBoB5GtC.mjs.map → planner-CspP59ya.mjs.map} +1 -1
- package/dist/{postgres-D-96lgE1.mjs → postgres-CCAGq1qp.mjs} +5 -5
- package/dist/{postgres-D-96lgE1.mjs.map → postgres-CCAGq1qp.mjs.map} +1 -1
- package/dist/postgres-CzkEPfAm.mjs +7 -0
- package/dist/{program-BXns_5kO.mjs → program-cWqZI0_L.mjs} +77 -119
- package/dist/program-cWqZI0_L.mjs.map +1 -0
- package/dist/{query-feedback-VI7Cl-Lr.mjs → query-feedback-GPeVGvIa.mjs} +2 -2
- package/dist/{query-feedback-VI7Cl-Lr.mjs.map → query-feedback-GPeVGvIa.mjs.map} +1 -1
- package/dist/{reranker-3lnggwgq.mjs → reranker-BTiOia8Y.mjs} +1 -1
- package/dist/{reranker-3lnggwgq.mjs.map → reranker-BTiOia8Y.mjs.map} +1 -1
- package/dist/{reranker-CZ2mP4cf.mjs → reranker-YwDT2n5e.mjs} +1 -1
- package/dist/{router-dfjlaOMI.mjs → router-Bk_ySIZT.mjs} +2 -2
- package/dist/{router-dfjlaOMI.mjs.map → router-Bk_ySIZT.mjs.map} +1 -1
- package/dist/router-DyiZNI6o.mjs +3 -0
- package/dist/{run-DPwQkRwh.mjs → run-BZNdpUGQ.mjs} +126 -1795
- package/dist/run-BZNdpUGQ.mjs.map +1 -0
- package/dist/run-env-Cd0Son-Y.mjs +115 -0
- package/dist/run-env-Cd0Son-Y.mjs.map +1 -0
- package/dist/{runtime-paths-BiEi_gaG.mjs → runtime-paths-DkZoxofH.mjs} +1 -1
- package/dist/{runtime-paths-BiEi_gaG.mjs.map → runtime-paths-DkZoxofH.mjs.map} +1 -1
- package/dist/{search-CNAGTiJP.mjs → search-cPDj-9fO.mjs} +2 -2
- package/dist/{search-CNAGTiJP.mjs.map → search-cPDj-9fO.mjs.map} +1 -1
- package/dist/{sources-BxXbzpAJ.mjs → sources-CvvzNZad.mjs} +1 -1
- package/dist/{sources-BxXbzpAJ.mjs.map → sources-CvvzNZad.mjs.map} +1 -1
- package/dist/{stop-words-DtxaTWU_.mjs → stop-words-LtkS-Pej.mjs} +1 -1
- package/dist/{stop-words-DtxaTWU_.mjs.map → stop-words-LtkS-Pej.mjs.map} +1 -1
- package/dist/worktree-CwMRTa0H.mjs +1578 -0
- package/dist/worktree-CwMRTa0H.mjs.map +1 -0
- package/dist/{zettelkasten-wh2Yy1nS.mjs → zettelkasten-DSIe6wpF.mjs} +4 -4
- package/dist/{zettelkasten-wh2Yy1nS.mjs.map → zettelkasten-DSIe6wpF.mjs.map} +1 -1
- package/dist/{zettelkasten-CzcPzEsL.mjs → zettelkasten-DtkAvA18.mjs} +5 -5
- package/package.json +1 -1
- package/dist/auto-route-CboBqsN3.mjs +0 -3
- package/dist/context-handover-cache-Bb-H_mTa.mjs.map +0 -1
- package/dist/daemon-BXTn2EgV.mjs +0 -19
- package/dist/factory-XrH66erA.mjs +0 -9
- package/dist/main-resolver-CobQGm2B.mjs +0 -7
- package/dist/pai-marker-DXVpFsYz.mjs.map +0 -1
- package/dist/postgres-TedJP3yF.mjs +0 -7
- package/dist/program-BXns_5kO.mjs.map +0 -1
- package/dist/router-CKn9e0fo.mjs +0 -3
- package/dist/run-DPwQkRwh.mjs.map +0 -1
- package/dist/run-env-CkAmx3lb.mjs.map +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { s as loadConfig } from "./config-DjZELvoA.mjs";
|
|
2
|
+
import { n as configureEmbeddingModel, o as generateEmbeddings, t as EMBEDDING_DIM } from "./embeddings-BUjZMD9F.mjs";
|
|
3
3
|
import { createReadStream, createWriteStream, existsSync, mkdirSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
|
-
import { createHash } from "node:crypto";
|
|
7
6
|
import { execFileSync } from "node:child_process";
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
8
8
|
import { Readable } from "node:stream";
|
|
9
9
|
import { pipeline } from "node:stream/promises";
|
|
10
10
|
|
|
@@ -375,4 +375,4 @@ async function measureThroughput(backend, texts) {
|
|
|
375
375
|
|
|
376
376
|
//#endregion
|
|
377
377
|
export { requireQueryEmbedding as a, detectBackends as c, OLLAMA_DEFAULT_MODEL as d, OLLAMA_DEFAULT_URL as f, mismatchReason as g, bindingMatches as h, measureThroughput as i, getConfiguredBackend as l, LEGACY_EMBEDDING_BINDING as m, embedQuery as n, BACKEND_IDS as o, OLLAMA_ID as p, gateBackend as r, createBackend as s, QUERY_PREFIX as t, provisionOllama as u };
|
|
378
|
-
//# sourceMappingURL=embedding-gate-
|
|
378
|
+
//# sourceMappingURL=embedding-gate-DqbKvn56.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embedding-gate-BVrr0BgS.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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"}
|
|
1
|
+
{"version":3,"file":"embedding-gate-DqbKvn56.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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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import "./
|
|
3
|
-
import "./
|
|
1
|
+
import "./config-DjZELvoA.mjs";
|
|
2
|
+
import { a as requireQueryEmbedding, i as measureThroughput, n as embedQuery, r as gateBackend, t as QUERY_PREFIX } from "./embedding-gate-DqbKvn56.mjs";
|
|
3
|
+
import "./embeddings-BUjZMD9F.mjs";
|
|
4
4
|
|
|
5
5
|
export { embedQuery, gateBackend };
|
|
@@ -129,4 +129,4 @@ function cosineSimilarity(a, b) {
|
|
|
129
129
|
|
|
130
130
|
//#endregion
|
|
131
131
|
export { generateEmbedding as a, deserializeEmbedding as i, configureEmbeddingModel as n, generateEmbeddings as o, cosineSimilarity as r, serializeEmbedding as s, EMBEDDING_DIM as t };
|
|
132
|
-
//# sourceMappingURL=embeddings-
|
|
132
|
+
//# sourceMappingURL=embeddings-BUjZMD9F.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embeddings-
|
|
1
|
+
{"version":3,"file":"embeddings-BUjZMD9F.mjs","names":[],"sources":["../src/memory/embeddings.ts"],"sourcesContent":["/**\n * Embedding generation for the PAI federation memory engine (Phase 2.5).\n *\n * Uses @huggingface/transformers with the Snowflake/snowflake-arctic-embed-m-v1.5 model\n * (768 dims, q8 quantization, MTEB strong retrieval quality).\n *\n * The model uses CLS pooling (first token) — NOT mean pooling.\n * For retrieval, queries require a prefix: \"Represent this sentence for searching relevant passages: \"\n * Documents should be embedded WITHOUT a prefix.\n *\n * The pipeline is a lazy singleton — loaded on first call, reused thereafter.\n * This avoids loading the heavy ML model on every CLI invocation.\n */\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nexport const EMBEDDING_DIM = 768;\nconst DEFAULT_EMBEDDING_MODEL = \"Snowflake/snowflake-arctic-embed-m-v1.5\";\n\n/** Texts per forward pass in generateEmbeddings (length-sorted, see there). */\nconst SUB_BATCH = 16;\n\n/** Query prefix required by Snowflake Arctic Embed for retrieval tasks. */\nconst QUERY_PREFIX = \"Represent this sentence for searching relevant passages: \";\n\n// ---------------------------------------------------------------------------\n// Lazy pipeline singleton\n// ---------------------------------------------------------------------------\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet _embeddingPipeline: any = null;\nlet _currentModel: string | null = null;\n\n/**\n * Configure the embedding model to use.\n * Must be called before the first generateEmbedding() call.\n * If the pipeline is already loaded with a different model, it will be reloaded.\n *\n * @param model HuggingFace model ID (e.g. \"Snowflake/snowflake-arctic-embed-m-v1.5\").\n * Pass undefined or empty string to use the default model.\n */\nexport function configureEmbeddingModel(model?: string): void {\n const resolved = model?.trim() || DEFAULT_EMBEDDING_MODEL;\n if (_currentModel !== null && _currentModel !== resolved) {\n // Model changed — force reload on next call\n _embeddingPipeline = null;\n }\n _currentModel = resolved;\n}\n\nasync function getEmbedder() {\n const model = _currentModel ?? DEFAULT_EMBEDDING_MODEL;\n if (!_embeddingPipeline) {\n // Dynamic import to avoid loading the ML runtime on startup\n const { pipeline } = await import(\"@huggingface/transformers\");\n _embeddingPipeline = await pipeline(\n \"feature-extraction\",\n model,\n { dtype: \"q8\" },\n );\n }\n return _embeddingPipeline;\n}\n\n// ---------------------------------------------------------------------------\n// Embedding generation\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a normalized 768-dim embedding for the given text.\n *\n * Uses CLS pooling (first token) and L2 normalization (cosine similarity ready).\n *\n * @param text The text to embed.\n * @param isQuery If true, prepend the Snowflake query prefix. Use for search queries.\n * Documents should be embedded without the prefix (default: false).\n */\nexport async function generateEmbedding(text: string, isQuery: boolean = false): Promise<Float32Array> {\n const prefix = isQuery ? QUERY_PREFIX : \"\";\n const input = prefix + text;\n const extractor = await getEmbedder();\n // Snowflake Arctic Embed uses CLS pooling (first token), not mean pooling\n const output = await extractor(input, { pooling: \"cls\", normalize: true });\n return new Float32Array(output.data);\n}\n\n/**\n * Generate normalized embeddings for several documents in one forward pass.\n *\n * The model call dominates embedding cost, and calling it once per chunk leaves\n * most of the available throughput unused: measured on this machine, one-at-a-\n * time runs at ~5 chunks/s, which turns a six-figure backlog into days of\n * work. Batching amortises the per-call overhead across the batch.\n *\n * The pipeline returns one flat buffer for the whole batch, so it is sliced\n * back apart by row. Order is preserved: result[i] corresponds to texts[i].\n *\n * Documents only — queries take the prefixed single-text path, since a search\n * embeds exactly one string and gains nothing here.\n */\nexport async function generateEmbeddings(texts: string[]): Promise<Float32Array[]> {\n if (texts.length === 0) return [];\n if (texts.length === 1) return [await generateEmbedding(texts[0])];\n\n const extractor = await getEmbedder();\n\n // A batch is padded to its longest text, and cost grows with padded length.\n // Sorting by length and running small sub-batches keeps neighbours similar:\n // measured 2.1x faster on mixed-length texts (5.5 -> 11.7 chunks/s).\n const order = texts.map((_, i) => i).sort((a, b) => texts[a].length - texts[b].length);\n const out: Float32Array[] = new Array(texts.length);\n for (let s = 0; s < order.length; s += SUB_BATCH) {\n const idx = order.slice(s, s + SUB_BATCH);\n const output = await extractor(idx.map((i) => texts[i]), { pooling: \"cls\", normalize: true });\n const flat = output.data as Float32Array;\n\n const dim = flat.length / idx.length;\n if (!Number.isInteger(dim)) {\n throw new Error(\n `Batched embedding returned ${flat.length} values for ${idx.length} inputs — not divisible`\n );\n }\n idx.forEach((orig, k) => {\n // Copy rather than subarray: the caller stores these, and a view would\n // pin the whole batch buffer in memory for the lifetime of one vector.\n out[orig] = new Float32Array(flat.slice(k * dim, (k + 1) * dim));\n });\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Serialization helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a Float32Array to a Buffer for storage in a SQLite BLOB column.\n */\nexport function serializeEmbedding(vec: Float32Array): Buffer {\n return Buffer.from(vec.buffer, vec.byteOffset, vec.byteLength);\n}\n\n/**\n * Deserialize a Buffer (from a SQLite BLOB column) back into a Float32Array.\n */\nexport function deserializeEmbedding(blob: Buffer): Float32Array {\n return new Float32Array(blob.buffer, blob.byteOffset, blob.byteLength / 4);\n}\n\n// ---------------------------------------------------------------------------\n// Similarity computation\n// ---------------------------------------------------------------------------\n\n/**\n * Compute cosine similarity between two normalized embedding vectors.\n *\n * Since both vectors are already L2-normalized by the embedding model,\n * cosine similarity reduces to a dot product — but we compute the full\n * formula for correctness when embeddings may not be pre-normalized.\n *\n * Returns a value in [-1, 1] where 1 = identical.\n */\nexport function cosineSimilarity(a: Float32Array, b: Float32Array): number {\n let dot = 0;\n let normA = 0;\n let normB = 0;\n for (let i = 0; i < a.length; i++) {\n dot += a[i] * b[i];\n normA += a[i] * a[i];\n normB += b[i] * b[i];\n }\n const denom = Math.sqrt(normA) * Math.sqrt(normB);\n if (denom === 0) return 0;\n return dot / denom;\n}\n"],"mappings":";;;;;;;;;;;;;;AAkBA,MAAa,gBAAgB;AAC7B,MAAM,0BAA0B;;AAGhC,MAAM,YAAY;;AAGlB,MAAM,eAAe;AAOrB,IAAI,qBAA0B;AAC9B,IAAI,gBAA+B;;;;;;;;;AAUnC,SAAgB,wBAAwB,OAAsB;CAC5D,MAAM,WAAW,OAAO,MAAM,IAAI;AAClC,KAAI,kBAAkB,QAAQ,kBAAkB,SAE9C,sBAAqB;AAEvB,iBAAgB;;AAGlB,eAAe,cAAc;CAC3B,MAAM,QAAQ,iBAAiB;AAC/B,KAAI,CAAC,oBAAoB;EAEvB,MAAM,EAAE,aAAa,MAAM,OAAO;AAClC,uBAAqB,MAAM,SACzB,sBACA,OACA,EAAE,OAAO,MAAM,CAChB;;AAEH,QAAO;;;;;;;;;;;AAgBT,eAAsB,kBAAkB,MAAc,UAAmB,OAA8B;CAErG,MAAM,SADS,UAAU,eAAe,MACjB;CAGvB,MAAM,SAAS,OAFG,MAAM,aAAa,EAEN,OAAO;EAAE,SAAS;EAAO,WAAW;EAAM,CAAC;AAC1E,QAAO,IAAI,aAAa,OAAO,KAAK;;;;;;;;;;;;;;;;AAiBtC,eAAsB,mBAAmB,OAA0C;AACjF,KAAI,MAAM,WAAW,EAAG,QAAO,EAAE;AACjC,KAAI,MAAM,WAAW,EAAG,QAAO,CAAC,MAAM,kBAAkB,MAAM,GAAG,CAAC;CAElE,MAAM,YAAY,MAAM,aAAa;CAKrC,MAAM,QAAQ,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,MAAM,GAAG,MAAM,MAAM,GAAG,SAAS,MAAM,GAAG,OAAO;CACtF,MAAM,MAAsB,IAAI,MAAM,MAAM,OAAO;AACnD,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,WAAW;EAChD,MAAM,MAAM,MAAM,MAAM,GAAG,IAAI,UAAU;EAEzC,MAAM,QADS,MAAM,UAAU,IAAI,KAAK,MAAM,MAAM,GAAG,EAAE;GAAE,SAAS;GAAO,WAAW;GAAM,CAAC,EACzE;EAEpB,MAAM,MAAM,KAAK,SAAS,IAAI;AAC9B,MAAI,CAAC,OAAO,UAAU,IAAI,CACxB,OAAM,IAAI,MACR,8BAA8B,KAAK,OAAO,cAAc,IAAI,OAAO,yBACpE;AAEH,MAAI,SAAS,MAAM,MAAM;AAGvB,OAAI,QAAQ,IAAI,aAAa,KAAK,MAAM,IAAI,MAAM,IAAI,KAAK,IAAI,CAAC;IAChE;;AAEJ,QAAO;;;;;AAUT,SAAgB,mBAAmB,KAA2B;AAC5D,QAAO,OAAO,KAAK,IAAI,QAAQ,IAAI,YAAY,IAAI,WAAW;;;;;AAMhE,SAAgB,qBAAqB,MAA4B;AAC/D,QAAO,IAAI,aAAa,KAAK,QAAQ,KAAK,YAAY,KAAK,aAAa,EAAE;;;;;;;;;;;AAgB5E,SAAgB,iBAAiB,GAAiB,GAAyB;CACzE,IAAI,MAAM;CACV,IAAI,QAAQ;CACZ,IAAI,QAAQ;AACZ,MAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AACjC,SAAO,EAAE,KAAK,EAAE;AAChB,WAAS,EAAE,KAAK,EAAE;AAClB,WAAS,EAAE,KAAK,EAAE;;CAEpB,MAAM,QAAQ,KAAK,KAAK,MAAM,GAAG,KAAK,KAAK,MAAM;AACjD,KAAI,UAAU,EAAG,QAAO;AACxB,QAAO,MAAM"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as generateEmbedding, i as deserializeEmbedding, n as configureEmbeddingModel, o as generateEmbeddings, r as cosineSimilarity, s as serializeEmbedding, t as EMBEDDING_DIM } from "./embeddings-
|
|
1
|
+
import { a as generateEmbedding, i as deserializeEmbedding, n as configureEmbeddingModel, o as generateEmbeddings, r as cosineSimilarity, s as serializeEmbedding, t as EMBEDDING_DIM } from "./embeddings-BUjZMD9F.mjs";
|
|
2
2
|
|
|
3
3
|
export { serializeEmbedding };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env-
|
|
1
|
+
{"version":3,"file":"env-CPCNHz3i.mjs","names":[],"sources":["../src/utils/env.ts"],"sourcesContent":["/**\n * iTerm2 exports __CFBundleIdentifier into its shells. Any inherited process\n * that touches AppKit/LaunchServices (osascript, chromedriver, helpers) then\n * registers AS iTerm2, and AppleScript sent to iTerm2 hits that impostor\n * (-600/-1708). Entry points call this first so nothing they spawn inherits it.\n */\nexport function dropItermBundleId(\n env: NodeJS.ProcessEnv = process.env,\n platform: NodeJS.Platform = process.platform,\n): void {\n if (platform === \"darwin\" && env.__CFBundleIdentifier === \"com.googlecode.iterm2\") {\n delete env.__CFBundleIdentifier;\n }\n}\n"],"mappings":";;;;;;;AAMA,SAAgB,kBACd,MAAyB,QAAQ,KACjC,WAA4B,QAAQ,UAC9B;AACN,KAAI,aAAa,YAAY,IAAI,yBAAyB,wBACxD,QAAO,IAAI"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import "./config-DjZELvoA.mjs";
|
|
2
|
+
import "./helpers-B9LHRJ8R.mjs";
|
|
3
|
+
import "./postgres-CCAGq1qp.mjs";
|
|
4
|
+
import { a as getStorageBackend, i as getRegistryBackend, n as createRegistryBackend, r as createStorageBackend, t as closeStorage } from "./factory-yElzJc6f.mjs";
|
|
5
|
+
import "./embedding-gate-DqbKvn56.mjs";
|
|
6
|
+
import "./embeddings-BUjZMD9F.mjs";
|
|
7
|
+
import "./router-Bk_ySIZT.mjs";
|
|
8
|
+
|
|
9
|
+
export { createStorageBackend, getRegistryBackend };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { n as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { t as routeNotification } from "./router-
|
|
1
|
+
import { s as loadConfig } from "./config-DjZELvoA.mjs";
|
|
2
|
+
import { d as resolvePaiFile, u as paiHomePath } from "./runtime-paths-DkZoxofH.mjs";
|
|
3
|
+
import { a as parseSessionTitleChunk, c as yieldToEventLoop, f as fileContentHash, h as sha256File, i as isPathTooBroadForContentScan, n as chunkId, o as walkContentFiles, r as detectTier, s as walkMdFiles, t as INDEX_YIELD_EVERY, u as chunkMarkdown } from "./helpers-B9LHRJ8R.mjs";
|
|
4
|
+
import { t as STOP_WORDS } from "./stop-words-LtkS-Pej.mjs";
|
|
5
|
+
import { n as buildFtsQuery, r as isQuerySyntaxError } from "./search-cPDj-9fO.mjs";
|
|
6
|
+
import { a as entityContentId, t as PostgresBackend } from "./postgres-CCAGq1qp.mjs";
|
|
7
|
+
import { m as LEGACY_EMBEDDING_BINDING } from "./embedding-gate-DqbKvn56.mjs";
|
|
8
|
+
import { i as deserializeEmbedding, r as cosineSimilarity } from "./embeddings-BUjZMD9F.mjs";
|
|
9
|
+
import { t as routeNotification } from "./router-Bk_ySIZT.mjs";
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
12
|
import { basename, dirname, join, relative } from "node:path";
|
|
@@ -2911,4 +2911,4 @@ async function closeStorage() {
|
|
|
2911
2911
|
|
|
2912
2912
|
//#endregion
|
|
2913
2913
|
export { getStorageBackend as a, MergeError as c, getBackendOutage as d, humanDuration as f, getRegistryBackend as i, federationDbPath as l, createRegistryBackend as n, oldRegistryPath as o, createStorageBackend as r, registryDbPath as s, closeStorage as t, oldFederationPath as u };
|
|
2914
|
-
//# sourceMappingURL=factory-
|
|
2914
|
+
//# sourceMappingURL=factory-yElzJc6f.mjs.map
|