|
|
|
|
@@ -355,35 +355,32 @@ ${conversation}`,
|
|
|
|
|
return ordered.map(n => mem.find(m => m.name === n)!).filter(Boolean);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async memorize(history: LLMMessage[], memories: Memory[] | MemoryCache, options: LLMRequest): Promise<void> {
|
|
|
|
|
async memorize(history: LLMMessage[], memories: Memory[] | MemoryCache, options: LLMRequest): Promise<Memory[]> {
|
|
|
|
|
const conversation = history
|
|
|
|
|
.filter(h => h.role === 'user' || h.role === 'assistant')
|
|
|
|
|
.map(h => `[${h.role}]: ${h.content}`).join('\n\n').trim();
|
|
|
|
|
|
|
|
|
|
if (!conversation) return;
|
|
|
|
|
|
|
|
|
|
const trackingId = `${Date.now()}_${Math.random()}`;
|
|
|
|
|
if(!conversation) return [];
|
|
|
|
|
|
|
|
|
|
// Create and insert temp memory immediately
|
|
|
|
|
const trackingId = `${Date.now()}_${Math.random()}`;
|
|
|
|
|
const tempMemory = await this.createTempMemory(conversation);
|
|
|
|
|
const mem = memories instanceof MemoryCache ? memories.memories : memories;
|
|
|
|
|
mem.push(tempMemory);
|
|
|
|
|
|
|
|
|
|
if (memories instanceof MemoryCache) {
|
|
|
|
|
memories.rebuild();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (memories instanceof MemoryCache) memories.rebuild();
|
|
|
|
|
this.pendingMemorizations.set(trackingId, {
|
|
|
|
|
memories,
|
|
|
|
|
tempMemoryName: tempMemory.name,
|
|
|
|
|
timestamp: Date.now(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this._memorizeBackground(conversation, memories, options, trackingId)
|
|
|
|
|
.catch(err => {
|
|
|
|
|
console.error('[memorize] Background memorization failed:', err);
|
|
|
|
|
})
|
|
|
|
|
.finally(() => {
|
|
|
|
|
try {
|
|
|
|
|
await this._memorizeBackground(conversation, memories, options);
|
|
|
|
|
// Return the final memories (excluding temp ones)
|
|
|
|
|
const finalMem = memories instanceof MemoryCache ? memories.memories : memories;
|
|
|
|
|
return finalMem.filter(m => !m.name.startsWith('_temp_'));
|
|
|
|
|
} catch (err) {
|
|
|
|
|
throw err;
|
|
|
|
|
} finally {
|
|
|
|
|
// Remove temp memory from the exact same memory array/cache
|
|
|
|
|
const pending = this.pendingMemorizations.get(trackingId);
|
|
|
|
|
if (pending) {
|
|
|
|
|
@@ -393,30 +390,21 @@ ${conversation}`,
|
|
|
|
|
const idx = cleanMem.findIndex(m => m.name === pending.tempMemoryName);
|
|
|
|
|
if (idx !== -1) {
|
|
|
|
|
cleanMem.splice(idx, 1);
|
|
|
|
|
console.log(`[memorize] Removed temp memory: ${pending.tempMemoryName}`);
|
|
|
|
|
}
|
|
|
|
|
if (pending.memories instanceof MemoryCache) {
|
|
|
|
|
pending.memories.rebuild();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
this.pendingMemorizations.delete(trackingId);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async _memorizeBackground(conversation: string, memories: Memory[] | MemoryCache, options: LLMRequest, trackingId: string): Promise<void> {
|
|
|
|
|
private async _memorizeBackground(conversation: string, memories: Memory[] | MemoryCache, options: LLMRequest): Promise<void> {
|
|
|
|
|
const mem = memories instanceof MemoryCache ? memories.memories : memories;
|
|
|
|
|
const monday = getWeekMonday();
|
|
|
|
|
const sunday = getWeekSunday(monday);
|
|
|
|
|
const weekKey = monday;
|
|
|
|
|
|
|
|
|
|
console.log('[memorize] Starting fact extraction...');
|
|
|
|
|
const buckets = await this.factAgent(conversation, mem, options, weekKey);
|
|
|
|
|
console.log(`[memorize] Extracted ${buckets.length} buckets:`, buckets);
|
|
|
|
|
|
|
|
|
|
if (!buckets.length) {
|
|
|
|
|
console.log('[memorize] No facts extracted, exiting');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const buckets = await this.factAgent(conversation, mem, options, monday);
|
|
|
|
|
if(!buckets.length) return;
|
|
|
|
|
|
|
|
|
|
const runDocAgent = (node: Memory, bucket: FactBucket, embedding?: number[], week?: {monday: string, sunday: string}) => {
|
|
|
|
|
if (memories instanceof MemoryCache) {
|
|
|
|
|
@@ -443,13 +431,10 @@ ${conversation}`,
|
|
|
|
|
await runDocAgent(node, bucket, embedding, week);
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
if (memories instanceof MemoryCache) {
|
|
|
|
|
if(memories instanceof MemoryCache)
|
|
|
|
|
memories.rebuild();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('[memorize] Completed successfully');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private buildHeader(node: Memory, week?: {monday: string, sunday: string}, links: string[] = [], backlinks: string[] = []): string {
|
|
|
|
|
const tags = node.name.split('/')[0]?.toLowerCase();
|
|
|
|
|
const lines = [
|
|
|
|
|
@@ -587,9 +572,6 @@ ${node.content || '(empty — this is a new document)'}
|
|
|
|
|
|
|
|
|
|
private async factAgent(conversation: string, memories: Memory[], options: LLMRequest, weekKey: string): Promise<FactBucket[]> {
|
|
|
|
|
const buckets: FactBucket[] = [];
|
|
|
|
|
|
|
|
|
|
console.log('[factAgent] Starting extraction...');
|
|
|
|
|
|
|
|
|
|
await this.llm.ask(conversation, {
|
|
|
|
|
model: options.model,
|
|
|
|
|
temperature: 0.2,
|
|
|
|
|
@@ -604,7 +586,8 @@ Rules:
|
|
|
|
|
|
|
|
|
|
When extracting facts, you MUST also decide the exact destination path:
|
|
|
|
|
- Use an existing node name if the facts clearly belong there
|
|
|
|
|
- Create a new path following collection/subject format if needed (e.g., People/Sarah, Projects/Oxide)
|
|
|
|
|
- All information primary about the user should go under "Personal/Subject" (e.g., Personal/Info, Personal/Todos)
|
|
|
|
|
- When required, create a new path following collection/subject format (e.g., People/Sarah, Projects/Oxide)
|
|
|
|
|
- For journal entries, use "journal" (will auto-route to Journal/${weekKey})
|
|
|
|
|
|
|
|
|
|
Available nodes:
|
|
|
|
|
@@ -618,7 +601,6 @@ ${this.listNodes(memories).map(n => `- ${n.name}: ${n.description}`).join('\n')
|
|
|
|
|
create_new: {type: 'boolean', description: 'True if this is a new node that doesn\'t exist yet', required: true},
|
|
|
|
|
},
|
|
|
|
|
fn: (args: any) => {
|
|
|
|
|
console.log('[factAgent] Tool called with:', args);
|
|
|
|
|
const subject = args.destination.trim().toLowerCase() === 'journal'
|
|
|
|
|
? `Journal/${weekKey}`
|
|
|
|
|
: args.destination;
|
|
|
|
|
@@ -631,8 +613,6 @@ ${this.listNodes(memories).map(n => `- ${n.name}: ${n.description}`).join('\n')
|
|
|
|
|
},
|
|
|
|
|
}],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
console.log(`[factAgent] Extracted ${buckets.length} buckets:`, buckets);
|
|
|
|
|
return buckets;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|