feat: implement QuestionBank CRUD with pagination and template query

- Add pagination support to findAll (page, limit query params)
- Add findByTemplateId method to service
- Add GET /by-template/:templateId endpoint to controller
- Service already includes CRUD for QuestionBank and QuestionBankItem
This commit is contained in:
Developer
2026-04-23 17:19:11 +08:00
commit 0a9588abb7
492 changed files with 112453 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
import { StateGraph, MemorySaver } from '@langchain/langgraph';
import { EvaluationAnnotation } from './state';
import { questionGeneratorNode } from './nodes/generator.node';
import { interviewerNode } from './nodes/interviewer.node';
import { graderNode } from './nodes/grader.node';
import { reportAnalyzerNode } from './nodes/analyzer.node';
/**
* Conditional routing logic for the Grader node.
*/
const routeAfterGrading = (state: typeof EvaluationAnnotation.State) => {
const targetCount = state.questionCount || 5;
const questionsLen = state.questions?.length || 0;
console.log('[Router] Evaluation Result:', {
currentIndex: state.currentQuestionIndex,
shouldFollowUp: state.shouldFollowUp,
numQuestions: questionsLen,
targetCount,
});
if (state.shouldFollowUp) {
console.log('[Router] Routing to follow-up interviewer');
return 'interviewer';
}
if (state.currentQuestionIndex < targetCount) {
// If the next question isn't generated yet, go back to generator
if (state.currentQuestionIndex >= questionsLen) {
console.log('[Router] Index >= Questions, routing to generator');
return 'generator';
}
// If it is generated, go to interviewer
console.log('[Router] Index < Questions, routing to interviewer');
return 'interviewer';
}
console.log('[Router] Assessment complete, routing to analyzer');
return 'analyzer';
};
/**
* Builds and compiles the Evaluation Graph.
*/
export const createEvaluationGraph = () => {
const workflow = new StateGraph(EvaluationAnnotation)
.addNode('generator', questionGeneratorNode)
.addNode('interviewer', interviewerNode)
.addNode('grader', graderNode)
.addNode('analyzer', reportAnalyzerNode)
// Flow definition
.addEdge('__start__', 'generator')
.addEdge('generator', 'interviewer')
// After interviewer, the graph will naturally pause for user input
// if we use it in a thread-safe way with interrupts or simple invocation.
.addEdge('interviewer', 'grader')
// After grading, decide where to go
.addConditionalEdges('grader', routeAfterGrading, {
interviewer: 'interviewer',
generator: 'generator',
analyzer: 'analyzer',
})
.addEdge('analyzer', '__end__');
// Using MemorySaver for thread-based persistence
const checkpointer = new MemorySaver();
return workflow.compile({
checkpointer,
// We want the graph to stop after the interviewer presents the question
interruptAfter: ['interviewer'],
});
};