Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
85 lines
4.1 KiB
Python
85 lines
4.1 KiB
Python
#!/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
|
|
Tongyi Wanxiang (DashScope), Jimeng/Kolors, 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): # Alibaba Tongyi Wanxiang (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)")
|