All skills
oaustegard avatar

/invoking-gemini

@4ac0233

Invokes Google Gemini models for structured outputs, image generation, text-to-speech narration, multi-modal tasks, and Google-specific features. Use when users request Gemini, image generation, Gemini TTS or a synthesized voice, structured JSON output, Google API integration, or cost-effective parallel processing.

Use this Skill: https://skilld.dev/gh/oaustegard/claude-skills/invoking-gemini

This session only. Nothing lands on disk.

SKILL.md

β‰ˆ83 tokens always: the name and description. β‰ˆ4k when used: this file. β‰ˆ16k more on demand in 5 files.

Invoking Gemini

Delegate tasks to Google's Gemini models when they offer advantages over Claude.

When to Use Gemini

Image generation:

  • Blog header images, illustrations, diagrams
  • Style-guided image creation (risograph, editorial, etc.)
  • Text rendering in images

Speech (TTS):

  • Narration, voice-over, read-aloud with style direction per line
  • A custom voice designed from a written description
  • Two-speaker dialogue

Structured outputs:

  • JSON Schema validation with property ordering guarantees
  • Pydantic model compliance
  • Strict schema adherence (enum values, required fields)

Cost optimization:

  • Parallel batch processing (Gemini 3 Flash is lightweight)
  • High-volume simple tasks

Multi-modal tasks:

  • Image analysis with JSON output
  • Video processing
  • Audio transcription with structure

Setup

uv pip install requests pydantic

Credentials β€” Option A (recommended): Cloudflare AI Gateway

Source /mnt/project/proxy.env with CF_ACCOUNT_ID, CF_GATEWAY_ID, CF_API_TOKEN. Requests route through Cloudflare AI Gateway, bypassing IP blocks. Google API key stored in gateway via BYOK.

Credentials β€” Option B: Direct Google API

If no proxy.env, falls back to direct: GOOGLE_API_KEY.txt or API_CREDENTIALS.json.

Image Generation

Generate images using Gemini's native image models. This is the primary way to create illustrations, blog headers, diagrams, and visual content.

Quick Start

import sys
sys.path.append('/mnt/skills/user/invoking-gemini/scripts')
from gemini_client import generate_image

# One call β€” returns {"path": "...", "caption": "..."} or None
result = generate_image("A watercolor painting of a mountain lake at sunset")
print(result["path"])  # /mnt/user-data/outputs/gemini_image_1740000000.png

Function Signature

generate_image(
    prompt: str,                    # The image description
    output_path: str = None,        # Auto-generates if omitted
    model: str = "nano-banana-2",   # Default: fast. Use "image-pro" for quality
    temperature: float = 0.7,       # 0.5-0.7 for diagrams, 0.7-0.8 for illustrations
) -> dict | None
# Returns: {"path": "/mnt/user-data/outputs/gemini_image_*.png", "caption": str|None}
# Returns None on failure

Model Selection

Alias Model Best For Cost/image
"nano-banana-2" or "image" gemini-3.1-flash-image-preview Fast iteration, drafts $0.067
"image-pro" or "nano-banana-pro" gemini-3-pro-image-preview Published content, text rendering $0.134

Complete Blog Header Example

import sys
sys.path.append('/mnt/skills/user/invoking-gemini/scripts')
from gemini_client import generate_image

# 1. Compose prompt with style prefix + subject
style_prefix = (
    "Style: Risograph-inspired editorial illustration. "
    "Visible halftone dot texture and slight color misregistration between layers. "
    "Limited ink palette: deep indigo, warm coral, and sage green on off-white paper. "
    "Layered transparency where colors overlap creates rich secondary tones. "
    "Modern and professional β€” the aesthetic of an indie design studio, not a fantasy novel. "
    "Generous whitespace. No photorealism, no glow effects, no cyberpunk. No text or labels."
)
subject = "A raven perched on a stack of books, observing a network graph"
prompt = f"{style_prefix}\n\nSubject: {subject}. Wide landscape format, suitable as a blog header."

# 2. Generate (use image-pro for published content)
result = generate_image(prompt, model="image-pro", temperature=0.75)

if result:
    print(f"Saved: {result['path']}")
    # 3. Present to user
    # present_files([result["path"]])

