69 lines
5.0 KiB
JavaScript
69 lines
5.0 KiB
JavaScript
import { describe, it, expect } from 'vitest';
|
||
import { distillSpan } from './secretary-distill.mjs';
|
||
import { EMPTY_PROTOCOL } from './secretary-protocol.mjs';
|
||
|
||
describe('distillSpan — честная пометка спана в шаге (без модели)', () => {
|
||
it('передаёт note в шаг, когда спан помечен продолжением', async () => {
|
||
const proto = EMPTY_PROTOCOL();
|
||
const spanEx = { user: 'настоящая просьба длинная', assistant: 'докончил', actions: [] };
|
||
const out = await distillSpan(proto, spanEx, { start: 3, end: 4, note: '(связь прерывалась — продолжено)' }, { callModel: null });
|
||
const step = out.steps.find((s) => s.turn === 3);
|
||
expect(step.text.endsWith('(связь прерывалась — продолжено)')).toBe(true);
|
||
});
|
||
});
|
||
|
||
describe('distillSpan — разбор одного завершённого спана (reconcile + аудит)', () => {
|
||
it('добавляет шаг спана, применяет reconcile и аудит', async () => {
|
||
const proto = EMPTY_PROTOCOL();
|
||
const spanEx = { user: 'реши А или Б', assistant: 'беру А', actions: [{ tool: 'Read', input: 'x', result: 'y' }] };
|
||
let call = 0;
|
||
const callModel = async () => {
|
||
call++;
|
||
if (call === 1) return JSON.stringify({ subject: 'дело', decisions: [{ text: 'A', struck: false }], alternatives: [], consequences: [], will: [], open: [], doneNext: [], step: { user: 'реши', assistant: 'взял A' } });
|
||
return JSON.stringify({ new: [{ text: 'почему A?', lens: 'Л2' }], ops: [], resolved: [] });
|
||
};
|
||
const out = await distillSpan(proto, spanEx, { start: 3, end: 4 }, { callModel, session: 's' });
|
||
expect(out.steps[0].text).toContain('Ход (промпт) 3 [вобрал ходы 3-4]');
|
||
expect(out.decisions.map((d) => d.text)).toContain('A');
|
||
expect(out.hidden.map((h) => h.text)).toContain('почему A?');
|
||
});
|
||
it('без модели — только детерминированный шаг, категории пусты', async () => {
|
||
const out = await distillSpan(EMPTY_PROTOCOL(), { user: 'вопрос достаточно длинный', assistant: 'ответ', actions: [] },
|
||
{ start: 5, end: 5 }, { callModel: null, session: 's' });
|
||
expect(out.steps[0].text).toContain('Ход (промпт) 5');
|
||
expect(out.decisions).toEqual([]);
|
||
});
|
||
});
|
||
|
||
describe('distillSpan — новый конвейер «пушистое дерево» за флагом', () => {
|
||
it('при fluffy=on: ветка из диагностики + кандидат из брейншторма, аудит не зовётся', async () => {
|
||
const proto = EMPTY_PROTOCOL();
|
||
const spanEx = { user: 'предложи улучшение наставнику', assistant: 'разобрал по коду', actions: [] };
|
||
let auditCalled = false;
|
||
const callModel = async ({ system }) => {
|
||
if (/секретарь-редактор/.test(system)) return JSON.stringify({ subject: 'дело', decisions: [{ text: 'A', struck: false }], alternatives: [], consequences: [], will: [], open: [], doneNext: [], step: { user: 'u', assistant: 'a' } });
|
||
if (/сборщик скрытых веток/.test(system)) return JSON.stringify({ lenses: [{ lens: 'Л4', verdict: 'finding', text: 'без пруфа', опора: 'внутр', ref: 'x:1', тяжесть: 'важное' }] });
|
||
if (/ловец/.test(system)) return JSON.stringify({ dropped: [] });
|
||
if (/брейншторма/.test(system)) return JSON.stringify({ forks: [{ branch: 'идея', trigger: 't', опора: 'догадка', релевантность: 'low' }] });
|
||
if (/САДОВНИК/.test(system)) return JSON.stringify({ tend: [] });
|
||
if (/аудитор|скрытые вопросы/i.test(system)) { auditCalled = true; return '{"new":[]}'; }
|
||
return '{}';
|
||
};
|
||
const out = await distillSpan(proto, spanEx, { start: 15, end: 16 }, { callModel, session: 's', flags: { fluffy: true } });
|
||
expect(out.hidden.some((h) => h.text === 'без пруфа')).toBe(true);
|
||
expect((out.candidates || []).map((c) => c.branch)).toContain('идея');
|
||
expect(auditCalled).toBe(false);
|
||
});
|
||
|
||
it('флаг ВЫКЛ (по умолчанию) — старый путь, candidates нет', async () => {
|
||
const proto = EMPTY_PROTOCOL();
|
||
const spanEx = { user: 'вопрос достаточно длинный', assistant: 'ответ', actions: [] };
|
||
const callModel = async ({ system }) => {
|
||
if (/секретарь-редактор/.test(system)) return JSON.stringify({ subject: 'd', decisions: [], alternatives: [], consequences: [], will: [], open: [], doneNext: [], step: { user: 'u', assistant: 'a' } });
|
||
return JSON.stringify({ new: [], ops: [], resolved: [] });
|
||
};
|
||
const out = await distillSpan(proto, spanEx, { start: 5, end: 5 }, { callModel, session: 's', flags: { fluffy: false } });
|
||
expect(out.candidates).toBeUndefined();
|
||
});
|
||
});
|