A tool that generates prompts for multiple shots across all platforms — all at once.
Day 63 of 80
The batch generator takes a list of shots and generates prompts for all of them across all platforms simultaneously. 5 shots × 3 platforms = 15 API calls — all in parallel, completing in roughly the time of one call.
This is a real productivity tool. When you're planning a shoot, you can describe 10 shots and get 30 prompts back in under 5 seconds.
import asyncio
import json
import anthropic
from dotenv import load_dotenv
from logger import get_logger
load_dotenv()
logger = get_logger(__name__)
client = anthropic.AsyncAnthropic()
PLATFORMS = ["Kling", "Runway", "Veo"]
async def generate_one(shot, platform):
"""Generate a single prompt. Returns a dict with the result or error info."""
try:
message = await client.messages.create(
model="claude-sonnet-4-6",
max_tokens=150,
messages=[{
"role": "user",
"content": f"Write a {platform} AI video prompt for: {shot}. "
f"Under 75 words. Only the prompt."
}]
)
logger.info(f"Generated: [{platform}] {shot[:40]}...")
return {
"platform": platform,
"shot": shot,
"prompt": message.content[0].text,
"status": "success",
}
except Exception as e:
logger.error(f"Failed: [{platform}] {shot[:40]}... — {e}")
return {
"platform": platform,
"shot": shot,
"prompt": None,
"status": f"error: {e}",
}
async def batch_generate(shots, platforms=None):
"""Generate prompts for all shots × all platforms in parallel."""
platforms = platforms or PLATFORMS
# Build a flat list of all tasks — every combination of shot × platform.
tasks = []
for shot in shots:
for platform in platforms:
tasks.append(generate_one(shot, platform))
print(f"Generating {len(tasks)} prompts ({len(shots)} shots × {len(platforms)} platforms)...")
# asyncio.gather runs them ALL in parallel and returns results in order.
results = await asyncio.gather(*tasks)
return results
async def main():
"""Interactive mode — enter shots, generate in batch."""
print("=== DVP Batch Prompt Generator ===\n")
print("Enter shots one per line. Type 'done' when finished.\n")
shots = []
while True:
shot = input(f" Shot {len(shots) + 1} (or 'done'): ")
if shot.strip().lower() == "done":
break
if shot.strip():
shots.append(shot.strip())
if not shots:
print("No shots entered.")
return
# Run the async batch generation
results = await batch_generate(shots)
# Display results grouped by shot
print(f"\n{'=' * 60}")
current_shot = None
for r in results:
if r["shot"] != current_shot:
current_shot = r["shot"]
print(f"\n SHOT: {current_shot}")
print(f" {'-' * 50}")
if r["status"] == "success":
print(f"\n [{r['platform']}]")
print(f" {r['prompt']}\n")
else:
print(f"\n [{r['platform']}] FAILED: {r['status']}\n")
# Offer to save results
successful = [r for r in results if r["status"] == "success"]
print(f"\n{len(successful)} of {len(results)} prompts generated successfully.")
if successful and input("Save all to database? (y/n): ").lower() == "y":
from database import Database
db = Database("prompt_vault.db")
for r in successful:
db.add_prompt(r["platform"], r["shot"], r["prompt"])
db.close()
print(f"Saved {len(successful)} prompts to database.")
# In a .py file, run async code with asyncio.run().
# This starts the event loop, runs main(), and cleans up.
if __name__ == "__main__":
asyncio.run(main())
Error handling per task — the try/except inside generate_one() catches failures at the individual level. If one API call fails (rate limit, network blip), you still get all the successful results. The failed one returns a dict with status: "error" instead of crashing the whole batch.
Results stay in order. asyncio.gather() always returns results in the same order you passed the tasks. So results[0] corresponds to tasks[0], results[1] to tasks[1], etc. — even if task[5] finished before task[0].
asyncio.run(main()) is the standard entry point for async programs. It creates the event loop, runs your async main() function, and cleans up when it's done. Only call this once, at the very end of your script.
Run: python batch_generator.py
Enter 3–5 shots from a real project you're working on. Watch them all generate simultaneously — notice how the "Starting..." messages appear all at once, then results come in as they complete.
batch_generator.py created and runs without errorsgenerate_one() handles errors gracefully (returns error dict, doesn't raise)Day 64 finishes the batch generator with additional features, then Day 65 adds an async /generate endpoint to your FastAPI app.