This skill should be used when the user asks to "use Gemini Batch API", "process documents at scale", "submit a batch job", "upload files to Gemini", or needs large-scale LLM processing. Includes production gotchas and best practices.
/plugin marketplace add edwinhu/workflows/plugin install workflows@edwinhu-pluginsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
examples/batch_processor.pyexamples/icon_batch_vision.pyexamples/pipeline_template.pyreferences/best-practices.mdreferences/cli-reference.mdreferences/gcs-setup.mdreferences/gotchas.mdreferences/troubleshooting.mdreferences/vertex-ai.mdscripts/test_single.pyscripts/validate_jsonl.pyLarge-scale asynchronous document processing using Google's Gemini models.
YOU MUST READ EXAMPLES BEFORE WRITING ANY CODE. NO EXCEPTIONS.
User asks for batch API work
↓
MANDATORY: Read examples/batch_processor.py or examples/icon_batch_vision.py
↓
Copy the pattern exactly
↓
DO NOT guess parameter names
DO NOT try wrapper types
DO NOT improvise API calls
The Batch API has non-obvious requirements that will fail silently:
dest= not destination= - Wrong name → TypeErrorRationale: Previous agents wasted hours debugging API errors that the examples would have prevented. The patterns in examples/ are battle-tested production code.
| Excuse | Reality | Do Instead |
|---|---|---|
| "I know how APIs work" | This API has non-obvious gotchas | Read examples first |
| "I can figure it out" | You'll waste 30+ minutes on trial-and-error | Copy working patterns |
| "The examples might be outdated" | They're maintained and tested | Trust the examples |
| "I need to customize anyway" | Customization comes AFTER copying base pattern | Start with examples, then adapt |
| "Reading examples takes too long" | 2 minutes to read saves 30 minutes debugging | Read examples first |
| "My approach is simpler" | Simpler approaches already failed | Use proven patterns |
destination= instead of dest=" → WRONG. Read examples.CreateBatchJobConfig object" → WRONG. Plain dict only.examples/batch_processor.py OR examples/icon_batch_vision.pyEnforcement: If you write batch API code without reading examples first, you are violating this IRON LAW and WILL encounter preventable errors.
# macOS
brew install google-cloud-sdk
# Linux
curl https://sdk.cloud.google.com | bash
# Step 1: User authentication (for gcloud commands)
gcloud auth login
# Step 2: Application Default Credentials (for Python libraries)
gcloud auth application-default login
# Step 3: Enable Vertex AI API
gcloud services enable aiplatform.googleapis.com
Why both auth methods?
gcloud auth login: For gsutil and gcloud CLI commandsgcloud auth application-default login: For google-generativeai Python library# Create bucket in us-central1 (required region)
gsutil mb -l us-central1 gs://your-batch-bucket
# Verify location
gsutil ls -L -b gs://your-batch-bucket | grep "Location"
See references/gcs-setup.md for complete setup guide.
from examples.batch_processor import GeminiBatchProcessor
processor = GeminiBatchProcessor(
bucket_name="my-batch-bucket", # Must be in us-central1
model="gemini-2.0-flash-lite"
)
results = processor.run_pipeline(
input_dir="./documents",
prompt="Extract as JSON: {title, date, summary}",
output_dir="./results"
)
import google.generativeai as genai
# Use Vertex AI with ADC
client = genai.Client(
vertexai=True,
project="your-project-id",
location="us-central1"
)
# Submit batch job
job = client.batches.create(
model="gemini-2.5-flash-lite",
src="gs://bucket/requests.jsonl",
dest="gs://bucket/outputs/"
)
genai.batches.create()METADATA MUST BE FLAT PRIMITIVES. API PARAMETERS ARE SIMPLE STRINGS.
CORRECT ✓
"metadata": {
"request_id": "icon_123", # String
"file_name": "copy.svg", # String
"file_size": 1024 # Integer
}
WRONG ✗
"metadata": {
"request_id": "icon_123",
"file_info": { # ← NESTED OBJECT FAILS!
"name": "copy.svg",
"size": 1024
}
}
WORKAROUND (if complex data needed)
"metadata": {
"request_id": "icon_123",
"file_info": json.dumps({"name": "copy.svg", "size": 1024}) # JSON string OK
}
Why: Vertex AI stores metadata in BigQuery-compatible format. BigQuery doesn't support nested types. Violation causes: "metadata" in the specified input data is of unsupported type.
CORRECT ✓
job = client.batches.create(
model="gemini-2.5-flash-lite",
src="gs://bucket/input.jsonl", # Just a string
dest="gs://bucket/output/", # Just a string
config={"display_name": "my-job"} # Just a dict
)
WRONG ✗
job = client.batches.create(
model="gemini-2.5-flash-lite",
src="gs://bucket/input.jsonl",
destination="gs://bucket/output/", # ← PARAMETER DOESN'T EXIST!
)
WRONG ✗
job = client.batches.create(
model="gemini-2.5-flash-lite",
src="gs://bucket/input.jsonl",
config=types.CreateBatchJobConfig( # ← DON'T INSTANTIATE TYPES!
dest="gs://bucket/output/"
)
)
Why: The SDK uses simple types. Parameter is dest= (not destination). Config is a plain dict (not a type instance). The SDK converts internally.
| Excuse | Reality | Do Instead |
|---|---|---|
| "Nested metadata is cleaner" | It fails silently with cryptic errors | Flatten or use json.dumps() |
"I'll try destination= parameter" | Parameter doesn't exist, causes TypeError | Use dest= |
"I should use CreateBatchJobConfig" | That's internal typing, not for you | Pass plain dict to config= |
| "Other APIs accept nested objects" | This one doesn't, it's BigQuery-backed | Follow the examples |
| "I'll fix it if it breaks" | Job fails 5 minutes after submission | Get it right the first time |
# Add this check BEFORE submitting batch job
def validate_metadata(metadata: dict):
"""Ensure metadata contains only primitive types."""
for key, value in metadata.items():
if isinstance(value, (dict, list)):
raise ValueError(
f"Metadata '{key}' is {type(value).__name__}. "
f"Only primitives (str, int, float, bool) allowed. "
f"Use json.dumps() for complex data."
)
if not isinstance(value, (str, int, float, bool, type(None))):
raise ValueError(f"Unsupported type for '{key}': {type(value)}")
# Use it:
for request in batch_requests:
validate_metadata(request["metadata"])
Enforcement: Jobs WILL fail if metadata contains nested objects. There is no way around this requirement.
| Issue | Solution |
|---|---|
| Nested metadata fails | Use flat primitives or json.dumps() for complex data |
| TypeError: unexpected keyword | Use dest= not destination=, pass plain dict |
| Auth errors with Vertex AI | Run gcloud auth application-default login |
| vertexai=True requires ADC | API key is ignored with vertexai=True |
| Missing aiplatform API | Run gcloud services enable aiplatform.googleapis.com |
| Region mismatch | Use us-central1 bucket only |
| Wrong URI format | Use gs:// not https:// |
| Invalid JSONL | Use scripts/validate_jsonl.py |
| Image batch: inline data | Use fileData.fileUri for batch, not inline |
| Duplicate IDs | Hash file content + prompt for unique IDs |
| Large PDFs fail | Split at 50 pages / 50MB max |
| JSON parsing fails | Use robust extraction (see gotchas.md) |
| Output not found | Output URI is prefix, not file path |
Top 2 mistakes (bolded above):
dest=See references/gotchas.md for detailed solutions (now with Gotchas 10 & 11).
| Limit | Value |
|---|---|
| Max requests per JSONL | 10,000 |
| Max concurrent jobs | 10 |
| Max job size | 100MB |
| Job expiration | 24 hours |
| Model | Use Case | Cost |
|---|---|---|
gemini-2.0-flash-lite | Most batch jobs | Lowest |
gemini-2.0-flash | Complex extraction | Medium |
gemini-1.5-pro | Highest accuracy | Highest |
references/gcs-setup.md - NEW: Complete GCS and Vertex AI setup guidereferences/gotchas.md - 9 critical production gotchas (updated auth section)references/best-practices.md - Idempotent IDs, state tracking, validationreferences/troubleshooting.md - Common errors and debuggingreferences/vertex-ai.md - Enterprise alternative with comparisonreferences/cli-reference.md - gsutil and gcloud commandsexamples/icon_batch_vision.py - NEW: Batch vision analysis with Vertex AIexamples/batch_processor.py - Complete GeminiBatchProcessor classexamples/pipeline_template.py - Customizable pipeline templatescripts/validate_jsonl.py - Validate JSONL before submissionscripts/test_single.py - Test single request before batchPattern from oh-my-opencode: Gemini API and documentation evolve rapidly.
Current date: Use datetime.now() for:
When in doubt about API features or model names, verify against current date and check latest Gemini API docs.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.