Spaces:
Sleeping
Sleeping
| # API Layer π | |
| > FastAPI backend decoupling agents from frontends. | |
| ## Quick Start | |
| ```bash | |
| # Local | |
| uvicorn src.api.app:app --reload --port 8080 | |
| # Docker | |
| docker compose --env-file .env -f docker/docker-compose.yml up supervisor_api | |
| ``` | |
| **Docs:** http://localhost:8080/docs | |
| ## Endpoints | |
| ### Supervisor Agent `/api/v1/supervisor` | |
| | Method | Endpoint | Description | | |
| |--------|----------|-------------| | |
| | POST | `/chat` | Batch response with context compaction | | |
| | POST | `/chat/stream` | SSE streaming with context compaction β οΈ | | |
| | POST | `/raw/chat` | Batch response, direct agent (no compaction) | | |
| | POST | `/raw/chat/stream` | SSE streaming, direct agent β οΈ | | |
| | POST | `/new` | Create new chat session | | |
| | GET | `/health` | Health check | | |
| β οΈ **Note:** Streaming endpoints have known issues. Use batch endpoints (`/chat` or `/raw/chat`) for reliable operation. | |
| **With vs Raw endpoints:** | |
| - `/chat` and `/chat/stream` use `CompactingSupervisor` wrapper (auto context management) | |
| - `/raw/chat` and `/raw/chat/stream` bypass wrapper (direct agent access, useful for debugging) | |
| **Streaming (SSE) events:** | |
| ``` | |
| event: token β {"content": "Hello"} | |
| event: done β {"thread_id": "abc123", "token_count": 150} | |
| event: error β {"error": "Something went wrong"} | |
| ``` | |
| ### CV Upload `/api/v1/cv` | |
| | Method | Endpoint | Description | | |
| |--------|----------|-------------| | |
| | POST | `/submit` | Submit application + CV | | |
| | GET | `/health` | Health check | | |
| **Submit flow:** | |
| 1. Save CV file to disk | |
| 2. Register candidate in DB | |
| 3. Parse CV β Markdown (GPT-4 Vision) | |
| 4. Update parsed path in DB | |
| ### Database `/api/v1/db` | |
| | Method | Endpoint | Description | | |
| |--------|----------|-------------| | |
| | POST | `/query` | Flexible query any table | | |
| | GET | `/candidates` | List candidates with filters | | |
| | GET | `/candidates/{id}` | Get full candidate profile by UUID | | |
| | GET | `/candidates/email/{email}` | Get full candidate profile by email | | |
| | GET | `/cv-screening` | List CV screening results | | |
| | GET | `/voice-screening` | List voice screening results | | |
| | GET | `/interviews` | List interview scheduling | | |
| | GET | `/decisions` | List final decisions | | |
| | GET | `/stats` | Database statistics | | |
| | GET | `/health` | Health check | | |
| **Full Candidate Profile** (`/candidates/{id}` and `/candidates/email/{email}`): | |
| Returns ALL data for a candidate including related records (by default `include_relations=true`): | |
| - **Base fields:** id, full_name, email, phone_number, cv_file_path, parsed_cv_file_path, status, created_at, updated_at | |
| - **cv_screening_results:** list of CV screening scores and feedback | |
| - **voice_screening_results:** list of voice screening transcripts and scores | |
| - **interview_scheduling:** list of scheduled interviews | |
| - **final_decision:** hiring decision with rationale (if any) | |
| Use `?include_relations=false` to fetch only base candidate fields. | |
| **Flexible Query Example:** | |
| ```json | |
| POST /api/v1/db/query | |
| { | |
| "table": "candidates", | |
| "filters": {"status": "applied"}, | |
| "fields": ["id", "full_name", "email"], | |
| "include_relations": true, | |
| "limit": 10, | |
| "offset": 0, | |
| "sort_by": "created_at", | |
| "sort_order": "desc" | |
| } | |
| ``` | |
| **Supported filter operators:** | |
| - `$eq`, `$ne`: equality/inequality | |
| - `$gt`, `$gte`, `$lt`, `$lte`: comparisons | |
| - `$in`, `$nin`: list membership | |
| - `$like`, `$ilike`: pattern matching | |
| ## Structure | |
| ``` | |
| src/api/ | |
| βββ app.py β FastAPI app + CORS + router mounting | |
| βββ routers/ | |
| β βββ supervisor.py β Chat endpoints (regular + streaming) | |
| β βββ cv_upload.py β CV submission endpoint | |
| β βββ database.py β Flexible database query endpoints | |
| βββ schemas/ | |
| βββ supervisor_chat.py β ChatRequest, ChatResponse | |
| βββ cv_upload.py β SubmitResponse | |
| βββ database.py β QueryRequest, QueryResponse, etc. | |
| ``` | |
| ## SDK Clients | |
| Frontends use SDK clients instead of raw HTTP: | |
| ```python | |
| # Supervisor | |
| from src.sdk import SupervisorClient | |
| client = SupervisorClient() | |
| for chunk in client.stream("Show candidates", thread_id): | |
| print(chunk.content) | |
| # CV Upload | |
| from src.sdk import CVUploadClient | |
| client = CVUploadClient() | |
| response = client.submit(name, email, phone, cv_file, filename) | |
| # Database Queries | |
| from src.sdk import DatabaseClient | |
| db = DatabaseClient() | |
| # List candidates | |
| candidates = db.get_candidates(status="applied", include_relations=True) | |
| for c in candidates.data: | |
| print(c["full_name"], c["status"]) | |
| # Get full candidate profile by email | |
| profile = db.get_candidate_by_email("ada@example.com") | |
| print(profile.data["cv_screening_results"]) | |
| # Flexible query with filters | |
| results = db.query( | |
| table="cv_screening_results", | |
| filters={"overall_fit_score": {"$gte": 0.8}}, | |
| sort_by="overall_fit_score", | |
| sort_order="desc" | |
| ) | |
| # Get database stats | |
| stats = db.get_stats() | |
| print(stats.stats["candidates"]["by_status"]) | |
| ``` | |
| ## Environment | |
| | Variable | Default | Used By | | |
| |----------|---------|---------| | |
| | `OPENAI_API_KEY` | required | Validated at startup | | |
| | `CV_UPLOAD_PATH` | `src/database/cvs/uploads` | CV router | | |
| | `CV_PARSED_PATH` | `src/database/cvs/parsed` | CV router | | |
| | `POSTGRES_*` | varies | Database connection | | |
| ## TODO | |
| - [ ] Voice agent router | |
| - [x] Candidate database router | |