CtrlK
BlogDocsLog inGet started
Tessl Logo

image-gen

Generate images via Nano Banana (Gemini 2.5/3.1 Flash Image) on OpenRouter. Use when the user asks to draw, illustrate, render or generate any kind of picture/diagram/scene.

68

Quality

83%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Low

Low-risk findings worth noting

SKILL.md
Quality
Evals
Security

image-gen — Nano Banana via OpenRouter

Generates one or more images from a text prompt (and optionally one or more input images) by calling Google's Nano Banana family on OpenRouter:

  • google/gemini-2.5-flash-image — original (Nano Banana)
  • google/gemini-3.1-flash-image-preview — latest (Nano Banana 2)

OpenRouter speaks the OpenAI-compatible chat-completions API for these models, with two extras:

  1. The request must include "modalities": ["image", "text"] so the server knows to return image bytes, not just a description.
  2. The response carries images in a top-level message.images array (NOT in content — that field still holds optional commentary text).

Quick recipe

import os, base64, json, urllib.request

KEY = os.environ["OPENROUTER_API_KEY"]
MODEL = "google/gemini-2.5-flash-image"   # or 3.1 for "Nano Banana 2"

def generate_image(prompt: str, out_path: str = "out.png") -> str:
    req = urllib.request.Request(
        "https://openrouter.ai/api/v1/chat/completions",
        data=json.dumps({
            "model": MODEL,
            "messages": [{"role": "user", "content": prompt}],
            "modalities": ["image", "text"],
        }).encode(),
        headers={
            "Authorization": f"Bearer {KEY}",
            "Content-Type": "application/json",
        },
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=120) as resp:
        data = json.loads(resp.read())

    msg = data["choices"][0]["message"]
    # ``message.images[i].image_url.url`` is a data URI:
    #   "data:image/png;base64,<base64-bytes>"
    url = msg["images"][0]["image_url"]["url"]
    b64 = url.split(",", 1)[1]
    with open(out_path, "wb") as f:
        f.write(base64.b64decode(b64))

    text_note = msg.get("content") or ""
    return f"Wrote {out_path} ({len(b64)//1024} KB). Model said: {text_note[:200]!r}"

Request shape (full)

{
  "model": "google/gemini-2.5-flash-image",
  "messages": [
    {"role": "user", "content": "A red circle on white background"}
  ],
  "modalities": ["image", "text"]
}

For image input + image output (edit / vary / extend), use the standard multipart content form OpenAI clients accept. The image part can be either a remote URL or a base64 data URI:

{
  "role": "user",
  "content": [
    {"type": "text", "text": "Make this watercolor style"},
    {"type": "image_url",
     "image_url": {"url": "data:image/png;base64,iVBORw0KGgo..."}}
  ]
}

Response shape

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "optional text commentary",
      "images": [
        {
          "type": "image_url",
          "image_url": {
            "url": "data:image/png;base64,<bytes>"
          }
        }
      ]
    }
  }],
  "usage": {
    "prompt_tokens": 8,
    "completion_tokens": 1295,
    "total_tokens": 1303,
    "cost": 0.0383,
    "completion_tokens_details": {"image_tokens": 1290}
  }
}

Cost (observed)

~$0.04 per 1024×1024 PNG at the time of writing (1290 image tokens × $0.00003/tok via the v2.5 flash-image model). Burst test: 10 images ≈ $0.40. Budget gates accordingly when looping in agent code.

When to use this skill

Trigger keywords / patterns you should recognize:

  • "generate / create / make / draw / render / illustrate (an image / a picture / a logo / ...)"
  • "show me what X looks like"
  • "design a banner / poster / icon for ..."
  • "make it watercolor / cyberpunk / pencil sketch / pixel art" (edit existing)
  • "vary this image" / "more like this but with ..."

Don't use this skill for:

  • Charts / plots from data → prefer a Python plotting skill (matplotlib / plotly) rather than a generative image. Charts need accurate numbers; Nano Banana will hallucinate axes.
  • Diagrams with precise structure (UML, DAGs) → use mermaid / graphviz instead.

Troubleshooting

  • Empty images array: you forgot "modalities": ["image", "text"]. OpenRouter falls back to text-only without it.
  • HTTP 403 with "not available in your region": OpenRouter blocks Anthropic + some Google models from China-mainland IPs. Set HTTPS_PROXY to a proxy that exits via a non-blocked region.
  • Response includes refusal: prompt was content-filtered. Rewrite the prompt; don't retry the same string.
  • PNG opens to blank / corrupt: you wrote the data URI verbatim instead of decoding. Always strip the "data:image/png;base64," prefix and base64.b64decode the rest before writing.
Repository
EverMind-AI/Raven
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.