package ai import ( "context" "os/exec" "strings" "testing" ) func TestTTSRejectsExcessPendingAudioWithoutRetry(t *testing.T) { if _, err := exec.LookPath("ffmpeg"); err != nil { t.Skip("Bailian audio conversion requires ffmpeg") } task, providers := currentFixture(t, "full_ai") task = changeCurrentAgent(t, task, func(agent map[string]any) { conversation := agent["conversation"].(map[string]any) conversation["sentence_max_chars"] = 2 conversation["max_pending_audio_chunks"] = 1 }) endpoint, requests := mockBailianTTS(t, []byte{1, 0}, nil) p := providers["tts-example"] p.Endpoint = endpoint providers[p.ProviderRef] = p bound, err := Bind(task, providers) if err != nil { t.Fatal(err) } if _, err := bound.SynthesizeReply(context.Background(), "你好世界"); err == nil || !strings.Contains(err.Error(), "pending audio") || strings.Contains(err.Error(), p.Credential) { t.Fatalf("explicit pending-audio bound was ignored or leaked credentials: %v", err) } if requests.Load() != 1 { t.Fatalf("provider must not retry/bill a second time: requests=%d", requests.Load()) } } func TestReplyChunksUnicodeByApprovedSentenceLimit(t *testing.T) { if _, err := exec.LookPath("ffmpeg"); err != nil { t.Skip("Bailian audio conversion requires ffmpeg") } task, providers := currentFixture(t, "full_ai") task = changeCurrentAgent(t, task, func(agent map[string]any) { conversation := agent["conversation"].(map[string]any) conversation["sentence_max_chars"] = 3 conversation["max_pending_audio_chunks"] = 3 }) texts := make(chan string, 3) endpoint, requests := mockBailianTTS(t, []byte{1, 0}, func(text string) { texts <- text }) p := providers["tts-example"] p.Endpoint = endpoint providers[p.ProviderRef] = p bound, err := Bind(task, providers) if err != nil { t.Fatal(err) } audio, err := bound.SynthesizeReply(context.Background(), "你好世界,再见") if err != nil || len(audio) != 6 || requests.Load() != 3 { t.Fatalf("reply must synthesize all bounded chunks: len=%d requests=%d err=%v", len(audio), requests.Load(), err) } for _, want := range []string{"你好世", "界,再", "见"} { if got := <-texts; got != want { t.Fatalf("Unicode reply was lost/reordered while splitting: got=%q want=%q", got, want) } } }