From 818c8ea54d67ac1b5992e34c18bb97bd1227b3c8 Mon Sep 17 00:00:00 2001 From: Huaaudio Date: Sat, 21 Mar 2026 08:05:54 +0100 Subject: [PATCH] refactor for #1648 --- pkg/voice/agent.go | 1 + pkg/voice/tts.go | 108 --------------------------------------------- 2 files changed, 1 insertion(+), 108 deletions(-) delete mode 100644 pkg/voice/tts.go diff --git a/pkg/voice/agent.go b/pkg/voice/agent.go index 8cd01c338..f3bfbea9c 100644 --- a/pkg/voice/agent.go +++ b/pkg/voice/agent.go @@ -65,6 +65,7 @@ func (a *speechAccumulator) Close() { type Agent struct { bus *bus.MessageBus transcriber asr.Transcriber + transcriber asr.Transcriber mu sync.Mutex sessions map[string]*speechAccumulator // keyed by sessionID_speakerID diff --git a/pkg/voice/tts.go b/pkg/voice/tts.go deleted file mode 100644 index 8de0bbc9c..000000000 --- a/pkg/voice/tts.go +++ /dev/null @@ -1,108 +0,0 @@ -package voice - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "strings" - "time" - - "github.com/sipeed/picoclaw/pkg/config" - "github.com/sipeed/picoclaw/pkg/logger" -) - -type TTSProvider interface { - Name() string - Synthesize(ctx context.Context, text string) (io.ReadCloser, error) -} - -type OpenAITTSProvider struct { - apiKey string - apiBase string - voice string - model string - httpClient *http.Client -} - -func NewOpenAITTSProvider(apiKey string, apiBase string, proxyURL string) *OpenAITTSProvider { - if apiBase == "" || apiBase == "https://api.openai.com/v1" { - apiBase = "https://api.openai.com/v1/audio/speech" - } else if !strings.HasSuffix(apiBase, "/audio/speech") { - // Just in case they provide openrouter base or standard base - apiBase = strings.TrimSuffix(apiBase, "/") + "/audio/speech" - } - - client := &http.Client{ - Timeout: 60 * time.Second, - } - - if proxyURL != "" { - if pURL, err := url.Parse(proxyURL); err == nil { - client.Transport = &http.Transport{ - Proxy: http.ProxyURL(pURL), - } - } - } - - return &OpenAITTSProvider{ - apiKey: apiKey, - apiBase: apiBase, - voice: "alloy", - model: "tts-1", - httpClient: client, - } -} - -func (t *OpenAITTSProvider) Name() string { - return "openai-tts" -} - -func (t *OpenAITTSProvider) Synthesize(ctx context.Context, text string) (io.ReadCloser, error) { - logger.InfoCF("voice-tts", "Starting TTS synthesis", map[string]any{"text_len": len(text)}) - - reqBody := map[string]any{ - "model": t.model, - "input": text, - "voice": t.voice, - "response_format": "opus", - } - - jsonData, err := json.Marshal(reqBody) - if err != nil { - return nil, fmt.Errorf("failed to marshal request: %w", err) - } - - req, err := http.NewRequestWithContext(ctx, "POST", t.apiBase, bytes.NewReader(jsonData)) - if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) - } - - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+t.apiKey) - - resp, err := t.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("failed to send request: %w", err) - } - - if resp.StatusCode != http.StatusOK { - defer resp.Body.Close() - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body)) - } - - return resp.Body, nil -} - -func DetectTTS(cfg *config.Config) TTSProvider { - for _, mc := range cfg.ModelList { - if strings.Contains(strings.ToLower(mc.ModelName), "tts") && mc.APIKey != "" { - return NewOpenAITTSProvider(mc.APIKey, mc.APIBase, mc.Proxy) - } - } - return nil -}