0
stars
79
commits
Python
primary language
Aug 12, 2026
updated
Evidence-first Formula 1 team-radio review and Radio incident and context copilot.
Pit Wall Copilot turns a radio clip into a word-timed transcript, exposes the exact phrases behind operational incident tags, and adds lap context only when independent timing evidence supports it.
Live product · Open workspace · Live API

Race radio is valuable evidence, but it is difficult to search, align, and review consistently. Pit Wall Copilot keeps the source audio and machine output together so a reviewer can move from a transcript word to its audio, inspect why a tag exists, and see whether race timing supports the surrounding context.
The product deliberately does not infer a driver's internal state, score stress, rank urgency, establish causality, or forecast performance. If evidence is absent or conflicting, the interface says so.
aligned, ambiguous, or unaligned.The responsive landing page demonstrates the real evidence pipeline, includes an interactive product film, exposes the technical provenance, and respects prefers-reduced-motion.
The workspace connects to the Railway API, supports curated or uploaded audio, and keeps the player, transcript, incident evidence, and lap context synchronized.

Vercel
└── React + TypeScript + Vite SPA
├── evidence-led landing page
├── product film
└── live review workspace
│ HTTPS / typed JSON
▼
Railway
└── FastAPI / single Uvicorn worker
├── bounded upload staging and validation
├── 16 kHz mono decoding
├── Silero VAD speech boundaries
├── faster-whisper CPU/int8 word timestamps
├── deterministic incident lexicon v1
├── optional UTC-to-session lap alignment
└── expiring audio artifact store
│
▼
Hugging Face Hub
└── pinned public ASR model and curated dataset provenance
Heavy models load once per API process. Readiness is separate from basic health, so the UI can distinguish a running server from a model that is still warming.
| Layer | Technology |
|---|---|
| Frontend | React 19, TypeScript, Vite, GSAP |
| UI | Responsive CSS, self-hosted Inter and DM Mono |
| Audio UI | WaveSurfer |
| API | FastAPI, Uvicorn, Pydantic |
| ASR | faster-whisper / CTranslate2, CPU int8 |
| Speech boundaries | Silero VAD 6.2.1 |
| Audio processing | PyTorch, torchaudio, SoundFile, SciPy, NumPy |
| Race context | FastF1 3.8.3 validation artifacts |
| Hosting | Vercel frontend, Railway Docker backend |
| Tooling | uv, npm, Vitest, Testing Library, Pytest, Playwright |
| Method | Route | Purpose |
|---|---|---|
GET | /health | Process health check |
GET | /ready | Model readiness |
GET | /runtime-config | Upload limits and public runtime mode |
POST | /analyze/curated | Analyze a manifest-allowlisted curated clip |
POST | /analyze | Validate and analyze an uploaded audio file |
POST | /align | Align supplied message timestamps to session evidence |
GET | /sessions/{session_id} | Read curated session metadata |
GET | /incident-rules | Read active deterministic rule metadata |
GET | /curated-audio/{clip_path} | Stream allowlisted source audio |
GET | /audio/{analysis_id}/{filename} | Stream a temporary analysis artifact |
Interactive OpenAPI documentation is available at the deployed API docs.
Requirements: Docker Engine with Compose, internet access for the first model download, and at least 4 GB of available RAM.
docker compose up --build
Open:
The ASR model is downloaded at API startup and cached in the named hf-cache volume. Stop the stack with docker compose down. Use docker compose down -v only when you intentionally want to delete that cache.
Requirements: Python 3.11, uv, Node.js, and npm.
Backend:
uv sync --frozen --group dev --group research
uv run uvicorn backend.app:app --reload
Frontend:
cd frontend
npm ci
npm run dev
Vite proxies API routes to http://localhost:8000 during development.
Copy .env.example for local configuration. The safe public-demo defaults work without a secret.
| Variable | Default / example | Description |
|---|---|---|
APP_MODE | public_demo | public_demo or token-protected invite_beta |
ASR_MODEL_ID | Systran/faster-whisper-small | Must be a repository-approved pinned model |
ASR_MODEL_REVISION | pinned SHA | Must match the approved model revision |
ASR_DEVICE | cpu | Production-supported inference device |
ASR_COMPUTE_TYPE | int8 | Production-supported compute type |
HF_TOKEN | optional | Read-only Hugging Face token for authenticated downloads |
HF_HOME | platform-specific | Hugging Face cache location |
CORS_ORIGINS | local Vite origins | Comma-separated allowed frontend origins |
MAX_UPLOAD_BYTES | 10485760 | Maximum upload size |
MAX_AUDIO_DURATION_S | 60 | Maximum decoded duration |
MAX_DECODED_SAMPLES | 960000 | Maximum decoded 16 kHz samples |
MAX_PARALLEL_ANALYSES | 1 | Concurrent analysis limit |
MAX_QUEUED_ANALYSES | 0 | Waiting analysis limit |
ARTIFACT_TTL_S | 3600 | Temporary artifact lifetime |
ARTIFACT_STORE_MAX_BYTES | 536870912 | Artifact-store memory bound |
UPLOAD_ACCESS_TOKEN | unset | Required only in invite_beta; minimum 32 safe ASCII characters |
VITE_API_BASE | empty / proxied locally | Public HTTPS API base compiled into the frontend |
Never commit tokens. The model is public, so HF_TOKEN is not required for inference quality; it only authenticates Hub downloads and can improve download reliability or rate limits.
railway.toml selects backend/Dockerfile, uses /health for deployment health checks, and runs one CPU worker. Configure these service variables in Railway:
APP_MODE=public_demo
HF_HOME=/tmp/huggingface
CORS_ORIGINS=https://pit-wall-copilot.vercel.app
MAX_AUDIO_DURATION_S=60
MAX_DECODED_SAMPLES=960000
MAX_PARALLEL_ANALYSES=1
MAX_QUEUED_ANALYSES=0
ARTIFACT_TTL_S=3600
Add a newly generated read-only HF_TOKEN directly in Railway Variables if authenticated Hub downloads are desired. Do not paste it into GitHub, Vercel, issues, screenshots, or documentation.
vercel.json installs and builds the frontend and rewrites browser routes to the SPA. Set:
VITE_API_BASE=https://pit-wall-copilot-api-production.up.railway.app
If the frontend hostname changes, add its exact origin to CORS_ORIGINS on Railway and redeploy the API.
| Asset | Exact ID / version | Role | License / attribution |
|---|---|---|---|
| ASR | Systran/faster-whisper-small, revision 536b0662742c02347bc0e980a01041f333bce120 | CPU/int8 word-timed transcription | MIT according to the model repository; converted from openai/whisper-small |
| Radio dataset | MikCil/f1-team-radio, revision a0b99e1a325d92d63b574541a24902a660a352ea | Curated demo and evaluation audio | CC BY 4.0; credit Michele Ciletti / MikCil/f1-team-radio |
| Speech segmentation | Silero VAD 6.2.1 | Speech boundaries | MIT, Silero Team |
| Race context | FastF1 3.8.3 | Session clock and lap evidence | MIT software; unofficial and unaffiliated with Formula 1 |
| Incident rules | backend/data/incident-lexicon-v1.json | Exact deterministic matching | Project-authored rules v1 |
| Fonts | @fontsource/inter, @fontsource/dm-mono | Self-hosted typography | OFL-1.1 according to installed packages |
Formula 1 names, broadcasts, timing data, and marks remain the property of their respective owners. This project is unofficial and is not affiliated with Formula 1.
The 12 checked-in MP3 files are exact extracts from the pinned dataset revision. backend/data/curated/manifest.json records source rows, IDs, checksums, transcript provenance, session metadata, and usage notes. backend/data/curated/alignment_validation.json records the FastF1 timing evidence used to validate the car #28 “box” call against lap 1.
python -m backend.data.curated.validate_manifest
python -m backend.data.curated.validate_alignment
Dataset transcripts are publisher-provided machine transcriptions, not verified ground truth. Checksums establish audio identity, not timestamp accuracy.
Each teammate should complete this outside the repository if required by the event:
Systran/faster-whisper-small and MikCil/f1-team-radio.Neither asset is currently gated. This checklist documents account and event compliance; it does not add authentication to the product.
The checked-in walkthrough is served from frontend/public/media/pit-wall-walkthrough.mp4.
Supporting files:
scripts/product-film-script.txtscripts/product-film-captions.srtscripts/record-product-film.mjsscripts/generate-product-film.shThe landing page uses user-controlled playback: no autoplay, forced mute, or loop.
# Backend
uv run pytest -q
uv run pytest -q tests/test_runtime_claim_guard.py
# Frontend
cd frontend
npm test -- --run
npm run build
# Live browser flow (API + upload)
APP_URL=https://pit-wall-copilot.vercel.app node scripts/verify-live-stack.mjs
The frontend suite covers routing, source selection, player controls, transcript interaction, incident evidence, alignment states, accessibility semantics, live error states, and reduced-motion behavior. Packaging and compliance checks cover Docker structure, runtime caching, disclosure copy, model pinning, and credential signatures.
backend/
app.py FastAPI routes and runtime lifecycle
service.py Analysis orchestration
pipeline/ Decode, ASR, VAD, messages, rules, alignment
data/ Curated manifest, validation, incident lexicon
tests/ Backend unit and API tests
frontend/
src/pages/ Landing and workspace routes
src/components/ Player, transcript, incident feed, source controls
src/api/ Typed live and fixture adapters
src/motion/ Reduced-motion-aware GSAP choreography
public/media/ Product walkthrough and poster
fixtures/ Frozen v1 analysis response
schemas/ Versioned JSON contracts
scripts/ Film, recording, and live-stack verification
docs/ Demo notes, status, and screenshots
railway.toml Railway backend deployment
vercel.json Vercel frontend deployment
The original voice-state direction failed preregistered viability gates. The dimensional audeering model reached only 0.0739 high-minus-low arousal spread and 0.3092 Spearman correlation on the fixed sample. The HuBERT fallback collapsed 10 of 12 clips to hap and found no high-stress clips at its fixed threshold. Additional candidates also failed the required 0.25 spread and 0.40 correlation bars.
The project therefore ships auditable incident and race context instead of unreliable driver-state inference. Full measurements and NO-GO evidence remain in NOTES.md.
docs/DEMO.md - presentation script and readiness checklistdocs/MVP_PIPELINE_STATUS.md - component-by-component status and limitationsNOTES.md - research decisions and rejected model evidence62 commits
17 commits
Python
67.7%
TypeScript
28.8%
JavaScript
2.4%
0
stars
79
commits
Python
primary language
Aug 12, 2026
updated
Evidence-first Formula 1 team-radio review and Radio incident and context copilot.
Pit Wall Copilot turns a radio clip into a word-timed transcript, exposes the exact phrases behind operational incident tags, and adds lap context only when independent timing evidence supports it.
Live product · Open workspace · Live API

