Skip to content

Illustrator Agent

Filelib/agents/illustratorAgent.ts
Triggered byapp/api/generate-image/route.ts
Image modelgemini-2.5-flash-image (Vertex AI, via lib/gcp/imagen.ts)
StorageGoogle Cloud Storage (via lib/gcp/storage.ts)

What it does

Generates one watercolor-style illustration per story page and uploads it to Google Cloud Storage. Returns a signed GCS URL for the frontend to display.

This agent does NOT use ADK (LlmAgent / FunctionTool). It is a plain async function that calls generateImage() and uploadAndSign() directly — no LLM routing layer. This eliminates ~3–5s of ADK overhead per page.


Decision logic

Input received

    ├── preGeneratedBase64 provided?
    │       YES → decode base64 → uploadAndSign()

    └── NO → generateImage(imagePrompt, referenceImages) → uploadAndSign()

Storage path

images/{storyId}/page-{pageNumber}.png

Uploaded to Google Cloud Storage and returned as a signed URL valid for public access.


Character reference images

When a user uploads a photo (e.g. a photo of their child), it is passed as referenceImages to generateImage(). The prompt instructs Gemini to preserve face shape, hair color, hair style, and skin tone while rendering in watercolor cartoon style.

typescript
referenceImages: [{ name: "Mia", base64: "..." }];

Image style

All prompts are passed to generateImage() with a watercolor children's book illustration style suffix. The imagePrompt from StoryWriter is always in English regardless of the story language.


Rate limiting & retry

gemini-2.5-flash-image has a 2 RPM quota on Vertex AI. Pages are generated sequentially with a 32s start-to-start gap enforced by GenerationProvider.tsx and useStoryAssembler.ts. Running 2 in parallel exhausts the quota immediately and triggers exponential backoff (3s→5s→9s→17s→33s).

Inside generateImage() there are 3 progressive strategies:

StrategyDescription
originalFull prompt + reference images. Up to 5 retries on 429.
scene-with-refsScene-only prompt (characters stripped) + reference images. Up to 3 retries.
scene-onlyScene-only prompt, no reference images. Up to 3 retries.

Each 429 retry uses exponential backoff: 2^attempt × 2000ms + random jitter. If a strategy gets IMAGE_PROHIBITED_CONTENT, it moves to the next strategy immediately without retrying.


Input / Output

typescript
// Input
{
  imagePrompt: string;      // English description from StoryWriter
  storyId: string;          // Firestore story document ID
  pageNumber: number;       // 1-based
  referenceImages?: { name: string; base64: string }[];
  preGeneratedBase64?: string;
}

// Output
{
  imageUrl: string | null;  // GCS signed URL, or null if failed
  pageNumber: number;
  error?: string;
}

Released under the MIT License.