Skip to content

Narrator Agent

Filelib/agents/narratorAgent.ts
Triggered byapp/api/generate-audio/route.ts
TTS enginegemini-2.5-flash-preview-tts (Vertex AI, via lib/gcp/tts.ts)
StorageGoogle Cloud Storage (via lib/gcp/storage.ts)

What it does

Converts story page text to speech and uploads the audio to Google Cloud Storage. Also generates short spoken quiz feedback as inline base64 audio. Called once per page in parallel after the story is written.

This agent does NOT use ADK (LlmAgent / FunctionTool). It is a plain async function that calls synthesizeSpeech() and uploadAndSign() directly — no LLM routing layer. This reduces audio generation time from ~28s to ~12–15s per page.


Two modes

Mode 1 — Page narration (runNarratorAgent)

Called once per story page during generation.

  • Input: text, storyId, pageNumber, voice, language
  • Output: audioUrl — GCS signed URL
  • Storage path: audio/{storyId}/page-{pageNumber}.mp3
  • Played when: Child navigates to that page in the story viewer

Mode 2 — Feedback audio (runNarratorFeedback)

Called after a child answers a quiz question (fallback path only).

  • Input: text, language
  • Output: audioData — base64 WAV data URI (not stored in GCS)
  • Played when: Feedback is shown after quiz answer

Retry logic (page narration only)

runNarratorAgent retries TTS up to 4 attempts with exponential backoff capped at 30s:

AttemptMax delay before retry
13s
26s
312s
4 (final)throws error

Retries on:

  • fetch failed (network error)
  • Timeout
  • HTTP 429 (rate limit)
  • UND_ERR_HEADERS_TIMEOUT

runNarratorFeedback has no retry — a single synthesizeSpeech() call; failure returns { audioData: null }.


Voice

Default voice: Aoede — warm, child-friendly narration.

Configurable per request via the voice parameter. Voice selection affects tone and gender of narration.


Supported languages

All 21 languages supported by StoryWriter are also supported for narration. The TTS engine resolves the language code to a full language name (e.g. pt_pt"Portuguese (Portugal)") and passes it to the Gemini TTS model in the prompt.


Input / Output

typescript
// Page narration input
{
  text: string;         // Story page text
  storyId: string;      // Firestore story ID
  pageNumber: number;   // 1-based
  voice?: string;       // default: "Aoede"
  language?: string;    // default: "en"
}

// Page narration output
{
  audioUrl: string | null;  // GCS signed URL, or null if failed
  pageNumber: number;
  error?: string;
}

// Feedback audio input
{
  text: string;
  language?: string;  // default: "en"
}

// Feedback audio output
{
  audioData: string | null;  // "data:audio/wav;base64,..."
  error?: string;
}

Released under the MIT License.