This commit is contained in:
@@ -47,6 +47,10 @@ export class InMemoryMemoryStore implements BoxBrainMemoryStore {
|
|||||||
return stored;
|
return stored;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async listFacts(spaceId: string): Promise<StoredFact[]> {
|
||||||
|
return [...(this.facts.get(spaceId) ?? [])].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
||||||
|
}
|
||||||
|
|
||||||
async findFacts(spaceId: string, topics: string[]): Promise<StoredFact[]> {
|
async findFacts(spaceId: string, topics: string[]): Promise<StoredFact[]> {
|
||||||
const facts = this.facts.get(spaceId) ?? [];
|
const facts = this.facts.get(spaceId) ?? [];
|
||||||
if (topics.length === 0) return [...facts];
|
if (topics.length === 0) return [...facts];
|
||||||
@@ -120,6 +124,17 @@ export class IdentityDbMemoryStore implements BoxBrainMemoryStore {
|
|||||||
return this.fromIdentityFact(stored);
|
return this.fromIdentityFact(stored);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async listFacts(spaceId: string): Promise<StoredFact[]> {
|
||||||
|
const topics = await this.options.db.listTopics({ spaceName: spaceId, includeFacts: true });
|
||||||
|
const collected = new Map<string, StoredFact>();
|
||||||
|
for (const topic of topics) {
|
||||||
|
for (const fact of topic.facts) {
|
||||||
|
collected.set(fact.id, this.fromIdentityFact(fact));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [...collected.values()].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
||||||
|
}
|
||||||
|
|
||||||
async findFacts(spaceId: string, topics: string[]): Promise<StoredFact[]> {
|
async findFacts(spaceId: string, topics: string[]): Promise<StoredFact[]> {
|
||||||
const uniqueTopics = normalizeTopics(topics);
|
const uniqueTopics = normalizeTopics(topics);
|
||||||
const collected = new Map<string, StoredFact>();
|
const collected = new Map<string, StoredFact>();
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ export interface BoxBrainMemoryStore {
|
|||||||
createSpace(input: { displayName: string; seedMessage: string; now: string }): Promise<MemorySpace>;
|
createSpace(input: { displayName: string; seedMessage: string; now: string }): Promise<MemorySpace>;
|
||||||
getSpace(spaceId: string): Promise<MemorySpace | null>;
|
getSpace(spaceId: string): Promise<MemorySpace | null>;
|
||||||
addFact(spaceId: string, fact: FactDraft): Promise<StoredFact>;
|
addFact(spaceId: string, fact: FactDraft): Promise<StoredFact>;
|
||||||
|
listFacts(spaceId: string): Promise<StoredFact[]>;
|
||||||
findFacts(spaceId: string, topics: string[]): Promise<StoredFact[]>;
|
findFacts(spaceId: string, topics: string[]): Promise<StoredFact[]>;
|
||||||
saveScheduleEntries(spaceId: string, entries: ScheduleEntry[]): Promise<void>;
|
saveScheduleEntries(spaceId: string, entries: ScheduleEntry[]): Promise<void>;
|
||||||
listScheduleEntries(spaceId: string, fromInclusive: string, toExclusive: string): Promise<ScheduleEntry[]>;
|
listScheduleEntries(spaceId: string, fromInclusive: string, toExclusive: string): Promise<ScheduleEntry[]>;
|
||||||
|
|||||||
60
tests/memory.test.ts
Normal file
60
tests/memory.test.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { mkdtemp, rm } from 'node:fs/promises';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
import { tmpdir } from 'node:os';
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { createSqliteIdentityMemoryStore, InMemoryMemoryStore } from '../src';
|
||||||
|
|
||||||
|
async function seedFacts(memory: InMemoryMemoryStore, spaceId: string) {
|
||||||
|
await memory.addFact(spaceId, {
|
||||||
|
statement: 'Mina likes quiet cafes.',
|
||||||
|
topics: ['persona', 'Mina'],
|
||||||
|
source: 'test',
|
||||||
|
});
|
||||||
|
await memory.addFact(spaceId, {
|
||||||
|
statement: 'The user prefers short replies.',
|
||||||
|
topics: ['user'],
|
||||||
|
source: 'test',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('BoxBrain memory stores', () => {
|
||||||
|
it('lists every fact in an in-memory persona space without requiring topic guesses', async () => {
|
||||||
|
const memory = new InMemoryMemoryStore();
|
||||||
|
const space = await memory.createSpace({ displayName: 'Mina', seedMessage: 'seed', now: '2026-05-14T00:00:00.000Z' });
|
||||||
|
await seedFacts(memory, space.id);
|
||||||
|
|
||||||
|
const facts = await memory.listFacts(space.id);
|
||||||
|
|
||||||
|
expect(facts.map((fact) => fact.statement)).toEqual([
|
||||||
|
'Mina likes quiet cafes.',
|
||||||
|
'The user prefers short replies.',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('lists every fact in a SQLite IdentityDB-backed persona space', async () => {
|
||||||
|
const dir = await mkdtemp(join(tmpdir(), 'boxbrain-memory-'));
|
||||||
|
try {
|
||||||
|
const memory = await createSqliteIdentityMemoryStore(join(dir, 'boxbrain.sqlite'));
|
||||||
|
const space = await memory.createSpace({ displayName: 'Mina', seedMessage: 'seed', now: '2026-05-14T00:00:00.000Z' });
|
||||||
|
await memory.addFact(space.id, {
|
||||||
|
statement: 'Mina likes quiet cafes.',
|
||||||
|
topics: ['persona', 'Mina'],
|
||||||
|
source: 'test',
|
||||||
|
});
|
||||||
|
await memory.addFact(space.id, {
|
||||||
|
statement: 'The user prefers short replies.',
|
||||||
|
topics: ['user'],
|
||||||
|
source: 'test',
|
||||||
|
});
|
||||||
|
|
||||||
|
const facts = await memory.listFacts(space.id);
|
||||||
|
|
||||||
|
expect(facts.map((fact) => fact.statement)).toEqual([
|
||||||
|
'Mina likes quiet cafes.',
|
||||||
|
'The user prefers short replies.',
|
||||||
|
]);
|
||||||
|
} finally {
|
||||||
|
await rm(dir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user