128 lines
4.9 KiB
TypeScript
128 lines
4.9 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { InMemoryMemoryStore, Persona, type ReplyGenerationInput } from '../src';
|
|
|
|
describe('Conversation API', () => {
|
|
it('loads mandatory memory, schedules, availability, and formatted history before replying', async () => {
|
|
const memory = new InMemoryMemoryStore();
|
|
let captured: ReplyGenerationInput | undefined;
|
|
const persona = new Persona('Mina', 'Mina likes quiet cafes.', {
|
|
memory,
|
|
now: '2026-05-01T10:00:00.000Z',
|
|
models: {
|
|
conversation: {
|
|
async generateReply(input) {
|
|
captured = input;
|
|
return { messages: ['카페에 있었어.', '너는 뭐해?'] };
|
|
},
|
|
},
|
|
schedule: {
|
|
async generateDailySchedule() {
|
|
return [
|
|
{ startTime: '09:00', endTime: '18:00', activity: 'work', title: 'Work', availabilityMode: 'do-not-disturb' as const },
|
|
];
|
|
},
|
|
async generateMonthlySchedule() {
|
|
return [];
|
|
},
|
|
},
|
|
},
|
|
});
|
|
const space = await persona.ready();
|
|
await memory.addFact(space.id, { statement: 'The user is close to Mina.', topics: ['user', 'persona'] });
|
|
await persona.createDailySchedule('2026-05-01T10:00:00.000Z', 'study for an exam');
|
|
|
|
const reply = await persona.sendMessage({
|
|
datetime: '2026-05-01T12:00:00.000Z',
|
|
messageHistory: [
|
|
{ sender: 'persona', time: '2026-04-30T23:00:00.000Z', content: '다음에 보자' },
|
|
{ sender: 'user', time: '2026-05-01T12:00:00.000Z', content: '지금 뭐해?' },
|
|
],
|
|
});
|
|
|
|
expect(reply.messages).toEqual(['카페에 있었어.', '너는 뭐해?']);
|
|
expect(captured?.context.formattedMessageHistory).toContain('Mina@2026-04-30T23:00:00.000Z: 다음에 보자');
|
|
expect(captured?.context.formattedMessageHistory).toContain('user@2026-05-01T12:00:00.000Z: 지금 뭐해?');
|
|
expect(captured?.context.memorySummary).toContain('The user is close to Mina.');
|
|
expect(captured?.context.scheduleEntries.length).toBeGreaterThan(0);
|
|
expect(captured?.context.availability.ranges.length).toBeGreaterThan(0);
|
|
expect(captured?.instruction).toContain('send_message');
|
|
});
|
|
|
|
it('explicitly tells the response model when mandatory memory is missing', async () => {
|
|
const memory = new InMemoryMemoryStore();
|
|
let memorySummary = '';
|
|
const persona = new Persona('Mina', 'Mina is new.', {
|
|
memory,
|
|
now: '2026-05-01T10:00:00.000Z',
|
|
models: {
|
|
initialization: { async extractInitialFacts() { return []; } },
|
|
conversation: {
|
|
async generateReply(input) {
|
|
memorySummary = input.context.memorySummary;
|
|
return { messages: ['잘 모르겠어.'] };
|
|
},
|
|
},
|
|
},
|
|
});
|
|
await persona.ready();
|
|
|
|
await persona.sendMessage({
|
|
datetime: '2026-05-01T12:00:00.000Z',
|
|
messageHistory: [{ sender: 'user', time: '2026-05-01T12:00:00.000Z', content: '나 기억해?' }],
|
|
});
|
|
|
|
expect(memorySummary).toBe('기억이 없음');
|
|
});
|
|
|
|
it('lets a rewrite model discard a stale draft when new user messages arrive', async () => {
|
|
const memory = new InMemoryMemoryStore();
|
|
const persona = new Persona('Mina', 'Mina is concise.', {
|
|
memory,
|
|
now: '2026-05-01T10:00:00.000Z',
|
|
models: {
|
|
conversation: { async generateReply() { return { messages: ['첫 답장'] }; } },
|
|
rewrite: {
|
|
async decide() {
|
|
return { rewrite: true, draft: { messages: ['새 메시지까지 보고 답장'] }, reason: 'latest user message changes intent' };
|
|
},
|
|
},
|
|
},
|
|
});
|
|
await persona.ready();
|
|
|
|
const reply = await persona.sendMessage({
|
|
datetime: '2026-05-01T12:00:00.000Z',
|
|
messageHistory: [{ sender: 'user', time: '2026-05-01T12:00:00.000Z', content: '안녕' }],
|
|
getLatestMessageHistory: async () => [
|
|
{ sender: 'user', time: '2026-05-01T12:00:00.000Z', content: '안녕' },
|
|
{ sender: 'user', time: '2026-05-01T12:00:05.000Z', content: '아 맞다, 지금 바빠?' },
|
|
],
|
|
});
|
|
|
|
expect(reply.messages).toEqual(['새 메시지까지 보고 답장']);
|
|
});
|
|
|
|
it('can proactively start a conversation with the same mandatory context pipeline', async () => {
|
|
const memory = new InMemoryMemoryStore();
|
|
let mode = '';
|
|
const persona = new Persona('Mina', 'Mina starts soft conversations.', {
|
|
memory,
|
|
now: '2026-05-01T10:00:00.000Z',
|
|
models: {
|
|
conversation: {
|
|
async generateReply(input) {
|
|
mode = input.mode;
|
|
return { messages: ['오늘 좀 조용하네.'] };
|
|
},
|
|
},
|
|
},
|
|
});
|
|
await persona.ready();
|
|
|
|
const started = await persona.startConversation({ datetime: '2026-05-01T20:00:00.000Z', messageHistory: [] });
|
|
|
|
expect(mode).toBe('start-conversation');
|
|
expect(started.messages).toEqual(['오늘 좀 조용하네.']);
|
|
});
|
|
});
|