Merge reply recovery and simplified consultation UI into ongoing chats
This commit is contained in:
@@ -12,10 +12,9 @@ import { TeacherError } from '../../electron/coding-teacher/config-client';
|
||||
import * as teacherConfig from '../../electron/coding-teacher/config-client';
|
||||
import * as cloudFetch from '../../electron/utils/proxy-fetch';
|
||||
import { compileTeacherContext, estimateTeacherTokens } from '../../electron/coding-teacher/context';
|
||||
import { discussionInstructions } from '../../electron/coding-teacher/discussion';
|
||||
import { teacherReplyInstructions } from '../../electron/coding-teacher/reply';
|
||||
const TEACHER_BEHAVIOR_PROMPT = '你是麦洛的创作老师';
|
||||
import { parseTeacherSuggestions } from '../../electron/coding-teacher/suggestions';
|
||||
import { parseTeacherDiscussionReply } from '../../shared/teacher-discussion';
|
||||
import { parseTeacherReply } from '../../shared/teacher-reply';
|
||||
import type { TeacherRequestIntent, TeacherTopic } from '../../shared/coding-teacher';
|
||||
|
||||
const roots: string[] = [];
|
||||
@@ -377,25 +376,20 @@ it('closes a pending tool as failed when its question fails after a raw tool err
|
||||
]);
|
||||
});
|
||||
|
||||
it('submits the active discussion protocol, current tool content and selected focus', async () => {
|
||||
it('submits only the reply protocol without archived component state or focus', async () => {
|
||||
const f = await fixture();
|
||||
f.topic.definition.limits.max_input_tokens = 16000;
|
||||
f.topic.discussion = {
|
||||
id: 'ideas', revision: 4, status: 'active',
|
||||
content: { kind: 'ideas', title: '跳跃游戏', items: [{ id: 'gravity', text: '比较两种重力', state: 'kept' }] },
|
||||
};
|
||||
const protocol = discussionInstructions(f.topic, { toolId: 'ideas', revision: 4, focusId: 'gravity' });
|
||||
const { compiled } = await submitCompiledContext(f, 'question', protocol);
|
||||
f.topic.discussion = { id: 'legacy', revision: 4, status: 'active', content: { kind: 'ideas', title: 'legacy-state-sentinel' } };
|
||||
const protocol = teacherReplyInstructions();
|
||||
const { compiled, body } = await submitCompiledContext(f, 'question', protocol);
|
||||
expect(compiled.messages.at(-2)).toEqual({ role: 'system', content: protocol });
|
||||
expect(protocol).toContain('只输出一个JSON对象');
|
||||
expect(protocol).toContain('"status":"active"');
|
||||
expect(protocol).toContain('"focusId":"gravity"');
|
||||
expect(protocol).toContain('比较两种重力');
|
||||
expect(protocol).toContain('quickReplies');
|
||||
expect(body.query).not.toContain('legacy-state-sentinel');
|
||||
for (const removed of ['focusId', 'firstItemId', '先这些', 'tool:', '组件']) expect(protocol).not.toContain(removed);
|
||||
});
|
||||
|
||||
it('submits a short first question with the normal discussion protocol and published budget', async () => {
|
||||
const f = await fixture();
|
||||
const { body } = await submitCompiledContext(f, 'question', discussionInstructions(f.topic));
|
||||
const { body } = await submitCompiledContext(f, 'question', teacherReplyInstructions());
|
||||
expect(body.query).toContain('下一步怎么想?');
|
||||
});
|
||||
|
||||
@@ -411,7 +405,7 @@ it('fits code excerpts beside the unchanged discussion protocol in the original
|
||||
const f = await fixture();
|
||||
const original = 'const config = { "title": "小游戏" };\n'.repeat(500);
|
||||
f.access.source.messages[0].text = original;
|
||||
const protocol = discussionInstructions(f.topic);
|
||||
const protocol = teacherReplyInstructions();
|
||||
const { body, compiled } = await submitCompiledContext(f, 'question', protocol);
|
||||
expect(compiled.messages.at(-2)?.content).toBe(protocol);
|
||||
expect(compiled.truncatedMessages).toBeGreaterThan(0);
|
||||
@@ -749,14 +743,14 @@ it('reconnects from the last cursor and fills the final message after an earlier
|
||||
expect(f.progress).toHaveBeenCalledWith('连接中断,正在恢复智能体回复…');
|
||||
});
|
||||
|
||||
it.each(['suggestions', 'discussion-v1'] as const)(
|
||||
it.each(['suggestions', 'reply-v1', 'discussion-v1'] as const)(
|
||||
'delivers only the final %s JSON after a streamed preamble and a local read', async (format) => {
|
||||
const f = await fixture();
|
||||
f.topic.requests.push({
|
||||
id: requestId, text: '一起讨论', references: [], createdAt: 'now',
|
||||
sourceCursor: f.access.source.cursor, sourceCapturedAt: 'now',
|
||||
includedSourceMessageIds: [], omittedMessages: 0, status: 'running', response: '',
|
||||
...(format === 'suggestions' ? { intent: 'suggestions' } : { presentation: 'discussion-v1' }),
|
||||
...(format === 'suggestions' ? { intent: 'suggestions' } : { presentation: format }),
|
||||
});
|
||||
const result = format === 'suggestions'
|
||||
? { intro: '我们可以从重力聊起。', questions: ['重力影响了什么?', '怎样比较两种重力?'] }
|
||||
@@ -806,9 +800,9 @@ it.each(['suggestions', 'discussion-v1'] as const)(
|
||||
[{ id: requestId + ':file', name: 'read_project_file', status: 'completed' }],
|
||||
]);
|
||||
const response = text.mock.calls.map(([delta]) => delta).join('');
|
||||
expect(format === 'suggestions'
|
||||
? parseTeacherSuggestions(response)
|
||||
: parseTeacherDiscussionReply(response)).toMatchObject(result);
|
||||
expect(parseTeacherReply(response)).toEqual(format === 'suggestions'
|
||||
? { reply: result.intro, quickReplies: result.questions }
|
||||
: { reply: result.reply, quickReplies: result.quickReplies });
|
||||
expect(f.progress).toHaveBeenCalledWith('正在读取项目与会话…');
|
||||
expect(f.progress).toHaveBeenCalledWith('智能体正在继续思考…');
|
||||
expect(transport.json).toHaveBeenCalledWith('/runs/before-read/tool-results', {
|
||||
|
||||
Reference in New Issue
Block a user