This commit is contained in:
@@ -10,11 +10,11 @@ import {
|
||||
startOfUtcDay,
|
||||
toIso,
|
||||
} from "./schedule";
|
||||
import { extractFact } from "identitydb";
|
||||
import {
|
||||
buildMandatoryConversationContext,
|
||||
conversationInstruction,
|
||||
formatMessageHistory,
|
||||
memoryExtractionInstruction,
|
||||
} from "./conversation";
|
||||
import type {
|
||||
BoxBrainMemoryStore,
|
||||
@@ -334,35 +334,50 @@ export class Persona {
|
||||
messageHistory: PersonaMessage[];
|
||||
}): Promise<FactDraft[]> {
|
||||
const persona = await this.ready();
|
||||
if (!this.options.models?.memoryExtraction) {
|
||||
throw new Error("sleepMemory requires options.models.memoryExtraction.");
|
||||
if (!this.options.models?.factExtractor) {
|
||||
throw new Error("sleepMemory requires options.models.factExtractor.");
|
||||
}
|
||||
const contextFacts = await this.memory.findFacts(persona.id, [
|
||||
"persona",
|
||||
persona.displayName,
|
||||
"user",
|
||||
]);
|
||||
const drafts = await this.options.models.memoryExtraction.extract({
|
||||
persona,
|
||||
now: toIso(input.datetime),
|
||||
formattedMessageHistory: formatMessageHistory({
|
||||
const statement = [
|
||||
`Current objective time: ${toIso(input.datetime)}.`,
|
||||
"Read the message history and extract durable facts worth remembering.",
|
||||
"Objectivize subjective statements before storage.",
|
||||
'Example: "I started TypeScript in 2025" becomes "The user started TypeScript in 2025."',
|
||||
"Prefer facts about the persona, the user, their relationship, preferences, history, schedule-relevant events, and stable traits.",
|
||||
"",
|
||||
"Context facts:",
|
||||
...contextFacts.map((f) => `- ${f.statement}`),
|
||||
"",
|
||||
"Message history:",
|
||||
formatMessageHistory({
|
||||
personaName: persona.displayName,
|
||||
messages: input.messageHistory,
|
||||
}),
|
||||
contextFacts,
|
||||
instruction: memoryExtractionInstruction(toIso(input.datetime)),
|
||||
});
|
||||
for (const draft of drafts) {
|
||||
await this.memory.addFact(persona.id, {
|
||||
...draft,
|
||||
topics: [...draft.topics, "sleepMemory"],
|
||||
source: draft.source ?? "boxbrain.sleepMemory",
|
||||
});
|
||||
}
|
||||
].join("\n");
|
||||
const extracted = await extractFact(
|
||||
statement,
|
||||
this.options.models.factExtractor,
|
||||
);
|
||||
const draft: FactDraft = {
|
||||
statement: extracted.statement ?? statement,
|
||||
topics: [...extracted.topics.map((t) => t.name), "sleepMemory"],
|
||||
source: extracted.source ?? "boxbrain.sleepMemory",
|
||||
...(typeof extracted.confidence === 'number'
|
||||
? { confidence: extracted.confidence }
|
||||
: {}),
|
||||
...(extracted.metadata !== undefined && extracted.metadata !== null
|
||||
? { metadata: extracted.metadata as Record<string, unknown> }
|
||||
: {}),
|
||||
};
|
||||
await this.memory.addFact(persona.id, draft);
|
||||
await this.emit("persona.memory.sleep.persisted", {
|
||||
factCount: drafts.length,
|
||||
factCount: 1,
|
||||
});
|
||||
return drafts;
|
||||
return [draft];
|
||||
}
|
||||
|
||||
private async initialize(): Promise<MemorySpace> {
|
||||
@@ -381,24 +396,38 @@ export class Persona {
|
||||
seedMessage: this.mode.seedMessage,
|
||||
now,
|
||||
});
|
||||
const modelFacts = this.options.models?.initialization
|
||||
? await this.options.models.initialization.extractInitialFacts({
|
||||
displayName: this.mode.displayName,
|
||||
seedMessage: this.mode.seedMessage,
|
||||
now,
|
||||
})
|
||||
: undefined;
|
||||
const facts = modelFacts ?? [
|
||||
defaultInitialFact(this.mode.displayName, this.mode.seedMessage),
|
||||
];
|
||||
for (const fact of facts) {
|
||||
if (this.options.models?.factExtractor) {
|
||||
const statement = `Persona: ${this.mode.displayName}\nSeed: ${this.mode.seedMessage}`;
|
||||
const extracted = await extractFact(
|
||||
statement,
|
||||
this.options.models.factExtractor,
|
||||
);
|
||||
const draft: FactDraft = {
|
||||
statement: extracted.statement ?? statement,
|
||||
topics: extracted.topics.map((t) => t.name),
|
||||
source: extracted.source ?? "boxbrain.persona.initialization",
|
||||
...(typeof extracted.confidence === 'number'
|
||||
? { confidence: extracted.confidence }
|
||||
: {}),
|
||||
...(extracted.metadata !== undefined && extracted.metadata !== null
|
||||
? { metadata: extracted.metadata as Record<string, unknown> }
|
||||
: {}),
|
||||
};
|
||||
await this.memory.addFact(space.id, draft);
|
||||
await this.emit(
|
||||
"persona.initialized",
|
||||
{ displayName: space.displayName, factCount: 1 },
|
||||
space.id,
|
||||
);
|
||||
} else {
|
||||
const fact = defaultInitialFact(this.mode.displayName, this.mode.seedMessage);
|
||||
await this.memory.addFact(space.id, fact);
|
||||
await this.emit(
|
||||
"persona.initialized",
|
||||
{ displayName: space.displayName, factCount: 1 },
|
||||
space.id,
|
||||
);
|
||||
}
|
||||
await this.emit(
|
||||
"persona.initialized",
|
||||
{ displayName: space.displayName, factCount: facts.length },
|
||||
space.id,
|
||||
);
|
||||
await this.refreshAvailability(now, space);
|
||||
return space;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user