From 8fc4e5dea9fa1d26ad4f13409e15e13616770336 Mon Sep 17 00:00:00 2001 From: viettranx Date: Tue, 14 Apr 2026 22:31:58 +0700 Subject: [PATCH] refactor(tools): delegate ElevenLabs SFX to audio.SFXProvider Rewrite create_audio_elevenlabs.go as a thin shim calling elevenlabs.NewSFXProvider(...).GenerateSFX(ctx, audio.SFXOptions{...}). Preserves 30s duration cap, 60s timeout, and byte-identical request body. Phase 3 removes the shim and wires audio.Manager.GenerateSFX directly. --- internal/tools/create_audio_elevenlabs.go | 78 +++++------------------ 1 file changed, 16 insertions(+), 62 deletions(-) diff --git a/internal/tools/create_audio_elevenlabs.go b/internal/tools/create_audio_elevenlabs.go index 7b2a0537..e3a6fe45 100644 --- a/internal/tools/create_audio_elevenlabs.go +++ b/internal/tools/create_audio_elevenlabs.go @@ -1,72 +1,26 @@ package tools import ( - "bytes" "context" - "encoding/json" - "fmt" - "io" - "net/http" - "strings" - "time" + + "github.com/nextlevelbuilder/goclaw/internal/audio" + "github.com/nextlevelbuilder/goclaw/internal/audio/elevenlabs" ) -// callElevenLabsSoundEffect generates a sound effect via the ElevenLabs sound generation API. -// Endpoint: POST {baseURL}/v1/sound-generation -// Auth: xi-api-key header (NOT Bearer token). -// Response: binary audio data directly (not JSON). +// callElevenLabsSoundEffect delegates to audio/elevenlabs.SFXProvider. +// Phase 1 keeps this thin shim so create_audio.go callers don't change; +// Phase 3 removes the shim and wires audio.Manager.GenerateSFX directly. func (t *CreateAudioTool) callElevenLabsSoundEffect(ctx context.Context, prompt string, durationSeconds int) ([]byte, error) { - baseURL := t.elevenlabsBaseURL - if baseURL == "" { - baseURL = "https://api.elevenlabs.io" - } - - // Cap duration at ElevenLabs max of 30 seconds. - if durationSeconds > 30 { - durationSeconds = 30 - } - - body := map[string]any{ - "text": prompt, - "output_format": "mp3_44100_128", - "prompt_influence": 0.3, - } - if durationSeconds > 0 { - body["duration_seconds"] = durationSeconds - } - - jsonBody, err := json.Marshal(body) + provider := elevenlabs.NewSFXProvider(elevenlabs.Config{ + APIKey: t.elevenlabsAPIKey, + BaseURL: t.elevenlabsBaseURL, + }) + result, err := provider.GenerateSFX(ctx, audio.SFXOptions{ + Prompt: prompt, + Duration: durationSeconds, + }) if err != nil { - return nil, fmt.Errorf("marshal request: %w", err) + return nil, err } - - url := strings.TrimRight(baseURL, "/") + "/v1/sound-generation" - req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(jsonBody)) - if err != nil { - return nil, fmt.Errorf("create request: %w", err) - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("xi-api-key", t.elevenlabsAPIKey) - - client := &http.Client{Timeout: 60 * time.Second} - resp, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("http request: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - errBody, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("ElevenLabs API error %d: %s", resp.StatusCode, truncateBytes(errBody, 500)) - } - - audioBytes, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("read audio response: %w", err) - } - if len(audioBytes) == 0 { - return nil, fmt.Errorf("empty audio response from ElevenLabs") - } - - return audioBytes, nil + return result.Audio, nil }