#!/usr/bin/env python3 """Generate the book cover image with an image-generation model. This is, fittingly, the book eating its own dog food: the cover of a book about AI agents is produced by calling an image-generation model. Run it once; the cover (cover.tex) automatically switches to images/cover-image.png when present — no other change needed. You can then note on the colophon that the cover was generated by AI. Usage (OpenAI, the default): pip install openai export OPENAI_API_KEY=your-openai-api-key python gen_cover.py Swapping providers: edit generate() below. Stubs/notes are included for 通義萬相 (DashScope)、即夢/可圖, and Flux (fal / Replicate) — pick whichever you have access to. The prompt is the important part and is provider-agnostic. """ import os # ── The prompt ──────────────────────────────────────────────────────────── # O'Reilly "animal book" homage: a single woodcut/engraving animal on pure # white, which cover.tex composites under the serif title. The octopus suits an # AI-agent book — highly intelligent, a famous tool-user, eight semi-autonomous # arms ≈ one brain + many tools/hands (and even multi-agent). Swap the animal in # the prompt if you prefer another. PROMPT = ( "Vintage scientific engraving illustration of an octopus, in the classic style of " "19th-century natural-history woodcuts and the O'Reilly animal book covers. Finely " "detailed black pen-and-ink crosshatching and fine line work; pure black line art, " "no color, no gray wash, no shading fills. The whole octopus rendered elegantly with " "gracefully curling tentacles, anatomically believable, slightly stylized. Perfectly " "clean pure white background, no scenery, no frame, no border, no text, no lettering, " "no numbers. Centered composition, crisp, high detail." ) OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "images", "cover-image.png") def generate_openai(prompt, out): """OpenAI Images API. Uses gpt-image-1 if available, else dall-e-3.""" from openai import OpenAI import base64, urllib.request client = OpenAI() try: # gpt-image-1: best prompt adherence; returns b64. Portrait 1024x1536. r = client.images.generate(model="gpt-image-1", prompt=prompt, size="1024x1536", quality="high", n=1) data = base64.b64decode(r.data[0].b64_json) open(out, "wb").write(data) except Exception as e: print(f"gpt-image-1 unavailable ({e}); falling back to dall-e-3 …") r = client.images.generate(model="dall-e-3", prompt=prompt, size="1024x1792", quality="hd", style="natural", n=1) url = r.data[0].url urllib.request.urlretrieve(url, out) # ── Alternative providers (uncomment / adapt the one you use) ─────────────── # def generate_dashscope(prompt, out): # 阿里 通義萬相 (wanx) # import dashscope # pip install dashscope ; export DASHSCOPE_API_KEY=... # rsp = dashscope.ImageSynthesis.call(model="wanx-v1", prompt=prompt, # n=1, size="1024*1536") # import urllib.request # urllib.request.urlretrieve(rsp.output.results[0].url, out) # # def generate_fal(prompt, out): # Flux via fal.ai # import fal_client, urllib.request # pip install fal-client ; export FAL_KEY=... # r = fal_client.run("fal-ai/flux-pro/v1.1", # arguments={"prompt": prompt, "image_size": "portrait_4_3"}) # urllib.request.urlretrieve(r["images"][0]["url"], out) def generate(prompt, out): return generate_openai(prompt, out) # ← swap to your provider here if __name__ == "__main__": os.makedirs(os.path.dirname(OUT), exist_ok=True) print("Generating cover image …") generate(PROMPT, OUT) print(f"Saved {OUT}") print("Now rebuild: bash build_pdf.sh (cover.tex auto-detects the image)")