JobSelect 0.11.2__tar.gz → 0.11.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {jobselect-0.11.2 → jobselect-0.11.3}/JobSelect.egg-info/PKG-INFO +54 -15
- {jobselect-0.11.2 → jobselect-0.11.3}/JobSelect.egg-info/SOURCES.txt +0 -2
- {jobselect-0.11.2 → jobselect-0.11.3}/PKG-INFO +54 -15
- {jobselect-0.11.2 → jobselect-0.11.3}/README.md +53 -14
- {jobselect-0.11.2 → jobselect-0.11.3}/cli/api_val.py +4 -2
- {jobselect-0.11.2 → jobselect-0.11.3}/cli/jobselect.py +18 -16
- {jobselect-0.11.2 → jobselect-0.11.3}/cli/model_select.py +14 -15
- {jobselect-0.11.2 → jobselect-0.11.3}/model/prep/data_prep.py +0 -2
- {jobselect-0.11.2 → jobselect-0.11.3}/model/prep/test.py +1 -0
- {jobselect-0.11.2 → jobselect-0.11.3}/pyproject.toml +1 -1
- {jobselect-0.11.2 → jobselect-0.11.3}/test/test_model.py +1 -3
- jobselect-0.11.2/cli/test_cli.py +0 -60
- {jobselect-0.11.2 → jobselect-0.11.3}/JobSelect.egg-info/dependency_links.txt +0 -0
- {jobselect-0.11.2 → jobselect-0.11.3}/JobSelect.egg-info/entry_points.txt +0 -0
- {jobselect-0.11.2 → jobselect-0.11.3}/JobSelect.egg-info/requires.txt +0 -0
- {jobselect-0.11.2 → jobselect-0.11.3}/JobSelect.egg-info/top_level.txt +0 -0
- {jobselect-0.11.2 → jobselect-0.11.3}/LICENSE +0 -0
- {jobselect-0.11.2 → jobselect-0.11.3}/cli/utils.py +0 -0
- {jobselect-0.11.2 → jobselect-0.11.3}/model/Model.py +0 -0
- {jobselect-0.11.2 → jobselect-0.11.3}/model/__init__.py +0 -0
- {jobselect-0.11.2 → jobselect-0.11.3}/model/eval.py +0 -0
- {jobselect-0.11.2 → jobselect-0.11.3}/model/pred.py +0 -0
- {jobselect-0.11.2 → jobselect-0.11.3}/model/prep/__init__.py +0 -0
- {jobselect-0.11.2 → jobselect-0.11.3}/model/prep/label_vocab.json +0 -0
- {jobselect-0.11.2 → jobselect-0.11.3}/model/prep/prepared_data.npz +0 -0
- {jobselect-0.11.2 → jobselect-0.11.3}/model/prep/sym_map.py +0 -0
- {jobselect-0.11.2 → jobselect-0.11.3}/model/prep/vectorizer.pkl +0 -0
- {jobselect-0.11.2 → jobselect-0.11.3}/model_out/skill_classifier.pt +0 -0
- {jobselect-0.11.2 → jobselect-0.11.3}/model_out/training_history.json +0 -0
- {jobselect-0.11.2 → jobselect-0.11.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: JobSelect
|
|
3
|
-
Version: 0.11.
|
|
3
|
+
Version: 0.11.3
|
|
4
4
|
Summary: A CLI Based AI Skill Classifier for Job Descriptions Build by Akshay Babu
|
|
5
5
|
Author-email: Akshay Babu <akshaysureshbabu100@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -19,7 +19,7 @@ Requires-Dist: requests==2.34.2
|
|
|
19
19
|
Requires-Dist: torch==2.12.1
|
|
20
20
|
Dynamic: license-file
|
|
21
21
|
|
|
22
|
-
# Job Description Skill Classifier [JobSelect v0.
|
|
22
|
+
# Job Description Skill Classifier [JobSelect v0.11.2 & JobAnalyze 6k v1.0] (Multi-Label)
|
|
23
23
|
|
|
24
24
|
[](https://www.python.org/)
|
|
25
25
|
[](https://pytorch.org/)
|
|
@@ -29,6 +29,8 @@ Dynamic: license-file
|
|
|
29
29
|
|
|
30
30
|
This project builds a lightweight text classification pipeline that predicts **multiple technical skills** from a job posting. Given a job description (optionally augmented with role and job type), the model outputs a ranked list of likely skills.
|
|
31
31
|
|
|
32
|
+

|
|
33
|
+
|
|
32
34
|
Installation:
|
|
33
35
|
|
|
34
36
|
- `pip install jobselect`
|
|
@@ -64,7 +66,7 @@ It uses:
|
|
|
64
66
|
|
|
65
67
|
3. **Evaluation** (`model/eval.py`)
|
|
66
68
|
- Loads the trained model.
|
|
67
|
-
- Applies a fixed sigmoid + threshold (**0.
|
|
69
|
+
- Applies a fixed sigmoid + threshold (**0.3**) to obtain binary skill predictions.
|
|
68
70
|
- Reports:
|
|
69
71
|
- Per-skill precision/recall/F1
|
|
70
72
|
- Micro-F1 and Macro-F1
|
|
@@ -118,7 +120,10 @@ It uses:
|
|
|
118
120
|
│ └─ training_history.json # Training loss history (generated by model.py)
|
|
119
121
|
│
|
|
120
122
|
├─ cli/
|
|
121
|
-
│
|
|
123
|
+
│ ├─ jobselect.py # Rich terminal CLI (prompts + prints top skills)
|
|
124
|
+
│ ├─ model_select.py # Inference routing: API-first, LOCAL fallback (key resolved lazily)
|
|
125
|
+
│ └─ api_val.py # API key prompt / mode selection for CLI
|
|
126
|
+
|
|
122
127
|
│
|
|
123
128
|
├─ test/
|
|
124
129
|
│ └─ test_model.py # Pytest checks expected artifacts exist in model_out/ and model/prep/
|
|
@@ -127,10 +132,16 @@ It uses:
|
|
|
127
132
|
│ ├─ 01_EDA.ipynb # Exploratory Data Analysis
|
|
128
133
|
│ └─ 02_Data_Engineering.ipynb # Data engineering / cleaning notes
|
|
129
134
|
│
|
|
135
|
+
├─ api/
|
|
136
|
+
│ ├─ JobAnalyze_API.py # FastAPI service + Pydantic validation + API-key verification
|
|
137
|
+
│ ├─ pred.py # API/server-side prediction wrapper (imports model.pred)
|
|
138
|
+
│ └─ supabase_client.py # Optional API key persistence (Supabase)
|
|
139
|
+
│
|
|
130
140
|
├─ pipeline.py # Executes notebooks + training/eval steps in order
|
|
131
|
-
├─ pyproject.toml # Installs as a cli tool
|
|
141
|
+
├─ pyproject.toml # Installs as a cli tool (jobselect)
|
|
132
142
|
├─ requirements.txt
|
|
133
143
|
└─ README.md
|
|
144
|
+
|
|
134
145
|
```
|
|
135
146
|
|
|
136
147
|
---
|
|
@@ -189,24 +200,48 @@ Outputs include:
|
|
|
189
200
|
|
|
190
201
|
### 5) Predict skills for a new job description
|
|
191
202
|
|
|
192
|
-
#### Option A: Use Python function
|
|
203
|
+
#### Option A: Use Python function (LOCAL model)
|
|
193
204
|
|
|
194
|
-
`from
|
|
205
|
+
`from api.pred import JobAnalyze_6k`
|
|
195
206
|
|
|
196
|
-
`data/sample_data/test.txt` contains an example job description inside.
|
|
207
|
+
`data/sample_data/test.txt` contains an example job description inside. Use:
|
|
197
208
|
|
|
198
|
-
|
|
209
|
+
```python
|
|
210
|
+
JobAnalyze_6k(job_desc, role="AI Engineer", job_type="Junior", top_k=50)
|
|
211
|
+
```
|
|
199
212
|
|
|
200
|
-
|
|
201
|
-
python cli/jobselect.py
|
|
213
|
+
#### Option B: Use the interactive CLI (API-first with validation)
|
|
202
214
|
|
|
203
|
-
|
|
215
|
+
```bash
|
|
216
|
+
python -m cli.jobselect
|
|
204
217
|
|
|
218
|
+
# or after install
|
|
205
219
|
pip install jobselect
|
|
206
220
|
jobselect
|
|
207
221
|
```
|
|
208
222
|
|
|
209
|
-
The CLI
|
|
223
|
+
The CLI:
|
|
224
|
+
|
|
225
|
+
- prompts for **Job Description**, **Role**, and **Type**
|
|
226
|
+
- validates them via the API schema when running in API mode
|
|
227
|
+
- prints the top skills ranked by probability
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
#### Option C: Get Predictions through API (recommended)
|
|
232
|
+
|
|
233
|
+
Currently, the API service is under development, you could press `Enter` on first screen:
|
|
234
|
+
|
|
235
|
+
- The CLI will always prompt the user for an API Key, press `Enter` to skip to LOCAL Mode
|
|
236
|
+
|
|
237
|
+

|
|
238
|
+
|
|
239
|
+
#### Option D: Call the FastAPI service (optional)
|
|
240
|
+
|
|
241
|
+
Run `api/JobAnalyze_API.py`. Requests must include:
|
|
242
|
+
|
|
243
|
+
- header `JobAnalyze_6k_Key` with a valid API key
|
|
244
|
+
- JSON body with `Job_Desc`, `Role`, and `Type` (validated via Pydantic)
|
|
210
245
|
|
|
211
246
|
---
|
|
212
247
|
|
|
@@ -224,14 +259,18 @@ The CLI prompts for job description, role, and job type, then prints the top ski
|
|
|
224
259
|
## Important implementation notes
|
|
225
260
|
|
|
226
261
|
- **Multi-label learning:** Each skill is treated independently (binary relevance via sigmoid + `BCEWithLogitsLoss`).
|
|
227
|
-
- **Evaluation threshold:** `model/eval.py` uses a fixed threshold of **0.
|
|
262
|
+
- **Evaluation threshold:** `model/eval.py` uses a fixed threshold of **0.3**. For production use, you may want per-label thresholds tuned on a validation set.
|
|
228
263
|
- **Dataset size:** The included notebooks and evaluation code suggest the dataset may be small; results can be limited by label frequency and data coverage.
|
|
229
264
|
|
|
230
265
|
---
|
|
231
266
|
|
|
232
267
|
## New features / capabilities
|
|
233
268
|
|
|
234
|
-
- **Rich terminal CLI** (`cli/
|
|
269
|
+
- **Rich terminal CLI** (`cli/jobselect.py`) using `rich` + `pyfiglet` for interactive top-skill display.
|
|
270
|
+
- **API validation + schema enforcement**
|
|
271
|
+
- Input validation via **Pydantic** model constraints in `api/JobAnalyze_API.py`.
|
|
272
|
+
- API key auth via header + secure verification, with optional Supabase-backed storage in `api/supabase_client.py`.
|
|
273
|
+
- CLI mode auto-detection (`cli/api_val.py` + `cli/model_select.py`): uses API when a key is available, otherwise falls back to local inference.
|
|
235
274
|
- **Synonym/phrase normalization hook** (`model/prep/sym_map.py`) applied during data preparation.
|
|
236
275
|
- **Pipeline runner** (`pipeline.py`) to execute notebooks and training steps in sequence.
|
|
237
276
|
|
|
@@ -4,7 +4,6 @@ pyproject.toml
|
|
|
4
4
|
./cli/api_val.py
|
|
5
5
|
./cli/jobselect.py
|
|
6
6
|
./cli/model_select.py
|
|
7
|
-
./cli/test_cli.py
|
|
8
7
|
./cli/utils.py
|
|
9
8
|
./model/Model.py
|
|
10
9
|
./model/__init__.py
|
|
@@ -28,7 +27,6 @@ JobSelect.egg-info/top_level.txt
|
|
|
28
27
|
cli/api_val.py
|
|
29
28
|
cli/jobselect.py
|
|
30
29
|
cli/model_select.py
|
|
31
|
-
cli/test_cli.py
|
|
32
30
|
cli/utils.py
|
|
33
31
|
model/Model.py
|
|
34
32
|
model/__init__.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: JobSelect
|
|
3
|
-
Version: 0.11.
|
|
3
|
+
Version: 0.11.3
|
|
4
4
|
Summary: A CLI Based AI Skill Classifier for Job Descriptions Build by Akshay Babu
|
|
5
5
|
Author-email: Akshay Babu <akshaysureshbabu100@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -19,7 +19,7 @@ Requires-Dist: requests==2.34.2
|
|
|
19
19
|
Requires-Dist: torch==2.12.1
|
|
20
20
|
Dynamic: license-file
|
|
21
21
|
|
|
22
|
-
# Job Description Skill Classifier [JobSelect v0.
|
|
22
|
+
# Job Description Skill Classifier [JobSelect v0.11.2 & JobAnalyze 6k v1.0] (Multi-Label)
|
|
23
23
|
|
|
24
24
|
[](https://www.python.org/)
|
|
25
25
|
[](https://pytorch.org/)
|
|
@@ -29,6 +29,8 @@ Dynamic: license-file
|
|
|
29
29
|
|
|
30
30
|
This project builds a lightweight text classification pipeline that predicts **multiple technical skills** from a job posting. Given a job description (optionally augmented with role and job type), the model outputs a ranked list of likely skills.
|
|
31
31
|
|
|
32
|
+

|
|
33
|
+
|
|
32
34
|
Installation:
|
|
33
35
|
|
|
34
36
|
- `pip install jobselect`
|
|
@@ -64,7 +66,7 @@ It uses:
|
|
|
64
66
|
|
|
65
67
|
3. **Evaluation** (`model/eval.py`)
|
|
66
68
|
- Loads the trained model.
|
|
67
|
-
- Applies a fixed sigmoid + threshold (**0.
|
|
69
|
+
- Applies a fixed sigmoid + threshold (**0.3**) to obtain binary skill predictions.
|
|
68
70
|
- Reports:
|
|
69
71
|
- Per-skill precision/recall/F1
|
|
70
72
|
- Micro-F1 and Macro-F1
|
|
@@ -118,7 +120,10 @@ It uses:
|
|
|
118
120
|
│ └─ training_history.json # Training loss history (generated by model.py)
|
|
119
121
|
│
|
|
120
122
|
├─ cli/
|
|
121
|
-
│
|
|
123
|
+
│ ├─ jobselect.py # Rich terminal CLI (prompts + prints top skills)
|
|
124
|
+
│ ├─ model_select.py # Inference routing: API-first, LOCAL fallback (key resolved lazily)
|
|
125
|
+
│ └─ api_val.py # API key prompt / mode selection for CLI
|
|
126
|
+
|
|
122
127
|
│
|
|
123
128
|
├─ test/
|
|
124
129
|
│ └─ test_model.py # Pytest checks expected artifacts exist in model_out/ and model/prep/
|
|
@@ -127,10 +132,16 @@ It uses:
|
|
|
127
132
|
│ ├─ 01_EDA.ipynb # Exploratory Data Analysis
|
|
128
133
|
│ └─ 02_Data_Engineering.ipynb # Data engineering / cleaning notes
|
|
129
134
|
│
|
|
135
|
+
├─ api/
|
|
136
|
+
│ ├─ JobAnalyze_API.py # FastAPI service + Pydantic validation + API-key verification
|
|
137
|
+
│ ├─ pred.py # API/server-side prediction wrapper (imports model.pred)
|
|
138
|
+
│ └─ supabase_client.py # Optional API key persistence (Supabase)
|
|
139
|
+
│
|
|
130
140
|
├─ pipeline.py # Executes notebooks + training/eval steps in order
|
|
131
|
-
├─ pyproject.toml # Installs as a cli tool
|
|
141
|
+
├─ pyproject.toml # Installs as a cli tool (jobselect)
|
|
132
142
|
├─ requirements.txt
|
|
133
143
|
└─ README.md
|
|
144
|
+
|
|
134
145
|
```
|
|
135
146
|
|
|
136
147
|
---
|
|
@@ -189,24 +200,48 @@ Outputs include:
|
|
|
189
200
|
|
|
190
201
|
### 5) Predict skills for a new job description
|
|
191
202
|
|
|
192
|
-
#### Option A: Use Python function
|
|
203
|
+
#### Option A: Use Python function (LOCAL model)
|
|
193
204
|
|
|
194
|
-
`from
|
|
205
|
+
`from api.pred import JobAnalyze_6k`
|
|
195
206
|
|
|
196
|
-
`data/sample_data/test.txt` contains an example job description inside.
|
|
207
|
+
`data/sample_data/test.txt` contains an example job description inside. Use:
|
|
197
208
|
|
|
198
|
-
|
|
209
|
+
```python
|
|
210
|
+
JobAnalyze_6k(job_desc, role="AI Engineer", job_type="Junior", top_k=50)
|
|
211
|
+
```
|
|
199
212
|
|
|
200
|
-
|
|
201
|
-
python cli/jobselect.py
|
|
213
|
+
#### Option B: Use the interactive CLI (API-first with validation)
|
|
202
214
|
|
|
203
|
-
|
|
215
|
+
```bash
|
|
216
|
+
python -m cli.jobselect
|
|
204
217
|
|
|
218
|
+
# or after install
|
|
205
219
|
pip install jobselect
|
|
206
220
|
jobselect
|
|
207
221
|
```
|
|
208
222
|
|
|
209
|
-
The CLI
|
|
223
|
+
The CLI:
|
|
224
|
+
|
|
225
|
+
- prompts for **Job Description**, **Role**, and **Type**
|
|
226
|
+
- validates them via the API schema when running in API mode
|
|
227
|
+
- prints the top skills ranked by probability
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
#### Option C: Get Predictions through API (recommended)
|
|
232
|
+
|
|
233
|
+
Currently, the API service is under development, you could press `Enter` on first screen:
|
|
234
|
+
|
|
235
|
+
- The CLI will always prompt the user for an API Key, press `Enter` to skip to LOCAL Mode
|
|
236
|
+
|
|
237
|
+

|
|
238
|
+
|
|
239
|
+
#### Option D: Call the FastAPI service (optional)
|
|
240
|
+
|
|
241
|
+
Run `api/JobAnalyze_API.py`. Requests must include:
|
|
242
|
+
|
|
243
|
+
- header `JobAnalyze_6k_Key` with a valid API key
|
|
244
|
+
- JSON body with `Job_Desc`, `Role`, and `Type` (validated via Pydantic)
|
|
210
245
|
|
|
211
246
|
---
|
|
212
247
|
|
|
@@ -224,14 +259,18 @@ The CLI prompts for job description, role, and job type, then prints the top ski
|
|
|
224
259
|
## Important implementation notes
|
|
225
260
|
|
|
226
261
|
- **Multi-label learning:** Each skill is treated independently (binary relevance via sigmoid + `BCEWithLogitsLoss`).
|
|
227
|
-
- **Evaluation threshold:** `model/eval.py` uses a fixed threshold of **0.
|
|
262
|
+
- **Evaluation threshold:** `model/eval.py` uses a fixed threshold of **0.3**. For production use, you may want per-label thresholds tuned on a validation set.
|
|
228
263
|
- **Dataset size:** The included notebooks and evaluation code suggest the dataset may be small; results can be limited by label frequency and data coverage.
|
|
229
264
|
|
|
230
265
|
---
|
|
231
266
|
|
|
232
267
|
## New features / capabilities
|
|
233
268
|
|
|
234
|
-
- **Rich terminal CLI** (`cli/
|
|
269
|
+
- **Rich terminal CLI** (`cli/jobselect.py`) using `rich` + `pyfiglet` for interactive top-skill display.
|
|
270
|
+
- **API validation + schema enforcement**
|
|
271
|
+
- Input validation via **Pydantic** model constraints in `api/JobAnalyze_API.py`.
|
|
272
|
+
- API key auth via header + secure verification, with optional Supabase-backed storage in `api/supabase_client.py`.
|
|
273
|
+
- CLI mode auto-detection (`cli/api_val.py` + `cli/model_select.py`): uses API when a key is available, otherwise falls back to local inference.
|
|
235
274
|
- **Synonym/phrase normalization hook** (`model/prep/sym_map.py`) applied during data preparation.
|
|
236
275
|
- **Pipeline runner** (`pipeline.py`) to execute notebooks and training steps in sequence.
|
|
237
276
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Job Description Skill Classifier [JobSelect v0.
|
|
1
|
+
# Job Description Skill Classifier [JobSelect v0.11.2 & JobAnalyze 6k v1.0] (Multi-Label)
|
|
2
2
|
|
|
3
3
|
[](https://www.python.org/)
|
|
4
4
|
[](https://pytorch.org/)
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
This project builds a lightweight text classification pipeline that predicts **multiple technical skills** from a job posting. Given a job description (optionally augmented with role and job type), the model outputs a ranked list of likely skills.
|
|
10
10
|
|
|
11
|
+

|
|
12
|
+
|
|
11
13
|
Installation:
|
|
12
14
|
|
|
13
15
|
- `pip install jobselect`
|
|
@@ -43,7 +45,7 @@ It uses:
|
|
|
43
45
|
|
|
44
46
|
3. **Evaluation** (`model/eval.py`)
|
|
45
47
|
- Loads the trained model.
|
|
46
|
-
- Applies a fixed sigmoid + threshold (**0.
|
|
48
|
+
- Applies a fixed sigmoid + threshold (**0.3**) to obtain binary skill predictions.
|
|
47
49
|
- Reports:
|
|
48
50
|
- Per-skill precision/recall/F1
|
|
49
51
|
- Micro-F1 and Macro-F1
|
|
@@ -97,7 +99,10 @@ It uses:
|
|
|
97
99
|
│ └─ training_history.json # Training loss history (generated by model.py)
|
|
98
100
|
│
|
|
99
101
|
├─ cli/
|
|
100
|
-
│
|
|
102
|
+
│ ├─ jobselect.py # Rich terminal CLI (prompts + prints top skills)
|
|
103
|
+
│ ├─ model_select.py # Inference routing: API-first, LOCAL fallback (key resolved lazily)
|
|
104
|
+
│ └─ api_val.py # API key prompt / mode selection for CLI
|
|
105
|
+
|
|
101
106
|
│
|
|
102
107
|
├─ test/
|
|
103
108
|
│ └─ test_model.py # Pytest checks expected artifacts exist in model_out/ and model/prep/
|
|
@@ -106,10 +111,16 @@ It uses:
|
|
|
106
111
|
│ ├─ 01_EDA.ipynb # Exploratory Data Analysis
|
|
107
112
|
│ └─ 02_Data_Engineering.ipynb # Data engineering / cleaning notes
|
|
108
113
|
│
|
|
114
|
+
├─ api/
|
|
115
|
+
│ ├─ JobAnalyze_API.py # FastAPI service + Pydantic validation + API-key verification
|
|
116
|
+
│ ├─ pred.py # API/server-side prediction wrapper (imports model.pred)
|
|
117
|
+
│ └─ supabase_client.py # Optional API key persistence (Supabase)
|
|
118
|
+
│
|
|
109
119
|
├─ pipeline.py # Executes notebooks + training/eval steps in order
|
|
110
|
-
├─ pyproject.toml # Installs as a cli tool
|
|
120
|
+
├─ pyproject.toml # Installs as a cli tool (jobselect)
|
|
111
121
|
├─ requirements.txt
|
|
112
122
|
└─ README.md
|
|
123
|
+
|
|
113
124
|
```
|
|
114
125
|
|
|
115
126
|
---
|
|
@@ -168,24 +179,48 @@ Outputs include:
|
|
|
168
179
|
|
|
169
180
|
### 5) Predict skills for a new job description
|
|
170
181
|
|
|
171
|
-
#### Option A: Use Python function
|
|
182
|
+
#### Option A: Use Python function (LOCAL model)
|
|
172
183
|
|
|
173
|
-
`from
|
|
184
|
+
`from api.pred import JobAnalyze_6k`
|
|
174
185
|
|
|
175
|
-
`data/sample_data/test.txt` contains an example job description inside.
|
|
186
|
+
`data/sample_data/test.txt` contains an example job description inside. Use:
|
|
176
187
|
|
|
177
|
-
|
|
188
|
+
```python
|
|
189
|
+
JobAnalyze_6k(job_desc, role="AI Engineer", job_type="Junior", top_k=50)
|
|
190
|
+
```
|
|
178
191
|
|
|
179
|
-
|
|
180
|
-
python cli/jobselect.py
|
|
192
|
+
#### Option B: Use the interactive CLI (API-first with validation)
|
|
181
193
|
|
|
182
|
-
|
|
194
|
+
```bash
|
|
195
|
+
python -m cli.jobselect
|
|
183
196
|
|
|
197
|
+
# or after install
|
|
184
198
|
pip install jobselect
|
|
185
199
|
jobselect
|
|
186
200
|
```
|
|
187
201
|
|
|
188
|
-
The CLI
|
|
202
|
+
The CLI:
|
|
203
|
+
|
|
204
|
+
- prompts for **Job Description**, **Role**, and **Type**
|
|
205
|
+
- validates them via the API schema when running in API mode
|
|
206
|
+
- prints the top skills ranked by probability
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
#### Option C: Get Predictions through API (recommended)
|
|
211
|
+
|
|
212
|
+
Currently, the API service is under development, you could press `Enter` on first screen:
|
|
213
|
+
|
|
214
|
+
- The CLI will always prompt the user for an API Key, press `Enter` to skip to LOCAL Mode
|
|
215
|
+
|
|
216
|
+

|
|
217
|
+
|
|
218
|
+
#### Option D: Call the FastAPI service (optional)
|
|
219
|
+
|
|
220
|
+
Run `api/JobAnalyze_API.py`. Requests must include:
|
|
221
|
+
|
|
222
|
+
- header `JobAnalyze_6k_Key` with a valid API key
|
|
223
|
+
- JSON body with `Job_Desc`, `Role`, and `Type` (validated via Pydantic)
|
|
189
224
|
|
|
190
225
|
---
|
|
191
226
|
|
|
@@ -203,14 +238,18 @@ The CLI prompts for job description, role, and job type, then prints the top ski
|
|
|
203
238
|
## Important implementation notes
|
|
204
239
|
|
|
205
240
|
- **Multi-label learning:** Each skill is treated independently (binary relevance via sigmoid + `BCEWithLogitsLoss`).
|
|
206
|
-
- **Evaluation threshold:** `model/eval.py` uses a fixed threshold of **0.
|
|
241
|
+
- **Evaluation threshold:** `model/eval.py` uses a fixed threshold of **0.3**. For production use, you may want per-label thresholds tuned on a validation set.
|
|
207
242
|
- **Dataset size:** The included notebooks and evaluation code suggest the dataset may be small; results can be limited by label frequency and data coverage.
|
|
208
243
|
|
|
209
244
|
---
|
|
210
245
|
|
|
211
246
|
## New features / capabilities
|
|
212
247
|
|
|
213
|
-
- **Rich terminal CLI** (`cli/
|
|
248
|
+
- **Rich terminal CLI** (`cli/jobselect.py`) using `rich` + `pyfiglet` for interactive top-skill display.
|
|
249
|
+
- **API validation + schema enforcement**
|
|
250
|
+
- Input validation via **Pydantic** model constraints in `api/JobAnalyze_API.py`.
|
|
251
|
+
- API key auth via header + secure verification, with optional Supabase-backed storage in `api/supabase_client.py`.
|
|
252
|
+
- CLI mode auto-detection (`cli/api_val.py` + `cli/model_select.py`): uses API when a key is available, otherwise falls back to local inference.
|
|
214
253
|
- **Synonym/phrase normalization hook** (`model/prep/sym_map.py`) applied during data preparation.
|
|
215
254
|
- **Pipeline runner** (`pipeline.py`) to execute notebooks and training steps in sequence.
|
|
216
255
|
|
|
@@ -7,12 +7,14 @@ Handles the API key prompt. Stores the user-entered key in module-level
|
|
|
7
7
|
No imports from model_select or api_logic — circular import is gone.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
import os
|
|
12
|
+
|
|
13
|
+
from .utils import clear_console, API_title, query
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
key: str = ""
|
|
15
|
-
|
|
17
|
+
|
|
16
18
|
|
|
17
19
|
def val_api() -> str:
|
|
18
20
|
clear_console()
|
|
@@ -4,54 +4,56 @@ Uses relative imports so it works both as an installed pip package
|
|
|
4
4
|
(jobselect command) and during local development (python -m cli.jobselect).
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
|
|
8
|
+
from rich import print
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
|
|
7
11
|
from .api_val import val_api, infer_mode
|
|
8
12
|
from .utils import clear_console, title, query
|
|
9
13
|
from .model_select import predict
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
|
|
15
|
+
|
|
14
16
|
def cli() -> None:
|
|
15
17
|
try:
|
|
16
18
|
console = Console()
|
|
17
|
-
|
|
19
|
+
|
|
18
20
|
val_api()
|
|
19
21
|
infer = infer_mode()
|
|
20
|
-
|
|
22
|
+
|
|
21
23
|
clear_console()
|
|
22
24
|
title()
|
|
23
|
-
|
|
25
|
+
|
|
24
26
|
print(f"[yellow][JobAnalyze : {infer}]")
|
|
25
27
|
print("[yellow]>> Welcome to JobSelect!")
|
|
26
|
-
|
|
28
|
+
|
|
27
29
|
query("Enter Job Description")
|
|
28
30
|
jd = input(" >> ")
|
|
29
|
-
|
|
31
|
+
|
|
30
32
|
query("Enter Role (AI Engineer / AI Developer)")
|
|
31
33
|
role = input(" >> ")
|
|
32
|
-
|
|
34
|
+
|
|
33
35
|
query("Enter Type (Internship / Junior / Senior)")
|
|
34
36
|
job_type = input(" >> ")
|
|
35
|
-
|
|
37
|
+
|
|
36
38
|
with console.status("[bold green]Loading...", spinner="dots2"):
|
|
37
39
|
results, mode = predict(jd, role=role, job_type=job_type)
|
|
38
|
-
|
|
40
|
+
|
|
39
41
|
clear_console()
|
|
40
42
|
title()
|
|
41
|
-
|
|
43
|
+
|
|
42
44
|
mode_colour = "green" if mode == "API" else "cyan"
|
|
43
45
|
print(f"\n [{mode_colour}][Mode: {mode}][/{mode_colour}]")
|
|
44
|
-
|
|
46
|
+
|
|
45
47
|
print("\n [yellow]Job Description Provided : \n", jd.strip())
|
|
46
48
|
print("\n [yellow]Job Role : \n", role)
|
|
47
49
|
print("\n [yellow]Type : \n", job_type)
|
|
48
50
|
print()
|
|
49
|
-
|
|
51
|
+
|
|
50
52
|
print("\n [yellow]TOP Skills \n")
|
|
51
53
|
for label, prob in results:
|
|
52
54
|
bar = "█" * int(prob * 30)
|
|
53
55
|
print(f" {label:25s} {prob:.2f} {bar}\n")
|
|
54
|
-
|
|
56
|
+
|
|
55
57
|
except KeyboardInterrupt:
|
|
56
58
|
print("\n[yellow]Exiting JobSelect")
|
|
57
59
|
|
|
@@ -8,20 +8,19 @@ from dotenv import load_dotenv
|
|
|
8
8
|
import os
|
|
9
9
|
import requests
|
|
10
10
|
import sys
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
|
13
13
|
if str(PROJECT_ROOT) not in sys.path:
|
|
14
14
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
_env_path = Path(__file__).resolve().parent / ".env"
|
|
17
17
|
if not _env_path.exists():
|
|
18
18
|
_env_path = PROJECT_ROOT / ".env"
|
|
19
19
|
load_dotenv(dotenv_path=_env_path)
|
|
20
|
-
|
|
21
|
-
API_URL =
|
|
20
|
+
|
|
21
|
+
API_URL = os.getenv("JOBSELECT_API_URL", "")
|
|
22
22
|
_ENV_KEY = os.getenv("JOBSELECT_API_KEY", "").strip()
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
|
|
25
24
|
def _resolve_key() -> str:
|
|
26
25
|
"""
|
|
27
26
|
Lazy key resolution at inference time (not import time).
|
|
@@ -40,27 +39,27 @@ def _resolve_key() -> str:
|
|
|
40
39
|
pass
|
|
41
40
|
return _ENV_KEY
|
|
42
41
|
|
|
43
|
-
|
|
42
|
+
|
|
44
43
|
def _local_predict(jd: str, role: str, job_type: str):
|
|
45
44
|
from model.pred import JobAnalyze_6k as _job_analyze
|
|
46
45
|
return _job_analyze(jd, role=role, job_type=job_type)
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
|
|
47
|
+
|
|
49
48
|
def _call_api(jd: str, role: str, job_type: str, api_key: str) -> list[tuple[str, float]]:
|
|
50
49
|
endpoint = f"{API_URL}/JobAnalyze_6k"
|
|
51
50
|
headers = {"JobAnalyze_6k_Key": api_key}
|
|
52
51
|
payload = {"Job_Desc": jd, "Role": role, "Type": job_type}
|
|
53
|
-
|
|
52
|
+
|
|
54
53
|
response = requests.post(endpoint, json=payload, headers=headers, timeout=(10, 120))
|
|
55
54
|
response.raise_for_status()
|
|
56
|
-
|
|
55
|
+
|
|
57
56
|
data = response.json()
|
|
58
57
|
return [(skill, float(score)) for skill, score in data["answer"]]
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
|
|
59
|
+
|
|
61
60
|
def predict(jd: str, role: str, job_type: str) -> tuple[list[tuple[str, float]], str]:
|
|
62
61
|
api_key = _resolve_key()
|
|
63
|
-
|
|
62
|
+
|
|
64
63
|
if API_URL and api_key:
|
|
65
64
|
try:
|
|
66
65
|
results = _call_api(jd, role, job_type, api_key)
|
|
@@ -74,6 +73,6 @@ def predict(jd: str, role: str, job_type: str) -> tuple[list[tuple[str, float]],
|
|
|
74
73
|
print(f"[JobSelect] API error {code} — falling back to LOCAL")
|
|
75
74
|
except Exception as e:
|
|
76
75
|
print(f"[JobSelect] Unexpected error: {e} — falling back to LOCAL")
|
|
77
|
-
|
|
76
|
+
|
|
78
77
|
results = _local_predict(jd, role=role, job_type=job_type)
|
|
79
78
|
return results, "LOCAL"
|
|
@@ -6,7 +6,6 @@ Run this script, model/model.py and the notebooks through pipeline.py
|
|
|
6
6
|
|
|
7
7
|
import json
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
|
|
10
9
|
import numpy as np
|
|
11
10
|
import pandas as pd
|
|
12
11
|
import pickle
|
|
@@ -16,7 +15,6 @@ from sklearn.model_selection import train_test_split
|
|
|
16
15
|
|
|
17
16
|
from sym_map import SYNONYM_MAP
|
|
18
17
|
|
|
19
|
-
|
|
20
18
|
df = pd.read_csv(r'C:\Portfolio-Projects\Job-Description-Analysis\data\clean\cleaned_job_descriptions.csv')
|
|
21
19
|
|
|
22
20
|
SKILLS_FIX = {
|
jobselect-0.11.2/cli/test_cli.py
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
test_cli.py — dev/test entry point.
|
|
3
|
-
Run as: python -m cli.test_cli
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from rich import print
|
|
7
|
-
from rich.console import Console
|
|
8
|
-
from .utils import clear_console, title, query
|
|
9
|
-
from .model_select import predict
|
|
10
|
-
from .api_val import val_api, infer_mode
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def cli() -> None:
|
|
14
|
-
try:
|
|
15
|
-
console = Console()
|
|
16
|
-
|
|
17
|
-
val_api()
|
|
18
|
-
infer = infer_mode()
|
|
19
|
-
|
|
20
|
-
clear_console()
|
|
21
|
-
title()
|
|
22
|
-
|
|
23
|
-
print(f"[yellow][JobAnalyze : {infer}]")
|
|
24
|
-
print("[yellow]>> Welcome to JobSelect!")
|
|
25
|
-
|
|
26
|
-
query("Enter Job Description")
|
|
27
|
-
jd = input(" >> ")
|
|
28
|
-
|
|
29
|
-
query("Enter Role (AI Engineer / AI Developer)")
|
|
30
|
-
role = input(" >> ")
|
|
31
|
-
|
|
32
|
-
query("Enter Type (Internship / Junior / Senior)")
|
|
33
|
-
job_type = input(" >> ")
|
|
34
|
-
|
|
35
|
-
with console.status("[bold green]Loading...", spinner="dots2"):
|
|
36
|
-
results, mode = predict(jd, role=role, job_type=job_type)
|
|
37
|
-
|
|
38
|
-
clear_console()
|
|
39
|
-
title()
|
|
40
|
-
|
|
41
|
-
mode_colour = "green" if mode == "API" else "cyan"
|
|
42
|
-
print(f"\n [{mode_colour}][Mode: {mode}][/{mode_colour}]")
|
|
43
|
-
|
|
44
|
-
print("\n [yellow]Job Description Provided : \n", jd.strip())
|
|
45
|
-
print("\n [yellow]Job Role : \n", role)
|
|
46
|
-
print("\n [yellow]Type : \n", job_type)
|
|
47
|
-
print()
|
|
48
|
-
|
|
49
|
-
print("\n [yellow]TOP Skills \n")
|
|
50
|
-
for label, prob in results:
|
|
51
|
-
bar = "█" * int(prob * 30)
|
|
52
|
-
print(f" {label:25s} {prob:.2f} {bar}\n")
|
|
53
|
-
|
|
54
|
-
except KeyboardInterrupt:
|
|
55
|
-
print("\n[yellow]Exiting JobSelect")
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if __name__ == "__main__":
|
|
59
|
-
cli()
|
|
60
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|