Files
ai-agent-book/chapter6/controllable-tts/test_fish_s1.py
T
liqiang b119135836
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
2026-08-20 13:12:50 +00:00

92 lines
3.2 KiB
Python

import json
import subprocess
import pytest
from evaluate_audio_quality import DIMENSIONS, OUTPUTS, PERMUTATIONS, aggregate, validate_response
from markup import parse
from tts import concat_mp3, make_silence
from voice_library import EMOTIONS, SPEEDS, STYLES, load_voice_library
def test_native_s1_nonverbal_events_not_onomatopoeia():
segments = parse("[THINKING]好吧,[SIGH][LAUGH:small][BREATH]继续。")
texts = [s["text"] for s in segments if s["type"] == "speech"]
assert "(uncertain)嗯……" in texts
assert "(sighing)" in texts
assert "(chuckling)" in texts
assert "(gasping)" in texts
assert "哈哈," not in texts and "唉——" not in texts
@pytest.mark.parametrize(
"directory_name",
["speaker clips", "speaker's clips", "d'angelo's clips"],
)
def test_concat_handles_apostrophe_in_output_directory(tmp_path, directory_name):
"""FFconcat must preserve ordinary, single-quote, and multi-quote paths."""
output_dir = tmp_path / directory_name
output_dir.mkdir()
parts = [output_dir / "first.mp3", output_dir / "second.mp3"]
for part in parts:
make_silence(100, part)
output = output_dir / "joined.mp3"
concat_mp3(parts, output)
duration = float(subprocess.check_output([
"ffprobe", "-v", "error", "-show_entries", "format=duration",
"-of", "default=noprint_wrappers=1:nokey=1", str(output),
], text=True).strip())
assert output.is_file()
assert duration >= 0.18
def test_library_requires_exact_cartesian_product(tmp_path):
manifest = tmp_path / "manifest.json"
manifest.write_text(json.dumps({"profiles": {}}))
with pytest.raises(ValueError, match="24"):
load_voice_library(manifest)
def test_dimensions_are_4_by_3_by_2():
assert len(EMOTIONS) * len(SPEEDS) * len(STYLES) == 24
def _judge_response(scores):
return {
"clips": {
alias: {
dimension: {"score": scores[alias], "reason": f"audible evidence for {alias}"}
for dimension in DIMENSIONS
}
for alias in ("X", "Y", "Z")
},
"ranking": sorted(("X", "Y", "Z"), key=scores.get, reverse=True),
"ranking_reason": "audible comparison",
}
def test_quality_response_rejects_bare_scores_without_audible_evidence():
response = _judge_response({"X": 1, "Y": 2, "Z": 3})
response["clips"]["X"]["naturalness"]["reason"] = ""
with pytest.raises(ValueError, match="audible evidence"):
validate_response(response)
def test_position_balanced_aggregate_maps_aliases_back_to_configurations():
# Each pass gives C=5, B=4, A=2 regardless of its anonymous position.
passes = []
score_by_name = {
"A_no_control_markers": 2,
"B_single_reference": 4,
"C_24_reference_library": 5,
}
for permutation in PERMUTATIONS:
mapping = dict(zip(("X", "Y", "Z"), permutation))
scores = {alias: score_by_name[name] for alias, name in mapping.items()}
passes.append({"alias_to_configuration": mapping, "response": _judge_response(scores)})
result = aggregate(passes)
assert result["aggregate_ranking"] == list(reversed(list(OUTPUTS)))
assert result["manuscript_quality_claim_reproduced"] is True