File size: 3,263 Bytes
c008e60
2d01d7b
1f7b170
2d01d7b
 
 
4b0145e
699844f
4b0145e
699844f
2d01d7b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1f7b170
2d01d7b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1f7b170
2d01d7b
 
 
 
 
1f7b170
2d01d7b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c008e60
2d01d7b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
699844f
 
 
 
2d01d7b
 
 
699844f
 
 
 
 
 
 
2d01d7b
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# Talentry AI - HTTP API

The FastAPI server (`talentry-serve` or the HuggingFace Space deployment) exposes
three endpoints. All payloads are JSON; the only non-JSON response is the
CSV download.

Live URL: `https://huggingface.co/spaces/williyam/talentry-ai`
Repo on HF: `https://huggingface.co/spaces/williyam/talentry-ai`
Open-source fine-tuned LLM: `https://huggingface.co/williyam/redrob-qwen-grpo`

(the same origin serves the React SPA).

---

## `GET /api/health`

Liveness probe.

```json
{ "status": "ok", "version": "1.0.0" }
```

---

## `GET /api/sample?limit=10`

Return up to `limit` candidate records from the default 50-row fixture
(`data/raw/sample_candidates.json`). Useful for UI bootstrapping.

`limit` is clamped to `[1, 100]`. Returns an array of raw candidate dicts
matching `candidate_schema.json`.

---

## `POST /api/rank?use_sample=false&top_k=10`

Run the full ranking pipeline.

**Multipart body:**

| Field        | Required             | Notes                                                |
| ------------ | -------------------- | ---------------------------------------------------- |
| `candidates` | yes unless `use_sample=true` | `.json` / `.jsonl` / `.jsonl.gz` upload     |
| `jd`         | no                   | `.txt` / `.md`; defaults to the default JD           |

**Query parameters:**

| Name         | Default | Notes                                                         |
| ------------ | ------- | ------------------------------------------------------------- |
| `use_sample` | `false` | If true, ignores `candidates` and uses the default fixture.   |
| `top_k`      | `10`    | Clamped to `[1, 100]`. Validator-clean CSV only at `top_k=100`. |

**Response:**

```jsonc
{
  "version": "1.0.0",
  "jd": {
    "title": "Senior AI Engineer",
    "seniority": "senior",
    "min_years": 5.0,
    "max_years": 9.0,
    "must_have_skills": [...],
    "preferred_locations": [...]
  },
  "n_candidates": 50,
  "n_returned": 10,
  "session_id": null,
  "results": [
    {
      "candidate_id": "CAND_0000031",
      "rank": 1,
      "score": 1.0101,
      "reasoning": "Strong match - Recommendation Systems Engineer ...",
      "breakdown": {
        "title_alignment": 0.9,
        "semantic_fit": 0.74,
        "skill_evidence": 0.81,
        "experience_band": 1.0,
        "location": 0.85,
        "behavioural": 1.18,
        "honeypot_penalty": 0.0,
        "final": 1.0101
      }
    }
  ]
}
```

When `top_k=100` *and* the input has ≥ 100 candidates, a `session_id` is
returned and the CSV is persisted for download.

---

## `GET /api/submission.csv?session=<id>`

Download the validator-clean ranked shortlist produced by a prior
`/api/rank` call. The file is delivered as `Ranked_shortlist.csv` in
the browser; only `top_k=100` results pass the official
`validate_submission.py` validator.

Sessions are kept in process memory and will not survive a restart.

## `GET /api/submission.xlsx?session=<id>`

Same shortlist as `submission.csv` but returned as a styled XLSX workbook
(frozen header, sized columns, wrapped reasoning column). The file is
delivered as `Ranked_shortlist.xlsx`.


---

## CORS

The server allows all origins (`*`) so the UI and any external sandbox
checker can call it freely.