Files
XCDesktop/api/modules/voice/routes.ts

118 lines
3.4 KiB
TypeScript

import { Router } from 'express'
import { config } from '../../config/index.js'
interface AIMLAPISTTCreateResponse {
generation_id?: string
status?: string
error?: {
message: string
}
}
interface AIMLAPISTTQueryResponse {
status?: string
result?: {
results?: {
channels?: Array<{
alternatives?: Array<{
transcript?: string
}>
}>
}
}
error?: {
message: string
}
}
export function createVoiceRoutes(): Router {
const router = Router()
router.post('/stt', async (req, res) => {
console.log('[Voice] Request received, body keys:', Object.keys(req.body || {}))
try {
const apiKey = config.minimaxApiKey
console.log('[Voice] API Key exists:', !!apiKey)
if (!apiKey) {
res.status(500).json({ error: 'MiniMax API key not configured' })
return
}
if (!req.body.audio) {
res.status(400).json({ error: 'No audio data provided' })
return
}
const audioBuffer = Buffer.from(req.body.audio, 'base64')
console.log('[Voice] Audio buffer size:', audioBuffer.length)
const formData = new FormData()
const blob = new Blob([audioBuffer], { type: 'audio/webm' })
formData.append('file', blob, 'audio.webm')
formData.append('model', '#g1_whisper-large')
console.log('[Voice] Creating STT job via MiniMax...')
const createResponse = await fetch('https://api.minimax.chat/v1/stt/create', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
},
body: formData,
})
console.log('[Voice] Create response status:', createResponse.status)
const createData: AIMLAPISTTCreateResponse = await createResponse.json()
console.log('[Voice] Create response:', createData)
if (!createResponse.ok || !createData.generation_id) {
console.error('[Voice] Failed to create STT job:', createData.error?.message)
res.status(500).json({ error: createData.error?.message || 'Failed to create STT job' })
return
}
const jobId = createData.generation_id
console.log('[Voice] Job ID:', jobId)
console.log('[Voice] Polling for result...')
let resultText = ''
for (let i = 0; i < 30; i++) {
await new Promise(resolve => setTimeout(resolve, 1000))
const queryResponse = await fetch(`https://api.minimax.chat/v1/stt/${jobId}`, {
headers: {
'Authorization': `Bearer ${apiKey}`,
},
})
const queryData: AIMLAPISTTQueryResponse = await queryResponse.json()
console.log('[Voice] Query response:', queryData)
if (queryData.status === 'succeeded') {
resultText = queryData.result?.results?.channels?.[0]?.alternatives?.[0]?.transcript || ''
break
} else if (queryData.status === 'failed') {
console.error('[Voice] STT job failed:', queryData.error?.message)
res.status(500).json({ error: queryData.error?.message || 'STT processing failed' })
return
}
}
if (!resultText) {
res.status(500).json({ error: 'STT processing timeout' })
return
}
console.log('[Voice] Final result:', resultText)
res.json({ text: resultText })
} catch (error) {
console.error('[Voice] STT error:', error)
res.status(500).json({ error: 'Failed to process audio' })
}
})
return router
}