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:
@@ -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'],
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user