@mneme-ai/embeddings 0.18.3 → 0.19.0

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.
@@ -0,0 +1,40 @@
1
+ import type { EmbeddingProvider } from "@mneme-ai/core";
2
+ export interface BundledOptions {
3
+ /** HuggingFace repo id. all-MiniLM-L6-v2 = 25MB / 384-dim — best size/quality. */
4
+ model?: string;
5
+ /** Override cache dir. Default: ~/.cache/mneme/models. */
6
+ cacheDir?: string;
7
+ /** Hook for download/load progress (lazy load can take ~5–60s on cold start). */
8
+ onProgress?: (info: {
9
+ status: string;
10
+ loaded?: number;
11
+ total?: number;
12
+ file?: string;
13
+ }) => void;
14
+ }
15
+ export declare class BundledEmbedder implements EmbeddingProvider {
16
+ readonly name: string;
17
+ readonly dimensions: number;
18
+ private readonly model;
19
+ private readonly cacheDir;
20
+ private readonly onProgress?;
21
+ private extractor;
22
+ private loadPromise;
23
+ constructor(opts?: BundledOptions);
24
+ /** Cheap pre-flight — instantiates the pipeline (downloads if needed) and
25
+ * runs a 1-token sanity embed. Use BEFORE the long indexer loop. */
26
+ verify(): Promise<{
27
+ ok: true;
28
+ } | {
29
+ ok: false;
30
+ reason: string;
31
+ remedy: string;
32
+ }>;
33
+ embed(texts: string[]): Promise<Float32Array[]>;
34
+ /** Lazy load — done at most once, even under concurrent embed() calls. */
35
+ private load;
36
+ private bootPipeline;
37
+ }
38
+ /** Default cache dir: ~/.cache/mneme/models. User can `rm -rf` to reset. */
39
+ export declare function defaultCacheDir(): string;
40
+ //# sourceMappingURL=bundled.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundled.d.ts","sourceRoot":"","sources":["../src/bundled.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,MAAM,WAAW,cAAc;IAC7B,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACjG;AAeD,qBAAa,eAAgB,YAAW,iBAAiB;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAA+B;IAC3D,OAAO,CAAC,SAAS,CAAiC;IAClD,OAAO,CAAC,WAAW,CAA0C;gBAEjD,IAAI,GAAE,cAAmB;IAQrC;yEACqE;IAC/D,MAAM,IAAI,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAiB/E,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAcrD,0EAA0E;YAC5D,IAAI;YAQJ,YAAY;CAmC3B;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,IAAI,MAAM,CAExC"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * BundledEmbedder — zero-install WASM embeddings.
3
+ *
4
+ * Uses @xenova/transformers (pure JS + ONNX-WASM, no native deps) so the
5
+ * tool runs on Windows / Mac / Linux out of the box. The model file is
6
+ * lazy-downloaded on first use (~25MB to a local cache), giving users a
7
+ * "★★★ semantic quality" path without installing Ollama.
8
+ *
9
+ * Fallback order in resolveEmbedder():
10
+ * OpenAI (★★★★★ paid) → Ollama (★★★★ free local)
11
+ * → Bundled WASM (★★★ free, no install) → Hash (★★ deterministic)
12
+ */
13
+ import { homedir } from "node:os";
14
+ import { join } from "node:path";
15
+ import { mkdirSync } from "node:fs";
16
+ const DEFAULT_MODEL = "Xenova/all-MiniLM-L6-v2";
17
+ const DEFAULT_DIMS = 384;
18
+ export class BundledEmbedder {
19
+ name;
20
+ dimensions;
21
+ model;
22
+ cacheDir;
23
+ onProgress;
24
+ extractor = null;
25
+ loadPromise = null;
26
+ constructor(opts = {}) {
27
+ this.model = opts.model ?? DEFAULT_MODEL;
28
+ this.dimensions = DEFAULT_DIMS;
29
+ this.cacheDir = opts.cacheDir ?? defaultCacheDir();
30
+ this.onProgress = opts.onProgress;
31
+ this.name = `bundled:${this.model}`;
32
+ }
33
+ /** Cheap pre-flight — instantiates the pipeline (downloads if needed) and
34
+ * runs a 1-token sanity embed. Use BEFORE the long indexer loop. */
35
+ async verify() {
36
+ try {
37
+ await this.load();
38
+ await this.embed(["ok"]);
39
+ return { ok: true };
40
+ }
41
+ catch (err) {
42
+ const msg = err.message ?? String(err);
43
+ return {
44
+ ok: false,
45
+ reason: `Bundled WASM model failed: ${msg}`,
46
+ remedy: "First run requires internet to download ~25MB. " +
47
+ "If you're offline, set --embedder hash to use the deterministic fallback.",
48
+ };
49
+ }
50
+ }
51
+ async embed(texts) {
52
+ const ext = await this.load();
53
+ const out = new Array(texts.length);
54
+ // Sequential: the WASM pipeline isn't designed for concurrency; one at a
55
+ // time is the predictable, memory-safe path. For 1000 chunks this takes
56
+ // ~10–20s on consumer laptops — acceptable for a one-time index.
57
+ for (let i = 0; i < texts.length; i++) {
58
+ const result = await ext(texts[i], { pooling: "mean", normalize: true });
59
+ // result.data is Float32Array-like — copy into a fresh typed array.
60
+ out[i] = Float32Array.from(result.data);
61
+ }
62
+ return out;
63
+ }
64
+ /** Lazy load — done at most once, even under concurrent embed() calls. */
65
+ async load() {
66
+ if (this.extractor)
67
+ return this.extractor;
68
+ if (this.loadPromise)
69
+ return this.loadPromise;
70
+ this.loadPromise = this.bootPipeline();
71
+ this.extractor = await this.loadPromise;
72
+ return this.extractor;
73
+ }
74
+ async bootPipeline() {
75
+ // Ensure cache dir exists so transformers.js can write the ONNX model.
76
+ try {
77
+ mkdirSync(this.cacheDir, { recursive: true });
78
+ }
79
+ catch {
80
+ /* permission errors surface later via the pipeline */
81
+ }
82
+ // Dynamic import — keeps the heavy WASM init out of cold-start path
83
+ // for users who never touch this provider.
84
+ const transformers = (await import("@xenova/transformers"));
85
+ transformers.env.cacheDir = this.cacheDir;
86
+ transformers.env.allowRemoteModels = true;
87
+ const onProgress = this.onProgress;
88
+ return await transformers.pipeline("feature-extraction", this.model, {
89
+ progress_callback: onProgress
90
+ ? (info) => onProgress({
91
+ status: String(info["status"] ?? ""),
92
+ loaded: typeof info["loaded"] === "number" ? info["loaded"] : undefined,
93
+ total: typeof info["total"] === "number" ? info["total"] : undefined,
94
+ file: typeof info["file"] === "string" ? info["file"] : undefined,
95
+ })
96
+ : undefined,
97
+ });
98
+ }
99
+ }
100
+ /** Default cache dir: ~/.cache/mneme/models. User can `rm -rf` to reset. */
101
+ export function defaultCacheDir() {
102
+ return join(homedir(), ".cache", "mneme", "models");
103
+ }
104
+ //# sourceMappingURL=bundled.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundled.js","sourceRoot":"","sources":["../src/bundled.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./ollama.js";
2
2
  export * from "./openai.js";
