|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +AI Resume Analyzer — a fullstack app that accepts a resume (PDF/TXT/text) and target role, calls the Gemini API for structured ATS scoring, and returns `{ ats_score, missing_skills, strengths, recommendations }`. The AI layer has a heuristic fallback so the app works without a Gemini key (used in CI and local demos). |
| 8 | + |
| 9 | +## Commands |
| 10 | + |
| 11 | +### Backend |
| 12 | + |
| 13 | +```bash |
| 14 | +cd backend |
| 15 | +python3.11 -m venv .venv && source .venv/bin/activate |
| 16 | +pip install -r requirements.txt |
| 17 | +uvicorn app.main:app --reload --host 127.0.0.1 --port 8000 |
| 18 | +``` |
| 19 | + |
| 20 | +Run tests (from `backend/` with venv active): |
| 21 | +```bash |
| 22 | +pytest # all tests |
| 23 | +pytest tests/test_analyze.py # single file |
| 24 | +``` |
| 25 | + |
| 26 | +Or from the repo root (no venv needed if installed globally): |
| 27 | +```bash |
| 28 | +npm run backend:test |
| 29 | +``` |
| 30 | + |
| 31 | +### Frontend |
| 32 | + |
| 33 | +```bash |
| 34 | +cd frontend |
| 35 | +npm install |
| 36 | +npm run dev # http://localhost:5173 |
| 37 | +npm run build |
| 38 | +``` |
| 39 | + |
| 40 | +### Docker |
| 41 | + |
| 42 | +```bash |
| 43 | +docker compose up --build backend # backend only at :8000 |
| 44 | +``` |
| 45 | + |
| 46 | +### Environment setup |
| 47 | + |
| 48 | +```bash |
| 49 | +cp .env.example .env |
| 50 | +cp frontend/.env.example frontend/.env |
| 51 | +``` |
| 52 | + |
| 53 | +Set `GEMINI_API_KEY` in `.env` for live AI; leave `ENABLE_AI_FALLBACK=true` to use heuristics instead. |
| 54 | + |
| 55 | +## Architecture |
| 56 | + |
| 57 | +``` |
| 58 | +Request → FastAPI (routers/analyze.py) |
| 59 | + → ResumeAnalyzerService (services/resume_service.py) |
| 60 | + → input extraction (utils/pdf.py) |
| 61 | + → Pydantic validation (schemas/resume.py: AnalyzePayload) |
| 62 | + → GeminiResumeService (services/gemini_service.py) |
| 63 | + → retry_async (utils/retry.py, exponential backoff + jitter) |
| 64 | + → Gemini API with response_json_schema enforcement |
| 65 | + [on failure or no key] |
| 66 | + → HeuristicResumeService (services/heuristic_service.py) |
| 67 | + → AnalyzeResponse (Pydantic, validated on the way out) |
| 68 | + monitoring_middleware → metrics singleton (services/monitoring_service.py) |
| 69 | +``` |
| 70 | + |
| 71 | +### Key architectural rules |
| 72 | + |
| 73 | +- **Strict schema enforcement everywhere.** `AnalyzePayload` validates inputs before any AI call. `AnalyzeResponse` validates Gemini output via `model_validate_json`. `ANALYSIS_JSON_SCHEMA` is passed to Gemini's `response_json_schema` to constrain generation at the API level. |
| 74 | +- **Fallback is controlled by `ENABLE_AI_FALLBACK`.** In production (`render.yaml`) it is `false` so Gemini failures surface as errors. In CI and local dev it defaults to `true`. |
| 75 | +- **All config lives in `app/config.py` (`Settings`).** Access via `get_settings()` (lru_cache). Never read env vars directly in route or service code. |
| 76 | +- **New endpoints** require a router in `app/routers/`, a service in `app/services/`, and Pydantic schemas in `app/schemas/`. Wire the router into `app/main.py`. |
| 77 | +- **Error types** (`AppError`, `BadRequestError`, `AIServiceError`, etc.) are in `app/utils/exceptions.py`. The global handlers in `main.py` convert them to consistent JSON with `request_id`. |
| 78 | +- **Monitoring** is in-memory only — `metrics` singleton in `monitoring_service.py`, recorded by `monitoring_middleware` in `main.py`. Not persistent across restarts. |
| 79 | + |
| 80 | +### Frontend |
| 81 | + |
| 82 | +Single-page React app (`frontend/src/App.jsx`). Components: `AnalysisForm`, `ResultPanel`, `HealthBadge`, `ErrorBanner`, `ErrorBoundary`. API calls are in `frontend/src/lib/api.js`. Backend URL is set via `VITE_API_BASE_URL`. |
| 83 | + |
| 84 | +## Deployment |
| 85 | + |
| 86 | +- **Backend**: Dockerized, deployed to Render via `render.yaml`. Health check on `/health`. |
| 87 | +- **Frontend**: Static Vite build, deployed to Firebase Hosting (`frontend/firebase.json`). Set `VITE_API_BASE_URL` to the Render URL before `npm run build`. |
| 88 | +- **CI**: GitHub Actions (`.github/workflows/ci.yml`) runs `pytest` (backend) and `npm run build` (frontend) on every push/PR to `main`. Tests run with `ENABLE_AI_FALLBACK=true`. |
| 89 | + |
| 90 | +## Environment Variables |
| 91 | + |
| 92 | +| Variable | Default | Purpose | |
| 93 | +|---|---|---| |
| 94 | +| `GEMINI_API_KEY` | — | Required for live AI; omit to use heuristic fallback | |
| 95 | +| `ENABLE_AI_FALLBACK` | `true` | Fall back to heuristics when Gemini fails or is unconfigured | |
| 96 | +| `GEMINI_MODEL` | `gemini-2.5-flash` | Gemini model name | |
| 97 | +| `CORS_ORIGINS` | `http://localhost:5173` | Comma-separated allowed origins | |
| 98 | +| `ENVIRONMENT` | `development` | Surfaced in `/health` response | |
| 99 | +| `MAX_RESUME_CHARS` | `20000` | Resume text is truncated to this before AI call | |
| 100 | +| `MAX_UPLOAD_MB` | `5` | Max file upload size | |
0 commit comments