@mnemoai/core 1.1.0 → 1.1.1

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.
Files changed (220) hide show
  1. package/dist/cli.d.ts +2 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/dist/cli.js +7 -0
  4. package/dist/cli.js.map +7 -0
  5. package/dist/index.d.ts +128 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/{index.ts → dist/index.js} +526 -1333
  8. package/dist/index.js.map +7 -0
  9. package/dist/src/access-tracker.d.ts +97 -0
  10. package/dist/src/access-tracker.d.ts.map +1 -0
  11. package/dist/src/access-tracker.js +184 -0
  12. package/dist/src/access-tracker.js.map +7 -0
  13. package/dist/src/adapters/chroma.d.ts +31 -0
  14. package/dist/src/adapters/chroma.d.ts.map +1 -0
  15. package/{src/adapters/chroma.ts → dist/src/adapters/chroma.js} +45 -107
  16. package/dist/src/adapters/chroma.js.map +7 -0
  17. package/dist/src/adapters/lancedb.d.ts +29 -0
  18. package/dist/src/adapters/lancedb.d.ts.map +1 -0
  19. package/{src/adapters/lancedb.ts → dist/src/adapters/lancedb.js} +41 -109
  20. package/dist/src/adapters/lancedb.js.map +7 -0
  21. package/dist/src/adapters/pgvector.d.ts +33 -0
  22. package/dist/src/adapters/pgvector.d.ts.map +1 -0
  23. package/{src/adapters/pgvector.ts → dist/src/adapters/pgvector.js} +42 -104
  24. package/dist/src/adapters/pgvector.js.map +7 -0
  25. package/dist/src/adapters/qdrant.d.ts +34 -0
  26. package/dist/src/adapters/qdrant.d.ts.map +1 -0
  27. package/dist/src/adapters/qdrant.js +132 -0
  28. package/dist/src/adapters/qdrant.js.map +7 -0
  29. package/dist/src/adaptive-retrieval.d.ts +14 -0
  30. package/dist/src/adaptive-retrieval.d.ts.map +1 -0
  31. package/dist/src/adaptive-retrieval.js +52 -0
  32. package/dist/src/adaptive-retrieval.js.map +7 -0
  33. package/dist/src/audit-log.d.ts +56 -0
  34. package/dist/src/audit-log.d.ts.map +1 -0
  35. package/dist/src/audit-log.js +139 -0
  36. package/dist/src/audit-log.js.map +7 -0
  37. package/dist/src/chunker.d.ts +45 -0
  38. package/dist/src/chunker.d.ts.map +1 -0
  39. package/dist/src/chunker.js +157 -0
  40. package/dist/src/chunker.js.map +7 -0
  41. package/dist/src/config.d.ts +70 -0
  42. package/dist/src/config.d.ts.map +1 -0
  43. package/dist/src/config.js +142 -0
  44. package/dist/src/config.js.map +7 -0
  45. package/dist/src/decay-engine.d.ts +73 -0
  46. package/dist/src/decay-engine.d.ts.map +1 -0
  47. package/dist/src/decay-engine.js +119 -0
  48. package/dist/src/decay-engine.js.map +7 -0
  49. package/dist/src/embedder.d.ts +94 -0
  50. package/dist/src/embedder.d.ts.map +1 -0
  51. package/{src/embedder.ts → dist/src/embedder.js} +119 -317
  52. package/dist/src/embedder.js.map +7 -0
  53. package/dist/src/extraction-prompts.d.ts +12 -0
  54. package/dist/src/extraction-prompts.d.ts.map +1 -0
  55. package/dist/src/extraction-prompts.js +311 -0
  56. package/dist/src/extraction-prompts.js.map +7 -0
  57. package/dist/src/license.d.ts +29 -0
  58. package/dist/src/license.d.ts.map +1 -0
  59. package/{src/license.ts → dist/src/license.js} +42 -113
  60. package/dist/src/license.js.map +7 -0
  61. package/dist/src/llm-client.d.ts +23 -0
  62. package/dist/src/llm-client.d.ts.map +1 -0
  63. package/{src/llm-client.ts → dist/src/llm-client.js} +22 -55
  64. package/dist/src/llm-client.js.map +7 -0
  65. package/dist/src/logger.d.ts +33 -0
  66. package/dist/src/logger.d.ts.map +1 -0
  67. package/dist/src/logger.js +35 -0
  68. package/dist/src/logger.js.map +7 -0
  69. package/dist/src/mcp-server.d.ts +16 -0
  70. package/dist/src/mcp-server.d.ts.map +1 -0
  71. package/{src/mcp-server.ts → dist/src/mcp-server.js} +81 -181
  72. package/dist/src/mcp-server.js.map +7 -0
  73. package/dist/src/memory-categories.d.ts +40 -0
  74. package/dist/src/memory-categories.d.ts.map +1 -0
  75. package/dist/src/memory-categories.js +33 -0
  76. package/dist/src/memory-categories.js.map +7 -0
  77. package/dist/src/memory-upgrader.d.ts +71 -0
  78. package/dist/src/memory-upgrader.d.ts.map +1 -0
  79. package/dist/src/memory-upgrader.js +238 -0
  80. package/dist/src/memory-upgrader.js.map +7 -0
  81. package/dist/src/migrate.d.ts +47 -0
  82. package/dist/src/migrate.d.ts.map +1 -0
  83. package/{src/migrate.ts → dist/src/migrate.js} +57 -165
  84. package/dist/src/migrate.js.map +7 -0
  85. package/dist/src/mnemo.d.ts +67 -0
  86. package/dist/src/mnemo.d.ts.map +1 -0
  87. package/dist/src/mnemo.js +66 -0
  88. package/dist/src/mnemo.js.map +7 -0
  89. package/dist/src/noise-filter.d.ts +23 -0
  90. package/dist/src/noise-filter.d.ts.map +1 -0
  91. package/dist/src/noise-filter.js +62 -0
  92. package/dist/src/noise-filter.js.map +7 -0
  93. package/dist/src/noise-prototypes.d.ts +40 -0
  94. package/dist/src/noise-prototypes.d.ts.map +1 -0
  95. package/dist/src/noise-prototypes.js +116 -0
  96. package/dist/src/noise-prototypes.js.map +7 -0
  97. package/dist/src/observability.d.ts +16 -0
  98. package/dist/src/observability.d.ts.map +1 -0
  99. package/dist/src/observability.js +53 -0
  100. package/dist/src/observability.js.map +7 -0
  101. package/dist/src/query-tracker.d.ts +27 -0
  102. package/dist/src/query-tracker.d.ts.map +1 -0
  103. package/dist/src/query-tracker.js +32 -0
  104. package/dist/src/query-tracker.js.map +7 -0
  105. package/dist/src/reflection-event-store.d.ts +44 -0
  106. package/dist/src/reflection-event-store.d.ts.map +1 -0
  107. package/dist/src/reflection-event-store.js +50 -0
  108. package/dist/src/reflection-event-store.js.map +7 -0
  109. package/dist/src/reflection-item-store.d.ts +58 -0
  110. package/dist/src/reflection-item-store.d.ts.map +1 -0
  111. package/dist/src/reflection-item-store.js +69 -0
  112. package/dist/src/reflection-item-store.js.map +7 -0
  113. package/dist/src/reflection-mapped-metadata.d.ts +47 -0
  114. package/dist/src/reflection-mapped-metadata.d.ts.map +1 -0
  115. package/dist/src/reflection-mapped-metadata.js +40 -0
  116. package/dist/src/reflection-mapped-metadata.js.map +7 -0
  117. package/dist/src/reflection-metadata.d.ts +11 -0
  118. package/dist/src/reflection-metadata.d.ts.map +1 -0
  119. package/dist/src/reflection-metadata.js +24 -0
  120. package/dist/src/reflection-metadata.js.map +7 -0
  121. package/dist/src/reflection-ranking.d.ts +13 -0
  122. package/dist/src/reflection-ranking.d.ts.map +1 -0
  123. package/{src/reflection-ranking.ts → dist/src/reflection-ranking.js} +12 -21
  124. package/dist/src/reflection-ranking.js.map +7 -0
  125. package/dist/src/reflection-retry.d.ts +30 -0
  126. package/dist/src/reflection-retry.d.ts.map +1 -0
  127. package/{src/reflection-retry.ts → dist/src/reflection-retry.js} +24 -64
  128. package/dist/src/reflection-retry.js.map +7 -0
  129. package/dist/src/reflection-slices.d.ts +42 -0
  130. package/dist/src/reflection-slices.d.ts.map +1 -0
  131. package/{src/reflection-slices.ts → dist/src/reflection-slices.js} +60 -136
  132. package/dist/src/reflection-slices.js.map +7 -0
  133. package/dist/src/reflection-store.d.ts +85 -0
  134. package/dist/src/reflection-store.d.ts.map +1 -0
  135. package/dist/src/reflection-store.js +407 -0
  136. package/dist/src/reflection-store.js.map +7 -0
  137. package/dist/src/resonance-state.d.ts +19 -0
  138. package/dist/src/resonance-state.d.ts.map +1 -0
  139. package/{src/resonance-state.ts → dist/src/resonance-state.js} +13 -42
  140. package/dist/src/resonance-state.js.map +7 -0
  141. package/dist/src/retriever.d.ts +228 -0
  142. package/dist/src/retriever.d.ts.map +1 -0
  143. package/dist/src/retriever.js +1006 -0
  144. package/dist/src/retriever.js.map +7 -0
  145. package/dist/src/scopes.d.ts +58 -0
  146. package/dist/src/scopes.d.ts.map +1 -0
  147. package/dist/src/scopes.js +252 -0
  148. package/dist/src/scopes.js.map +7 -0
  149. package/dist/src/self-improvement-files.d.ts +20 -0
  150. package/dist/src/self-improvement-files.d.ts.map +1 -0
  151. package/{src/self-improvement-files.ts → dist/src/self-improvement-files.js} +24 -49
  152. package/dist/src/self-improvement-files.js.map +7 -0
  153. package/dist/src/semantic-gate.d.ts +24 -0
  154. package/dist/src/semantic-gate.d.ts.map +1 -0
  155. package/dist/src/semantic-gate.js +86 -0
  156. package/dist/src/semantic-gate.js.map +7 -0
  157. package/dist/src/session-recovery.d.ts +9 -0
  158. package/dist/src/session-recovery.d.ts.map +1 -0
  159. package/{src/session-recovery.ts → dist/src/session-recovery.js} +40 -57
  160. package/dist/src/session-recovery.js.map +7 -0
  161. package/dist/src/smart-extractor.d.ts +107 -0
  162. package/dist/src/smart-extractor.d.ts.map +1 -0
  163. package/{src/smart-extractor.ts → dist/src/smart-extractor.js} +130 -383
  164. package/dist/src/smart-extractor.js.map +7 -0
  165. package/dist/src/smart-metadata.d.ts +103 -0
  166. package/dist/src/smart-metadata.d.ts.map +1 -0
  167. package/dist/src/smart-metadata.js +361 -0
  168. package/dist/src/smart-metadata.js.map +7 -0
  169. package/dist/src/storage-adapter.d.ts +102 -0
  170. package/dist/src/storage-adapter.d.ts.map +1 -0
  171. package/dist/src/storage-adapter.js +22 -0
  172. package/dist/src/storage-adapter.js.map +7 -0
  173. package/dist/src/store.d.ts +108 -0
  174. package/dist/src/store.d.ts.map +1 -0
  175. package/dist/src/store.js +939 -0
  176. package/dist/src/store.js.map +7 -0
  177. package/dist/src/tier-manager.d.ts +57 -0
  178. package/dist/src/tier-manager.d.ts.map +1 -0
  179. package/dist/src/tier-manager.js +80 -0
  180. package/dist/src/tier-manager.js.map +7 -0
  181. package/dist/src/tools.d.ts +43 -0
  182. package/dist/src/tools.d.ts.map +1 -0
  183. package/dist/src/tools.js +1075 -0
  184. package/dist/src/tools.js.map +7 -0
  185. package/dist/src/wal-recovery.d.ts +30 -0
  186. package/dist/src/wal-recovery.d.ts.map +1 -0
  187. package/{src/wal-recovery.ts → dist/src/wal-recovery.js} +26 -79
  188. package/dist/src/wal-recovery.js.map +7 -0
  189. package/package.json +21 -2
  190. package/openclaw.plugin.json +0 -815
  191. package/src/access-tracker.ts +0 -341
  192. package/src/adapters/README.md +0 -78
  193. package/src/adapters/qdrant.ts +0 -191
  194. package/src/adaptive-retrieval.ts +0 -90
  195. package/src/audit-log.ts +0 -238
  196. package/src/chunker.ts +0 -254
  197. package/src/config.ts +0 -271
  198. package/src/decay-engine.ts +0 -238
  199. package/src/extraction-prompts.ts +0 -339
  200. package/src/memory-categories.ts +0 -71
  201. package/src/memory-upgrader.ts +0 -388
  202. package/src/mnemo.ts +0 -142
  203. package/src/noise-filter.ts +0 -97
  204. package/src/noise-prototypes.ts +0 -164
  205. package/src/observability.ts +0 -81
  206. package/src/query-tracker.ts +0 -57
  207. package/src/reflection-event-store.ts +0 -98
  208. package/src/reflection-item-store.ts +0 -112
  209. package/src/reflection-mapped-metadata.ts +0 -84
  210. package/src/reflection-metadata.ts +0 -23
  211. package/src/reflection-store.ts +0 -602
  212. package/src/retriever.ts +0 -1510
  213. package/src/scopes.ts +0 -375
  214. package/src/semantic-gate.ts +0 -121
  215. package/src/smart-metadata.ts +0 -561
  216. package/src/storage-adapter.ts +0 -153
  217. package/src/store.ts +0 -1330
  218. package/src/tier-manager.ts +0 -189
  219. package/src/tools.ts +0 -1292
  220. package/test/core.test.mjs +0 -301