Prompt Patterns

  • Style prefix + subject: Prepend a style description, then describe the subject
  • Be specific about style: "Risograph-inspired editorial illustration" not "a nice picture"
  • Include composition: "Wide landscape format" / "centered, high contrast"
  • Text rendering: "A poster with the text 'SALE' in bold red letters" (works well with image-pro)
  • Negative constraints: "No photorealism, no glow effects" to avoid defaults

Custom Output Path

result = generate_image(
    "A logo for a coffee shop called 'Bean There'",
    output_path="/mnt/user-data/outputs/coffee_logo.png"
)

Speech Generation (TTS)

Gemini 3.8 Flash TTS and Flash-Lite TTS went GA on 2026-09-23. Output is WAV, 24 kHz mono 16-bit, SynthID-watermarked.

from gemini_client import generate_speech, design_voice, list_voices

r = generate_speech("Odin kept two ravens. <short pause> Huginn was thought.",
                    output_path="/tmp/line.wav", voice="Algenib",
                    style="quiet and dry, unhurried")
# {'path': '/tmp/line.wav', 'seconds': 4.2, 'audio_tokens': 135} or None

v = design_voice("A low, dry, quietly amused male voice with a faint rasp. "
                 "Soft southern British accent.", "narrator", gender="male",
                 language_code="en-GB")      # {'id': 'voice_...', 'sample_path': ...}
generate_speech("...", voice=v["id"])

lows = list_voices(gender="male", pitch="low")   # library of 2,089 prebuilt voices
  • Voices: 30 studio voices (Charon, Kore, Algenib "gravelly", Enceladus "breathy", ...) plus 2,059 persona voices with ids like en-gb-storyteller-4. list_voices() returns accent, pitch, gender and a description for each; the accent filter needs the exact string ("Winchester English"), so filter accents on the returned field.
  • Style: pass style= (a speech_metadata annotation). Do not prefix the text with "Style: ..." β€” the 3.8 models read the prefix aloud, and systemInstruction is rejected. Inline events go in the text: <laugh>, <sigh>, <breath>, <short pause>; CAPITALS stress a word.
  • Designed voices are stored (1-year expiry, 200 per project). The description sets baseline delivery too: "thoughtful pauses" in it produced 1–1.8 s mid-line pauses that no per-line style removed.
  • The model can change words. Seen in a 29-line narration: "Hmm, I get things wrong", "tell them" for "tell him". Anything with subtitles or a fixed script needs an ASR check (faster-whisper medium.en) and a retake.
  • Cost: about 32 audio tokens per second of speech, $9/M through 2026-12-31 on 3.8 Flash TTS ($6/M Lite), so a minute is about $0.02.
  • Voice replication (cloning from a 30 s sample plus a recorded consent clip) is not wired in, and is unavailable in the EEA, UK, Switzerland, India, Illinois and Texas.

Basic Text Usage

import sys
sys.path.append('/mnt/skills/user/invoking-gemini/scripts')
from gemini_client import invoke_gemini

response = invoke_gemini(
    prompt="Explain quantum computing in 3 bullet points",
    model="flash",  # gemini-3.8-flash (default)
)
print(response)

Structured Output

Use Pydantic models for guaranteed JSON Schema compliance:

from gemini_client import invoke_with_structured_output
from pydantic import BaseModel, Field

class BookAnalysis(BaseModel):
    title: str
    genre: str = Field(description="Primary genre")
    key_themes: list[str] = Field(max_length=5)
    rating: int = Field(ge=1, le=5)

result = invoke_with_structured_output(
    prompt="Analyze the book '1984' by George Orwell",
    pydantic_model=BookAnalysis
)
print(result.title)  # "1984"

Nested models are supported. Gemini's responseSchema rejects $ref/$defs, which pydantic emits for every nested model, so the client inlines them before sending:

class Finding(BaseModel):
    claim: str
    confidence: Literal["high", "medium", "low"]
    note: str | None = None

class Analysis(BaseModel):
    findings: list[Finding]     # nested β€” inlined for you
    gaps: list[str]

Budget output generously. Thinking tokens count against max_output_tokens (default 32768). Too low and the JSON truncates mid-object, which surfaces as a pydantic parse error rather than a length error β€” the client now detects finishReason=MAX_TOKENS and says so explicitly.

Parallel Invocation

from gemini_client import invoke_parallel

results = invoke_parallel(
    prompts=["Summarize Hamlet", "Summarize Macbeth", "Summarize Othello"],
    model="lite",  # gemini-3.5-flash-lite β€” cheap/fast tier for batch
)

