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

+24 -9
View File
@@ -53,9 +53,10 @@ type job struct {
// jobResult carries the outcome written back to the caller.
type jobResult struct {
text string
audio Audio
err error
text string
audio Audio
provider string
err error
}
// NewService builds a Service backed by chain and starts the worker pool.
@@ -99,9 +100,9 @@ func (s *Service) run(j job) {
res := jobResult{}
switch j.kind {
case jobSTT:
res.text, res.err = s.chain.STT(s.bgCtx, j.audio)
res.text, res.provider, res.err = s.chain.STTWithProvider(s.bgCtx, j.audio)
case jobTTS:
res.audio, res.err = s.chain.TTS(s.bgCtx, j.text)
res.audio, res.provider, res.err = s.chain.TTSWithProvider(s.bgCtx, j.text)
}
j.res <- res
}
@@ -109,21 +110,35 @@ func (s *Service) run(j job) {
// STT enqueues a transcription request and waits for the result. ctx cancels
// the wait and the underlying provider call.
func (s *Service) STT(ctx context.Context, audio Audio) (string, error) {
text, _, err := s.STTWithProvider(ctx, audio)
return text, err
}
// STTWithProvider is STT but also reports the provider rung that produced the
// transcription.
func (s *Service) STTWithProvider(ctx context.Context, audio Audio) (string, string, error) {
res, err := s.submit(ctx, job{kind: jobSTT, audio: audio, res: make(chan jobResult, 1)})
if err != nil {
return "", err
return "", "", err
}
return res.text, res.err
return res.text, res.provider, res.err
}
// TTS enqueues a synthesis request and waits for the result. ctx cancels the
// wait and the underlying provider call.
func (s *Service) TTS(ctx context.Context, text string) (Audio, error) {
audio, _, err := s.TTSWithProvider(ctx, text)
return audio, err
}
// TTSWithProvider is TTS but also reports the provider rung that produced the
// audio.
func (s *Service) TTSWithProvider(ctx context.Context, text string) (Audio, string, error) {
res, err := s.submit(ctx, job{kind: jobTTS, text: text, res: make(chan jobResult, 1)})
if err != nil {
return Audio{}, err
return Audio{}, "", err
}
return res.audio, res.err
return res.audio, res.provider, res.err
}
// CloneVoice creates a custom voice, delegating to the underlying chain. It is a