feat(teacher): stream Yuxi reasoning and answer previews

This commit is contained in:
brother7 committed 2026-09-29 18:18:56 +08:00
1 parent f443f3bc66
commit 8ccc4650a2
12 files changed
+364 -42

No files matched your search

+33
View File
@@ -1633,3 +1633,36 @@ describe('project consultations with selected cloud teachers', () => {
await vi.waitFor(async () => expect((await f.service.read(scope, topic.id)).requests[0].status).toBe('completed'));
});
});
it.each(['completed', 'cancelled'] as const)('persists streamed reasoning and prose separately when %s', async terminal => {
const f = await fixture({ cloudTeacher: true, mockCloud: true });
const scope = { ...f.scope, sourceId: 'project' };
const topic = await f.service.create(scope, undefined, undefined, 9);
f.replyWith(terminal === 'completed' ? '{"reply":"最终答案","quickReplies":["继续"]}' : '');
const requestId = crypto.randomUUID();
const seen: TeacherTopic[] = [];
const unsubscribe = await f.service.subscribe(scope, topic.id, next => seen.push(next));
await f.service.send(scope, topic.id, { requestId, text: '检查项目' });
const update = f.prepareCloud.mock.calls[0][8] as (value: { response?: string; reasoning?: string }) => void;
update({ reasoning: '先分析当前代码。' });
update({ response: '{"reply":"正在生成' });
const live = await f.service.read(scope, topic.id);
expect(live.requests[0]).toMatchObject({ status: 'running', reasoning: '先分析当前代码。', response: '正在生成' });
expect(seen.at(-1)!.requests[0].response).toBe('正在生成');
update({ response: '' });
expect((await f.service.read(scope, topic.id)).requests[0].response).toBe('');
update({ response: '{"reply":"新的正文' });
if (terminal === 'cancelled') await f.service.cancel(scope, topic.id, requestId);
else f.finish();
await vi.waitFor(async () => expect((await f.service.read(scope, topic.id)).requests[0].status).toBe(terminal));
unsubscribe();
const restarted = await f.restart();
const saved = (await restarted.read(scope, topic.id)).requests[0];
expect(saved.reasoning).toBe('先分析当前代码。');
expect(saved.response).toBe(terminal === 'completed' ? '最终答案' : '新的正文');
if (terminal === 'cancelled') expect(saved.unparsedResponse).toBe('{"reply":"新的正文');
const nextId = crypto.randomUUID();
await restarted.send(scope, topic.id, { requestId: nextId, text: '接着解释' });
expect(JSON.stringify(f.run.mock.calls.at(-1)![0])).not.toContain('先分析当前代码。');
await restarted.cancel(scope, topic.id, nextId);
});