feat(voice): 阿里百炼 STT/TTS 主链路接通(base64 STT + cosyvoice TTS按voice_id + REST /audio/* + voice_input.deviceId)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- voice/dashscope.go 新建:DashscopeSTT(qwen-audio-3.0-asr-flash, base64 data URI→text) + DashscopeTTS(cosyvoice 按 voice_id, 取非流式 24h URL 下载字节)
- cloud.go newDashboardSTT/TTS 由空 endpoint stub 改为真实百炼 adapter
- chain.go gating 从 DashscopeAPIKey!="" 改为 voiceConfigured(cfg); silent 兜底保留
- config.VoiceConfig 扩展 DASHSCOPE_BASE_URL/VOICE_STT_*/VOICE_TTS_* 字段(必填 DASHSCOPE_API_KEY)
- gateway: POST /api/v1/audio/{transcribe,speech} (owner+审计+MaxBytesReader); voiceInputRequest 增 deviceId 并透传 runner.Run
- 实测: cosyvoice-v3.5-plus+longxiaochun 回 418(引擎拒绝), 改用 cosyvoice-v3-flash+longxiaochun_v3; TTS 你好我是昔涟->106124B wav -> STT 回 你好我是西联, 链返回真实字节/文本
- 单测: dashscope_test/config voice defaults/gateway audio_test; go build/vet/test 全绿
- 声音复刻(响昔涟音色)待公网参考音频; 不做 create_voice
This commit is contained in:
Aska committed 2026-09-08 06:06:02 +08:00
1 parent d8044763ac
commit 73dabb9dde
13 files changed
+1070 -15

No files matched your search

+6 -1
View File
@@ -26,12 +26,14 @@ type VoiceService interface {
// voiceInputRequest is the decoded payload of a voice_input frame. Audio is a
// base64 (or data: URI) audio blob; Format is its encoding ("wav"/"mp3"/"ogg");
// Text overrides STT when present (a client that already transcribed can skip
// recognition); SessionID defaults to "web".
// recognition); SessionID defaults to "web"; DeviceID is the client-produced
// end identifier (design/16 §4.5) threaded into the persisted voice turn.
type voiceInputRequest struct {
Audio string `json:"audio"`
Format string `json:"format"`
Text string `json:"text"`
SessionID string `json:"sessionId"`
DeviceID string `json:"deviceId,omitempty"`
}
// SetVoice injects the voice STT/TTS service used by the voice_input WS route.
@@ -66,6 +68,9 @@ func (c *wsConn) handleVoiceInput(in wsInbound) {
// carrying the base64 audio plus the recognized transcript and assistant reply.
func (h *hub) runVoiceTurn(c *wsConn, req voiceInputRequest) {
ctx := context.Background()
// Thread the client's deviceID so the persisted voice turn is tagged with the
// sending end (design/16 §4.5), mirroring ws_hub.go:240.
ctx = withDeviceID(ctx, req.DeviceID)
if req.SessionID == "" {
req.SessionID = "web"
}