A personal esports project

kiroku.

A notebook for the streams
I can’t watch.

It absorbs esports co-streams, writes down what was said, and messages me when something I care about happens.

Open the live notebook

Public transcripts and Notes. No login needed to read.

From the recordingsLeague of Legends
Caedrel

RED Kalunga vs FlyQuest

08:38
“RED should be still favorable.”
08:38

Caedrel favors RED.

A recorded example, not a live feed.
The notebook keeps each take tied to its timestamp.

Why I built it

Too many games.
One pair of eyes.

I love esports. On a big weekend, League, Dota, Valorant and CS2 can all be live at once. I can’t keep four streams open.

And the co-stream is part of the reason I watch: the streamer’s reads, predictions and opinions. Highlights often miss those. So I built something that listens for me, and lets me decide what matters.

What it does

Speech becomes something I can use.

A live transcript

Captures stream audio and transcribes it as it arrives. Speech lands in Postgres about 20–30 seconds after it’s said.

Rules that send alerts

A Rule is a plain-English yes/no question. The Gate checks every 45 seconds of speech; passing Rules lead to Telegram alerts, with feedback buttons.

Rolling Notes

Every three minutes, structured Notes collect teams, draft, key moments, takes and predictions, tied to timestamps.

Game knowledge, in Packs

The League Pack supplies champion, item and player names, plus live teams and picks. The Dota Pack supplies hero, item, team and player names.

A notebook in the browser

Read transcripts and Notes on desktop or phone, or install the PWA. Owner login unlocks Rule editing, tests against past speech, and the Review queue.

How it works

A fixed pipeline. Each step has a job.

  1. CaptureTwitch audiostreamlink + ffmpeg
  2. TranscribeSpeech to textGroq Whisper
  3. CheckGate + RulesOne Jev call per Window
  4. ExtractExplain + deduplicategpt-oss-120b
  5. NotifyTelegramAlert + yes/no feedback

From the transcriptPostgres → rolling Notes → web appThree-minute spans · FastAPI + Server-Sent Events

Alongside captureOpus archive → crash BackfillRecover missed speech after a restart

The alert steps never change, so they run as a pipeline. An agent is reserved for the planned Q&A path, where the steps depend on the question.

Measured, with limits

What the recordings actually show.

Small experiments on recorded Caedrel and Gorgc streams. These are fixture results, not accuracy guarantees.

15 → 9

Name errors with the League Pack

Out of 36 audited player and champion names on one Caedrel recording. Player-name errors fell from 3 to 0.

A text audit of one clip, not an audio-grounded word error rate. It cannot find names both versions missed.

18/18

Notes passed schema validation

Across two recorded transcripts, repeated three times. All 132 timestamps were inside their source spans.

Valid structure and timestamps don’t prove that a Note is true or complete.

6/9

Annotated predictions captured

Caedrel’s favorite and Gorgc’s explicit odds were caught in every repeat. Gorgc’s implicit winner was missed every time.

Some generated predictions also failed to match the annotations. Precision and recall need broader evaluation.

Still in progress

The full live pro-match acceptance check hasn’t run yet. Caster commentary is still mixed into the transcript. League and Dota have Packs; Dota has no live match context.

Under the hood

Built to measure.
Built to keep running.

Every paid call has a cost-ledger row. Default hard caps are $1.50 per session and $20 per month. Tests replay recordings so changes to model requests can be checked against what was measured.

Backend
Python 3.12 · asyncio · FastAPI · Pydantic · Typer · uv
Storage & audio
Postgres 17 · psycopg 3 · raw SQL · streamlink · ffmpeg · Opus
Models
Groq Whisper · Jev · Groq gpt-oss-120b
Web & alerts
SvelteKit · TypeScript · Server-Sent Events · Telegram Bot API
Checks
pytest · Playwright · Vitest · Pyright · Ruff · GitHub Actions