Files
goclaw/internal/audio/auto_apply_helper.go
viettranx 284c9cb2cc feat(tts): per-tenant TTS config and per-agent voice override
- Add per-tenant TTS configuration endpoints (GET/POST /v1/tts/config)
  with RoleAdmin auth instead of master scope
- Implement TenantTTSResolver for channels to use tenant-specific
  TTS providers and auto mode settings
- Add per-agent voice override in channel TTS auto-apply:
  - Extend OutboundMessage with AgentID and AgentOtherConfig
  - Inject AgentAudioSnapshot in dispatch.go from outbound message
  - MaybeApply reads tts_voice_id/tts_model_id from agent context
- Fix events.go to use RunContext.TenantID directly (H3)
- Fix dispatch.go error notification to use sendCtx (H1)
- Update UI to use new /v1/tts/config endpoints
2026-04-16 21:26:45 +07:00

63 lines
1.9 KiB
Go

package audio
import (
"context"
"os"
"path/filepath"
"time"
)
// AutoApplyResult holds the result of TTS auto-apply processing.
type AutoApplyResult struct {
// Text is the message content with [[tts]] directives stripped.
Text string
// AudioPath is the path to generated audio file (empty if no TTS applied).
AudioPath string
// AudioMime is the MIME type of generated audio (e.g. "audio/ogg").
AudioMime string
}
// AutoApplyToText checks if TTS should be auto-applied to the message content.
// Returns modified text (directives stripped) and audio path if TTS was applied.
// channel: "telegram", "discord", etc. - used for format selection (opus for telegram).
// isVoiceInbound: true if user sent voice message (for "inbound" auto mode).
// workspace: directory to save generated audio files.
func (m *Manager) AutoApplyToText(
ctx context.Context,
content string,
channel string,
isVoiceInbound bool,
workspace string,
) (*AutoApplyResult, error) {
if m == nil || content == "" {
return &AutoApplyResult{Text: content}, nil
}
// Check if TTS should be applied (respects auto mode: off/always/inbound/tagged)
result, ok := m.MaybeApply(ctx, content, channel, isVoiceInbound, "final")
if !ok || result == nil {
return &AutoApplyResult{Text: StripTTSDirectives(content)}, nil
}
// Write audio to workspace/tts/ directory
ttsDir := workspace
if ttsDir == "" {
ttsDir = os.TempDir()
}
ttsDir = filepath.Join(ttsDir, "tts")
if err := os.MkdirAll(ttsDir, 0755); err != nil {
return &AutoApplyResult{Text: StripTTSDirectives(content)}, err
}
audioPath := filepath.Join(ttsDir, "auto-"+time.Now().Format("20060102-150405")+"."+result.Extension)
if err := os.WriteFile(audioPath, result.Audio, 0644); err != nil {
return &AutoApplyResult{Text: StripTTSDirectives(content)}, err
}
return &AutoApplyResult{
Text: StripTTSDirectives(content),
AudioPath: audioPath,
AudioMime: result.MimeType,
}, nil
}