mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-12 16:15:24 +00:00
The dashboard /tts page writes to system_configs[tts.<provider>.voice], but the LLM-invoked tts tool was only checking args > agent OtherConfig > builtin_tool_tenant_configs[tts].default_voice_id. Two different storage locations → the user's chosen voice was ignored, Edge defaulted to en-US-AriaNeural even when "HoaiMy" (vi-VN-HoaiMyNeural) was set correctly in the dashboard. Add system_configs as a 4th-level fallback so the dashboard becomes the single source of truth. - TtsTool gains SetSystemConfigStore(s) setter - resolveVoiceAndModel takes providerName + looks up tts.<provider>.voice/model when no higher-precedence source set - effectiveProvider resolved BEFORE voice/model so the right key is hit - Wired at gateway boot in cmd/gateway.go (after pgStores ready) - 4 unit tests covering: fallback, arg precedence, no-store, empty provider Verified against production trace 019e6036-44bb-703b-85fa-dee34f7ab2c0 where the tts tool was called with provider=edge, no voice arg, and defaulted to English instead of the configured Vietnamese voice.
77 lines
2.2 KiB
Go
77 lines
2.2 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
)
|
|
|
|
type fakeSystemConfigStore struct {
|
|
data map[string]string
|
|
}
|
|
|
|
func (f *fakeSystemConfigStore) Get(_ context.Context, key string) (string, error) {
|
|
return f.data[key], nil
|
|
}
|
|
func (f *fakeSystemConfigStore) Set(_ context.Context, key, value string) error {
|
|
if f.data == nil {
|
|
f.data = map[string]string{}
|
|
}
|
|
f.data[key] = value
|
|
return nil
|
|
}
|
|
func (f *fakeSystemConfigStore) Delete(_ context.Context, key string) error {
|
|
delete(f.data, key)
|
|
return nil
|
|
}
|
|
func (f *fakeSystemConfigStore) List(_ context.Context) (map[string]string, error) {
|
|
return f.data, nil
|
|
}
|
|
|
|
func TestResolveVoiceAndModel_SystemConfigsFallback(t *testing.T) {
|
|
tool := NewTtsTool(nil)
|
|
sc := &fakeSystemConfigStore{data: map[string]string{
|
|
"tts.edge.voice": "vi-VN-HoaiMyNeural",
|
|
"tts.edge.model": "edge-tts-1",
|
|
}}
|
|
tool.SetSystemConfigStore(sc)
|
|
|
|
v, m, _ := tool.resolveVoiceAndModel(context.Background(), "edge", "", "")
|
|
if v != "vi-VN-HoaiMyNeural" {
|
|
t.Errorf("voice fallback failed: got %q, want vi-VN-HoaiMyNeural", v)
|
|
}
|
|
if m != "edge-tts-1" {
|
|
t.Errorf("model fallback failed: got %q, want edge-tts-1", m)
|
|
}
|
|
}
|
|
|
|
func TestResolveVoiceAndModel_ArgWinsOverSystemConfigs(t *testing.T) {
|
|
tool := NewTtsTool(nil)
|
|
tool.SetSystemConfigStore(&fakeSystemConfigStore{data: map[string]string{
|
|
"tts.edge.voice": "vi-VN-HoaiMyNeural",
|
|
}})
|
|
|
|
v, _, _ := tool.resolveVoiceAndModel(context.Background(), "edge", "en-US-AriaNeural", "")
|
|
if v != "en-US-AriaNeural" {
|
|
t.Errorf("arg voice must win over system_configs: got %q", v)
|
|
}
|
|
}
|
|
|
|
func TestResolveVoiceAndModel_NoStoreNoFallback(t *testing.T) {
|
|
tool := NewTtsTool(nil)
|
|
v, m, _ := tool.resolveVoiceAndModel(context.Background(), "edge", "", "")
|
|
if v != "" || m != "" {
|
|
t.Errorf("expected empty fallback when no system_configs wired, got voice=%q model=%q", v, m)
|
|
}
|
|
}
|
|
|
|
func TestResolveVoiceAndModel_EmptyProviderSkipsFallback(t *testing.T) {
|
|
tool := NewTtsTool(nil)
|
|
tool.SetSystemConfigStore(&fakeSystemConfigStore{data: map[string]string{
|
|
"tts..voice": "should-not-match",
|
|
}})
|
|
v, m, _ := tool.resolveVoiceAndModel(context.Background(), "", "", "")
|
|
if v != "" || m != "" {
|
|
t.Errorf("empty provider must skip lookup, got voice=%q model=%q", v, m)
|
|
}
|
|
}
|