export const dynamic = 'force-dynamic' export const runtime = 'nodejs' import { NextResponse } from 'next/server' import Groq from 'groq-sdk' import { HttpsProxyAgent } from 'https-proxy-agent' import { toFile } from 'groq-sdk/uploads' const STT_MODEL = process.env.GROQ_STT_MODEL || 'whisper-large-v3-turbo' let _client: Groq | null = null function client(): Groq { if (_client) return _client const apiKey = process.env.GROQ_API_KEY if (!apiKey) throw new Error('GROQ_API_KEY not set') const proxy = process.env.GROQ_PROXY || process.env.HTTPS_PROXY || '' const httpAgent = proxy ? new HttpsProxyAgent(proxy) : undefined _client = new Groq({ apiKey, httpAgent }) return _client } // Принимает либо multipart/form-data с полем "file", // либо raw audio в теле (Content-Type: audio/* — например audio/webm). // Возвращает {text: string}. export async function POST(req: Request) { let audio: { name: string; data: Buffer; mime: string } const ct = req.headers.get('content-type') || '' try { if (ct.startsWith('multipart/form-data')) { const fd = await req.formData() const file = fd.get('file') if (!(file instanceof Blob)) { return NextResponse.json({ error: 'file field required' }, { status: 400 }) } const ab = await file.arrayBuffer() audio = { name: (file as any).name || 'audio.webm', data: Buffer.from(ab), mime: file.type || 'audio/webm', } } else { const ab = await req.arrayBuffer() if (!ab.byteLength) { return NextResponse.json({ error: 'empty body' }, { status: 400 }) } audio = { name: 'audio.webm', data: Buffer.from(ab), mime: ct || 'audio/webm', } } } catch (e) { return NextResponse.json({ error: 'failed_to_read_body' }, { status: 400 }) } try { const file = await toFile(audio.data, audio.name, { type: audio.mime }) const result = await client().audio.transcriptions.create({ file, model: STT_MODEL, language: 'ru', }) const text = (result as any).text || '' return NextResponse.json({ text }) } catch (e: any) { console.error('[voice/stt] groq error:', e?.message || e) return NextResponse.json({ error: 'stt_failed', detail: String(e?.message || e) }, { status: 502 }) } }