mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-17 12:16:04 +00:00
Baseline groundwork for TTS expansion plan. Introduces a capabilities system that the follow-up plan (phase-01..04) builds on. Backend: - `audio.ParamSchema` / `ProviderCapabilities` types + per-provider capabilities.go for edge, elevenlabs, minimax, openai, gemini. - Gemini TTS provider (client, models, voices, wav encoder, multi-speaker via SpeakerVoice, audio-tag aware prompts). - TTSOptions gains `Params map[string]any` (read-only) + `Speakers`. - `VoiceListProvider` interface decouples HTTP voice handler from provider-specific impls. - `nested_keys.go` resolves dot-separated param paths for nested provider bodies (voice_settings.stability etc.). - Characterization + defaults-invariant tests per provider lock nil-params byte-equivalence before new params land. - `/v1/tts/capabilities` HTTP endpoint + integration coverage. - Dual-read tests (PG + SQLite) for tts_config. Frontend (web + desktop): - `DynamicParamForm` with depends-on evaluation, split into fields/logic modules. Slider primitive added. - `AudioTagPicker` + `MultiSpeakerEditor` for Gemini. - `voice-picker` refactored toward portal UX; combobox tightened. - tts-capabilities API client + typed hooks. - i18n catalogs (en/vi/zh) expanded; parity tests guard key drift. - TTS page reorganised (voice-model-section removed; playground + credentials + provider-setup split cleanly). Docs: codebase-summary, project-changelog, tts-provider-capabilities.
54 lines
1.6 KiB
Go
54 lines
1.6 KiB
Go
package minimax_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/nextlevelbuilder/goclaw/internal/audio"
|
|
"github.com/nextlevelbuilder/goclaw/internal/audio/minimax"
|
|
)
|
|
|
|
// TestDefaults_PreserveLegacyBody verifies that populating opts.Params with all
|
|
// Capabilities defaults produces a body byte-equivalent to the nil-Params
|
|
// characterization fixture. Guards against silent default flip on save.
|
|
func TestDefaults_PreserveLegacyBody(t *testing.T) {
|
|
cfg := minimax.Config{APIKey: "test-key", GroupID: "grp1"}
|
|
p := minimax.NewProvider(cfg)
|
|
caps := p.Capabilities()
|
|
|
|
if len(caps.Params) == 0 {
|
|
t.Skip("Capabilities.Params not yet populated (Phase C enrichment pending)")
|
|
}
|
|
|
|
params := make(map[string]any)
|
|
for _, s := range caps.Params {
|
|
if s.Default != nil {
|
|
audio.SetNested(params, s.Key, s.Default)
|
|
}
|
|
}
|
|
|
|
bodyWithDefaults, urlWithDefaults := captureMiniMaxBody(t, cfg, audio.TTSOptions{Params: params})
|
|
bodyNilParams, urlNilParams := captureMiniMaxBody(t, cfg, audio.TTSOptions{})
|
|
|
|
wantJSON := canonicalMiniMaxJSON(t, bodyNilParams)
|
|
gotJSON := canonicalMiniMaxJSON(t, bodyWithDefaults)
|
|
if gotJSON != wantJSON {
|
|
t.Errorf("defaults-invariant body FAILED:\n with-defaults: %s\n nil-params: %s",
|
|
gotJSON, wantJSON)
|
|
}
|
|
|
|
if urlWithDefaults != urlNilParams {
|
|
t.Errorf("defaults-invariant URL FAILED:\n with-defaults: %s\n nil-params: %s",
|
|
urlWithDefaults, urlNilParams)
|
|
}
|
|
}
|
|
|
|
func canonicalMiniMaxJSON(t *testing.T, m map[string]any) string {
|
|
t.Helper()
|
|
b, err := json.Marshal(m)
|
|
if err != nil {
|
|
t.Fatalf("canonicalMiniMaxJSON marshal: %v", err)
|
|
}
|
|
return string(b)
|
|
}
|