Available Models

The current frontier Flash is gemini-3.8-flash (GA 2026-09-02), the default and the flash alias. Google shipped three Flash generations in six weeks: 3.6 (2026-07-21), 3.7 (2026-08-13), 3.8 (2026-09-02). Each stays callable under a pinned alias (flash-3.7, flash-3.6, flash-3.5, flash-3), and none has a shutdown date. gemini-3.1-flash-lite-preview from earlier docs is gone (shut down 2026-05-25).

The Pro tier is off routing. gemini-3.1-pro-preview costs 2.7Γ— the input and 3.2Γ— the output of 3.8 Flash at today's rates and loses to the 3.5+ Flash line on the coding and agentic benchmarks that matter here. Do not target it; the pro alias now resolves to gemini-3.8-flash, and "maximum reasoning" means thinking_level='high' on Flash.

Text / Reasoning Models

Model Alias Input/1M Output/1M Context Notes
gemini-3.8-flash flash $0.75 β†’ $1.50 $3.75 β†’ $7.50 1M in / 64K out Default. GA 2026-09-02. Current frontier Flash. Vs 3.7: Terminal-Bench 2.1 90.8% vs 81.6%, SWE-Bench Pro 61.6% vs 60.4%, SWE-Atlas 51.9% vs 48.0%, HLE flat (45.4% vs 45.7%). Google says it "works harder" at higher effort, so expect more thinking tokens per task. thinking_level is low/medium/high only β€” minimal returns HTTP 400 and the client downgrades it to low. Default medium spent 79 thinking tokens on a one-word reply (measured 2026-09-03); pass low for non-reasoning tasks.
gemini-3.7-flash flash-3.7 $0.75 β†’ $1.50 $3.75 β†’ $7.50 1M / 64K GA 2026-08-13. DeepSWE v1.1 65.3% vs 49.0% on 3.6, Terminal-Bench 2.1 85.8%. Same minimal restriction as 3.8. Google keeps it "fully supported for efficiency-first workloads".
gemini-3.6-flash flash-3.6 $0.75 β†’ $1.50 $3.75 β†’ $7.50 1M / 64K GA 2026-07-21. ~17% fewer output tokens than 3.5 Flash. Last Flash that accepts thinking_level='minimal' (verified 2026-09-03).
gemini-3.5-flash flash-3.5 $1.50 $9.00 1M GA 2026-05-19. Google's model list now labels it "legacy". Accepts minimal. Costs more on output than 3.6–3.8.
gemini-3-flash-preview flash-3 $0.30 $2.50 1M Older preview Flash, kept for back compat. Google's listed migration target for it is gemini-3.6-flash; no shutdown date.
gemini-3.1-pro-preview β€” $2.00 (≀200K) / $4.00 $12.00 / $18.00 1M DEPRECATED from routing (2026-09-03). Price/quality dominated by 3.6+ Flash; 3.5 Flash already beat it on most coding/agentic benchmarks. ID stays callable for pinned code. pro now resolves to gemini-3.8-flash. 3.5 Pro was announced at I/O 2026-05-19 for June and is still absent from the API as of 2026-09-03; it gets the same price/quality test before any alias points at it.
gemini-3.5-flash-lite lite $0.30 $2.50 1M Cheap/bulk tier. GA 2026-07-21. Fastest 3.5-class (350 output tok/sec); beats gemini-3-flash on SWE-Bench Pro and OSWorld-Verified.
gemini-2.5-flash stable-flash $0.30 $2.50 1M DEPRECATED β€” 2025-era generation, do not route here.
gemini-2.5-flash-lite β€” $0.10 $0.40 1M DEPRECATED β€” cheaper, but a 2025-era generation. lite now resolves to gemini-3.5-flash-lite.
gemini-2.5-pro stable-pro $1.25 (≀200K) / $2.50 $10.00 / $20.00 1M DEPRECATED β€” 2025-era generation, do not route here.

$0.75 β†’ $1.50 means introductory pricing: Google's pricing page (fetched 2026-09-03) lists 3.6, 3.7 and 3.8 Flash at $0.75 in / $3.75 out through 2026-12-31 and $1.50 / $7.50 from 2027-01-01. Context caching is $0.075 β†’ $0.15; Batch is half of standard. Output prices include thinking tokens.

Image Models