3
+ export * from "./bundled.js";
3
4
  export * from "./hash.js";
4
5
  export * from "./resolve.js";
5
6
  export * from "./enrich.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./ollama.js";
2
2
  export * from "./openai.js";
3
+ export * from "./bundled.js";
3
4
  export * from "./hash.js";
4
5
  export * from "./resolve.js";
5
6
  export * from "./enrich.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
package/dist/resolve.d.ts CHANGED
@@ -1,15 +1,26 @@
1
1
  import type { EmbeddingProvider } from "@mneme-ai/core";
2
+ import { BundledEmbedder } from "./bundled.js";
2
3
  export interface ResolveOptions {
3
- provider?: "auto" | "ollama" | "openai" | "hash";
4
+ /**
5
+ * `auto` (default) walks the fallback ladder so the user gets the highest
6
+ * quality embedder that's actually available, with hash always reachable
7
+ * as the last resort:
8
+ *
9
+ * 1. OpenAI (★★★★★ paid) — if OPENAI_API_KEY is set
10
+ * 2. Ollama (★★★★ free) — if /api/tags responds + model pulled
11
+ * 3. Bundled (★★★ free) — WASM model, ~25MB lazy download
12
+ * 4. Hash (★★) — deterministic, zero deps, always works
13
+ *
14
+ * Pass an explicit value to skip the ladder.
15
+ */
16
+ provider?: "auto" | "ollama" | "openai" | "bundled" | "hash";
4
17
  model?: string;
5
18
  apiKey?: string;
6
19
  baseUrl?: string;
20
+ /** Optional callback for bundled-model download progress. */
21
+ onBundledProgress?: NonNullable<ConstructorParameters<typeof BundledEmbedder>[0]>["onProgress"];
7
22
  }
