SmartHire-AI / api /README.md
Vishu2006's picture
Initial commit: SmartHire-AI FastAPI + Streamlit
91e794e
|
Raw
History Blame Contribute Delete
6.23 kB
# SmartHire AI — REST API Reference
Base URL: `http://localhost:8000`
Interactive Docs: `http://localhost:8000/docs`
Redoc: `http://localhost:8000/redoc`
---
## Start the API
```bash
# Install new dependencies first (one time)
pip install fastapi uvicorn[standard] python-multipart
# Start the API server
uvicorn api.main:app --host 0.0.0.0 --port 8000 --reload
# Or double-click RUN_API.bat on Windows
```
The Streamlit UI still runs separately:
```bash
streamlit run app/streamlit_app.py # port 8501
uvicorn api.main:app --port 8000 # port 8000
```
---
## Endpoints
### Health
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/` | Root — confirms server is running |
| GET | `/health` | Health check with timestamp |
| GET | `/model/info` | Loaded model metadata |
---
### Core Matching
#### `POST /match`
Match resumes against a job description. Returns ranked candidates.
**Form fields:**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `resumes` | File(s) | ✅ | PDF, DOCX, or TXT resume files |
| `jd_text` | string | one of | JD as plain text |
| `jd_file` | File | one of | JD as file |
| `similarity_weight` | float | ❌ | 0.5–0.9, default 0.7 |
**Example (JavaScript fetch):**
```javascript
const form = new FormData();
form.append("resumes", resumeFile1);
form.append("resumes", resumeFile2);
form.append("jd_text", "We are looking for a Python ML Engineer...");
form.append("similarity_weight", "0.7");
const res = await fetch("http://localhost:8000/match", {
method: "POST",
body: form,
});
const data = await res.json();
```
**Response:**
```json
{
"status": "success",
"duration_sec": 1.23,
"total_candidates": 2,
"summary": {
"total_candidates": 2,
"average_score": 72.5,
"highest_score": 85.0,
"highly_recommended": 1,
"recommended": 1,
"consider": 0,
"not_recommended": 0
},
"candidates": [
{
"rank": 1,
"name": "John_Doe",
"score_pct": 85.0,
"semantic_similarity": 91.2,
"skill_coverage_pct": 75.0,
"recommendation": "Highly Recommended",
"confidence": "High",
"percentile_rank": 100.0,
"matching_skills": ["python", "pytorch", "docker"],
"missing_skills": ["kubernetes"],
"critical_missing": [],
"important_missing": ["kubernetes"],
"resume_only_skills": ["flask", "pandas"],
"ai_insight": "Strong contextual alignment..."
}
],
"parse_errors": []
}
```
---
### Skills
#### `POST /skills`
Extract and compare skills from a single resume vs JD.
**Form fields:**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `resume` | File | ✅ | Resume file |
| `jd_text` | string | ✅ | JD text |
**Response:**
```json
{
"status": "success",
"candidate": "John_Doe",
"matching_skills": ["python", "pytorch"],
"missing_skills": ["kubernetes"],
"critical_missing": [],
"skill_coverage_pct": 75.0,
"weighted_coverage_pct": 80.0,
"jd_skills": ["python", "pytorch", "kubernetes"],
"resume_skills": ["python", "pytorch", "flask"]
}
```
---
### Vector Index
#### `POST /index/build`
Encode and store resumes in the persistent vector index.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `resumes` | File(s) | ✅ | Resume files to index |
| `rebuild` | bool | ❌ | Clear index first (default false) |
#### `POST /index/search`
Instantly search the index for the best matching resumes.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `jd_text` | string | one of | JD text |
| `jd_file` | File | one of | JD file |
| `top_k` | int | ❌ | Number of results (default 5, max 20) |
**Response:**
```json
{
"status": "success",
"duration_ms": 12.4,
"total_found": 2,
"results": [
{
"rank": 1,
"name": "John_Doe",
"similarity_pct": 95.8,
"indexed_at": "2026-07-01T20:29:18",
"text_length": 1763,
"embedding_dim": 768,
"preview": "john doe machine learning engineer..."
}
]
}
```
#### `GET /index/info`
Get index stats (count, backend, dim, etc.)
#### `GET /index/candidates`
List all indexed candidates with metadata.
#### `POST /index/add`
Add a single resume to the existing index without rebuilding.
#### `DELETE /index/clear`
Wipe the entire index.
---
### Utilities
#### `POST /parse`
Parse a file and return raw + cleaned text. Good for debugging.
#### `POST /embed`
Encode any text and return its raw embedding vector.
---
## Frontend Integration (React/Next.js example)
```javascript
// api/smarthire.js
const BASE_URL = "http://localhost:8000";
// Match resumes against a JD
export async function matchResumes(resumeFiles, jdText, similarityWeight = 0.7) {
const form = new FormData();
resumeFiles.forEach(f => form.append("resumes", f));
form.append("jd_text", jdText);
form.append("similarity_weight", similarityWeight);
const res = await fetch(`${BASE_URL}/match`, { method: "POST", body: form });
if (!res.ok) throw new Error(await res.text());
return res.json();
}
// Build vector index
export async function buildIndex(resumeFiles, rebuild = false) {
const form = new FormData();
resumeFiles.forEach(f => form.append("resumes", f));
form.append("rebuild", rebuild);
const res = await fetch(`${BASE_URL}/index/build`, { method: "POST", body: form });
if (!res.ok) throw new Error(await res.text());
return res.json();
}
// Search the vector index
export async function searchIndex(jdText, topK = 5) {
const form = new FormData();
form.append("jd_text", jdText);
form.append("top_k", topK);
const res = await fetch(`${BASE_URL}/index/search`, { method: "POST", body: form });
if (!res.ok) throw new Error(await res.text());
return res.json();
}
// Get model info
export async function getModelInfo() {
const res = await fetch(`${BASE_URL}/model/info`);
return res.json();
}
```
---
## CORS
By default the API allows all origins (`*`).
For production, update `allow_origins` in `api/main.py`:
```python
app.add_middleware(
CORSMiddleware,
allow_origins=["https://your-frontend.com"],
...
)
```