binario 0.1.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.
package/dist/memory.js ADDED
@@ -0,0 +1,931 @@
1
+ // src/memory/utils.ts
2
+ function countTokens(text) {
3
+ if (!text) return 0;
4
+ const words = text.split(/\s+/).filter(Boolean);
5
+ const avgCharsPerWord = text.length / Math.max(words.length, 1);
6
+ const ratio = avgCharsPerWord > 6 ? 3.5 : 4;
7
+ return Math.ceil(text.length / ratio);
8
+ }
9
+ function countMessageTokens(message) {
10
+ let tokens = 0;
11
+ tokens += 4;
12
+ tokens += countTokens(message.content);
13
+ if (message.name) {
14
+ tokens += countTokens(message.name) + 1;
15
+ }
16
+ if (message.tool_calls) {
17
+ for (const call of message.tool_calls) {
18
+ tokens += countTokens(call.function.name);
19
+ tokens += countTokens(call.function.arguments);
20
+ tokens += 10;
21
+ }
22
+ }
23
+ return tokens;
24
+ }
25
+ function countMessagesTokens(messages) {
26
+ return messages.reduce((sum, msg) => sum + countMessageTokens(msg), 0);
27
+ }
28
+ function generateId() {
29
+ return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
30
+ }
31
+ function createStoredMessage(message, conversationId, options) {
32
+ return {
33
+ id: generateId(),
34
+ message,
35
+ timestamp: Date.now(),
36
+ conversationId,
37
+ tokenCount: countMessageTokens(message),
38
+ embedding: options?.embedding,
39
+ metadata: options?.metadata
40
+ };
41
+ }
42
+ function truncateMessages(messages, maxTokens, options) {
43
+ const { keepSystemMessages = true } = options ?? {};
44
+ const systemMessages = keepSystemMessages ? messages.filter((m) => m.role === "system") : [];
45
+ const nonSystemMessages = keepSystemMessages ? messages.filter((m) => m.role !== "system") : [...messages];
46
+ const systemTokens = countMessagesTokens(systemMessages);
47
+ const remainingTokens = maxTokens - systemTokens;
48
+ if (remainingTokens <= 0) {
49
+ return truncateMessagesSimple(messages, maxTokens);
50
+ }
51
+ const truncated = truncateMessagesSimple(nonSystemMessages, remainingTokens);
52
+ return [...systemMessages, ...truncated];
53
+ }
54
+ function truncateMessagesSimple(messages, maxTokens) {
55
+ let totalTokens = 0;
56
+ const result = [];
57
+ for (let i = messages.length - 1; i >= 0; i--) {
58
+ const msgTokens = countMessageTokens(messages[i]);
59
+ if (totalTokens + msgTokens > maxTokens) {
60
+ break;
61
+ }
62
+ totalTokens += msgTokens;
63
+ result.unshift(messages[i]);
64
+ }
65
+ return result;
66
+ }
67
+ function truncateMessagesByCount(messages, maxMessages, options) {
68
+ const { keepSystemMessages = true } = options ?? {};
69
+ if (messages.length <= maxMessages) {
70
+ return messages;
71
+ }
72
+ if (keepSystemMessages) {
73
+ const systemMessages = messages.filter((m) => m.role === "system");
74
+ const nonSystemMessages = messages.filter((m) => m.role !== "system");
75
+ const remainingSlots = maxMessages - systemMessages.length;
76
+ if (remainingSlots <= 0) {
77
+ return systemMessages.slice(-maxMessages);
78
+ }
79
+ return [...systemMessages, ...nonSystemMessages.slice(-remainingSlots)];
80
+ }
81
+ return messages.slice(-maxMessages);
82
+ }
83
+ function formatContextWithSummary(recentMessages, summary) {
84
+ if (!summary) {
85
+ return recentMessages;
86
+ }
87
+ const summaryMessage = {
88
+ role: "system",
89
+ content: `Previous conversation summary:
90
+ ${summary}`
91
+ };
92
+ const filtered = recentMessages.filter(
93
+ (m) => !(m.role === "system" && m.content.startsWith("Previous conversation summary:"))
94
+ );
95
+ const firstNonSystemIdx = filtered.findIndex((m) => m.role !== "system");
96
+ const insertIdx = firstNonSystemIdx === -1 ? filtered.length : firstNonSystemIdx;
97
+ return [
98
+ ...filtered.slice(0, insertIdx),
99
+ summaryMessage,
100
+ ...filtered.slice(insertIdx)
101
+ ];
102
+ }
103
+ function createContext(messages, summary) {
104
+ return {
105
+ messages,
106
+ summary: summary ?? void 0,
107
+ tokenCount: countMessagesTokens(messages),
108
+ messageCount: messages.length
109
+ };
110
+ }
111
+ function cosineSimilarity(a, b) {
112
+ if (a.length !== b.length) {
113
+ throw new Error("Vectors must have the same length");
114
+ }
115
+ let dotProduct = 0;
116
+ let normA = 0;
117
+ let normB = 0;
118
+ for (let i = 0; i < a.length; i++) {
119
+ dotProduct += a[i] * b[i];
120
+ normA += a[i] * a[i];
121
+ normB += b[i] * b[i];
122
+ }
123
+ const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
124
+ if (magnitude === 0) return 0;
125
+ return dotProduct / magnitude;
126
+ }
127
+ var DEFAULT_SUMMARY_PROMPT = `Summarize the following conversation concisely, preserving key information, decisions, and context that would be useful for continuing the conversation. Focus on:
128
+ - Main topics discussed
129
+ - Key decisions or conclusions
130
+ - Important user preferences or requirements
131
+ - Any pending questions or tasks
132
+
133
+ Conversation:
134
+ {conversation}
135
+
136
+ Summary:`;
137
+ function formatConversationForSummary(messages) {
138
+ return messages.filter((m) => m.role !== "system").map((m) => `${m.role.toUpperCase()}: ${m.content}`).join("\n\n");
139
+ }
140
+
141
+ // src/memory/stores/in-memory.ts
142
+ var InMemoryStore = class {
143
+ messages = /* @__PURE__ */ new Map();
144
+ metadata = /* @__PURE__ */ new Map();
145
+ async getMessages(conversationId) {
146
+ return this.messages.get(conversationId) ?? [];
147
+ }
148
+ async addMessage(message) {
149
+ const messages = this.messages.get(message.conversationId) ?? [];
150
+ messages.push(message);
151
+ this.messages.set(message.conversationId, messages);
152
+ }
153
+ async updateMessage(id, updates) {
154
+ for (const [conversationId, messages] of this.messages) {
155
+ const index = messages.findIndex((m) => m.id === id);
156
+ if (index !== -1) {
157
+ messages[index] = { ...messages[index], ...updates };
158
+ this.messages.set(conversationId, messages);
159
+ return;
160
+ }
161
+ }
162
+ }
163
+ async deleteMessage(id) {
164
+ for (const [conversationId, messages] of this.messages) {
165
+ const filtered = messages.filter((m) => m.id !== id);
166
+ if (filtered.length !== messages.length) {
167
+ this.messages.set(conversationId, filtered);
168
+ return;
169
+ }
170
+ }
171
+ }
172
+ async clear(conversationId) {
173
+ this.messages.delete(conversationId);
174
+ this.metadata.delete(conversationId);
175
+ }
176
+ async getMetadata(conversationId) {
177
+ return this.metadata.get(conversationId) ?? null;
178
+ }
179
+ async setMetadata(conversationId, metadata) {
180
+ this.metadata.set(conversationId, metadata);
181
+ }
182
+ /** Clear all data (useful for testing) */
183
+ clearAll() {
184
+ this.messages.clear();
185
+ this.metadata.clear();
186
+ }
187
+ /** Get all conversation IDs */
188
+ getConversationIds() {
189
+ return Array.from(this.messages.keys());
190
+ }
191
+ };
192
+ function createInMemoryStore() {
193
+ return new InMemoryStore();
194
+ }
195
+
196
+ // src/memory/buffer.ts
197
+ var DEFAULT_MAX_MESSAGES = 50;
198
+ var DEFAULT_MAX_TOKENS = 4e3;
199
+ var BufferMemory = class {
200
+ type = "buffer";
201
+ store;
202
+ conversationId;
203
+ maxMessages;
204
+ maxTokens;
205
+ includeSystemMessages;
206
+ strategy;
207
+ constructor(options = {}) {
208
+ this.store = options.store ?? new InMemoryStore();
209
+ this.conversationId = options.conversationId ?? generateId();
210
+ this.maxMessages = options.maxMessages ?? DEFAULT_MAX_MESSAGES;
211
+ this.maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
212
+ this.includeSystemMessages = options.includeSystemMessages ?? true;
213
+ this.strategy = options.strategy ?? "sliding";
214
+ }
215
+ async add(message) {
216
+ const stored = createStoredMessage(message, this.conversationId);
217
+ await this.store.addMessage(stored);
218
+ await this.trim();
219
+ }
220
+ async addMany(messages) {
221
+ for (const message of messages) {
222
+ const stored = createStoredMessage(message, this.conversationId);
223
+ await this.store.addMessage(stored);
224
+ }
225
+ await this.trim();
226
+ }
227
+ async getMessages() {
228
+ const stored = await this.store.getMessages(this.conversationId);
229
+ return this.extractMessages(stored);
230
+ }
231
+ async getContext() {
232
+ const messages = await this.getMessages();
233
+ return createContext(messages);
234
+ }
235
+ async getContextWindow(maxTokens) {
236
+ const messages = await this.getMessages();
237
+ const truncated = truncateMessages(messages, maxTokens, {
238
+ keepSystemMessages: this.includeSystemMessages
239
+ });
240
+ return createContext(truncated);
241
+ }
242
+ async clear() {
243
+ await this.store.clear(this.conversationId);
244
+ }
245
+ async getMessageCount() {
246
+ const stored = await this.store.getMessages(this.conversationId);
247
+ return stored.length;
248
+ }
249
+ async getTokenCount() {
250
+ const messages = await this.getMessages();
251
+ return countMessagesTokens(messages);
252
+ }
253
+ /** Get the conversation ID */
254
+ getConversationId() {
255
+ return this.conversationId;
256
+ }
257
+ /** Switch to a different conversation */
258
+ setConversationId(id) {
259
+ this.conversationId = id;
260
+ }
261
+ extractMessages(stored) {
262
+ return stored.sort((a, b) => a.timestamp - b.timestamp).map((s) => s.message);
263
+ }
264
+ async trim() {
265
+ const stored = await this.store.getMessages(this.conversationId);
266
+ const messages = this.extractMessages(stored);
267
+ let trimmed = truncateMessagesByCount(messages, this.maxMessages, {
268
+ keepSystemMessages: this.includeSystemMessages
269
+ });
270
+ trimmed = truncateMessages(trimmed, this.maxTokens, {
271
+ keepSystemMessages: this.includeSystemMessages
272
+ });
273
+ if (trimmed.length < messages.length) {
274
+ await this.store.clear(this.conversationId);
275
+ for (const message of trimmed) {
276
+ const storedMsg = createStoredMessage(message, this.conversationId);
277
+ await this.store.addMessage(storedMsg);
278
+ }
279
+ }
280
+ }
281
+ };
282
+ function createBufferMemory(options = {}) {
283
+ return new BufferMemory(options);
284
+ }
285
+
286
+ // src/memory/summary.ts
287
+ var DEFAULT_SUMMARIZE_THRESHOLD = 2e3;
288
+ var DEFAULT_SUMMARY_MODEL = "google/gemini-2.5-flash";
289
+ var SummaryMemory = class {
290
+ type = "summary";
291
+ store;
292
+ conversationId;
293
+ summarizeThreshold;
294
+ summaryModel;
295
+ summaryPrompt;
296
+ summarizer;
297
+ includeSystemMessages;
298
+ constructor(options = {}) {
299
+ this.store = options.store ?? new InMemoryStore();
300
+ this.conversationId = options.conversationId ?? generateId();
301
+ this.summarizeThreshold = options.summarizeThreshold ?? DEFAULT_SUMMARIZE_THRESHOLD;
302
+ this.summaryModel = options.summaryModel ?? DEFAULT_SUMMARY_MODEL;
303
+ this.summaryPrompt = options.summaryPrompt ?? DEFAULT_SUMMARY_PROMPT;
304
+ this.summarizer = options.summarizer;
305
+ this.includeSystemMessages = options.includeSystemMessages ?? true;
306
+ }
307
+ /** Set the summarizer function */
308
+ setSummarizer(fn) {
309
+ this.summarizer = fn;
310
+ }
311
+ async add(message) {
312
+ const stored = createStoredMessage(message, this.conversationId);
313
+ await this.store.addMessage(stored);
314
+ await this.checkAndSummarize();
315
+ }
316
+ async addMany(messages) {
317
+ for (const message of messages) {
318
+ const stored = createStoredMessage(message, this.conversationId);
319
+ await this.store.addMessage(stored);
320
+ }
321
+ await this.checkAndSummarize();
322
+ }
323
+ async getMessages() {
324
+ const stored = await this.store.getMessages(this.conversationId);
325
+ const messages = this.extractMessages(stored);
326
+ const summary = await this.getSummary();
327
+ if (summary) {
328
+ const summaryMsg = {
329
+ role: "system",
330
+ content: `Previous conversation summary:
331
+ ${summary}`
332
+ };
333
+ const filtered = messages.filter(
334
+ (m) => !(m.role === "system" && m.content.startsWith("Previous conversation summary:"))
335
+ );
336
+ return [summaryMsg, ...filtered];
337
+ }
338
+ return messages;
339
+ }
340
+ async getContext() {
341
+ const messages = await this.getMessages();
342
+ const summary = await this.getSummary();
343
+ return createContext(messages, summary);
344
+ }
345
+ async getContextWindow(maxTokens) {
346
+ const messages = await this.getMessages();
347
+ const summary = await this.getSummary();
348
+ const truncated = truncateMessages(messages, maxTokens, {
349
+ keepSystemMessages: this.includeSystemMessages
350
+ });
351
+ return createContext(truncated, summary);
352
+ }
353
+ async clear() {
354
+ await this.store.clear(this.conversationId);
355
+ }
356
+ async getMessageCount() {
357
+ const stored = await this.store.getMessages(this.conversationId);
358
+ return stored.length;
359
+ }
360
+ async getTokenCount() {
361
+ const messages = await this.getMessages();
362
+ return countMessagesTokens(messages);
363
+ }
364
+ async getSummary() {
365
+ const metadata = await this.store.getMetadata(this.conversationId);
366
+ return metadata?.summary ?? null;
367
+ }
368
+ async summarize() {
369
+ if (!this.summarizer) {
370
+ throw new Error("No summarizer function provided. Set one with setSummarizer()");
371
+ }
372
+ const stored = await this.store.getMessages(this.conversationId);
373
+ const messages = this.extractMessages(stored);
374
+ if (messages.length === 0) {
375
+ return "";
376
+ }
377
+ const conversationText = formatConversationForSummary(messages);
378
+ const prompt = this.summaryPrompt.replace("{conversation}", conversationText);
379
+ const summary = await this.summarizer(messages, prompt);
380
+ await this.store.setMetadata(this.conversationId, { summary });
381
+ return summary;
382
+ }
383
+ /** Get the conversation ID */
384
+ getConversationId() {
385
+ return this.conversationId;
386
+ }
387
+ /** Switch to a different conversation */
388
+ setConversationId(id) {
389
+ this.conversationId = id;
390
+ }
391
+ extractMessages(stored) {
392
+ return stored.sort((a, b) => a.timestamp - b.timestamp).map((s) => s.message);
393
+ }
394
+ async checkAndSummarize() {
395
+ if (!this.summarizer) return;
396
+ const tokenCount = await this.getTokenCount();
397
+ if (tokenCount > this.summarizeThreshold) {
398
+ const stored = await this.store.getMessages(this.conversationId);
399
+ const messages = this.extractMessages(stored);
400
+ const keepCount = Math.max(4, Math.floor(messages.length / 4));
401
+ const toSummarize = messages.slice(0, -keepCount);
402
+ const toKeep = messages.slice(-keepCount);
403
+ if (toSummarize.length > 0) {
404
+ const existingSummary = await this.getSummary();
405
+ const messagesForSummary = existingSummary ? [{ role: "system", content: `Previous summary: ${existingSummary}` }, ...toSummarize] : toSummarize;
406
+ const conversationText = formatConversationForSummary(messagesForSummary);
407
+ const prompt = this.summaryPrompt.replace("{conversation}", conversationText);
408
+ const summary = await this.summarizer(messagesForSummary, prompt);
409
+ await this.store.clear(this.conversationId);
410
+ await this.store.setMetadata(this.conversationId, { summary });
411
+ for (const message of toKeep) {
412
+ const storedMsg = createStoredMessage(message, this.conversationId);
413
+ await this.store.addMessage(storedMsg);
414
+ }
415
+ }
416
+ }
417
+ }
418
+ };
419
+ function createSummaryMemory(options = {}) {
420
+ return new SummaryMemory(options);
421
+ }
422
+
423
+ // src/memory/summary-buffer.ts
424
+ var DEFAULT_BUFFER_SIZE = 10;
425
+ var DEFAULT_SUMMARIZE_THRESHOLD2 = 2e3;
426
+ var SummaryBufferMemory = class {
427
+ type = "summary-buffer";
428
+ store;
429
+ conversationId;
430
+ bufferSize;
431
+ summarizeThreshold;
432
+ summaryPrompt;
433
+ summarizer;
434
+ includeSystemMessages;
435
+ constructor(options = {}) {
436
+ this.store = options.store ?? new InMemoryStore();
437
+ this.conversationId = options.conversationId ?? generateId();
438
+ this.bufferSize = options.bufferSize ?? DEFAULT_BUFFER_SIZE;
439
+ this.summarizeThreshold = options.summarizeThreshold ?? DEFAULT_SUMMARIZE_THRESHOLD2;
440
+ this.summaryPrompt = options.summaryPrompt ?? DEFAULT_SUMMARY_PROMPT;
441
+ this.summarizer = options.summarizer;
442
+ this.includeSystemMessages = options.includeSystemMessages ?? true;
443
+ }
444
+ /** Set the summarizer function */
445
+ setSummarizer(fn) {
446
+ this.summarizer = fn;
447
+ }
448
+ async add(message) {
449
+ const stored = createStoredMessage(message, this.conversationId);
450
+ await this.store.addMessage(stored);
451
+ await this.checkAndSummarize();
452
+ }
453
+ async addMany(messages) {
454
+ for (const message of messages) {
455
+ const stored = createStoredMessage(message, this.conversationId);
456
+ await this.store.addMessage(stored);
457
+ }
458
+ await this.checkAndSummarize();
459
+ }
460
+ async getMessages() {
461
+ const stored = await this.store.getMessages(this.conversationId);
462
+ const messages = this.extractMessages(stored);
463
+ const summary = await this.getSummary();
464
+ const buffer = messages.slice(-this.bufferSize);
465
+ if (summary) {
466
+ const summaryMsg = {
467
+ role: "system",
468
+ content: `Previous conversation summary:
469
+ ${summary}`
470
+ };
471
+ const systemMessages = messages.slice(0, -this.bufferSize).filter((m) => m.role === "system" && !m.content.startsWith("Previous conversation summary:"));
472
+ return [...systemMessages, summaryMsg, ...buffer];
473
+ }
474
+ return messages;
475
+ }
476
+ async getContext() {
477
+ const messages = await this.getMessages();
478
+ const summary = await this.getSummary();
479
+ return createContext(messages, summary);
480
+ }
481
+ async getContextWindow(maxTokens) {
482
+ const messages = await this.getMessages();
483
+ const summary = await this.getSummary();
484
+ const truncated = truncateMessages(messages, maxTokens, {
485
+ keepSystemMessages: this.includeSystemMessages
486
+ });
487
+ return createContext(truncated, summary);
488
+ }
489
+ async clear() {
490
+ await this.store.clear(this.conversationId);
491
+ }
492
+ async getMessageCount() {
493
+ const stored = await this.store.getMessages(this.conversationId);
494
+ return stored.length;
495
+ }
496
+ async getTokenCount() {
497
+ const messages = await this.getMessages();
498
+ return countMessagesTokens(messages);
499
+ }
500
+ async getSummary() {
501
+ const metadata = await this.store.getMetadata(this.conversationId);
502
+ return metadata?.summary ?? null;
503
+ }
504
+ async summarize() {
505
+ if (!this.summarizer) {
506
+ throw new Error("No summarizer function provided. Set one with setSummarizer()");
507
+ }
508
+ const stored = await this.store.getMessages(this.conversationId);
509
+ const messages = this.extractMessages(stored);
510
+ const toSummarize = messages.slice(0, -this.bufferSize);
511
+ if (toSummarize.length === 0) {
512
+ return "";
513
+ }
514
+ const existingSummary = await this.getSummary();
515
+ const messagesForSummary = existingSummary ? [{ role: "system", content: `Previous summary: ${existingSummary}` }, ...toSummarize] : toSummarize;
516
+ const conversationText = formatConversationForSummary(messagesForSummary);
517
+ const prompt = this.summaryPrompt.replace("{conversation}", conversationText);
518
+ const summary = await this.summarizer(messagesForSummary, prompt);
519
+ await this.store.setMetadata(this.conversationId, { summary });
520
+ return summary;
521
+ }
522
+ /** Get buffer size */
523
+ getBufferSize() {
524
+ return this.bufferSize;
525
+ }
526
+ /** Get the conversation ID */
527
+ getConversationId() {
528
+ return this.conversationId;
529
+ }
530
+ /** Switch to a different conversation */
531
+ setConversationId(id) {
532
+ this.conversationId = id;
533
+ }
534
+ extractMessages(stored) {
535
+ return stored.sort((a, b) => a.timestamp - b.timestamp).map((s) => s.message);
536
+ }
537
+ async checkAndSummarize() {
538
+ if (!this.summarizer) return;
539
+ const stored = await this.store.getMessages(this.conversationId);
540
+ const messages = this.extractMessages(stored);
541
+ if (messages.length <= this.bufferSize) return;
542
+ const tokenCount = await this.getTokenCount();
543
+ if (tokenCount > this.summarizeThreshold) {
544
+ const buffer = messages.slice(-this.bufferSize);
545
+ const toSummarize = messages.slice(0, -this.bufferSize);
546
+ if (toSummarize.length > 0) {
547
+ const existingSummary = await this.getSummary();
548
+ const messagesForSummary = existingSummary ? [{ role: "system", content: `Previous summary: ${existingSummary}` }, ...toSummarize] : toSummarize;
549
+ const conversationText = formatConversationForSummary(messagesForSummary);
550
+ const prompt = this.summaryPrompt.replace("{conversation}", conversationText);
551
+ const summary = await this.summarizer(messagesForSummary, prompt);
552
+ await this.store.clear(this.conversationId);
553
+ await this.store.setMetadata(this.conversationId, { summary });
554
+ for (const message of buffer) {
555
+ const storedMsg = createStoredMessage(message, this.conversationId);
556
+ await this.store.addMessage(storedMsg);
557
+ }
558
+ }
559
+ }
560
+ }
561
+ };
562
+ function createSummaryBufferMemory(options = {}) {
563
+ return new SummaryBufferMemory(options);
564
+ }
565
+
566
+ // src/memory/vector.ts
567
+ var DEFAULT_TOP_K = 5;
568
+ var DEFAULT_MIN_SCORE = 0.5;
569
+ var VectorMemory = class {
570
+ type = "vector";
571
+ store;
572
+ embeddings;
573
+ conversationId;
574
+ topK;
575
+ minScore;
576
+ maxMessages;
577
+ maxTokens;
578
+ includeSystemMessages;
579
+ constructor(options) {
580
+ if (!options.embeddings) {
581
+ throw new Error("VectorMemory requires an embeddings provider");
582
+ }
583
+ this.store = options.store ?? new InMemoryStore();
584
+ this.embeddings = options.embeddings;
585
+ this.conversationId = options.conversationId ?? generateId();
586
+ this.topK = options.topK ?? DEFAULT_TOP_K;
587
+ this.minScore = options.minScore ?? DEFAULT_MIN_SCORE;
588
+ this.maxMessages = options.maxMessages;
589
+ this.maxTokens = options.maxTokens;
590
+ this.includeSystemMessages = options.includeSystemMessages ?? true;
591
+ }
592
+ async add(message) {
593
+ const { embedding } = await this.embeddings.embed(message.content);
594
+ const stored = createStoredMessage(message, this.conversationId, {
595
+ embedding
596
+ });
597
+ await this.store.addMessage(stored);
598
+ }
599
+ async addMany(messages) {
600
+ if (messages.length === 0) return;
601
+ const texts = messages.map((m) => m.content);
602
+ const { embeddings } = await this.embeddings.embedMany(texts);
603
+ for (let i = 0; i < messages.length; i++) {
604
+ const stored = createStoredMessage(messages[i], this.conversationId, {
605
+ embedding: embeddings[i].embedding
606
+ });
607
+ await this.store.addMessage(stored);
608
+ }
609
+ }
610
+ async getMessages() {
611
+ const stored = await this.store.getMessages(this.conversationId);
612
+ return this.extractMessages(stored);
613
+ }
614
+ async getContext() {
615
+ const messages = await this.getMessages();
616
+ return createContext(messages);
617
+ }
618
+ async getContextWindow(maxTokens) {
619
+ const messages = await this.getMessages();
620
+ const truncated = truncateMessages(messages, maxTokens, {
621
+ keepSystemMessages: this.includeSystemMessages
622
+ });
623
+ return createContext(truncated);
624
+ }
625
+ async clear() {
626
+ await this.store.clear(this.conversationId);
627
+ }
628
+ async getMessageCount() {
629
+ const stored = await this.store.getMessages(this.conversationId);
630
+ return stored.length;
631
+ }
632
+ async getTokenCount() {
633
+ const messages = await this.getMessages();
634
+ return countMessagesTokens(messages);
635
+ }
636
+ async search(query, topK) {
637
+ const k = topK ?? this.topK;
638
+ const { embedding: queryEmbedding } = await this.embeddings.embed(query);
639
+ const stored = await this.store.getMessages(this.conversationId);
640
+ const results = [];
641
+ for (const msg of stored) {
642
+ if (!msg.embedding) continue;
643
+ const score = cosineSimilarity(queryEmbedding, msg.embedding);
644
+ if (score >= this.minScore) {
645
+ results.push({ message: msg, score });
646
+ }
647
+ }
648
+ return results.sort((a, b) => b.score - a.score).slice(0, k);
649
+ }
650
+ /**
651
+ * Get relevant context for a query.
652
+ * Returns messages most semantically similar to the query.
653
+ */
654
+ async getRelevantContext(query, topK) {
655
+ const results = await this.search(query, topK);
656
+ const sorted = results.sort((a, b) => a.message.timestamp - b.message.timestamp);
657
+ return sorted.map((r) => r.message.message);
658
+ }
659
+ /**
660
+ * Build context with recent messages + relevant history.
661
+ * Useful for including both recency and relevance.
662
+ */
663
+ async buildContext(query, options) {
664
+ const { recentCount = 5, relevantCount = 3 } = options ?? {};
665
+ const stored = await this.store.getMessages(this.conversationId);
666
+ const messages = this.extractMessages(stored);
667
+ const recent = messages.slice(-recentCount);
668
+ const recentIds = new Set(stored.slice(-recentCount).map((s) => s.id));
669
+ const relevantResults = await this.search(query, relevantCount + recentCount);
670
+ const relevant = relevantResults.filter((r) => !recentIds.has(r.message.id)).slice(0, relevantCount).sort((a, b) => a.message.timestamp - b.message.timestamp).map((r) => r.message.message);
671
+ return [...relevant, ...recent];
672
+ }
673
+ /** Get the conversation ID */
674
+ getConversationId() {
675
+ return this.conversationId;
676
+ }
677
+ /** Switch to a different conversation */
678
+ setConversationId(id) {
679
+ this.conversationId = id;
680
+ }
681
+ extractMessages(stored) {
682
+ return stored.sort((a, b) => a.timestamp - b.timestamp).map((s) => s.message);
683
+ }
684
+ };
685
+ function createVectorMemory(options) {
686
+ return new VectorMemory(options);
687
+ }
688
+
689
+ // src/memory/stores/local-storage.ts
690
+ var MESSAGES_PREFIX = "binario:memory:messages:";
691
+ var METADATA_PREFIX = "binario:memory:metadata:";
692
+ var LocalStorageStore = class {
693
+ prefix;
694
+ constructor(prefix = "") {
695
+ this.prefix = prefix;
696
+ }
697
+ getMessagesKey(conversationId) {
698
+ return `${MESSAGES_PREFIX}${this.prefix}${conversationId}`;
699
+ }
700
+ getMetadataKey(conversationId) {
701
+ return `${METADATA_PREFIX}${this.prefix}${conversationId}`;
702
+ }
703
+ getStorage() {
704
+ if (typeof window !== "undefined" && window.localStorage) {
705
+ return window.localStorage;
706
+ }
707
+ return null;
708
+ }
709
+ async getMessages(conversationId) {
710
+ const storage = this.getStorage();
711
+ if (!storage) return [];
712
+ try {
713
+ const data = storage.getItem(this.getMessagesKey(conversationId));
714
+ return data ? JSON.parse(data) : [];
715
+ } catch {
716
+ return [];
717
+ }
718
+ }
719
+ async addMessage(message) {
720
+ const storage = this.getStorage();
721
+ if (!storage) return;
722
+ const messages = await this.getMessages(message.conversationId);
723
+ messages.push(message);
724
+ try {
725
+ storage.setItem(
726
+ this.getMessagesKey(message.conversationId),
727
+ JSON.stringify(messages)
728
+ );
729
+ } catch (e) {
730
+ if (e instanceof Error && e.name === "QuotaExceededError") {
731
+ try {
732
+ const trimmed = messages.slice(-Math.floor(messages.length / 2));
733
+ storage.setItem(
734
+ this.getMessagesKey(message.conversationId),
735
+ JSON.stringify(trimmed)
736
+ );
737
+ } catch {
738
+ }
739
+ }
740
+ }
741
+ }
742
+ async updateMessage(id, updates) {
743
+ const storage = this.getStorage();
744
+ if (!storage) return;
745
+ const keys = Object.keys(storage).filter((k) => k.startsWith(MESSAGES_PREFIX + this.prefix));
746
+ for (const key of keys) {
747
+ try {
748
+ const data = storage.getItem(key);
749
+ if (!data) continue;
750
+ const messages = JSON.parse(data);
751
+ const index = messages.findIndex((m) => m.id === id);
752
+ if (index !== -1) {
753
+ messages[index] = { ...messages[index], ...updates };
754
+ storage.setItem(key, JSON.stringify(messages));
755
+ return;
756
+ }
757
+ } catch {
758
+ continue;
759
+ }
760
+ }
761
+ }
762
+ async deleteMessage(id) {
763
+ const storage = this.getStorage();
764
+ if (!storage) return;
765
+ const keys = Object.keys(storage).filter((k) => k.startsWith(MESSAGES_PREFIX + this.prefix));
766
+ for (const key of keys) {
767
+ try {
768
+ const data = storage.getItem(key);
769
+ if (!data) continue;
770
+ const messages = JSON.parse(data);
771
+ const filtered = messages.filter((m) => m.id !== id);
772
+ if (filtered.length !== messages.length) {
773
+ storage.setItem(key, JSON.stringify(filtered));
774
+ return;
775
+ }
776
+ } catch {
777
+ continue;
778
+ }
779
+ }
780
+ }
781
+ async clear(conversationId) {
782
+ const storage = this.getStorage();
783
+ if (!storage) return;
784
+ storage.removeItem(this.getMessagesKey(conversationId));
785
+ storage.removeItem(this.getMetadataKey(conversationId));
786
+ }
787
+ async getMetadata(conversationId) {
788
+ const storage = this.getStorage();
789
+ if (!storage) return null;
790
+ try {
791
+ const data = storage.getItem(this.getMetadataKey(conversationId));
792
+ return data ? JSON.parse(data) : null;
793
+ } catch {
794
+ return null;
795
+ }
796
+ }
797
+ async setMetadata(conversationId, metadata) {
798
+ const storage = this.getStorage();
799
+ if (!storage) return;
800
+ storage.setItem(this.getMetadataKey(conversationId), JSON.stringify(metadata));
801
+ }
802
+ /** Clear all binario memory data */
803
+ clearAll() {
804
+ const storage = this.getStorage();
805
+ if (!storage) return;
806
+ const keysToRemove = Object.keys(storage).filter(
807
+ (k) => k.startsWith(MESSAGES_PREFIX + this.prefix) || k.startsWith(METADATA_PREFIX + this.prefix)
808
+ );
809
+ keysToRemove.forEach((key) => storage.removeItem(key));
810
+ }
811
+ /** Get all conversation IDs */
812
+ getConversationIds() {
813
+ const storage = this.getStorage();
814
+ if (!storage) return [];
815
+ const prefix = MESSAGES_PREFIX + this.prefix;
816
+ return Object.keys(storage).filter((k) => k.startsWith(prefix)).map((k) => k.slice(prefix.length));
817
+ }
818
+ };
819
+ function createLocalStorageStore(prefix = "") {
820
+ return new LocalStorageStore(prefix);
821
+ }
822
+
823
+ // src/memory/stores/cloudflare-kv.ts
824
+ var MESSAGES_PREFIX2 = "messages:";
825
+ var METADATA_PREFIX2 = "metadata:";
826
+ var CloudflareKVStore = class {
827
+ kv;
828
+ ttl;
829
+ /**
830
+ * @param kv - Cloudflare KV namespace binding
831
+ * @param ttl - Optional TTL in seconds for automatic expiration
832
+ */
833
+ constructor(kv, ttl) {
834
+ this.kv = kv;
835
+ this.ttl = ttl;
836
+ }
837
+ getMessagesKey(conversationId) {
838
+ return `${MESSAGES_PREFIX2}${conversationId}`;
839
+ }
840
+ getMetadataKey(conversationId) {
841
+ return `${METADATA_PREFIX2}${conversationId}`;
842
+ }
843
+ async getMessages(conversationId) {
844
+ try {
845
+ const data = await this.kv.get(this.getMessagesKey(conversationId), { type: "json" });
846
+ return data ?? [];
847
+ } catch {
848
+ return [];
849
+ }
850
+ }
851
+ async addMessage(message) {
852
+ const messages = await this.getMessages(message.conversationId);
853
+ messages.push(message);
854
+ await this.kv.put(
855
+ this.getMessagesKey(message.conversationId),
856
+ JSON.stringify(messages),
857
+ this.ttl ? { expirationTtl: this.ttl } : void 0
858
+ );
859
+ }
860
+ async updateMessage(id, updates) {
861
+ const { keys } = await this.kv.list({ prefix: MESSAGES_PREFIX2 });
862
+ for (const { name: key } of keys) {
863
+ const conversationId = key.slice(MESSAGES_PREFIX2.length);
864
+ const messages = await this.getMessages(conversationId);
865
+ const index = messages.findIndex((m) => m.id === id);
866
+ if (index !== -1) {
867
+ messages[index] = { ...messages[index], ...updates };
868
+ await this.kv.put(
869
+ key,
870
+ JSON.stringify(messages),
871
+ this.ttl ? { expirationTtl: this.ttl } : void 0
872
+ );
873
+ return;
874
+ }
875
+ }
876
+ }
877
+ async deleteMessage(id) {
878
+ const { keys } = await this.kv.list({ prefix: MESSAGES_PREFIX2 });
879
+ for (const { name: key } of keys) {
880
+ const conversationId = key.slice(MESSAGES_PREFIX2.length);
881
+ const messages = await this.getMessages(conversationId);
882
+ const filtered = messages.filter((m) => m.id !== id);
883
+ if (filtered.length !== messages.length) {
884
+ await this.kv.put(
885
+ key,
886
+ JSON.stringify(filtered),
887
+ this.ttl ? { expirationTtl: this.ttl } : void 0
888
+ );
889
+ return;
890
+ }
891
+ }
892
+ }
893
+ async clear(conversationId) {
894
+ await Promise.all([
895
+ this.kv.delete(this.getMessagesKey(conversationId)),
896
+ this.kv.delete(this.getMetadataKey(conversationId))
897
+ ]);
898
+ }
899
+ async getMetadata(conversationId) {
900
+ try {
901
+ const data = await this.kv.get(this.getMetadataKey(conversationId), { type: "json" });
902
+ return data ?? null;
903
+ } catch {
904
+ return null;
905
+ }
906
+ }
907
+ async setMetadata(conversationId, metadata) {
908
+ await this.kv.put(
909
+ this.getMetadataKey(conversationId),
910
+ JSON.stringify(metadata),
911
+ this.ttl ? { expirationTtl: this.ttl } : void 0
912
+ );
913
+ }
914
+ /** Clear all binario memory data in this KV namespace */
915
+ async clearAll() {
916
+ const { keys } = await this.kv.list();
917
+ await Promise.all(keys.map(({ name }) => this.kv.delete(name)));
918
+ }
919
+ /** Get all conversation IDs */
920
+ async getConversationIds() {
921
+ const { keys } = await this.kv.list({ prefix: MESSAGES_PREFIX2 });
922
+ return keys.map(({ name }) => name.slice(MESSAGES_PREFIX2.length));
923
+ }
924
+ };
925
+ function createCloudflareKVStore(kv, ttl) {
926
+ return new CloudflareKVStore(kv, ttl);
927
+ }
928
+
929
+ export { BufferMemory, CloudflareKVStore, DEFAULT_SUMMARY_PROMPT, InMemoryStore, LocalStorageStore, SummaryBufferMemory, SummaryMemory, VectorMemory, cosineSimilarity, countMessageTokens, countMessagesTokens, countTokens, createBufferMemory, createCloudflareKVStore, createInMemoryStore, createLocalStorageStore, createStoredMessage, createSummaryBufferMemory, createSummaryMemory, createVectorMemory, formatContextWithSummary, formatConversationForSummary, generateId, truncateMessages, truncateMessagesByCount };
930
+ //# sourceMappingURL=memory.js.map
931
+ //# sourceMappingURL=memory.js.map