@@ -1,109 +1,60 @@
1
- // SPDX-License-Identifier: MIT
2
- /**
3
- * Embedding Abstraction Layer
4
- * OpenAI-compatible API for various embedding providers.
5
- * Supports automatic chunking for documents exceeding embedding context limits.
6
- *
7
- * Note: Some providers (e.g. Jina) support extra parameters like `task` and
8
- * `normalized` on the embeddings endpoint. The OpenAI SDK types do not include
9
- * these fields, so we pass them via a narrow `any` cast.
10
- */
11
-
12
1
  import OpenAI from "openai";
13
2
  import { createHash } from "node:crypto";
14
3
  import { smartChunk } from "./chunker.js";
15
-
16
- // ============================================================================
17
- // Embedding Cache (LRU with TTL)
18
- // ============================================================================
19
-
20
- interface CacheEntry {
21
- vector: number[];
22
- createdAt: number;
23
- }
24
-
4
+ import { log } from "./logger.js";
25
5
  class EmbeddingCache {
26
- private cache = new Map<string, CacheEntry>();
27
- private readonly maxSize: number;
28
- private readonly ttlMs: number;
29
- public hits = 0;
30
- public misses = 0;
31
-
6
+ cache = /* @__PURE__ */ new Map();
7
+ maxSize;
8
+ ttlMs;
9
+ hits = 0;
10
+ misses = 0;
32
11
  constructor(maxSize = 256, ttlMinutes = 30) {
33
12
  this.maxSize = maxSize;
34
- this.ttlMs = ttlMinutes * 60_000;
13
+ this.ttlMs = ttlMinutes * 6e4;
35
14
  }
36
-
37
- private key(text: string, task?: string): string {
15
+ key(text, task) {
38
16
  const hash = createHash("sha256").update(`${task || ""}:${text}`).digest("hex").slice(0, 24);
39
17
  return hash;
40
18
  }
41
-
42
- get(text: string, task?: string): number[] | undefined {
19
+ get(text, task) {
43
20
  const k = this.key(text, task);
44
21
  const entry = this.cache.get(k);
45
22
  if (!entry) {
46
23
  this.misses++;
47
- return undefined;
24
+ return void 0;
48
25
  }
49
26
  if (Date.now() - entry.createdAt > this.ttlMs) {
50
27
  this.cache.delete(k);
51
28
  this.misses++;
52
- return undefined;
29
+ return void 0;
53
30
  }
54
- // Move to end (most recently used)
55
31
  this.cache.delete(k);
56
32
  this.cache.set(k, entry);
57
33
  this.hits++;
58
34
  return entry.vector;
59
35
  }
60
-
61
- set(text: string, task: string | undefined, vector: number[]): void {
36
+ set(text, task, vector) {
62
37
  const k = this.key(text, task);
63
- // Evict oldest if full
64
38
  if (this.cache.size >= this.maxSize) {
65
39
  const firstKey = this.cache.keys().next().value;
66
- if (firstKey !== undefined) this.cache.delete(firstKey);
40
+ if (firstKey !== void 0) this.cache.delete(firstKey);
67
41
  }
68
42
  this.cache.set(k, { vector, createdAt: Date.now() });
69
43
  }
70
-
71
- get size(): number { return this.cache.size; }
72
- get stats(): { size: number; hits: number; misses: number; hitRate: string } {
44
+ get size() {
45
+ return this.cache.size;
46
+ }
47
+ get stats() {
73
48
  const total = this.hits + this.misses;
74
49
  return {
75
50
  size: this.cache.size,
76
51
  hits: this.hits,
77
52
  misses: this.misses,
78
- hitRate: total > 0 ? `${((this.hits / total) * 100).toFixed(1)}%` : "N/A",
53
+ hitRate: total > 0 ? `${(this.hits / total * 100).toFixed(1)}%` : "N/A"
79
54
  };
80
55
  }
81
56
  }
82
-
83
- // ============================================================================
84
- // Types & Configuration
85
- // ============================================================================
86
-
87
- export interface EmbeddingConfig {
88
- provider: "openai-compatible";
89
- /** Single API key or array of keys for round-robin rotation with failover. */
90
- apiKey: string | string[];
91
- model: string;
92
- baseURL?: string;
93
- dimensions?: number;
94
-
95
- /** Optional task type for query embeddings (e.g. "retrieval.query") */
96
- taskQuery?: string;
97
- /** Optional task type for passage/document embeddings (e.g. "retrieval.passage") */
98
- taskPassage?: string;
99
- /** Optional flag to request normalized embeddings (provider-dependent, e.g. Jina v5) */
100
- normalized?: boolean;
101
- /** Enable automatic chunking for documents exceeding context limits (default: true) */
102
- chunking?: boolean;
103
- }
104
-
105
- // Known embedding model dimensions
106
- const EMBEDDING_DIMENSIONS: Record<string, number> = {
57
+ const EMBEDDING_DIMENSIONS = {
107
58
  "text-embedding-3-small": 1536,
108
59
  "text-embedding-3-large": 3072,
109
60
  "text-embedding-004": 768,
@@ -113,17 +64,11 @@ const EMBEDDING_DIMENSIONS: Record<string, number> = {
113
64
  "BAAI/bge-m3": 1024,
114
65
  "all-MiniLM-L6-v2": 384,
115
66
  "all-mpnet-base-v2": 512,
116
-
117
67
  // Jina v5
118
68
  "jina-embeddings-v5-text-small": 1024,
119
- "jina-embeddings-v5-text-nano": 768,
69
+ "jina-embeddings-v5-text-nano": 768
120
70
  };
121
-
122
- // ============================================================================
123
- // Utility Functions
124
- // ============================================================================
125
-
126
- function resolveEnvVars(value: string): string {
71
+ function resolveEnvVars(value) {
127
72
  return value.replace(/\$\{([^}]+)\}/g, (_, envVar) => {
128
73
  const envValue = process.env[envVar];
129
74
  if (!envValue) {
@@ -132,310 +77,231 @@ function resolveEnvVars(value: string): string {
132
77
  return envValue;
133
78
  });
134
79
  }
135
-
136
- function getErrorMessage(error: unknown): string {
80
+ function getErrorMessage(error) {
137
81
  return error instanceof Error ? error.message : String(error);
138
82
  }
139
-
140
- function getErrorStatus(error: unknown): number | undefined {
141
- if (!error || typeof error !== "object") return undefined;
142
- const err = error as Record<string, any>;
83
+ function getErrorStatus(error) {
84
+ if (!error || typeof error !== "object") return void 0;
85
+ const err = error;
143
86
  if (typeof err.status === "number") return err.status;
144
87
  if (typeof err.statusCode === "number") return err.statusCode;
145
88
  if (err.error && typeof err.error === "object") {
146
89
  if (typeof err.error.status === "number") return err.error.status;
147
90
  if (typeof err.error.statusCode === "number") return err.error.statusCode;
148
91
  }
149
- return undefined;
92
+ return void 0;
150
93
  }
151
-
152
- function getErrorCode(error: unknown): string | undefined {
153
- if (!error || typeof error !== "object") return undefined;
154
- const err = error as Record<string, any>;
94
+ function getErrorCode(error) {
95
+ if (!error || typeof error !== "object") return void 0;
96
+ const err = error;
155
97
  if (typeof err.code === "string") return err.code;
156
98
  if (err.error && typeof err.error === "object" && typeof err.error.code === "string") {
157
99
  return err.error.code;
158
100
  }
159
- return undefined;
101
+ return void 0;
160
102
  }
161
-
162
- function getProviderLabel(baseURL: string | undefined, model: string): string {
103
+ function getProviderLabel(baseURL, model) {
163
104
  const base = baseURL || "";
164
-
165
105
  if (base) {
166
106
  if (/api\.jina\.ai/i.test(base)) return "Jina";
167
107
  if (/localhost:11434|127\.0\.0\.1:11434|\/ollama\b/i.test(base)) return "Ollama";
168
108
  if (/api\.openai\.com/i.test(base)) return "OpenAI";
169
-
170
109
  try {
171
110
  return new URL(base).host;
172
111
  } catch {
173
112
  return base;
174
113
  }
175
114
  }
176
-
177
115
  if (/^jina-/i.test(model)) return "Jina";
178
-
179
116
  return "embedding provider";
180
117
  }
181
-
182
- function isAuthError(error: unknown): boolean {
118
+ function isAuthError(error) {
183
119
  const status = getErrorStatus(error);
184
120
  if (status === 401 || status === 403) return true;
185
-
186
121
  const code = getErrorCode(error);
187
122
  if (code && /invalid.*key|auth|forbidden|unauthorized/i.test(code)) return true;
188
-
189
123
  const msg = getErrorMessage(error);
190
124
  return /\b401\b|\b403\b|invalid api key|api key expired|expired api key|forbidden|unauthorized|authentication failed|access denied/i.test(msg);
191
125
  }
192
-
193
- function isNetworkError(error: unknown): boolean {
126
+ function isNetworkError(error) {
194
127
  const code = getErrorCode(error);
195
128
  if (code && /ECONNREFUSED|ECONNRESET|ENOTFOUND|EHOSTUNREACH|ETIMEDOUT/i.test(code)) {
196
129
  return true;
197
130
  }
198
-
199
131
  const msg = getErrorMessage(error);
200
132
  return /ECONNREFUSED|ECONNRESET|ENOTFOUND|EHOSTUNREACH|ETIMEDOUT|fetch failed|network error|socket hang up|connection refused|getaddrinfo/i.test(msg);
201
133
  }
202
-
203
- export function formatEmbeddingProviderError(
204
- error: unknown,
205
- opts: { baseURL?: string; model: string; mode?: "single" | "batch" },
206
- ): string {
134
+ function formatEmbeddingProviderError(error, opts) {
207
135
  const raw = getErrorMessage(error).trim();
208
- if (
209
- raw.startsWith("Embedding provider authentication failed") ||
210
- raw.startsWith("Embedding provider unreachable") ||
211
- raw.startsWith("Failed to generate embedding from ") ||
212
- raw.startsWith("Failed to generate batch embeddings from ")
213
- ) {
136
+ if (raw.startsWith("Embedding provider authentication failed") || raw.startsWith("Embedding provider unreachable") || raw.startsWith("Failed to generate embedding from ") || raw.startsWith("Failed to generate batch embeddings from ")) {
214
137
  return raw;
215
138
  }
216
-
217
139
  const status = getErrorStatus(error);
218
140
  const code = getErrorCode(error);
219
141
  const provider = getProviderLabel(opts.baseURL, opts.model);
220
142
  const detail = raw.length > 0 ? raw : "unknown error";
221
143
  const suffix = [status, code].filter(Boolean).join(" ");
222
144
  const detailText = suffix ? `${suffix}: ${detail}` : detail;
223
- const genericPrefix =
224
- opts.mode === "batch"
225
- ? `Failed to generate batch embeddings from ${provider}: `
226
- : `Failed to generate embedding from ${provider}: `;
227
-
145
+ const genericPrefix = opts.mode === "batch" ? `Failed to generate batch embeddings from ${provider}: ` : `Failed to generate embedding from ${provider}: `;
228
146
  if (isAuthError(error)) {
229
147
  let hint = `Check embedding.apiKey and endpoint for ${provider}.`;
230
148
  if (provider === "Jina") {
231
- hint +=
232
- " If your Jina key expired or lost access, replace the key or switch to a local OpenAI-compatible endpoint such as Ollama (for example baseURL http://127.0.0.1:11434/v1, with a matching model and embedding.dimensions).";
149
+ hint += " If your Jina key expired or lost access, replace the key or switch to a local OpenAI-compatible endpoint such as Ollama (for example baseURL http://127.0.0.1:11434/v1, with a matching model and embedding.dimensions).";
233
150
  } else if (provider === "Ollama") {
234
- hint +=
235
- " Ollama usually works with a dummy apiKey; verify the local server is running, the model is pulled, and embedding.dimensions matches the model output.";
151
+ hint += " Ollama usually works with a dummy apiKey; verify the local server is running, the model is pulled, and embedding.dimensions matches the model output.";
236
152
  }
237
153
  return `Embedding provider authentication failed (${detailText}). ${hint}`;
238
154
  }
239
-
240
155
  if (isNetworkError(error)) {
241
156
  let hint = `Verify the endpoint is reachable`;
242
157
  if (opts.baseURL) {
243
158
  hint += ` at ${opts.baseURL}`;
244
159
  }
245
- hint += ` and that model \"${opts.model}\" is available.`;
160
+ hint += ` and that model "${opts.model}" is available.`;
246
161
  return `Embedding provider unreachable (${detailText}). ${hint}`;
247
162
  }
248
-
249
163
  return `${genericPrefix}${detailText}`;
250
164
  }
251
-
252
- export function getVectorDimensions(model: string, overrideDims?: number): number {
165
+ function getVectorDimensions(model, overrideDims) {
253
166
  if (overrideDims && overrideDims > 0) {
254
167
  return overrideDims;
255
168
  }
256
-
257
169
  const dims = EMBEDDING_DIMENSIONS[model];
258
170
  if (!dims) {
259
171
  throw new Error(
260
172
  `Unsupported embedding model: ${model}. Either add it to EMBEDDING_DIMENSIONS or set embedding.dimensions in config.`
261
173
  );
262
174
  }
263
-
264
175
  return dims;
265
176
  }
266
-
267
- // ============================================================================
268
- // Embedder Class
269
- // ============================================================================
270
-
271
- export class Embedder {
177
+ class Embedder {
272
178
  /** Pool of OpenAI clients — one per API key for round-robin rotation. */
273
- private clients: OpenAI[];
179
+ clients;
274
180
  /** Round-robin index for client rotation. */
275
- private _clientIndex: number = 0;
276
-
277
- public readonly dimensions: number;
278
- private readonly _cache: EmbeddingCache;
279
-
280
- private readonly _model: string;
281
- private readonly _baseURL?: string;
282
- private readonly _taskQuery?: string;
283
- private readonly _taskPassage?: string;
284
- private readonly _normalized?: boolean;
285
-
181
+ _clientIndex = 0;
182
+ dimensions;
183
+ _cache;
184
+ _model;
185
+ _baseURL;
186
+ _taskQuery;
187
+ _taskPassage;
188
+ _normalized;
286
189
  /** Optional requested dimensions to pass through to the embedding provider (OpenAI-compatible). */
287
- private readonly _requestDimensions?: number;
190
+ _requestDimensions;
288
191
  /** Enable automatic chunking for long documents (default: true) */
289
- private readonly _autoChunk: boolean;
290
-
291
- constructor(config: EmbeddingConfig & { chunking?: boolean }) {
292
- // Normalize apiKey to array and resolve environment variables
192
+ _autoChunk;
193
+ constructor(config) {
293
194
  const apiKeys = Array.isArray(config.apiKey) ? config.apiKey : [config.apiKey];
294
- const resolvedKeys = apiKeys.map(k => resolveEnvVars(k));
295
-
195
+ const resolvedKeys = apiKeys.map((k) => resolveEnvVars(k));
296
196
  this._model = config.model;
297
197
  this._baseURL = config.baseURL;
298
198
  this._taskQuery = config.taskQuery;
299
199
  this._taskPassage = config.taskPassage;
300
200
  this._normalized = config.normalized;
301
201
  this._requestDimensions = config.dimensions;
302
- // Enable auto-chunking by default for better handling of long documents
303
202
  this._autoChunk = config.chunking !== false;
304
-
305
- // Create a client pool — one OpenAI client per key
306
- this.clients = resolvedKeys.map(key => new OpenAI({
203
+ this.clients = resolvedKeys.map((key) => new OpenAI({
307
204
  apiKey: key,
308
- ...(config.baseURL ? { baseURL: config.baseURL } : {}),
205
+ ...config.baseURL ? { baseURL: config.baseURL } : {}
309
206
  }));
310
-
311
207
  if (this.clients.length > 1) {
312
- console.log(`[mnemo] Initialized ${this.clients.length} API keys for round-robin rotation`);
208
+ log.info(`Initialized ${this.clients.length} API keys for round-robin rotation`);
313
209
  }
314
-
315
210
  this.dimensions = getVectorDimensions(config.model, config.dimensions);
316
- this._cache = new EmbeddingCache(256, 30); // 256 entries, 30 min TTL
211
+ this._cache = new EmbeddingCache(256, 30);
317
212
  }
318
-
319
213
  // --------------------------------------------------------------------------
320
214
  // Multi-key rotation helpers
321
215
  // --------------------------------------------------------------------------
322
-
323
216
  /** Return the next client in round-robin order. */
324
- private nextClient(): OpenAI {
217
+ nextClient() {
325
218
  const client = this.clients[this._clientIndex % this.clients.length];
326
219
  this._clientIndex = (this._clientIndex + 1) % this.clients.length;
327
220
  return client;
328
221
  }
329
-
330
222
  /** Check whether an error is a rate-limit / quota-exceeded / overload error. */
331
- private isRateLimitError(error: unknown): boolean {
223
+ isRateLimitError(error) {
332
224
  if (!error || typeof error !== "object") return false;
333
-
334
- const err = error as Record<string, any>;
335
-
336
- // HTTP status: 429 (rate limit) or 503 (service overload)
225
+ const err = error;
337
226
  if (err.status === 429 || err.status === 503) return true;
338
-
339
- // OpenAI SDK structured error code
340
227
  if (err.code === "rate_limit_exceeded" || err.code === "insufficient_quota") return true;
341
-
342
- // Nested error object (some providers)
343
228
  const nested = err.error;
344
229
  if (nested && typeof nested === "object") {
345
230
  if (nested.type === "rate_limit_exceeded" || nested.type === "insufficient_quota") return true;
346
231
  if (nested.code === "rate_limit_exceeded" || nested.code === "insufficient_quota") return true;
347
232
  }
348
-
349
- // Fallback: message text matching
350
233
  const msg = error instanceof Error ? error.message : String(error);
351
234
  return /rate.limit|quota|too many requests|insufficient.*credit|429|503.*overload/i.test(msg);
352
235
  }
353
-
354
236
  /**
355
237
  * Call embeddings.create with automatic key rotation on rate-limit errors.
356
238
  * Tries each key in the pool at most once before giving up.
357
239
  */
358
- private async embedWithRetry(payload: any): Promise<any> {
240
+ // TODO: type payload as OpenAI.EmbeddingCreateParams & extra provider fields; type return as CreateEmbeddingResponse
241
+ async embedWithRetry(payload) {
359
242
  const maxAttempts = this.clients.length;
360
- let lastError: Error | undefined;
361
-
243
+ let lastError;
362
244
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
363
245
  const client = this.nextClient();
364
246
  try {
365
247
  return await client.embeddings.create(payload);
366
248
  } catch (error) {
367
249
  lastError = error instanceof Error ? error : new Error(String(error));
368
-
369
250
  if (this.isRateLimitError(error) && attempt < maxAttempts - 1) {
370
- console.log(
371
- `[mnemo] Attempt ${attempt + 1}/${maxAttempts} hit rate limit, rotating to next key...`
251
+ log.info(
252
+ `Attempt ${attempt + 1}/${maxAttempts} hit rate limit, rotating to next key...`
372
253
  );
373
254
  continue;
374
255
  }
375
-
376
- // Non-rate-limit error → don't retry, let caller handle (e.g. chunking)
377
256
  if (!this.isRateLimitError(error)) {
378
257
  throw error;
379
258
  }
380
259
  }
381
260
  }
382
-
383
- // All keys exhausted with rate-limit errors
384
261
  throw new Error(
385
262
  `All ${maxAttempts} API keys exhausted (rate limited). Last error: ${lastError?.message || "unknown"}`,
386
263
  { cause: lastError }
387
264
  );
388
265
  }
389
-
390
266
  /** Number of API keys in the rotation pool. */
391
- get keyCount(): number {
267
+ get keyCount() {
392
268
  return this.clients.length;
393
269
  }
394
-
395
270
  // --------------------------------------------------------------------------
396
271
  // Backward-compatible API
397
272
  // --------------------------------------------------------------------------
398
-
399
273
  /**
400
274
  * Backward-compatible embedding API.
401
275
  *
402
276
  * Historically the plugin used a single `embed()` method for both query and
403
277
  * passage embeddings. With task-aware providers we treat this as passage.
404
278
  */
405
- async embed(text: string): Promise<number[]> {
279
+ async embed(text) {
406
280
  return this.embedPassage(text);
407
281
  }
408
-
409
282
  /** Backward-compatible batch embedding API (treated as passage). */
410
- async embedBatch(texts: string[]): Promise<number[][]> {
283
+ async embedBatch(texts) {
411
284
  return this.embedBatchPassage(texts);
412
285
  }
413
-
414
286
  // --------------------------------------------------------------------------
415
287
  // Task-aware API
416
288
  // --------------------------------------------------------------------------
417
-
418
- async embedQuery(text: string): Promise<number[]> {
289
+ async embedQuery(text) {
419
290
  return this.embedSingle(text, this._taskQuery);
420
291
  }
421
-
422
- async embedPassage(text: string): Promise<number[]> {
292
+ async embedPassage(text) {
423
293
  return this.embedSingle(text, this._taskPassage);
424
294
  }
425
-
426
- async embedBatchQuery(texts: string[]): Promise<number[][]> {
295
+ async embedBatchQuery(texts) {
427
296
  return this.embedMany(texts, this._taskQuery);
428
297
  }
429
-
430
- async embedBatchPassage(texts: string[]): Promise<number[][]> {
298
+ async embedBatchPassage(texts) {
431
299
  return this.embedMany(texts, this._taskPassage);
432
300
  }
433
-
434
301
  // --------------------------------------------------------------------------
435
302
  // Internals
436
303
  // --------------------------------------------------------------------------
437
-
438
- private validateEmbedding(embedding: number[]): void {
304
+ validateEmbedding(embedding) {
439
305
  if (!Array.isArray(embedding)) {
440
306
  throw new Error(`Embedding is not an array (got ${typeof embedding})`);
441
307
  }
@@ -445,91 +311,66 @@ export class Embedder {
445
311
  );
446
312
  }
447
313
  }
448
-
449
- private buildPayload(input: string | string[], task?: string): any {
450
- const payload: any = {
314
+ // TODO: type return as OpenAI.EmbeddingCreateParams & provider-specific fields
315
+ buildPayload(input, task) {
316
+ const payload = {
451
317
  model: this.model,
452
- input,
318
+ input
453
319
  };
454
-
455
- // Force float output to avoid SDK default base64 decoding path.
456
- // Skip for providers that reject this field (e.g. Voyage).
457
320
  const isVoyage = this._baseURL?.includes("voyageai.com");
458
321
  if (!isVoyage) {
459
322
  payload.encoding_format = "float";
460
323
  }
461
-
462
- // Voyage uses "input_type" instead of "task"
463
324
  if (task && isVoyage) {
464
- // Map taskQuery/taskPassage to Voyage input_type
465
325
  if (task.includes("query")) payload.input_type = "query";
466
326
  else if (task.includes("passage") || task.includes("document")) payload.input_type = "document";
467
327
  else payload.input_type = task;
468
328
  } else if (task) {
469
329
  payload.task = task;
470
330
  }
471
- if (this._normalized !== undefined) payload.normalized = this._normalized;
472
-
473
- // Some OpenAI-compatible providers support requesting a specific vector size.
474
- // We only pass it through when explicitly configured to avoid breaking providers
475
- // that reject unknown fields.
331
+ if (this._normalized !== void 0) payload.normalized = this._normalized;
476
332
  if (this._requestDimensions && this._requestDimensions > 0 && !isVoyage) {
477
333
  payload.dimensions = this._requestDimensions;
478
334
  }
479
-
480
-
481
-
482
335
  return payload;
483
336
  }
484
-
485
- private async embedSingle(text: string, task?: string): Promise<number[]> {
337
+ async embedSingle(text, task) {
486
338
  if (!text || text.trim().length === 0) {
487
339
  throw new Error("Cannot embed empty text");
488
340
  }
489
-
490
- // Check cache first
491
341
  const cached = this._cache.get(text, task);
492
342
  if (cached) return cached;
493
-
494
343
  try {
495
344
  const response = await this.embedWithRetry(this.buildPayload(text, task));
496
- const embedding = response.data[0]?.embedding as number[] | undefined;
345
+ const embedding = response.data[0]?.embedding;
497
346
  if (!embedding) {
498
347
  throw new Error("No embedding returned from provider");
499
348
  }
500
-
501
349
  this.validateEmbedding(embedding);
502
350
  this._cache.set(text, task, embedding);
503
351
  return embedding;
504
352
  } catch (error) {
505
- // Check if this is a context length exceeded error and try chunking
506
353
  const errorMsg = error instanceof Error ? error.message : String(error);
507
354
  const isContextError = /context|too long|exceed|length/i.test(errorMsg);
508
-
509
355
  if (isContextError && this._autoChunk) {
510
356
  try {
511
- console.log(`Document exceeded context limit (${errorMsg}), attempting chunking...`);
357
+ log.info(`Document exceeded context limit (${errorMsg}), attempting chunking...`);
512
358
  const chunkResult = smartChunk(text, this._model);
513
-
514
359
  if (chunkResult.chunks.length === 0) {
515
360
  throw new Error(`Failed to chunk document: ${errorMsg}`);
516
361
  }
517
-
518
- // Embed all chunks in parallel
519
- console.log(`Split document into ${chunkResult.chunkCount} chunks for embedding`);
362
+ log.info(`Split document into ${chunkResult.chunkCount} chunks for embedding`);
520
363
  const chunkEmbeddings = await Promise.all(
521
364
  chunkResult.chunks.map(async (chunk, idx) => {
522
365
  try {
523
366
  const embedding = await this.embedSingle(chunk, task);
524
367
  return { embedding };
525
368
  } catch (chunkError) {
526
- console.warn(`Failed to embed chunk ${idx}:`, chunkError);
369
+ log.warn(`Failed to embed chunk ${idx}:`, chunkError);
527
370
  throw chunkError;
528
371
  }
529
372
  })
530
373
  );
531
-
532
- // Compute average embedding across chunks
533
374
  const avgEmbedding = chunkEmbeddings.reduce(
534
375
  (sum, { embedding }) => {
535
376
  for (let i = 0; i < embedding.length; i++) {
@@ -539,101 +380,75 @@ export class Embedder {
539
380
  },
540
381
  new Array(this.dimensions).fill(0)
541
382
  );
542
-
543
- const finalEmbedding = avgEmbedding.map(v => v / chunkEmbeddings.length);
544
-
545
- // Cache the result for the original text (using its hash)
383
+ const finalEmbedding = avgEmbedding.map((v) => v / chunkEmbeddings.length);
546
384
  this._cache.set(text, task, finalEmbedding);
547
- console.log(`Successfully embedded long document as ${chunkEmbeddings.length} averaged chunks`);
548
-
385
+ log.info(`Successfully embedded long document as ${chunkEmbeddings.length} averaged chunks`);
549
386
  return finalEmbedding;
550
387
  } catch (chunkError) {
551
- // If chunking fails, throw the original error
552
- console.warn(`Chunking failed, using original error:`, chunkError);
553
- const friendly = formatEmbeddingProviderError(error, {
388
+ log.warn(`Chunking failed, using original error:`, chunkError);
389
+ const friendly2 = formatEmbeddingProviderError(error, {
554
390
  baseURL: this._baseURL,
555
391
  model: this._model,
556
- mode: "single",
392
+ mode: "single"
557
393
  });
558
- throw new Error(friendly, { cause: error });
394
+ throw new Error(friendly2, { cause: error });
559
395
  }
560
396
  }
561
-
562
397
  const friendly = formatEmbeddingProviderError(error, {
563
398
  baseURL: this._baseURL,
564
399
  model: this._model,
565
- mode: "single",
400
+ mode: "single"
566
401
  });
567
- throw new Error(friendly, { cause: error instanceof Error ? error : undefined });
402
+ throw new Error(friendly, { cause: error instanceof Error ? error : void 0 });
568
403
  }
569
404
  }
570
-
571
- private async embedMany(texts: string[], task?: string): Promise<number[][]> {
405
+ async embedMany(texts, task) {
572
406
  if (!texts || texts.length === 0) {
573
407
  return [];
574
408
  }
575
-
576
- // Filter out empty texts and track indices
577
- const validTexts: string[] = [];
578
- const validIndices: number[] = [];
579
-
409
+ const validTexts = [];
410
+ const validIndices = [];
580
411
  texts.forEach((text, index) => {
581
412
  if (text && text.trim().length > 0) {
582
413
  validTexts.push(text);
583
414
  validIndices.push(index);
584
415
  }
585
416
  });
586
-
587
417
  if (validTexts.length === 0) {
588
418
  return texts.map(() => []);
589
419
  }
590
-
591
420
  try {
592
421
  const response = await this.embedWithRetry(
593
422
  this.buildPayload(validTexts, task)
594
423
  );
595
-
596
- // Create result array with proper length
597
- const results: number[][] = new Array(texts.length);
598
-
599
- // Fill in embeddings for valid texts
424
+ const results = new Array(texts.length);
600
425
  response.data.forEach((item, idx) => {
601
426
  const originalIndex = validIndices[idx];
602
- const embedding = item.embedding as number[];
603
-
427
+ const embedding = item.embedding;
604
428
  this.validateEmbedding(embedding);
605
429
  results[originalIndex] = embedding;
606
430
  });
607
-
608
- // Fill empty arrays for invalid texts
609
431
  for (let i = 0; i < texts.length; i++) {
610
432
  if (!results[i]) {
611
433
  results[i] = [];
612
434
  }
613
435
  }
614
-
615
436
  return results;
616
437
  } catch (error) {
617
- // Check if this is a context length exceeded error and try chunking each text
618
438
  const errorMsg = error instanceof Error ? error.message : String(error);
619
439
  const isContextError = /context|too long|exceed|length/i.test(errorMsg);
620
-
621
440
  if (isContextError && this._autoChunk) {
622
441
  try {
623
- console.log(`Batch embedding failed with context error, attempting chunking...`);
624
-
442
+ log.info(`Batch embedding failed with context error, attempting chunking...`);
625
443
  const chunkResults = await Promise.all(
626
444
  validTexts.map(async (text, idx) => {
627
445
  const chunkResult = smartChunk(text, this._model);
628
446
  if (chunkResult.chunks.length === 0) {
629
447
  throw new Error("Chunker produced no chunks");
630
448
  }
631
-
632
- // Embed all chunks in parallel, then average.
633
449
  const embeddings = await Promise.all(
634
450
  chunkResult.chunks.map((chunk) => this.embedSingle(chunk, task))
635
451
  );
636
-
637
452
  const avgEmbedding = embeddings.reduce(
638
453
  (sum, emb) => {
639
454
  for (let i = 0; i < emb.length; i++) {
@@ -643,20 +458,13 @@ export class Embedder {
643
458
  },
644
459
  new Array(this.dimensions).fill(0)
645
460
  );
646
-
647
461
  const finalEmbedding = avgEmbedding.map((v) => v / embeddings.length);
648
-
649
- // Cache the averaged embedding for the original (long) text.
650
462
  this._cache.set(text, task, finalEmbedding);
651
-
652
463
  return { embedding: finalEmbedding, index: validIndices[idx] };
653
464
  })
654
465
  );
655
-
656
- console.log(`Successfully chunked and embedded ${chunkResults.length} long documents`);
657
-
658
- // Build results array
659
- const results: number[][] = new Array(texts.length);
466
+ log.info(`Successfully chunked and embedded ${chunkResults.length} long documents`);
467
+ const results = new Array(texts.length);
660
468
  chunkResults.forEach(({ embedding, index }) => {
661
469
  if (embedding.length > 0) {
662
470
  this.validateEmbedding(embedding);
@@ -665,71 +473,65 @@ export class Embedder {
665
473
  results[index] = [];
666
474
  }
667
475
  });
668
-
669
- // Fill empty arrays for invalid texts
670
476
  for (let i = 0; i < texts.length; i++) {
671
477
  if (!results[i]) {
672
478
  results[i] = [];
673
479
  }
674
480
  }
675
-
676
481
  return results;
677
482
  } catch (chunkError) {
678
- const friendly = formatEmbeddingProviderError(error, {
483
+ const friendly2 = formatEmbeddingProviderError(error, {
679
484
  baseURL: this._baseURL,
680
485
  model: this._model,
681
- mode: "batch",
486
+ mode: "batch"
682
487
  });
683
- throw new Error(`Failed to embed documents after chunking attempt: ${friendly}`, {
684
- cause: error instanceof Error ? error : undefined,
488
+ throw new Error(`Failed to embed documents after chunking attempt: ${friendly2}`, {
489
+ cause: error instanceof Error ? error : void 0
685
490
  });
686
491
  }
687
492
  }
688
-
689
493
  const friendly = formatEmbeddingProviderError(error, {
690
494
  baseURL: this._baseURL,
691
495
  model: this._model,
692
- mode: "batch",
496
+ mode: "batch"
693
497
  });
694
498
  throw new Error(friendly, {
695
- cause: error instanceof Error ? error : undefined,
499
+ cause: error instanceof Error ? error : void 0
696
500
  });
697
501
  }
698
502
  }
699
-
700
- get model(): string {
503
+ get model() {
701
504
  return this._model;
702
505
  }
703
-
704
506
  // Test connection and validate configuration
705
- async test(): Promise<{ success: boolean; error?: string; dimensions?: number }> {
507
+ async test() {
706
508
  try {
707
509
  const testEmbedding = await this.embedPassage("test");
708
510
  return {
709
511
  success: true,
710
- dimensions: testEmbedding.length,
512
+ dimensions: testEmbedding.length
711
513
  };
712
514
  } catch (error) {
713
-
714
515
  return {
715
516
  success: false,
716
- error: error instanceof Error ? error.message : String(error),
517
+ error: error instanceof Error ? error.message : String(error)
717
518
  };
718
519
  }
719
520
  }
720
-
721
521
  get cacheStats() {
722
522
  return {
723
523
  ...this._cache.stats,
724
- keyCount: this.clients.length,
524
+ keyCount: this.clients.length
725
525
  };
726
526
  }
727
527
  }
728
-
729
- // ============================================================================
730
- // Factory Function
731
- // ============================================================================
732
-
733
- export function createEmbedder(config: EmbeddingConfig): Embedder {
528
+ function createEmbedder(config) {
734
529
  return new Embedder(config);
735
530
  }
531
+ export {
532
+ Embedder,
533
+ createEmbedder,
534
+ formatEmbeddingProviderError,
535
+ getVectorDimensions
536
+ };
537
+ //# sourceMappingURL=embedder.js.map