{"repo": "asdqwe21212/pr-review-agent", "files": {"README.md": {"8b64e330a171fe0425a68a626e42aaa418c72df5": {"t": 1785746410.6272058, "n": 17831, "h": "53e4d9213c4e4509", "c": "# PR Review Agent\n\nAI-powered pull request code review system using Claude API with multi-turn reasoning.\nDesigned for teams handling 50+ PRs/week with ~2M tokens weekly budget.\n\n## Architecture\n\n```\n┌──────────────────────────────────────────────────────────────────────┐\n│ PR Review Agent │\n│ │\n│ GitHub PR ──► Agent ──► Turn 1: Bug & Security Scan │\n│ │──► Turn 2: Performance & Quality │\n│ │──► Turn 3: Structured JSON Report │\n│ └──► Post Comment to GitHub PR │\n│ │\n│ FastAPI Server ──► REST API ──► Dashboard UI │\n│ GitHub Webhook ──► Auto-trigger on PR open/update/label │\n│ Task Queue (arq) ──► Async Processing ──► Worker Pool │\n│ Job Store ──► In-Memory / File (JSONL) / Redis │\n│ Monitoring ──► Prometheus Metrics ──► Grafana │\n└──────────────────────────────────────────────────────────────────────┘\n```\n\n## Quick Start\n\n### 1. Clone & Install\n\n```bash\ngit clone \ncd pr-review-agent\npip install -r requirements.txt\n```\n\n### 2. Configure Environment\n\n```bash\ncp config/.env.example config/.env\n# Edit config/.env with your API keys\n```\n\nRequired:\n- `ANTHROPIC_API_KEY` (or any provider key — see [Multi-Provider LLM Support](#multi-provider-llm-support)) — for Anthropic\n- `GITHUB_TOKEN` — GitHub Personal Access Token with `repo` scope\n\n### 3a. CLI Usage (Quick)\n\n```bash\ncd backend\n# Load env vars\nexport $(cat ../config/.env | xargs) # Linux/Mac\n# Or on Windows: use set or a .env loader\n\n# Review a PR (posts comment to GitHub)\npython cli.py --repo owner/repo-name --pr 42\n\n# Dry run (no GitHub comment)\npython cli.py --repo owner/repo-name --pr 42 --no-comment\n\n# Save full JSON result\npython cli.py --repo owner/repo-name --pr 42 --output result.json\n```\n\n### 3b. Web Server + Dashboard\n\n```bash\ncd backend\nexport $(cat ../config/.env | xargs)\npython server.py\n\n# Open http://localhost:8000\n```\n\n### 3c. Worker (Production)\n\nFor production deployments with async task queue:\n\n```bash\n# Start the arq worker\npython scripts/start_worker.py\n\n# Worker reads REDIS_URL and WORKER_CONCURRENCY from environment\n```\n\n### 3c. GitHub Actions (Automated)\n\nCopy `.github/workflows/pr-review.yml` to your repository. Add these secrets:\n- `ANTHROPIC_API_KEY`\n- `GITHUB_TOKEN`\n\nEvery PR open/sync/reopen triggers an automatic review.\n\n## Multi-Provider LLM Support\n\nThe agent is not locked to Anthropic — the LLM provider is configurable end-to-end. The backend speaks two wire formats:\n\n| Backend format | Used by |\n|----------------|---------|\n| `anthropic` | Native Anthropic Messages API. The **Base URL** is optional — set it to point at any Anthropic-protocol proxy. The **Model** field accepts any id (e.g. `claude-3-5-sonnet-20241022`, `claude-3-opus-20240229`, `claude-3-haiku-20240307`, or a custom id your proxy exposes). |\n| `openai-compatible` | OpenAI Chat Completions and any drop-in compatible endpoint (OpenAI, DeepSeek, Moonshot, Zhipu GLM, Ollama, vLLM, Together, Groq, OpenRouter, …). The **Model** field accepts any id (e.g. `gpt-4o`, `gpt-4o-mini`, `deepseek-chat`, `llama3.1`, fine-tunes, etc.). |\n\nIn short: **both formats support custom model names**, and the Anthropic format additionally supports pointing at a custom endpoint (LiteLLM, AWS Bedrock, gateway, etc.).\n\n### Switching providers from the dashboard\n\nThe fastest way is via the web UI. Start the server (`python backend/server.py`) and open `http://localhost:8000`. Click **Settings** in the top-right corner to open the LLM configuration dialog:\n\n- Pick a **Provider** preset:\n - **Anthropic (Claude)** — official Anthropic endpoint, no base URL needed\n - **Custom (Anthropic-compatible)** — point at any Anthropic-protocol proxy and supply a model id\n - **OpenAI / DeepSeek / Moonshot / 智谱 GLM / Ollama / Custom (OpenAI-compatible)** — covers the OpenAI wire format\n- The dialog auto-fills a sensible **Base URL** and **Model** for each preset; you can override either with your own value\n- Paste your **API Key** (stored server-side only; never echoed back to the browser)\n- Adjust **Temperature** if you want\n\nThe provider is persisted at `data/llm_config.json` and is hot-reloaded on every review job, so the next PR you submit uses the new provider.\n\n### Dashboard language\n\nThe dashboard ships with **English** and **简体中文** and exposes a one-click toggle in the header (the pill that reads `EN` or `中`):\n\n- The active language is stored in `localStorage` under `pr-review.lang`, so the choice persists across reloads and browser restarts\n- On first visit the language is auto-detected from `navigator.language` — anything starting with `zh` lands on Chinese, anything else on English\n- Switching languages re-translates all dashboard chrome, the Settings dialog (including provider notes and hints), the job list status pills, the review detail labels, the loading messages, and the error/alert text\n- LLM-generated review content (the `summary`, `bugs`, `security`, `performance`, `quality`, `positives` fields) is left in the language the model produced it in — that is controlled by your prompt and provider, not by the UI language\n\n### Switching providers via environment variables\n\nIf you'd rather configure at deploy time, the same fields can be set as env vars. The dashboard `data/llm_config.json` (if it exists) takes precedence over the env.\n\n| Env var | Description |\n|---------|-------------|\n| `LLM_PROVIDER` | `anthropic` or `openai-compatible` |\n| `LLM_API_KEY` | Provider API key. `ANTHROPIC_API_KEY` is still respected as a fallback for `anthropic`. |\n| `LLM_MODEL` | Model id — free-form. `claude-sonnet-4-20250514`, `claude-3-opus-20240229`, `gpt-4o-mini`, `deepseek-chat`, `llama3.1`, fine-tunes, custom proxy ids, etc. |\n| `LLM_BASE_URL` | Optional for `anthropic` (omit to use the official endpoint), required for `openai-compatible`. e.g. `https://api.openai.com/v1`, `http://localhost:11434/v1`, `https://your-litellm-proxy.example.com` |\n| `LLM_TEMPERATURE` | `0.0` – `2.0` (default `0.2`) |\n\n### Example: DeepSeek via env\n\n```bash\nexport LLM_PROVIDER=openai-compatible\nexport LLM_API_KEY=sk-...\nexport LLM_BASE_URL=https://api.deepseek.com/v1\nexport LLM_MODEL=deepseek-chat\n```\n\n### Example: Local Ollama\n\n```bash\nexport LLM_PROVIDER=openai-compatible\nexport LLM_BASE_URL=http://localhost:11434/v1\nexport LLM_MODEL=llama3.1\n# Ollama ignores the key, but the field must be non-empty to be sent\nexport LLM_API_KEY=ollama\n```\n\n### Example: Anthropic-compatible proxy (LiteLLM, gateway, …)\n\n```bash\nexport LLM_PROVIDER=anthropic\nexport LLM_API_KEY=sk-anything-the-proxy-expects\nexport LLM_BASE_URL=https://your-litellm-proxy.example.com\n# Any model id your proxy exposes — LiteLLM accepts Anthropic model names,\n# OpenAI model names, or any custom routing key\nexport LLM_MODEL=claude-3-5-sonnet-20241022\n```\n\nThe agent will speak the Anthropic Messages protocol but talk to your proxy instead of `api.anthropic.com`. Useful for centralized key management, audit logging, on-prem deployments, or routing through Cloudflare / VPC.\n\n### How requests are dispatched\n\n| Provider type | Endpoint called | Auth header |\n|---------------|-----------------|-------------|\n| `anthropic` (no `base_url`) | Anthropic SDK → `https://api.anthropic.com` | handled by SDK |\n| `anthropic` (with `base_url`) | Anthropic SDK → `{base_url}` | handled by SDK |\n| `openai-compatible` | `POST {base_url}/chat/completions` | `Authorization: Bearer ` |\n\nReview output structure is identical across providers — switching providers does not change the GitHub comment format, the score, or the issue categories the agent reports.\n\n## How Multi-Turn Reasoning Works\n\nThe agent uses 3 sequential Claude API calls per review, each building on prior context:\n\n| Turn | Focus | Approach |\n|------|-------|----------|\n| 1 | Bugs & Security | Deep scan: logic errors, null refs, injection risks, auth flaws |\n| 2 | Performance & Quality | O(n^2) detection, N+1 queries, naming, complexity, test gaps |\n| 3 | Structured Report | Synthesize all findings into final JSON review object |\n\n## Review Output Structure\n\n```json\n{\n \"summary\": \"High-level assessment\",\n \"severity\": \"critical|high|medium|low\",\n \"overall_score\": 0-100,\n \"approve\": true|false,\n \"bugs\": [{\"line\": \"file.py:42\", \"description\": \"...\", \"suggestion\": \"...\"}],\n \"security\": [...],\n \"performance\": [...],\n \"quality\": [...],\n \"positives\": [\"Good error handling in X\", ...]\n}\n```\n\n## Configuration Reference\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `ANTHROPIC_API_KEY` | (required) | Anthropic API key (fallback for `LLM_API_KEY` when provider is `anthropic`) |\n| `LLM_PROVIDER` | `anthropic` | `anthropic` or `openai-compatible` |\n| `LLM_API_KEY` | — | Provider API key. Overrides `ANTHROPIC_API_KEY` when set. |\n| `LLM_MODEL` | `claude-sonnet-4-20250514` | Model id sent to the provider |\n| `LLM_BASE_URL` | — | Required for `openai-compatible` providers |\n| `LLM_TEMPERATURE` | `0.2` | Sampling temperature (0.0 – 2.0) |\n| `GITHUB_TOKEN` | (required) | GitHub PAT with `repo` scope |\n| `GITHUB_WEBHOOK_SECRET` | (required for webhooks) | HMAC-SHA256 webhook verification |\n| `API_KEY` | — | Optional API key for endpoint authentication |\n| `CORS_ORIGINS` | `http://localhost:8000,http://127.0.0.1:8000` | Comma-separated allowed CORS origins |\n| `CLAUDE_MODEL` | `claude-sonnet-4-20250514` | Legacy alias for `LLM_MODEL` |\n| `PR_REVIEW_MODE` | `full` | `full` / `diff-only` / `auto` |\n| `PR_REVIEW_MAX_FILES` | `20` | Max files to include in context |\n| `PR_REVIEW_MAX_FILE_CHARS` | `5000` | Max chars per file content |\n| `PR_REVIEW_MAX_CONTEXT_CHARS` | `80000` | Total context size cap |\n| `PR_REVIEW_FULL_CONTENT_THRESHOLD` | `500` | Max additions to fetch full content |\n| `PR_REVIEW_CONTEXT_ENABLED` | `true` | Load CONTEXT.md from PR branch |\n| `TOKEN_BUDGET_WEEKLY` | `2000000` | Weekly token budget for alerts |\n| `TOKEN_BUDGET_ALERT_THRESHOLD` | `0.8` | Alert at 80% budget consumed |\n| `LOG_LEVEL` | `INFO` | Logging level |\n| `REDIS_URL` | — | Redis connection (required for production) |\n| `WORKER_CONCURRENCY` | `10` | Max concurrent review tasks per worker |\n| `MAX_QUEUE_SIZE` | `100` | Max pending reviews in queue |\n| `ENVIRONMENT` | `production` | Environment identifier for logging |\n| `SENTRY_DSN` | — | Optional Sentry error tracking DSN |\n\n## API Endpoints\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `POST` | `/api/review` | Start a new review job |\n| `GET` | `/api/jobs` | List all jobs (filter: `?repo=`, `?days=`) |\n| `GET` | `/api/jobs/{id}` | Get job status & result |\n| `GET` | `/api/stats` | Aggregate statistics (filter: `?days=`) |\n| `GET` | `/api/config/llm` | Read the current LLM provider config (api_key is masked) |\n| `POST` | `/api/config/llm` | Persist the LLM provider config; leave `api_key` blank to keep the existing key |\n| `GET` | `/api/metrics` | Combined quality + throughput + tokens |\n| `GET` | `/api/metrics/tokens` | Weekly budget, daily breakdown, per-review stats |\n| `GET` | `/api/metrics/quality` | Score distribution, severity, approval rate |\n| `GET` | `/api/metrics/throughput` | Reviews/day, files reviewed |\n| `POST` | `/webhook/github` | GitHub webhook handler |\n| `GET` | `/api/health` | Health check (Redis, queue status) |\n| `GET` | `/api/health/ready` | Kubernetes readiness probe |\n| `GET` | `/api/health/live` | Kubernetes liveness probe |\n| `GET` | `/metrics` | Prometheus metrics endpoint |\n\n## Token Usage\n\nTypical per-PR consumption:\n- **Small PR** (< 5 files): ~15K–30K tokens\n- **Medium PR** (5–20 files): ~40K–80K tokens\n- **Large PR** (20+ files): ~80K–150K tokens\n\nUse `PR_REVIEW_MODE=diff-only` to cut token usage ~50% for large PRs.\n\n## Production Deployment\n\n### Job Store\n\nThree backends, auto-selected:\n\n| Backend | Trigger | Persistence |\n|---------|---------|-------------|\n| `InMemoryJobStore` | (fallback) | Lost on restart |\n| `FileJobStore` | Default | JSONL file at `data/jobs.jsonl` |\n| `RedisJobStore` | Set `REDIS_URL` | Redis, with 7-day TTL, status indexing |\n\n### Task Queue (arq + Redis)\n\nFor production workloads, the system uses an async task queue:\n\n```bash\n# Start the API server\npython server.py\n\n# Start worker(s) in separate processes\npython scripts/start_worker.py\n```\n\nFeatures:\n- Async task processing with automatic retries (3 attempts)\n- Request deduplication prevents duplicate reviews\n- Result caching (1 hour TTL based on PR head SHA)\n- Graceful shutdown with active task tracking\n\n### Process Manager\n\n```bash\n# Single worker (fine for < 100 PRs/week)\npython server.py\n\n# Multi-worker production with gunicorn\ngunicorn server:app -w 4 -k uvicorn.workers.UvicornWorker\n```\n\n### Token Budget Monitoring\n\nSet `TOKEN_BUDGET_WEEKLY` and check the dashboard for real-time budget tracking.\nThe stats bar shows a color-coded progress bar: green (< 50%), yellow (50–80%), red (> 80%).\n\n## Security\n\n### API Authentication\n\nSet `API_KEY` environment variable to enable bearer token authentication on all `/api/*` endpoints. When set, requests must include:\n\n```\nAuthorization: Bearer your-api-key-here\n```\n\n### Webhook Security\n\n`GITHUB_WEBHOOK_SECRET` is **required** for webhook endpoints. Webhooks without a valid HMAC-SHA256 signature will be rejected. Generate a secret with:\n\n```bash\nopenssl rand -hex 32\n```\n\n### CORS Configuration\n\nConfigure allowed origins with `CORS_ORIGINS` (comma-separated). Defaults to localhost only.\n\n## GitHub Token Permissions\n\nMinimum required scopes: `repo` (private repos) or `public_repo` (public). This allows reading PR diffs and posting review comments.\n\n## Project-Specific Review Rules\n\nAdd `.pr-review-rules.md` to your repository root (or `.github/pr-review-rules.md`). The agent will inject these rules into the review prompt. Example:\n\n```markdown\n# PR Review Rules\n- All API endpoints must validate input with Pydantic models\n- Database queries must use parameterized queries\n- All new functions must have type hints\n- No console.log or print() in production code\n```\n\nAlso supported: `CONTEXT.md` / `.pr-review-context.md` / `CONTRIBUTING.md` for general project context.\n\n## Changelog\n\n### v1.3.0 - Multi-Provider LLM Support\n\n**New Features:**\n- Dashboard **Settings** dialog to switch LLM provider, model, base URL, API key, and temperature without restarting the server\n- Provider presets: Anthropic, OpenAI, DeepSeek, Moonshot (Kimi), Zhipu GLM, Ollama, Custom OpenAI-compatible, **Custom Anthropic-compatible** (LiteLLM, gateways, etc.)\n- Custom model support: the **Model** field is free-form on both wire formats, so any model id works (claude-3-opus-*, claude-3-haiku-*, gpt-4o-*, deepseek-*, llama*, fine-tunes, proxy routing keys, …)\n- Anthropic provider now accepts an optional **Base URL**, so you can route Anthropic-protocol traffic through a proxy while keeping the same review output\n- Header status indicator reflects the active provider and shows a warning dot when no API key is set\n- Dashboard language toggle: one-click switch between **English** and **简体中文** in the header, with `localStorage` persistence and `navigator.language` auto-detection on first visit\n- Added `LLM_PROVIDER`, `LLM_API_KEY`, `LLM_MODEL`, `LLM_BASE_URL`, `LLM_TEMPERATURE` environment variables (dashboard `data/llm_config.json` still wins when present)\n\n**Notes:**\n- No breaking changes. Existing `ANTHROPIC_API_KEY` + `CLAUDE_MODEL` deployments continue to work without any edit.\n\n### v1.2.0 - Production Readiness\n\n**New Features:**\n- Async task queue with arq + Redis for concurrent processing\n- Request deduplication to prevent duplicate reviews\n- Result caching with SHA-based keys (1 hour TTL)\n- Prometheus metrics endpoint (`/metrics`)\n- Kubernetes health probes (`/api/health/ready`, `/api/health/live`)\n- Graceful shutdown with active task tracking\n- Thread-safe singleton pattern for all managers\n- Comprehensive unit tests for core modules\n\n**Bug Fixes:**\n- Fixed deprecated `datetime.utcnow()` (Python 3.12+ compatible)\n- Fixed frontend polling memory leak using AbortController\n- Fixed asyncio event loop handling with `asyncio.to_thread()`\n- Enhanced RedisJobStore with status indexing for faster queries\n\n**Infrastructure:**\n- Added `backend/tasks.py` - arq task definitions\n- Added `backend/dedup.py` - request deduplication\n- Added `backend/cache.py` - result caching\n- Added `backend/shutdown.py` - graceful shutdown\n- Added `backend/metrics_middleware.py` - Prometheus middleware\n- Added `backend/singleton.py` - thread-safe singleton decorator\n- Added `scripts/start_worker.py` - Worker startup script\n- Added comprehensive test suite in `tests/`\n\n### v1.1.0 - Security & Reliability Improvements\n\n**Security Fixes:**\n- Fixed XSS vulnerability in frontend dashboard\n- Added optional API key authentication (`API_KEY` env var)\n- Webhook signature verification now required (no more bypass)\n- CORS origins now configurable (defaults to localhost only)\n\n**Bug Fixes:**\n- Fixed job ID collision when submitting multiple reviews rapidly\n- Fixed KeyError crash in stats endpoint with incomplete data\n- Fixed fragile JSON parsing for Claude responses\n- Fixed metrics dead code in throughput calculation\n- Fixed token tracker week start normalization\n- Updated deprecated `datetime.utcnow()` calls\n- Fixed GitHub Actions workflow to install all dependencies\n\n**New Features:**\n- Added `CORS_ORIGINS` configuration\n- Added `API_KEY` for endpoint authentication\n- Added `backend/__init__.py` for proper module imports\n"}, "6ca0444b043338cb98f4086e2a89ae57b54ef073": {"t": 1785763651.3577068, "n": 16764, "h": "808ee402168ab157", "c": "# PR Review Agent\n\nAI-powered pull request code review system using Claude API with multi-turn reasoning.\nDesigned for teams handling 50+ PRs/week with ~2M tokens weekly budget.\n\n## Architecture\n\n```\n┌──────────────────────────────────────────────────────────────────────┐\n│ PR Review Agent │\n│ │\n│ GitHub PR ──► Agent ──► Turn 1: Bug & Security Scan │\n│ │──► Turn 2: Performance & Quality │\n│ │──► Turn 3: Structured JSON Report │\n│ └──► Post Comment to GitHub PR │\n│ │\n│ FastAPI Server ──► REST API ──► Dashboard UI │\n│ GitHub Webhook ──► Auto-trigger on PR open/update/label │\n│ Task Queue (arq) ──► Async Processing ──► Worker Pool │\n│ Job Store ──► In-Memory / File (JSONL) / Redis │\n│ Monitoring ──► Prometheus Metrics ──► Grafana │\n└──────────────────────────────────────────────────────────────────────┘\n```\n\n## Quick Start\n\n### 1. Clone & Install\n\n```bash\ngit clone \ncd pr-review-agent\npip install -r requirements.txt\n```\n\n### 2. Configure Environment\n\n```bash\ncp config/.env.example config/.env\n# Edit config/.env with your API keys\n```\n\nRequired:\n- `ANTHROPIC_API_KEY` (or any provider key — see [Multi-Provider LLM Support](#multi-provider-llm-support)) — for Anthropic\n- `GITHUB_TOKEN` — GitHub Personal Access Token with `repo` scope\n\n### 3a. CLI Usage (Quick)\n\n```bash\ncd backend\n# Load env vars\nexport $(cat ../config/.env | xargs) # Linux/Mac\n# Or on Windows: use set or a .env loader\n\n# Review a PR (posts comment to GitHub)\npython cli.py --repo owner/repo-name --pr 42\n\n# Dry run (no GitHub comment)\npython cli.py --repo owner/repo-name --pr 42 --no-comment\n\n# Save full JSON result\npython cli.py --repo owner/repo-name --pr 42 --output result.json\n```\n\n### 3b. Web Server + Dashboard\n\n```bash\ncd backend\nexport $(cat ../config/.env | xargs)\npython server.py\n\n# Open http://localhost:8000\n```\n\n### 3c. Worker (Production)\n\nFor production deployments with async task queue:\n\n```bash\n# Start the arq worker\npython scripts/start_worker.py\n\n# Worker reads REDIS_URL and WORKER_CONCURRENCY from environment\n```\n\n### 3c. GitHub Actions (Automated)\n\nCopy `.github/workflows/pr-review.yml` to your repository. Add these secrets:\n- `ANTHROPIC_API_KEY`\n- `GITHUB_TOKEN`\n\nEvery PR open/sync/reopen triggers an automatic review.\n\n## Multi-Provider LLM Support\n\nThe agent is not locked to Anthropic — the LLM provider is configurable end-to-end. The backend speaks two wire formats:\n\n| Backend format | Used by |\n|----------------|---------|\n| `anthropic` | Native Anthropic Messages API. The **Base URL** is optional — set it to point at any Anthropic-protocol proxy. The **Model** field accepts any id (e.g. `claude-3-5-sonnet-20241022`, `claude-3-opus-20240229`, `claude-3-haiku-20240307`, or a custom id your proxy exposes). |\n| `openai-compatible` | OpenAI Chat Completions and any drop-in compatible endpoint (OpenAI, DeepSeek, Moonshot, Zhipu GLM, Ollama, vLLM, Together, Groq, OpenRouter, …). The **Model** field accepts any id (e.g. `gpt-4o`, `gpt-4o-mini`, `deepseek-chat`, `llama3.1`, fine-tunes, etc.). |\n\nIn short: **both formats support custom model names**, and the Anthropic format additionally supports pointing at a custom endpoint (LiteLLM, AWS Bedrock, gateway, etc.).\n\n### Switching providers from the dashboard\n\nThe fastest way is via the web UI. Start the server (`python backend/server.py`) and open `http://localhost:8000`. Click **Settings** in the top-right corner to open the LLM configuration dialog:\n\n- Pick a **Provider** preset:\n - **Anthropic (Claude)** — official Anthropic endpoint, no base URL needed\n - **Custom (Anthropic-compatible)** — point at any Anthropic-protocol proxy and supply a model id\n - **OpenAI / DeepSeek / Moonshot / 智谱 GLM / Ollama / Custom (OpenAI-compatible)** — covers the OpenAI wire format\n- The dialog auto-fills a sensible **Base URL** and **Model** for each preset; you can override either with your own value\n- Paste your **API Key** (stored server-side only; never echoed back to the browser)\n- Adjust **Temperature** if you want\n\nThe provider is persisted at `data/llm_config.json` and is hot-reloaded on every review job, so the next PR you submit uses the new provider.\n\n### Switching providers via environment variables\n\nIf you'd rather configure at deploy time, the same fields can be set as env vars. The dashboard `data/llm_config.json` (if it exists) takes precedence over the env.\n\n| Env var | Description |\n|---------|-------------|\n| `LLM_PROVIDER` | `anthropic` or `openai-compatible` |\n| `LLM_API_KEY` | Provider API key. `ANTHROPIC_API_KEY` is still respected as a fallback for `anthropic`. |\n| `LLM_MODEL` | Model id — free-form. `claude-sonnet-4-20250514`, `claude-3-opus-20240229`, `gpt-4o-mini`, `deepseek-chat`, `llama3.1`, fine-tunes, custom proxy ids, etc. |\n| `LLM_BASE_URL` | Optional for `anthropic` (omit to use the official endpoint), required for `openai-compatible`. e.g. `https://api.openai.com/v1`, `http://localhost:11434/v1`, `https://your-litellm-proxy.example.com` |\n| `LLM_TEMPERATURE` | `0.0` – `2.0` (default `0.2`) |\n\n### Example: DeepSeek via env\n\n```bash\nexport LLM_PROVIDER=openai-compatible\nexport LLM_API_KEY=sk-...\nexport LLM_BASE_URL=https://api.deepseek.com/v1\nexport LLM_MODEL=deepseek-chat\n```\n\n### Example: Local Ollama\n\n```bash\nexport LLM_PROVIDER=openai-compatible\nexport LLM_BASE_URL=http://localhost:11434/v1\nexport LLM_MODEL=llama3.1\n# Ollama ignores the key, but the field must be non-empty to be sent\nexport LLM_API_KEY=ollama\n```\n\n### Example: Anthropic-compatible proxy (LiteLLM, gateway, …)\n\n```bash\nexport LLM_PROVIDER=anthropic\nexport LLM_API_KEY=sk-anything-the-proxy-expects\nexport LLM_BASE_URL=https://your-litellm-proxy.example.com\n# Any model id your proxy exposes — LiteLLM accepts Anthropic model names,\n# OpenAI model names, or any custom routing key\nexport LLM_MODEL=claude-3-5-sonnet-20241022\n```\n\nThe agent will speak the Anthropic Messages protocol but talk to your proxy instead of `api.anthropic.com`. Useful for centralized key management, audit logging, on-prem deployments, or routing through Cloudflare / VPC.\n\n### How requests are dispatched\n\n| Provider type | Endpoint called | Auth header |\n|---------------|-----------------|-------------|\n| `anthropic` (no `base_url`) | Anthropic SDK → `https://api.anthropic.com` | handled by SDK |\n| `anthropic` (with `base_url`) | Anthropic SDK → `{base_url}` | handled by SDK |\n| `openai-compatible` | `POST {base_url}/chat/completions` | `Authorization: Bearer ` |\n\nReview output structure is identical across providers — switching providers does not change the GitHub comment format, the score, or the issue categories the agent reports.\n\n## How Multi-Turn Reasoning Works\n\nThe agent uses 3 sequential Claude API calls per review, each building on prior context:\n\n| Turn | Focus | Approach |\n|------|-------|----------|\n| 1 | Bugs & Security | Deep scan: logic errors, null refs, injection risks, auth flaws |\n| 2 | Performance & Quality | O(n^2) detection, N+1 queries, naming, complexity, test gaps |\n| 3 | Structured Report | Synthesize all findings into final JSON review object |\n\n## Review Output Structure\n\n```json\n{\n \"summary\": \"High-level assessment\",\n \"severity\": \"critical|high|medium|low\",\n \"overall_score\": 0-100,\n \"approve\": true|false,\n \"bugs\": [{\"line\": \"file.py:42\", \"description\": \"...\", \"suggestion\": \"...\"}],\n \"security\": [...],\n \"performance\": [...],\n \"quality\": [...],\n \"positives\": [\"Good error handling in X\", ...]\n}\n```\n\n## Configuration Reference\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `ANTHROPIC_API_KEY` | (required) | Anthropic API key (fallback for `LLM_API_KEY` when provider is `anthropic`) |\n| `LLM_PROVIDER` | `anthropic` | `anthropic` or `openai-compatible` |\n| `LLM_API_KEY` | — | Provider API key. Overrides `ANTHROPIC_API_KEY` when set. |\n| `LLM_MODEL` | `claude-sonnet-4-20250514` | Model id sent to the provider |\n| `LLM_BASE_URL` | — | Required for `openai-compatible` providers |\n| `LLM_TEMPERATURE` | `0.2` | Sampling temperature (0.0 – 2.0) |\n| `GITHUB_TOKEN` | (required) | GitHub PAT with `repo` scope |\n| `GITHUB_WEBHOOK_SECRET` | (required for webhooks) | HMAC-SHA256 webhook verification |\n| `API_KEY` | — | Optional API key for endpoint authentication |\n| `CORS_ORIGINS` | `http://localhost:8000,http://127.0.0.1:8000` | Comma-separated allowed CORS origins |\n| `CLAUDE_MODEL` | `claude-sonnet-4-20250514` | Legacy alias for `LLM_MODEL` |\n| `PR_REVIEW_MODE` | `full` | `full` / `diff-only` / `auto` |\n| `PR_REVIEW_MAX_FILES` | `20` | Max files to include in context |\n| `PR_REVIEW_MAX_FILE_CHARS` | `5000` | Max chars per file content |\n| `PR_REVIEW_MAX_CONTEXT_CHARS` | `80000` | Total context size cap |\n| `PR_REVIEW_FULL_CONTENT_THRESHOLD` | `500` | Max additions to fetch full content |\n| `PR_REVIEW_CONTEXT_ENABLED` | `true` | Load CONTEXT.md from PR branch |\n| `TOKEN_BUDGET_WEEKLY` | `2000000` | Weekly token budget for alerts |\n| `TOKEN_BUDGET_ALERT_THRESHOLD` | `0.8` | Alert at 80% budget consumed |\n| `LOG_LEVEL` | `INFO` | Logging level |\n| `REDIS_URL` | — | Redis connection (required for production) |\n| `WORKER_CONCURRENCY` | `10` | Max concurrent review tasks per worker |\n| `MAX_QUEUE_SIZE` | `100` | Max pending reviews in queue |\n| `ENVIRONMENT` | `production` | Environment identifier for logging |\n| `SENTRY_DSN` | — | Optional Sentry error tracking DSN |\n\n## API Endpoints\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `POST` | `/api/review` | Start a new review job |\n| `GET` | `/api/jobs` | List all jobs (filter: `?repo=`, `?days=`) |\n| `GET` | `/api/jobs/{id}` | Get job status & result |\n| `GET` | `/api/stats` | Aggregate statistics (filter: `?days=`) |\n| `GET` | `/api/config/llm` | Read the current LLM provider config (api_key is masked) |\n| `POST` | `/api/config/llm` | Persist the LLM provider config; leave `api_key` blank to keep the existing key |\n| `GET` | `/api/metrics` | Combined quality + throughput + tokens |\n| `GET` | `/api/metrics/tokens` | Weekly budget, daily breakdown, per-review stats |\n| `GET` | `/api/metrics/quality` | Score distribution, severity, approval rate |\n| `GET` | `/api/metrics/throughput` | Reviews/day, files reviewed |\n| `POST` | `/webhook/github` | GitHub webhook handler |\n| `GET` | `/api/health` | Health check (Redis, queue status) |\n| `GET` | `/api/health/ready` | Kubernetes readiness probe |\n| `GET` | `/api/health/live` | Kubernetes liveness probe |\n| `GET` | `/metrics` | Prometheus metrics endpoint |\n\n## Token Usage\n\nTypical per-PR consumption:\n- **Small PR** (< 5 files): ~15K–30K tokens\n- **Medium PR** (5–20 files): ~40K–80K tokens\n- **Large PR** (20+ files): ~80K–150K tokens\n\nUse `PR_REVIEW_MODE=diff-only` to cut token usage ~50% for large PRs.\n\n## Production Deployment\n\n### Job Store\n\nThree backends, auto-selected:\n\n| Backend | Trigger | Persistence |\n|---------|---------|-------------|\n| `InMemoryJobStore` | (fallback) | Lost on restart |\n| `FileJobStore` | Default | JSONL file at `data/jobs.jsonl` |\n| `RedisJobStore` | Set `REDIS_URL` | Redis, with 7-day TTL, status indexing |\n\n### Task Queue (arq + Redis)\n\nFor production workloads, the system uses an async task queue:\n\n```bash\n# Start the API server\npython server.py\n\n# Start worker(s) in separate processes\npython scripts/start_worker.py\n```\n\nFeatures:\n- Async task processing with automatic retries (3 attempts)\n- Request deduplication prevents duplicate reviews\n- Result caching (1 hour TTL based on PR head SHA)\n- Graceful shutdown with active task tracking\n\n### Process Manager\n\n```bash\n# Single worker (fine for < 100 PRs/week)\npython server.py\n\n# Multi-worker production with gunicorn\ngunicorn server:app -w 4 -k uvicorn.workers.UvicornWorker\n```\n\n### Token Budget Monitoring\n\nSet `TOKEN_BUDGET_WEEKLY` and check the dashboard for real-time budget tracking.\nThe stats bar shows a color-coded progress bar: green (< 50%), yellow (50–80%), red (> 80%).\n\n## Security\n\n### API Authentication\n\nSet `API_KEY` environment variable to enable bearer token authentication on all `/api/*` endpoints. When set, requests must include:\n\n```\nAuthorization: Bearer your-api-key-here\n```\n\n### Webhook Security\n\n`GITHUB_WEBHOOK_SECRET` is **required** for webhook endpoints. Webhooks without a valid HMAC-SHA256 signature will be rejected. Generate a secret with:\n\n```bash\nopenssl rand -hex 32\n```\n\n### CORS Configuration\n\nConfigure allowed origins with `CORS_ORIGINS` (comma-separated). Defaults to localhost only.\n\n## GitHub Token Permissions\n\nMinimum required scopes: `repo` (private repos) or `public_repo` (public). This allows reading PR diffs and posting review comments.\n\n## Project-Specific Review Rules\n\nAdd `.pr-review-rules.md` to your repository root (or `.github/pr-review-rules.md`). The agent will inject these rules into the review prompt. Example:\n\n```markdown\n# PR Review Rules\n- All API endpoints must validate input with Pydantic models\n- Database queries must use parameterized queries\n- All new functions must have type hints\n- No console.log or print() in production code\n```\n\nAlso supported: `CONTEXT.md` / `.pr-review-context.md` / `CONTRIBUTING.md` for general project context.\n\n## Changelog\n\n### v1.3.0 - Multi-Provider LLM Support\n\n**New Features:**\n- Dashboard **Settings** dialog to switch LLM provider, model, base URL, API key, and temperature without restarting the server\n- Provider presets: Anthropic, OpenAI, DeepSeek, Moonshot (Kimi), Zhipu GLM, Ollama, Custom OpenAI-compatible, **Custom Anthropic-compatible** (LiteLLM, gateways, etc.)\n- Custom model support: the **Model** field is free-form on both wire formats, so any model id works (claude-3-opus-*, claude-3-haiku-*, gpt-4o-*, deepseek-*, llama*, fine-tunes, proxy routing keys, …)\n- Anthropic provider now accepts an optional **Base URL**, so you can route Anthropic-protocol traffic through a proxy while keeping the same review output\n- Header status indicator reflects the active provider and shows a warning dot when no API key is set\n- Added `LLM_PROVIDER`, `LLM_API_KEY`, `LLM_MODEL`, `LLM_BASE_URL`, `LLM_TEMPERATURE` environment variables (dashboard `data/llm_config.json` still wins when present)\n\n**Notes:**\n- No breaking changes. Existing `ANTHROPIC_API_KEY` + `CLAUDE_MODEL` deployments continue to work without any edit.\n\n### v1.2.0 - Production Readiness\n\n**New Features:**\n- Async task queue with arq + Redis for concurrent processing\n- Request deduplication to prevent duplicate reviews\n- Result caching with SHA-based keys (1 hour TTL)\n- Prometheus metrics endpoint (`/metrics`)\n- Kubernetes health probes (`/api/health/ready`, `/api/health/live`)\n- Graceful shutdown with active task tracking\n- Thread-safe singleton pattern for all managers\n- Comprehensive unit tests for core modules\n\n**Bug Fixes:**\n- Fixed deprecated `datetime.utcnow()` (Python 3.12+ compatible)\n- Fixed frontend polling memory leak using AbortController\n- Fixed asyncio event loop handling with `asyncio.to_thread()`\n- Enhanced RedisJobStore with status indexing for faster queries\n\n**Infrastructure:**\n- Added `backend/tasks.py` - arq task definitions\n- Added `backend/dedup.py` - request deduplication\n- Added `backend/cache.py` - result caching\n- Added `backend/shutdown.py` - graceful shutdown\n- Added `backend/metrics_middleware.py` - Prometheus middleware\n- Added `backend/singleton.py` - thread-safe singleton decorator\n- Added `scripts/start_worker.py` - Worker startup script\n- Added comprehensive test suite in `tests/`\n\n### v1.1.0 - Security & Reliability Improvements\n\n**Security Fixes:**\n- Fixed XSS vulnerability in frontend dashboard\n- Added optional API key authentication (`API_KEY` env var)\n- Webhook signature verification now required (no more bypass)\n- CORS origins now configurable (defaults to localhost only)\n\n**Bug Fixes:**\n- Fixed job ID collision when submitting multiple reviews rapidly\n- Fixed KeyError crash in stats endpoint with incomplete data\n- Fixed fragile JSON parsing for Claude responses\n- Fixed metrics dead code in throughput calculation\n- Fixed token tracker week start normalization\n- Updated deprecated `datetime.utcnow()` calls\n- Fixed GitHub Actions workflow to install all dependencies\n\n**New Features:**\n- Added `CORS_ORIGINS` configuration\n- Added `API_KEY` for endpoint authentication\n- Added `backend/__init__.py` for proper module imports\n"}, "6bc23a0e70c52ae7a1db74230b7dc53dfd3872a8": {"t": 1785763653.0626216, "n": 10575, "h": "0715bed0ee4f0583", "c": "# PR Review Agent\n\nAI-powered pull request code review system using Claude API with multi-turn reasoning.\nDesigned for teams handling 50+ PRs/week with ~2M tokens weekly budget.\n\n## Architecture\n\n```\n┌──────────────────────────────────────────────────────────────────────┐\n│ PR Review Agent │\n│ │\n│ GitHub PR ──► Agent ──► Turn 1: Bug & Security Scan │\n│ │──► Turn 2: Performance & Quality │\n│ │──► Turn 3: Structured JSON Report │\n│ └──► Post Comment to GitHub PR │\n│ │\n│ FastAPI Server ──► REST API ──► Dashboard UI │\n│ GitHub Webhook ──► Auto-trigger on PR open/update/label │\n│ Task Queue (arq) ──► Async Processing ──► Worker Pool │\n│ Job Store ──► In-Memory / File (JSONL) / Redis │\n│ Monitoring ──► Prometheus Metrics ──► Grafana │\n└──────────────────────────────────────────────────────────────────────┘\n```\n\n## Quick Start\n\n### 1. Clone & Install\n\n```bash\ngit clone \ncd pr-review-agent\npip install -r requirements.txt\n```\n\n### 2. Configure Environment\n\n```bash\ncp config/.env.example config/.env\n# Edit config/.env with your API keys\n```\n\nRequired:\n- `ANTHROPIC_API_KEY` — from [console.anthropic.com](https://console.anthropic.com)\n- `GITHUB_TOKEN` — GitHub Personal Access Token with `repo` scope\n\n### 3a. CLI Usage (Quick)\n\n```bash\ncd backend\n# Load env vars\nexport $(cat ../config/.env | xargs) # Linux/Mac\n# Or on Windows: use set or a .env loader\n\n# Review a PR (posts comment to GitHub)\npython cli.py --repo owner/repo-name --pr 42\n\n# Dry run (no GitHub comment)\npython cli.py --repo owner/repo-name --pr 42 --no-comment\n\n# Save full JSON result\npython cli.py --repo owner/repo-name --pr 42 --output result.json\n```\n\n### 3b. Web Server + Dashboard\n\n```bash\ncd backend\nexport $(cat ../config/.env | xargs)\npython server.py\n\n# Open http://localhost:8000\n```\n\n### 3c. Worker (Production)\n\nFor production deployments with async task queue:\n\n```bash\n# Start the arq worker\npython scripts/start_worker.py\n\n# Worker reads REDIS_URL and WORKER_CONCURRENCY from environment\n```\n\n### 3c. GitHub Actions (Automated)\n\nCopy `.github/workflows/pr-review.yml` to your repository. Add these secrets:\n- `ANTHROPIC_API_KEY`\n- `GITHUB_TOKEN`\n\nEvery PR open/sync/reopen triggers an automatic review.\n\n## How Multi-Turn Reasoning Works\n\nThe agent uses 3 sequential Claude API calls per review, each building on prior context:\n\n| Turn | Focus | Approach |\n|------|-------|----------|\n| 1 | Bugs & Security | Deep scan: logic errors, null refs, injection risks, auth flaws |\n| 2 | Performance & Quality | O(n^2) detection, N+1 queries, naming, complexity, test gaps |\n| 3 | Structured Report | Synthesize all findings into final JSON review object |\n\n## Review Output Structure\n\n```json\n{\n \"summary\": \"High-level assessment\",\n \"severity\": \"critical|high|medium|low\",\n \"overall_score\": 0-100,\n \"approve\": true|false,\n \"bugs\": [{\"line\": \"file.py:42\", \"description\": \"...\", \"suggestion\": \"...\"}],\n \"security\": [...],\n \"performance\": [...],\n \"quality\": [...],\n \"positives\": [\"Good error handling in X\", ...]\n}\n```\n\n## Configuration Reference\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `ANTHROPIC_API_KEY` | (required) | Anthropic API key |\n| `GITHUB_TOKEN` | (required) | GitHub PAT with `repo` scope |\n| `GITHUB_WEBHOOK_SECRET` | (required for webhooks) | HMAC-SHA256 webhook verification |\n| `API_KEY` | — | Optional API key for endpoint authentication |\n| `CORS_ORIGINS` | `http://localhost:8000,http://127.0.0.1:8000` | Comma-separated allowed CORS origins |\n| `CLAUDE_MODEL` | `claude-sonnet-4-20250514` | Claude model to use |\n| `PR_REVIEW_MODE` | `full` | `full` / `diff-only` / `auto` |\n| `PR_REVIEW_MAX_FILES` | `20` | Max files to include in context |\n| `PR_REVIEW_MAX_FILE_CHARS` | `5000` | Max chars per file content |\n| `PR_REVIEW_MAX_CONTEXT_CHARS` | `80000` | Total context size cap |\n| `PR_REVIEW_FULL_CONTENT_THRESHOLD` | `500` | Max additions to fetch full content |\n| `PR_REVIEW_CONTEXT_ENABLED` | `true` | Load CONTEXT.md from PR branch |\n| `TOKEN_BUDGET_WEEKLY` | `2000000` | Weekly token budget for alerts |\n| `TOKEN_BUDGET_ALERT_THRESHOLD` | `0.8` | Alert at 80% budget consumed |\n| `LOG_LEVEL` | `INFO` | Logging level |\n| `REDIS_URL` | — | Redis connection (required for production) |\n| `WORKER_CONCURRENCY` | `10` | Max concurrent review tasks per worker |\n| `MAX_QUEUE_SIZE` | `100` | Max pending reviews in queue |\n| `ENVIRONMENT` | `production` | Environment identifier for logging |\n| `SENTRY_DSN` | — | Optional Sentry error tracking DSN |\n\n## API Endpoints\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `POST` | `/api/review` | Start a new review job |\n| `GET` | `/api/jobs` | List all jobs (filter: `?repo=`, `?days=`) |\n| `GET` | `/api/jobs/{id}` | Get job status & result |\n| `GET` | `/api/stats` | Aggregate statistics (filter: `?days=`) |\n| `GET` | `/api/metrics` | Combined quality + throughput + tokens |\n| `GET` | `/api/metrics/tokens` | Weekly budget, daily breakdown, per-review stats |\n| `GET` | `/api/metrics/quality` | Score distribution, severity, approval rate |\n| `GET` | `/api/metrics/throughput` | Reviews/day, files reviewed |\n| `POST` | `/webhook/github` | GitHub webhook handler |\n| `GET` | `/api/health` | Health check (Redis, queue status) |\n| `GET` | `/api/health/ready` | Kubernetes readiness probe |\n| `GET` | `/api/health/live` | Kubernetes liveness probe |\n| `GET` | `/metrics` | Prometheus metrics endpoint |\n\n## Token Usage\n\nTypical per-PR consumption:\n- **Small PR** (< 5 files): ~15K–30K tokens\n- **Medium PR** (5–20 files): ~40K–80K tokens\n- **Large PR** (20+ files): ~80K–150K tokens\n\nUse `PR_REVIEW_MODE=diff-only` to cut token usage ~50% for large PRs.\n\n## Production Deployment\n\n### Job Store\n\nThree backends, auto-selected:\n\n| Backend | Trigger | Persistence |\n|---------|---------|-------------|\n| `InMemoryJobStore` | (fallback) | Lost on restart |\n| `FileJobStore` | Default | JSONL file at `data/jobs.jsonl` |\n| `RedisJobStore` | Set `REDIS_URL` | Redis, with 7-day TTL, status indexing |\n\n### Task Queue (arq + Redis)\n\nFor production workloads, the system uses an async task queue:\n\n```bash\n# Start the API server\npython server.py\n\n# Start worker(s) in separate processes\npython scripts/start_worker.py\n```\n\nFeatures:\n- Async task processing with automatic retries (3 attempts)\n- Request deduplication prevents duplicate reviews\n- Result caching (1 hour TTL based on PR head SHA)\n- Graceful shutdown with active task tracking\n\n### Process Manager\n\n```bash\n# Single worker (fine for < 100 PRs/week)\npython server.py\n\n# Multi-worker production with gunicorn\ngunicorn server:app -w 4 -k uvicorn.workers.UvicornWorker\n```\n\n### Token Budget Monitoring\n\nSet `TOKEN_BUDGET_WEEKLY` and check the dashboard for real-time budget tracking.\nThe stats bar shows a color-coded progress bar: green (< 50%), yellow (50–80%), red (> 80%).\n\n## Security\n\n### API Authentication\n\nSet `API_KEY` environment variable to enable bearer token authentication on all `/api/*` endpoints. When set, requests must include:\n\n```\nAuthorization: Bearer your-api-key-here\n```\n\n### Webhook Security\n\n`GITHUB_WEBHOOK_SECRET` is **required** for webhook endpoints. Webhooks without a valid HMAC-SHA256 signature will be rejected. Generate a secret with:\n\n```bash\nopenssl rand -hex 32\n```\n\n### CORS Configuration\n\nConfigure allowed origins with `CORS_ORIGINS` (comma-separated). Defaults to localhost only.\n\n## GitHub Token Permissions\n\nMinimum required scopes: `repo` (private repos) or `public_repo` (public). This allows reading PR diffs and posting review comments.\n\n## Project-Specific Review Rules\n\nAdd `.pr-review-rules.md` to your repository root (or `.github/pr-review-rules.md`). The agent will inject these rules into the review prompt. Example:\n\n```markdown\n# PR Review Rules\n- All API endpoints must validate input with Pydantic models\n- Database queries must use parameterized queries\n- All new functions must have type hints\n- No console.log or print() in production code\n```\n\nAlso supported: `CONTEXT.md` / `.pr-review-context.md` / `CONTRIBUTING.md` for general project context.\n\n## Changelog\n\n### v1.2.0 - Production Readiness\n\n**New Features:**\n- Async task queue with arq + Redis for concurrent processing\n- Request deduplication to prevent duplicate reviews\n- Result caching with SHA-based keys (1 hour TTL)\n- Prometheus metrics endpoint (`/metrics`)\n- Kubernetes health probes (`/api/health/ready`, `/api/health/live`)\n- Graceful shutdown with active task tracking\n- Thread-safe singleton pattern for all managers\n- Comprehensive unit tests for core modules\n\n**Bug Fixes:**\n- Fixed deprecated `datetime.utcnow()` (Python 3.12+ compatible)\n- Fixed frontend polling memory leak using AbortController\n- Fixed asyncio event loop handling with `asyncio.to_thread()`\n- Enhanced RedisJobStore with status indexing for faster queries\n\n**Infrastructure:**\n- Added `backend/tasks.py` - arq task definitions\n- Added `backend/dedup.py` - request deduplication\n- Added `backend/cache.py` - result caching\n- Added `backend/shutdown.py` - graceful shutdown\n- Added `backend/metrics_middleware.py` - Prometheus middleware\n- Added `backend/singleton.py` - thread-safe singleton decorator\n- Added `scripts/start_worker.py` - Worker startup script\n- Added comprehensive test suite in `tests/`\n\n### v1.1.0 - Security & Reliability Improvements\n\n**Security Fixes:**\n- Fixed XSS vulnerability in frontend dashboard\n- Added optional API key authentication (`API_KEY` env var)\n- Webhook signature verification now required (no more bypass)\n- CORS origins now configurable (defaults to localhost only)\n\n**Bug Fixes:**\n- Fixed job ID collision when submitting multiple reviews rapidly\n- Fixed KeyError crash in stats endpoint with incomplete data\n- Fixed fragile JSON parsing for Claude responses\n- Fixed metrics dead code in throughput calculation\n- Fixed token tracker week start normalization\n- Updated deprecated `datetime.utcnow()` calls\n- Fixed GitHub Actions workflow to install all dependencies\n\n**New Features:**\n- Added `CORS_ORIGINS` configuration\n- Added `API_KEY` for endpoint authentication\n- Added `backend/__init__.py` for proper module imports\n"}, "8e4a1790c9276492958c343a14fdc03ad29a3d3b": {"t": 1785763654.5584946, "n": 7956, "h": "c73e4ad9f3dd3b64", "c": "# PR Review Agent\n\nAI-powered pull request code review system using Claude API with multi-turn reasoning.\nDesigned for teams handling 50+ PRs/week with ~2M tokens weekly budget.\n\n## Architecture\n\n```\n┌──────────────────────────────────────────────────────────┐\n│ PR Review Agent │\n│ │\n│ GitHub PR ──► Agent ──► Turn 1: Bug & Security Scan │\n│ │──► Turn 2: Performance & Quality │\n│ │──► Turn 3: Structured JSON Report │\n│ └──► Post Comment to GitHub PR │\n│ │\n│ FastAPI Server ──► REST API ──► Dashboard UI │\n│ GitHub Webhook ──► Auto-trigger on PR open/update/label │\n│ Job Store ──► In-Memory / File (JSONL) / Redis │\n└──────────────────────────────────────────────────────────┘\n```\n\n## Quick Start\n\n### 1. Clone & Install\n\n```bash\ngit clone \ncd pr-review-agent\npip install -r requirements.txt\n```\n\n### 2. Configure Environment\n\n```bash\ncp config/.env.example config/.env\n# Edit config/.env with your API keys\n```\n\nRequired:\n- `ANTHROPIC_API_KEY` — from [console.anthropic.com](https://console.anthropic.com)\n- `GITHUB_TOKEN` — GitHub Personal Access Token with `repo` scope\n\n### 3a. CLI Usage (Quick)\n\n```bash\ncd backend\n# Load env vars\nexport $(cat ../config/.env | xargs) # Linux/Mac\n# Or on Windows: use set or a .env loader\n\n# Review a PR (posts comment to GitHub)\npython cli.py --repo owner/repo-name --pr 42\n\n# Dry run (no GitHub comment)\npython cli.py --repo owner/repo-name --pr 42 --no-comment\n\n# Save full JSON result\npython cli.py --repo owner/repo-name --pr 42 --output result.json\n```\n\n### 3b. Web Server + Dashboard\n\n```bash\ncd backend\nexport $(cat ../config/.env | xargs)\npython server.py\n\n# Open http://localhost:8000\n```\n\n### 3c. GitHub Actions (Automated)\n\nCopy `.github/workflows/pr-review.yml` to your repository. Add these secrets:\n- `ANTHROPIC_API_KEY`\n- `GITHUB_TOKEN`\n\nEvery PR open/sync/reopen triggers an automatic review.\n\n## How Multi-Turn Reasoning Works\n\nThe agent uses 3 sequential Claude API calls per review, each building on prior context:\n\n| Turn | Focus | Approach |\n|------|-------|----------|\n| 1 | Bugs & Security | Deep scan: logic errors, null refs, injection risks, auth flaws |\n| 2 | Performance & Quality | O(n^2) detection, N+1 queries, naming, complexity, test gaps |\n| 3 | Structured Report | Synthesize all findings into final JSON review object |\n\n## Review Output Structure\n\n```json\n{\n \"summary\": \"High-level assessment\",\n \"severity\": \"critical|high|medium|low\",\n \"overall_score\": 0-100,\n \"approve\": true|false,\n \"bugs\": [{\"line\": \"file.py:42\", \"description\": \"...\", \"suggestion\": \"...\"}],\n \"security\": [...],\n \"performance\": [...],\n \"quality\": [...],\n \"positives\": [\"Good error handling in X\", ...]\n}\n```\n\n## Configuration Reference\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `ANTHROPIC_API_KEY` | (required) | Anthropic API key |\n| `GITHUB_TOKEN` | (required) | GitHub PAT with `repo` scope |\n| `GITHUB_WEBHOOK_SECRET` | (required for webhooks) | HMAC-SHA256 webhook verification |\n| `API_KEY` | — | Optional API key for endpoint authentication |\n| `CORS_ORIGINS` | `http://localhost:8000,http://127.0.0.1:8000` | Comma-separated allowed CORS origins |\n| `CLAUDE_MODEL` | `claude-sonnet-4-20250514` | Claude model to use |\n| `PR_REVIEW_MODE` | `full` | `full` / `diff-only` / `auto` |\n| `PR_REVIEW_MAX_FILES` | `20` | Max files to include in context |\n| `PR_REVIEW_MAX_FILE_CHARS` | `5000` | Max chars per file content |\n| `PR_REVIEW_MAX_CONTEXT_CHARS` | `80000` | Total context size cap |\n| `PR_REVIEW_FULL_CONTENT_THRESHOLD` | `500` | Max additions to fetch full content |\n| `PR_REVIEW_CONTEXT_ENABLED` | `true` | Load CONTEXT.md from PR branch |\n| `TOKEN_BUDGET_WEEKLY` | `2000000` | Weekly token budget for alerts |\n| `TOKEN_BUDGET_ALERT_THRESHOLD` | `0.8` | Alert at 80% budget consumed |\n| `LOG_LEVEL` | `INFO` | Logging level |\n| `REDIS_URL` | — | Redis connection (optional, for prod) |\n\n## API Endpoints\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `POST` | `/api/review` | Start a new review job |\n| `GET` | `/api/jobs` | List all jobs (filter: `?repo=`, `?days=`) |\n| `GET` | `/api/jobs/{id}` | Get job status & result |\n| `GET` | `/api/stats` | Aggregate statistics (filter: `?days=`) |\n| `GET` | `/api/metrics` | Combined quality + throughput + tokens |\n| `GET` | `/api/metrics/tokens` | Weekly budget, daily breakdown, per-review stats |\n| `GET` | `/api/metrics/quality` | Score distribution, severity, approval rate |\n| `GET` | `/api/metrics/throughput` | Reviews/day, files reviewed |\n| `POST` | `/webhook/github` | GitHub webhook handler |\n| `GET` | `/api/health` | Health check |\n\n## Token Usage\n\nTypical per-PR consumption:\n- **Small PR** (< 5 files): ~15K–30K tokens\n- **Medium PR** (5–20 files): ~40K–80K tokens\n- **Large PR** (20+ files): ~80K–150K tokens\n\nUse `PR_REVIEW_MODE=diff-only` to cut token usage ~50% for large PRs.\n\n## Production Deployment\n\n### Job Store\n\nThree backends, auto-selected:\n\n| Backend | Trigger | Persistence |\n|---------|---------|-------------|\n| `InMemoryJobStore` | (fallback) | Lost on restart |\n| `FileJobStore` | Default | JSONL file at `data/jobs.jsonl` |\n| `RedisJobStore` | Set `REDIS_URL` | Redis, with 7-day TTL |\n\n### Process Manager\n\n```bash\n# Single worker (fine for < 100 PRs/week)\npython server.py\n\n# Multi-worker production\ngunicorn server:app -w 4 -k uvicorn.workers.UvicornWorker\n```\n\n### Token Budget Monitoring\n\nSet `TOKEN_BUDGET_WEEKLY` and check the dashboard for real-time budget tracking.\nThe stats bar shows a color-coded progress bar: green (< 50%), yellow (50–80%), red (> 80%).\n\n## Security\n\n### API Authentication\n\nSet `API_KEY` environment variable to enable bearer token authentication on all `/api/*` endpoints. When set, requests must include:\n\n```\nAuthorization: Bearer your-api-key-here\n```\n\n### Webhook Security\n\n`GITHUB_WEBHOOK_SECRET` is **required** for webhook endpoints. Webhooks without a valid HMAC-SHA256 signature will be rejected. Generate a secret with:\n\n```bash\nopenssl rand -hex 32\n```\n\n### CORS Configuration\n\nConfigure allowed origins with `CORS_ORIGINS` (comma-separated). Defaults to localhost only.\n\n## GitHub Token Permissions\n\nMinimum required scopes: `repo` (private repos) or `public_repo` (public). This allows reading PR diffs and posting review comments.\n\n## Project-Specific Review Rules\n\nAdd `.pr-review-rules.md` to your repository root (or `.github/pr-review-rules.md`). The agent will inject these rules into the review prompt. Example:\n\n```markdown\n# PR Review Rules\n- All API endpoints must validate input with Pydantic models\n- Database queries must use parameterized queries\n- All new functions must have type hints\n- No console.log or print() in production code\n```\n\nAlso supported: `CONTEXT.md` / `.pr-review-context.md` / `CONTRIBUTING.md` for general project context.\n\n## Changelog\n\n### v1.1.0 - Security & Reliability Improvements\n\n**Security Fixes:**\n- Fixed XSS vulnerability in frontend dashboard\n- Added optional API key authentication (`API_KEY` env var)\n- Webhook signature verification now required (no more bypass)\n- CORS origins now configurable (defaults to localhost only)\n\n**Bug Fixes:**\n- Fixed job ID collision when submitting multiple reviews rapidly\n- Fixed KeyError crash in stats endpoint with incomplete data\n- Fixed fragile JSON parsing for Claude responses\n- Fixed metrics dead code in throughput calculation\n- Fixed token tracker week start normalization\n- Updated deprecated `datetime.utcnow()` calls\n- Fixed GitHub Actions workflow to install all dependencies\n\n**New Features:**\n- Added `CORS_ORIGINS` configuration\n- Added `API_KEY` for endpoint authentication\n- Added `backend/__init__.py` for proper module imports\n"}, "c9fb806fc9a1a114e835bed35409452441fed825": {"t": 1785763656.1090593, "n": 6341, "h": "d91793f344a7cfc1", "c": "# PR Review Agent\n\nAI-powered pull request code review system using Claude API with multi-turn reasoning.\nDesigned for teams handling 50+ PRs/week with ~2M tokens weekly budget.\n\n## Architecture\n\n```\n┌──────────────────────────────────────────────────────────┐\n│ PR Review Agent │\n│ │\n│ GitHub PR ──► Agent ──► Turn 1: Bug & Security Scan │\n│ │──► Turn 2: Performance & Quality │\n│ │──► Turn 3: Structured JSON Report │\n│ └──► Post Comment to GitHub PR │\n│ │\n│ FastAPI Server ──► REST API ──► Dashboard UI │\n│ GitHub Webhook ──► Auto-trigger on PR open/update/label │\n│ Job Store ──► In-Memory / File (JSONL) / Redis │\n└──────────────────────────────────────────────────────────┘\n```\n\n## Quick Start\n\n### 1. Clone & Install\n\n```bash\ngit clone \ncd pr-review-agent\npip install -r requirements.txt\n```\n\n### 2. Configure Environment\n\n```bash\ncp config/.env.example config/.env\n# Edit config/.env with your API keys\n```\n\nRequired:\n- `ANTHROPIC_API_KEY` — from [console.anthropic.com](https://console.anthropic.com)\n- `GITHUB_TOKEN` — GitHub Personal Access Token with `repo` scope\n\n### 3a. CLI Usage (Quick)\n\n```bash\ncd backend\n# Load env vars\nexport $(cat ../config/.env | xargs) # Linux/Mac\n# Or on Windows: use set or a .env loader\n\n# Review a PR (posts comment to GitHub)\npython cli.py --repo owner/repo-name --pr 42\n\n# Dry run (no GitHub comment)\npython cli.py --repo owner/repo-name --pr 42 --no-comment\n\n# Save full JSON result\npython cli.py --repo owner/repo-name --pr 42 --output result.json\n```\n\n### 3b. Web Server + Dashboard\n\n```bash\ncd backend\nexport $(cat ../config/.env | xargs)\npython server.py\n\n# Open http://localhost:8000\n```\n\n### 3c. GitHub Actions (Automated)\n\nCopy `.github/workflows/pr-review.yml` to your repository. Add these secrets:\n- `ANTHROPIC_API_KEY`\n- `GITHUB_TOKEN`\n\nEvery PR open/sync/reopen triggers an automatic review.\n\n## How Multi-Turn Reasoning Works\n\nThe agent uses 3 sequential Claude API calls per review, each building on prior context:\n\n| Turn | Focus | Approach |\n|------|-------|----------|\n| 1 | Bugs & Security | Deep scan: logic errors, null refs, injection risks, auth flaws |\n| 2 | Performance & Quality | O(n^2) detection, N+1 queries, naming, complexity, test gaps |\n| 3 | Structured Report | Synthesize all findings into final JSON review object |\n\n## Review Output Structure\n\n```json\n{\n \"summary\": \"High-level assessment\",\n \"severity\": \"critical|high|medium|low\",\n \"overall_score\": 0-100,\n \"approve\": true|false,\n \"bugs\": [{\"line\": \"file.py:42\", \"description\": \"...\", \"suggestion\": \"...\"}],\n \"security\": [...],\n \"performance\": [...],\n \"quality\": [...],\n \"positives\": [\"Good error handling in X\", ...]\n}\n```\n\n## Configuration Reference\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `ANTHROPIC_API_KEY` | (required) | Anthropic API key |\n| `GITHUB_TOKEN` | (required) | GitHub PAT with `repo` scope |\n| `GITHUB_WEBHOOK_SECRET` | — | HMAC-SHA256 webhook verification |\n| `CLAUDE_MODEL` | `claude-sonnet-4-20250514` | Claude model to use |\n| `PR_REVIEW_MODE` | `full` | `full` / `diff-only` / `auto` |\n| `PR_REVIEW_MAX_FILES` | `20` | Max files to include in context |\n| `PR_REVIEW_MAX_FILE_CHARS` | `5000` | Max chars per file content |\n| `PR_REVIEW_MAX_CONTEXT_CHARS` | `80000` | Total context size cap |\n| `PR_REVIEW_FULL_CONTENT_THRESHOLD` | `500` | Max additions to fetch full content |\n| `PR_REVIEW_CONTEXT_ENABLED` | `true` | Load CONTEXT.md from PR branch |\n| `TOKEN_BUDGET_WEEKLY` | `2000000` | Weekly token budget for alerts |\n| `TOKEN_BUDGET_ALERT_THRESHOLD` | `0.8` | Alert at 80% budget consumed |\n| `LOG_LEVEL` | `INFO` | Logging level |\n| `REDIS_URL` | — | Redis connection (optional, for prod) |\n\n## API Endpoints\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `POST` | `/api/review` | Start a new review job |\n| `GET` | `/api/jobs` | List all jobs (filter: `?repo=`, `?days=`) |\n| `GET` | `/api/jobs/{id}` | Get job status & result |\n| `GET` | `/api/stats` | Aggregate statistics (filter: `?days=`) |\n| `GET` | `/api/metrics` | Combined quality + throughput + tokens |\n| `GET` | `/api/metrics/tokens` | Weekly budget, daily breakdown, per-review stats |\n| `GET` | `/api/metrics/quality` | Score distribution, severity, approval rate |\n| `GET` | `/api/metrics/throughput` | Reviews/day, files reviewed |\n| `POST` | `/webhook/github` | GitHub webhook handler |\n| `GET` | `/api/health` | Health check |\n\n## Token Usage\n\nTypical per-PR consumption:\n- **Small PR** (< 5 files): ~15K–30K tokens\n- **Medium PR** (5–20 files): ~40K–80K tokens\n- **Large PR** (20+ files): ~80K–150K tokens\n\nUse `PR_REVIEW_MODE=diff-only` to cut token usage ~50% for large PRs.\n\n## Production Deployment\n\n### Job Store\n\nThree backends, auto-selected:\n\n| Backend | Trigger | Persistence |\n|---------|---------|-------------|\n| `InMemoryJobStore` | (fallback) | Lost on restart |\n| `FileJobStore` | Default | JSONL file at `data/jobs.jsonl` |\n| `RedisJobStore` | Set `REDIS_URL` | Redis, with 7-day TTL |\n\n### Process Manager\n\n```bash\n# Single worker (fine for < 100 PRs/week)\npython server.py\n\n# Multi-worker production\ngunicorn server:app -w 4 -k uvicorn.workers.UvicornWorker\n```\n\n### Token Budget Monitoring\n\nSet `TOKEN_BUDGET_WEEKLY` and check the dashboard for real-time budget tracking.\nThe stats bar shows a color-coded progress bar: green (< 50%), yellow (50–80%), red (> 80%).\n\n## GitHub Token Permissions\n\nMinimum required scopes: `repo` (private repos) or `public_repo` (public). This allows reading PR diffs and posting review comments.\n\n## Project-Specific Review Rules\n\nAdd `.pr-review-rules.md` to your repository root (or `.github/pr-review-rules.md`). The agent will inject these rules into the review prompt. Example:\n\n```markdown\n# PR Review Rules\n- All API endpoints must validate input with Pydantic models\n- Database queries must use parameterized queries\n- All new functions must have type hints\n- No console.log or print() in production code\n```\n\nAlso supported: `CONTEXT.md` / `.pr-review-context.md` / `CONTRIBUTING.md` for general project context.\n"}, "c2c9f2d370d033a8fdb5226625b25d0bb03880ab": {"t": 1785763657.4740846, "n": 3965, "h": "18c0ce650fc2f51e", "c": "# 🤖 PR Review Agent\n\nAI-powered pull request code review system using Claude API with multi-turn reasoning.\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────┐\n│ PR Review Agent │\n│ │\n│ GitHub PR ──► Agent ──► Turn 1: Bug & Security Scan │\n│ │──► Turn 2: Performance & Quality │\n│ │──► Turn 3: Structured JSON Report │\n│ └──► Post Comment to GitHub PR │\n│ │\n│ FastAPI Server ──► REST API ──► React Dashboard UI │\n│ GitHub Webhook ──► Auto-trigger on PR open/update │\n└─────────────────────────────────────────────────────────┘\n```\n\n## Quick Start\n\n### 1. Clone & Install\n```bash\ngit clone \ncd pr-review-agent\npip install -r requirements.txt\n```\n\n### 2. Configure Environment\n```bash\ncp config/.env.example config/.env\n# Edit config/.env with your API keys\n```\n\nRequired:\n- `ANTHROPIC_API_KEY` — from [console.anthropic.com](https://console.anthropic.com)\n- `GITHUB_TOKEN` — GitHub Personal Access Token with `repo` scope\n\n### 3a. CLI Usage (Quick)\n```bash\ncd backend\nexport $(cat ../config/.env | xargs)\n\n# Review a PR (posts comment to GitHub)\npython cli.py --repo owner/repo-name --pr 42\n\n# Dry run (no GitHub comment)\npython cli.py --repo owner/repo-name --pr 42 --no-comment\n\n# Save full JSON result\npython cli.py --repo owner/repo-name --pr 42 --output result.json\n```\n\n### 3b. Web Server + Dashboard\n```bash\ncd backend\nexport $(cat ../config/.env | xargs)\npython server.py\n\n# Open http://localhost:8000\n```\n\n### 3c. GitHub Actions (Automated)\nCopy `.github/workflows/pr-review.yml` to your repository.\n\nAdd secrets in GitHub repo settings:\n- `ANTHROPIC_API_KEY`\n- (GITHUB_TOKEN is provided automatically)\n\nEvery PR open/update will trigger an automatic review.\n\n## How Multi-Turn Reasoning Works\n\nThe agent uses 3 sequential Claude API calls per review:\n\n| Turn | Focus | Approach |\n|------|-------|----------|\n| 1 | Bugs & Security | Deep scan of logic errors, null refs, injection risks, auth flaws |\n| 2 | Performance & Quality | O(n²) algos, N+1 queries, naming, complexity, missing tests |\n| 3 | Structured Report | Synthesize all findings into final JSON review object |\n\nEach turn builds on the previous conversation, enabling deeper chain-of-thought reasoning than a single prompt.\n\n## Review Output Structure\n\n```json\n{\n \"summary\": \"High-level assessment\",\n \"severity\": \"critical|high|medium|low\",\n \"overall_score\": 0-100,\n \"approve\": true|false,\n \"bugs\": [{\"line\": \"file.py:42\", \"description\": \"...\", \"suggestion\": \"...\"}],\n \"security\": [...],\n \"performance\": [...],\n \"quality\": [...],\n \"positives\": [\"Good error handling in X\", ...]\n}\n```\n\n## API Endpoints\n\n| Method | Path | Description |\n|--------|------|-------------|\n| POST | `/api/review` | Start a new review job |\n| GET | `/api/jobs` | List all jobs |\n| GET | `/api/jobs/{id}` | Get job status & result |\n| GET | `/api/stats` | Aggregate statistics |\n| POST | `/webhook/github` | GitHub webhook handler |\n| GET | `/api/health` | Health check |\n\n## Token Usage\n\nTypical usage per PR review:\n- **Small PR** (< 5 files): ~15K–30K tokens\n- **Medium PR** (5–20 files): ~40K–80K tokens \n- **Large PR** (20+ files): ~80K–150K tokens\n\nAt 50 PRs/week, estimated weekly consumption: ~1.5M–3M tokens.\n\n## GitHub Token Permissions\n\nMinimum required scopes:\n- `repo` (for private repos) or `public_repo` (for public)\n- This allows reading PRs and posting comments\n\n## Production Deployment\n\nFor production, replace the in-memory job store with Redis:\n\n```python\n# In server.py, use redis-py:\nimport redis\nr = redis.Redis()\nr.setex(f\"job:{job_id}\", 3600, json.dumps(job_data))\n```\n\nUse a process manager like `gunicorn` with `uvicorn` workers:\n```bash\ngunicorn server:app -w 4 -k uvicorn.workers.UvicornWorker\n```\n"}}}}