recallrai 0.0.1__py3-none-any.whl → 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of recallrai might be problematic. Click here for more details.
- recallrai/__init__.py +19 -0
- recallrai/client.py +220 -0
- recallrai/models/__init__.py +16 -0
- recallrai/models/session.py +138 -0
- recallrai/models/user.py +75 -0
- recallrai/session.py +169 -0
- recallrai/user.py +130 -0
- recallrai/utils/__init__.py +10 -0
- recallrai/utils/exceptions.py +61 -0
- recallrai/utils/http.py +167 -0
- recallrai-0.1.0.dist-info/METADATA +268 -0
- recallrai-0.1.0.dist-info/RECORD +13 -0
- {recallrai-0.0.1.dist-info → recallrai-0.1.0.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/__init__.py
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Path: recallrai/__init__.py
|
|
2
|
+
# Description: Package initialization file with SDK version and main class exports
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
RecallrAI Python SDK
|
|
6
|
+
|
|
7
|
+
This package provides a Python interface to interact with the RecallrAI API.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .client import RecallrAI
|
|
11
|
+
from .user import User
|
|
12
|
+
from .session import Session
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
15
|
+
__all__ = [
|
|
16
|
+
"RecallrAI",
|
|
17
|
+
"User",
|
|
18
|
+
"Session",
|
|
19
|
+
]
|
recallrai/client.py
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Path: recallrai/client.py
|
|
2
|
+
# Description: Main client class for the RecallrAI SDK
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Main client class for the RecallrAI SDK.
|
|
6
|
+
|
|
7
|
+
This module provides the RecallrAI class, which is the primary interface for the SDK.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import uuid
|
|
11
|
+
from typing import Any, Dict, Optional
|
|
12
|
+
from pydantic import HttpUrl
|
|
13
|
+
from .utils import HTTPClient, RecallrAIError
|
|
14
|
+
from .models import User as UserModel, UserList, SessionStatus, SessionList
|
|
15
|
+
from .user import User
|
|
16
|
+
from .session import Session
|
|
17
|
+
from logging import getLogger
|
|
18
|
+
|
|
19
|
+
logger = getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
class RecallrAI:
|
|
22
|
+
"""
|
|
23
|
+
Main client for interacting with the RecallrAI API.
|
|
24
|
+
|
|
25
|
+
This class provides methods for creating and managing users, sessions, and memories.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
api_key: str,
|
|
31
|
+
project_id: uuid.UUID,
|
|
32
|
+
base_url: HttpUrl = "https://api.recallrai.com",
|
|
33
|
+
timeout: int = 30,
|
|
34
|
+
):
|
|
35
|
+
"""
|
|
36
|
+
Initialize the RecallrAI client.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
api_key: Your RecallrAI API key
|
|
40
|
+
project_id: Your project ID
|
|
41
|
+
base_url: The base URL for the RecallrAI API
|
|
42
|
+
timeout: Request timeout in seconds
|
|
43
|
+
"""
|
|
44
|
+
if not api_key.startswith("rai_"):
|
|
45
|
+
raise ValueError("API key must start with 'rai_'")
|
|
46
|
+
|
|
47
|
+
self.api_key = api_key
|
|
48
|
+
self.project_id = str(project_id)
|
|
49
|
+
self.base_url = str(base_url)
|
|
50
|
+
|
|
51
|
+
self.http = HTTPClient(
|
|
52
|
+
api_key=self.api_key,
|
|
53
|
+
project_id=self.project_id,
|
|
54
|
+
base_url=self.base_url,
|
|
55
|
+
timeout=timeout,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# User management
|
|
59
|
+
def create_user(self, user_id: str, metadata: Optional[Dict[str, Any]] = None) -> User:
|
|
60
|
+
"""
|
|
61
|
+
Create a new user.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
user_id: Unique identifier for the user
|
|
65
|
+
metadata: Optional metadata to associate with the user
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
The created user object
|
|
69
|
+
|
|
70
|
+
Raises:
|
|
71
|
+
ValidationError: If the user_id is invalid
|
|
72
|
+
BadRequestError: If a user with the same ID already exists
|
|
73
|
+
"""
|
|
74
|
+
response = self.http.post("/api/v1/users", data={"user_id": user_id, "metadata": metadata or {}})
|
|
75
|
+
user_data = UserModel.from_api_response(response)
|
|
76
|
+
return User(self.http, user_data)
|
|
77
|
+
|
|
78
|
+
def get_user(self, user_id: str) -> User:
|
|
79
|
+
"""
|
|
80
|
+
Get a user by ID.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
user_id: Unique identifier of the user
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
A User object representing the user
|
|
87
|
+
|
|
88
|
+
Raises:
|
|
89
|
+
NotFoundError: If the user is not found
|
|
90
|
+
"""
|
|
91
|
+
response = self.http.get(f"/api/v1/users/{user_id}")
|
|
92
|
+
user_data = UserModel.from_api_response(response)
|
|
93
|
+
return User(self.http, user_data)
|
|
94
|
+
|
|
95
|
+
def list_users(self, offset: int = 0, limit: int = 10) -> UserList:
|
|
96
|
+
"""
|
|
97
|
+
List users with pagination.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
offset: Number of records to skip
|
|
101
|
+
limit: Maximum number of records to return
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
List of users with pagination info
|
|
105
|
+
"""
|
|
106
|
+
response = self.http.get("/api/v1/users", params={"offset": offset, "limit": limit})
|
|
107
|
+
return UserList.from_api_response(response)
|
|
108
|
+
|
|
109
|
+
def update_user(
|
|
110
|
+
self,
|
|
111
|
+
user_id: str,
|
|
112
|
+
new_metadata: Optional[Dict[str, Any]] = None,
|
|
113
|
+
new_user_id: Optional[str] = None
|
|
114
|
+
) -> User:
|
|
115
|
+
"""
|
|
116
|
+
Update a user's metadata or ID.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
user_id: Current ID of the user
|
|
120
|
+
new_metadata: New metadata to associate with the user
|
|
121
|
+
new_user_id: New ID for the user
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
The updated user
|
|
125
|
+
|
|
126
|
+
Raises:
|
|
127
|
+
NotFoundError: If the user is not found
|
|
128
|
+
ValidationError: If the new_user_id is invalid
|
|
129
|
+
BadRequestError: If a user with the new_user_id already exists
|
|
130
|
+
"""
|
|
131
|
+
data = {}
|
|
132
|
+
if new_metadata is not None:
|
|
133
|
+
data["new_metadata"] = new_metadata
|
|
134
|
+
if new_user_id is not None:
|
|
135
|
+
data["new_user_id"] = new_user_id
|
|
136
|
+
|
|
137
|
+
response = self.http.put(f"/api/v1/users/{user_id}", data=data)
|
|
138
|
+
user_data = UserModel.from_api_response(response)
|
|
139
|
+
return User(self.http, user_data)
|
|
140
|
+
|
|
141
|
+
def delete_user(self, user_id: str) -> None:
|
|
142
|
+
"""
|
|
143
|
+
Delete a user.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
user_id: ID of the user to delete
|
|
147
|
+
|
|
148
|
+
Raises:
|
|
149
|
+
NotFoundError: If the user is not found
|
|
150
|
+
"""
|
|
151
|
+
self.http.delete(f"/api/v1/users/{user_id}")
|
|
152
|
+
|
|
153
|
+
# Session management
|
|
154
|
+
def create_session(self, user_id: str, auto_process_after_minutes: int = -1) -> Session:
|
|
155
|
+
"""
|
|
156
|
+
Create a new session for a user.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
user_id: ID of the user to create the session for
|
|
160
|
+
auto_process_after_minutes: Minutes to wait before auto-processing (-1 to disable)
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
A Session object to interact with the created session
|
|
164
|
+
|
|
165
|
+
Raises:
|
|
166
|
+
NotFoundError: If the user is not found
|
|
167
|
+
ValidationError: If auto_process_after_minutes is invalid
|
|
168
|
+
"""
|
|
169
|
+
response = self.http.post(
|
|
170
|
+
f"/api/v1/users/{user_id}/sessions",
|
|
171
|
+
data={"auto_process_after_minutes": auto_process_after_minutes},
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
session_id = response["session_id"]
|
|
175
|
+
return Session(self.http, user_id, session_id)
|
|
176
|
+
|
|
177
|
+
def get_session(self, user_id: str, session_id: str) -> Session:
|
|
178
|
+
"""
|
|
179
|
+
Get an existing session.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
user_id: ID of the user who owns the session
|
|
183
|
+
session_id: ID of the session to retrieve
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
A Session object to interact with the session
|
|
187
|
+
|
|
188
|
+
Raises:
|
|
189
|
+
NotFoundError: If the user or session is not found
|
|
190
|
+
"""
|
|
191
|
+
# Ensure the session exists by checking its status
|
|
192
|
+
session = Session(self.http, user_id, session_id)
|
|
193
|
+
status = session.get_status()
|
|
194
|
+
if status == SessionStatus.PROCESSING:
|
|
195
|
+
raise RecallrAIError("Session is already processing. You can't add messages to it. Create a new session instead.")
|
|
196
|
+
elif status == SessionStatus.PROCESSED:
|
|
197
|
+
raise RecallrAIError("Session has already been processed. You can't add messages to it. Create a new session instead.")
|
|
198
|
+
|
|
199
|
+
return session
|
|
200
|
+
|
|
201
|
+
def list_sessions(self, user_id: str, offset: int = 0, limit: int = 10) -> SessionList:
|
|
202
|
+
"""
|
|
203
|
+
List sessions for a user with pagination.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
user_id: ID of the user
|
|
207
|
+
offset: Number of records to skip
|
|
208
|
+
limit: Maximum number of records to return
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
List of sessions with pagination info
|
|
212
|
+
|
|
213
|
+
Raises:
|
|
214
|
+
NotFoundError: If the user is not found
|
|
215
|
+
"""
|
|
216
|
+
response = self.http.get(
|
|
217
|
+
f"/api/v1/users/{user_id}/sessions",
|
|
218
|
+
params={"offset": offset, "limit": limit},
|
|
219
|
+
)
|
|
220
|
+
return SessionList.from_api_response(response)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Path: recallrai/models/__init__.py
|
|
2
|
+
# Description: Package initialization for data models
|
|
3
|
+
|
|
4
|
+
from .session import Context, Message, MessageRole, Session, SessionList, SessionStatus
|
|
5
|
+
from .user import User, UserList
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"User",
|
|
9
|
+
"UserList",
|
|
10
|
+
"Session",
|
|
11
|
+
"SessionList",
|
|
12
|
+
"SessionStatus",
|
|
13
|
+
"Message",
|
|
14
|
+
"MessageRole",
|
|
15
|
+
"Context",
|
|
16
|
+
]
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Path: recallrai/models/session.py
|
|
2
|
+
# Description: Session data models for the RecallrAI SDK
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Session-related data models for the RecallrAI SDK.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import enum, uuid
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel, Field
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MessageRole(str, enum.Enum):
|
|
16
|
+
"""
|
|
17
|
+
Message role in a conversation.
|
|
18
|
+
"""
|
|
19
|
+
USER = "user"
|
|
20
|
+
ASSISTANT = "assistant"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Message(BaseModel):
|
|
24
|
+
"""
|
|
25
|
+
Represents a message in a conversation session.
|
|
26
|
+
"""
|
|
27
|
+
role: MessageRole = Field(..., description="Role of the message sender (user or assistant)")
|
|
28
|
+
content: str = Field(..., description="Content of the message")
|
|
29
|
+
timestamp: datetime = Field(..., description="When the message was sent")
|
|
30
|
+
|
|
31
|
+
class Config:
|
|
32
|
+
"""Pydantic configuration."""
|
|
33
|
+
frozen = True
|
|
34
|
+
json_encoders = {
|
|
35
|
+
datetime: lambda dt: dt.isoformat()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class SessionStatus(str, enum.Enum):
|
|
40
|
+
"""
|
|
41
|
+
Status of a session.
|
|
42
|
+
"""
|
|
43
|
+
PENDING = "pending"
|
|
44
|
+
PROCESSING = "processing"
|
|
45
|
+
PROCESSED = "processed"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Session(BaseModel):
|
|
49
|
+
"""
|
|
50
|
+
Represents a conversation session.
|
|
51
|
+
"""
|
|
52
|
+
session_id: uuid.UUID = Field(..., description="Unique identifier for the session")
|
|
53
|
+
status: SessionStatus = Field(..., description="Current status of the session")
|
|
54
|
+
created_at: datetime = Field(..., description="When the session was created")
|
|
55
|
+
|
|
56
|
+
class Config:
|
|
57
|
+
"""Pydantic configuration."""
|
|
58
|
+
frozen = True
|
|
59
|
+
json_encoders = {
|
|
60
|
+
datetime: lambda dt: dt.isoformat(),
|
|
61
|
+
uuid.UUID: lambda id: str(id)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def from_api_response(cls, data: Dict[str, Any]) -> "Session":
|
|
66
|
+
"""
|
|
67
|
+
Create a Session instance from an API response.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
data: API response data
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
A Session instance
|
|
74
|
+
"""
|
|
75
|
+
return cls(
|
|
76
|
+
session_id=data["session_id"],
|
|
77
|
+
status=data.get("status", SessionStatus.PENDING),
|
|
78
|
+
created_at=data.get("created_at", datetime.now()),
|
|
79
|
+
messages=None
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class SessionList(BaseModel):
|
|
84
|
+
"""
|
|
85
|
+
Represents a paginated list of sessions.
|
|
86
|
+
"""
|
|
87
|
+
sessions: List[Session] = Field(..., description="List of sessions")
|
|
88
|
+
total: int = Field(..., description="Total number of sessions")
|
|
89
|
+
has_more: bool = Field(..., description="Whether there are more sessions to fetch")
|
|
90
|
+
|
|
91
|
+
class Config:
|
|
92
|
+
"""Pydantic configuration."""
|
|
93
|
+
frozen = True
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
def from_api_response(cls, data: Dict[str, Any]) -> "SessionList":
|
|
97
|
+
"""
|
|
98
|
+
Create a SessionList instance from an API response.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
data: API response data
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
A SessionList instance
|
|
105
|
+
"""
|
|
106
|
+
return cls(
|
|
107
|
+
sessions=[Session.from_api_response(session) for session in data["sessions"]],
|
|
108
|
+
total=data["total"],
|
|
109
|
+
has_more=data["has_more"],
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class Context(BaseModel):
|
|
114
|
+
"""
|
|
115
|
+
Represents the context for a session.
|
|
116
|
+
"""
|
|
117
|
+
memory_used: bool = Field(..., description="Whether memory was used to generate the context")
|
|
118
|
+
context: str = Field(..., description="The context for the session")
|
|
119
|
+
|
|
120
|
+
class Config:
|
|
121
|
+
"""Pydantic configuration."""
|
|
122
|
+
frozen = True
|
|
123
|
+
|
|
124
|
+
@classmethod
|
|
125
|
+
def from_api_response(cls, data: Dict[str, Any]) -> "Context":
|
|
126
|
+
"""
|
|
127
|
+
Create a Context instance from an API response.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
data: API response data
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
A Context instance
|
|
134
|
+
"""
|
|
135
|
+
return cls(
|
|
136
|
+
memory_used=data["memory_used"],
|
|
137
|
+
context=data["context"],
|
|
138
|
+
)
|
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,169 @@
|
|
|
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, RecallrAIError
|
|
10
|
+
from .models import (
|
|
11
|
+
Context,
|
|
12
|
+
Message,
|
|
13
|
+
SessionStatus,
|
|
14
|
+
MessageRole
|
|
15
|
+
)
|
|
16
|
+
from logging import getLogger
|
|
17
|
+
|
|
18
|
+
logger = getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
class Session:
|
|
21
|
+
"""
|
|
22
|
+
Manages a conversation session with RecallrAI.
|
|
23
|
+
|
|
24
|
+
This class handles adding messages, retrieving context, and processing the session
|
|
25
|
+
to update the user's memory.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
http_client: HTTPClient,
|
|
31
|
+
user_id: str,
|
|
32
|
+
session_id: str,
|
|
33
|
+
):
|
|
34
|
+
"""
|
|
35
|
+
Initialize a session.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
http_client: HTTP client for API communication
|
|
39
|
+
user_id: ID of the user who owns this session
|
|
40
|
+
session_id: Unique identifier for the session
|
|
41
|
+
"""
|
|
42
|
+
self._http = http_client
|
|
43
|
+
self.user_id = user_id
|
|
44
|
+
self.session_id = session_id
|
|
45
|
+
|
|
46
|
+
def add_user_message(self, message: str) -> None:
|
|
47
|
+
"""
|
|
48
|
+
Add a user message to the session.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
message: Content of the user message
|
|
52
|
+
|
|
53
|
+
Raises:
|
|
54
|
+
BadRequestError: If the session is already processed
|
|
55
|
+
NotFoundError: If the session or user is not found
|
|
56
|
+
"""
|
|
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
|
+
|
|
70
|
+
def add_assistant_message(self, message: str) -> None:
|
|
71
|
+
"""
|
|
72
|
+
Add an assistant message to the session.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
message: Content of the assistant message
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
BadRequestError: If the session is already processed
|
|
79
|
+
NotFoundError: If the session or user is not found
|
|
80
|
+
"""
|
|
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")
|
|
87
|
+
|
|
88
|
+
# Add the assistant message
|
|
89
|
+
self._http.post(
|
|
90
|
+
f"/api/v1/users/{self.user_id}/sessions/{self.session_id}/add-message",
|
|
91
|
+
data={"message": message, "role": MessageRole.ASSISTANT},
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def get_context(self) -> Context:
|
|
95
|
+
"""
|
|
96
|
+
Get the current context for this session.
|
|
97
|
+
|
|
98
|
+
The context contains information from the user's memory that is relevant
|
|
99
|
+
to the current conversation.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Context information with the memory text and whether memory was used
|
|
103
|
+
|
|
104
|
+
Raises:
|
|
105
|
+
NotFoundError: If the session or user is not found
|
|
106
|
+
"""
|
|
107
|
+
status = self.get_status()
|
|
108
|
+
if status == SessionStatus.PROCESSED:
|
|
109
|
+
logger.warning("Cannot add message to a session that has already been processed")
|
|
110
|
+
elif status == SessionStatus.PROCESSING:
|
|
111
|
+
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)
|
|
116
|
+
|
|
117
|
+
def process(self) -> None:
|
|
118
|
+
"""
|
|
119
|
+
Process the session to update the user's memory.
|
|
120
|
+
|
|
121
|
+
This method triggers the processing of the conversation to extract and update
|
|
122
|
+
the user's memory.
|
|
123
|
+
|
|
124
|
+
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
|
|
128
|
+
"""
|
|
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(
|
|
138
|
+
f"/api/v1/users/{self.user_id}/sessions/{self.session_id}/process"
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
def get_status(self) -> SessionStatus:
|
|
142
|
+
"""
|
|
143
|
+
Get the current status of the session.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
SessionStatus: The current status of the session
|
|
147
|
+
|
|
148
|
+
Raises:
|
|
149
|
+
NotFoundError: If the session or user is not found
|
|
150
|
+
"""
|
|
151
|
+
response = self._http.get(
|
|
152
|
+
f"/api/v1/users/{self.user_id}/sessions/{self.session_id}/status"
|
|
153
|
+
)
|
|
154
|
+
return SessionStatus(response["status"])
|
|
155
|
+
|
|
156
|
+
def get_messages(self) -> List[Message]:
|
|
157
|
+
"""
|
|
158
|
+
Get all messages in the session.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
List of messages in the session
|
|
162
|
+
|
|
163
|
+
Raises:
|
|
164
|
+
NotFoundError: If the session or user is not found
|
|
165
|
+
"""
|
|
166
|
+
response = self._http.get(
|
|
167
|
+
f"/api/v1/users/{self.user_id}/sessions/{self.session_id}/messages"
|
|
168
|
+
)
|
|
169
|
+
return [Message(**msg) for msg in response["messages"]]
|
recallrai/user.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
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
|
+
|
|
10
|
+
class User:
|
|
11
|
+
"""
|
|
12
|
+
Represents a user in the RecallrAI system with methods for user management.
|
|
13
|
+
|
|
14
|
+
This class wraps a user object and provides methods for updating user data,
|
|
15
|
+
and for creating and managing sessions.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
http_client: HTTPClient,
|
|
21
|
+
user_data: UserModel,
|
|
22
|
+
):
|
|
23
|
+
"""
|
|
24
|
+
Initialize a user.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
http_client: HTTP client for API communication
|
|
28
|
+
user_data: User data model with user information
|
|
29
|
+
"""
|
|
30
|
+
self._http = http_client
|
|
31
|
+
self._user_data = user_data
|
|
32
|
+
self.user_id = user_data.user_id
|
|
33
|
+
self.metadata = user_data.metadata
|
|
34
|
+
self.created_at = user_data.created_at
|
|
35
|
+
self.last_active_at = user_data.last_active_at
|
|
36
|
+
|
|
37
|
+
def update(self, new_metadata: Optional[Dict[str, Any]] = None, new_user_id: Optional[str] = None) -> 'User':
|
|
38
|
+
"""
|
|
39
|
+
Update this user's metadata or ID.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
new_metadata: New metadata to associate with the user
|
|
43
|
+
new_user_id: New ID for the user
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
The updated user object
|
|
47
|
+
|
|
48
|
+
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
|
|
52
|
+
"""
|
|
53
|
+
data = {}
|
|
54
|
+
if new_metadata is not None:
|
|
55
|
+
data["new_metadata"] = new_metadata
|
|
56
|
+
if new_user_id is not None:
|
|
57
|
+
data["new_user_id"] = new_user_id
|
|
58
|
+
|
|
59
|
+
response = self._http.put(f"/api/v1/users/{self.user_id}", data=data)
|
|
60
|
+
updated_data = UserModel.from_api_response(response)
|
|
61
|
+
|
|
62
|
+
# Update internal state
|
|
63
|
+
self._user_data = updated_data
|
|
64
|
+
self.user_id = updated_data.user_id
|
|
65
|
+
self.metadata = updated_data.metadata
|
|
66
|
+
self.last_active_at = updated_data.last_active_at
|
|
67
|
+
|
|
68
|
+
return self
|
|
69
|
+
|
|
70
|
+
def delete(self) -> None:
|
|
71
|
+
"""
|
|
72
|
+
Delete this user.
|
|
73
|
+
|
|
74
|
+
Raises:
|
|
75
|
+
NotFoundError: If the user is not found
|
|
76
|
+
"""
|
|
77
|
+
self._http.delete(f"/api/v1/users/{self.user_id}")
|
|
78
|
+
|
|
79
|
+
def create_session(self, auto_process_after_minutes: int = -1) -> Session:
|
|
80
|
+
"""
|
|
81
|
+
Create a new session for this user.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
auto_process_after_minutes: Minutes to wait before auto-processing (-1 to disable)
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
A Session object to interact with the created session
|
|
88
|
+
|
|
89
|
+
Raises:
|
|
90
|
+
ValidationError: If auto_process_after_minutes is invalid
|
|
91
|
+
"""
|
|
92
|
+
response = self._http.post(
|
|
93
|
+
f"/api/v1/users/{self.user_id}/sessions",
|
|
94
|
+
data={"auto_process_after_minutes": auto_process_after_minutes},
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
session_id = response["session_id"]
|
|
98
|
+
return Session(self._http, self.user_id, session_id)
|
|
99
|
+
|
|
100
|
+
def get_session(self, session_id: str) -> Session:
|
|
101
|
+
"""
|
|
102
|
+
Get an existing session for this user.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
session_id: ID of the session to retrieve
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
A Session object to interact with the session
|
|
109
|
+
|
|
110
|
+
Raises:
|
|
111
|
+
NotFoundError: If the session is not found
|
|
112
|
+
"""
|
|
113
|
+
return Session(self._http, self.user_id, session_id)
|
|
114
|
+
|
|
115
|
+
def list_sessions(self, offset: int = 0, limit: int = 10) -> SessionList:
|
|
116
|
+
"""
|
|
117
|
+
List sessions for this user with pagination.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
offset: Number of records to skip
|
|
121
|
+
limit: Maximum number of records to return
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
List of sessions with pagination info
|
|
125
|
+
"""
|
|
126
|
+
response = self._http.get(
|
|
127
|
+
f"/api/v1/users/{self.user_id}/sessions",
|
|
128
|
+
params={"offset": offset, "limit": limit},
|
|
129
|
+
)
|
|
130
|
+
return SessionList.from_api_response(response)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Path: recallrai/exceptions.py
|
|
2
|
+
# Description: Custom exceptions for the RecallrAI SDK
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Custom exceptions for the RecallrAI SDK.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RecallrAIError(Exception):
|
|
12
|
+
"""Base exception for all RecallrAI SDK errors."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, message: str, code: Optional[str] = None, details: Optional[Dict[str, Any]] = None):
|
|
15
|
+
"""
|
|
16
|
+
Initialize a RecallrAI error.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
message: A human-readable error message
|
|
20
|
+
code: An optional error code
|
|
21
|
+
details: Optional additional details about the error
|
|
22
|
+
"""
|
|
23
|
+
self.message = message
|
|
24
|
+
self.code = code
|
|
25
|
+
self.details = details or {}
|
|
26
|
+
super().__init__(self.message)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class AuthenticationError(RecallrAIError):
|
|
30
|
+
"""Raised when there is an authentication issue with the API key or project ID."""
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class NotFoundError(RecallrAIError):
|
|
35
|
+
"""Raised when a requested resource is not found."""
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ValidationError(RecallrAIError):
|
|
40
|
+
"""Raised when the API rejects a request due to validation errors."""
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class RateLimitError(RecallrAIError):
|
|
45
|
+
"""Raised when the API rate limit has been exceeded."""
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ServerError(RecallrAIError):
|
|
50
|
+
"""Raised when the API encounters an unexpected server error."""
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class BadRequestError(RecallrAIError):
|
|
55
|
+
"""Raised when the API rejects a request due to invalid parameters."""
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class SessionProcessingError(RecallrAIError):
|
|
60
|
+
"""Raised when there is an error processing a session."""
|
|
61
|
+
pass
|
recallrai/utils/http.py
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Path: recallrai/utils/http.py
|
|
2
|
+
# Description: HTTP client utilities for the RecallrAI SDK
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
HTTP client utilities for making requests to the RecallrAI API.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from typing import Any, Dict, Optional, Tuple, Union
|
|
10
|
+
|
|
11
|
+
import httpx
|
|
12
|
+
|
|
13
|
+
from .exceptions import (
|
|
14
|
+
AuthenticationError,
|
|
15
|
+
BadRequestError,
|
|
16
|
+
NotFoundError,
|
|
17
|
+
RateLimitError,
|
|
18
|
+
RecallrAIError,
|
|
19
|
+
ServerError,
|
|
20
|
+
ValidationError,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
ACCEPTED_STATUS_CODES = [
|
|
24
|
+
200, # OK
|
|
25
|
+
201, # Created
|
|
26
|
+
204, # No Content
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
class HTTPClient:
|
|
30
|
+
"""HTTP client for making requests to the RecallrAI API."""
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
api_key: str,
|
|
35
|
+
project_id: str,
|
|
36
|
+
base_url: str,
|
|
37
|
+
timeout: int = 30,
|
|
38
|
+
):
|
|
39
|
+
"""
|
|
40
|
+
Initialize the HTTP client.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
api_key: Your RecallrAI API key
|
|
44
|
+
project_id: Your project ID
|
|
45
|
+
base_url: The base URL for the RecallrAI API
|
|
46
|
+
timeout: Request timeout in seconds
|
|
47
|
+
"""
|
|
48
|
+
self.api_key = api_key
|
|
49
|
+
self.project_id = project_id
|
|
50
|
+
self.base_url = base_url.rstrip("/")
|
|
51
|
+
self.timeout = timeout
|
|
52
|
+
self.client = httpx.Client(
|
|
53
|
+
timeout=self.timeout,
|
|
54
|
+
headers={
|
|
55
|
+
"X-Api-Key": self.api_key,
|
|
56
|
+
"X-Project-Id": self.project_id,
|
|
57
|
+
"Content-Type": "application/json",
|
|
58
|
+
"Accept": "application/json",
|
|
59
|
+
"User-Agent": "RecallrAI-Python-SDK",
|
|
60
|
+
# TODO: "SDK-Version": "0.1.0",
|
|
61
|
+
},
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def _handle_response(self, response: httpx.Response) -> Dict[str, Any]:
|
|
65
|
+
"""
|
|
66
|
+
Handle the HTTP response and raise appropriate exceptions for errors.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
response: The HTTP response from the API
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
The parsed JSON response body
|
|
73
|
+
|
|
74
|
+
Raises:
|
|
75
|
+
AuthenticationError: When the API key or project ID is invalid
|
|
76
|
+
NotFoundError: When the requested resource is not found
|
|
77
|
+
ValidationError: When the API rejects the request due to validation errors
|
|
78
|
+
RateLimitError: When the API rate limit has been exceeded
|
|
79
|
+
ServerError: When the API encounters an unexpected server error
|
|
80
|
+
RecallrAIError: For other types of errors
|
|
81
|
+
"""
|
|
82
|
+
if response.status_code == 204:
|
|
83
|
+
return {}
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
data = response.json() if response.content else {}
|
|
87
|
+
except json.JSONDecodeError:
|
|
88
|
+
data = {}
|
|
89
|
+
|
|
90
|
+
error_detail = data.get("detail", "Unknown error")
|
|
91
|
+
|
|
92
|
+
if response.status_code in ACCEPTED_STATUS_CODES:
|
|
93
|
+
return data
|
|
94
|
+
elif response.status_code == 400:
|
|
95
|
+
raise BadRequestError(message=f"Bad request: {error_detail}", details=data)
|
|
96
|
+
elif response.status_code == 401:
|
|
97
|
+
raise AuthenticationError(message="Invalid API key or project ID", details=data)
|
|
98
|
+
elif response.status_code == 404:
|
|
99
|
+
raise NotFoundError(message=f"Resource not found: {error_detail}", details=data)
|
|
100
|
+
elif response.status_code == 422:
|
|
101
|
+
raise ValidationError(message=f"Validation error: {error_detail}", details=data)
|
|
102
|
+
elif response.status_code == 429:
|
|
103
|
+
raise RateLimitError(message="API rate limit exceeded", details=data)
|
|
104
|
+
elif response.status_code >= 500:
|
|
105
|
+
raise ServerError(message=f"Server error: {error_detail}", details=data)
|
|
106
|
+
else:
|
|
107
|
+
raise RecallrAIError(
|
|
108
|
+
message=f"Unexpected error: {response.status_code} - {error_detail}",
|
|
109
|
+
details=data,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
def request(
|
|
113
|
+
self,
|
|
114
|
+
method: str,
|
|
115
|
+
path: str,
|
|
116
|
+
params: Optional[Dict[str, Any]] = None,
|
|
117
|
+
data: Optional[Dict[str, Any]] = None,
|
|
118
|
+
) -> Dict[str, Any]:
|
|
119
|
+
"""
|
|
120
|
+
Make a request to the RecallrAI API.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
method: HTTP method (GET, POST, PUT, DELETE)
|
|
124
|
+
path: API endpoint path
|
|
125
|
+
params: Query parameters
|
|
126
|
+
data: Request body data
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
The parsed JSON response
|
|
130
|
+
"""
|
|
131
|
+
url = f"{self.base_url}{path}"
|
|
132
|
+
|
|
133
|
+
# Filter out None values in params and data
|
|
134
|
+
if params:
|
|
135
|
+
params = {k: v for k, v in params.items() if v is not None}
|
|
136
|
+
|
|
137
|
+
if data:
|
|
138
|
+
data = {k: v for k, v in data.items() if v is not None}
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
response = self.client.request(
|
|
142
|
+
method=method,
|
|
143
|
+
url=url,
|
|
144
|
+
params=params,
|
|
145
|
+
json=data,
|
|
146
|
+
)
|
|
147
|
+
return self._handle_response(response)
|
|
148
|
+
except httpx.HTTPError as e:
|
|
149
|
+
raise RecallrAIError(f"HTTP error: {str(e)}")
|
|
150
|
+
except Exception as e:
|
|
151
|
+
raise RecallrAIError(f"Unexpected error: {str(e)}")
|
|
152
|
+
|
|
153
|
+
def get(self, path: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
154
|
+
"""Make a GET request."""
|
|
155
|
+
return self.request("GET", path, params=params)
|
|
156
|
+
|
|
157
|
+
def post(self, path: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
158
|
+
"""Make a POST request."""
|
|
159
|
+
return self.request("POST", path, data=data)
|
|
160
|
+
|
|
161
|
+
def put(self, path: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
162
|
+
"""Make a PUT request."""
|
|
163
|
+
return self.request("PUT", path, data=data)
|
|
164
|
+
|
|
165
|
+
def delete(self, path: str) -> Dict[str, Any]:
|
|
166
|
+
"""Make a DELETE request."""
|
|
167
|
+
return self.request("DELETE", path)
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: recallrai
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for RecallrAI - Revolutionary contextual memory system that enables AI assistants to form meaningful connections between conversations, just like human memory.
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: ai,memory,context,llm,mem0,getzep,zep
|
|
7
|
+
Author: Devasheesh Mishra
|
|
8
|
+
Author-email: devasheeshmishra4@gmail.com
|
|
9
|
+
Requires-Python: >=3.8,<4.0
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Requires-Dist: httpx (>=0.25.0,<0.26.0)
|
|
21
|
+
Requires-Dist: pydantic (>=2.4.0,<3.0.0)
|
|
22
|
+
Project-URL: Homepage, https://recallrai.com
|
|
23
|
+
Project-URL: Repository, https://github.com/recallrai/sdk-python
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# RecallrAI Python SDK
|
|
27
|
+
|
|
28
|
+
Official Python SDK for RecallrAI – a revolutionary contextual memory system that enables AI assistants to form meaningful connections between conversations, just like human memory.
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
Install the SDK via Poetry or pip:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
poetry add recallrai
|
|
36
|
+
# or
|
|
37
|
+
pip install recallrai
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Initialization
|
|
41
|
+
|
|
42
|
+
Create a client instance with your API key and project ID:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from recallrai import RecallrAI
|
|
46
|
+
|
|
47
|
+
api_key = "rai_yourapikey"
|
|
48
|
+
project_id = "project-uuid"
|
|
49
|
+
client = RecallrAI(api_key=api_key, project_id=project_id)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## User Management
|
|
53
|
+
|
|
54
|
+
### Create a User
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from recallrai.user import User
|
|
58
|
+
|
|
59
|
+
user_id = "user123"
|
|
60
|
+
metadata = {"key": "value"}
|
|
61
|
+
user = client.create_user(user_id=user_id, metadata=metadata)
|
|
62
|
+
print("Created user:", user.user_id)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Get a User
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
user = client.get_user("user123")
|
|
69
|
+
print("User metadata:", user.metadata)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### List Users
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
user_list = client.list_users(offset=0, limit=10)
|
|
76
|
+
for user in user_list.users:
|
|
77
|
+
print(user.user_id, user.metadata)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Update a User
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
# Update the user's metadata and/or change the user ID
|
|
84
|
+
updated_user = client.update_user(user_id="user123", new_metadata={"role": "user"}, new_user_id="user1234")
|
|
85
|
+
print("Updated user id:", updated_user.user_id)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Delete a User
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
client.delete_user("user1234")
|
|
92
|
+
print("User deleted.")
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Session Management
|
|
96
|
+
|
|
97
|
+
### Create a Session
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from recallrai.session import Session
|
|
101
|
+
|
|
102
|
+
# Create a session for a user; auto_process_after_minutes set to -1 disables auto-processing.
|
|
103
|
+
session = client.create_session(user_id="user123", auto_process_after_minutes=5)
|
|
104
|
+
print("Created session id:", session.session_id)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Get an Existing Session
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
# Retrieve an existing session by its ID
|
|
111
|
+
session = client.get_session(user_id="user123", session_id="session-uuid")
|
|
112
|
+
print("Session status:", session.get_status())
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### List Sessions
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
session_list = client.list_sessions(user_id="user123", offset=0, limit=10)
|
|
119
|
+
for session in session_list.sessions:
|
|
120
|
+
print(session.session_id, session.status)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Session – Adding Messages
|
|
124
|
+
|
|
125
|
+
#### Add a User Message
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
session.add_user_message("Hello! How are you?")
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Session – Retrieving Context
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
context = session.get_context()
|
|
135
|
+
print("Memory used:", context.memory_used)
|
|
136
|
+
print("Context:", context.context)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### Add an Assistant Message
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
session.add_assistant_message("I'm an assistant. How can I help you?")
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Session – Process Session
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
session.process()
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Session – Get Status and Messages
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
status = session.get_status()
|
|
155
|
+
print("Session status:", status)
|
|
156
|
+
|
|
157
|
+
messages = session.get_messages()
|
|
158
|
+
for message in messages:
|
|
159
|
+
print(f"{message.role}: {message.content} at {message.timestamp}")
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Example Usage with LLMs
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
import openai
|
|
166
|
+
from recallrai import RecallrAI
|
|
167
|
+
|
|
168
|
+
# Initialize RecallrAI and OpenAI clients
|
|
169
|
+
recallrai_client = RecallrAI(api_key="rai_yourapikey", project_id="project-uuid")
|
|
170
|
+
openai_client = openai.OpenAI(api_key="your-openai-api-key")
|
|
171
|
+
|
|
172
|
+
def chat_with_memory(user_id, session_id=None):
|
|
173
|
+
# Get or create user
|
|
174
|
+
try:
|
|
175
|
+
user = recallrai_client.get_user(user_id)
|
|
176
|
+
except:
|
|
177
|
+
user = recallrai_client.create_user(user_id)
|
|
178
|
+
|
|
179
|
+
# Create a new session or get an existing one
|
|
180
|
+
if session_id:
|
|
181
|
+
session = recallrai_client.get_session(user_id=user_id, session_id=session_id)
|
|
182
|
+
else:
|
|
183
|
+
session = recallrai_client.create_session(user_id=user_id, auto_process_after_minutes=30)
|
|
184
|
+
print(f"Created new session: {session.session_id}")
|
|
185
|
+
|
|
186
|
+
print("Chat session started. Type 'exit' to end the conversation.")
|
|
187
|
+
|
|
188
|
+
while True:
|
|
189
|
+
# Get user input
|
|
190
|
+
user_message = input("You: ")
|
|
191
|
+
if user_message.lower() == 'exit':
|
|
192
|
+
break
|
|
193
|
+
|
|
194
|
+
# Add the user message to RecallrAI
|
|
195
|
+
session.add_user_message(user_message)
|
|
196
|
+
|
|
197
|
+
# Get context from RecallrAI after adding the user message
|
|
198
|
+
context = session.get_context()
|
|
199
|
+
|
|
200
|
+
# Create a system prompt that includes the context
|
|
201
|
+
system_prompt = f"""You are a helpful assistant with memory of previous conversations.
|
|
202
|
+
|
|
203
|
+
MEMORIES ABOUT THE USER:
|
|
204
|
+
{context.context}
|
|
205
|
+
|
|
206
|
+
You can use the above memories to provide better responses to the user.
|
|
207
|
+
Don't mention that you have access to memories unless you are explicitly asked."""
|
|
208
|
+
|
|
209
|
+
# Get previous messages
|
|
210
|
+
previous_messages = session.get_messages()
|
|
211
|
+
previous_messages = [{"role": message.role, "content": message.content} for message in previous_messages]
|
|
212
|
+
|
|
213
|
+
# Call the LLM with the system prompt and user message
|
|
214
|
+
response = openai_client.chat.completions.create(
|
|
215
|
+
model="gpt-4o-mini",
|
|
216
|
+
messages=[
|
|
217
|
+
{"role": "system", "content": system_prompt},
|
|
218
|
+
**previous_messages,
|
|
219
|
+
],
|
|
220
|
+
temperature=0.7
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
assistant_message = response.choices[0].message.content
|
|
224
|
+
|
|
225
|
+
# Print the assistant's response
|
|
226
|
+
print(f"Assistant: {assistant_message}")
|
|
227
|
+
|
|
228
|
+
# Add the assistant's response to RecallrAI
|
|
229
|
+
session.add_assistant_message(assistant_message)
|
|
230
|
+
|
|
231
|
+
# Process the session at the end of the conversation
|
|
232
|
+
print("Processing session to update memory...")
|
|
233
|
+
session.process()
|
|
234
|
+
print(f"Session ended. Session ID: {session.session_id}")
|
|
235
|
+
return session.session_id
|
|
236
|
+
|
|
237
|
+
# Example usage
|
|
238
|
+
if __name__ == "__main__":
|
|
239
|
+
user_id = "user123"
|
|
240
|
+
# To continue a previous session, uncomment below and provide the session ID
|
|
241
|
+
# previous_session_id = "previously-saved-session-uuid"
|
|
242
|
+
# session_id = chat_with_memory(user_id, previous_session_id)
|
|
243
|
+
|
|
244
|
+
# Start a new session
|
|
245
|
+
session_id = chat_with_memory(user_id)
|
|
246
|
+
print(f"To continue this conversation later, use session ID: {session_id}")
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Exception Handling
|
|
250
|
+
|
|
251
|
+
> Exception handling will be improved in future.
|
|
252
|
+
Each operation may raise custom exceptions defined in the SDK:
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
from recallrai.utils.exceptions import NotFoundError, ValidationError
|
|
256
|
+
|
|
257
|
+
try:
|
|
258
|
+
user = client.get_user("nonexistent_id")
|
|
259
|
+
except NotFoundError as e:
|
|
260
|
+
print("User not found:", e.message)
|
|
261
|
+
except ValidationError as e:
|
|
262
|
+
print("Invalid input:", e.message)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Conclusion
|
|
266
|
+
|
|
267
|
+
This README outlines the basic usage of the RecallrAI SDK functions for user and session management. For additional documentation and advanced usage, please see the [official documentation](https://recallrai.com) or the source code repository on [GitHub](https://github.com/recallrai/sdk-python).
|
|
268
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
recallrai/__init__.py,sha256=lw4pmQKtKkfTZ50xY3Z3J9IRxaG1XMrvEsN-jSazeHY,385
|
|
2
|
+
recallrai/client.py,sha256=lzD4DYL8kedr6P-9zJbf93CCt9PxmmHcSE2HzKBIU0w,6976
|
|
3
|
+
recallrai/models/__init__.py,sha256=2wwbyVAY4IgBn1a7YcbA4i1_pXJpYPYuQo80N8PVTlI,361
|
|
4
|
+
recallrai/models/session.py,sha256=Okn2tMmnl0UPL9q-Bl4cBaUcgs_U_nnvmKj3CHWavGY,3701
|
|
5
|
+
recallrai/models/user.py,sha256=yu7HzceZ-U2Hp6lbRjA31SQsyfD3dmuBoQ9dVGvfZ14,2190
|
|
6
|
+
recallrai/session.py,sha256=r045TL3a4_g0JjbCJ7VyRdhMX4JsUpVTDmMeeVwpGtY,5690
|
|
7
|
+
recallrai/user.py,sha256=VwQhKSJrwEEV7LgGDHOIRbIfwRi7OE5V-F146CGD8kY,4035
|
|
8
|
+
recallrai/utils/__init__.py,sha256=p_RBdfNvgBWQ62psiokCPCmZI5H7AQm8EJiRLxfHUjA,212
|
|
9
|
+
recallrai/utils/exceptions.py,sha256=b-8ONmtUgcjJxUg68pXwnana-5zt4C0cR2rTliFBGBE,1581
|
|
10
|
+
recallrai/utils/http.py,sha256=dGd4uUWtRDRBLZctNDNfdDr5njekMjCPBYSWcvKRpJM,5513
|
|
11
|
+
recallrai-0.1.0.dist-info/METADATA,sha256=WU87emxQgZ4t5p8k9ipp8MwbbwDLWDmzrWyYzY_-TJg,7801
|
|
12
|
+
recallrai-0.1.0.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
|
13
|
+
recallrai-0.1.0.dist-info/RECORD,,
|
recallrai/__main__.py
DELETED
|
File without changes
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: recallrai
|
|
3
|
-
Version: 0.0.1
|
|
4
|
-
Summary: Comming soon
|
|
5
|
-
License: MIT
|
|
6
|
-
Author: Devasheesh Mishra
|
|
7
|
-
Author-email: devasheeshmishra4@gmail.com
|
|
8
|
-
Requires-Python: >=3.10,<4.0
|
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
-
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
-
Description-Content-Type: text/markdown
|
|
16
|
-
|
|
17
|
-
Comming soon...
|
recallrai-0.0.1.dist-info/RECORD
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
recallrai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
recallrai/__main__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
recallrai-0.0.1.dist-info/METADATA,sha256=SBCR1N69hXrhhNh6qZ2T9qO8ZyXBd8chEXPUNCiCZio,545
|
|
4
|
-
recallrai-0.0.1.dist-info/WHEEL,sha256=RaoafKOydTQ7I_I3JTrPCg6kUmTgtm4BornzOqyEfJ8,88
|
|
5
|
-
recallrai-0.0.1.dist-info/RECORD,,
|