Repository navigation
Conversation
Plumbing for the smallest possible A/B test of Azure mstts:express-as on Hebrew at high intensity, gated entirely by an env var. Background ---------- 2026-05-07 listening test on PR #95's candidate render said VIC at I3–I5 "sounds the same in both, just a robot whose pitch is a bit higher" — rate/pitch alone don't carry distress on Azure he-IL voices. PR #95 closed the Whisper WER gap (#87); this issue is the descendant perceptual gap. Plumbing -------- 1. AzureProvider.supports_style_tags: False → True. M14's False default was speculative ("express-as is unavailable for he-IL"), never validated by listening test. All production speakers in configs/speakers/ use style="General", so the flip is a no-op for default renders — non-General styles only emit when (2) fires. 2. renderer._SPIKE_97_ENV_VAR: when SYNTHBANSHEE_SPIKE_97_VIC_STYLE is set AND the current turn is VIC at intensity ≥ 4 AND the provider supports style tags, the renderer overrides style_entry.style with the env-var value before SSML emission. Cache key (SHA-256 of SSML) changes only for affected turns, so non-VIC and I≤3 turns hit cache → baseline A/B costs only the VIC@I4–I5 re-renders. Scope ----- Single knob. No prosody changes, no #87 cap relaxation, no speaker-YAML edits, no SSML-builder changes (it already emitted express-as conditionally on supports_style_tags). When the spike concludes, the next change replaces this with whatever the listening test decided — a permanent VIC YAML edit, a different lever from #97's enumerated list, or a revert of the flag flip if Azure he-IL express-as turns out to be perceptually flat. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
This PR introduces a targeted spike to A/B-test Azure SSML <mstts:express-as> styles for VIC turns at high intensity (I≥4) by (1) enabling style-tag capabilities for Azure and (2) adding an env-var-driven override in the renderer, with a corresponding unit test update. The PR description reports a negative result (Azure he-IL voice ignores express-as), which makes the current capability flip and spike plumbing unsuitable to merge as-is.
Changes:
- Flip Azure provider
supports_style_tagsfromFalsetoTrue. - Add
SYNTHBANSHEE_SPIKE_97_VIC_STYLEenv-var override inTTSRenderer.render_utterance()for VIC at intensity ≥ 4. - Update unit test expectations to match the new Azure capability value.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| synthbanshee/tts/azure_provider.py | Advertises Azure backend style-tag support by toggling supports_style_tags to True. |
| synthbanshee/tts/renderer.py | Adds spike-specific env-var logic to override per-turn style before SSML generation. |
| tests/unit/test_google_provider.py | Updates TestProviderCapabilities.test_azure_capabilities to assert supports_style_tags=True and adds rationale comments. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…e, unit tests Addresses the senior-dev review on PR #98: 1. Per-call supports_style_tags instead of a global flip. - azure_provider.py: revert supports_style_tags to False (M14 default restored). The cache survived the revert because both eras of cache entries (pre-M14 True, post-M14 False) coexist. - renderer.py: when the override fires, force supports_style_tags=True for that single SSML build. The provider's global capability is no longer touched; non-spike turns are byte-identical to main. - test_google_provider.py: revert the matching test assertion. 2. Generic naming for reuse. - SYNTHBANSHEE_SPIKE_97_VIC_STYLE → SYNTHBANSHEE_VIC_STYLE_OVERRIDE. - _SPIKE_97_* constants → _VIC_STYLE_OVERRIDE_*. - The next style spike (terrified, whispering, …) needs no code change. 3. Direct byte-identity proof. - tests/fixtures/issue_97_fearful_ssml.json captures, for VIC@I=4 turn 14 of sp_it_a_0001: the General-style SSML SHA, the fearful-style SSML SHA, the cache files each maps to (both populated by real Azure synthesis), and the audio SHA — proving the cached bytes are byte-identical despite the express-as wrapper difference. - Hebrew utterance text redacted from the SSML body per CLAUDE.md ("Hebrew text goes in .j2 templates or .txt transcripts only"); the SHAs remain authoritative. 4. Unit test for the gate. - tests/unit/test_vic_style_override.py covers the four-condition gate (env-set + role + intensity + provider-default-unchanged) with parameterized coverage over multiple style values. 1698 unit tests pass; ruff + mypy clean. Net effect on PR #98 disposition: still recommend close-without-merge — the override now has cleaner architecture and better evidence, but the finding is the same (Azure he-IL silently ignores express-as). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
pr-agent-context report: This is a refreshed snapshot of the current PR state.
This run includes a patch coverage gap on PR #98 in repository https://github.com/DataHackIL/SynthBanshee
Address the patch coverage gaps below, then push all of these changes in a single commit.
# Patch coverage
Patch test coverage is 62.50%; please raise it to 100%. These are the uncovered code lines:
- synthbanshee/tts/renderer.py: 80, 94, 95, 96, 97, 103Run metadata: |
|
Closing without merge — spike concluded with a negative result. Disposition
Successor work
Cross-references
|
TL;DR — negative result, do not merge
A/B-tested Azure
<mstts:express-as style="fearful">on a VIC@I=4 turn insp_it_a_0001. The two cached audio bytes are byte-identical (f495bf6a…) despite distinct SSML inputs — Azure'she-IL-HilaNeuralvoice silently ignores the express-as wrapper for this voice + prosody combination. M14's defensivesupports_style_tags=Falsedefault was empirically correct.Recommendation: close this PR without merging. Pursue candidate #2 in #97 (disfluency density at high intensity) under the same one-knob discipline.
What changed
synthbanshee/tts/renderer.pySYNTHBANSHEE_VIC_STYLE_OVERRIDEthat, when set AND the turn is VIC at intensity ≥ 4, overridesstyle_entry.styleAND forcessupports_style_tags=Trueper call. The provider's global capability is left at M14'sFalsedefault. Cache key changes only for the affected turn.tests/unit/test_vic_style_override.pytests/fixtures/issue_97_fearful_ssml.jsonOut of scope, intentionally:
supports_style_tagsglobal default unchanged (override forcesTrueper call).Scope-narrowing rationale
Issue says "VIC at I3–I5 does not sound distressed." Override gate is
I ≥ 4. Reasoning: the negative result generalises downward — if Azure ignores the express-as wrapper at I=4, it ignores it at I=3 too. Lowering the gate would burn 4× more Azure calls re-rendering I=3 turns for the same finding.Test plan
pytest tests/unit/— 1698 passed (was 1688; +10 new gate tests).sp_it_a_0001(env unset): all 17 turns hit cache → $0 Azure.SYNTHBANSHEE_VIC_STYLE_OVERRIDE=fearful): only VIC@I=4 turn re-rendered → ~1¢ Azure.Tier-3 ASR sanity (local)
ASR re-run is mechanically a no-op given byte-identity (the audio is the same as the General-style baseline, which has known WER ≈ 0.052 from PR #95). Included only because CLAUDE.md mandates it for any
synthbanshee/tts/touch.Direct byte-identity evidence
tests/fixtures/issue_97_fearful_ssml.jsonrecords, for the same VIC@I=4 turn (same Hebrew text, same prosody, same voice):xmlns:msttsdeclared<mstts:express-as>wrapper9880c52f…18aa1ae1…9880c52f….wav18aa1ae1….wavf495bf6a…f495bf6a…Two distinct SSML strings → two distinct cache keys → two real Azure synthesis events → byte-identical audio output. No listening-test interpretation needed.
What this falsifies — and what it doesn't
style="fearful"onhe-IL-HilaNeuralfor this prosody combination.terrified,whispering,shouting,sad). Strong prior they're also no-ops (same voice model), but unverified. One more spike (~1¢ + listening test) would close the family.Recommendation
DisfluencyProfileplumbing already exists; same one-knob discipline.Reproducer
Refs