import type { GatewayConfig } from '@shared/types' /** Native transcription adapter. A provider failure is returned to the actual voice UI. */ export async function transcribeAudio(config: GatewayConfig, payload: { audioBase64: string; mimeType: string }): Promise<{ text: string }> { if (!config.apiKey) throw new Error('语音转写需要配置支持转写的模型服务') if (typeof payload?.audioBase64 !== 'string' || payload.audioBase64.length > 32 * 1024 * 1024) throw new Error('录音内容无效或过大') if (!['audio/wav', 'audio/x-wav', 'audio/webm', 'audio/ogg', 'audio/mp4'].includes(payload.mimeType)) throw new Error('录音格式不受支持') const bytes = Buffer.from(payload.audioBase64, 'base64') const extension = payload.mimeType.includes('wav') ? 'wav' : payload.mimeType.split('/')[1] const form = new FormData() form.append('file', new Blob([bytes], { type: payload.mimeType }), `recording.${extension}`) form.append('model', config.speechModel || 'whisper-1') const base = (config.speechBaseUrl || config.baseUrl).replace(/\/+$/, '') const response = await fetch(`${base}/audio/transcriptions`, { method: 'POST', headers: { authorization: `Bearer ${config.apiKey}` }, body: form, signal: AbortSignal.timeout(90000) }) if (!response.ok) throw new Error(`语音转写失败(${response.status});请检查模型服务是否支持语音转写`) const result = await response.json() as { text?: unknown } if (typeof result.text !== 'string') throw new Error('语音服务未返回有效文本') return { text: result.text } }