feat: end-to-end choice question support in assessment pipeline

- Data pathway: flow options through questions, answerKey in graph state
- Interviewer: format MULTIPLE_CHOICE with A/B/C/D options
- Grader: instant choice scoring (zero LLM), compare correctAnswer
- AssessmentView: render choice buttons vs textarea based on questionType
- Security: sanitizeStateForClient strips correctAnswer/judgment/answerKey
- Bank detection: check PUBLISHED items (not PUBLISHED bank status)
- Batch UI: select all / batch approve / batch reject on detail view
This commit is contained in:
Developer
2026-05-21 10:06:33 +08:00
parent 57898f939c
commit 3993099907
7 changed files with 228 additions and 24 deletions
@@ -33,8 +33,6 @@ export const interviewerNode = async (
const currentQuestion = questions[currentQuestionIndex];
// If it's a follow-up, we add a prefix to the label later.
// If we've run out of questions and no follow-up requested, we shouldn't be here, but let's be safe.
if (currentQuestionIndex >= questions.length) {
return { shouldFollowUp: false };
}
@@ -49,12 +47,10 @@ export const interviewerNode = async (
state.feedbackHistory &&
state.feedbackHistory.length > 0
) {
// Construct a follow-up prompt based on last feedback
const lastFeedbackMsg =
state.feedbackHistory[state.feedbackHistory.length - 1];
const feedbackText = lastFeedbackMsg.content.toString();
// Extract the "Feedback: ..." part if possible, otherwise use whole text
const feedbackMatch = feedbackText.match(
/(?:Feedback|反馈|フィードバック): ([\s\S]*)/i,
);
@@ -74,8 +70,22 @@ export const interviewerNode = async (
: 'Based on the feedback above, please provide more specific details:';
prompt = `${followUpLabel}\n\n${specificFeedback}\n\n${followUpInstruction}`;
} else if (currentQuestion.questionType === 'MULTIPLE_CHOICE' && currentQuestion.options?.length > 0) {
const label = isZh
? `问题 ${currentQuestionIndex + 1}`
: isJa
? `質問 ${currentQuestionIndex + 1}`
: `Question ${currentQuestionIndex + 1}`;
const optionsText = currentQuestion.options.join('\n');
const instruction = isZh
? '请选择一个选项(输入字母 A/B/C/D)'
: isJa
? '選択肢から1つ選んでください(A/B/C/Dを入力)'
: 'Please select one option (enter A, B, C, or D)';
prompt = `${label}: ${currentQuestion.questionText}\n\n${optionsText}\n\n${instruction}`;
} else {
// Standard question presentation
const label = isZh
? `问题 ${currentQuestionIndex + 1}`
: isJa