Add contextual teacher help entry and simplify consultation panel
This commit is contained in:
@@ -26,6 +26,7 @@ import { compileTeacherContext } from './context';
|
||||
import { prepareTeacherModel } from './model-runner';
|
||||
import { consultationDefinition } from './consultation-role';
|
||||
import { readTeacherSource } from './source-reader';
|
||||
import { parseTeacherSuggestions } from './suggestions';
|
||||
import { subscribeWorksSquareSession } from '../services/works-square-session';
|
||||
|
||||
export interface TeacherScope {
|
||||
@@ -203,6 +204,13 @@ export class CodingTeacherService {
|
||||
}
|
||||
async send(scope: TeacherScope, id: string, input: TeacherSend): Promise<TeacherTopic> {
|
||||
teacherTopicId(input.requestId);
|
||||
const intent = input.intent === undefined ? 'question' : input.intent;
|
||||
if (!['question', 'suggestions', 'guided-help'].includes(intent))
|
||||
throw new TeacherError(422, 'teacher_intent_invalid', '提问方式无效,请重新打开老师后再试。');
|
||||
if (intent !== 'question' && (
|
||||
scope.projectId === 'preview' || scope.sourceId !== 'project' || (scope.role ?? 'teacher') !== 'teacher'
|
||||
))
|
||||
throw new TeacherError(422, 'teacher_intent_invalid', '这种提问方式只适用于项目里的老师。');
|
||||
if (input.sourceConversationId !== undefined) teacherTopicId(input.sourceConversationId);
|
||||
if (typeof input.text !== 'string' || !input.text.trim() || input.text.length > 6000)
|
||||
throw new TeacherError(422, 'teacher_question_invalid', '请输入 1–6000 字的问题。');
|
||||
@@ -228,6 +236,7 @@ export class CodingTeacherService {
|
||||
if (existing) {
|
||||
if (
|
||||
existing.text !== input.text ||
|
||||
(existing.intent ?? 'question') !== intent ||
|
||||
JSON.stringify(existing.references) !== JSON.stringify(refs) ||
|
||||
(existing.sourceConversationId ?? undefined) !== (input.sourceConversationId ?? undefined)
|
||||
)
|
||||
@@ -301,7 +310,8 @@ export class CodingTeacherService {
|
||||
topic.requests,
|
||||
input.text,
|
||||
references,
|
||||
model.inputLimit
|
||||
model.inputLimit,
|
||||
intent
|
||||
);
|
||||
// Reading context and resolving model credentials can yield while a source
|
||||
// is being deleted. Project consultations must recheck the actual source.
|
||||
@@ -309,6 +319,7 @@ export class CodingTeacherService {
|
||||
throw new TeacherError(404, 'teacher_source_not_found', '来源会话已删除。');
|
||||
const request = {
|
||||
id: input.requestId,
|
||||
intent,
|
||||
...(input.sourceConversationId ? { sourceConversationId: input.sourceConversationId } : {}),
|
||||
text: input.text,
|
||||
references,
|
||||
@@ -349,6 +360,12 @@ export class CodingTeacherService {
|
||||
topic.revision++;
|
||||
this.events.emit(key, structuredClone(topic));
|
||||
});
|
||||
this.assertAccount(account);
|
||||
if (!controller.signal.aborted && intent === 'suggestions') {
|
||||
const suggestions = parseTeacherSuggestions(current.response);
|
||||
current.response = suggestions.intro;
|
||||
current.suggestedQuestions = suggestions.questions;
|
||||
}
|
||||
current.status = controller.signal.aborted ? 'cancelled' : 'completed';
|
||||
} catch (error) {
|
||||
current.status = controller.signal.aborted ? 'cancelled' : 'failed';
|
||||
@@ -358,6 +375,10 @@ export class CodingTeacherService {
|
||||
? error.message
|
||||
: '老师回复失败,已保留本次问题与收到的内容。';
|
||||
} finally {
|
||||
if (intent === 'suggestions' && current.status !== 'completed') {
|
||||
current.response = '';
|
||||
delete current.suggestedQuestions;
|
||||
}
|
||||
topic.updatedAt = new Date().toISOString();
|
||||
topic.revision++;
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user