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

+21 -6
View File
@@ -25,8 +25,16 @@ func NewChain(steps ...Provider) *Chain {
// STT walks the chain for a transcription.
func (c *Chain) STT(ctx context.Context, audio Audio) (string, error) {
text, _, err := c.STTWithProvider(ctx, audio)
return text, err
}
// STTWithProvider is STT but also reports the name of the provider rung that
// produced the transcription (cloud / local / edge-tts / silent), so a caller can
// surface which tier actually served the request.
func (c *Chain) STTWithProvider(ctx context.Context, audio Audio) (string, string, error) {
if len(c.steps) == 0 {
return "", ErrProviderFailed
return "", "", ErrProviderFailed
}
var lastErr error
for _, p := range c.steps {
@@ -36,11 +44,11 @@ func (c *Chain) STT(ctx context.Context, audio Audio) (string, error) {
}
text, err := stt.STT(ctx, audio)
if err == nil {
return text, nil
return text, p.Name(), nil
}
lastErr = err
}
return "", fmt.Errorf("voice: %w: %v", ErrProviderFailed, lastErr)
return "", "", fmt.Errorf("voice: %w: %v", ErrProviderFailed, lastErr)
}
// CloneVoice creates a custom voice via the cloud provider's voice-enrollment
@@ -55,8 +63,15 @@ func (c *Chain) CloneVoice(ctx context.Context, referenceURL, prefix string) (st
// TTS walks the chain for synthesized audio.
func (c *Chain) TTS(ctx context.Context, text string) (Audio, error) {
audio, _, err := c.TTSWithProvider(ctx, text)
return audio, err
}
// TTSWithProvider is TTS but also reports the name of the provider rung that
// produced the audio.
func (c *Chain) TTSWithProvider(ctx context.Context, text string) (Audio, string, error) {
if len(c.steps) == 0 {
return Audio{}, ErrProviderFailed
return Audio{}, "", ErrProviderFailed
}
var lastErr error
for _, p := range c.steps {
@@ -66,11 +81,11 @@ func (c *Chain) TTS(ctx context.Context, text string) (Audio, error) {
}
audio, err := tts.TTS(ctx, text)
if err == nil {
return audio, nil
return audio, p.Name(), nil
}
lastErr = err
}
return Audio{}, fmt.Errorf("voice: %w: %v", ErrProviderFailed, lastErr)
return Audio{}, "", fmt.Errorf("voice: %w: %v", ErrProviderFailed, lastErr)
}
// Option customizes a chain built by New.