aiondtech 2.1.0__py3-none-any.whl → 2.2.0__py3-none-any.whl

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.
aiondtech/__init__.py CHANGED
@@ -31,7 +31,7 @@ Environment Variables:
31
31
  AIONDTECH_BASE_URL: Custom API URL (default: https://api.dev.aiondtech.com)
32
32
  """
33
33
 
34
- __version__ = "2.1.0"
34
+ __version__ = "2.2.0"
35
35
  __author__ = "AiondTech"
36
36
 
37
37
  from .client import ResumeAnalyser, Client
aiondtech/client.py CHANGED
@@ -1,609 +1,3 @@
1
- # """
2
- # AiondTech Resume Analyser API Client - Updated Version
3
-
4
- # This is the main entry point for interacting with the API.
5
- # Supports all external endpoints with credit tracking.
6
-
7
- # Endpoints:
8
- # - resumes.upload() - Upload resume, get ID
9
- # - resumes.upload_and_analyze() - Upload + parse
10
- # - resumes.upload_analyze_compare() - Upload + parse + compare to job
11
- # - resumes.analyze() - Parse existing resume by ID
12
- # - resumes.list() - List all uploaded resumes
13
- # - jobs.create() - Create job posting
14
- # - jobs.list() - List all jobs
15
- # - matching.compare() - Compare resume to job
16
- # """
17
-
18
- # import os
19
- # import mimetypes
20
- # from typing import Optional, Dict, Any, List, Union
21
- # from urllib.parse import urljoin
22
-
23
- # import requests
24
-
25
- # from .models import (
26
- # ResumeUploadResult,
27
- # ResumeAnalysisResult,
28
- # ResumeComparisonResult,
29
- # JobResult,
30
- # ResumeListResult,
31
- # JobListResult,
32
- # ParsedResumeResult,
33
- # APIError,
34
- # AuthenticationError,
35
- # RateLimitError,
36
- # ValidationError,
37
- # NotFoundError,
38
- # InsufficientCreditsError,
39
- # )
40
-
41
-
42
- # # Default API base URLs
43
- # DEFAULT_BASE_URL = "https://api.dev.aiondtech.com"
44
- # PRODUCTION_BASE_URL = "https://api.aiondtech.com"
45
-
46
-
47
- # class HTTPClient:
48
- # """Low-level HTTP client for API requests."""
49
-
50
- # def __init__(
51
- # self,
52
- # api_key: str,
53
- # base_url: str,
54
- # timeout: int = 120,
55
- # max_retries: int = 3
56
- # ):
57
- # self.api_key = api_key
58
- # self.base_url = base_url.rstrip("/")
59
- # self.timeout = timeout
60
- # self.max_retries = max_retries
61
-
62
- # self.session = requests.Session()
63
- # self.session.headers.update({
64
- # "Accept": "application/json",
65
- # "User-Agent": "aiondtech-python/2.0.0",
66
- # "X-API-Key": api_key,
67
- # })
68
-
69
- # def request(
70
- # self,
71
- # method: str,
72
- # endpoint: str,
73
- # data: Optional[Dict] = None,
74
- # json_data: Optional[Dict] = None,
75
- # files: Optional[Dict] = None,
76
- # params: Optional[Dict] = None,
77
- # ) -> Any:
78
- # """Make an HTTP request to the API."""
79
- # url = f"{self.base_url}{endpoint}"
80
-
81
- # try:
82
- # response = self.session.request(
83
- # method=method,
84
- # url=url,
85
- # data=data,
86
- # json=json_data,
87
- # files=files,
88
- # params=params,
89
- # timeout=self.timeout,
90
- # )
91
-
92
- # return self._handle_response(response)
93
-
94
- # except requests.exceptions.Timeout:
95
- # raise APIError("Request timed out", status_code=408)
96
- # except requests.exceptions.ConnectionError:
97
- # raise APIError("Failed to connect to API server")
98
- # except requests.exceptions.RequestException as e:
99
- # raise APIError(f"Request failed: {str(e)}")
100
-
101
- # def _handle_response(self, response: requests.Response) -> Any:
102
- # """Handle API response and raise appropriate exceptions."""
103
-
104
- # # Try to parse JSON response
105
- # try:
106
- # data = response.json()
107
- # except ValueError:
108
- # data = {"detail": response.text}
109
-
110
- # # Handle error responses
111
- # if response.status_code == 401:
112
- # raise AuthenticationError(
113
- # data.get("detail", "Invalid API key"),
114
- # status_code=401,
115
- # response=data
116
- # )
117
-
118
- # if response.status_code == 403:
119
- # detail = data.get("detail", "")
120
- # if "credit" in detail.lower():
121
- # raise InsufficientCreditsError(
122
- # detail or "Insufficient API credits",
123
- # status_code=403,
124
- # response=data
125
- # )
126
- # raise AuthenticationError(
127
- # detail or "Invalid or inactive API key",
128
- # status_code=403,
129
- # response=data
130
- # )
131
-
132
- # if response.status_code == 404:
133
- # raise NotFoundError(
134
- # data.get("detail", "Resource not found"),
135
- # status_code=404,
136
- # response=data
137
- # )
138
-
139
- # if response.status_code == 422:
140
- # raise ValidationError(
141
- # data.get("detail", "Validation error"),
142
- # status_code=422,
143
- # response=data
144
- # )
145
-
146
- # if response.status_code == 429:
147
- # retry_after = response.headers.get("Retry-After")
148
- # raise RateLimitError(
149
- # data.get("detail", "Rate limit exceeded"),
150
- # retry_after=int(retry_after) if retry_after else None,
151
- # status_code=429,
152
- # response=data
153
- # )
154
-
155
- # if response.status_code >= 500:
156
- # raise APIError(
157
- # data.get("detail", "Server error"),
158
- # status_code=response.status_code,
159
- # response=data
160
- # )
161
-
162
- # if response.status_code >= 400:
163
- # raise APIError(
164
- # data.get("detail", f"Request failed with status {response.status_code}"),
165
- # status_code=response.status_code,
166
- # response=data
167
- # )
168
-
169
- # return data
170
-
171
-
172
- # class Resumes:
173
- # """
174
- # Resume operations.
175
-
176
- # Usage:
177
- # client.resumes.upload("resume.pdf")
178
- # client.resumes.upload_and_analyze("resume.pdf")
179
- # client.resumes.upload_analyze_compare("resume.pdf", job_id=123)
180
- # client.resumes.analyze(resume_id=123)
181
- # client.resumes.list()
182
- # """
183
-
184
- # def __init__(self, client: HTTPClient):
185
- # self._client = client
186
-
187
- # def upload(self, file_path: str) -> ResumeUploadResult:
188
- # """
189
- # Upload a resume file (PDF only).
190
-
191
- # Credits: 1 API credit
192
-
193
- # Args:
194
- # file_path: Path to the resume file (PDF)
195
-
196
- # Returns:
197
- # ResumeUploadResult with resume_id
198
-
199
- # Example:
200
- # result = client.resumes.upload("path/to/resume.pdf")
201
- # print(f"Uploaded resume ID: {result.resume_id}")
202
- # """
203
- # self._validate_file(file_path)
204
-
205
- # mime_type = mimetypes.guess_type(file_path)[0] or "application/pdf"
206
- # filename = os.path.basename(file_path)
207
-
208
- # with open(file_path, "rb") as f:
209
- # files = {"file": (filename, f, mime_type)}
210
- # data = self._client.request("POST", "/external/upload-resume", files=files)
211
-
212
- # return ResumeUploadResult.from_response(data)
213
-
214
- # def upload_and_analyze(self, file_path: str) -> ResumeAnalysisResult:
215
- # """
216
- # Upload a resume and parse/extract structured data.
217
-
218
- # Credits: 3 API credits (includes AI parsing)
219
-
220
- # Args:
221
- # file_path: Path to the resume file (PDF)
222
-
223
- # Returns:
224
- # ResumeAnalysisResult with resume_id and parsed_data
225
-
226
- # Example:
227
- # result = client.resumes.upload_and_analyze("resume.pdf")
228
- # print(f"Name: {result.parsed_data.get('full_name')}")
229
- # print(f"Skills: {result.parsed_data.get('skills')}")
230
- # """
231
- # self._validate_file(file_path)
232
-
233
- # mime_type = mimetypes.guess_type(file_path)[0] or "application/pdf"
234
- # filename = os.path.basename(file_path)
235
-
236
- # with open(file_path, "rb") as f:
237
- # files = {"file": (filename, f, mime_type)}
238
- # data = self._client.request(
239
- # "POST",
240
- # "/external/upload-resume-analyze",
241
- # files=files
242
- # )
243
-
244
- # return ResumeAnalysisResult.from_response(data)
245
-
246
- # def upload_analyze_compare(
247
- # self,
248
- # file_path: str,
249
- # job_id: int
250
- # ) -> ResumeComparisonResult:
251
- # """
252
- # Upload, parse, and compare resume against a job posting.
253
-
254
- # Credits: 5 API credits (includes AI parsing + comparison)
255
-
256
- # Args:
257
- # file_path: Path to the resume file (PDF)
258
- # job_id: ID of the job posting to compare against
259
-
260
- # Returns:
261
- # ResumeComparisonResult with resume_id, parsed_data, score, and reason
262
-
263
- # Example:
264
- # result = client.resumes.upload_analyze_compare("resume.pdf", job_id=42)
265
- # print(f"Match Score: {result.comparison_score}%")
266
- # print(f"Reason: {result.comparison_reason}")
267
- # """
268
- # self._validate_file(file_path)
269
-
270
- # mime_type = mimetypes.guess_type(file_path)[0] or "application/pdf"
271
- # filename = os.path.basename(file_path)
272
-
273
- # with open(file_path, "rb") as f:
274
- # files = {"file": (filename, f, mime_type)}
275
- # form_data = {"job_id": str(job_id)}
276
- # data = self._client.request(
277
- # "POST",
278
- # "/external/upload-resume-analyze-compare",
279
- # files=files,
280
- # data=form_data
281
- # )
282
-
283
- # return ResumeComparisonResult.from_response(data)
284
-
285
- # def analyze(self, resume_id: int) -> ParsedResumeResult:
286
- # """
287
- # Parse and extract structured data from an existing resume.
288
-
289
- # Credits: 2 API credits (AI parsing only)
290
-
291
- # Args:
292
- # resume_id: ID of the uploaded resume
293
-
294
- # Returns:
295
- # ParsedResumeResult with extracted fields
296
-
297
- # Example:
298
- # parsed = client.resumes.analyze(resume_id=123)
299
- # print(f"Name: {parsed.full_name}")
300
- # print(f"Skills: {parsed.skills}")
301
- # """
302
- # data = self._client.request(
303
- # "POST",
304
- # "/external/analyze-resume-by-id",
305
- # params={"resume_id": resume_id}
306
- # )
307
- # return ParsedResumeResult.from_response(data)
308
-
309
- # def list(
310
- # self,
311
- # page: int = 1,
312
- # limit: int = 50
313
- # ) -> ResumeListResult:
314
- # """
315
- # List all uploaded resumes for this API key.
316
-
317
- # Credits: 0 (free)
318
-
319
- # Args:
320
- # page: Page number (default: 1)
321
- # limit: Items per page (default: 50, max: 100)
322
-
323
- # Returns:
324
- # ResumeListResult with list of resumes and pagination
325
-
326
- # Example:
327
- # result = client.resumes.list()
328
- # for resume in result.resumes:
329
- # print(f"ID: {resume['id']}, Name: {resume.get('full_name')}")
330
- # """
331
- # data = self._client.request(
332
- # "GET",
333
- # "/external/list-resumes",
334
- # params={"page": page, "limit": min(limit, 100)}
335
- # )
336
- # return ResumeListResult.from_response(data)
337
-
338
- # def _validate_file(self, file_path: str) -> None:
339
- # """Validate file exists and is PDF."""
340
- # if not os.path.exists(file_path):
341
- # raise ValidationError(f"File not found: {file_path}")
342
-
343
- # ext = os.path.splitext(file_path)[1].lower()
344
- # if ext != ".pdf":
345
- # raise ValidationError(
346
- # f"Invalid file type: {ext}. Only PDF files are supported."
347
- # )
348
-
349
-
350
- # class Jobs:
351
- # """
352
- # Job posting operations.
353
-
354
- # Usage:
355
- # client.jobs.create("Python Developer", "Job description...")
356
- # client.jobs.list()
357
- # """
358
-
359
- # def __init__(self, client: HTTPClient):
360
- # self._client = client
361
-
362
- # def create(self, title: str, description: str) -> JobResult:
363
- # """
364
- # Create a new job posting.
365
-
366
- # Credits: 1 API credit
367
-
368
- # Args:
369
- # title: Job title
370
- # description: Full job description
371
-
372
- # Returns:
373
- # JobResult with job_id
374
-
375
- # Example:
376
- # job = client.jobs.create(
377
- # title="Senior Python Developer",
378
- # description="We are looking for..."
379
- # )
380
- # print(f"Created job ID: {job.job_id}")
381
- # """
382
- # form_data = {
383
- # "title": title,
384
- # "description": description
385
- # }
386
- # data = self._client.request("POST", "/external/create-job", data=form_data)
387
- # return JobResult.from_response(data)
388
-
389
- # def list(
390
- # self,
391
- # page: int = 1,
392
- # limit: int = 50
393
- # ) -> JobListResult:
394
- # """
395
- # List all job postings for this API key.
396
-
397
- # Credits: 0 (free)
398
-
399
- # Args:
400
- # page: Page number (default: 1)
401
- # limit: Items per page (default: 50, max: 100)
402
-
403
- # Returns:
404
- # JobListResult with list of jobs and pagination
405
-
406
- # Example:
407
- # result = client.jobs.list()
408
- # for job in result.jobs:
409
- # print(f"ID: {job['id']}, Title: {job['title']}")
410
- # """
411
- # data = self._client.request(
412
- # "GET",
413
- # "/external/list-jobs",
414
- # params={"page": page, "limit": min(limit, 100)}
415
- # )
416
- # return JobListResult.from_response(data)
417
-
418
-
419
- # class Matching:
420
- # """
421
- # Resume-job matching/comparison operations.
422
-
423
- # Usage:
424
- # client.matching.compare(resume_id=123, job_id=456)
425
- # """
426
-
427
- # def __init__(self, client: HTTPClient):
428
- # self._client = client
429
-
430
- # def compare(
431
- # self,
432
- # resume_id: int,
433
- # job_id: int
434
- # ) -> ResumeComparisonResult:
435
- # """
436
- # Compare a resume against a job posting.
437
-
438
- # Credits: 3 API credits (AI comparison)
439
-
440
- # Args:
441
- # resume_id: ID of the uploaded resume
442
- # job_id: ID of the job posting
443
-
444
- # Returns:
445
- # ResumeComparisonResult with score and reasoning
446
-
447
- # Example:
448
- # result = client.matching.compare(resume_id=123, job_id=456)
449
- # print(f"Match Score: {result.comparison_score}%")
450
- # print(f"Reasoning: {result.comparison_reason}")
451
- # """
452
- # payload = {
453
- # "resume_id": resume_id,
454
- # "job_id": job_id
455
- # }
456
- # data = self._client.request(
457
- # "POST",
458
- # "/external/compare-resumes",
459
- # json_data=payload
460
- # )
461
- # return ResumeComparisonResult.from_response(data)
462
-
463
-
464
- # class Credits:
465
- # """
466
- # Credit balance and usage operations.
467
-
468
- # Usage:
469
- # client.credits.balance()
470
- # client.credits.usage()
471
- # """
472
-
473
- # def __init__(self, client: HTTPClient):
474
- # self._client = client
475
-
476
- # def balance(self) -> Dict[str, Any]:
477
- # """
478
- # Get current credit balance.
479
-
480
- # Returns:
481
- # Dictionary with credit balance info
482
-
483
- # Example:
484
- # balance = client.credits.balance()
485
- # print(f"Remaining: {balance['remaining']}")
486
- # print(f"Used this month: {balance['used_mtd']}")
487
- # """
488
- # data = self._client.request("GET", "/external/credits/balance")
489
- # return data
490
-
491
- # def usage(
492
- # self,
493
- # start_date: Optional[str] = None,
494
- # end_date: Optional[str] = None
495
- # ) -> Dict[str, Any]:
496
- # """
497
- # Get detailed usage history.
498
-
499
- # Args:
500
- # start_date: Start date (YYYY-MM-DD)
501
- # end_date: End date (YYYY-MM-DD)
502
-
503
- # Returns:
504
- # Dictionary with usage details
505
-
506
- # Example:
507
- # usage = client.credits.usage()
508
- # for entry in usage['history']:
509
- # print(f"{entry['endpoint']}: {entry['credits']} credits")
510
- # """
511
- # params = {}
512
- # if start_date:
513
- # params["start_date"] = start_date
514
- # if end_date:
515
- # params["end_date"] = end_date
516
-
517
- # data = self._client.request("GET", "/external/credits/usage", params=params)
518
- # return data
519
-
520
-
521
- # class ResumeAnalyser:
522
- # """
523
- # AiondTech Resume Analyser API Client.
524
-
525
- # This is the main entry point for interacting with the API.
526
-
527
- # Args:
528
- # api_key: Your API key. Can also be set via AIONDTECH_API_KEY env var.
529
- # base_url: API base URL. Defaults to https://api.dev.aiondtech.com
530
- # timeout: Request timeout in seconds. Default 120.
531
- # production: If True, use production URL (https://api.aiondtech.com)
532
-
533
- # Example:
534
- # from aiondtech import ResumeAnalyser
535
-
536
- # # Initialize client
537
- # client = ResumeAnalyser(api_key="your-api-key")
538
-
539
- # # Or use environment variable
540
- # # export AIONDTECH_API_KEY="your-api-key"
541
- # client = ResumeAnalyser()
542
-
543
- # # Upload resume
544
- # result = client.resumes.upload("resume.pdf")
545
-
546
- # # Upload and analyze
547
- # analysis = client.resumes.upload_and_analyze("resume.pdf")
548
-
549
- # # Create job posting
550
- # job = client.jobs.create("Title", "Description")
551
-
552
- # # Compare resume to job
553
- # comparison = client.matching.compare(
554
- # resume_id=result.resume_id,
555
- # job_id=job.job_id
556
- # )
557
-
558
- # # Check credits
559
- # balance = client.credits.balance()
560
- # """
561
-
562
- # def __init__(
563
- # self,
564
- # api_key: Optional[str] = None,
565
- # base_url: Optional[str] = None,
566
- # timeout: int = 120,
567
- # production: bool = False,
568
- # ):
569
- # # Get API key from argument or environment
570
- # self.api_key = api_key or os.getenv("AIONDTECH_API_KEY")
571
- # if not self.api_key:
572
- # raise AuthenticationError(
573
- # "API key is required. Pass api_key argument or set AIONDTECH_API_KEY environment variable."
574
- # )
575
-
576
- # # Get base URL
577
- # if base_url:
578
- # self.base_url = base_url
579
- # elif production:
580
- # self.base_url = PRODUCTION_BASE_URL
581
- # else:
582
- # self.base_url = os.getenv("AIONDTECH_BASE_URL") or DEFAULT_BASE_URL
583
-
584
- # # Initialize HTTP client
585
- # self._client = HTTPClient(
586
- # api_key=self.api_key,
587
- # base_url=self.base_url,
588
- # timeout=timeout,
589
- # )
590
-
591
- # # Initialize resource classes
592
- # self.resumes = Resumes(self._client)
593
- # self.jobs = Jobs(self._client)
594
- # self.matching = Matching(self._client)
595
- # self.credits = Credits(self._client)
596
-
597
- # def __repr__(self):
598
- # masked_key = f"{self.api_key[:8]}...{self.api_key[-4:]}" if len(self.api_key) > 12 else "***"
599
- # return f"<ResumeAnalyser base_url='{self.base_url}' api_key='{masked_key}'>"
600
-
601
-
602
- # # Alias for convenience
603
- # Client = ResumeAnalyser
604
-
605
-
606
-
607
1
  """
608
2
  AiondTech Resume Analyser API Client - Updated Version
609
3
 
@@ -668,7 +62,7 @@ class HTTPClient:
668
62
  self.session = requests.Session()
669
63
  self.session.headers.update({
670
64
  "Accept": "application/json",
671
- "User-Agent": "aiondtech-python/2.1.0",
65
+ "User-Agent": "aiondtech-python/2.2.0",
672
66
  "X-API-Key": api_key,
673
67
  })
674
68
 
@@ -794,7 +188,7 @@ class Resumes:
794
188
  """
795
189
  Upload a resume file (PDF only).
796
190
 
797
- Credits: 1 API credit
191
+ Credits: Varies
798
192
 
799
193
  Args:
800
194
  file_path: Path to the resume file (PDF)
@@ -821,7 +215,7 @@ class Resumes:
821
215
  """
822
216
  Upload a resume and parse/extract structured data.
823
217
 
824
- Credits: 3 API credits (includes AI parsing)
218
+ Credits: Varies (includes AI parsing)
825
219
 
826
220
  Args:
827
221
  file_path: Path to the resume file (PDF)
@@ -857,7 +251,7 @@ class Resumes:
857
251
  """
858
252
  Upload, parse, and compare resume against a job posting.
859
253
 
860
- Credits: 6 API credits (includes AI parsing + comparison)
254
+ Credits: Varies (includes AI parsing + comparison)
861
255
 
862
256
  Args:
863
257
  file_path: Path to the resume file (PDF)
@@ -892,7 +286,7 @@ class Resumes:
892
286
  """
893
287
  Parse and extract structured data from an existing resume.
894
288
 
895
- Credits: 2 API credits (AI parsing only)
289
+ Credits: Varies (AI parsing)
896
290
 
897
291
  Args:
898
292
  resume_id: ID of the uploaded resume
@@ -949,7 +343,7 @@ class Resumes:
949
343
  """
950
344
  Compare a resume against a job posting.
951
345
 
952
- Credits: 3 API credits (AI comparison)
346
+ Credits: Varies (AI comparison)
953
347
 
954
348
  Args:
955
349
  resume_id: ID of the uploaded resume
@@ -1003,7 +397,7 @@ class Jobs:
1003
397
  """
1004
398
  Create a new job posting.
1005
399
 
1006
- Credits: 1 API credit
400
+ Credits: Varies
1007
401
 
1008
402
  Args:
1009
403
  title: Job title