family-ai-agent 1.0.6 → 1.0.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.
Files changed (149) hide show
  1. package/.letta/settings.local.json +3 -0
  2. package/dist/cli/index.js +1 -1
  3. package/dist/database/adapters/base-adapter.d.ts +81 -0
  4. package/dist/database/adapters/base-adapter.d.ts.map +1 -0
  5. package/dist/database/adapters/base-adapter.js +105 -0
  6. package/dist/database/adapters/base-adapter.js.map +1 -0
  7. package/dist/database/adapters/index.d.ts +49 -0
  8. package/dist/database/adapters/index.d.ts.map +1 -0
  9. package/dist/database/adapters/index.js +200 -0
  10. package/dist/database/adapters/index.js.map +1 -0
  11. package/dist/database/adapters/postgres-adapter.d.ts +75 -0
  12. package/dist/database/adapters/postgres-adapter.d.ts.map +1 -0
  13. package/dist/database/adapters/postgres-adapter.js +225 -0
  14. package/dist/database/adapters/postgres-adapter.js.map +1 -0
  15. package/dist/database/adapters/sqlite-adapter.d.ts +72 -0
  16. package/dist/database/adapters/sqlite-adapter.d.ts.map +1 -0
  17. package/dist/database/adapters/sqlite-adapter.js +368 -0
  18. package/dist/database/adapters/sqlite-adapter.js.map +1 -0
  19. package/dist/database/cache/cache-keys.d.ts +180 -0
  20. package/dist/database/cache/cache-keys.d.ts.map +1 -0
  21. package/dist/database/cache/cache-keys.js +107 -0
  22. package/dist/database/cache/cache-keys.js.map +1 -0
  23. package/dist/database/cache/index.d.ts +24 -0
  24. package/dist/database/cache/index.d.ts.map +1 -0
  25. package/dist/database/cache/index.js +34 -0
  26. package/dist/database/cache/index.js.map +1 -0
  27. package/dist/database/cache/query-cache.d.ts +67 -0
  28. package/dist/database/cache/query-cache.d.ts.map +1 -0
  29. package/dist/database/cache/query-cache.js +177 -0
  30. package/dist/database/cache/query-cache.js.map +1 -0
  31. package/dist/database/client.d.ts +63 -4
  32. package/dist/database/client.d.ts.map +1 -1
  33. package/dist/database/client.js +147 -59
  34. package/dist/database/client.js.map +1 -1
  35. package/dist/database/db-config.d.ts +104 -0
  36. package/dist/database/db-config.d.ts.map +1 -0
  37. package/dist/database/db-config.js +167 -0
  38. package/dist/database/db-config.js.map +1 -0
  39. package/dist/database/drizzle/index.d.ts +42 -0
  40. package/dist/database/drizzle/index.d.ts.map +1 -0
  41. package/dist/database/drizzle/index.js +48 -0
  42. package/dist/database/drizzle/index.js.map +1 -0
  43. package/dist/database/drizzle/schema/audit.d.ts +533 -0
  44. package/dist/database/drizzle/schema/audit.d.ts.map +1 -0
  45. package/dist/database/drizzle/schema/audit.js +71 -0
  46. package/dist/database/drizzle/schema/audit.js.map +1 -0
  47. package/dist/database/drizzle/schema/checkpoints.d.ts +665 -0
  48. package/dist/database/drizzle/schema/checkpoints.d.ts.map +1 -0
  49. package/dist/database/drizzle/schema/checkpoints.js +110 -0
  50. package/dist/database/drizzle/schema/checkpoints.js.map +1 -0
  51. package/dist/database/drizzle/schema/conversations.d.ts +449 -0
  52. package/dist/database/drizzle/schema/conversations.d.ts.map +1 -0
  53. package/dist/database/drizzle/schema/conversations.js +91 -0
  54. package/dist/database/drizzle/schema/conversations.js.map +1 -0
  55. package/dist/database/drizzle/schema/documents.d.ts +600 -0
  56. package/dist/database/drizzle/schema/documents.d.ts.map +1 -0
  57. package/dist/database/drizzle/schema/documents.js +100 -0
  58. package/dist/database/drizzle/schema/documents.js.map +1 -0
  59. package/dist/database/drizzle/schema/index.d.ts +3084 -0
  60. package/dist/database/drizzle/schema/index.d.ts.map +1 -0
  61. package/dist/database/drizzle/schema/index.js +46 -0
  62. package/dist/database/drizzle/schema/index.js.map +1 -0
  63. package/dist/database/drizzle/schema/memories.d.ts +435 -0
  64. package/dist/database/drizzle/schema/memories.d.ts.map +1 -0
  65. package/dist/database/drizzle/schema/memories.js +73 -0
  66. package/dist/database/drizzle/schema/memories.js.map +1 -0
  67. package/dist/database/drizzle/schema/tasks.d.ts +565 -0
  68. package/dist/database/drizzle/schema/tasks.d.ts.map +1 -0
  69. package/dist/database/drizzle/schema/tasks.js +84 -0
  70. package/dist/database/drizzle/schema/tasks.js.map +1 -0
  71. package/dist/database/health/circuit-breaker.d.ts +81 -0
  72. package/dist/database/health/circuit-breaker.d.ts.map +1 -0
  73. package/dist/database/health/circuit-breaker.js +184 -0
  74. package/dist/database/health/circuit-breaker.js.map +1 -0
  75. package/dist/database/health/health-monitor.d.ts +69 -0
  76. package/dist/database/health/health-monitor.d.ts.map +1 -0
  77. package/dist/database/health/health-monitor.js +174 -0
  78. package/dist/database/health/health-monitor.js.map +1 -0
  79. package/dist/database/health/index.d.ts +27 -0
  80. package/dist/database/health/index.d.ts.map +1 -0
  81. package/dist/database/health/index.js +23 -0
  82. package/dist/database/health/index.js.map +1 -0
  83. package/dist/database/index.d.ts +16 -0
  84. package/dist/database/index.d.ts.map +1 -0
  85. package/dist/database/index.js +41 -0
  86. package/dist/database/index.js.map +1 -0
  87. package/dist/database/migrations/index.d.ts +34 -0
  88. package/dist/database/migrations/index.d.ts.map +1 -0
  89. package/dist/database/migrations/index.js +45 -0
  90. package/dist/database/migrations/index.js.map +1 -0
  91. package/dist/database/migrations/migrator.d.ts +77 -0
  92. package/dist/database/migrations/migrator.d.ts.map +1 -0
  93. package/dist/database/migrations/migrator.js +258 -0
  94. package/dist/database/migrations/migrator.js.map +1 -0
  95. package/dist/database/migrations/versions/001_initial.d.ts +9 -0
  96. package/dist/database/migrations/versions/001_initial.d.ts.map +1 -0
  97. package/dist/database/migrations/versions/001_initial.js +183 -0
  98. package/dist/database/migrations/versions/001_initial.js.map +1 -0
  99. package/dist/database/types.d.ts +255 -0
  100. package/dist/database/types.d.ts.map +1 -0
  101. package/dist/database/types.js +8 -0
  102. package/dist/database/types.js.map +1 -0
  103. package/dist/database/vector/embedding-cache.d.ts +92 -0
  104. package/dist/database/vector/embedding-cache.d.ts.map +1 -0
  105. package/dist/database/vector/embedding-cache.js +185 -0
  106. package/dist/database/vector/embedding-cache.js.map +1 -0
  107. package/dist/database/vector/hnsw-index.d.ts +111 -0
  108. package/dist/database/vector/hnsw-index.d.ts.map +1 -0
  109. package/dist/database/vector/hnsw-index.js +337 -0
  110. package/dist/database/vector/hnsw-index.js.map +1 -0
  111. package/dist/database/vector/index.d.ts +75 -0
  112. package/dist/database/vector/index.d.ts.map +1 -0
  113. package/dist/database/vector/index.js +213 -0
  114. package/dist/database/vector/index.js.map +1 -0
  115. package/dist/database/vector/similarity.d.ts +67 -0
  116. package/dist/database/vector/similarity.d.ts.map +1 -0
  117. package/dist/database/vector/similarity.js +176 -0
  118. package/dist/database/vector/similarity.js.map +1 -0
  119. package/package.json +6 -3
  120. package/src/cli/index.ts +1 -1
  121. package/src/database/adapters/base-adapter.ts +171 -0
  122. package/src/database/adapters/index.ts +224 -0
  123. package/src/database/adapters/postgres-adapter.ts +285 -0
  124. package/src/database/adapters/sqlite-adapter.ts +420 -0
  125. package/src/database/cache/cache-keys.ts +150 -0
  126. package/src/database/cache/index.ts +44 -0
  127. package/src/database/cache/query-cache.ts +213 -0
  128. package/src/database/client.ts +166 -64
  129. package/src/database/db-config.ts +194 -0
  130. package/src/database/drizzle/index.ts +66 -0
  131. package/src/database/drizzle/schema/audit.ts +127 -0
  132. package/src/database/drizzle/schema/checkpoints.ts +164 -0
  133. package/src/database/drizzle/schema/conversations.ts +138 -0
  134. package/src/database/drizzle/schema/documents.ts +157 -0
  135. package/src/database/drizzle/schema/index.ts +139 -0
  136. package/src/database/drizzle/schema/memories.ts +127 -0
  137. package/src/database/drizzle/schema/tasks.ts +129 -0
  138. package/src/database/health/circuit-breaker.ts +214 -0
  139. package/src/database/health/health-monitor.ts +224 -0
  140. package/src/database/health/index.ts +41 -0
  141. package/src/database/index.ts +157 -0
  142. package/src/database/migrations/index.ts +52 -0
  143. package/src/database/migrations/migrator.ts +325 -0
  144. package/src/database/migrations/versions/001_initial.ts +198 -0
  145. package/src/database/types.ts +324 -0
  146. package/src/database/vector/embedding-cache.ts +234 -0
  147. package/src/database/vector/hnsw-index.ts +452 -0
  148. package/src/database/vector/index.ts +292 -0
  149. package/src/database/vector/similarity.ts +198 -0
