feat(voice): 语音silent降级信号(C2) — provider仅在结果为空时出现
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- voice/Chain+Service: STTWithProvider/TTSWithProvider(真实档位cloud/local/edge-tts/espeak/silent), 旧方法改薄包装
- gateway: transcribe空文本带{text,provider} / voice_output空音频带provider / speech空音频tts_empty+X-Voice-Provider头
- middleware: CORS Access-Control-Expose-Headers: X-Voice-Provider
- 可选接口 voiceProviderReporter(type断言透传), 自定义实现回退silent
- 新增测试10个(provider_signal/voice_signal), build/vet/test全绿+交叉编译
- Docs/api-client v0.23 + api-reference CORS 同步
This commit is contained in:
AskaEth committed 2026-09-08 21:23:12 +08:00
1 parent 00f784b55f
commit 99f222761e
9 files changed
+345 -25

No files matched your search

+8 -3
View File
@@ -61,7 +61,7 @@ func (s *Server) handleTranscribe(w http.ResponseWriter, r *http.Request) {
writeError(w, apperr.Wrap(apperr.ErrBadRequest, "bad_request", "audio is required"))
return
}
text, err := s.voice.STT(r.Context(), audio)
text, rung, err := sttWithProvider(r.Context(), s.voice, audio)
if err != nil {
s.auditClaims(r, "audio.transcribe", "error")
s.log.Warn("voice transcribe", "err", err)
@@ -69,7 +69,11 @@ func (s *Server) handleTranscribe(w http.ResponseWriter, r *http.Request) {
return
}
s.auditClaims(r, "audio.transcribe", "ok")
writeJSON(w, http.StatusOK, map[string]any{"text": text})
out := map[string]any{"text": text}
if sig := providerSignal(rung, strings.TrimSpace(text) == ""); sig != "" {
out["provider"] = sig
}
writeJSON(w, http.StatusOK, out)
}
// handleSpeech synthesizes speech for the given text and writes the raw audio
@@ -96,7 +100,7 @@ func (s *Server) handleSpeech(w http.ResponseWriter, r *http.Request) {
if req.Instruction != "" {
ctx = voice.WithTTSInstruction(ctx, req.Instruction)
}
audi, err := s.voice.TTS(ctx, req.Text)
audi, rung, err := ttsWithProvider(ctx, s.voice, req.Text)
if err != nil {
s.auditClaims(r, "audio.speech", "error")
s.log.Warn("voice speech", "err", err)
@@ -105,6 +109,7 @@ func (s *Server) handleSpeech(w http.ResponseWriter, r *http.Request) {
}
if len(audi.Data) == 0 {
s.auditClaims(r, "audio.speech", "error")
w.Header().Set(voiceProviderHeader, providerSignal(rung, true))
writeError(w, apperr.Wrap(apperr.ErrInternal, "tts_empty", "no audio synthesized"))
return
}