recallrai 0.1.0__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/session.py CHANGED
@@ -6,13 +6,19 @@ Session management functionality for the RecallrAI SDK.
6
6
  """
7
7
 
8
8
  from typing import List
9
- from .utils import HTTPClient, RecallrAIError
9
+ from .utils import HTTPClient
10
10
  from .models import (
11
11
  Context,
12
12
  Message,
13
13
  SessionStatus,
14
14
  MessageRole
15
15
  )
16
+ from .exceptions import (
17
+ UserNotFoundError,
18
+ SessionNotFoundError,
19
+ InvalidSessionStateError,
20
+ RecallrAIError
21
+ )
16
22
  from logging import getLogger
17
23
 
18
24
  logger = getLogger(__name__)
@@ -51,21 +57,16 @@ class Session:
51
57
  message: Content of the user message
52
58
 
53
59
  Raises:
54
- BadRequestError: If the session is already processed
55
- NotFoundError: If the session or user is not found
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
56
68
  """
57
- # Check the status of session
58
- status = self.get_status()
59
- if status == SessionStatus.PROCESSED:
60
- raise RecallrAIError("Cannot add message to a session that has already been processed")
61
- elif status == SessionStatus.PROCESSING:
62
- raise RecallrAIError("Cannot add message to a session that is currently being processed")
63
-
64
- # Add the user message
65
- self._http.post(
66
- f"/api/v1/users/{self.user_id}/sessions/{self.session_id}/add-message",
67
- data={"message": message, "role": MessageRole.USER},
68
- )
69
+ self._add_message(message, MessageRole.USER)
69
70
 
70
71
  def add_assistant_message(self, message: str) -> None:
71
72
  """
@@ -75,21 +76,56 @@ class Session:
75
76
  message: Content of the assistant message
76
77
 
77
78
  Raises:
78
- BadRequestError: If the session is already processed
79
- NotFoundError: If the session or user is not found
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
80
87
  """
81
- # Check the status of session
82
- status = self.get_status()
83
- if status == SessionStatus.PROCESSED:
84
- raise RecallrAIError("Cannot add message to a session that has already been processed")
85
- elif status == SessionStatus.PROCESSING:
86
- raise RecallrAIError("Cannot add message to a session that is currently being processed")
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.
87
93
 
