106 lines
4.3 KiB
JavaScript
106 lines
4.3 KiB
JavaScript
const { normalizeRefs } = require('../src/prompt/normalizeRefs');
|
|
const { buildFullContext, buildHybrid, prefixBeforeCheckpoint } = require('../src/prompt/build');
|
|
const { sampleBook } = require('./fixture');
|
|
|
|
const chapters = [
|
|
{ chapterNo: 0, title: 'Introduction', startPage: 1, endPage: 1 },
|
|
{ chapterNo: 1, title: 'Opening', startPage: 2, endPage: 2 },
|
|
{ chapterNo: 3, title: 'Workshop', startPage: 3, endPage: 4 },
|
|
];
|
|
|
|
describe('normalizeRefs', () => {
|
|
it('maps spoken chapter references', () => {
|
|
expect(normalizeRefs('summarize chapter three', chapters).hints[0].chapterNo).toBe(3);
|
|
expect(normalizeRefs('the third chapter', chapters).hints[0].chapterNo).toBe(3);
|
|
expect(normalizeRefs('chapter III', chapters).hints[0].chapterNo).toBe(3);
|
|
expect(normalizeRefs('the last chapter', chapters).hints[0].chapterNo).toBe(3);
|
|
expect(normalizeRefs('first chapter', chapters).hints[0].chapterNo).toBe(0);
|
|
expect(normalizeRefs('the introduction', chapters).hints[0].chapterNo).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('prompt prefix', () => {
|
|
it('keeps the cached prefix identical and puts hints after it', async () => {
|
|
const logger = { info() {}, warn() {} };
|
|
const book = sampleBook();
|
|
const first = await buildFullContext({
|
|
persona: 'companion',
|
|
channel: 'chat',
|
|
userText: 'hello',
|
|
book,
|
|
history: [],
|
|
rollingSummary: '',
|
|
cacheMinTokens: 4096,
|
|
cacheTtl: '5m',
|
|
logger,
|
|
});
|
|
const second = await buildFullContext({
|
|
persona: 'companion',
|
|
channel: 'chat',
|
|
userText: 'summarize chapter three',
|
|
book,
|
|
history: [{ role: 'user', text: 'earlier' }, { role: 'assistant', text: 'answer' }],
|
|
rollingSummary: 'old',
|
|
cacheMinTokens: 4096,
|
|
cacheTtl: '5m',
|
|
logger,
|
|
});
|
|
const prefixA = JSON.stringify(prefixBeforeCheckpoint(first.system));
|
|
const prefixB = JSON.stringify(prefixBeforeCheckpoint(second.system));
|
|
expect(prefixA).toBe(prefixB);
|
|
expect(prefixA).not.toContain('chapter three');
|
|
expect(JSON.stringify(second.system)).toContain('chapter three');
|
|
expect(second.system.some((block) => block.cachePoint && block.cachePoint.type === 'default')).toBe(true);
|
|
});
|
|
|
|
it('skips the checkpoint when the prefix is short', async () => {
|
|
const events = [];
|
|
const logger = { info(obj, msg) { events.push(msg || obj); }, warn() {} };
|
|
const built = await buildFullContext({
|
|
persona: 'companion',
|
|
channel: 'chat',
|
|
userText: 'hello',
|
|
book: sampleBook('Hi'),
|
|
history: [],
|
|
rollingSummary: '',
|
|
cacheMinTokens: 4096,
|
|
cacheTtl: '5m',
|
|
logger,
|
|
});
|
|
expect(built.system.some((block) => block.cachePoint)).toBe(false);
|
|
expect(events).toContain('cache_skipped_below_min');
|
|
});
|
|
|
|
it('keeps source-marker rules inside the cached prefix for both channels', async () => {
|
|
const logger = { info() {}, warn() {} };
|
|
const book = sampleBook();
|
|
const base = {
|
|
persona: 'companion',
|
|
userText: 'hello',
|
|
book,
|
|
history: [],
|
|
rollingSummary: '',
|
|
cacheMinTokens: 4096,
|
|
cacheTtl: '5m',
|
|
logger,
|
|
};
|
|
const chat = await buildFullContext({ ...base, channel: 'chat' });
|
|
const voice = await buildFullContext({ ...base, channel: 'voice' });
|
|
const chatPrefix = JSON.stringify(prefixBeforeCheckpoint(chat.system));
|
|
const voicePrefix = JSON.stringify(prefixBeforeCheckpoint(voice.system));
|
|
expect(chatPrefix).toBe(voicePrefix);
|
|
expect(chatPrefix).toContain('[[p:47]]');
|
|
expect(chatPrefix).not.toContain('Written reply style');
|
|
expect(chatPrefix).not.toContain('Spoken reply style');
|
|
expect(JSON.stringify(chat.system)).toContain('Markdown is allowed');
|
|
expect(JSON.stringify(voice.system)).toContain('in chapter three');
|
|
const hybridChat = await buildHybrid({ ...base, channel: 'chat' });
|
|
const hybridVoice = await buildHybrid({ ...base, channel: 'voice' });
|
|
expect(JSON.stringify(hybridChat.system)).toBe(JSON.stringify(hybridVoice.system));
|
|
expect(JSON.stringify(hybridChat.system)).toContain('[[p:47]]');
|
|
expect(JSON.stringify(hybridChat.system)).toContain('Cite only pages and chapters');
|
|
expect(JSON.stringify(hybridVoice.messages)).toContain('stripped before speech');
|
|
expect(JSON.stringify(hybridChat.messages)).toContain('Markdown is allowed');
|
|
});
|
|
});
|