Tech Stack
Guiding principles
Jester is built on three principles.
| Principle | Details |
|---|---|
| Serverless | No always-on servers; code runs only when needed, keeping cost and operational load down |
| Built on AWS | Compose managed AWS services instead of running our own infrastructure |
| AI first | Article and blog writing is delegated to Amazon Bedrock to reduce manual work |
There is one more principle that matters specifically for using generative AI.
Let the machine do what machines are good at
Asking an AI to transcribe or cross-reference numbers produces mix-ups. So interpretation, matching and transcription are done in code, and the AI only writes the Japanese. Entrant names and payout amounts are filled in from the official data via placeholders, and the generated text is machine-checked against that data afterwards.
AI technology
Amazon Bedrock
Where the generative models run. Different models are used for different jobs.
| Use | Model | Why |
|---|---|---|
| Race video / course image analysis | Amazon Nova Pro | Supports video input |
| Article and blog writing | Claude Sonnet (falls back to Nova Pro when unset) | Mostly Japanese text generation |
| Thumbnail moment selection | Amazon Nova Pro | Analyses the race video |
| Embeddings | Titan Text Embeddings v2 | Powers related-blog similarity search |
LangGraph
A library for assembling AI as an agent โ a setup where the model decides "what to do next" and autonomously calls tools (e.g. race video analysis) until it reaches its goal, rather than following a fixed sequence.
Jester builds two agents: combined race analysis (video, course image and race result) and thumbnail moment selection. Article and blog writing is tool-less text processing, so it uses a single structured-output call rather than an agent.
Where code runs
AWS Lambda
AWS's mechanism for running small programs that start only when needed. No always-on server is required, so you pay only for what you use.
Jester currently has seven Lambdas.
| Lambda | Language | Role |
|---|---|---|
App Lambda (odds_poc_app) |
Python | Web API serving the admin UI |
| article_generation | Python | Writes the 10-second race result summary with AI |
| article_builder | TypeScript | Renders the generated article data to HTML with React components and stores it |
| blog_generation | Python | Writes race recap / prediction blogs from scratch |
| blog_rewriter | Python | Rewrites past blog articles without changing their content |
| blog_builder | TypeScript | Pours the blog body into an article template, renders it and stores it |
| thumbnail_generation | Python | Extracts the frame at an AI-selected moment of the race video as a thumbnail |
Amazon API Gateway
The front door for incoming web requests, which it forwards to the App Lambda.
Where data lives
Amazon DocumentDB
The database holding articles, blogs, race information and so on. It is MongoDB-compatible, so data can be stored in flexible JSON-like documents.
Amazon S3
The object store for large data: race videos, course images, uploaded media and generated thumbnails.
Amazon CloudFront
The S3 bucket is private, so CloudFront is the delivery front-end that lets browsers load images. Thumbnails are embedded into blog bodies as CloudFront URLs.
How the pieces talk to each other
Amazon SQS (message queues)
Rather than calling one another directly, Lambdas drop work into a queue โ like a mailbox for
"please do this". When the App Lambda posts "generate this article", the article_generation Lambda
picks it up. When generation finishes, the result goes into another mailbox (the article-builder
queue) and the article_builder Lambda builds the HTML and stores it.
This gives us:
- A burst of requests is absorbed and processed in order
- Failed processing is retried automatically
Behaviour differs on the final attempt
Retries are capped. Jester cleans up records left mid-generation only when the final attempt
fails (articles are kept as failed with a reason; blogs are deleted). Cleaning up on the first
failure would prevent self-recovery from transient errors.
VPC (virtual private cloud)
A private area inside AWS. The database and the Lambdas live inside it, so they cannot be reached directly from outside.
Languages
Python
The backend is mostly Python 3.12 โ widely used for AI, data processing and web APIs, with a rich library ecosystem.
TypeScript / Node.js (article_builder and blog_builder)
Article and blog HTML is assembled from React components. blog_builder in particular bundles and
renders the real components and real SCSS from the design repository
(oddspark-static-pages). These two Lambdas are therefore written in TypeScript (Node.js 22).
| Name | Role |
|---|---|
| React / react-dom | Turns components into an HTML string (renderToStaticMarkup) |
| zod | Validates the shape of incoming messages |
| esbuild / sass | Bundles components and styles into a single file at deploy time |
| mongodb | Official driver for reading and writing DocumentDB |
Main libraries
| Name | Role |
|---|---|
| FastAPI | Web API framework (used by the App Lambda) |
| Pydantic / pydantic-settings | Automatic type validation and environment variable management |
| Beanie / Motor | Reading and writing DocumentDB from Python |
| LangGraph / langchain-aws | Assembling AI as agents |
| boto3 | Driving AWS services (S3 / SQS / Bedrock, โฆ) from Python |
| Mangum | Adapter that lets FastAPI run on Lambda |
| pyahocorasick | Fast prohibited-word screening |
| ffmpeg / ffprobe | Reading video duration and extracting a frame at a given moment |
Quality tooling
| Tool | What it does |
|---|---|
| ruff | Lints and formats Python code |
| mypy | Catches type mismatches ahead of time |
| uv | Manages and installs dependencies; the workspace ties all the apps together |
| mise | Keeps everyone on the same Python version |
| ESLint / tsc | Linting and type checking on the TypeScript side |
These act as a safety net that prevents bugs, catching mistakes developers would otherwise miss.
Mini glossary
| Term | Meaning |
|---|---|
| Serverless | Letting the cloud run and manage servers instead of doing it yourself |
| Lambda | AWS's way of running small programs that start only when needed |
| Queue | A "mailbox" that holds work until something is ready to process it |
| DLQ | A dedicated queue where messages go after repeated processing failures |
| API | The interface programs use to talk to each other |
| Database | Storage for structured data (articles, race information, โฆ) |
| Object storage | Storage for large files such as images and videos |
| Library | A collection of ready-made functionality |
| Framework | The scaffolding an application is built on |
| Embedding | A numeric representation of a text's meaning; comparing them finds similar articles |
| SSR | Turning components into HTML on the server |