MomentumOS
Challenge 3 · Team USA × Google Cloud Hackathon — The Road to LA28 Games Bracket
A multi-agent momentum engine that ranks every LA28 Olympic and Paralympic sport on a National Momentum Index, using Gemini with Google Search grounding plus structured scoring and conditional fan narratives. Ships as a live full-stack app on Google Cloud Run.
| Judges: open the frontend | Live app → |
|---|
Elevator pitch
Fans cannot manually digest momentum across twenty-five sports while drowning in headlines. MomentumOS runs three coordinated AI agents—Scout (grounded web search), Scorer (transparent index), Narrator (conditional storytelling)—so Olympic and Paralympic programs get equal analytical depth, explainable scores, and a ranked bracket judges can test at a real URL.
Live deployment
| Role | URL |
|---|---|
| Frontend (share this) | https://momentumos-frontend-76wlhnioma-uc.a.run.app |
| Backend API | https://momentumos-backend-76wlhnioma-uc.a.run.app |
| Smoke test | Link |
|---|---|
| Health | GET /health |
| API map | GET / |
| OpenAPI | /docs |
Redeployed? Replace URLs from gcloud run services describe SERVICE --region REGION --format='value(status.url)'.
Problem → solution
| Problem | How MomentumOS addresses it |
|---|---|
| Fragmented signals | Scout pulls live web context via Gemini + Google Search grounding, focused on Team USA per sport. |
| “Momentum” is vague | Scorer outputs an explicit 0–100 National Momentum Index plus Recency (0–33), Trajectory (0–33), News volume (0–34)—shown in the UI tooltip for technical depth. |
| Paralympic coverage gap | Same pipeline and prompts for Olympic and Paralympic sports; UI renders two equal columns in “All” view. |
| Demo must survive judging | FastAPI + Next.js containers on Cloud Run; 300s backend timeout for long pipelines; 30‑minute in-memory cache for instant repeat demos; /momentum/cached for cache-only reads. |
Technical architecture
System context
flowchart TB
subgraph users [Judges and fans]
Browser[Browser]
end
subgraph gcp [Google Cloud Run — us-central1]
FE[momentumos-frontend<br/>Next.js 14 standalone]
BE[momentumos-backend<br/>FastAPI + Uvicorn]
end
subgraph googleai [Google AI — Gemini API]
GS[Google Search grounding]
Flash[Gemini 2.5 Flash]
Pro[Gemini 2.5 Pro]
end
Browser <-->|HTTPS| FE
FE <-->|REST JSON CORS| BE
BE -->|generate_content + Search tool| Flash
BE --> GS
BE -->|batch narration| Pro
Request flow (one pipeline run)
sequenceDiagram
actor User
participant Next as Next.js
participant API as FastAPI
participant Scout as Agent Scout
participant Score as Agent Scorer
participant Narr as Agent Narrator
User->>Next: Run MomentumOS Pipeline
Next->>API: GET /momentum?sport_type=all
Note over API: Cache hit? return stored PipelineResult
API->>Scout: 25 sports batched parallel threads
Scout->>Scout: Flash + Google Search per sport
Scout-->>API: ScoutResult[]
API->>Score: Flash refines heuristics → MomentumScore[]
Score-->>API: sorted by momentum_score
API->>Narr: Pro generates 2-sentence blurbs
Narr-->>API: NarratorResult[]
API->>API: Split Olympic / Paralympic rerank
API-->>Next: PipelineResult + agent_steps
Next-->>User: Bracket + optional trace
Agent responsibilities
| Agent | Model | Responsibility |
|---|---|---|
| Scout | Gemini 2.5 Flash + Google Search tool | For each LA28 sport: synthesize recent news + major competition narrative; signal count + snippets for downstream math. |
| Scorer | Gemini 2.5 Flash | Combine keyword heuristics with LLM refinement into final sub-scores and one-line reasoning; global sort. |
| Narrator | Gemini 2.5 Pro | Fan-facing two-sentence blurbs with mandatory conditional phrasing (no “will win”). |
Concurrency: Scout and Scorer run blocking generate_content inside asyncio.to_thread with thread-local Gemini clients so parallel batches do not share one HTTP client across threads.
Data provenance
| Layer | Source |
|---|---|
| Sport universe | Static seed backend/data/sports_seed.py (LA28 Olympic + Paralympic list). |
| Live substance | No BigQuery — Gemini grounded search at request time; summaries are model + search, not a scraped warehouse. |
| Scores & copy | Derived in-process from Scout text through Scorer and Narrator. |
API surface (backend)
| Method | Path | Behavior |
|---|---|---|
| GET | / |
API discovery JSON |
| GET | /health |
Liveness |
| GET | /momentum |
Full pipeline; refresh=true bypasses cache; sport_type filters analysis |
| GET | /momentum/cached |
{ cached, result?, message? } — always 200 |
Repository layout
| Path | Purpose |
|---|---|
frontend/ |
Next.js 14 App Router, Tailwind, bracket UI, agent trace |
backend/ |
FastAPI, agents (scout, scorer, narrator, gemini_client), models, data |
frontend/cloudbuild.yaml |
Cloud Build Docker build with NEXT_PUBLIC_BACKEND_URL |
infra/deploy.sh |
Bash deploy (LF only — see .gitattributes) |
infra/deploy.ps1 |
PowerShell deploy for Windows |
DEMO_SCRIPT.md |
3-minute narration for your recorded demo |
Local development
Backend
cd backend
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
python -m pip install -r requirements.txt
cp .env.example .env # set GOOGLE_API_KEY
uvicorn main:app --reload --port 8080
Frontend
cd frontend
npm ci
export NEXT_PUBLIC_BACKEND_URL=http://localhost:8080 # PowerShell: $env:NEXT_PUBLIC_BACKEND_URL=...
npm run dev
Open http://localhost:3000.
Environment
| Variable | Service | Purpose |
|---|---|---|
GOOGLE_API_KEY |
Backend | Gemini + Search grounding |
NEXT_PUBLIC_BACKEND_URL |
Frontend build | Browser → API base URL |
Optional tuning (backend): SCOUT_BATCH_SIZE, SCOUT_SPORT_TIMEOUT_SEC, SCORER_CONCURRENCY (see code comments).
Deploy to Cloud Run
See infra/deploy.sh or infra/deploy.ps1.
$env:PROJECT_ID = "your-project"
$env:GOOGLE_API_KEY = "your-key"
.\infra\deploy.ps1
Prerequisites: gcloud auth login, APIs enabled (run, cloudbuild, containerregistry). Frontend image uses frontend/cloudbuild.yaml because gcloud builds submit does not accept raw --build-arg.
Troubleshooting (Windows): WinError 32 during upload — reduce context via .gcloudignore, pause OneDrive/antivirus on the repo, retry.
Why this stack (for judges asking “why?”)
- Gemini Flash for high-throughput Scout + Scorer; Google Search grounding so outputs track current public signal, not static training cutoffs alone.
- Gemini Pro for Narrator — richer conditional copy across all sports in one batched call.
- Cloud Run — serverless containers, scale to zero, 300s timeout for multi-minute pipelines, separate frontend and backend services with IAM-friendly URLs.
- Next.js standalone — minimal Node image;
NEXT_PUBLIC_BACKEND_URLbaked at build time for correct browser→API routing on Cloud Run.
Submission checklist
- [ ] Live frontend URL works end-to-end (run pipeline once before recording).
- [ ]
/healthreturns 200 on backend. - [ ] Olympic and Paralympic rankings visible with equal UI weight (All view).
- [ ] Agent trace toggle demonstrates Scout → Scorer → Narrator.
- [ ] README / Devpost mention Gemini + Google Search grounding explicitly.
- [ ] Demo video (~3 min) shows Cloud Console or Cloud Run + agent trace — script in
DEMO_SCRIPT.md.
License
Apache License 2.0 — see LICENSE.
MomentumOS — National Momentum Index for Team USA · LA28 · Built with Next.js, FastAPI, Gemini 2.5 Flash/Pro, Google Search grounding, Google Cloud Run.
Built With
- docker
- fastapi
- gemini-2.5-flash
- gemini-2.5-pro
- google-cloud-run
- google-search-grounding
- next.js-14
- python
- server-sent
- tailwind-css
- typescript
- uvicorn
Log in or sign up for Devpost to join the conversation.