# Gemini Image Generation Workflow

**Last Updated:** 2026-03-03  
**Purpose:** Standard workflow for generating blog featured images using Gemini API

## Standard Workflow

### 1. Create/Update IMAGE_PROMPT.md

Create or update `docs/content/blog/posts/{category}/{slug}/IMAGE_PROMPT.md` with a `## Prompt` section:

```markdown
## Prompt

[Your detailed prompt here - must explicitly exclude text/numbers]
```

**Critical:** The script reads **only** the `## Prompt` section. Do not use `## Specific Prompt` or `## Image Prompt` - use `## Prompt` exactly.

### 2. Generate Image

```bash
python3 v2/scripts/blog/generate-blog-featured-image.py --post=slug --category=lexikon
```

**What it does:**
- Reads prompt from `IMAGE_PROMPT.md` (`## Prompt` section)
- Generates PNG image using Gemini 2.5 Flash Image API
- Enforces 16:9 aspect ratio (crops if needed)
- **By default:** Runs `optimize-blog-featured-image.py` to:
  - Convert PNG → WebP variants (640w, 1024w, 1280w, 1792w)
  - Update post JSON `featured_image` with srcset
  - Remove source PNG (use `--keep-png` to retain)

### 3. Verify Output

- Check WebP files created: `v2/img/insights/{slug}-*.webp`
- Verify JSON updated: `v2/data/blog/posts/{category}/{slug}.json`
- Preview image on page: `http://localhost:8003/insights/lexikon/{slug}/`

## API Key Configuration

**Authoritative runbook:** [GEMINI_API_KEY_LOCAL.md](../../development/GEMINI_API_KEY_LOCAL.md)

The script uses `v2/scripts/gemini_api_key.py`, aligned with PHP `ordio_get_gemini_api_key()`:

1. **Environment variable:** `GEMINI_API_KEY` (recommended: repo-root `.env`)
2. **PHP config file:** `v2/config/gemini-api-key.php` (copy from `.example`, gitignored)
3. **PHP:** `ordio_get_gemini_api_key()` in `v2/config/ai-faq-config.php` (same order; no Google Maps fallback)

**Setup:**
```bash
# Option 1: Environment variable (recommended)
export GEMINI_API_KEY=your-key-here
# Or add GEMINI_API_KEY=... to repo-root .env

# Option 2: PHP config file
cp v2/config/gemini-api-key.php.example v2/config/gemini-api-key.php
# Edit file and replace YOUR_GEMINI_API_KEY_HERE with actual key
```

## Why Use the Script vs. Direct Tool?

**✅ Use `generate-blog-featured-image.py` script:**
- Reads IMAGE_PROMPT.md automatically
- Enforces 16:9 aspect ratio
- Auto-optimizes to WebP with responsive srcset
- Updates post JSON automatically
- Consistent workflow across all posts
- Handles API key configuration
- Includes NO_TEXT_PREFIX automatically

**❌ Avoid direct `generate_image` tool:**
- Requires manual prompt construction
- No automatic optimization
- No JSON update
- Inconsistent with project workflow
- May miss style guide requirements

## Troubleshooting

### Script Can't Read Prompt

**Issue:** Script says "Using custom prompt from IMAGE_PROMPT.md" but prompt is empty or wrong.

**Solution:** Ensure `IMAGE_PROMPT.md` has `## Prompt` section (not `## Specific Prompt` or `## Image Prompt`).

### API Key Not Found

**Issue:** Script fails with "Gemini API key not found"

**Solution:**
1. Check environment variables: `echo $GEMINI_API_KEY`
2. Check PHP config: `cat v2/config/gemini-api-key.php`
3. Ensure key is not placeholder: `YOUR_GEMINI_API_KEY_HERE`

### Pillow Required Error

**Issue:** Script fails with "Error: Pillow required. Run: pip install Pillow"

**Solution:**
1. **Scripts automatically use venv:** Both `generate-blog-featured-image.py` and `optimize-blog-featured-image.py` automatically switch to `.venv/bin/python` if run with system Python
2. **If error persists:** Ensure Pillow is installed in venv:
   ```bash
   .venv/bin/pip install Pillow
   ```
3. **Verify installation:**
   ```bash
   .venv/bin/python -c "import PIL; print('Pillow:', PIL.__version__)"
   ```
4. **Check requirements.txt:** Pillow should be listed (currently: `Pillow>=10.0.0`)

**Note:** Scripts call `_ensure_venv()` at module level (when run directly) to automatically switch to venv Python before any imports. This prevents Pillow import errors when running with system Python.

### Images Have Text/Numbers

**Issue:** Generated images contain text or numbers despite prompt.

**Solution:**
- Add explicit "No text, no numbers, no readable content" to prompt
- Avoid objects that invite text (calendars, documents with writing, screens with UI)
- Use abstract patterns instead of UI elements

## Best Practices

1. **Always use `## Prompt` section** in IMAGE_PROMPT.md (not `## Specific Prompt`)
2. **Explicitly exclude text/numbers** in prompt: "No text, no numbers, no readable content"
3. **Use topic-specific scenes** over abstract metaphors
4. **Test with `--dry-run`** first to verify prompt extraction
5. **Run scene-type audit** before generating: `audit-blog-image-scene-types.py --report-adjacent=3`

## Related Documentation

- [BLOG_FEATURED_IMAGE_GENERATION.md](BLOG_FEATURED_IMAGE_GENERATION.md) - Complete guide
- [BLOG_FEATURED_IMAGE_STYLE_GUIDE.md](BLOG_FEATURED_IMAGE_STYLE_GUIDE.md) - Style guidelines
- [IMAGE_PROMPT_TEMPLATE.md](posts/_templates/IMAGE_PROMPT_TEMPLATE.md) - Template for IMAGE_PROMPT.md