88
- # Add the assistant message
89
- self._http.post(
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(
90
109
  f"/api/v1/users/{self.user_id}/sessions/{self.session_id}/add-message",
91
- data={"message": message, "role": MessageRole.ASSISTANT},
110
+ data={"message": message, "role": role.value},
92
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
+ )
93
129
 
94
130
  def get_context(self) -> Context:
95
131
  """
@@ -102,17 +138,36 @@ class Session:
102
138
  Context information with the memory text and whether memory was used
103
139
 
104
140
  Raises:
105
- NotFoundError: If the session or user is not found
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
106
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
+ )
107
165
  status = self.get_status()
108
166
  if status == SessionStatus.PROCESSED:
109
167
  logger.warning("Cannot add message to a session that has already been processed")
110
168
  elif status == SessionStatus.PROCESSING:
111
169
  logger.warning("Cannot add message to a session that is currently being processed")
112
- response = self._http.get(
113
- f"/api/v1/users/{self.user_id}/sessions/{self.session_id}/context"
114
- )
115
- return Context.from_api_response(response)
170
+ return Context.from_api_response(response.json())
116
171
 
117
172
  def process(self) -> None:
118
173
  """
@@ -122,21 +177,35 @@ class Session:
122
177
  the user's memory.
123
178
 
124
179
  Raises:
125
- BadRequestError: If the session is already processed or being processed
126
- NotFoundError: If the session or user is not found
127
- SessionProcessingError: If there is an error during processing
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
128
188
  """
129
- # Check the status of session
130
- status = self.get_status()
131
- if status == SessionStatus.PROCESSED:
132
- raise RecallrAIError("Cannot process a session that has already been processed")
133
- elif status == SessionStatus.PROCESSING:
134
- raise RecallrAIError("Cannot process a session that is currently being processed")
135
-
136
- # Process the session
137
- self._http.post(
189
+ response = self._http.post(
138
190
  f"/api/v1/users/{self.user_id}/sessions/{self.session_id}/process"
139
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
+ )
140
209
 
141
210
  def get_status(self) -> SessionStatus:
142
211
  """
@@ -146,12 +215,32 @@ class Session:
146
215
  SessionStatus: The current status of the session
147
216
 
148
217
  Raises:
149
- NotFoundError: If the session or user is not found
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
150
225
  """
151
226
  response = self._http.get(
152
227
  f"/api/v1/users/{self.user_id}/sessions/{self.session_id}/status"
153
228
  )
154
- return SessionStatus(response["status"])
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"])
155
244
 
156
245
  def get_messages(self) -> List[Message]:
157
246
  """
@@ -161,9 +250,30 @@ class Session:
161
250
  List of messages in the session
162
251
 
163
252
  Raises:
164
- NotFoundError: If the session or user is not found
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
165
260
  """
166
261
  response = self._http.get(
167
262
  f"/api/v1/users/{self.user_id}/sessions/{self.session_id}/messages"
168
263
  )
169
- return [Message(**msg) for msg in response["messages"]]
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 CHANGED
@@ -6,6 +6,15 @@ from typing import Any, Dict, Optional
6
6
  from .utils import HTTPClient
7
7
  from .models import User as UserModel, SessionList
8
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__)
9
18
 
10
19
  class User:
11
20
  """
@@ -46,18 +55,33 @@ class User:
46
55
  The updated user object
47
56
 
48
57
  Raises:
49
- NotFoundError: If the user is not found
50
- ValidationError: If the new_user_id is invalid
51
- BadRequestError: If a user with the new_user_id already exists
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
52
65
  """
53
66
  data = {}
54
67
  if new_metadata is not None:
55
- data["new_metadata"] = new_metadata
68
+ data["metadata"] = new_metadata
56
69
  if new_user_id is not None:
57
70
  data["new_user_id"] = new_user_id
58
71
 
59
72
  response = self._http.put(f"/api/v1/users/{self.user_id}", data=data)
60
- updated_data = UserModel.from_api_response(response)
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())
61
85
 
62
86
  # Update internal state
63
87
  self._user_data = updated_data
@@ -72,9 +96,22 @@ class User:
72
96
  Delete this user.
73
97
 
74
98
  Raises:
75
- NotFoundError: If the user is not found
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
76
105
  """
77
- self._http.delete(f"/api/v1/users/{self.user_id}")
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
+ )
78
115
 
79
116
  def create_session(self, auto_process_after_minutes: int = -1) -> Session:
80
117
  """
@@ -87,14 +124,27 @@ class User:
87
124
  A Session object to interact with the created session
88
125
 
89
126
  Raises:
90
- ValidationError: If auto_process_after_minutes is invalid
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
91
133
  """
92
134
  response = self._http.post(
93
135
  f"/api/v1/users/{self.user_id}/sessions",
94
136
  data={"auto_process_after_minutes": auto_process_after_minutes},
95
137
  )
96
138
 
97
- session_id = response["session_id"]
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"]
98
148
  return Session(self._http, self.user_id, session_id)
99
149
 
100
150
  def get_session(self, session_id: str) -> Session:
@@ -108,9 +158,23 @@ class User:
108
158
  A Session object to interact with the session
109
159
 
110
160
  Raises:
111
- NotFoundError: If the session is not found
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
112
168
  """
113
- return Session(self._http, self.user_id, session_id)
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)}")
114
178
 
115
179
  def list_sessions(self, offset: int = 0, limit: int = 10) -> SessionList:
116
180
  """
@@ -122,9 +186,26 @@ class User:
122
186
 
123
187
  Returns:
124
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
125
197
  """
126
198
  response = self._http.get(
127
199
  f"/api/v1/users/{self.user_id}/sessions",
128
200
  params={"offset": offset, "limit": limit},
129
201
  )
130
- return SessionList.from_api_response(response)
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())
@@ -1,10 +1,8 @@
1
1
  # Path: recallrai/utils/__init__.py
2
2
  # Description: Package initialization for utilities
3
3
 
4
- from .http import HTTPClient
5
- from .exceptions import RecallrAIError
4
+ from .http_client import HTTPClient
6
5
 
7
6
  __all__ = [
8
7
  "HTTPClient",
9
- "RecallrAIError",
10
- ]
8
+ ]
@@ -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)