Model Alias Input/1M Per Image
gemini-3.1-flash-image-preview image, nano-banana-2 $0.25 $0.067
gemini-3-pro-image-preview image-pro, nano-banana-pro $2.00 $0.134

Speech Models

Model Alias Input/1M Output/1M (audio) Notes
gemini-3.8-flash-tts tts $0.50 β†’ $1.00 $9.00 β†’ $18.00 GA 2026-09-23. Expressive, 130 languages, 2 speakers. Use generate_speech(), not invoke_gemini().
gemini-3.8-flash-lite-tts tts-lite $0.50 β†’ $1.00 $6.00 β†’ $12.00 GA 2026-09-23. Bulk / read-aloud, 101 languages. Replaces gemini-3.1-flash-tts-preview ($1 / $20).

Speech aliases live in SPEECH_ALIASES, not MODEL_ALIASES, so a text call can never resolve to an audio model.

See references/models.md for full details.

Thinking Budget (Gemini 3.x)

Gemini 3.x models reason before responding. The parameter changed in 2026: integer thinking_budget is gone; use string thinking_level ∈ {minimal, low, medium, high}. Default for 3.5–3.8 Flash is medium. For transcription / classification / extraction tasks, pass thinking_level='minimal' or the model will silently spend output tokens on reasoning (symptom: empty response with finishReason=MAX_TOKENS).

3.7 and 3.8 Flash reject minimal with HTTP 400 (Thinking level MINIMAL is not supported for this model); low is their floor. The client downgrades minimal to low on those two models and prints a note to stderr, so existing callers keep working. Measured on 3.8 (2026-09-03): low spent 0 thinking tokens on a one-word reply, the default medium spent 79. On 3.7, low still spent 45–88, and a max_output_tokens=50 call at low hit MAX_TOKENS and returned None, so budget output generously there. If a job needs a true no-thinking pass, pin flash-3.6 or lite, which still accept minimal.

response = invoke_gemini(
    prompt="Transcribe this image.",
    model="flash",
    image_path="/tmp/screenshot.png",
    max_output_tokens=4000,
    thinking_level="minimal",  # don't burn output budget on reasoning
)

Error Handling

response = invoke_gemini(prompt="...", model="flash")
if response is None:
    print("API call failed β€” check credentials")

result = generate_image("...")
if result is None:
    print("Image generation failed β€” check credentials or try again")

Common issues: Missing API key β†’ see Setup. Rate limit β†’ auto-retries with backoff. Network error β†’ returns None.

Advanced Features

Custom Generation Config

response = invoke_gemini(
    prompt="Write a haiku",
    model="flash",                  # gemini-3.8-flash
    temperature=0.9,
    max_output_tokens=200,
    top_p=0.95,
    thinking_level="low",           # haiku is short; modest reasoning is fine
)

Multi-modal Input

from pydantic import BaseModel
from gemini_client import invoke_with_structured_output

class ImageDescription(BaseModel):
    objects: list[str]
    scene: str
    colors: list[str]

result = invoke_with_structured_output(
    prompt="Describe this image",
    pydantic_model=ImageDescription,
    image_path="/mnt/user-data/uploads/photo.jpg"
)

See references/advanced.md for more patterns.

Troubleshooting

"No credentials configured": Create /mnt/project/proxy.env with CF credentials, or add GOOGLE_API_KEY.txt.

CF Gateway 401/403: Verify CF_API_TOKEN has AI Gateway permissions. If not using BYOK, add GOOGLE_API_KEY to proxy.env.

Import errors: uv pip install requests pydantic

Image generation returns None: Check credentials. If persistent, try model="nano-banana-2" (more reliable than image-pro). Check for content policy blocks in error output.

Source: SKILL.md on GitHub

1 alert4mo4 checks Β· Risk SAFE
  • Gen Agent Trust Hub4mo

    The invoking-gemini skill is a professional implementation of a Google Gemini API client with support for Cloudflare AI Gateway. It handles credentials securely via local configuration files and uses trusted infrastructure. No security vulnerabilities or malicious patterns were found.

  • Socket4mo

    No alerts

  • Snyk4mo

    Risk: LOW Β· No issues

  • Runlayer7mo

    4/10 files flagged

Signed by skilld at 4ac0233. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub yesterday.

Activeupdated last week
metadata
{
  "version": "0.9.0"
}

README badge

README badge for oaustegard/claude-skills/invoking-gemini