API Layer π
FastAPI backend decoupling agents from frontends.
Quick Start
# 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:
/chatand/chat/streamuseCompactingSupervisorwrapper (auto context management)/raw/chatand/raw/chat/streambypass 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:
- Save CV file to disk
- Register candidate in DB
- Parse CV β Markdown (GPT-4 Vision)
- 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:
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:
# 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
- Candidate database router