profileShare

rasmusjy / voicetask

Read-only snapshot

No repository description.

main default branch 105 files Expires Sep 13, 2026, 9:06 AM
turn.ts 1,510 bytes
1 import type { AnswerResponse, InterviewTurn } from '../../shared/types'
2 import type { InterviewLlm } from '../providers/types'
3 import type { SessionStore } from '../store/sessionStore'
4 import { isDoneAnswer, runInterviewTurn } from './interview'
5
6 export class SessionNotFoundError extends Error {
7 constructor(sessionId: string) {
8 super(`session not found: ${sessionId}`)
9 }
10 }
11
12 export async function submitAnswer(
13 store: SessionStore,
14 llm: InterviewLlm,
15 sessionId: string,
16 text: string,
17 ): Promise<AnswerResponse> {
18 const existing = await store.getSession(sessionId)
19 if (!existing) throw new SessionNotFoundError(sessionId)
20
21 const { session: afterAnswer, segment } = await store.appendSegment(sessionId, 'user', text)
22
23 let turn: InterviewTurn
24 let remainingBlockers = afterAnswer.openBlockers
25
26 if (!isDoneAnswer(text) && afterAnswer.openBlockers.length > 0) {
27 const [nextQuestion, ...rest] = afterAnswer.openBlockers
28 remainingBlockers = rest
29 turn = {
30 coverage: afterAnswer.coverage,
31 nextQuestion,
32 contradiction: null,
33 done: false,
34 summaryUpdate: null,
35 }
36 } else {
37 turn = await runInterviewTurn(llm, afterAnswer)
38 }
39
40 await store.appendSegment(sessionId, 'interviewer', turn.nextQuestion)
41 await store.updateTurn(sessionId, {
42 coverage: turn.coverage,
43 summary: turn.summaryUpdate ?? afterAnswer.summary,
44 status: turn.done ? 'done' : 'interviewing',
45 openBlockers: remainingBlockers,
46 })
47
48 return { segment, turn }
49 }
50