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
92 lines
3.2 KiB
Python
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
|