@@ -0,0 +1,292 @@
1
+ /**
2
+ * Vector Search Engine
3
+ *
4
+ * Unified interface for vector similarity search.
5
+ * PostgreSQL uses pgvector, SQLite uses in-memory HNSW.
6
+ */
7
+
8
+ import { HNSWIndex, type SearchResult } from './hnsw-index.js';
9
+ import { EmbeddingCache } from './embedding-cache.js';
10
+ import { cosineSimilarity, parseEmbedding, serializeEmbedding } from './similarity.js';
11
+ import type { DatabaseAdapter, VectorSearchOptions, VectorSearchResult, VectorSearchEngine } from '../types.js';
12
+ import { createLogger } from '../../utils/logger.js';
13
+
14
+ const logger = createLogger('VectorSearch');
15
+
16
+ // Re-export components
17
+ export { HNSWIndex, type SearchResult } from './hnsw-index.js';
18
+ export { EmbeddingCache, type EmbeddingCacheStats } from './embedding-cache.js';
19
+ export * from './similarity.js';
20
+
21
+ /**
22
+ * PostgreSQL vector search engine using pgvector
23
+ */
24
+ export class PgVectorSearchEngine implements VectorSearchEngine {
25
+ private adapter: DatabaseAdapter;
26
+ private tableName: string;
27
+ private embeddingColumn: string;
28
+ private dimension: number;
29
+
30
+ constructor(
31
+ adapter: DatabaseAdapter,
32
+ options: {
33
+ tableName: string;
34
+ embeddingColumn?: string;
35
+ dimension?: number;
36
+ }
37
+ ) {
38
+ this.adapter = adapter;
39
+ this.tableName = options.tableName;
40
+ this.embeddingColumn = options.embeddingColumn ?? 'embedding';
41
+ this.dimension = options.dimension ?? 1536;
42
+ }
43
+
44
+ async initialize(): Promise<void> {
45
+ // pgvector is initialized at adapter level
46
+ logger.debug('PgVectorSearchEngine initialized', { table: this.tableName });
47
+ }
48
+
49
+ async store(id: string, embedding: number[], metadata?: Record<string, unknown>): Promise<void> {
50
+ // This is handled by the memory layer, not directly by vector engine
51
+ logger.debug('Store called on PgVectorSearchEngine', { id });
52
+ }
53
+
54
+ async search(queryEmbedding: number[], options: VectorSearchOptions = {}): Promise<VectorSearchResult[]> {
55
+ const { limit = 5, minSimilarity = 0.7, userId, memoryType } = options;
56
+ const embeddingStr = `[${queryEmbedding.join(',')}]`;
57
+
58
+ let sql = `
59
+ SELECT id, content, memory_type, metadata,
60
+ 1 - (${this.embeddingColumn} <=> $1::vector) as similarity
61
+ FROM ${this.tableName}
62
+ WHERE 1 - (${this.embeddingColumn} <=> $1::vector) >= $2
63
+ `;
64
+ const params: unknown[] = [embeddingStr, minSimilarity];
65
+ let paramIdx = 3;
66
+
67
+ if (userId) {
68
+ sql += ` AND user_id = $${paramIdx}`;
69
+ params.push(userId);
70
+ paramIdx++;
71
+ }
72
+
73
+ if (memoryType) {
74
+ sql += ` AND memory_type = $${paramIdx}`;
75
+ params.push(memoryType);
76
+ paramIdx++;
77
+ }
78
+
79
+ sql += ` ORDER BY similarity DESC LIMIT $${paramIdx}`;
80
+ params.push(limit);
81
+
82
+ const result = await this.adapter.query<{
83
+ id: string;
84
+ content: string;
85
+ memory_type: string;
86
+ metadata: Record<string, unknown>;
87
+ similarity: number;
88
+ }>(sql, params);
89
+
90
+ return result.rows.map((row) => ({
91
+ id: row.id,
92
+ content: row.content,
93
+ similarity: row.similarity,
94
+ memoryType: row.memory_type,
95
+ metadata: row.metadata,
96
+ }));
97
+ }
98
+
99
+ async remove(id: string): Promise<void> {
100
+ // Handled by memory layer
101
+ logger.debug('Remove called on PgVectorSearchEngine', { id });
102
+ }
103
+
104
+ async rebuildIndex(): Promise<void> {
105
+ // pgvector indexes are managed by PostgreSQL
106
+ logger.debug('Rebuild index called on PgVectorSearchEngine');
107
+ }
108
+
109
+ getStats(): { size: number; dimension: number } {
110
+ return { size: 0, dimension: this.dimension }; // Would need a count query
111
+ }
112
+ }
113
+
114
+ /**
115
+ * SQLite vector search engine using in-memory HNSW
116
+ */
117
+ export class SqliteVectorSearchEngine implements VectorSearchEngine {
118
+ private adapter: DatabaseAdapter;
119
+ private tableName: string;
120
+ private index: HNSWIndex;
121
+ private cache: EmbeddingCache;
122
+ private dimension: number;
123
+ private initialized: boolean = false;
124
+
125
+ constructor(
126
+ adapter: DatabaseAdapter,
127
+ options: {
128
+ tableName: string;
129
+ dimension?: number;
130
+ cacheSize?: number;
131
+ }
132
+ ) {
133
+ this.adapter = adapter;
134
+ this.tableName = options.tableName;
135
+ this.dimension = options.dimension ?? 1536;
136
+ this.index = new HNSWIndex({
137
+ dimension: this.dimension,
138
+ similarity: 'cosine',
139
+ M: 16,
140
+ efConstruction: 200,
141
+ });
142
+ this.cache = new EmbeddingCache({
143
+ maxSize: options.cacheSize ?? 10000,
144
+ dimension: this.dimension,
145
+ });
146
+ }
147
+
148
+ async initialize(): Promise<void> {
149
+ if (this.initialized) return;
150
+
151
+ // Load all embeddings from database into memory
152
+ const result = await this.adapter.query<{
153
+ id: string;
154
+ embedding: string;
155
+ memory_type: string;
156
+ content: string;
157
+ }>(`SELECT id, embedding, memory_type, content FROM ${this.tableName}`);
158
+
159
+ for (const row of result.rows) {
160
+ const embedding = parseEmbedding(row.embedding);
161
+ if (embedding && embedding.length === this.dimension) {
162
+ this.index.add(row.id, embedding);
163
+ this.cache.set(row.id, embedding);
164
+ }
165
+ }
166
+
167
+ this.initialized = true;
168
+ logger.info('SqliteVectorSearchEngine initialized', {
169
+ table: this.tableName,
170
+ vectorCount: this.index.size,
171
+ });
172
+ }
173
+
174
+ async store(id: string, embedding: number[], metadata?: Record<string, unknown>): Promise<void> {
175
+ this.index.add(id, embedding);
176
+ this.cache.set(id, embedding, metadata);
177
+ }
178
+
179
+ async search(queryEmbedding: number[], options: VectorSearchOptions = {}): Promise<VectorSearchResult[]> {
180
+ const { limit = 5, minSimilarity = 0.7, userId, memoryType } = options;
181
+
182
+ // Use HNSW for initial candidates (get more than needed for filtering)
183
+ const candidates = this.index.search(queryEmbedding, limit * 3);
184
+
185
+ // Filter candidates that don't meet similarity threshold
186
+ const filtered = candidates.filter((c) => c.similarity >= minSimilarity);
187
+
188
+ if (filtered.length === 0) {
189
+ return [];
190
+ }
191
+
192
+ // Get full records from database
193
+ const ids = filtered.map((c) => c.id);
194
+ const placeholders = ids.map((_, i) => `$${i + 1}`).join(',');
195
+
196
+ let sql = `SELECT id, content, memory_type, metadata FROM ${this.tableName} WHERE id IN (${placeholders})`;
197
+ const params: unknown[] = [...ids];
198
+
199
+ if (userId) {
200
+ sql = sql.replace('WHERE', `WHERE user_id = $${params.length + 1} AND`);
201
+ params.push(userId);
202
+ }
203
+
204
+ if (memoryType) {
205
+ sql = sql.replace('WHERE', `WHERE memory_type = $${params.length + 1} AND`);
206
+ params.push(memoryType);
207
+ }
208
+
209
+ const result = await this.adapter.query<{
210
+ id: string;
211
+ content: string;
212
+ memory_type: string;
213
+ metadata: string | Record<string, unknown>;
214
+ }>(sql, params);
215
+
216
+ // Create a map for quick lookup
217
+ const recordMap = new Map<string, typeof result.rows[0]>();
218
+ for (const row of result.rows) {
219
+ recordMap.set(row.id, row);
220
+ }
221
+
222
+ // Build results with similarity scores
223
+ const results: VectorSearchResult[] = [];
224
+ for (const candidate of filtered) {
225
+ const record = recordMap.get(candidate.id);
226
+ if (record) {
227
+ results.push({
228
+ id: record.id,
229
+ content: record.content,
230
+ similarity: candidate.similarity,
231
+ memoryType: record.memory_type,
232
+ metadata: typeof record.metadata === 'string'
233
+ ? JSON.parse(record.metadata)
234
+ : record.metadata,
235
+ });
236
+ }
237
+ }
238
+
239
+ // Sort by similarity and limit
240
+ return results
241
+ .sort((a, b) => b.similarity - a.similarity)
242
+ .slice(0, limit);
243
+ }
244
+
245
+ async remove(id: string): Promise<void> {
246
+ this.index.remove(id);
247
+ this.cache.delete(id);
248
+ }
249
+
250
+ async rebuildIndex(): Promise<void> {
251
+ this.initialized = false;
252
+ this.index.clear();
253
+ this.cache.clear();
254
+ await this.initialize();
255
+ }
256
+
257
+ getStats(): { size: number; dimension: number } {
258
+ const indexStats = this.index.getStats();
259
+ return {
260
+ size: indexStats.size,
261
+ dimension: indexStats.dimension,
262
+ };
263
+ }
264
+
265
+ getCacheStats() {
266
+ return this.cache.getStats();
267
+ }
268
+ }
269
+
270
+ /**
271
+ * Create vector search engine based on adapter type
272
+ */
273
+ export function createVectorSearchEngine(
274
+ adapter: DatabaseAdapter,
275
+ options: {
276
+ tableName: string;
277
+ embeddingColumn?: string;
278
+ dimension?: number;
279
+ cacheSize?: number;
280
+ }
281
+ ): VectorSearchEngine {
282
+ if (adapter.type === 'postgresql') {
283
+ return new PgVectorSearchEngine(adapter, options);
284
+ }
285
+ return new SqliteVectorSearchEngine(adapter, options);
286
+ }
287
+
288
+ export default {
289
+ PgVectorSearchEngine,
290
+ SqliteVectorSearchEngine,
291
+ createVectorSearchEngine,
292
+ };
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Vector Similarity Functions
3
+ *
4
+ * Mathematical utilities for computing vector similarity.
5
+ * Used for in-memory vector search in SQLite mode.
6
+ */
7
+
8
+ /**
9
+ * Compute cosine similarity between two vectors
10
+ * Returns a value between -1 and 1, where 1 means identical direction
11
+ */
12
+ export function cosineSimilarity(a: number[], b: number[]): number {
13
+ if (a.length !== b.length) {
14
+ throw new Error(`Vector dimension mismatch: ${a.length} vs ${b.length}`);
15
+ }
16
+
17
+ let dotProduct = 0;
18
+ let normA = 0;
19
+ let normB = 0;
20
+
21
+ for (let i = 0; i < a.length; i++) {
22
+ dotProduct += a[i]! * b[i]!;
23
+ normA += a[i]! * a[i]!;
24
+ normB += b[i]! * b[i]!;
25
+ }
26
+
27
+ if (normA === 0 || normB === 0) {
28
+ return 0;
29
+ }
30
+
31
+ return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
32
+ }
33
+
34
+ /**
35
+ * Compute Euclidean distance between two vectors
36
+ */
37
+ export function euclideanDistance(a: number[], b: number[]): number {
38
+ if (a.length !== b.length) {
39
+ throw new Error(`Vector dimension mismatch: ${a.length} vs ${b.length}`);
40
+ }
41
+
42
+ let sum = 0;
43
+ for (let i = 0; i < a.length; i++) {
44
+ const diff = a[i]! - b[i]!;
45
+ sum += diff * diff;
46
+ }
47
+
48
+ return Math.sqrt(sum);
49
+ }
50
+
51
+ /**
52
+ * Compute dot product of two vectors
53
+ */
54
+ export function dotProduct(a: number[], b: number[]): number {
55
+ if (a.length !== b.length) {
56
+ throw new Error(`Vector dimension mismatch: ${a.length} vs ${b.length}`);
57
+ }
58
+
59
+ let sum = 0;
60
+ for (let i = 0; i < a.length; i++) {
61
+ sum += a[i]! * b[i]!;
62
+ }
63
+
64
+ return sum;
65
+ }
66
+
67
+ /**
68
+ * Compute L2 norm (magnitude) of a vector
69
+ */
70
+ export function l2Norm(v: number[]): number {
71
+ let sum = 0;
72
+ for (let i = 0; i < v.length; i++) {
73
+ sum += v[i]! * v[i]!;
74
+ }
75
+ return Math.sqrt(sum);
76
+ }
77
+
78
+ /**
79
+ * Normalize a vector to unit length
80
+ */
81
+ export function normalize(v: number[]): number[] {
82
+ const norm = l2Norm(v);
83
+ if (norm === 0) {
84
+ return v.slice();
85
+ }
86
+ return v.map((x) => x / norm);
87
+ }
88
+
89
+ /**
90
+ * Parse embedding from JSON string or array
91
+ */
92
+ export function parseEmbedding(value: string | number[] | null | undefined): number[] | null {
93
+ if (!value) return null;
94
+
95
+ if (Array.isArray(value)) {
96
+ return value;
97
+ }
98
+
99
+ try {
100
+ // Try parsing as JSON array
101
+ const parsed = JSON.parse(value);
102
+ if (Array.isArray(parsed)) {
103
+ return parsed;
104
+ }
105
+ return null;
106
+ } catch {
107
+ // Try parsing as PostgreSQL vector format: [0.1, 0.2, ...]
108
+ const match = value.match(/^\[(.+)\]$/);
109
+ if (match) {
110
+ try {
111
+ return match[1]!.split(',').map((s) => parseFloat(s.trim()));
112
+ } catch {
113
+ return null;
114
+ }
115
+ }
116
+ return null;
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Serialize embedding to JSON string
122
+ */
123
+ export function serializeEmbedding(embedding: number[]): string {
124
+ return JSON.stringify(embedding);
125
+ }
126
+
127
+ /**
128
+ * Compute hash of embedding for caching
129
+ */
130
+ export function hashEmbedding(embedding: number[]): string {
131
+ // Simple hash using first and last few elements + length
132
+ const first = embedding.slice(0, 4).map((n) => n.toFixed(4)).join(',');
133
+ const last = embedding.slice(-4).map((n) => n.toFixed(4)).join(',');
134
+ return `${embedding.length}:${first}:${last}`;
135
+ }
136
+
137
+ /**
138
+ * Quantize embedding to reduce memory usage
139
+ * Converts float32 to int8 (-128 to 127)
140
+ */
141
+ export function quantize(embedding: number[]): Int8Array {
142
+ const quantized = new Int8Array(embedding.length);
143
+ for (let i = 0; i < embedding.length; i++) {
144
+ // Scale from [-1, 1] to [-128, 127]
145
+ quantized[i] = Math.round(Math.max(-128, Math.min(127, embedding[i]! * 127)));
146
+ }
147
+ return quantized;
148
+ }
149
+
150
+ /**
151
+ * Dequantize int8 back to float
152
+ */
153
+ export function dequantize(quantized: Int8Array): number[] {
154
+ const result = new Array<number>(quantized.length);
155
+ for (let i = 0; i < quantized.length; i++) {
156
+ result[i] = quantized[i]! / 127;
157
+ }
158
+ return result;
159
+ }
160
+
161
+ /**
162
+ * Cosine similarity for quantized vectors (faster)
163
+ */
164
+ export function quantizedCosineSimilarity(a: Int8Array, b: Int8Array): number {
165
+ if (a.length !== b.length) {
166
+ throw new Error(`Vector dimension mismatch: ${a.length} vs ${b.length}`);
167
+ }
168
+
169
+ let dotProduct = 0;
170
+ let normA = 0;
171
+ let normB = 0;
172
+
173
+ for (let i = 0; i < a.length; i++) {
174
+ dotProduct += a[i]! * b[i]!;
175
+ normA += a[i]! * a[i]!;
176
+ normB += b[i]! * b[i]!;
177
+ }
178
+
179
+ if (normA === 0 || normB === 0) {
180
+ return 0;
181
+ }
182
+
183
+ return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
184
+ }
185
+
186
+ export default {
187
+ cosineSimilarity,
188
+ euclideanDistance,
189
+ dotProduct,
190
+ l2Norm,
191
+ normalize,
192
+ parseEmbedding,
193
+ serializeEmbedding,
194
+ hashEmbedding,
195
+ quantize,
196
+ dequantize,
197
+ quantizedCosineSimilarity,
198
+ };