Race radio is valuable evidence, but it is difficult to search, align, and review consistently. Pit Wall Copilot keeps the source audio and machine output together so a reviewer can move from a transcript word to its audio, inspect why a tag exists, and see whether race timing supports the surrounding context.
The product deliberately does not infer a driver's internal state, score stress, rank urgency, establish causality, or forecast performance. If evidence is absent or conflicting, the interface says so.
aligned, ambiguous, or unaligned.The responsive landing page demonstrates the real evidence pipeline, includes an interactive product film, exposes the technical provenance, and respects prefers-reduced-motion.
The workspace connects to the Railway API, supports curated or uploaded audio, and keeps the player, transcript, incident evidence, and lap context synchronized.

Vercel
└── React + TypeScript + Vite SPA
├── evidence-led landing page
├── product film
└── live review workspace
│ HTTPS / typed JSON
▼
Railway
└── FastAPI / single Uvicorn worker
├── bounded upload staging and validation
├── 16 kHz mono decoding
├── Silero VAD speech boundaries
├── faster-whisper CPU/int8 word timestamps
├── deterministic incident lexicon v1
├── optional UTC-to-session lap alignment
└── expiring audio artifact store
│
▼
Hugging Face Hub
└── pinned public ASR model and curated dataset provenance
Heavy models load once per API process. Readiness is separate from basic health, so the UI can distinguish a running server from a model that is still warming.
| Layer | Technology |
|---|---|
| Frontend | React 19, TypeScript, Vite, GSAP |
| UI | Responsive CSS, self-hosted Inter and DM Mono |
| Audio UI | WaveSurfer |
| API | FastAPI, Uvicorn, Pydantic |
| ASR | faster-whisper / CTranslate2, CPU int8 |
| Speech boundaries | Silero VAD 6.2.1 |
| Audio processing | PyTorch, torchaudio, SoundFile, SciPy, NumPy |
| Race context | FastF1 3.8.3 validation artifacts |
| Hosting | Vercel frontend, Railway Docker backend |
| Tooling | uv, npm, Vitest, Testing Library, Pytest, Playwright |
| Method | Route | Purpose |
|---|---|---|
GET | /health | Process health check |
GET | /ready | Model readiness |
GET | /runtime-config | Upload limits and public runtime mode |
POST | /analyze/curated | Analyze a manifest-allowlisted curated clip |
POST | /analyze | Validate and analyze an uploaded audio file |
POST | /align | Align supplied message timestamps to session evidence |
GET | /sessions/{session_id} | Read curated session metadata |
GET | /incident-rules | Read active deterministic rule metadata |
GET | /curated-audio/{clip_path} | Stream allowlisted source audio |
GET | /audio/{analysis_id}/{filename} | Stream a temporary analysis artifact |
Interactive OpenAPI documentation is available at the deployed API docs.
Requirements: Docker Engine with Compose, internet access for the first model download, and at least 4 GB of available RAM.
docker compose up --build
Open:
The ASR model is downloaded at API startup and cached in the named hf-cache volume. Stop the stack with docker compose down. Use docker compose down -v only when you intentionally want to delete that cache.
Requirements: Python 3.11, uv, Node.js, and npm.
Backend:
uv sync --frozen --group dev --group research
uv run uvicorn backend.app:app --reload
Frontend:
cd frontend
npm ci
npm run dev
Vite proxies API routes to http://localhost:8000 during development.
Copy .env.example for local configuration. The safe public-demo defaults work without a secret.
| Variable | Default / example | Description |
|---|---|---|
APP_MODE | public_demo | public_demo or token-protected invite_beta |
ASR_MODEL_ID | Systran/faster-whisper-small | Must be a repository-approved pinned model |
ASR_MODEL_REVISION | pinned SHA | Must match the approved model revision |
ASR_DEVICE | cpu | Production-supported inference device |
ASR_COMPUTE_TYPE | int8 | Production-supported compute type |
HF_TOKEN | optional | Read-only Hugging Face token for authenticated downloads |
HF_HOME | platform-specific | Hugging Face cache location |
CORS_ORIGINS | local Vite origins | Comma-separated allowed frontend origins |
MAX_UPLOAD_BYTES | 10485760 | Maximum upload size |
MAX_AUDIO_DURATION_S | 60 | Maximum decoded duration |
MAX_DECODED_SAMPLES | 960000 | Maximum decoded 16 kHz samples |
MAX_PARALLEL_ANALYSES | 1 | Concurrent analysis limit |
MAX_QUEUED_ANALYSES | 0 | Waiting analysis limit |
ARTIFACT_TTL_S | 3600 | Temporary artifact lifetime |
ARTIFACT_STORE_MAX_BYTES | 536870912 | Artifact-store memory bound |
UPLOAD_ACCESS_TOKEN | unset | Required only in invite_beta; minimum 32 safe ASCII characters |
VITE_API_BASE | empty / proxied locally | Public HTTPS API base compiled into the frontend |
Never commit tokens. The model is public, so HF_TOKEN is not required for inference quality; it only authenticates Hub downloads and can improve download reliability or rate limits.
railway.toml selects backend/Dockerfile, uses /health for deployment health checks, and runs one CPU worker. Configure these service variables in Railway:
APP_MODE=public_demo
HF_HOME=/tmp/huggingface
CORS_ORIGINS=https://pit-wall-copilot.vercel.app
MAX_AUDIO_DURATION_S=60
MAX_DECODED_SAMPLES=960000
MAX_PARALLEL_ANALYSES=1
MAX_QUEUED_ANALYSES=0
ARTIFACT_TTL_S=3600
Add a newly generated read-only HF_TOKEN directly in Railway Variables if authenticated Hub downloads are desired. Do not paste it into GitHub, Vercel, issues, screenshots, or documentation.
vercel.json installs and builds the frontend and rewrites browser routes to the SPA. Set:
VITE_API_BASE=https://pit-wall-copilot-api-production.up.railway.app
If the frontend hostname changes, add its exact origin to CORS_ORIGINS on Railway and redeploy the API.
| Asset | Exact ID / version | Role | License / attribution |
|---|---|---|---|
| ASR | Systran/faster-whisper-small, revision 536b0662742c02347bc0e980a01041f333bce120 | CPU/int8 word-timed transcription | MIT according to the model repository; converted from openai/whisper-small |
| Radio dataset | MikCil/f1-team-radio, revision a0b99e1a325d92d63b574541a24902a660a352ea | Curated demo and evaluation audio | CC BY 4.0; credit Michele Ciletti / MikCil/f1-team-radio |
| Speech segmentation | Silero VAD 6.2.1 | Speech boundaries | MIT, Silero Team |
| Race context | FastF1 3.8.3 | Session clock and lap evidence | MIT software; unofficial and unaffiliated with Formula 1 |
| Incident rules | backend/data/incident-lexicon-v1.json | Exact deterministic matching | Project-authored rules v1 |
| Fonts | @fontsource/inter, @fontsource/dm-mono | Self-hosted typography | OFL-1.1 according to installed packages |
Formula 1 names, broadcasts, timing data, and marks remain the property of their respective owners. This project is unofficial and is not affiliated with Formula 1.
The 12 checked-in MP3 files are exact extracts from the pinned dataset revision. backend/data/curated/manifest.json records source rows, IDs, checksums, transcript provenance, session metadata, and usage notes. backend/data/curated/alignment_validation.json records the FastF1 timing evidence used to validate the car #28 “box” call against lap 1.
python -m backend.data.curated.validate_manifest
python -m backend.data.curated.validate_alignment
Dataset transcripts are publisher-provided machine transcriptions, not verified ground truth. Checksums establish audio identity, not timestamp accuracy.
Each teammate should complete this outside the repository if required by the event:
Systran/faster-whisper-small and MikCil/f1-team-radio.Neither asset is currently gated. This checklist documents account and event compliance; it does not add authentication to the product.
The checked-in walkthrough is served from frontend/public/media/pit-wall-walkthrough.mp4.
Supporting files:
scripts/product-film-script.txtscripts/product-film-captions.srtscripts/record-product-film.mjsscripts/generate-product-film.shThe landing page uses user-controlled playback: no autoplay, forced mute, or loop.
# Backend
uv run pytest -q
uv run pytest -q tests/test_runtime_claim_guard.py
# Frontend
cd frontend
npm test -- --run
npm run build
# Live browser flow (API + upload)
APP_URL=https://pit-wall-copilot.vercel.app node scripts/verify-live-stack.mjs
The frontend suite covers routing, source selection, player controls, transcript interaction, incident evidence, alignment states, accessibility semantics, live error states, and reduced-motion behavior. Packaging and compliance checks cover Docker structure, runtime caching, disclosure copy, model pinning, and credential signatures.
backend/
app.py FastAPI routes and runtime lifecycle
service.py Analysis orchestration
pipeline/ Decode, ASR, VAD, messages, rules, alignment
data/ Curated manifest, validation, incident lexicon
tests/ Backend unit and API tests
frontend/
src/pages/ Landing and workspace routes
src/components/ Player, transcript, incident feed, source controls
src/api/ Typed live and fixture adapters
src/motion/ Reduced-motion-aware GSAP choreography
public/media/ Product walkthrough and poster
fixtures/ Frozen v1 analysis response
schemas/ Versioned JSON contracts
scripts/ Film, recording, and live-stack verification
docs/ Demo notes, status, and screenshots
railway.toml Railway backend deployment
vercel.json Vercel frontend deployment
The original voice-state direction failed preregistered viability gates. The dimensional audeering model reached only 0.0739 high-minus-low arousal spread and 0.3092 Spearman correlation on the fixed sample. The HuBERT fallback collapsed 10 of 12 clips to hap and found no high-stress clips at its fixed threshold. Additional candidates also failed the required 0.25 spread and 0.40 correlation bars.
The project therefore ships auditable incident and race context instead of unreliable driver-state inference. Full measurements and NO-GO evidence remain in NOTES.md.
docs/DEMO.md - presentation script and readiness checklistdocs/MVP_PIPELINE_STATUS.md - component-by-component status and limitationsNOTES.md - research decisions and rejected model evidence62 commits
17 commits
Python
67.7%
TypeScript
28.8%
JavaScript
2.4%