rxresume-python 0.3.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 (37) hide show
  1. {rxresume_python-0.3.0/rxresume_python.egg-info → rxresume_python-0.4.0}/PKG-INFO +67 -28
  2. {rxresume_python-0.3.0 → rxresume_python-0.4.0}/README.md +66 -27
  3. {rxresume_python-0.3.0 → rxresume_python-0.4.0}/pyproject.toml +1 -1
  4. {rxresume_python-0.3.0 → rxresume_python-0.4.0}/reactive_resume/api/__init__.py +9 -6
  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.3.0 → rxresume_python-0.4.0}/reactive_resume/api/applications.py +36 -14
  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.3.0 → rxresume_python-0.4.0}/reactive_resume/core/async_client.py +6 -5
  14. {rxresume_python-0.3.0 → rxresume_python-0.4.0}/reactive_resume/core/client.py +6 -5
  15. {rxresume_python-0.3.0 → rxresume_python-0.4.0}/reactive_resume/models/__init__.py +2 -7
  16. {rxresume_python-0.3.0 → rxresume_python-0.4.0/rxresume_python.egg-info}/PKG-INFO +67 -28
  17. {rxresume_python-0.3.0 → rxresume_python-0.4.0}/rxresume_python.egg-info/SOURCES.txt +2 -3
  18. rxresume_python-0.3.0/reactive_resume/api/agent.py +0 -37
  19. rxresume_python-0.3.0/reactive_resume/api/ai_providers.py +0 -31
  20. rxresume_python-0.3.0/reactive_resume/api/auth.py +0 -66
  21. rxresume_python-0.3.0/reactive_resume/api/resumes.py +0 -169
  22. rxresume_python-0.3.0/reactive_resume/api/statistics.py +0 -27
  23. rxresume_python-0.3.0/reactive_resume/api/storage.py +0 -53
  24. rxresume_python-0.3.0/reactive_resume/models/agent.py +0 -15
  25. rxresume_python-0.3.0/reactive_resume/models/storage.py +0 -14
  26. {rxresume_python-0.3.0 → rxresume_python-0.4.0}/LICENSE +0 -0
  27. {rxresume_python-0.3.0 → rxresume_python-0.4.0}/reactive_resume/__init__.py +0 -0
  28. {rxresume_python-0.3.0 → rxresume_python-0.4.0}/reactive_resume/core/__init__.py +0 -0
  29. {rxresume_python-0.3.0 → rxresume_python-0.4.0}/reactive_resume/core/exceptions.py +0 -0
  30. {rxresume_python-0.3.0 → rxresume_python-0.4.0}/reactive_resume/models/application.py +0 -0
  31. {rxresume_python-0.3.0 → rxresume_python-0.4.0}/reactive_resume/models/resume.py +0 -0
  32. {rxresume_python-0.3.0 → rxresume_python-0.4.0}/reactive_resume/models/statistics.py +0 -0
  33. {rxresume_python-0.3.0 → rxresume_python-0.4.0}/reactive_resume/models/user.py +0 -0
  34. {rxresume_python-0.3.0 → rxresume_python-0.4.0}/rxresume_python.egg-info/dependency_links.txt +0 -0
  35. {rxresume_python-0.3.0 → rxresume_python-0.4.0}/rxresume_python.egg-info/requires.txt +0 -0
  36. {rxresume_python-0.3.0 → rxresume_python-0.4.0}/rxresume_python.egg-info/top_level.txt +0 -0
  37. {rxresume_python-0.3.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.3.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,14 +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
- - **Full API coverage**: Integrated modules for Resume management, Auth, Job Tracker (Applications), AI Agent prompts, AI Providers configurations, Statistics, and Storage uploads.
45
+ - **Full API coverage**: Integrated modules for Resume management, Job Applications tracking, AI Agent, AI Providers configurations, Global Statistics, and Feature Flags.
46
46
  - **Type safety**: Fully typed models for all entities using Pydantic V2.
47
47
  - **Robust error handling**: Raw API status errors are automatically parsed into specific exceptions (`AuthenticationError`, `NotFoundError`, etc.).
48
48
  - **Developer Experience (DX)**: Code-completion ready with clear typing and docstrings.
49
49
 
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
+ ```
50
79
 
51
80
  ---
52
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
+
53
96
  ## Installation
54
97
 
55
98
  Install the package via `pip` or your favorite package manager:
@@ -90,9 +133,9 @@ async def main():
90
133
  new_resume = await client.resumes.import_resume(import_data)
91
134
  print(f"Created resume: {new_resume.name} (ID: {new_resume.id})")
92
135
 
93
- # Fetch the generated PDF URL
94
- pdf_url = await client.resumes.get_pdf_url(new_resume.id)
95
- 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")
96
139
 
97
140
  except Exception as e:
98
141
  print(f"An error occurred: {e}")
@@ -105,7 +148,6 @@ if __name__ == "__main__":
105
148
 
106
149
  ```python
107
150
  from reactive_resume import RxResumeClient
108
- from reactive_resume.models import ResumeImportData
109
151
 
110
152
  with RxResumeClient(base_url="https://rxresu.me", api_key="your_api_key") as client:
111
153
  resumes = client.resumes.list()
@@ -113,33 +155,30 @@ with RxResumeClient(base_url="https://rxresu.me", api_key="your_api_key") as cli
113
155
  print(f"Resume: {resume.name} (Slug: {resume.slug})")
114
156
  ```
115
157
 
116
- ### 3. Advanced Features (AI Agent, Storage, Statistics, Applications)
158
+ ### 3. Advanced Features (AI Agent, Statistics, Applications)
117
159
 
118
160
  ```python
119
161
  with RxResumeClient(base_url="https://rxresu.me", api_key="your_api_key") as client:
120
- # 1. Ask the AI agent to optimize a resume summary
121
- ai_response = client.agent.chat("resume_id_here", "Suggest a professional summary for a software developer.")
122
- print(f"AI Suggestion: {ai_response.response}")
123
-
124
- # 2. Upload a profile image to storage
125
- with open("avatar.png", "rb") as f:
126
- file_metadata = client.storage.upload_file(f.read(), "avatar.png")
127
- print(f"Uploaded Image URL: {file_metadata.url}")
128
-
129
- # 3. Retrieve resume metrics
130
- stats = client.statistics.get("resume_id_here")
131
- print(f"Views: {stats.views}, Downloads: {stats.downloads}")
132
-
133
- # 4. Log a new job application
134
- from reactive_resume.models import ApplicationCreate
135
- app = client.applications.create(ApplicationCreate(
136
- company="Google",
137
- position="Senior Backend Engineer",
138
- stage="Interviewing"
139
- ))
140
- print(f"Logged Application ID: {app.id}")
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')}")
141
179
  ```
142
180
 
181
+
143
182
  ---
144
183
 
145
184
 
@@ -13,14 +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
- - **Full API coverage**: Integrated modules for Resume management, Auth, Job Tracker (Applications), AI Agent prompts, AI Providers configurations, Statistics, and Storage uploads.
16
+ - **Full API coverage**: Integrated modules for Resume management, Job Applications tracking, AI Agent, AI Providers configurations, Global Statistics, and Feature Flags.
17
17
  - **Type safety**: Fully typed models for all entities using Pydantic V2.
18
18
  - **Robust error handling**: Raw API status errors are automatically parsed into specific exceptions (`AuthenticationError`, `NotFoundError`, etc.).
19
19
  - **Developer Experience (DX)**: Code-completion ready with clear typing and docstrings.
20
20
 
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
+ ```
21
50
 
22
51
  ---
23
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
+
24
67
  ## Installation
25
68
 
26
69
  Install the package via `pip` or your favorite package manager:
@@ -61,9 +104,9 @@ async def main():
61
104
  new_resume = await client.resumes.import_resume(import_data)
62
105
  print(f"Created resume: {new_resume.name} (ID: {new_resume.id})")
63
106
 
64
- # Fetch the generated PDF URL
65
- pdf_url = await client.resumes.get_pdf_url(new_resume.id)
66
- 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")
67
110
 
68
111
  except Exception as e:
69
112
  print(f"An error occurred: {e}")
@@ -76,7 +119,6 @@ if __name__ == "__main__":
76
119
 
77
120
  ```python
78
121
  from reactive_resume import RxResumeClient
79
- from reactive_resume.models import ResumeImportData
80
122
 
81
123
  with RxResumeClient(base_url="https://rxresu.me", api_key="your_api_key") as client:
82
124
  resumes = client.resumes.list()
@@ -84,33 +126,30 @@ with RxResumeClient(base_url="https://rxresu.me", api_key="your_api_key") as cli
84
126
  print(f"Resume: {resume.name} (Slug: {resume.slug})")
85
127
  ```
86
128
 
87
- ### 3. Advanced Features (AI Agent, Storage, Statistics, Applications)
129
+ ### 3. Advanced Features (AI Agent, Statistics, Applications)
88
130
 
89
131
  ```python
90
132
  with RxResumeClient(base_url="https://rxresu.me", api_key="your_api_key") as client:
91
- # 1. Ask the AI agent to optimize a resume summary
92
- ai_response = client.agent.chat("resume_id_here", "Suggest a professional summary for a software developer.")
93
- print(f"AI Suggestion: {ai_response.response}")
94
-
95
- # 2. Upload a profile image to storage
96
- with open("avatar.png", "rb") as f:
97
- file_metadata = client.storage.upload_file(f.read(), "avatar.png")
98
- print(f"Uploaded Image URL: {file_metadata.url}")
99
-
100
- # 3. Retrieve resume metrics
101
- stats = client.statistics.get("resume_id_here")
102
- print(f"Views: {stats.views}, Downloads: {stats.downloads}")
103
-
104
- # 4. Log a new job application
105
- from reactive_resume.models import ApplicationCreate
106
- app = client.applications.create(ApplicationCreate(
107
- company="Google",
108
- position="Senior Backend Engineer",
109
- stage="Interviewing"
110
- ))
111
- print(f"Logged Application ID: {app.id}")
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')}")
112
150
  ```
113
151
 
152
+
114
153
  ---
115
154
 
116
155
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "rxresume-python"
7
- version = "0.3.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"
@@ -2,25 +2,28 @@
2
2
 
3
3
  from .auth import AuthAPI, AsyncAuthAPI
4
4
  from .resumes import ResumesAPI, AsyncResumesAPI
5
- from .applications import ApplicationsAPI, AsyncApplicationsAPI
6
5
  from .statistics import StatisticsAPI, AsyncStatisticsAPI
7
- from .storage import StorageAPI, AsyncStorageAPI
8
6
  from .agent import AgentAPI, AsyncAgentAPI
9
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
10
11
 
11
12
  __all__ = [
12
13
  "AuthAPI",
13
14
  "AsyncAuthAPI",
14
15
  "ResumesAPI",
15
16
  "AsyncResumesAPI",
16
- "ApplicationsAPI",
17
- "AsyncApplicationsAPI",
18
17
  "StatisticsAPI",
19
18
  "AsyncStatisticsAPI",
20
- "StorageAPI",
21
- "AsyncStorageAPI",
22
19
  "AgentAPI",
23
20
  "AsyncAgentAPI",
24
21
  "AiProvidersAPI",
25
22
  "AsyncAiProvidersAPI",
23
+ "FlagsAPI",
24
+ "AsyncFlagsAPI",
25
+ "AIAPI",
26
+ "AsyncAIAPI",
27
+ "ApplicationsAPI",
28
+ "AsyncApplicationsAPI",
26
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)