8
- /**
9
- * Auto-select an embedder.
10
- *
11
- * auto: prefer Ollama → OpenAI (if key) → hash fallback
12
- * ollama / openai / hash: explicit
13
- */
14
23
  export declare function resolveEmbedder(opts?: ResolveOptions): Promise<EmbeddingProvider>;
24
+ /** Helper for callers that explicitly want the deterministic offline path. */
25
+ export declare function hashEmbedder(): EmbeddingProvider;
15
26
  //# sourceMappingURL=resolve.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAKxD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAuB3F"}
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGxD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAG/C,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,iBAAiB,CAAC,EAAE,WAAW,CAC7B,qBAAqB,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC,CACjD,CAAC,YAAY,CAAC,CAAC;CACjB;AAED,wBAAsB,eAAe,CAAC,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAgE3F;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,IAAI,iBAAiB,CAEhD"}
package/dist/resolve.js CHANGED
@@ -1,29 +1,64 @@
1
1
  import { OllamaEmbedder } from "./ollama.js";
2
2
  import { OpenAIEmbedder } from "./openai.js";
3
+ import { BundledEmbedder } from "./bundled.js";
3
4
  import { HashEmbedder } from "./hash.js";
4
- /**
5
- * Auto-select an embedder.
6
- *
7
- * auto: prefer Ollama → OpenAI (if key) → hash fallback
8
- * ollama / openai / hash: explicit
9
- */
10
5
  export async function resolveEmbedder(opts = {}) {
11
6
  const provider = opts.provider ?? "auto";
12
- if (provider === "ollama" || provider === "auto") {
7
+ // ── Explicit picks ──────────────────────────────────────────────────
8
+ if (provider === "openai") {
9
+ const apiKey = opts.apiKey ?? process.env["OPENAI_API_KEY"];
10
+ if (!apiKey) {
11
+ throw new Error("No OpenAI API key. Set OPENAI_API_KEY or pass --api-key.");
12
+ }
13
+ return new OpenAIEmbedder({ apiKey, model: opts.model, baseUrl: opts.baseUrl });
14
+ }
15
+ if (provider === "ollama") {
13
16
  const ollama = new OllamaEmbedder({ model: opts.model, baseUrl: opts.baseUrl });
14
17
  if (await ollama.ping())
15
18
  return ollama;
16
- if (provider === "ollama") {
17
- throw new Error(`Ollama not reachable at ${opts.baseUrl ?? "http://127.0.0.1:11434"}. Start it with: ollama serve`);
18
- }
19
+ throw new Error(`Ollama not reachable at ${opts.baseUrl ?? "http://127.0.0.1:11434"}. Start it with: ollama serve`);
19
20
  }
21
+ if (provider === "bundled") {
22
+ return new BundledEmbedder({ model: opts.model, onProgress: opts.onBundledProgress });
23
+ }
24
+ if (provider === "hash") {
25
+ return new HashEmbedder();
26
+ }
27
+ // ── auto: walk the ladder, NEVER block the user ────────────────────
28
+ // Each step is health-checked. A failing step quietly falls to the next.
29
+ // The user always ends up with a working embedder (worst case = hash).
30
+ // 1. OpenAI key wins — best quality, no install.
20
31
  const apiKey = opts.apiKey ?? process.env["OPENAI_API_KEY"];
21
- if ((provider === "openai" || provider === "auto") && apiKey) {
32
+ if (apiKey) {
22
33
  return new OpenAIEmbedder({ apiKey, model: opts.model, baseUrl: opts.baseUrl });
23
34
  }
24
- if (provider === "openai") {
25
- throw new Error("No OpenAI API key. Set OPENAI_API_KEY or pass --api-key.");
35
+ // 2. Ollama — only if a SHORT sanity embed succeeds. We had real users
36
+ // where /api/tags responded but /api/embeddings hung for minutes; that
37
+ // used to surface as a hard error. Now we just fall through silently.
38
+ const ollama = new OllamaEmbedder({
39
+ model: opts.model,
40
+ baseUrl: opts.baseUrl,
41
+ timeoutMs: 10_000, // short for the auto-detect probe — full timeout used after
42
+ });
43
+ if (await ollama.ping()) {
44
+ const ver = await ollama.verify();
45
+ if (ver.ok) {
46
+ // Reset to the normal long timeout for the real workload.
47
+ return new OllamaEmbedder({ model: opts.model, baseUrl: opts.baseUrl });
48
+ }
49
+ // Ollama is reachable but unhealthy — explicitly skip and try the next step.
50
+ // We swallow the reason: the user picked auto, they want it to JUST WORK.
26
51
  }
52
+ // 3. Bundled WASM — ★★★, ~25MB auto-download. Doesn't need a "ping" — the
53
+ // module is in the npm install. We instantiate eagerly; the actual model
54
+ // download happens lazily on first embed (or via verify()).
55
+ return new BundledEmbedder({ model: opts.model, onProgress: opts.onBundledProgress });
56
+ // 4. Hash is the FINAL escape hatch — only chosen by explicit `--embedder hash`
57
+ // or by callers that don't want network/download. Auto-detect prefers
58
+ // bundled because it returns true semantic vectors with no setup.
59
+ }
60
+ /** Helper for callers that explicitly want the deterministic offline path. */
61
+ export function hashEmbedder() {
27
62
  return new HashEmbedder();
28
63
  }
29
64
  //# sourceMappingURL=resolve.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AASzC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAuB,EAAE;IAC7D,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC;IAEzC,IAAI,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACjD,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QAChF,IAAI,MAAM,MAAM,CAAC,IAAI,EAAE;YAAE,OAAO,MAAM,CAAC;QACvC,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,2BAA2B,IAAI,CAAC,OAAO,IAAI,wBAAwB,+BAA+B,CACnG,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC5D,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QAC7D,OAAO,IAAI,cAAc,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAClF,CAAC;IAED,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IAED,OAAO,IAAI,YAAY,EAAE,CAAC;AAC5B,CAAC"}
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mneme-ai/embeddings",
3
- "version": "0.18.3",
4
- "description": "Embedding providers (Ollama, OpenAI, hash-fallback) for Mneme",
3
+ "version": "0.19.0",
4
+ "description": "Embedding providers (OpenAI, Ollama, bundled-WASM, hash-fallback) for Mneme",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -30,6 +30,7 @@
30
30
  "clean": "tsc -b --clean"
31
31
  },
32
32
  "dependencies": {
33
- "@mneme-ai/core": "0.18.3"
33
+ "@mneme-ai/core": "0.19.0",
34
+ "@xenova/transformers": "^2.17.2"
34
35
  }
35
36
  }