rxresume-python 0.2.0__tar.gz → 0.4.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.
Files changed (34) hide show
  1. {rxresume_python-0.2.0/rxresume_python.egg-info → rxresume_python-0.4.0}/PKG-INFO +75 -6
  2. {rxresume_python-0.2.0 → rxresume_python-0.4.0}/README.md +74 -5
  3. {rxresume_python-0.2.0 → rxresume_python-0.4.0}/pyproject.toml +1 -1
  4. rxresume_python-0.4.0/reactive_resume/api/__init__.py +29 -0
  5. rxresume_python-0.4.0/reactive_resume/api/agent.py +243 -0
  6. rxresume_python-0.4.0/reactive_resume/api/ai.py +89 -0
  7. rxresume_python-0.4.0/reactive_resume/api/ai_providers.py +89 -0
  8. rxresume_python-0.4.0/reactive_resume/api/applications.py +100 -0
  9. rxresume_python-0.4.0/reactive_resume/api/auth.py +45 -0
  10. rxresume_python-0.4.0/reactive_resume/api/flags.py +27 -0
  11. rxresume_python-0.4.0/reactive_resume/api/resumes.py +251 -0
  12. rxresume_python-0.4.0/reactive_resume/api/statistics.py +41 -0
  13. {rxresume_python-0.2.0 → rxresume_python-0.4.0}/reactive_resume/core/async_client.py +12 -1
  14. {rxresume_python-0.2.0 → rxresume_python-0.4.0}/reactive_resume/core/client.py +12 -1
  15. {rxresume_python-0.2.0 → rxresume_python-0.4.0}/reactive_resume/models/__init__.py +6 -0
  16. rxresume_python-0.4.0/reactive_resume/models/application.py +33 -0
  17. rxresume_python-0.4.0/reactive_resume/models/statistics.py +14 -0
  18. {rxresume_python-0.2.0 → rxresume_python-0.4.0/rxresume_python.egg-info}/PKG-INFO +75 -6
  19. {rxresume_python-0.2.0 → rxresume_python-0.4.0}/rxresume_python.egg-info/SOURCES.txt +9 -3
  20. rxresume_python-0.2.0/reactive_resume/api/__init__.py +0 -11
  21. rxresume_python-0.2.0/reactive_resume/api/auth.py +0 -66
  22. rxresume_python-0.2.0/reactive_resume/api/resumes.py +0 -169
  23. rxresume_python-0.2.0/tests/test_client_async.py +0 -268
  24. rxresume_python-0.2.0/tests/test_client_sync.py +0 -259
  25. {rxresume_python-0.2.0 → rxresume_python-0.4.0}/LICENSE +0 -0
  26. {rxresume_python-0.2.0 → rxresume_python-0.4.0}/reactive_resume/__init__.py +0 -0
  27. {rxresume_python-0.2.0 → rxresume_python-0.4.0}/reactive_resume/core/__init__.py +0 -0
  28. {rxresume_python-0.2.0 → rxresume_python-0.4.0}/reactive_resume/core/exceptions.py +0 -0
  29. {rxresume_python-0.2.0 → rxresume_python-0.4.0}/reactive_resume/models/resume.py +0 -0
  30. {rxresume_python-0.2.0 → rxresume_python-0.4.0}/reactive_resume/models/user.py +0 -0
  31. {rxresume_python-0.2.0 → rxresume_python-0.4.0}/rxresume_python.egg-info/dependency_links.txt +0 -0
  32. {rxresume_python-0.2.0 → rxresume_python-0.4.0}/rxresume_python.egg-info/requires.txt +0 -0
  33. {rxresume_python-0.2.0 → rxresume_python-0.4.0}/rxresume_python.egg-info/top_level.txt +0 -0
  34. {rxresume_python-0.2.0 → rxresume_python-0.4.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rxresume-python
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: Unofficial Python API client (SDK) for Reactive Resume v4
5
5
  Author: Ata Can Yaymacı
6
6
  License: MIT
@@ -42,12 +42,57 @@ It supports both synchronous (`httpx.Client`) and asynchronous (`httpx.AsyncClie
42
42
  ## Features
43
43
 
44
44
  - **Dual client modes**: Support for both sync and async APIs.
45
- - **Type safety**: Fully typed models for Resumes, Sections, Basics, and Users using Pydantic V2.
45
+ - **Full API coverage**: Integrated modules for Resume management, Job Applications tracking, AI Agent, AI Providers configurations, Global Statistics, and Feature Flags.
46
+ - **Type safety**: Fully typed models for all entities using Pydantic V2.
46
47
  - **Robust error handling**: Raw API status errors are automatically parsed into specific exceptions (`AuthenticationError`, `NotFoundError`, etc.).
47
48
  - **Developer Experience (DX)**: Code-completion ready with clear typing and docstrings.
48
49
 
49
50
  ---
50
51
 
52
+ ## Architecture
53
+
54
+ ```mermaid
55
+ graph TD
56
+ UserApp[User Application / Script] -->|Instantiates| SyncClient[RxResumeClient]
57
+ UserApp -->|Instantiates| AsyncClient[AsyncRxResumeClient]
58
+
59
+ subgraph "Service Modules (Sync & Async)"
60
+ SyncClient --> Auth[auth]
61
+ SyncClient --> Resumes[resumes]
62
+ SyncClient --> Stats[statistics]
63
+ SyncClient --> Agent[agent]
64
+ SyncClient --> AIProviders[ai_providers]
65
+ SyncClient --> Flags[flags]
66
+ SyncClient --> AI[ai]
67
+ SyncClient --> Applications[applications]
68
+ end
69
+
70
+ Auth -->|HTTP/REST Calls| Backend[Reactive Resume v4 API Backend]
71
+ Resumes -->|HTTP/REST Calls| Backend
72
+ Stats -->|HTTP/REST Calls| Backend
73
+ Agent -->|HTTP/REST Calls| Backend
74
+ AIProviders -->|HTTP/REST Calls| Backend
75
+ Flags -->|HTTP/REST Calls| Backend
76
+ AI -->|HTTP/REST Calls| Backend
77
+ Applications -->|HTTP/REST Calls| Backend
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Capability Matrix
83
+
84
+ | Service Module | Sync | Async | Key Mapped Endpoints (Postman Collection) |
85
+ | :--- | :---: | :---: | :--- |
86
+ | **Resumes** (`client.resumes`) | Yes | Yes | List, Get, Create, Update, Delete, Import, Download PDF, Set/Verify/Remove Password, Duplicate, Lock, Version History, Public Resume, Statistics |
87
+ | **Applications** (`client.applications`) | Yes | Yes | List, Get, Create, Delete, List Tags, Pipeline Stats, Bulk Import |
88
+ | **Auth** (`client.auth`) | Yes | Yes | List Providers, Export Account, Delete Account |
89
+ | **Statistics** (`client.statistics`) | Yes | Yes | Users Count, GitHub Stars, Resumes Count (Global Stats) |
90
+ | **Agent** (`client.agent`) | Yes | Yes | List Threads, Create/Get/Delete Threads, Send Message, Stop Active Run, Resume Message Stream, Create/Delete Attachment, Revert Action |
91
+ | **AI Providers** (`client.ai_providers`) | Yes | Yes | List, Create, Update, Delete, Test saved AI providers |
92
+ | **AI Functions** (`client.ai`) | Yes | Yes | Parse PDF, Parse DOCX, Chat, Analyze Resume |
93
+ | **Feature Flags** (`client.flags`) | Yes | Yes | List Server-side Feature Flags |
94
+
95
+
51
96
  ## Installation
52
97
 
53
98
  Install the package via `pip` or your favorite package manager:
@@ -88,9 +133,9 @@ async def main():
88
133
  new_resume = await client.resumes.import_resume(import_data)
89
134
  print(f"Created resume: {new_resume.name} (ID: {new_resume.id})")
90
135
 
91
- # Fetch the generated PDF URL
92
- pdf_url = await client.resumes.get_pdf_url(new_resume.id)
93
- print(f"PDF URL: {pdf_url}")
136
+ # Download the compiled PDF bytes
137
+ pdf_bytes = await client.resumes.download_pdf(new_resume.id)
138
+ print(f"Downloaded PDF: {len(pdf_bytes)} bytes")
94
139
 
95
140
  except Exception as e:
96
141
  print(f"An error occurred: {e}")
@@ -103,7 +148,6 @@ if __name__ == "__main__":
103
148
 
104
149
  ```python
105
150
  from reactive_resume import RxResumeClient
106
- from reactive_resume.models import ResumeImportData
107
151
 
108
152
  with RxResumeClient(base_url="https://rxresu.me", api_key="your_api_key") as client:
109
153
  resumes = client.resumes.list()
@@ -111,8 +155,33 @@ with RxResumeClient(base_url="https://rxresu.me", api_key="your_api_key") as cli
111
155
  print(f"Resume: {resume.name} (Slug: {resume.slug})")
112
156
  ```
113
157
 
158
+ ### 3. Advanced Features (AI Agent, Statistics, Applications)
159
+
160
+ ```python
161
+ with RxResumeClient(base_url="https://rxresu.me", api_key="your_api_key") as client:
162
+ # 1. Start a new AI Agent thread and send a message
163
+ thread = client.agent.create_thread()
164
+ response = client.agent.send_message(thread["id"], "Suggest a professional summary for a software developer.")
165
+ print(f"AI Suggestion: {response}")
166
+
167
+ # 2. Retrieve global resume platform metrics
168
+ users_count = client.statistics.get_users_count()
169
+ print(f"Platform Users: {users_count}")
170
+
171
+ # 3. List job applications
172
+ apps = client.applications.list()
173
+ for app in apps:
174
+ print(f"Job application: {app.company} - {app.position} ({app.stage})")
175
+
176
+ # 4. Check server-side feature flags
177
+ flags = client.flags.list()
178
+ print(f"Signups disabled: {flags.get('isSignupsDisabled')}")
179
+ ```
180
+
181
+
114
182
  ---
115
183
 
184
+
116
185
  ## Error Handling
117
186
 
118
187
  All client API calls map HTTP errors to specific exception classes:
@@ -13,12 +13,57 @@ It supports both synchronous (`httpx.Client`) and asynchronous (`httpx.AsyncClie
13
13
  ## Features
14
14
 
15
15
  - **Dual client modes**: Support for both sync and async APIs.
16
- - **Type safety**: Fully typed models for Resumes, Sections, Basics, and Users using Pydantic V2.
16
+ - **Full API coverage**: Integrated modules for Resume management, Job Applications tracking, AI Agent, AI Providers configurations, Global Statistics, and Feature Flags.
17
+ - **Type safety**: Fully typed models for all entities using Pydantic V2.
17
18
  - **Robust error handling**: Raw API status errors are automatically parsed into specific exceptions (`AuthenticationError`, `NotFoundError`, etc.).
18
19
  - **Developer Experience (DX)**: Code-completion ready with clear typing and docstrings.
19
20
 
20
21
  ---
21
22
 
23
+ ## Architecture
24
+
25
+ ```mermaid
26
+ graph TD
27
+ UserApp[User Application / Script] -->|Instantiates| SyncClient[RxResumeClient]
28
+ UserApp -->|Instantiates| AsyncClient[AsyncRxResumeClient]
29
+
30
+ subgraph "Service Modules (Sync & Async)"
31
+ SyncClient --> Auth[auth]
32
+ SyncClient --> Resumes[resumes]
33
+ SyncClient --> Stats[statistics]
34
+ SyncClient --> Agent[agent]
35
+ SyncClient --> AIProviders[ai_providers]
36
+ SyncClient --> Flags[flags]
37
+ SyncClient --> AI[ai]
38
+ SyncClient --> Applications[applications]
39
+ end
40
+
41
+ Auth -->|HTTP/REST Calls| Backend[Reactive Resume v4 API Backend]
42
+ Resumes -->|HTTP/REST Calls| Backend
43
+ Stats -->|HTTP/REST Calls| Backend
44
+ Agent -->|HTTP/REST Calls| Backend
45
+ AIProviders -->|HTTP/REST Calls| Backend
46
+ Flags -->|HTTP/REST Calls| Backend
47
+ AI -->|HTTP/REST Calls| Backend
48
+ Applications -->|HTTP/REST Calls| Backend
49
+ ```
50
+
51
+ ---
52
+
53
+ ## Capability Matrix
54
+
55
+ | Service Module | Sync | Async | Key Mapped Endpoints (Postman Collection) |
56
+ | :--- | :---: | :---: | :--- |
57
+ | **Resumes** (`client.resumes`) | Yes | Yes | List, Get, Create, Update, Delete, Import, Download PDF, Set/Verify/Remove Password, Duplicate, Lock, Version History, Public Resume, Statistics |
58
+ | **Applications** (`client.applications`) | Yes | Yes | List, Get, Create, Delete, List Tags, Pipeline Stats, Bulk Import |
59
+ | **Auth** (`client.auth`) | Yes | Yes | List Providers, Export Account, Delete Account |
60
+ | **Statistics** (`client.statistics`) | Yes | Yes | Users Count, GitHub Stars, Resumes Count (Global Stats) |
61
+ | **Agent** (`client.agent`) | Yes | Yes | List Threads, Create/Get/Delete Threads, Send Message, Stop Active Run, Resume Message Stream, Create/Delete Attachment, Revert Action |
62
+ | **AI Providers** (`client.ai_providers`) | Yes | Yes | List, Create, Update, Delete, Test saved AI providers |
63
+ | **AI Functions** (`client.ai`) | Yes | Yes | Parse PDF, Parse DOCX, Chat, Analyze Resume |
64
+ | **Feature Flags** (`client.flags`) | Yes | Yes | List Server-side Feature Flags |
65
+
66
+
22
67
  ## Installation
23
68
 
24
69
  Install the package via `pip` or your favorite package manager:
@@ -59,9 +104,9 @@ async def main():
59
104
  new_resume = await client.resumes.import_resume(import_data)
60
105
  print(f"Created resume: {new_resume.name} (ID: {new_resume.id})")
61
106
 
62
- # Fetch the generated PDF URL
63
- pdf_url = await client.resumes.get_pdf_url(new_resume.id)
64
- print(f"PDF URL: {pdf_url}")
107
+ # Download the compiled PDF bytes
108
+ pdf_bytes = await client.resumes.download_pdf(new_resume.id)
109
+ print(f"Downloaded PDF: {len(pdf_bytes)} bytes")
65
110
 
66
111
  except Exception as e:
67
112
  print(f"An error occurred: {e}")
@@ -74,7 +119,6 @@ if __name__ == "__main__":
74
119
 
75
120
  ```python
76
121
  from reactive_resume import RxResumeClient
77
- from reactive_resume.models import ResumeImportData
78
122
 
79
123
  with RxResumeClient(base_url="https://rxresu.me", api_key="your_api_key") as client:
80
124
  resumes = client.resumes.list()
@@ -82,8 +126,33 @@ with RxResumeClient(base_url="https://rxresu.me", api_key="your_api_key") as cli
82
126
  print(f"Resume: {resume.name} (Slug: {resume.slug})")
83
127
  ```
84
128
 
129
+ ### 3. Advanced Features (AI Agent, Statistics, Applications)
130
+
131
+ ```python
132
+ with RxResumeClient(base_url="https://rxresu.me", api_key="your_api_key") as client:
133
+ # 1. Start a new AI Agent thread and send a message
134
+ thread = client.agent.create_thread()
135
+ response = client.agent.send_message(thread["id"], "Suggest a professional summary for a software developer.")
136
+ print(f"AI Suggestion: {response}")
137
+
138
+ # 2. Retrieve global resume platform metrics
139
+ users_count = client.statistics.get_users_count()
140
+ print(f"Platform Users: {users_count}")
141
+
142
+ # 3. List job applications
143
+ apps = client.applications.list()
144
+ for app in apps:
145
+ print(f"Job application: {app.company} - {app.position} ({app.stage})")
146
+
147
+ # 4. Check server-side feature flags
148
+ flags = client.flags.list()
149
+ print(f"Signups disabled: {flags.get('isSignupsDisabled')}")
150
+ ```
151
+
152
+
85
153
  ---
86
154
 
155
+
87
156
  ## Error Handling
88
157
 
89
158
  All client API calls map HTTP errors to specific exception classes:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "rxresume-python"
7
- version = "0.2.0"
7
+ version = "0.4.0"
8
8
  description = "Unofficial Python API client (SDK) for Reactive Resume v4"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -0,0 +1,29 @@
1
+ """API endpoints and service groups."""
2
+
3
+ from .auth import AuthAPI, AsyncAuthAPI
4
+ from .resumes import ResumesAPI, AsyncResumesAPI
5
+ from .statistics import StatisticsAPI, AsyncStatisticsAPI
6
+ from .agent import AgentAPI, AsyncAgentAPI
7
+ from .ai_providers import AiProvidersAPI, AsyncAiProvidersAPI
8
+ from .flags import FlagsAPI, AsyncFlagsAPI
9
+ from .ai import AIAPI, AsyncAIAPI
10
+ from .applications import ApplicationsAPI, AsyncApplicationsAPI
11
+
12
+ __all__ = [
13
+ "AuthAPI",
14
+ "AsyncAuthAPI",
15
+ "ResumesAPI",
16
+ "AsyncResumesAPI",
17
+ "StatisticsAPI",
18
+ "AsyncStatisticsAPI",
19
+ "AgentAPI",
20
+ "AsyncAgentAPI",
21
+ "AiProvidersAPI",
22
+ "AsyncAiProvidersAPI",
23
+ "FlagsAPI",
24
+ "AsyncFlagsAPI",
25
+ "AIAPI",
26
+ "AsyncAIAPI",
27
+ "ApplicationsAPI",
28
+ "AsyncApplicationsAPI",
29
+ ]
@@ -0,0 +1,243 @@
1
+ """Agent API endpoints implementation."""
2
+
3
+ from typing import List, Dict, Any, Optional
4
+
5
+
6
+ class AgentAPI:
7
+ """Synchronous AI Agent operations."""
8
+
9
+ def __init__(self, client) -> None:
10
+ self._client = client
11
+
12
+ def list_threads(self) -> List[Dict[str, Any]]:
13
+ """List all active agent threads."""
14
+ response = self._client._request("GET", "/api/openapi/agent/threads")
15
+ return list(response)
16
+
17
+ def get_thread(self, thread_id: str) -> Dict[str, Any]:
18
+ """Get details of a specific agent thread by ID."""
19
+ response = self._client._request("GET", f"/api/openapi/agent/threads/{thread_id}")
20
+ return dict(response)
21
+
22
+ def delete_thread(self, thread_id: str) -> None:
23
+ """Delete an agent thread by ID."""
24
+ self._client._request("DELETE", f"/api/openapi/agent/threads/{thread_id}")
25
+
26
+ def create_thread(
27
+ self, ai_provider_id: Optional[str] = None, source_resume_id: Optional[str] = None
28
+ ) -> Dict[str, Any]:
29
+ """Create a new agent thread."""
30
+ payload = {}
31
+ if ai_provider_id is not None:
32
+ payload["aiProviderId"] = ai_provider_id
33
+ if source_resume_id is not None:
34
+ payload["sourceResumeId"] = source_resume_id
35
+ response = self._client._request("POST", "/api/openapi/agent/threads", json=payload)
36
+ return dict(response)
37
+
38
+ def get_or_create_thread_for_resume(
39
+ self, ai_provider_id: Optional[str] = None, source_resume_id: Optional[str] = None
40
+ ) -> Dict[str, Any]:
41
+ """Get or create an in-resume agent thread."""
42
+ payload = {}
43
+ if ai_provider_id is not None:
44
+ payload["aiProviderId"] = ai_provider_id
45
+ if source_resume_id is not None:
46
+ payload["sourceResumeId"] = source_resume_id
47
+ response = self._client._request(
48
+ "POST", "/api/openapi/agent/threads/for-resume", json=payload
49
+ )
50
+ return dict(response)
51
+
52
+ def send_message(
53
+ self, thread_id: str, message: Any, attachment_ids: Optional[List[str]] = None
54
+ ) -> Dict[str, Any]:
55
+ """Send an agent message."""
56
+ payload = {
57
+ "threadId": thread_id,
58
+ "message": message,
59
+ }
60
+ if attachment_ids is not None:
61
+ payload["attachmentIds"] = attachment_ids
62
+ response = self._client._request("POST", "/api/openapi/agent/messages/send", json=payload)
63
+ return dict(response)
64
+
65
+ def archive_thread(
66
+ self,
67
+ thread_id: str,
68
+ ai_provider_id: Optional[str] = None,
69
+ source_resume_id: Optional[str] = None,
70
+ ) -> Dict[str, Any]:
71
+ """Archive an agent thread."""
72
+ payload = {}
73
+ if ai_provider_id is not None:
74
+ payload["aiProviderId"] = ai_provider_id
75
+ if source_resume_id is not None:
76
+ payload["sourceResumeId"] = source_resume_id
77
+ response = self._client._request(
78
+ "POST", f"/api/openapi/agent/threads/{thread_id}/archive", json=payload
79
+ )
80
+ return dict(response)
81
+
82
+ def stop_run(self, thread_id: str, partial_message: Optional[Any] = None) -> Dict[str, Any]:
83
+ """Stop active agent run."""
84
+ payload = {
85
+ "threadId": thread_id,
86
+ }
87
+ if partial_message is not None:
88
+ payload["partialMessage"] = partial_message
89
+ response = self._client._request("POST", "/api/openapi/agent/messages/stop", json=payload)
90
+ return dict(response)
91
+
92
+ def resume_message_stream(self, thread_id: str) -> Any:
93
+ """Resume agent message stream."""
94
+ return self._client._request(
95
+ "GET", f"/api/openapi/agent/messages/resume?threadId={thread_id}"
96
+ )
97
+
98
+ def create_attachment(
99
+ self, thread_id: str, filename: str, media_type: str, data: str
100
+ ) -> Dict[str, Any]:
101
+ """Create agent attachment."""
102
+ payload = {
103
+ "threadId": thread_id,
104
+ "filename": filename,
105
+ "mediaType": media_type,
106
+ "data": data,
107
+ }
108
+ response = self._client._request("POST", "/api/openapi/agent/attachments", json=payload)
109
+ return dict(response)
110
+
111
+ def delete_attachment(self, attachment_id: str) -> None:
112
+ """Delete agent attachment."""
113
+ self._client._request("DELETE", f"/api/openapi/agent/attachments/{attachment_id}")
114
+
115
+ def revert_action(self, action_id: str) -> Dict[str, Any]:
116
+ """Restore agent action snapshot."""
117
+ response = self._client._request("POST", f"/api/openapi/agent/actions/{action_id}/revert")
118
+ return dict(response)
119
+
120
+
121
+ class AsyncAgentAPI:
122
+ """Asynchronous AI Agent operations."""
123
+
124
+ def __init__(self, client) -> None:
125
+ self._client = client
126
+
127
+ async def list_threads(self) -> List[Dict[str, Any]]:
128
+ """List all active agent threads asynchronously."""
129
+ response = await self._client._request("GET", "/api/openapi/agent/threads")
130
+ return list(response)
131
+
132
+ async def get_thread(self, thread_id: str) -> Dict[str, Any]:
133
+ """Get details of a specific agent thread by ID asynchronously."""
134
+ response = await self._client._request("GET", f"/api/openapi/agent/threads/{thread_id}")
135
+ return dict(response)
136
+
137
+ async def delete_thread(self, thread_id: str) -> None:
138
+ """Delete an agent thread by ID asynchronously."""
139
+ await self._client._request("DELETE", f"/api/openapi/agent/threads/{thread_id}")
140
+
141
+ async def create_thread(
142
+ self, ai_provider_id: Optional[str] = None, source_resume_id: Optional[str] = None
143
+ ) -> Dict[str, Any]:
144
+ """Create a new agent thread asynchronously."""
145
+ payload = {}
146
+ if ai_provider_id is not None:
147
+ payload["aiProviderId"] = ai_provider_id
148
+ if source_resume_id is not None:
149
+ payload["sourceResumeId"] = source_resume_id
150
+ response = await self._client._request("POST", "/api/openapi/agent/threads", json=payload)
151
+ return dict(response)
152
+
153
+ async def get_or_create_thread_for_resume(
154
+ self, ai_provider_id: Optional[str] = None, source_resume_id: Optional[str] = None
155
+ ) -> Dict[str, Any]:
156
+ """Get or create an in-resume agent thread asynchronously."""
157
+ payload = {}
158
+ if ai_provider_id is not None:
159
+ payload["aiProviderId"] = ai_provider_id
160
+ if source_resume_id is not None:
161
+ payload["sourceResumeId"] = source_resume_id
162
+ response = await self._client._request(
163
+ "POST", "/api/openapi/agent/threads/for-resume", json=payload
164
+ )
165
+ return dict(response)
166
+
167
+ async def send_message(
168
+ self, thread_id: str, message: Any, attachment_ids: Optional[List[str]] = None
169
+ ) -> Dict[str, Any]:
170
+ """Send an agent message asynchronously."""
171
+ payload = {
172
+ "threadId": thread_id,
173
+ "message": message,
174
+ }
175
+ if attachment_ids is not None:
176
+ payload["attachmentIds"] = attachment_ids
177
+ response = await self._client._request(
178
+ "POST", "/api/openapi/agent/messages/send", json=payload
179
+ )
180
+ return dict(response)
181
+
182
+ async def archive_thread(
183
+ self,
184
+ thread_id: str,
185
+ ai_provider_id: Optional[str] = None,
186
+ source_resume_id: Optional[str] = None,
187
+ ) -> Dict[str, Any]:
188
+ """Archive an agent thread asynchronously."""
189
+ payload = {}
190
+ if ai_provider_id is not None:
191
+ payload["aiProviderId"] = ai_provider_id
192
+ if source_resume_id is not None:
193
+ payload["sourceResumeId"] = source_resume_id
194
+ response = await self._client._request(
195
+ "POST", f"/api/openapi/agent/threads/{thread_id}/archive", json=payload
196
+ )
197
+ return dict(response)
198
+
199
+ async def stop_run(
200
+ self, thread_id: str, partial_message: Optional[Any] = None
201
+ ) -> Dict[str, Any]:
202
+ """Stop active agent run asynchronously."""
203
+ payload = {
204
+ "threadId": thread_id,
205
+ }
206
+ if partial_message is not None:
207
+ payload["partialMessage"] = partial_message
208
+ response = await self._client._request(
209
+ "POST", "/api/openapi/agent/messages/stop", json=payload
210
+ )
211
+ return dict(response)
212
+
213
+ async def resume_message_stream(self, thread_id: str) -> Any:
214
+ """Resume agent message stream asynchronously."""
215
+ return await self._client._request(
216
+ "GET", f"/api/openapi/agent/messages/resume?threadId={thread_id}"
217
+ )
218
+
219
+ async def create_attachment(
220
+ self, thread_id: str, filename: str, media_type: str, data: str
221
+ ) -> Dict[str, Any]:
222
+ """Create agent attachment asynchronously."""
223
+ payload = {
224
+ "threadId": thread_id,
225
+ "filename": filename,
226
+ "mediaType": media_type,
227
+ "data": data,
228
+ }
229
+ response = await self._client._request(
230
+ "POST", "/api/openapi/agent/attachments", json=payload
231
+ )
232
+ return dict(response)
233
+
234
+ async def delete_attachment(self, attachment_id: str) -> None:
235
+ """Delete agent attachment asynchronously."""
236
+ await self._client._request("DELETE", f"/api/openapi/agent/attachments/{attachment_id}")
237
+
238
+ async def revert_action(self, action_id: str) -> Dict[str, Any]:
239
+ """Restore agent action snapshot asynchronously."""
240
+ response = await self._client._request(
241
+ "POST", f"/api/openapi/agent/actions/{action_id}/revert"
242
+ )
243
+ return dict(response)
@@ -0,0 +1,89 @@
1
+ """AI API endpoints implementation."""
2
+
3
+ from typing import Dict, Any
4
+
5
+
6
+ class AIAPI:
7
+ """Synchronous AI operations."""
8
+
9
+ def __init__(self, client) -> None:
10
+ self._client = client
11
+
12
+ def parse_pdf(self, file_name: str, file_data: str, provider_id: str) -> Dict[str, Any]:
13
+ """Parse a PDF file into resume data.
14
+
15
+ Args:
16
+ file_name: Name of the file.
17
+ file_data: Base64 or binary data representation.
18
+ provider_id: The ID of the configured AI provider.
19
+ """
20
+ payload = {
21
+ "file": {"name": file_name, "data": file_data},
22
+ "aiProviderId": provider_id,
23
+ }
24
+ response = self._client._request("POST", "/api/openapi/ai/parse-pdf", json=payload)
25
+ return dict(response)
26
+
27
+ def parse_docx(self, file_name: str, file_data: str, provider_id: str) -> Dict[str, Any]:
28
+ """Parse a DOCX file into resume data."""
29
+ payload = {
30
+ "file": {"name": file_name, "data": file_data},
31
+ "aiProviderId": provider_id,
32
+ }
33
+ response = self._client._request("POST", "/api/openapi/ai/parse-docx", json=payload)
34
+ return dict(response)
35
+
36
+ def chat(self, payload: Dict[str, Any]) -> Dict[str, Any]:
37
+ """Chat with AI to modify resume."""
38
+ response = self._client._request("POST", "/api/openapi/ai/chat", json=payload)
39
+ return dict(response)
40
+
41
+ def analyze_resume(self, resume_id: str, provider_id: str) -> Dict[str, Any]:
42
+ """Analyze a resume and persist the latest analysis."""
43
+ payload = {
44
+ "resumeId": resume_id,
45
+ "aiProviderId": provider_id,
46
+ }
47
+ response = self._client._request("POST", "/api/openapi/ai/analyze-resume", json=payload)
48
+ return dict(response)
49
+
50
+
51
+ class AsyncAIAPI:
52
+ """Asynchronous AI operations."""
53
+
54
+ def __init__(self, client) -> None:
55
+ self._client = client
56
+
57
+ async def parse_pdf(self, file_name: str, file_data: str, provider_id: str) -> Dict[str, Any]:
58
+ """Parse a PDF file into resume data asynchronously."""
59
+ payload = {
60
+ "file": {"name": file_name, "data": file_data},
61
+ "aiProviderId": provider_id,
62
+ }
63
+ response = await self._client._request("POST", "/api/openapi/ai/parse-pdf", json=payload)
64
+ return dict(response)
65
+
66
+ async def parse_docx(self, file_name: str, file_data: str, provider_id: str) -> Dict[str, Any]:
67
+ """Parse a DOCX file into resume data asynchronously."""
68
+ payload = {
69
+ "file": {"name": file_name, "data": file_data},
70
+ "aiProviderId": provider_id,
71
+ }
72
+ response = await self._client._request("POST", "/api/openapi/ai/parse-docx", json=payload)
73
+ return dict(response)
74
+
75
+ async def chat(self, payload: Dict[str, Any]) -> Dict[str, Any]:
76
+ """Chat with AI asynchronously to modify resume."""
77
+ response = await self._client._request("POST", "/api/openapi/ai/chat", json=payload)
78
+ return dict(response)
79
+
80
+ async def analyze_resume(self, resume_id: str, provider_id: str) -> Dict[str, Any]:
81
+ """Analyze a resume asynchronously and persist the latest analysis."""
82
+ payload = {
83
+ "resumeId": resume_id,
84
+ "aiProviderId": provider_id,
85
+ }
86
+ response = await self._client._request(
87
+ "POST", "/api/openapi/ai/analyze-resume", json=payload
88
+ )
89
+ return dict(response)