recallrai 0.0.1__py3-none-any.whl → 0.1.1__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.
Potentially problematic release.
This version of recallrai might be problematic. Click here for more details.
- recallrai/__init__.py +20 -0
- recallrai/client.py +135 -0
- recallrai/exceptions/__init__.py +28 -0
- recallrai/exceptions/auth.py +24 -0
- recallrai/exceptions/base.py +37 -0
- recallrai/exceptions/network.py +58 -0
- recallrai/exceptions/server.py +82 -0
- recallrai/exceptions/sessions.py +60 -0
- recallrai/exceptions/users.py +61 -0
- recallrai/exceptions/validation.py +24 -0
- recallrai/models/__init__.py +16 -0
- recallrai/models/session.py +138 -0
- recallrai/models/user.py +75 -0
- recallrai/session.py +279 -0
- recallrai/user.py +211 -0
- recallrai/utils/__init__.py +8 -0
- recallrai/utils/http_client.py +123 -0
- recallrai-0.1.1.dist-info/METADATA +440 -0
- recallrai-0.1.1.dist-info/RECORD +20 -0
- {recallrai-0.0.1.dist-info → recallrai-0.1.1.dist-info}/WHEEL +1 -1
- recallrai/__main__.py +0 -0
- recallrai-0.0.1.dist-info/METADATA +0 -17
- recallrai-0.0.1.dist-info/RECORD +0 -5
recallrai/models/user.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Path: recallrai/models/user.py
|
|
2
|
+
# Description: User data models for the RecallrAI SDK
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
User-related data models for the RecallrAI SDK.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import Any, Dict
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel, Field
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class User(BaseModel):
|
|
15
|
+
"""Represents a user in the RecallrAI system."""
|
|
16
|
+
|
|
17
|
+
user_id: str = Field(..., description="Unique identifier for the user")
|
|
18
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Custom metadata for the user")
|
|
19
|
+
created_at: datetime = Field(..., description="When the user was created")
|
|
20
|
+
last_active_at: datetime = Field(..., description="When the user was last active")
|
|
21
|
+
|
|
22
|
+
class Config:
|
|
23
|
+
"""Pydantic configuration."""
|
|
24
|
+
frozen = True
|
|
25
|
+
json_encoders = {
|
|
26
|
+
datetime: lambda dt: dt.isoformat()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def from_api_response(cls, data: Dict[str, Any]) -> "User":
|
|
31
|
+
"""
|
|
32
|
+
Create a User instance from an API response.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
data: API response data
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
A User instance
|
|
39
|
+
"""
|
|
40
|
+
if "user" in data:
|
|
41
|
+
user_data = data["user"]
|
|
42
|
+
else:
|
|
43
|
+
user_data = data
|
|
44
|
+
|
|
45
|
+
return cls(
|
|
46
|
+
user_id=user_data["user_id"],
|
|
47
|
+
metadata=user_data.get("metadata", {}),
|
|
48
|
+
created_at=user_data["created_at"],
|
|
49
|
+
last_active_at=user_data["last_active_at"],
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class UserList(BaseModel):
|
|
54
|
+
"""Represents a paginated list of users."""
|
|
55
|
+
|
|
56
|
+
users: list[User] = Field(..., description="List of users")
|
|
57
|
+
total: int = Field(..., description="Total number of users")
|
|
58
|
+
has_more: bool = Field(..., description="Whether there are more users to fetch")
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def from_api_response(cls, data: Dict[str, Any]) -> "UserList":
|
|
62
|
+
"""
|
|
63
|
+
Create a UserList instance from an API response.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
data: API response data
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
A UserList instance
|
|
70
|
+
"""
|
|
71
|
+
return cls(
|
|
72
|
+
users=[User.from_api_response({"user": user}) for user in data["users"]],
|
|
73
|
+
total=data["total"],
|
|
74
|
+
has_more=data["has_more"],
|
|
75
|
+
)
|
recallrai/session.py
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# Path: recallrai/session.py
|
|
2
|
+
# Description: Session management class for the RecallrAI SDK
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Session management functionality for the RecallrAI SDK.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import List
|
|
9
|
+
from .utils import HTTPClient
|
|
10
|
+
from .models import (
|
|
11
|
+
Context,
|
|
12
|
+
Message,
|
|
13
|
+
SessionStatus,
|
|
14
|
+
MessageRole
|
|
15
|
+
)
|
|
16
|
+
from .exceptions import (
|
|
17
|
+
UserNotFoundError,
|
|
18
|
+
SessionNotFoundError,
|
|
19
|
+
InvalidSessionStateError,
|
|
20
|
+
RecallrAIError
|
|
21
|
+
)
|
|
22
|
+
from logging import getLogger
|
|
23
|
+
|
|
24
|
+
logger = getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
class Session:
|
|
27
|
+
"""
|
|
28
|
+
Manages a conversation session with RecallrAI.
|
|
29
|
+
|
|
30
|
+
This class handles adding messages, retrieving context, and processing the session
|
|
31
|
+
to update the user's memory.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
http_client: HTTPClient,
|
|
37
|
+
user_id: str,
|
|
38
|
+
session_id: str,
|
|
39
|
+
):
|
|
40
|
+
"""
|
|
41
|
+
Initialize a session.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
http_client: HTTP client for API communication
|
|
45
|
+
user_id: ID of the user who owns this session
|
|
46
|
+
session_id: Unique identifier for the session
|
|
47
|
+
"""
|
|
48
|
+
self._http = http_client
|
|
49
|
+
self.user_id = user_id
|
|
50
|
+
self.session_id = session_id
|
|
51
|
+
|
|
52
|
+
def add_user_message(self, message: str) -> None:
|
|
53
|
+
"""
|
|
54
|
+
Add a user message to the session.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
message: Content of the user message
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
UserNotFoundError: If the user is not found
|
|
61
|
+
SessionNotFoundError: If the session is not found
|
|
62
|
+
InvalidSessionStateError: If the session is already processed or processing
|
|
63
|
+
AuthenticationError: If the API key or project ID is invalid
|
|
64
|
+
InternalServerError: If the server encounters an error
|
|
65
|
+
NetworkError: If there are network issues
|
|
66
|
+
TimeoutError: If the request times out
|
|
67
|
+
RecallrAIError: For other API-related errors
|
|
68
|
+
"""
|
|
69
|
+
self._add_message(message, MessageRole.USER)
|
|
70
|
+
|
|
71
|
+
def add_assistant_message(self, message: str) -> None:
|
|
72
|
+
"""
|
|
73
|
+
Add an assistant message to the session.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
message: Content of the assistant message
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
UserNotFoundError: If the user is not found
|
|
80
|
+
SessionNotFoundError: If the session is not found
|
|
81
|
+
InvalidSessionStateError: If the session is already processed or processing
|
|
82
|
+
AuthenticationError: If the API key or project ID is invalid
|
|
83
|
+
InternalServerError: If the server encounters an error
|
|
84
|
+
NetworkError: If there are network issues
|
|
85
|
+
TimeoutError: If the request times out
|
|
86
|
+
RecallrAIError: For other API-related errors
|
|
87
|
+
"""
|
|
88
|
+
self._add_message(message, MessageRole.ASSISTANT)
|
|
89
|
+
|
|
90
|
+
def _add_message(self, message: str, role: MessageRole) -> None:
|
|
91
|
+
"""
|
|
92
|
+
Internal helper to add a message to the session.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
message: Content of the message
|
|
96
|
+
role: Role of the message sender
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
UserNotFoundError: If the user is not found
|
|
100
|
+
SessionNotFoundError: If the session is not found
|
|
101
|
+
InvalidSessionStateError: If the session is already processed or processing
|
|
102
|
+
AuthenticationError: If the API key or project ID is invalid
|
|
103
|
+
InternalServerError: If the server encounters an error
|
|
104
|
+
NetworkError: If there are network issues
|
|
105
|
+
TimeoutError: If the request times out
|
|
106
|
+
RecallrAIError: For other API-related errors
|
|
107
|
+
"""
|
|
108
|
+
response = self._http.post(
|
|
109
|
+
f"/api/v1/users/{self.user_id}/sessions/{self.session_id}/add-message",
|
|
110
|
+
data={"message": message, "role": role.value},
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if response.status_code == 404:
|
|
114
|
+
# Check if it's a user not found or session not found error
|
|
115
|
+
detail = response.json().get('detail', '')
|
|
116
|
+
if f"User {self.user_id} not found" in detail:
|
|
117
|
+
raise UserNotFoundError(user_id=self.user_id)
|
|
118
|
+
else:
|
|
119
|
+
raise SessionNotFoundError(session_id=self.session_id)
|
|
120
|
+
elif response.status_code == 400:
|
|
121
|
+
raise InvalidSessionStateError(
|
|
122
|
+
message=f"Cannot add message to session with status {self.get_status()}",
|
|
123
|
+
)
|
|
124
|
+
elif response.status_code != 200:
|
|
125
|
+
raise RecallrAIError(
|
|
126
|
+
message=f"Failed to add message: {response.json().get('detail', 'Unknown error')}",
|
|
127
|
+
http_status=response.status_code
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def get_context(self) -> Context:
|
|
131
|
+
"""
|
|
132
|
+
Get the current context for this session.
|
|
133
|
+
|
|
134
|
+
The context contains information from the user's memory that is relevant
|
|
135
|
+
to the current conversation.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Context information with the memory text and whether memory was used
|
|
139
|
+
|
|
140
|
+
Raises:
|
|
141
|
+
UserNotFoundError: If the user is not found
|
|
142
|
+
SessionNotFoundError: If the session is not found
|
|
143
|
+
AuthenticationError: If the API key or project ID is invalid
|
|
144
|
+
InternalServerError: If the server encounters an error
|
|
145
|
+
NetworkError: If there are network issues
|
|
146
|
+
TimeoutError: If the request times out
|
|
147
|
+
RecallrAIError: For other API-related errors
|
|
148
|
+
"""
|
|
149
|
+
response = self._http.get(
|
|
150
|
+
f"/api/v1/users/{self.user_id}/sessions/{self.session_id}/context"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
if response.status_code == 404:
|
|
154
|
+
# Check if it's a user not found or session not found error
|
|
155
|
+
detail = response.json().get('detail', '')
|
|
156
|
+
if f"User {self.user_id} not found" in detail:
|
|
157
|
+
raise UserNotFoundError(user_id=self.user_id)
|
|
158
|
+
else:
|
|
159
|
+
raise SessionNotFoundError(session_id=self.session_id)
|
|
160
|
+
elif response.status_code != 200:
|
|
161
|
+
raise RecallrAIError(
|
|
162
|
+
message=f"Failed to get context: {response.json().get('detail', 'Unknown error')}",
|
|
163
|
+
http_status=response.status_code
|
|
164
|
+
)
|
|
165
|
+
status = self.get_status()
|
|
166
|
+
if status == SessionStatus.PROCESSED:
|
|
167
|
+
logger.warning("Cannot add message to a session that has already been processed")
|
|
168
|
+
elif status == SessionStatus.PROCESSING:
|
|
169
|
+
logger.warning("Cannot add message to a session that is currently being processed")
|
|
170
|
+
return Context.from_api_response(response.json())
|
|
171
|
+
|
|
172
|
+
def process(self) -> None:
|
|
173
|
+
"""
|
|
174
|
+
Process the session to update the user's memory.
|
|
175
|
+
|
|
176
|
+
This method triggers the processing of the conversation to extract and update
|
|
177
|
+
the user's memory.
|
|
178
|
+
|
|
179
|
+
Raises:
|
|
180
|
+
UserNotFoundError: If the user is not found
|
|
181
|
+
SessionNotFoundError: If the session is not found
|
|
182
|
+
InvalidSessionStateError: If the session is already processed or being processed
|
|
183
|
+
AuthenticationError: If the API key or project ID is invalid
|
|
184
|
+
InternalServerError: If the server encounters an error
|
|
185
|
+
NetworkError: If there are network issues
|
|
186
|
+
TimeoutError: If the request times out
|
|
187
|
+
RecallrAIError: For other API-related errors
|
|
188
|
+
"""
|
|
189
|
+
response = self._http.post(
|
|
190
|
+
f"/api/v1/users/{self.user_id}/sessions/{self.session_id}/process"
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
if response.status_code == 404:
|
|
194
|
+
# Check if it's a user not found or session not found error
|
|
195
|
+
detail = response.json().get('detail', '')
|
|
196
|
+
if f"User {self.user_id} not found" in detail:
|
|
197
|
+
raise UserNotFoundError(user_id=self.user_id)
|
|
198
|
+
else:
|
|
199
|
+
raise SessionNotFoundError(session_id=self.session_id)
|
|
200
|
+
elif response.status_code == 400:
|
|
201
|
+
raise InvalidSessionStateError(
|
|
202
|
+
message=f"{response.json().get('detail', f'Cannot process session with status {self.get_status()}')}",
|
|
203
|
+
)
|
|
204
|
+
elif response.status_code != 200:
|
|
205
|
+
raise RecallrAIError(
|
|
206
|
+
message=f"Failed to process session: {response.json().get('detail', 'Unknown error')}",
|
|
207
|
+
http_status=response.status_code
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
def get_status(self) -> SessionStatus:
|
|
211
|
+
"""
|
|
212
|
+
Get the current status of the session.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
SessionStatus: The current status of the session
|
|
216
|
+
|
|
217
|
+
Raises:
|
|
218
|
+
UserNotFoundError: If the user is not found
|
|
219
|
+
SessionNotFoundError: If the session is not found
|
|
220
|
+
AuthenticationError: If the API key or project ID is invalid
|
|
221
|
+
InternalServerError: If the server encounters an error
|
|
222
|
+
NetworkError: If there are network issues
|
|
223
|
+
TimeoutError: If the request times out
|
|
224
|
+
RecallrAIError: For other API-related errors
|
|
225
|
+
"""
|
|
226
|
+
response = self._http.get(
|
|
227
|
+
f"/api/v1/users/{self.user_id}/sessions/{self.session_id}/status"
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
if response.status_code == 404:
|
|
231
|
+
# Check if it's a user not found or session not found error
|
|
232
|
+
detail = response.json().get('detail', '')
|
|
233
|
+
if f"User {self.user_id} not found" in detail:
|
|
234
|
+
raise UserNotFoundError(user_id=self.user_id)
|
|
235
|
+
else:
|
|
236
|
+
raise SessionNotFoundError(session_id=self.session_id)
|
|
237
|
+
elif response.status_code != 200:
|
|
238
|
+
raise RecallrAIError(
|
|
239
|
+
message=f"Failed to get session status: {response.json().get('detail', 'Unknown error')}",
|
|
240
|
+
http_status=response.status_code
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
return SessionStatus(response.json()["status"])
|
|
244
|
+
|
|
245
|
+
def get_messages(self) -> List[Message]:
|
|
246
|
+
"""
|
|
247
|
+
Get all messages in the session.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
List of messages in the session
|
|
251
|
+
|
|
252
|
+
Raises:
|
|
253
|
+
UserNotFoundError: If the user is not found
|
|
254
|
+
SessionNotFoundError: If the session is not found
|
|
255
|
+
AuthenticationError: If the API key or project ID is invalid
|
|
256
|
+
InternalServerError: If the server encounters an error
|
|
257
|
+
NetworkError: If there are network issues
|
|
258
|
+
TimeoutError: If the request times out
|
|
259
|
+
RecallrAIError: For other API-related errors
|
|
260
|
+
"""
|
|
261
|
+
response = self._http.get(
|
|
262
|
+
f"/api/v1/users/{self.user_id}/sessions/{self.session_id}/messages"
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
if response.status_code == 404:
|
|
266
|
+
# Check if it's a user not found or session not found error
|
|
267
|
+
detail = response.json().get('detail', '')
|
|
268
|
+
if f"User {self.user_id} not found" in detail:
|
|
269
|
+
raise UserNotFoundError(user_id=self.user_id)
|
|
270
|
+
else:
|
|
271
|
+
raise SessionNotFoundError(session_id=self.session_id)
|
|
272
|
+
elif response.status_code != 200:
|
|
273
|
+
raise RecallrAIError(
|
|
274
|
+
message=f"Failed to get messages: {response.json().get('detail', 'Unknown error')}",
|
|
275
|
+
http_status=response.status_code
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
data = response.json()
|
|
279
|
+
return [Message(**msg) for msg in data["messages"]]
|
recallrai/user.py
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""
|
|
2
|
+
User management functionality for the RecallrAI SDK.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
from .utils import HTTPClient
|
|
7
|
+
from .models import User as UserModel, SessionList
|
|
8
|
+
from .session import Session
|
|
9
|
+
from .exceptions import (
|
|
10
|
+
UserNotFoundError,
|
|
11
|
+
UserAlreadyExistsError,
|
|
12
|
+
SessionNotFoundError,
|
|
13
|
+
RecallrAIError
|
|
14
|
+
)
|
|
15
|
+
from logging import getLogger
|
|
16
|
+
|
|
17
|
+
logger = getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
class User:
|
|
20
|
+
"""
|
|
21
|
+
Represents a user in the RecallrAI system with methods for user management.
|
|
22
|
+
|
|
23
|
+
This class wraps a user object and provides methods for updating user data,
|
|
24
|
+
and for creating and managing sessions.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
http_client: HTTPClient,
|
|
30
|
+
user_data: UserModel,
|
|
31
|
+
):
|
|
32
|
+
"""
|
|
33
|
+
Initialize a user.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
http_client: HTTP client for API communication
|
|
37
|
+
user_data: User data model with user information
|
|
38
|
+
"""
|
|
39
|
+
self._http = http_client
|
|
40
|
+
self._user_data = user_data
|
|
41
|
+
self.user_id = user_data.user_id
|
|
42
|
+
self.metadata = user_data.metadata
|
|
43
|
+
self.created_at = user_data.created_at
|
|
44
|
+
self.last_active_at = user_data.last_active_at
|
|
45
|
+
|
|
46
|
+
def update(self, new_metadata: Optional[Dict[str, Any]] = None, new_user_id: Optional[str] = None) -> 'User':
|
|
47
|
+
"""
|
|
48
|
+
Update this user's metadata or ID.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
new_metadata: New metadata to associate with the user
|
|
52
|
+
new_user_id: New ID for the user
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
The updated user object
|
|
56
|
+
|
|
57
|
+
Raises:
|
|
58
|
+
UserNotFoundError: If the user is not found
|
|
59
|
+
UserAlreadyExistsError: If a user with the new_user_id already exists
|
|
60
|
+
AuthenticationError: If the API key or project ID is invalid
|
|
61
|
+
InternalServerError: If the server encounters an error
|
|
62
|
+
NetworkError: If there are network issues
|
|
63
|
+
TimeoutError: If the request times out
|
|
64
|
+
RecallrAIError: For other API-related errors
|
|
65
|
+
"""
|
|
66
|
+
data = {}
|
|
67
|
+
if new_metadata is not None:
|
|
68
|
+
data["metadata"] = new_metadata
|
|
69
|
+
if new_user_id is not None:
|
|
70
|
+
data["new_user_id"] = new_user_id
|
|
71
|
+
|
|
72
|
+
response = self._http.put(f"/api/v1/users/{self.user_id}", data=data)
|
|
73
|
+
|
|
74
|
+
if response.status_code == 404:
|
|
75
|
+
raise UserNotFoundError(user_id=self.user_id)
|
|
76
|
+
elif response.status_code == 409:
|
|
77
|
+
raise UserAlreadyExistsError(user_id=new_user_id)
|
|
78
|
+
elif response.status_code != 200:
|
|
79
|
+
raise RecallrAIError(
|
|
80
|
+
message=f"Failed to update user: {response.json().get('detail', 'Unknown error')}",
|
|
81
|
+
http_status=response.status_code
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
updated_data = UserModel.from_api_response(response.json())
|
|
85
|
+
|
|
86
|
+
# Update internal state
|
|
87
|
+
self._user_data = updated_data
|
|
88
|
+
self.user_id = updated_data.user_id
|
|
89
|
+
self.metadata = updated_data.metadata
|
|
90
|
+
self.last_active_at = updated_data.last_active_at
|
|
91
|
+
|
|
92
|
+
return self
|
|
93
|
+
|
|
94
|
+
def delete(self) -> None:
|
|
95
|
+
"""
|
|
96
|
+
Delete this user.
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
UserNotFoundError: If the user is not found
|
|
100
|
+
AuthenticationError: If the API key or project ID is invalid
|
|
101
|
+
InternalServerError: If the server encounters an error
|
|
102
|
+
NetworkError: If there are network issues
|
|
103
|
+
TimeoutError: If the request times out
|
|
104
|
+
RecallrAIError: For other API-related errors
|
|
105
|
+
"""
|
|
106
|
+
response = self._http.delete(f"/api/v1/users/{self.user_id}")
|
|
107
|
+
|
|
108
|
+
if response.status_code == 404:
|
|
109
|
+
raise UserNotFoundError(user_id=self.user_id)
|
|
110
|
+
elif response.status_code != 204:
|
|
111
|
+
raise RecallrAIError(
|
|
112
|
+
message=f"Failed to delete user: {response.json().get('detail', 'Unknown error')}",
|
|
113
|
+
http_status=response.status_code
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
def create_session(self, auto_process_after_minutes: int = -1) -> Session:
|
|
117
|
+
"""
|
|
118
|
+
Create a new session for this user.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
auto_process_after_minutes: Minutes to wait before auto-processing (-1 to disable)
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
A Session object to interact with the created session
|
|
125
|
+
|
|
126
|
+
Raises:
|
|
127
|
+
UserNotFoundError: If the user is not found
|
|
128
|
+
AuthenticationError: If the API key or project ID is invalid
|
|
129
|
+
InternalServerError: If the server encounters an error
|
|
130
|
+
NetworkError: If there are network issues
|
|
131
|
+
TimeoutError: If the request times out
|
|
132
|
+
RecallrAIError: For other API-related errors
|
|
133
|
+
"""
|
|
134
|
+
response = self._http.post(
|
|
135
|
+
f"/api/v1/users/{self.user_id}/sessions",
|
|
136
|
+
data={"auto_process_after_minutes": auto_process_after_minutes},
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
if response.status_code == 404:
|
|
140
|
+
raise UserNotFoundError(user_id=self.user_id)
|
|
141
|
+
elif response.status_code != 201:
|
|
142
|
+
raise RecallrAIError(
|
|
143
|
+
message=f"Failed to create session: {response.json().get('detail', 'Unknown error')}",
|
|
144
|
+
http_status=response.status_code
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
session_id = response.json()["session_id"]
|
|
148
|
+
return Session(self._http, self.user_id, session_id)
|
|
149
|
+
|
|
150
|
+
def get_session(self, session_id: str) -> Session:
|
|
151
|
+
"""
|
|
152
|
+
Get an existing session for this user.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
session_id: ID of the session to retrieve
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
A Session object to interact with the session
|
|
159
|
+
|
|
160
|
+
Raises:
|
|
161
|
+
UserNotFoundError: If the user is not found
|
|
162
|
+
SessionNotFoundError: If the session is not found
|
|
163
|
+
AuthenticationError: If the API key or project ID is invalid
|
|
164
|
+
InternalServerError: If the server encounters an error
|
|
165
|
+
NetworkError: If there are network issues
|
|
166
|
+
TimeoutError: If the request times out
|
|
167
|
+
RecallrAIError: For other API-related errors
|
|
168
|
+
"""
|
|
169
|
+
# Verify the session exists by checking its status
|
|
170
|
+
session = Session(self._http, self.user_id, session_id)
|
|
171
|
+
try:
|
|
172
|
+
session.get_status() # This will raise appropriate errors if the session doesn't exist
|
|
173
|
+
return session
|
|
174
|
+
except SessionNotFoundError:
|
|
175
|
+
raise
|
|
176
|
+
except Exception as e:
|
|
177
|
+
raise RecallrAIError(f"Error retrieving session: {str(e)}")
|
|
178
|
+
|
|
179
|
+
def list_sessions(self, offset: int = 0, limit: int = 10) -> SessionList:
|
|
180
|
+
"""
|
|
181
|
+
List sessions for this user with pagination.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
offset: Number of records to skip
|
|
185
|
+
limit: Maximum number of records to return
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
List of sessions with pagination info
|
|
189
|
+
|
|
190
|
+
Raises:
|
|
191
|
+
UserNotFoundError: If the user is not found
|
|
192
|
+
AuthenticationError: If the API key or project ID is invalid
|
|
193
|
+
InternalServerError: If the server encounters an error
|
|
194
|
+
NetworkError: If there are network issues
|
|
195
|
+
TimeoutError: If the request times out
|
|
196
|
+
RecallrAIError: For other API-related errors
|
|
197
|
+
"""
|
|
198
|
+
response = self._http.get(
|
|
199
|
+
f"/api/v1/users/{self.user_id}/sessions",
|
|
200
|
+
params={"offset": offset, "limit": limit},
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
if response.status_code == 404:
|
|
204
|
+
raise UserNotFoundError(user_id=self.user_id)
|
|
205
|
+
elif response.status_code != 200:
|
|
206
|
+
raise RecallrAIError(
|
|
207
|
+
message=f"Failed to list sessions: {response.json().get('detail', 'Unknown error')}",
|
|
208
|
+
http_status=response.status_code
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
return SessionList.from_api_response(response.json())
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Path: recallrai/utils/http.py
|
|
2
|
+
# Description: HTTP client utilities for the RecallrAI SDK
|
|
3
|
+
|
|
4
|
+
from httpx import Response, Client, TimeoutException, NetworkError, ConnectError
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
from ..exceptions import (
|
|
7
|
+
TimeoutError,
|
|
8
|
+
NetworkError as CustomNetworkError,
|
|
9
|
+
ConnectionError,
|
|
10
|
+
ValidationError,
|
|
11
|
+
InternalServerError,
|
|
12
|
+
AuthenticationError,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
class HTTPClient:
|
|
16
|
+
"""HTTP client for making requests to the RecallrAI API."""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
api_key: str,
|
|
21
|
+
project_id: str,
|
|
22
|
+
base_url: str,
|
|
23
|
+
timeout: int = 30,
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
Initialize the HTTP client.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
api_key: Your RecallrAI API key
|
|
30
|
+
project_id: Your project ID
|
|
31
|
+
base_url: The base URL for the RecallrAI API
|
|
32
|
+
timeout: Request timeout in seconds
|
|
33
|
+
"""
|
|
34
|
+
self.api_key = api_key
|
|
35
|
+
self.project_id = project_id
|
|
36
|
+
self.base_url = base_url.rstrip("/")
|
|
37
|
+
self.timeout = timeout
|
|
38
|
+
self.client = Client(
|
|
39
|
+
timeout=self.timeout,
|
|
40
|
+
headers={
|
|
41
|
+
"X-Api-Key": self.api_key,
|
|
42
|
+
"X-Project-Id": self.project_id,
|
|
43
|
+
"Content-Type": "application/json",
|
|
44
|
+
"Accept": "application/json",
|
|
45
|
+
"User-Agent": "RecallrAI-Python-SDK",
|
|
46
|
+
# TODO: "SDK-Version": "0.1.0",
|
|
47
|
+
},
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def request(
|
|
51
|
+
self,
|
|
52
|
+
method: str,
|
|
53
|
+
path: str,
|
|
54
|
+
params: Optional[Dict[str, Any]] = None,
|
|
55
|
+
data: Optional[Dict[str, Any]] = None,
|
|
56
|
+
) -> Response:
|
|
57
|
+
"""
|
|
58
|
+
Make a request to the RecallrAI API.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
method: HTTP method (GET, POST, PUT, DELETE)
|
|
62
|
+
path: API endpoint path
|
|
63
|
+
params: Query parameters
|
|
64
|
+
data: Request body data
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
The parsed JSON response
|
|
68
|
+
"""
|
|
69
|
+
url = f"{self.base_url}{path}"
|
|
70
|
+
|
|
71
|
+
# Filter out None values in params and data
|
|
72
|
+
if params:
|
|
73
|
+
params = {k: v for k, v in params.items() if v is not None}
|
|
74
|
+
|
|
75
|
+
if data:
|
|
76
|
+
data = {k: v for k, v in data.items() if v is not None}
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
response = self.client.request(
|
|
80
|
+
method=method,
|
|
81
|
+
url=url,
|
|
82
|
+
params=params,
|
|
83
|
+
json=data,
|
|
84
|
+
)
|
|
85
|
+
if response.status_code == 422:
|
|
86
|
+
raise ValidationError(
|
|
87
|
+
details=response.json()["detail"],
|
|
88
|
+
)
|
|
89
|
+
elif response.status_code == 500:
|
|
90
|
+
raise InternalServerError(
|
|
91
|
+
details=response.json()["detail"],
|
|
92
|
+
)
|
|
93
|
+
elif response.status_code == 401:
|
|
94
|
+
raise AuthenticationError(
|
|
95
|
+
details=response.json()["detail"],
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return response
|
|
99
|
+
except TimeoutException as e:
|
|
100
|
+
raise TimeoutError(f"Request timed out: {e}")
|
|
101
|
+
except NetworkError as e:
|
|
102
|
+
raise CustomNetworkError(f"Network error occurred: {e}")
|
|
103
|
+
except ConnectError as e:
|
|
104
|
+
raise ConnectionError(f"Failed to connect to the API: {e}")
|
|
105
|
+
except Exception as e:
|
|
106
|
+
# Handle other exceptions as needed
|
|
107
|
+
raise e
|
|
108
|
+
|
|
109
|
+
def get(self, path: str, params: Optional[Dict[str, Any]] = None) -> Response:
|
|
110
|
+
"""Make a GET request."""
|
|
111
|
+
return self.request("GET", path, params=params)
|
|
112
|
+
|
|
113
|
+
def post(self, path: str, data: Optional[Dict[str, Any]] = None) -> Response:
|
|
114
|
+
"""Make a POST request."""
|
|
115
|
+
return self.request("POST", path, data=data)
|
|
116
|
+
|
|
117
|
+
def put(self, path: str, data: Optional[Dict[str, Any]] = None) -> Response:
|
|
118
|
+
"""Make a PUT request."""
|
|
119
|
+
return self.request("PUT", path, data=data)
|
|
120
|
+
|
|
121
|
+
def delete(self, path: str) -> Response:
|
|
122
|
+
"""Make a DELETE request."""
|
|
123
|
+
return self.request("DELETE", path)
|