Gata Newsroom
An automated multi-agent pipeline that transforms daily topics into a recurring satirical cartoon series starring Gata, a serious investigative calico cat who views all geopolitics through the lens of feline priorities.
Install
pipx install gata
If pipx is not installed: sudo apt install pipx && pipx ensurepath
Required API keys
Three LLM provider accounts are required before you can run Gata:
| Provider | Sign up | Environment variable |
|---|---|---|
| Anthropic | console.anthropic.com | ANTHROPIC_API_KEY |
| Google AI Studio | aistudio.google.com | GEMINI_API_KEY |
| xAI | console.x.ai | XAI_API_KEY |
Auto-topic mode (
pipeline.pywithout--topic) also requires a NewsAPI.org key inNEWSAPI_ORG_KEY. Thegatacommand always requires a topic — Trend Scout is never used by it.
Export the keys in your shell, or place them in a .env file in the project root (it is
gitignored and never committed):
# Option A — shell environment
export ANTHROPIC_API_KEY=...
export GEMINI_API_KEY=...
export XAI_API_KEY=...
export NEWSAPI_ORG_KEY=... # only needed for auto-topic mode
# Option B — .env file (loaded automatically on startup)
ANTHROPIC_API_KEY=your_key_here
GEMINI_API_KEY=your_key_here
XAI_API_KEY=your_key_here
NEWSAPI_ORG_KEY=your_key_here
Quick start
gata "World Cup final: Argentina vs France"
This infers the most culturally relevant audience, negotiates a cultural angle, generates three independent cartoon concepts, picks the strongest one, and saves two PNGs to your working directory — one for the inferred audience and one for the UK public.
How it works
- Trend Scout fetches today's top headlines for the community and ranks them by
satirical potential; only used by
pipeline.pywhen--topicis not supplied — thegatacommand always requires a topic and never invokes Trend Scout - Cultural Strategist — three Framers (Claude, Grok-build, Gemini) independently propose a cultural angle; Grok-4.3 (Resonator) aggregates and picks the sharpest one
- Satirist — three Panelists (Claude, Grok-build, Gemini) independently generate a cartoon concept; Grok-4.3 (Aggregator) picks the strongest concept
- Image Generator renders the approved concept into a PNG via a fallback chain of
Gemini image models; overlays the Satirist-authored title as a dark banner at the top
(suppressed with
--no-title). When--linkedin-postis set and the layout is single-panel or horizontal, the saved file is corrected to exactly LinkedIn's 1200x644 Article-cover size — cropped/resized before the title banner, then re-fitted afterward so the banner never pushes it off-size; a vertical multi-panel cartoon is left untouched - Explainer (opt-in via
--html) — three Writers (Claude, Grok-build, Gemini) independently draft an HTML explanation page; Grok-4.3 (Editor) picks the best one; runs twice — once in the target language, once in English - Bundle Writer saves the full output package: image, conversation logs, prompt card, telemetry, and summary
Agents
| Agent | Sub-agents | LLMs | What it does |
|---|---|---|---|
| Trend Scout | — | Gemini | Fetches today's headlines from NewsAPI.org and picks the top 3 ranked by satirical potential for the community |
| Cultural Strategist | Framer ×3, Resonator | Claude · Grok-build (grok-build-0.1) · Gemini (Framers) · Grok-4.3 (Resonator/aggregator) | Three Framers independently propose a cultural angle and audience references; Resonator picks the sharpest one |
| Satirist | Panelist ×3, Aggregator | Claude · Grok-build (grok-build-0.1) · Gemini (panelists) · Grok-4.3 (aggregator) | Three panelists independently generate a cartoon concept; Aggregator picks the strongest |
| Image Generator | — | Gemini image models | Renders the approved image prompt into a PNG; tries up to 5 models in order before failing; with --linkedin-post and a single-panel/horizontal layout, corrects the saved file to LinkedIn's exact 1200x644 cover size |
| Image Evaluator | — | Gemini vision models | Checks for LLM rendering artifacts and rates comedy; triggers regeneration up to 2 times on rejection |
| Explainer | Writer ×3, Editor | Claude · Grok-build (grok-build-0.1) · Gemini (writers) · Grok-4.3 (editor/aggregator) | Three Writers independently draft HTML explanation pages (in-language + English); Editor picks the best per run |
| LinkedIn Post | Research (Gemini/Claude/Grok, independent) → Angle Planner ×3 + Managing Editor → Writer ×3 + Managing Editor | Same panelist/aggregator chains as Satirist | Each panelist researches the topic with its own real web search, three panelists independently propose article angles (an optional --angle steers this), then three panelists draft a serious, non-satirical article from the agreed angles, its own labelled Executive Summary leading the piece; a code-built Sources list (every source URL always code-verified — never LLM-authored — with a genuinely descriptive title resolved from fetched page data, its own URL, or, as a last resort, that source's own provider; anything with nothing descriptive is dropped, never published bare) is appended, and the AI-authorship disclosure plus pipeline metrics close out the final "Behind the Scenes" section (--linkedin-post only) |
| Engagement Image Concept | Panelist ×N, Art Director | Same provider chains as Satirist panelists/aggregator (or providers.yaml) |
Deliberates one image-generation prompt that visually unifies an entire newsletter edition from its stories' text only (never their rendered images); rendered via the shared ImageGeneration class and pinned to LinkedIn's exact 1200x644 Article/Newsletter cover size. Runs before the Newsletter Editor call, on by default, skippable with --no-image |
| Newsletter Editor | — | Gemini text models (primary) · Grok · Claude (fallback only, cheapest-first) | Merges several stories' linkedin_post.md files into one newsletter-edition draft plus a network-facing notification teaser (edition_notification.txt), invoked via the standalone newsletter_merge.py script — not part of the gata/pipeline.py flow |
gata command
The simplest way to run the pipeline. Give it any topic and it generates two satirical cartoons: one for the most culturally relevant audience (inferred automatically) and one for the UK public.
# Generate cartoons for a news topic
gata "Interest rates stay high despite falling inflation"
# Any topic works — Gata will find the angle
gata "World Cup final: Argentina vs France"
gata "Tech layoffs hit Silicon Valley again"
gata "Portugal wins Eurovision"
# Skip the Cultural Strategist — feed the topic straight to the Satirist
gata "AI is replacing junior developers" --direct
# Also generate HTML explanation pages
gata "NATO summit in Brussels" --html
# Generate a researched (non-satirical) companion article alongside the cartoon
gata "Vibe coding in production" --linkedin-post
gata "Vibe coding in production" --linkedin-post --angle "where it should not be used" --angle "where it's fine as long as..."
Output folder: {cwd}/{topic_slug}/ — one PNG per audience, plus a bundle folder per
image. Run gata --help to see all options.
pipeline.py — advanced usage
# Named community (exact match in communities.yaml; topic selected by Trend Scout)
python pipeline.py --community uk-politics
# Free-text community (no entry required in communities.yaml)
python pipeline.py --community "US community that dislikes Trump"
python pipeline.py --community "Communauté française qui critique Macron"
# Community + topic mode (topic supplied directly — no Trend Scout)
python pipeline.py --community uk-politics --topic "Number 10 is becoming available for rent, again."
python pipeline.py --community "Adeptos portugueses de futebol" --topic "O Ronaldo vai levar Portugal ao mundial"
# Random community and topic
python pipeline.py
# Manual mode (bypasses communities.yaml entirely)
python pipeline.py --topic "AI hype" --audience "developers" --language "English" --tone "dry wit"
# Multi-panel cartoon
python pipeline.py --community uk-politics --panels 3 --layout horizontal
python pipeline.py --community portuguese-adults --panels 2 --layout vertical
# HTML explanation pages + suppress title banner
python pipeline.py --community uk-politics --html --no-title
# Skip the Cultural Strategist (direct mode)
python pipeline.py --topic "AI hype" --audience "developers" --language "English" --direct
# Custom LLM provider chains via providers.yaml
python pipeline.py --community uk-politics --providers providers.yaml
# Researched (non-satirical) companion article, with operator-supplied angles
python pipeline.py --topic "Vibe coding in production" --audience "engineering leaders" --language English --tone neutral --direct --linkedin-post --angle "where it should not be used" --angle "where it's fine as long as..."
Multi-panel flags
| Flag | Values | Default | Description |
|---|---|---|---|
--panels |
1–4 | 1 | Number of panels in the cartoon strip |
--layout |
horizontal, vertical |
horizontal |
Panel arrangement direction |
--no-title |
— | off | Suppress the title banner overlaid at the top of the image |
--direct |
— | off | Skip the Cultural Strategist; feed topic straight to the Satirist |
--providers |
path | built-in defaults | Path to providers.yaml — overrides built-in LLM assignments |
--linkedin-post |
— | off | Generate a researched LinkedIn article (linkedin_post.md) and notification snippet (linkedin_notification.txt) in the output bundle |
--angle |
text, repeatable | none | An angle the --linkedin-post article should explore (e.g. --angle "X" --angle "Y"); has no effect without --linkedin-post |
Output bundle
Each run writes a bundle folder containing:
| File | Description |
|---|---|
cartoon.png |
The generated image |
agent0_log.txt |
Cultural Strategist negotiation history |
bc_log.txt |
Satirist panel exchange log |
prompt_card.txt |
Verbatim image prompt for standalone reuse |
telemetry.json |
Per-agent timing, token counts, and cost (machine-readable) |
summary.txt |
Per-agent time, iterations, and cost (human-readable) |
explanation.html |
In-language explanation of the joke (--html only) |
deep_dive_en.html |
English operator deep-dive (--html only) |
linkedin_post.md |
Researched, non-satirical companion article — independently researched by Claude/Gemini/Grok, opening with a labelled Executive Summary and organised by agreed angles, with a code-built Sources list; the AI-authorship disclosure and pipeline metrics sit at the bottom, inside "Behind the Scenes" (--linkedin-post only) |
linkedin_notification.txt |
Serious push-notification teaser for LinkedIn followers (--linkedin-post only) |
Communities
Communities are defined in communities.yaml. Each community specifies a target
audience, output language, tone, seed topics, and optionally a default panel count.
| Community | Language | Tone |
|---|---|---|
uk-politics |
English | Dry British wit |
uk-tech-engineers |
English | Dry British wit |
portuguese-adults |
Portuguese | Sátira política afiada |
portuguese-politics |
Portuguese | Sátira política afiada |
us-startup-crowd |
English | Sarcastic Silicon Valley cynicism |
To add a new community, add an entry to communities.yaml — no code changes required.
LLM provider configuration (providers.yaml)
providers.yaml controls which LLM models handle each agent role and in what fallback order. It is optional — if absent, Gata uses its built-in defaults (Claude Sonnet, Grok grok-build-0.1, and Gemini Flash as panelists; Grok grok-4.3 as aggregator).
Each panelist slot is an ordered fallback chain. If the primary model fails, the next model in the slot is tried — including across provider boundaries (cross-provider fallback). The aggregator entry works the same way.
panelists:
- - provider: claude
model: claude-sonnet-4-6
timeout: 25.0 # optional: per-call limit in seconds
- provider: gemini
model: gemini-2.5-flash
timeout: 15.0
- - provider: grok
model: grok-build-0.1
- provider: gemini
model: gemini-2.5-flash
aggregator:
- provider: grok
model: grok-4.3
- provider: claude
model: claude-sonnet-4-6
The optional timeout field (Spec 036) gives each provider its own per-call budget. If a provider stalls beyond that limit it is abandoned and the next provider in the chain starts with a fresh budget. Omit timeout (the default) to keep unbounded calls.
Load a custom file with --providers providers.yaml or place it in ./providers.yaml (auto-discovered).
Comedy configuration (humor.yaml)
humor.yaml controls comedy style and agent personality. All fields default to off.
| Section | Field | Type | What it does |
|---|---|---|---|
framer |
wordplay_scan |
bool | Framer actively looks for pun/wordplay opportunities |
framer |
joke_types |
list | Menu of joke types the Framer chooses from |
framer |
language_register |
string | Register for wordplay (vernacular, formal, …) |
framer |
inconvenience |
0–100 | How aggressively Framer surfaces uncomfortable truths |
satirist |
preferred_style |
string | Tone commitment (deadpan, absurdist, …) |
satirist |
avoid |
list | Joke types/styles to avoid |
satirist |
subversion |
string | Subversion intensity (high, medium, low) |
satirist |
joke_explanation |
bool | Add a <joke_explanation> block after each concept |
satirist |
inconvenience |
0–100 | How aggressively Satirist forces uncomfortable truths |
Inconvenience levels: 0 = off; 1–33 = mild nudge; 34–66 = medium push; 67–100 = maximum.
Install from source (development)
python -m venv .venv && source .venv/bin/activate
pip install -e .
Architecture
See docs/architecture.md for agent diagrams and the
communication protocol framework.
Status
| Stage | Name | Status |
|---|---|---|
| 1 | Core pipeline — Satirist/Co-Satirist creative loop + image generation | ✅ |
| 2 | Community config + model fallback chains | ✅ |
| 3 | Cultural Strategist (Framer + Resonator) | ✅ |
| 4 | Text Output Bundle (logs, HTML explanations, prompt card) | ✅ |
| 5 | Trend Scout — automated topic discovery via NewsAPI.org + Gemini | ✅ |
| 6 | Free-text community mode | ✅ |
| 7 | Multi-panel cartoon format — --panels and --layout | ✅ |
| 8 | Multi-audience CLI | ✅ |
| 9 | Run telemetry — per-agent timing, token counts, cost | ✅ |
| 10 | Dynamic audiences | ✅ |
| 11 | Mood layer | ✅ |
| 12 | Run summary | ✅ |
| 13 | Optional HTML output | ✅ |
| 14 | Image cost pricing | ✅ |
| 15 | Single main audience | ✅ |
| 16 | Clean logging | ✅ |
| 19 | Inference model fallback | ✅ |
| 20 | Auto layout | ✅ |
| 21 | Gemini Satirist | ✅ |
| 22 | Image Evaluator | ✅ |
| 23 | Evaluator fidelity | ✅ |
| 24 | LLM provider abstraction | ✅ |
| 25 | Grok integration | ✅ |
| 26 | Protocol framework + Parallel Panel | ✅ |
| 27 | Cartoon title banner + --no-title flag | ✅ |
| 29 | Grok as primary decider — Grok-3 aggregator across all ParallelPanel agents | ✅ |
| 30 | Documentation overhaul — README + architecture doc | ✅ |
| 32 | LLM provider configurability + cross-provider fallback via providers.yaml |
✅ |
| 33 | Enhanced cost reporting — per-model breakdown in telemetry summary | ✅ |
| 34 | FairParallelPanel — multi-round parallel protocol with peer sharing | ✅ |
| 35 | Direct Satirist mode — --direct flag bypasses Cultural Strategist |
✅ |
| 36 | Per-provider call timeout — optional timeout field in providers.yaml |
✅ |
| 38 | LinkedIn Newsletter companion post — --linkedin-post generates linkedin_post.md + linkedin_notification.txt |
✅ |
| 41 | Newsletter engagement image & notification — newsletter_merge.py auto-generates engagement_image.png (FairParallelPanel concept panel + shared core/image_generation.py renderer) and edition_notification.txt |
✅ |
| 42 | Researched LinkedIn article — --linkedin-post article is independently researched by Claude/Gemini/Grok (each with its own real web search), angle-planned and written via two FairParallelPanel stages, with a repeatable --angle flag and a code-built Sources list. Amended 2026-08-29: the article now opens with a labelled Executive Summary section, and the AI-authorship disclosure + pipeline metrics moved from right after the title to the bottom of the article, inside "Behind the Scenes" |
✅ |
| 43 | Uniform source titles — every Sources-list entry reads as domain - page title regardless of provider; Gemini/Grok resolve it via a direct httpx fetch of the cited URL, Claude gets a domain prefix added to its own already-good title |
✅ |
| 44 | Descriptive source titles — a 4-step chain (fetched <title>/og:title/twitter:title → humanised URL-path slug → the source's own provider's same-call title → drop) replaces the single-tier fetch, so no source is ever published as a bare domain or the site's own name restated |
✅ |
| 45 | LinkedIn feature image size correction — engagement_image.png and, with --linkedin-post on a single-panel/horizontal cartoon, cartoon.png are corrected in Python (Gemini aspect-ratio hint + Pillow centre-crop/resize) to exactly LinkedIn's 1200x644 Article-cover size, so LinkedIn's own auto-crop never clips the image |
✅ |
Release files for gata 1.26.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| gata-1.26.0.tar.gz | 151.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gata-1.26.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size:233.6 kB
Release files / gata-1.26.0.tar.gz
| Download URL | gata-1.26.0.tar.gz |
|---|---|
| Size | 151.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5c6d44862885d31926e54ded7cfe787139102f67cfaca0c14eb118818173b71d
|
|
BLAKE2b-256 checksum How to use checksums |
41ca98654ec7de9ec77c04cc3f52b2f2fe5c30f1df629a9d8fa148ef34f457fb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 29, 2026.
Transparency logRelease files / gata-1.26.0-py3-none-any.whl
| Download URL | gata-1.26.0-py3-none-any.whl |
|---|---|
| Size | 81.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f3f3a0d364e13a21a5d49071eedb564a7d647b10a4dc8cfec915fff5dea412d9
|
|
BLAKE2b-256 checksum How to use checksums |
8752a766731a03761ca58bee03ed269008b66dc451d901fec1c1840e530381de
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 29, 2026.
Transparency log