mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-12 20:15:42 +00:00
Phase 1 of the TTS params/layout/agent-override plan.
Capability schema:
- `ParamSchema.Group` field (`"basic"` default, `"advanced"` when set).
- Tag existing advanced params across openai/elevenlabs/minimax/gemini.
Gemini:
- Expose `temperature` (basic, 0.0–2.0, default 1.0, subtle-effect note),
`seed`, `presencePenalty`, `frequencyPenalty` (advanced, experimental).
- Merge into `generationConfig` via explicit-presence resolvers so
nil-params bodies stay byte-equivalent.
ElevenLabs:
- `output_format` enum (27 variants, default `mp3_44100_128`, advanced).
- `FormatMeta` lookup drives SynthResult MIME + extension.
- URL built via `net/url.Values.Encode()`; regex pre-validation for
`output_format` (`^[a-z0-9_]+$`) and `language_code`
(`^[a-z]{2,3}(-[A-Z]{2})?$`) blocks query-string injection.
- Telegram opus contract preserved: `opts.Format=="opus"` forces
`audio/ogg; codecs=opus` regardless of user-set `output_format`.
MiniMax:
- `language_boost` (basic enum), `subtitle_enable` (basic bool),
`pronunciation_dict` (advanced text, 8KB cap, wrapped as
`{"tone":[...]}`). Parse failure logs length only + omits.
Validation:
- `audio.ValidateParams` enforces Min/Max/Enum + rejects unknown keys.
- Wired into `/v1/tts/synthesize` and `/v1/tts/config` write paths.
- `loadParamsBlob` capped at 16KB.
- i18n keys `MsgTtsParamOutOfRange`, `MsgTtsParamInvalidJSON`,
`MsgTtsParamUnknownKey` added to en/vi/zh catalogs.
Tests:
- Golden `testdata/default_body.golden.json` per provider (gemini,
elevenlabs, minimax, openai) checked in; invariant tests diff
against file instead of self-referential capture.
- Round-trip tests for each new param + Telegram opus contract +
URL-injection attempts.
51 lines
1.3 KiB
Go
51 lines
1.3 KiB
Go
package gemini
|
|
|
|
import "testing"
|
|
|
|
func TestCapabilities_Voices(t *testing.T) {
|
|
p := NewProvider(Config{APIKey: "k"})
|
|
caps := p.Capabilities()
|
|
if len(caps.Voices) != 30 {
|
|
t.Errorf("want 30 voices, got %d", len(caps.Voices))
|
|
}
|
|
}
|
|
|
|
func TestCapabilities_Models(t *testing.T) {
|
|
p := NewProvider(Config{APIKey: "k"})
|
|
caps := p.Capabilities()
|
|
if len(caps.Models) != 3 {
|
|
t.Errorf("want 3 models, got %d", len(caps.Models))
|
|
}
|
|
}
|
|
|
|
func TestCapabilities_CustomFeatures(t *testing.T) {
|
|
p := NewProvider(Config{APIKey: "k"})
|
|
caps := p.Capabilities()
|
|
if caps.CustomFeatures == nil {
|
|
t.Fatal("CustomFeatures is nil")
|
|
}
|
|
if _, ok := caps.CustomFeatures["multi_speaker"]; !ok {
|
|
t.Error("CustomFeatures missing 'multi_speaker'")
|
|
}
|
|
if _, ok := caps.CustomFeatures["audio_tags"]; !ok {
|
|
t.Error("CustomFeatures missing 'audio_tags'")
|
|
}
|
|
}
|
|
|
|
func TestCapabilities_RequiresAPIKey(t *testing.T) {
|
|
p := NewProvider(Config{APIKey: "k"})
|
|
caps := p.Capabilities()
|
|
if !caps.RequiresAPIKey {
|
|
t.Error("want RequiresAPIKey=true")
|
|
}
|
|
}
|
|
|
|
func TestCapabilities_ParamsPopulated(t *testing.T) {
|
|
p := NewProvider(Config{APIKey: "k"})
|
|
caps := p.Capabilities()
|
|
// Phase 1: Gemini now exposes 4 params (temperature + seed + presencePenalty + frequencyPenalty).
|
|
if len(caps.Params) != 4 {
|
|
t.Errorf("want 4 params, got %d", len(caps.Params))
|
|
}
|
|
}
|