Autonomous competitive intelligence — from a single URL to an evidence-linked report and a war-gamed prediction of your rival's next move.
Klue tells you what your competitor did. WarRoom tells you what they'll do next.
Built for the AMD Developer Hackathon — ACT II, Unicorn track.
Give WarRoom one company URL. It autonomously:
- Infers the market the company plays in (country, category, and its distinct product verticals) from the company's own site.
- Discovers ~10 real competitors — national and global — with vertical-aware, local-language search, each graded with an evidence-linked profile card.
- Scrapes their public surfaces (pricing, changelog, blog, careers), chunks and embeds them, and runs analyst agents to extract evidence-linked findings — every claim carries a source URL and confidence, or is honestly marked
inference. - Synthesizes a report: executive summary, a transparent multi-factor threat ranking, competitor dossiers, a pricing comparison matrix, SWOT, feature gaps, and opportunities.
- War-games a move: pick a competitor and a scenario, and two AI agents — a Strategist (you) and a Competitor persona grounded in that rival's own scraped data — debate over three rounds, judged by an Arbiter verdict.
Everything is grounded in the competitor's real, public data — no hallucinated intelligence.
- 🧭 Vertical-aware discovery — a multi-product company isn't collapsed into one generic search. Market inference emits the company's distinct verticals in the home market's language, and discovery fans out one region-scoped search per vertical — so a Turkish savings + pension + insurance app finds gold-app, BES, insurtech, and digital-contract rivals, not generic budgeting tools.
- 🔗 Evidence discipline (product-critical) — every synthesized claim is
{text, source_urls[], evidence_chunk_ids[], confidence, type}. A claim can only beevidenceif it cites a chunk the model was actually shown for that competitor; source URLs are re-derived from real chunk rows, never trusted from the LLM. Anything unverifiable degrades toinference— the run never fails, it just gets more honest. - ⚖️ Transparent threat score — a 0–100 rating that shows why: four weighted factors (category overlap · audience overlap · market proximity · momentum) rendered as a segmented bar with a per-factor rationale, in both the UI and the PDF.
- 🎮 Grounded war-game — a 3-round Strategist↔Competitor debate + Arbiter verdict, where the rival persona can only cite chunks retrieved from its own pages.
- ⚡ Hybrid inference — high-volume work (embeddings, extraction) routes to a local tier; heavy reasoning (reconcile, synthesis, debate) routes to a remote tier. Every call is metered into a cost panel — the AMD-usage evidence.
- 📄 Branded PDF export, EN/TR i18n, dark/light theming, and a seeded offline demo mode that replays without network.
flowchart LR
URL([company URL]) --> D[discover]
D -->|market inference + vertical search| GATE{{awaiting_confirmation<br/>HITL gate}}
GATE -->|PATCH confirm| C[collect]
C --> A[analyze]
A --> E[enrich]
E --> S[synthesize]
S --> R[[evidence-linked report]]
R --> W[war-game debate]
subgraph tiers[hybrid LLM routing]
L[local tier<br/>embeddings · extraction]
RM[remote tier<br/>reconcile · synthesis · debate]
end
C -. embed .-> L
A -. extract .-> L
A -. reconcile .-> RM
S -. synthesis .-> RM
W -. debate .-> RM
The pipeline is two Agno workflows split across a human-in-the-loop confirmation gate: phase 1 is discover (own-company market inference + search-grounded discovery), then the user confirms/rejects/adds competitors, then phase 2 runs collect → analyze → enrich → synthesize. Progress streams live over Server-Sent Events (Redis pub/sub); the war-game streams the same way.
A modular monolith with a strict, unidirectional module DAG and router → service → repository layering (routers hold no business logic).
flowchart TD
subgraph api[FastAPI · /api/v1]
AN[analyses] --> COL[collection]
COL --> INT[intelligence]
INT --> WG[wargame]
MET[metrics<br/>leaf]
end
subgraph core[app/core]
LLM[llm.py<br/>tiered gateway + model_calls]
DB[(Postgres + pgvector)]
BUS[bus.py<br/>Redis SSE]
TASKS[tasks.py<br/>background spawn]
SEARCH[search.py<br/>DuckDuckGo → Tavily]
end
AN & COL & INT & WG --> LLM
AN & COL & INT & WG & MET --> DB
AN & WG --> BUS
AN --> SEARCH
LLM --> DB
| Module | Responsibility |
|---|---|
analyses |
Pipeline orchestration + the HITL confirmation gate; owns the analysis lifecycle and SSE status. |
collection |
Public-surface scraping → snapshots → chunks → embeddings (the RAG substrate). Internal service; no HTTP routes. |
intelligence |
Analyst extraction + reconcile + the Synthesizer → the evidence-linked report. |
wargame |
The two-agent debate: a competitor persona grounded in its own chunks vs. your Strategist, judged by an Arbiter. |
metrics |
Aggregates model_calls into the cost-panel payload (leaf module). |
Hard rules (see CLAUDE.md): all LLM access through core/llm.py; agent prompts live in backend/app/prompts/*.md (versioned, loaded at runtime); Alembic migration for every schema change; SSE over Redis (no WebSockets); no Celery/Kafka (a supervised spawn() registry is enough); no auth/multi-tenancy (single-user demo).
core/llm.py is a single OpenAI-compatible gateway with two chat tiers — local and remote — plus an embedding client. Model names and base URLs come entirely from config, never hardcoded. Every call logs a model_calls row (tier, model, task_type, tokens in/out, latency, cost estimate), which powers the cost panel — the evidence of how much work runs on the local/AMD tier.
The routing intent: send high-volume, cheap work to local (an AMD-hosted model) and heavy reasoning to remote. A real seeded run shows the split:
local → chunk_embedding ×11 · pricing_extract ×5 · positioning_extract ×5
remote → pricing_reconcile ×3 · positioning_reconcile ×3 · synthesis ×1
Swapping the local tier onto AMD hardware is env-only — point LOCAL_LLM_BASE_URL / LOCAL_LLM_MODEL at a vLLM / Fireworks / AMD Instinct endpoint and set LLM_MAX_TOKENS_PARAM=max_tokens. No business-logic change.
Currently all tiers run on OpenAI (
gpt-5-minilocal,gpt-5.1remote,text-embedding-3-small); the design keeps the provider swap to a handful of env vars.
Prerequisites: Docker + Docker Compose, and (for the frontend) Node.js + pnpm. An OpenAI-compatible API key.
# 1. Configure — copy the template and fill in your key(s)
cp .env.example .env # set OPENAI_API_KEY (min); optionally TAVILY_API_KEY
# 2. Bring up the whole stack (Postgres + Redis + API + frontend)
docker compose up --build # API on http://localhost:8000 (GET /healthz), UI on http://localhost:3000The API container runs Alembic migrations against the pgvector Postgres on boot. The frontend reads NEXT_PUBLIC_API_URL (defaults to http://localhost:8000).
For frontend work with live reload, stop the Compose
frontendservice and runcd frontend && pnpm install && pnpm devinstead (same port 3000).
Set SEEDED_DEMO=true and analyze https://notion.so. Discovery returns a deterministic Notion / Coda / Obsidian trio and collection replays committed HTML snapshots from backend/seeds/ — no discovery search, no scraping, no enrichment calls. (Embeddings, analyst extraction, and synthesis still run, so the report is real.) This is the reproducible happy-path used for demos and CI-style checks.
Copy .env.example → .env. Key variables (full defaults in backend/app/core/config.py):
| Variable | Default | Notes |
|---|---|---|
OPENAI_API_KEY |
— | Shared fallback key for every tier. |
LOCAL_LLM_MODEL / LOCAL_LLM_BASE_URL |
gpt-5-mini / OpenAI |
High-volume tier (point at AMD/vLLM to tell the AMD story). |
REMOTE_LLM_MODEL / REMOTE_LLM_BASE_URL |
gpt-5.1 / OpenAI |
Heavy-reasoning tier. |
EMBEDDING_MODEL / EMBEDDING_DIM |
text-embedding-3-small / 1536 |
pgvector column + HNSW index size. |
LLM_MAX_TOKENS_PARAM |
max_completion_tokens |
Use max_tokens for vLLM/Fireworks. |
DATABASE_URL |
postgresql+asyncpg://warroom:warroom@localhost:5432/warroom |
Async driver. |
REDIS_URL |
redis://localhost:6379/0 |
SSE pub/sub bus. |
TAVILY_API_KEY |
— | Optional search fallback when DuckDuckGo rate-limits. |
SEEDED_DEMO |
false |
Offline replay of the demo trio. |
MODEL_PRICES |
(built-in) | USD per 1M tokens, per model — drives cost estimates. |
Secrets live only in .env / backend/.env (both git-ignored); only .env.example is tracked.
# Backend (from backend/) — Python 3.12, managed with uv
uv sync # install
uv run alembic upgrade head # migrate
uv run uvicorn app.main:app --reload # dev server
uv run pytest # service-layer tests
uv run ruff check . && uv run ruff format .
# Frontend (from frontend/) — Next.js 16 / React 19 / TS
pnpm install
pnpm dev # dev
pnpm build # gated: eslint . --max-warnings=0 && next buildThe frontend build is gated on a strict, type-aware ESLint pass (strictTypeChecked + stylisticTypeChecked, zero warnings) before next build. Tests cover service-layer logic (extraction parsing, diff logic, evidence-schema enforcement, threat scoring, war-game persona resolution).
backend/
app/
main.py # FastAPI app; routers under /api/v1; startup sweep
core/ # config · db · llm (tiered gateway) · bus · tasks · search · prompts
agents/ # Agno workflows: pipeline.py (2 phases) · wargame.py (debate)
modules/
analyses/ collection/ intelligence/ wargame/ metrics/
prompts/ # versioned *.md agent prompts
seeds/ # Notion/Coda/Obsidian HTML snapshots for SEEDED_DEMO
alembic/ # migrations (pgvector, domain tables, discovery + threat columns)
frontend/
app/ # App Router: / · analysis/[id] · report/[id] · report/[id]/wargame
components/ # report · competitor · wargame · evidence · analysis · ui (shadcn)
messages/{en,tr}/ # next-intl catalogs
PROJECT_SPEC.md # source of truth for scope, agents, schema, plan
| Method & path | Purpose |
|---|---|
POST /analyses |
Start a run (kicks off discovery). |
GET /analyses/{id} |
Status + per-step progress + competitors. |
PATCH /analyses/{id}/competitors |
HITL gate — confirm/reject/add, then phase 2 runs. |
GET /analyses/{id}/events |
SSE pipeline stream. |
GET /analyses/{id}/report · GET /reports/{id} |
The synthesized report. |
POST /reports/{id}/wargame |
Start a war-game debate. |
GET /wargame/{id} · GET /wargame/{id}/stream |
Debate state / SSE stream. |
GET /metrics/model-usage |
Cost-panel usage (never 404s). |
GET /healthz |
Liveness. |
Backend — FastAPI · async SQLAlchemy 2.0 · Alembic · PostgreSQL + pgvector (HNSW) · Redis (SSE) · Agno (agent workflows) · OpenAI-compatible LLMs · httpx + selectolax + trafilatura (scraping) · DuckDuckGo/Tavily (discovery search) · uv · ruff.
Frontend — Next.js 16 (App Router) · React 19 · TypeScript · Tailwind v4 · shadcn/ui + Radix · TanStack Query (server state) · Zustand (UI state) · Recharts · Motion · next-intl (EN/TR) · next-themes · @react-pdf/renderer · pnpm.
PROJECT_SPEC.md is the source of truth for scope, architecture, the agent pipeline, the data model, and the build plan. CLAUDE.md records the hard architecture rules.
Apache License 2.0 — see LICENSE. The self-hosted fonts
(frontend/public/fonts/) are under the SIL Open Font License 1.1, and the
demo snapshots in backend/seeds/ are archived third-party web pages outside
this license — see NOTICE.