careerlm 1.0.0__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.
- careerlm-1.0.0/PKG-INFO +103 -0
- careerlm-1.0.0/README.md +72 -0
- careerlm-1.0.0/careerlm/__init__.py +505 -0
- careerlm-1.0.0/careerlm.egg-info/PKG-INFO +103 -0
- careerlm-1.0.0/careerlm.egg-info/SOURCES.txt +8 -0
- careerlm-1.0.0/careerlm.egg-info/dependency_links.txt +1 -0
- careerlm-1.0.0/careerlm.egg-info/requires.txt +1 -0
- careerlm-1.0.0/careerlm.egg-info/top_level.txt +1 -0
- careerlm-1.0.0/setup.cfg +4 -0
- careerlm-1.0.0/setup.py +28 -0
careerlm-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: careerlm
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Official Python SDK for the CareerLM API — world's first career intelligence API
|
|
5
|
+
Home-page: https://careerstudiomax.com/api/docs
|
|
6
|
+
Author: Career Studio
|
|
7
|
+
Author-email: api@careerstudiomax.com
|
|
8
|
+
Project-URL: Documentation, https://careerstudiomax.com/api/docs
|
|
9
|
+
Project-URL: Source, https://github.com/owolat4real/career-studio
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/owolat4real/career-studio/issues
|
|
11
|
+
Keywords: career,ai,cv,resume,salary,api,careerlm
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Requires-Python: >=3.8
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: httpx>=0.24.0
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: author-email
|
|
22
|
+
Dynamic: classifier
|
|
23
|
+
Dynamic: description
|
|
24
|
+
Dynamic: description-content-type
|
|
25
|
+
Dynamic: home-page
|
|
26
|
+
Dynamic: keywords
|
|
27
|
+
Dynamic: project-url
|
|
28
|
+
Dynamic: requires-dist
|
|
29
|
+
Dynamic: requires-python
|
|
30
|
+
Dynamic: summary
|
|
31
|
+
|
|
32
|
+
# CareerLM Python SDK
|
|
33
|
+
|
|
34
|
+
Official Python client for the [CareerLM API](https://careerstudiomax.com/api/docs) — world's first career intelligence API.
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install careerlm
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quickstart
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from careerlm import CareerLM
|
|
46
|
+
|
|
47
|
+
client = CareerLM(api_key="csk_live_v1_your_key_here")
|
|
48
|
+
|
|
49
|
+
score = client.cv.score(cv_text, job_description)
|
|
50
|
+
print(score["ats_score"]) # 87
|
|
51
|
+
print(score["grade"]) # "B+"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## What you can do
|
|
55
|
+
|
|
56
|
+
| Method | Description |
|
|
57
|
+
|---|---|
|
|
58
|
+
| `client.cv.score(cv_text, job_description)` | Score a CV against a job description |
|
|
59
|
+
| `client.cv.optimise(cv_text, job_description)` | Rewrite and optimise a CV for a target role |
|
|
60
|
+
| `client.salary.benchmark(role, country)` | Salary benchmarking across 196 countries, PPP-adjusted |
|
|
61
|
+
| `client.cover_letter.generate(cv_text, job_description, mode=1)` | Generate a cover letter in one of 15 professional modes |
|
|
62
|
+
| `client.job.match(candidate, job)` | Score how well a candidate matches a job |
|
|
63
|
+
| `client.interview.questions(role)` | Tailored interview questions with model answers |
|
|
64
|
+
| `client.skills.gap(current_skills, target_role)` | Skill-gap analysis with learning ROI and timeline |
|
|
65
|
+
| `client.chat.completions.create(messages)` | OpenAI-compatible career chat completions |
|
|
66
|
+
| `client.context.save(profile)` / `.get(id)` / `.delete(id)` | Persistent career context across calls |
|
|
67
|
+
|
|
68
|
+
## Example: salary benchmark
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
salary = client.salary.benchmark("Data Engineer", "NG", city="Lagos", currency="NGN")
|
|
72
|
+
print(salary["salary_range"]["median"]) # 6800000
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Example: cover letter
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
letter = client.cover_letter.generate(
|
|
79
|
+
cv_text, job_description,
|
|
80
|
+
mode=5, company_name="Paystack", tone="confident",
|
|
81
|
+
)
|
|
82
|
+
print(letter["content"])
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Errors
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from careerlm import CareerLM, CareerLMError
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
client.cv.score(cv_text, job_description)
|
|
92
|
+
except CareerLMError as e:
|
|
93
|
+
print(e.code, e.status, str(e))
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Context manager
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
with CareerLM(api_key="...") as client:
|
|
100
|
+
...
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Full endpoint reference: [careerstudiomax.com/api/docs](https://careerstudiomax.com/api/docs)
|
careerlm-1.0.0/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# CareerLM Python SDK
|
|
2
|
+
|
|
3
|
+
Official Python client for the [CareerLM API](https://careerstudiomax.com/api/docs) — world's first career intelligence API.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install careerlm
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quickstart
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from careerlm import CareerLM
|
|
15
|
+
|
|
16
|
+
client = CareerLM(api_key="csk_live_v1_your_key_here")
|
|
17
|
+
|
|
18
|
+
score = client.cv.score(cv_text, job_description)
|
|
19
|
+
print(score["ats_score"]) # 87
|
|
20
|
+
print(score["grade"]) # "B+"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## What you can do
|
|
24
|
+
|
|
25
|
+
| Method | Description |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `client.cv.score(cv_text, job_description)` | Score a CV against a job description |
|
|
28
|
+
| `client.cv.optimise(cv_text, job_description)` | Rewrite and optimise a CV for a target role |
|
|
29
|
+
| `client.salary.benchmark(role, country)` | Salary benchmarking across 196 countries, PPP-adjusted |
|
|
30
|
+
| `client.cover_letter.generate(cv_text, job_description, mode=1)` | Generate a cover letter in one of 15 professional modes |
|
|
31
|
+
| `client.job.match(candidate, job)` | Score how well a candidate matches a job |
|
|
32
|
+
| `client.interview.questions(role)` | Tailored interview questions with model answers |
|
|
33
|
+
| `client.skills.gap(current_skills, target_role)` | Skill-gap analysis with learning ROI and timeline |
|
|
34
|
+
| `client.chat.completions.create(messages)` | OpenAI-compatible career chat completions |
|
|
35
|
+
| `client.context.save(profile)` / `.get(id)` / `.delete(id)` | Persistent career context across calls |
|
|
36
|
+
|
|
37
|
+
## Example: salary benchmark
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
salary = client.salary.benchmark("Data Engineer", "NG", city="Lagos", currency="NGN")
|
|
41
|
+
print(salary["salary_range"]["median"]) # 6800000
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Example: cover letter
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
letter = client.cover_letter.generate(
|
|
48
|
+
cv_text, job_description,
|
|
49
|
+
mode=5, company_name="Paystack", tone="confident",
|
|
50
|
+
)
|
|
51
|
+
print(letter["content"])
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Errors
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from careerlm import CareerLM, CareerLMError
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
client.cv.score(cv_text, job_description)
|
|
61
|
+
except CareerLMError as e:
|
|
62
|
+
print(e.code, e.status, str(e))
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Context manager
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
with CareerLM(api_key="...") as client:
|
|
69
|
+
...
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Full endpoint reference: [careerstudiomax.com/api/docs](https://careerstudiomax.com/api/docs)
|
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CareerLM Python SDK
|
|
3
|
+
pip install careerlm
|
|
4
|
+
|
|
5
|
+
from careerlm import CareerLM
|
|
6
|
+
client = CareerLM(api_key="csk_live_v1_your_key_here")
|
|
7
|
+
|
|
8
|
+
score = client.cv.score(cv_text, job_description)
|
|
9
|
+
print(score["ats_score"]) # 87
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import httpx
|
|
13
|
+
from typing import Optional, List, Dict, Any
|
|
14
|
+
|
|
15
|
+
__version__ = "1.0.0"
|
|
16
|
+
__all__ = ["CareerLM", "CareerLMError"]
|
|
17
|
+
|
|
18
|
+
_BASE_URL = "https://careerlm-api.careerstudiomax.com/v1"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CareerLMError(Exception):
|
|
22
|
+
"""Raised when the CareerLM API returns an error response."""
|
|
23
|
+
def __init__(self, message: str, code: str = "api_error", status: int = 500):
|
|
24
|
+
super().__init__(message)
|
|
25
|
+
self.code = code
|
|
26
|
+
self.status = status
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class _Namespace:
|
|
30
|
+
"""Base class for API namespaces."""
|
|
31
|
+
def __init__(self, client: "CareerLM"):
|
|
32
|
+
self._c = client
|
|
33
|
+
|
|
34
|
+
def _post(self, endpoint: str, **kwargs) -> Dict[str, Any]:
|
|
35
|
+
return self._c._post(endpoint, **kwargs)
|
|
36
|
+
|
|
37
|
+
def _get(self, endpoint: str) -> Dict[str, Any]:
|
|
38
|
+
return self._c._get(endpoint)
|
|
39
|
+
|
|
40
|
+
def _delete(self, endpoint: str) -> Dict[str, Any]:
|
|
41
|
+
return self._c._delete(endpoint)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class _CV(_Namespace):
|
|
45
|
+
def score(
|
|
46
|
+
self,
|
|
47
|
+
cv_text: str,
|
|
48
|
+
job_description: Optional[str] = None,
|
|
49
|
+
*,
|
|
50
|
+
target_role: Optional[str] = None,
|
|
51
|
+
target_country: str = "GB",
|
|
52
|
+
include_suggestions: bool = True,
|
|
53
|
+
include_keywords: bool = True,
|
|
54
|
+
format: str = "detailed",
|
|
55
|
+
**kwargs,
|
|
56
|
+
) -> Dict[str, Any]:
|
|
57
|
+
"""Score a CV against a job description.
|
|
58
|
+
|
|
59
|
+
Returns ats_score, grade, keyword_match, section_scores, top_suggestions.
|
|
60
|
+
|
|
61
|
+
Example:
|
|
62
|
+
score = client.cv.score(cv_text, job_desc, target_role="Senior Engineer")
|
|
63
|
+
print(score["ats_score"]) # 87
|
|
64
|
+
print(score["grade"]) # "B+"
|
|
65
|
+
"""
|
|
66
|
+
return self._post(
|
|
67
|
+
"/career/cv/score",
|
|
68
|
+
cv_text=cv_text,
|
|
69
|
+
job_description=job_description,
|
|
70
|
+
target_role=target_role,
|
|
71
|
+
target_country=target_country,
|
|
72
|
+
options=dict(
|
|
73
|
+
include_suggestions=include_suggestions,
|
|
74
|
+
include_keywords=include_keywords,
|
|
75
|
+
format=format,
|
|
76
|
+
**kwargs,
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def optimise(
|
|
81
|
+
self,
|
|
82
|
+
cv_text: str,
|
|
83
|
+
job_description: Optional[str] = None,
|
|
84
|
+
*,
|
|
85
|
+
target_role: Optional[str] = None,
|
|
86
|
+
target_country: str = "GB",
|
|
87
|
+
rewrite_level: str = "moderate",
|
|
88
|
+
preserve_voice: bool = True,
|
|
89
|
+
add_metrics: bool = True,
|
|
90
|
+
**kwargs,
|
|
91
|
+
) -> Dict[str, Any]:
|
|
92
|
+
"""Rewrite and optimise a CV for a target role.
|
|
93
|
+
|
|
94
|
+
Requires Starter tier or above.
|
|
95
|
+
|
|
96
|
+
Returns optimised_cv, changes, before_score, after_score.
|
|
97
|
+
|
|
98
|
+
Example:
|
|
99
|
+
result = client.cv.optimise(cv_text, jd, target_role="Data Engineer")
|
|
100
|
+
print(result["after_score"]) # 91
|
|
101
|
+
"""
|
|
102
|
+
return self._post(
|
|
103
|
+
"/career/cv/optimise",
|
|
104
|
+
cv_text=cv_text,
|
|
105
|
+
job_description=job_description,
|
|
106
|
+
target_role=target_role,
|
|
107
|
+
target_country=target_country,
|
|
108
|
+
options=dict(
|
|
109
|
+
rewrite_level=rewrite_level,
|
|
110
|
+
preserve_voice=preserve_voice,
|
|
111
|
+
add_metrics=add_metrics,
|
|
112
|
+
**kwargs,
|
|
113
|
+
),
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class _Salary(_Namespace):
|
|
118
|
+
def benchmark(
|
|
119
|
+
self,
|
|
120
|
+
role: str,
|
|
121
|
+
country: str = "GB",
|
|
122
|
+
*,
|
|
123
|
+
city: Optional[str] = None,
|
|
124
|
+
years_experience: int = 0,
|
|
125
|
+
skills: Optional[List[str]] = None,
|
|
126
|
+
company_size: Optional[str] = None,
|
|
127
|
+
currency: Optional[str] = None,
|
|
128
|
+
include_equity: bool = False,
|
|
129
|
+
include_benefits: bool = False,
|
|
130
|
+
**kwargs,
|
|
131
|
+
) -> Dict[str, Any]:
|
|
132
|
+
"""Benchmark salary across 196 countries with PPP adjustment.
|
|
133
|
+
|
|
134
|
+
Requires Starter tier or above.
|
|
135
|
+
|
|
136
|
+
Returns salary_range (low/median/high/top_10_percent), usd_equivalent,
|
|
137
|
+
skill_premiums, negotiation_target, negotiation_script.
|
|
138
|
+
|
|
139
|
+
Example:
|
|
140
|
+
s = client.salary.benchmark("Data Engineer", "NG",
|
|
141
|
+
city="Lagos", currency="NGN")
|
|
142
|
+
print(s["salary_range"]["median"]) # 6800000
|
|
143
|
+
"""
|
|
144
|
+
options: Dict[str, Any] = {}
|
|
145
|
+
if currency: options["currency"] = currency
|
|
146
|
+
if include_equity: options["include_equity"] = include_equity
|
|
147
|
+
if include_benefits: options["include_benefits"] = include_benefits
|
|
148
|
+
options.update(kwargs)
|
|
149
|
+
return self._post(
|
|
150
|
+
"/career/salary/benchmark",
|
|
151
|
+
role=role,
|
|
152
|
+
country=country,
|
|
153
|
+
city=city,
|
|
154
|
+
years_experience=years_experience,
|
|
155
|
+
skills=skills or [],
|
|
156
|
+
company_size=company_size,
|
|
157
|
+
options=options or None,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class _CoverLetter(_Namespace):
|
|
162
|
+
"""Generate cover letters in 15 professional modes."""
|
|
163
|
+
|
|
164
|
+
MODES = {
|
|
165
|
+
1: "Achievement-led", 2: "Insight-led", 3: "Problem-solution",
|
|
166
|
+
4: "Narrative arc", 5: "Company-specific", 6: "Question-led",
|
|
167
|
+
7: "Bold statement", 8: "Data-driven", 9: "Cultural fit",
|
|
168
|
+
10: "Career transition", 11: "Promotion-within", 12: "Referral",
|
|
169
|
+
13: "Remote confidence", 14: "Multilingual", 15: "Executive gravitas",
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
def generate(
|
|
173
|
+
self,
|
|
174
|
+
cv_text: Optional[str] = None,
|
|
175
|
+
job_description: Optional[str] = None,
|
|
176
|
+
*,
|
|
177
|
+
mode: int = 1,
|
|
178
|
+
company_name: Optional[str] = None,
|
|
179
|
+
candidate_name: Optional[str] = None,
|
|
180
|
+
target_country: str = "GB",
|
|
181
|
+
tone: str = "confident",
|
|
182
|
+
word_limit: int = 280,
|
|
183
|
+
**kwargs,
|
|
184
|
+
) -> Dict[str, Any]:
|
|
185
|
+
"""Generate a cover letter in one of 15 professional modes.
|
|
186
|
+
|
|
187
|
+
Requires Starter tier or above.
|
|
188
|
+
|
|
189
|
+
Modes:
|
|
190
|
+
1 Achievement-led 2 Insight-led 3 Problem-solution
|
|
191
|
+
4 Narrative arc 5 Company-specific 6 Question-led
|
|
192
|
+
7 Bold statement 8 Data-driven 9 Cultural fit
|
|
193
|
+
10 Career transition 11 Promotion-within 12 Referral
|
|
194
|
+
13 Remote confidence 14 Multilingual 15 Executive gravitas
|
|
195
|
+
|
|
196
|
+
Example:
|
|
197
|
+
letter = client.cover_letter.generate(
|
|
198
|
+
cv_text, jd,
|
|
199
|
+
mode=5, company_name="Paystack", tone="confident"
|
|
200
|
+
)
|
|
201
|
+
print(letter["content"])
|
|
202
|
+
"""
|
|
203
|
+
if not 1 <= mode <= 15:
|
|
204
|
+
raise CareerLMError("mode must be between 1 and 15", "invalid_mode", 400)
|
|
205
|
+
return self._post(
|
|
206
|
+
"/career/cover-letter/generate",
|
|
207
|
+
cv_text=cv_text,
|
|
208
|
+
job_description=job_description,
|
|
209
|
+
mode=mode,
|
|
210
|
+
company_name=company_name,
|
|
211
|
+
candidate_name=candidate_name,
|
|
212
|
+
target_country=target_country,
|
|
213
|
+
tone=tone,
|
|
214
|
+
options={"word_limit": word_limit, **kwargs},
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class _Job(_Namespace):
|
|
219
|
+
def match(
|
|
220
|
+
self,
|
|
221
|
+
candidate: Dict[str, Any],
|
|
222
|
+
job: Dict[str, Any],
|
|
223
|
+
) -> Dict[str, Any]:
|
|
224
|
+
"""Score how well a candidate matches a job.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
candidate: { cv_text?, skills, years_experience, location, salary_expectation? }
|
|
228
|
+
job: { title, description?, required_skills, salary_min?, salary_max?,
|
|
229
|
+
location?, remote? }
|
|
230
|
+
|
|
231
|
+
Returns overall_score, grade, dimensions, gaps, strengths, interview_probability,
|
|
232
|
+
apply_recommendation.
|
|
233
|
+
|
|
234
|
+
Example:
|
|
235
|
+
result = client.job.match(
|
|
236
|
+
candidate={"skills": ["Python","SQL"], "years_experience": 5},
|
|
237
|
+
job={"title": "Data Engineer", "required_skills": ["Python","Spark","dbt"]}
|
|
238
|
+
)
|
|
239
|
+
print(result["interview_probability"]) # 0.72
|
|
240
|
+
"""
|
|
241
|
+
return self._post("/career/job/match", candidate=candidate, job=job)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class _Interview(_Namespace):
|
|
245
|
+
def questions(
|
|
246
|
+
self,
|
|
247
|
+
role: str,
|
|
248
|
+
*,
|
|
249
|
+
company: Optional[str] = None,
|
|
250
|
+
interview_type: str = "behavioural",
|
|
251
|
+
cv_text: Optional[str] = None,
|
|
252
|
+
count: int = 10,
|
|
253
|
+
include_answers: bool = True,
|
|
254
|
+
seniority: str = "mid",
|
|
255
|
+
**kwargs,
|
|
256
|
+
) -> Dict[str, Any]:
|
|
257
|
+
"""Generate tailored interview questions with model answers.
|
|
258
|
+
|
|
259
|
+
Requires Starter tier or above.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
role: Target job role
|
|
263
|
+
company: Specific company (makes questions more targeted)
|
|
264
|
+
interview_type: "behavioural" | "technical" | "case" | "all"
|
|
265
|
+
count: Number of questions (max 20)
|
|
266
|
+
seniority: "junior" | "mid" | "senior" | "director"
|
|
267
|
+
|
|
268
|
+
Example:
|
|
269
|
+
qs = client.interview.questions(
|
|
270
|
+
"Product Manager", company="Spotify",
|
|
271
|
+
interview_type="behavioural", count=10
|
|
272
|
+
)
|
|
273
|
+
"""
|
|
274
|
+
return self._post(
|
|
275
|
+
"/career/interview/questions",
|
|
276
|
+
role=role,
|
|
277
|
+
company=company,
|
|
278
|
+
interview_type=interview_type,
|
|
279
|
+
cv_text=cv_text,
|
|
280
|
+
count=min(count, 20),
|
|
281
|
+
include_answers=include_answers,
|
|
282
|
+
seniority=seniority,
|
|
283
|
+
**kwargs,
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
class _Skills(_Namespace):
|
|
288
|
+
def gap(
|
|
289
|
+
self,
|
|
290
|
+
current_skills: List[str],
|
|
291
|
+
target_role: str,
|
|
292
|
+
*,
|
|
293
|
+
target_country: str = "GB",
|
|
294
|
+
years_to_achieve: int = 12,
|
|
295
|
+
current_salary: Optional[int] = None,
|
|
296
|
+
include_roi: bool = False,
|
|
297
|
+
include_resources: bool = True,
|
|
298
|
+
currency: str = "GBP",
|
|
299
|
+
**kwargs,
|
|
300
|
+
) -> Dict[str, Any]:
|
|
301
|
+
"""Analyse skill gaps with learning ROI and timeline.
|
|
302
|
+
|
|
303
|
+
Returns readiness_score, gaps (with learn_hours, salary_impact, free_resources),
|
|
304
|
+
estimated_time_to_ready, total_salary_unlock.
|
|
305
|
+
|
|
306
|
+
Example:
|
|
307
|
+
gap = client.skills.gap(
|
|
308
|
+
["Python", "SQL", "Excel"],
|
|
309
|
+
"Senior Data Scientist",
|
|
310
|
+
target_country="DE",
|
|
311
|
+
currency="EUR",
|
|
312
|
+
include_roi=True,
|
|
313
|
+
)
|
|
314
|
+
print(gap["estimated_time_to_ready"]) # "14 months"
|
|
315
|
+
print(gap["total_salary_unlock"]) # "+€22,000/year"
|
|
316
|
+
"""
|
|
317
|
+
return self._post(
|
|
318
|
+
"/career/skills/gap",
|
|
319
|
+
current_skills=current_skills,
|
|
320
|
+
target_role=target_role,
|
|
321
|
+
target_country=target_country,
|
|
322
|
+
years_to_achieve=years_to_achieve,
|
|
323
|
+
current_salary=current_salary,
|
|
324
|
+
options=dict(
|
|
325
|
+
include_roi=include_roi,
|
|
326
|
+
include_resources=include_resources,
|
|
327
|
+
currency=currency,
|
|
328
|
+
**kwargs,
|
|
329
|
+
),
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
class _Chat(_Namespace):
|
|
334
|
+
"""OpenAI-compatible career chat completions."""
|
|
335
|
+
|
|
336
|
+
class _Completions:
|
|
337
|
+
def __init__(self, client: "CareerLM"):
|
|
338
|
+
self._c = client
|
|
339
|
+
|
|
340
|
+
def create(
|
|
341
|
+
self,
|
|
342
|
+
messages: List[Dict[str, str]],
|
|
343
|
+
*,
|
|
344
|
+
model: str = "careerlm-standard",
|
|
345
|
+
max_tokens: int = 800,
|
|
346
|
+
temperature: float = 0.7,
|
|
347
|
+
**kwargs,
|
|
348
|
+
) -> Dict[str, Any]:
|
|
349
|
+
"""Career-aware chat completions (OpenAI-compatible).
|
|
350
|
+
|
|
351
|
+
Drop-in replacement for openai.chat.completions.create().
|
|
352
|
+
|
|
353
|
+
Example:
|
|
354
|
+
resp = client.chat.completions.create([
|
|
355
|
+
{"role": "user", "content": "How do I negotiate salary?"}
|
|
356
|
+
])
|
|
357
|
+
print(resp["choices"][0]["message"]["content"])
|
|
358
|
+
"""
|
|
359
|
+
return self._c._post(
|
|
360
|
+
"/career/chat/completions",
|
|
361
|
+
messages=messages,
|
|
362
|
+
model=model,
|
|
363
|
+
max_tokens=max_tokens,
|
|
364
|
+
temperature=temperature,
|
|
365
|
+
**kwargs,
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
def __init__(self, client: "CareerLM"):
|
|
369
|
+
super().__init__(client)
|
|
370
|
+
self.completions = self._Completions(client)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
class _Context(_Namespace):
|
|
374
|
+
def save(
|
|
375
|
+
self,
|
|
376
|
+
profile: Dict[str, Any],
|
|
377
|
+
context_id: Optional[str] = None,
|
|
378
|
+
) -> Dict[str, Any]:
|
|
379
|
+
"""Save or update a career context for persistent memory across API calls.
|
|
380
|
+
|
|
381
|
+
Example:
|
|
382
|
+
ctx = client.context.save({
|
|
383
|
+
"current_role": "Software Engineer",
|
|
384
|
+
"target_role": "Engineering Manager",
|
|
385
|
+
"skills": ["Python", "Go", "AWS"],
|
|
386
|
+
"country": "GB",
|
|
387
|
+
})
|
|
388
|
+
context_id = ctx["context_id"]
|
|
389
|
+
"""
|
|
390
|
+
return self._post("/career/context", profile=profile, context_id=context_id)
|
|
391
|
+
|
|
392
|
+
def get(self, context_id: str) -> Dict[str, Any]:
|
|
393
|
+
"""Retrieve a career context by ID."""
|
|
394
|
+
return self._get(f"/career/context/{context_id}")
|
|
395
|
+
|
|
396
|
+
def delete(self, context_id: str) -> Dict[str, Any]:
|
|
397
|
+
"""Delete a career context."""
|
|
398
|
+
return self._delete(f"/career/context/{context_id}")
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
class CareerLM:
|
|
402
|
+
"""CareerLM Python SDK — world's first career intelligence API.
|
|
403
|
+
|
|
404
|
+
Usage:
|
|
405
|
+
from careerlm import CareerLM
|
|
406
|
+
|
|
407
|
+
client = CareerLM(api_key="csk_live_v1_your_key_here")
|
|
408
|
+
|
|
409
|
+
# Score a CV
|
|
410
|
+
score = client.cv.score(cv_text, job_description)
|
|
411
|
+
print(score["ats_score"]) # 87
|
|
412
|
+
|
|
413
|
+
# Benchmark salary (196 countries)
|
|
414
|
+
salary = client.salary.benchmark("Data Engineer", "NG",
|
|
415
|
+
city="Lagos", currency="NGN")
|
|
416
|
+
print(salary["salary_range"]["median"]) # 6800000
|
|
417
|
+
|
|
418
|
+
# Generate cover letter
|
|
419
|
+
letter = client.cover_letter.generate(cv_text, jd,
|
|
420
|
+
mode=5, company_name="Paystack")
|
|
421
|
+
print(letter["content"])
|
|
422
|
+
|
|
423
|
+
# Skill gap analysis
|
|
424
|
+
gap = client.skills.gap(["Python", "SQL"], "Senior Data Scientist",
|
|
425
|
+
target_country="DE", include_roi=True)
|
|
426
|
+
print(gap["estimated_time_to_ready"]) # "14 months"
|
|
427
|
+
"""
|
|
428
|
+
|
|
429
|
+
def __init__(
|
|
430
|
+
self,
|
|
431
|
+
api_key: str,
|
|
432
|
+
*,
|
|
433
|
+
base_url: str = _BASE_URL,
|
|
434
|
+
timeout: float = 30.0,
|
|
435
|
+
):
|
|
436
|
+
if not api_key:
|
|
437
|
+
raise CareerLMError(
|
|
438
|
+
"API key is required — get yours free at careerstudiomax.com/api",
|
|
439
|
+
"missing_api_key",
|
|
440
|
+
0,
|
|
441
|
+
)
|
|
442
|
+
self._api_key = api_key
|
|
443
|
+
self._base_url = base_url.rstrip("/")
|
|
444
|
+
self._http = httpx.Client(
|
|
445
|
+
headers={
|
|
446
|
+
"X-Api-Key": api_key,
|
|
447
|
+
"Content-Type": "application/json",
|
|
448
|
+
"User-Agent": f"careerlm-python/{__version__}",
|
|
449
|
+
},
|
|
450
|
+
timeout=timeout,
|
|
451
|
+
)
|
|
452
|
+
# Namespace instances
|
|
453
|
+
self.cv = _CV(self)
|
|
454
|
+
self.salary = _Salary(self)
|
|
455
|
+
self.cover_letter = _CoverLetter(self)
|
|
456
|
+
self.job = _Job(self)
|
|
457
|
+
self.interview = _Interview(self)
|
|
458
|
+
self.skills = _Skills(self)
|
|
459
|
+
self.chat = _Chat(self)
|
|
460
|
+
self.context = _Context(self)
|
|
461
|
+
|
|
462
|
+
def _request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
|
|
463
|
+
url = f"{self._base_url}{endpoint}"
|
|
464
|
+
# Strip None values so optional params don't pollute the JSON body
|
|
465
|
+
body = {k: v for k, v in kwargs.items() if v is not None} if kwargs else None
|
|
466
|
+
try:
|
|
467
|
+
resp = self._http.request(method, url, json=body)
|
|
468
|
+
except httpx.TimeoutException:
|
|
469
|
+
raise CareerLMError("Request timed out — try a shorter input", "timeout", 504)
|
|
470
|
+
except httpx.ConnectError:
|
|
471
|
+
raise CareerLMError(
|
|
472
|
+
"Could not reach the CareerLM API — check your network connection",
|
|
473
|
+
"connection_error",
|
|
474
|
+
0,
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
if not resp.is_success:
|
|
478
|
+
try:
|
|
479
|
+
data = resp.json()
|
|
480
|
+
msg = data.get("error", {}).get("message") or "Request failed"
|
|
481
|
+
code = data.get("error", {}).get("code") or "api_error"
|
|
482
|
+
except Exception:
|
|
483
|
+
msg, code = resp.text[:200] or "Request failed", "api_error"
|
|
484
|
+
raise CareerLMError(msg, code, resp.status_code)
|
|
485
|
+
|
|
486
|
+
return resp.json()
|
|
487
|
+
|
|
488
|
+
def _post(self, endpoint: str, **kwargs) -> Dict[str, Any]:
|
|
489
|
+
return self._request("POST", endpoint, **kwargs)
|
|
490
|
+
|
|
491
|
+
def _get(self, endpoint: str) -> Dict[str, Any]:
|
|
492
|
+
return self._request("GET", endpoint)
|
|
493
|
+
|
|
494
|
+
def _delete(self, endpoint: str) -> Dict[str, Any]:
|
|
495
|
+
return self._request("DELETE", endpoint)
|
|
496
|
+
|
|
497
|
+
def close(self):
|
|
498
|
+
"""Close the underlying HTTP client."""
|
|
499
|
+
self._http.close()
|
|
500
|
+
|
|
501
|
+
def __enter__(self):
|
|
502
|
+
return self
|
|
503
|
+
|
|
504
|
+
def __exit__(self, *_):
|
|
505
|
+
self.close()
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: careerlm
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Official Python SDK for the CareerLM API — world's first career intelligence API
|
|
5
|
+
Home-page: https://careerstudiomax.com/api/docs
|
|
6
|
+
Author: Career Studio
|
|
7
|
+
Author-email: api@careerstudiomax.com
|
|
8
|
+
Project-URL: Documentation, https://careerstudiomax.com/api/docs
|
|
9
|
+
Project-URL: Source, https://github.com/owolat4real/career-studio
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/owolat4real/career-studio/issues
|
|
11
|
+
Keywords: career,ai,cv,resume,salary,api,careerlm
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Requires-Python: >=3.8
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: httpx>=0.24.0
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: author-email
|
|
22
|
+
Dynamic: classifier
|
|
23
|
+
Dynamic: description
|
|
24
|
+
Dynamic: description-content-type
|
|
25
|
+
Dynamic: home-page
|
|
26
|
+
Dynamic: keywords
|
|
27
|
+
Dynamic: project-url
|
|
28
|
+
Dynamic: requires-dist
|
|
29
|
+
Dynamic: requires-python
|
|
30
|
+
Dynamic: summary
|
|
31
|
+
|
|
32
|
+
# CareerLM Python SDK
|
|
33
|
+
|
|
34
|
+
Official Python client for the [CareerLM API](https://careerstudiomax.com/api/docs) — world's first career intelligence API.
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install careerlm
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quickstart
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from careerlm import CareerLM
|
|
46
|
+
|
|
47
|
+
client = CareerLM(api_key="csk_live_v1_your_key_here")
|
|
48
|
+
|
|
49
|
+
score = client.cv.score(cv_text, job_description)
|
|
50
|
+
print(score["ats_score"]) # 87
|
|
51
|
+
print(score["grade"]) # "B+"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## What you can do
|
|
55
|
+
|
|
56
|
+
| Method | Description |
|
|
57
|
+
|---|---|
|
|
58
|
+
| `client.cv.score(cv_text, job_description)` | Score a CV against a job description |
|
|
59
|
+
| `client.cv.optimise(cv_text, job_description)` | Rewrite and optimise a CV for a target role |
|
|
60
|
+
| `client.salary.benchmark(role, country)` | Salary benchmarking across 196 countries, PPP-adjusted |
|
|
61
|
+
| `client.cover_letter.generate(cv_text, job_description, mode=1)` | Generate a cover letter in one of 15 professional modes |
|
|
62
|
+
| `client.job.match(candidate, job)` | Score how well a candidate matches a job |
|
|
63
|
+
| `client.interview.questions(role)` | Tailored interview questions with model answers |
|
|
64
|
+
| `client.skills.gap(current_skills, target_role)` | Skill-gap analysis with learning ROI and timeline |
|
|
65
|
+
| `client.chat.completions.create(messages)` | OpenAI-compatible career chat completions |
|
|
66
|
+
| `client.context.save(profile)` / `.get(id)` / `.delete(id)` | Persistent career context across calls |
|
|
67
|
+
|
|
68
|
+
## Example: salary benchmark
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
salary = client.salary.benchmark("Data Engineer", "NG", city="Lagos", currency="NGN")
|
|
72
|
+
print(salary["salary_range"]["median"]) # 6800000
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Example: cover letter
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
letter = client.cover_letter.generate(
|
|
79
|
+
cv_text, job_description,
|
|
80
|
+
mode=5, company_name="Paystack", tone="confident",
|
|
81
|
+
)
|
|
82
|
+
print(letter["content"])
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Errors
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from careerlm import CareerLM, CareerLMError
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
client.cv.score(cv_text, job_description)
|
|
92
|
+
except CareerLMError as e:
|
|
93
|
+
print(e.code, e.status, str(e))
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Context manager
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
with CareerLM(api_key="...") as client:
|
|
100
|
+
...
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Full endpoint reference: [careerstudiomax.com/api/docs](https://careerstudiomax.com/api/docs)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
httpx>=0.24.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
careerlm
|
careerlm-1.0.0/setup.cfg
ADDED
careerlm-1.0.0/setup.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="careerlm",
|
|
5
|
+
version="1.0.0",
|
|
6
|
+
description="Official Python SDK for the CareerLM API — world's first career intelligence API",
|
|
7
|
+
long_description=open("README.md").read() if __import__("os").path.exists("README.md") else "",
|
|
8
|
+
long_description_content_type="text/markdown",
|
|
9
|
+
author="Career Studio",
|
|
10
|
+
author_email="api@careerstudiomax.com",
|
|
11
|
+
url="https://careerstudiomax.com/api/docs",
|
|
12
|
+
project_urls={
|
|
13
|
+
"Documentation": "https://careerstudiomax.com/api/docs",
|
|
14
|
+
"Source": "https://github.com/owolat4real/career-studio",
|
|
15
|
+
"Bug Tracker": "https://github.com/owolat4real/career-studio/issues",
|
|
16
|
+
},
|
|
17
|
+
packages=find_packages(),
|
|
18
|
+
python_requires=">=3.8",
|
|
19
|
+
install_requires=["httpx>=0.24.0"],
|
|
20
|
+
keywords=["career", "ai", "cv", "resume", "salary", "api", "careerlm"],
|
|
21
|
+
classifiers=[
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"License :: OSI Approved :: MIT License",
|
|
24
|
+
"Operating System :: OS Independent",
|
|
25
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
26
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
27
|
+
],
|
|
28
|
+
)
|