All projects

AI in the newsroom's daily routine

The paper's AI platform and the portal where journalists use it

Role Core contributor to the newsroom's AI platform and to the portal journalists use it from. I ship features end to end across both codebases and take part in the platform's design and architecture decisions.
Period May 2025 –
Outcome In daily use by the newsroom; both codebases still get new features.
Stack
  • Python
  • FastAPI
  • Celery
  • PydanticAI
  • LiteLLM
  • SvelteKit
  • TypeScript
  • OpenTelemetry
  • Kubernetes
Internal tool · Folha

Context

The AI Lab at Folha de S.Paulo, Brazil’s largest newspaper, runs the paper’s central AI platform. It’s a FastAPI service with Celery workers that exposes text, audio, image, video and RAG capabilities to the newsroom. It’s multi-provider by design. A LiteLLM gateway sits in front of the model vendors, structured output goes through Instructor and PydanticAI, and capabilities are plugins. Bigger decisions get written down as ADRs (architecture decision records). On top of it sits the portal journalists log into, a SvelteKit app with around fifteen tools that talks to the platform through a typed internal SDK. I work across both sides of that line and take part in the platform’s design and architecture decisions.

My role

I’m a core contributor to both codebases. Over about a year I shipped features end to end (backend module, API, portal page) and helped shape the platform’s design and architecture in its ADR discussions.

The context generator is mine end to end: the service, prompts, schemas, API and tests on the platform, plus the page, store and usecases in the portal. A journalist pastes a story and gets background material on it.

I also built the front end of the portal’s video-script tool from scratch. You give it a story URL and it returns a video script, exported as DOCX or PDF. Later it grew a second mode for the paper’s news bulletins; I wrote that mode’s script-generation side on the backend too, with a discriminated-union API so both modes share one endpoint and each keeps its own response schema.

Smaller things across the year: the multi-voice TTS picker for AI video generation, editable video credits, URL handling in the RAG tools, and several migrations to new versions of that internal SDK on the front end. Plus a lot of prompt and schema iteration, driven by what the newsroom and the TV editorial team kept telling us was wrong.

Journalists’ portal SvelteKit, about fifteen tools
  • Context generator page, store and usecases
  • Video-script tool front end from scratch; DOCX and PDF generated in the browser
  • Bulletin mode the script tool’s second mode
  • TTS voice picker, editable credits AI video generation
  • the other tools
Newsroom AI platform FastAPI + Celery; text, audio, image, video and RAG as plugins; ADRs for the big decisions
  • Context generator API service, prompts, schemas, tests
  • Bulletin screenwriting mode one endpoint, a discriminated-union API
  • Persistent event loop for async Celery tasks a production fix, applied across every async task module
  • RAG URL handling
  • the other capabilities
Model providers multi-provider by design: swapping a model is configuration, not code

= my work

The newsroom’s AI platform and the portal journalists use it from. Marked cards are the features I built end to end.

The hard part

The one I still tell people about is an event-loop bug. Celery tasks are synchronous, the platform’s code is async, and the naive fix (spinning up a fresh event loop per task) quietly breaks any client that holds connections across calls. It showed up in production. My fix was a persistent event loop reused by the worker, applied across every async task module in the platform.

The DOCX and PDF export was the other one. Journalists need the file itself, and I didn’t want a server round-trip for it, so the documents are generated in the browser. That means laying out headings, tables and speaker blocks by hand in JavaScript and getting them to look the same in both formats.

Every feature here also lands in two repositories, three languages (Python, TypeScript, Svelte) and two review cultures. A feature isn’t done when the endpoint returns. It’s done when it works in both repos.

The video-script tool and the context generator in the journalists' portal, running locally with fictional example data (internal name blurred).The video-script tool and the context generator in the journalists' portal, running locally with fictional example data (internal name blurred).
The video-script tool and the context generator in the journalists' portal, running locally with fictional example data (internal name blurred).

Outcome

Both systems are in daily use by the newsroom and both still get new features. The tools I built are in use and I still get feedback on them. Most of the year went into changing them.

Internal Folha de S.Paulo project (described here without code, editorial data or infrastructure details).