From df903379febee28526d561df08b829a465b5db8a Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Sat, 7 Mar 2026 20:19:38 -0500 Subject: [PATCH] Add imagegen tool for Gemini-based image generation Bash script that calls the Gemini API (gemini-2.5-flash-image) to generate images from text prompts. Reads GEMINI_API_KEY from .env. Co-Authored-By: Claude Opus 4.6 --- tools/imagegen | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100755 tools/imagegen diff --git a/tools/imagegen b/tools/imagegen new file mode 100755 index 000000000..5232db519 --- /dev/null +++ b/tools/imagegen @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Load API key from .env +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +if [[ -f "$REPO_ROOT/.env" ]]; then + # shellcheck disable=SC1091 + source "$REPO_ROOT/.env" +fi + +if [[ -z "${GEMINI_API_KEY:-}" ]]; then + echo "Error: GEMINI_API_KEY not set" >&2 + exit 1 +fi + +usage() { + echo "Usage: imagegen [output.png]" >&2 + echo "" >&2 + echo "Generate an image from a text prompt using Gemini." >&2 + echo "Output defaults to 'output.png' if not specified." >&2 + exit 1 +} + +if [[ $# -lt 1 ]]; then + usage +fi + +PROMPT="$1" +OUTPUT="${2:-output.png}" +MODEL="${IMAGEGEN_MODEL:-gemini-2.5-flash-image}" +API_URL="https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent?key=${GEMINI_API_KEY}" + +echo "Generating image for: ${PROMPT}" >&2 +echo "Model: ${MODEL}" >&2 + +RESPONSE=$(curl -s -X POST "$API_URL" \ + -H "Content-Type: application/json" \ + -d "$(jq -n --arg prompt "$PROMPT" '{ + contents: [{ parts: [{ text: $prompt }] }], + generationConfig: { responseModalities: ["TEXT", "IMAGE"] } + }')") + +# Check for errors +ERROR=$(echo "$RESPONSE" | jq -r '.error.message // empty') +if [[ -n "$ERROR" ]]; then + echo "API error: $ERROR" >&2 + exit 1 +fi + +# Extract base64 image data from the first image part +IMAGE_DATA=$(echo "$RESPONSE" | jq -r ' + .candidates[0].content.parts[] + | select(.inlineData) + | .inlineData.data +' | head -1) + +if [[ -z "$IMAGE_DATA" ]]; then + # Show any text response for debugging + TEXT=$(echo "$RESPONSE" | jq -r '.candidates[0].content.parts[] | select(.text) | .text // empty') + if [[ -n "$TEXT" ]]; then + echo "Model response (no image): $TEXT" >&2 + else + echo "No image in response. Raw:" >&2 + echo "$RESPONSE" | jq . >&2 + fi + exit 1 +fi + +echo "$IMAGE_DATA" | base64 -d > "$OUTPUT" +echo "Saved to ${OUTPUT}" >&2