profileShare

rasmusjy / voicetask

Read-only snapshot

No repository description.

main default branch 105 files Expires Sep 13, 2026, 9:06 AM
audio.ts 1,773 bytes
1 import type { FastifyInstance } from 'fastify'
2 import { SessionNotFoundError, submitAnswer } from '../engine/turn'
3 import {
4 EmptyTranscriptError,
5 ProviderNotConfiguredError,
6 type InterviewLlm,
7 type SttProvider,
8 } from '../providers/types'
9 import type { SessionStore } from '../store/sessionStore'
10
11 export interface AudioRouteDeps {
12 store: SessionStore
13 llm: InterviewLlm
14 stt: SttProvider
15 }
16
17 export function registerAudioRoutes(app: FastifyInstance, deps: AudioRouteDeps): void {
18 const { store, llm, stt } = deps
19
20 app.post<{ Params: { id: string } }>('/api/sessions/:id/audio', async (request, reply) => {
21 const file = await request.file()
22 if (!file) return reply.code(400).send({ error: 'no audio file uploaded' })
23
24 const buffer = await file.toBuffer()
25 if (buffer.length === 0) {
26 return reply.code(400).send({ error: 'empty audio upload' })
27 }
28
29 let text: string
30 try {
31 text = await stt.transcribe(buffer, file.mimetype)
32 } catch (err) {
33 if (err instanceof EmptyTranscriptError) {
34 return reply.code(400).send({ error: 'empty transcript' })
35 }
36 if (err instanceof ProviderNotConfiguredError) {
37 return reply.code(503).send({ error: err.message })
38 }
39 return reply.code(502).send({ error: err instanceof Error ? err.message : 'STT provider error' })
40 }
41
42 try {
43 const response = await submitAnswer(store, llm, request.params.id, text)
44 return reply.send(response)
45 } catch (err) {
46 if (err instanceof SessionNotFoundError) {
47 return reply.code(404).send({ error: 'session not found' })
48 }
49 if (err instanceof ProviderNotConfiguredError) {
50 return reply.code(503).send({ error: err.message })
51 }
52 throw err
53 }
54 })
55 }
56