mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-03 06:12:14 +08:00
fix: add error handling for podcast generation failures (#1257)
* fix: add error handling for podcast generation failures When TTS processing fails, the system was generating 0-second audio files without any error indication. This fix adds: 1. Track failed TTS lines and log warning with indices 2. Raise ValueError when all TTS generation fails with helpful message 3. Check for empty audio output in mix_audio and raise error 4. Log success/failure ratio for debugging Fixes #30 * fix: address Copilot review feedback - Use `not audio` to catch both None and empty bytes - Log failed lines with 1-based indices for user-friendly output - Handle empty script case with clear error message - Validate env vars before ThreadPoolExecutor for fast-fail on config errors --------- Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
@@ -123,15 +123,37 @@ def tts_node(script: Script, max_workers: int = 4) -> list[bytes]:
|
||||
logger.info(f"Converting script to audio using {max_workers} workers...")
|
||||
|
||||
total = len(script.lines)
|
||||
|
||||
# Handle empty script case
|
||||
if total == 0:
|
||||
raise ValueError("Script contains no lines to process")
|
||||
|
||||
# Validate required environment variables before starting TTS
|
||||
if not os.getenv("VOLCENGINE_TTS_APPID") or not os.getenv("VOLCENGINE_TTS_ACCESS_TOKEN"):
|
||||
raise ValueError(
|
||||
"Missing required environment variables: VOLCENGINE_TTS_APPID and VOLCENGINE_TTS_ACCESS_TOKEN must be set"
|
||||
)
|
||||
|
||||
tasks = [(i, line, total) for i, line in enumerate(script.lines)]
|
||||
|
||||
# Use ThreadPoolExecutor for parallel TTS generation
|
||||
results: dict[int, Optional[bytes]] = {}
|
||||
failed_indices: list[int] = []
|
||||
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
||||
futures = {executor.submit(_process_line, task): task[0] for task in tasks}
|
||||
for future in as_completed(futures):
|
||||
idx, audio = future.result()
|
||||
results[idx] = audio
|
||||
# Use `not audio` to catch both None and empty bytes
|
||||
if not audio:
|
||||
failed_indices.append(idx)
|
||||
|
||||
# Log failed lines with 1-based indices for user-friendly output
|
||||
if failed_indices:
|
||||
logger.warning(
|
||||
f"Failed to generate audio for {len(failed_indices)}/{total} lines: "
|
||||
f"line numbers {sorted(i + 1 for i in failed_indices)}"
|
||||
)
|
||||
|
||||
# Collect results in order, skipping failed ones
|
||||
audio_chunks = []
|
||||
@@ -140,15 +162,30 @@ def tts_node(script: Script, max_workers: int = 4) -> list[bytes]:
|
||||
if audio:
|
||||
audio_chunks.append(audio)
|
||||
|
||||
logger.info(f"Generated {len(audio_chunks)} audio chunks")
|
||||
logger.info(f"Generated {len(audio_chunks)}/{total} audio chunks successfully")
|
||||
|
||||
if not audio_chunks:
|
||||
raise ValueError(
|
||||
f"TTS generation failed for all {total} lines. "
|
||||
"Please check VOLCENGINE_TTS_APPID and VOLCENGINE_TTS_ACCESS_TOKEN environment variables."
|
||||
)
|
||||
|
||||
return audio_chunks
|
||||
|
||||
|
||||
def mix_audio(audio_chunks: list[bytes]) -> bytes:
|
||||
"""Combine audio chunks into a single audio file."""
|
||||
logger.info("Mixing audio chunks...")
|
||||
|
||||
if not audio_chunks:
|
||||
raise ValueError("No audio chunks to mix - TTS generation may have failed")
|
||||
|
||||
output = b"".join(audio_chunks)
|
||||
logger.info("Audio mixing complete")
|
||||
|
||||
if len(output) == 0:
|
||||
raise ValueError("Mixed audio is empty - TTS generation may have failed")
|
||||
|
||||
logger.info(f"Audio mixing complete: {len(output)} bytes")
|
||||
return output
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user