The retried podcast failed with "Failed to parse ValidatedTranscript ... 2
validation errors ... missing": qwen3.5:27b had emitted "sender" instead of
"speaker" in 2 of 14 dialogue entries. Pydantic rejects the object, langchain
raises OUTPUT_PARSING_FAILURE and the entire job dies — after the outline and most
of the transcript were already done.
The template showed the schema as a JSON example but never forbade other key names,
so the model was free to drift. transcript.jinja now states the constraint
explicitly, right where the schema is defined.
This does not make the model deterministic — a parse failure still occurred once on
the next run, but podcast_creator's own retry absorbed it and the episode completed:
15:16 min of audio, 53 clips. Before the change the same failure was fatal.
Also, since this is the run that proved the non-root switch end to end: the final
MP3 is owned by dschlueter, and prune_podcast_data.py cleaned up the leftovers of
the two dead runs (5.5 MB) with a plain host-side rmtree.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two root causes, both fixed in the bind-mounted podcast prompt templates
(prompts/podcast/*.jinja):
1. Language: the episode profile's `language` field IS passed to both
podcast_creator templates as {{ language }}, but the stock templates never
reference it — so podcasts came out English (driven only by the English
stock briefings/speakers), then read aloud by the German-locked Chatterbox
TTS = "English with a German accent". Added a CRITICAL LANGUAGE REQUIREMENT
block keyed on {{ language }} to both templates; now `language: "de"`
actually forces German. Verified end-to-end: a full run produced a 44-line
all-German transcript + audio.
2. Invalid json output failures: qwen3.5:27b is a thinking model; on long
segments the <think> block ate the response-token budget and truncated the
JSON. Prepended /no_think to both templates (~3x faster, valid JSON).
Templates are bind-mounted read-only via docker-compose (directory mount, so
edits survive a container restart without inode-staleness). Bundled briefings
and speaker backstories were also translated to German to reduce drift.
Known limitation documented: feeding an entire book (~70k tokens) as podcast
content makes each of the 6 LLM calls take ~3.5 min and is unreliable; use a
shorter source or summary. Confirmed working with concise content.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>