Merge Yuxi teacher support with classroom discussions
This commit is contained in:
@@ -14,6 +14,7 @@ const stream = Object.assign(new EventTarget(), { close() {}, onerror: null, ono
|
||||
let timer: ReturnType<typeof setTimeout> | undefined;
|
||||
const snapshot = () => { current.revision++; stream.dispatchEvent(new MessageEvent('snapshot', { data: JSON.stringify(current) })); };
|
||||
export const teacherApi = {
|
||||
catalog: async () => ({ items: [{ teacher_id: definition.teacher_id, version: 1, is_default: true, definition }] }),
|
||||
config: async () => ({ enabled: true, definition }),
|
||||
list: async () => ({ items: [{ id: current.id, title: '我的小狗世界' }], lastSelectedTopicId: current.id }),
|
||||
read: async () => structuredClone(current),
|
||||
|
||||
@@ -450,6 +450,10 @@ async function installCodingFirstChatHost(
|
||||
});
|
||||
|
||||
const teacherDefinition = {schema_version:1,teacher_id:'coding-teacher',name:'代码老师',description:'',avatar_id:'avatar-01',welcome_message:'一起理解代码',suggested_questions:['解释当前代码'],system_prompt:'教学',skills:[],model:{model_id:'model-a',reasoning_choice:{mode:'default'}},limits:{max_input_tokens:8000,max_output_tokens:1500}};
|
||||
const teachers = [
|
||||
{ teacher_id: 'teacher-code', version: 1, is_default: true, definition: { ...teacherDefinition, config_id: 'teacher-code', runtime: 'yuxi', system_prompt: '', yuxi: { agent_slug: 'code', agent_version: 2 } } },
|
||||
{ teacher_id: 'teacher-algorithm', version: 9, is_default: false, definition: { ...teacherDefinition, config_id: 'teacher-algorithm', runtime: 'yuxi', name: '算法老师', system_prompt: '', yuxi: { agent_slug: 'algorithm', agent_version: 4 } } },
|
||||
];
|
||||
const friendDefinition = {...teacherDefinition,teacher_id:'coding-friend',name:'小麦',welcome_message:'一起看看你的作品',suggested_questions:['想听听你对作品的第一印象']};
|
||||
const consultationTopics: Record<string, Record<string, unknown>> = {};
|
||||
ipcMain.removeHandler('hostapi:fetch');
|
||||
@@ -529,6 +533,7 @@ async function installCodingFirstChatHost(
|
||||
consultationTopics.teacher = {...current,revision:Number(current.revision)+1,requests:[...(current.requests as unknown[]),{id:body!.requestId,intent:'check-in',text:'',references:[],sourceConversationId:body!.sourceConversationId,createdAt:now,sourceCursor:{workerGeneration:1,seq:1},sourceCapturedAt:now,includedSourceMessageIds:[],omittedMessages:0,status:'completed',response:'你刚才想加排行榜,我们一起想想比什么更有意思?'}]};
|
||||
return respond({topic:consultationTopics.teacher});
|
||||
}
|
||||
if (path === '/api/coding/teacher/teachers') return respond({ items: teachers });
|
||||
if (path === '/api/coding/teacher/config') return respond({enabled:true,revision:1,published_version:1,definition:teacherDefinition});
|
||||
if (path === '/api/coding/friend/config') return respond({enabled:true,revision:1,published_version:1,definition:friendDefinition});
|
||||
const consultationRole = path.match(/\/(teacher|friend)-topics(?:\/|$)/)?.[1];
|
||||
@@ -538,13 +543,14 @@ async function installCodingFirstChatHost(
|
||||
const currentTopic = consultationTopics[consultationRole];
|
||||
if (path.endsWith(topicBase) && method === 'GET') return respond({items:currentTopic?[{id:currentTopic.id,title:consultationRole==='friend'?'朋友话题':'老师话题'}]:[],lastSelectedTopicId:currentTopic?.id??null});
|
||||
if (path.endsWith(topicBase) && method === 'POST') {
|
||||
consultationTopics[consultationRole]={schemaVersion:1,revision:0,id:topicId,accountId:'e2e',projectId:project.id,role:consultationRole,sourceConversationId:conversation.id,definition:consultationRole==='friend'?friendDefinition:teacherDefinition,version:1,createdAt:now,updatedAt:now,requests:[]};
|
||||
const selected = teachers.find(item => item.version === body?.teacherVersion) ?? teachers[0];
|
||||
consultationTopics[consultationRole]={schemaVersion:1,revision:0,id:topicId,accountId:'e2e',projectId:project.id,role:consultationRole,sourceConversationId:conversation.id,definition:consultationRole==='friend'?friendDefinition:selected.definition,version:consultationRole==='friend'?1:selected.version,createdAt:now,updatedAt:now,requests:[]};
|
||||
return respond(consultationTopics[consultationRole],201);
|
||||
}
|
||||
if (path.endsWith(`${topicBase}/${topicId}/messages`) && method === 'POST') {
|
||||
const suggestionRequest = body!.intent === 'suggestions';
|
||||
const teacherResponse = suggestionRequest ? '我们可以从你最近试过的地方聊起。' : body!.intent === 'guided-help' ? '你最近做的哪一步,让你停下来想了一会儿?' : '先理解状态如何随点击变化,再修改代码。';
|
||||
consultationTopics[consultationRole]={...currentTopic,revision:Number(currentTopic?.revision??0)+1,requests:[...(currentTopic?.requests as unknown[]??[]),{id:body!.requestId,text:body!.text,intent:body!.intent,references:body!.references??[],createdAt:now,sourceCursor:{workerGeneration:1,seq:1},sourceCapturedAt:now,includedSourceMessageIds:[],omittedMessages:0,status:'completed',response:consultationRole==='friend'?'听你的描述,我有点好奇该从哪里开始玩。':teacherResponse,...(suggestionRequest?{suggestedQuestions:['怎样观察别人玩游戏?','我该先试哪个想法?']}:{})}]};
|
||||
consultationTopics[consultationRole]={...currentTopic,revision:Number(currentTopic?.revision??0)+1,requests:[...(currentTopic?.requests as unknown[]??[]),{id:body!.requestId,text:body!.text,intent:body!.intent,references:body!.references??[],createdAt:now,sourceCursor:{workerGeneration:1,seq:1},sourceCapturedAt:now,includedSourceMessageIds:[],omittedMessages:0,truncatedMessages:1,status:'completed',response:consultationRole==='friend'?'听你的描述,我有点好奇该从哪里开始玩。':teacherResponse,...(suggestionRequest?{suggestedQuestions:['怎样观察别人玩游戏?','我该先试哪个想法?']}:{})}]};
|
||||
return respond(consultationTopics[consultationRole],202);
|
||||
}
|
||||
if (path.endsWith(`${topicBase}/${topicId}`)) return respond(currentTopic);
|
||||
@@ -1781,6 +1787,7 @@ test('project consultations preserve student drafts and switch between work and
|
||||
await expect(composer).toBeHidden();
|
||||
await expect(composer).toHaveValue('保留我的草稿');
|
||||
await expect(teacher.getByText('有问题,随时来找我')).toBeVisible();
|
||||
await teacher.getByRole('combobox', { name: '新话题使用的老师' }).selectOption('9');
|
||||
await expect(page.locator('#coding-consultation-dock')).toHaveCSS('width', '508px');
|
||||
await expect(teacher.getByTestId('consultation-composer-actions').getByRole('button', { name: '老师帮我看看', exact: true })).toBeVisible();
|
||||
await expect(teacher.getByText('把你的困惑说出来就好')).toHaveCount(0);
|
||||
@@ -1823,6 +1830,8 @@ test('project consultations preserve student drafts and switch between work and
|
||||
expect(requests.filter(item=>item.path.endsWith('/prompt')&&item.method==='POST')).toHaveLength(0);
|
||||
const consultationRequests=requests.filter(item=>item.path.endsWith('/messages')&&item.method==='POST');
|
||||
expect(consultationRequests).toHaveLength(3);
|
||||
expect(requests.find(item => item.path.endsWith('/teacher-topics') && item.method === 'POST')?.body?.teacherVersion).toBe(9);
|
||||
await expect(teacher.getByRole('heading', { name: '算法老师', exact: true })).toBeVisible();
|
||||
expect(consultationRequests[0].body).toMatchObject({intent:'suggestions',text:'老师帮我看看'});
|
||||
expect(consultationRequests[1].body).toMatchObject({text:'怎样观察别人玩游戏?'});
|
||||
expect(consultationRequests[1].body?.intent).toBeUndefined();
|
||||
|
||||
Reference in New Issue
Block a user