letta-nightly 0.10.0.dev20250806104523__py3-none-any.whl → 0.11.0.dev20250807104511__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.
- letta/__init__.py +1 -4
- letta/agent.py +1 -2
- letta/agents/base_agent.py +4 -7
- letta/agents/letta_agent.py +59 -51
- letta/agents/letta_agent_batch.py +1 -2
- letta/agents/voice_agent.py +1 -2
- letta/agents/voice_sleeptime_agent.py +1 -3
- letta/constants.py +4 -1
- letta/embeddings.py +1 -1
- letta/functions/function_sets/base.py +0 -1
- letta/functions/mcp_client/types.py +4 -0
- letta/groups/supervisor_multi_agent.py +1 -1
- letta/interfaces/anthropic_streaming_interface.py +16 -24
- letta/interfaces/openai_streaming_interface.py +16 -28
- letta/llm_api/llm_api_tools.py +3 -3
- letta/local_llm/vllm/api.py +3 -0
- letta/orm/__init__.py +3 -1
- letta/orm/agent.py +8 -0
- letta/orm/archive.py +86 -0
- letta/orm/archives_agents.py +27 -0
- letta/orm/job.py +5 -1
- letta/orm/mixins.py +8 -0
- letta/orm/organization.py +7 -8
- letta/orm/passage.py +12 -10
- letta/orm/sqlite_functions.py +2 -2
- letta/orm/tool.py +5 -4
- letta/schemas/agent.py +4 -2
- letta/schemas/agent_file.py +18 -1
- letta/schemas/archive.py +44 -0
- letta/schemas/embedding_config.py +2 -16
- letta/schemas/enums.py +2 -1
- letta/schemas/group.py +28 -3
- letta/schemas/job.py +4 -0
- letta/schemas/llm_config.py +29 -14
- letta/schemas/memory.py +9 -3
- letta/schemas/npm_requirement.py +12 -0
- letta/schemas/passage.py +3 -3
- letta/schemas/providers/letta.py +1 -1
- letta/schemas/providers/vllm.py +4 -4
- letta/schemas/sandbox_config.py +3 -1
- letta/schemas/tool.py +10 -38
- letta/schemas/tool_rule.py +2 -2
- letta/server/db.py +8 -2
- letta/server/rest_api/routers/v1/agents.py +9 -8
- letta/server/server.py +6 -40
- letta/server/startup.sh +3 -0
- letta/services/agent_manager.py +92 -31
- letta/services/agent_serialization_manager.py +62 -3
- letta/services/archive_manager.py +269 -0
- letta/services/helpers/agent_manager_helper.py +111 -37
- letta/services/job_manager.py +24 -0
- letta/services/passage_manager.py +98 -54
- letta/services/tool_executor/core_tool_executor.py +0 -1
- letta/services/tool_executor/sandbox_tool_executor.py +2 -2
- letta/services/tool_executor/tool_execution_manager.py +1 -1
- letta/services/tool_manager.py +70 -26
- letta/services/tool_sandbox/base.py +2 -2
- letta/services/tool_sandbox/local_sandbox.py +5 -1
- letta/templates/template_helper.py +8 -0
- {letta_nightly-0.10.0.dev20250806104523.dist-info → letta_nightly-0.11.0.dev20250807104511.dist-info}/METADATA +5 -6
- {letta_nightly-0.10.0.dev20250806104523.dist-info → letta_nightly-0.11.0.dev20250807104511.dist-info}/RECORD +64 -61
- letta/client/client.py +0 -2207
- letta/orm/enums.py +0 -21
- {letta_nightly-0.10.0.dev20250806104523.dist-info → letta_nightly-0.11.0.dev20250807104511.dist-info}/LICENSE +0 -0
- {letta_nightly-0.10.0.dev20250806104523.dist-info → letta_nightly-0.11.0.dev20250807104511.dist-info}/WHEEL +0 -0
- {letta_nightly-0.10.0.dev20250806104523.dist-info → letta_nightly-0.11.0.dev20250807104511.dist-info}/entry_points.txt +0 -0
letta/client/client.py
DELETED
@@ -1,2207 +0,0 @@
|
|
1
|
-
import time
|
2
|
-
from typing import Callable, Dict, List, Optional, Union
|
3
|
-
|
4
|
-
import requests
|
5
|
-
|
6
|
-
from letta.constants import ADMIN_PREFIX, BASE_MEMORY_TOOLS, BASE_TOOLS, DEFAULT_HUMAN, DEFAULT_PERSONA, FUNCTION_RETURN_CHAR_LIMIT
|
7
|
-
from letta.data_sources.connectors import DataConnector
|
8
|
-
from letta.functions.functions import parse_source_code
|
9
|
-
from letta.schemas.agent import AgentState, AgentType, CreateAgent, UpdateAgent
|
10
|
-
from letta.schemas.block import Block, BlockUpdate, CreateBlock, Human, Persona
|
11
|
-
from letta.schemas.embedding_config import EmbeddingConfig
|
12
|
-
|
13
|
-
# new schemas
|
14
|
-
from letta.schemas.enums import JobStatus, MessageRole
|
15
|
-
from letta.schemas.environment_variables import SandboxEnvironmentVariable
|
16
|
-
from letta.schemas.file import FileMetadata
|
17
|
-
from letta.schemas.job import Job
|
18
|
-
from letta.schemas.letta_message import LettaMessage, LettaMessageUnion
|
19
|
-
from letta.schemas.letta_request import LettaRequest, LettaStreamingRequest
|
20
|
-
from letta.schemas.letta_response import LettaResponse
|
21
|
-
from letta.schemas.llm_config import LLMConfig
|
22
|
-
from letta.schemas.memory import ArchivalMemorySummary, ChatMemory, CreateArchivalMemory, Memory, RecallMemorySummary
|
23
|
-
from letta.schemas.message import Message, MessageCreate
|
24
|
-
from letta.schemas.openai.chat_completion_response import UsageStatistics
|
25
|
-
from letta.schemas.organization import Organization
|
26
|
-
from letta.schemas.passage import Passage
|
27
|
-
from letta.schemas.response_format import ResponseFormatUnion
|
28
|
-
from letta.schemas.run import Run
|
29
|
-
from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfig
|
30
|
-
from letta.schemas.source import Source, SourceCreate, SourceUpdate
|
31
|
-
from letta.schemas.tool import Tool, ToolCreate, ToolUpdate
|
32
|
-
from letta.schemas.tool_rule import BaseToolRule
|
33
|
-
from letta.utils import get_human_text, get_persona_text
|
34
|
-
|
35
|
-
|
36
|
-
class AbstractClient(object):
|
37
|
-
def __init__(
|
38
|
-
self,
|
39
|
-
debug: bool = False,
|
40
|
-
):
|
41
|
-
self.debug = debug
|
42
|
-
|
43
|
-
def agent_exists(self, agent_id: Optional[str] = None, agent_name: Optional[str] = None) -> bool:
|
44
|
-
raise NotImplementedError
|
45
|
-
|
46
|
-
def create_agent(
|
47
|
-
self,
|
48
|
-
name: Optional[str] = None,
|
49
|
-
agent_type: Optional[AgentType] = AgentType.memgpt_agent,
|
50
|
-
embedding_config: Optional[EmbeddingConfig] = None,
|
51
|
-
llm_config: Optional[LLMConfig] = None,
|
52
|
-
memory=None,
|
53
|
-
block_ids: Optional[List[str]] = None,
|
54
|
-
system: Optional[str] = None,
|
55
|
-
tool_ids: Optional[List[str]] = None,
|
56
|
-
tool_rules: Optional[List[BaseToolRule]] = None,
|
57
|
-
include_base_tools: Optional[bool] = True,
|
58
|
-
metadata: Optional[Dict] = {"human:": DEFAULT_HUMAN, "persona": DEFAULT_PERSONA},
|
59
|
-
description: Optional[str] = None,
|
60
|
-
tags: Optional[List[str]] = None,
|
61
|
-
message_buffer_autoclear: bool = False,
|
62
|
-
response_format: Optional[ResponseFormatUnion] = None,
|
63
|
-
) -> AgentState:
|
64
|
-
raise NotImplementedError
|
65
|
-
|
66
|
-
def update_agent(
|
67
|
-
self,
|
68
|
-
agent_id: str,
|
69
|
-
name: Optional[str] = None,
|
70
|
-
description: Optional[str] = None,
|
71
|
-
system: Optional[str] = None,
|
72
|
-
tool_ids: Optional[List[str]] = None,
|
73
|
-
metadata: Optional[Dict] = None,
|
74
|
-
llm_config: Optional[LLMConfig] = None,
|
75
|
-
embedding_config: Optional[EmbeddingConfig] = None,
|
76
|
-
message_ids: Optional[List[str]] = None,
|
77
|
-
memory: Optional[Memory] = None,
|
78
|
-
tags: Optional[List[str]] = None,
|
79
|
-
response_format: Optional[ResponseFormatUnion] = None,
|
80
|
-
):
|
81
|
-
raise NotImplementedError
|
82
|
-
|
83
|
-
def get_tools_from_agent(self, agent_id: str) -> List[Tool]:
|
84
|
-
raise NotImplementedError
|
85
|
-
|
86
|
-
def attach_tool(self, agent_id: str, tool_id: str) -> AgentState:
|
87
|
-
raise NotImplementedError
|
88
|
-
|
89
|
-
def detach_tool(self, agent_id: str, tool_id: str) -> AgentState:
|
90
|
-
raise NotImplementedError
|
91
|
-
|
92
|
-
def rename_agent(self, agent_id: str, new_name: str) -> AgentState:
|
93
|
-
raise NotImplementedError
|
94
|
-
|
95
|
-
def delete_agent(self, agent_id: str) -> None:
|
96
|
-
raise NotImplementedError
|
97
|
-
|
98
|
-
def get_agent(self, agent_id: str) -> AgentState:
|
99
|
-
raise NotImplementedError
|
100
|
-
|
101
|
-
def get_agent_id(self, agent_name: str) -> AgentState:
|
102
|
-
raise NotImplementedError
|
103
|
-
|
104
|
-
def get_in_context_memory(self, agent_id: str) -> Memory:
|
105
|
-
raise NotImplementedError
|
106
|
-
|
107
|
-
def update_in_context_memory(self, agent_id: str, section: str, value: Union[List[str], str]) -> Memory:
|
108
|
-
raise NotImplementedError
|
109
|
-
|
110
|
-
def get_archival_memory_summary(self, agent_id: str) -> ArchivalMemorySummary:
|
111
|
-
raise NotImplementedError
|
112
|
-
|
113
|
-
def get_recall_memory_summary(self, agent_id: str) -> RecallMemorySummary:
|
114
|
-
raise NotImplementedError
|
115
|
-
|
116
|
-
def get_in_context_messages(self, agent_id: str) -> List[Message]:
|
117
|
-
raise NotImplementedError
|
118
|
-
|
119
|
-
def send_message(
|
120
|
-
self,
|
121
|
-
message: str,
|
122
|
-
role: str,
|
123
|
-
agent_id: Optional[str] = None,
|
124
|
-
name: Optional[str] = None,
|
125
|
-
stream: Optional[bool] = False,
|
126
|
-
stream_steps: bool = False,
|
127
|
-
stream_tokens: bool = False,
|
128
|
-
max_steps: Optional[int] = None,
|
129
|
-
) -> LettaResponse:
|
130
|
-
raise NotImplementedError
|
131
|
-
|
132
|
-
def user_message(self, agent_id: str, message: str) -> LettaResponse:
|
133
|
-
raise NotImplementedError
|
134
|
-
|
135
|
-
def create_human(self, name: str, text: str) -> Human:
|
136
|
-
raise NotImplementedError
|
137
|
-
|
138
|
-
def create_persona(self, name: str, text: str) -> Persona:
|
139
|
-
raise NotImplementedError
|
140
|
-
|
141
|
-
def list_humans(self) -> List[Human]:
|
142
|
-
raise NotImplementedError
|
143
|
-
|
144
|
-
def list_personas(self) -> List[Persona]:
|
145
|
-
raise NotImplementedError
|
146
|
-
|
147
|
-
def update_human(self, human_id: str, text: str) -> Human:
|
148
|
-
raise NotImplementedError
|
149
|
-
|
150
|
-
def update_persona(self, persona_id: str, text: str) -> Persona:
|
151
|
-
raise NotImplementedError
|
152
|
-
|
153
|
-
def get_persona(self, id: str) -> Persona:
|
154
|
-
raise NotImplementedError
|
155
|
-
|
156
|
-
def get_human(self, id: str) -> Human:
|
157
|
-
raise NotImplementedError
|
158
|
-
|
159
|
-
def get_persona_id(self, name: str) -> str:
|
160
|
-
raise NotImplementedError
|
161
|
-
|
162
|
-
def get_human_id(self, name: str) -> str:
|
163
|
-
raise NotImplementedError
|
164
|
-
|
165
|
-
def delete_persona(self, id: str):
|
166
|
-
raise NotImplementedError
|
167
|
-
|
168
|
-
def delete_human(self, id: str):
|
169
|
-
raise NotImplementedError
|
170
|
-
|
171
|
-
def load_langchain_tool(self, langchain_tool: "LangChainBaseTool", additional_imports_module_attr_map: dict[str, str] = None) -> Tool:
|
172
|
-
raise NotImplementedError
|
173
|
-
|
174
|
-
def load_composio_tool(self, action: "ActionType") -> Tool:
|
175
|
-
raise NotImplementedError
|
176
|
-
|
177
|
-
def create_tool(self, func, tags: Optional[List[str]] = None, return_char_limit: int = FUNCTION_RETURN_CHAR_LIMIT) -> Tool:
|
178
|
-
raise NotImplementedError
|
179
|
-
|
180
|
-
def create_or_update_tool(self, func, tags: Optional[List[str]] = None, return_char_limit: int = FUNCTION_RETURN_CHAR_LIMIT) -> Tool:
|
181
|
-
raise NotImplementedError
|
182
|
-
|
183
|
-
def update_tool(
|
184
|
-
self,
|
185
|
-
id: str,
|
186
|
-
description: Optional[str] = None,
|
187
|
-
func: Optional[Callable] = None,
|
188
|
-
tags: Optional[List[str]] = None,
|
189
|
-
return_char_limit: int = FUNCTION_RETURN_CHAR_LIMIT,
|
190
|
-
) -> Tool:
|
191
|
-
raise NotImplementedError
|
192
|
-
|
193
|
-
def list_tools(self, after: Optional[str] = None, limit: Optional[int] = 50) -> List[Tool]:
|
194
|
-
raise NotImplementedError
|
195
|
-
|
196
|
-
def get_tool(self, id: str) -> Tool:
|
197
|
-
raise NotImplementedError
|
198
|
-
|
199
|
-
def delete_tool(self, id: str):
|
200
|
-
raise NotImplementedError
|
201
|
-
|
202
|
-
def get_tool_id(self, name: str) -> Optional[str]:
|
203
|
-
raise NotImplementedError
|
204
|
-
|
205
|
-
def list_attached_tools(self, agent_id: str) -> List[Tool]:
|
206
|
-
"""
|
207
|
-
List all tools attached to an agent.
|
208
|
-
|
209
|
-
Args:
|
210
|
-
agent_id (str): ID of the agent
|
211
|
-
|
212
|
-
Returns:
|
213
|
-
List[Tool]: A list of attached tools
|
214
|
-
"""
|
215
|
-
raise NotImplementedError
|
216
|
-
|
217
|
-
def upsert_base_tools(self) -> List[Tool]:
|
218
|
-
raise NotImplementedError
|
219
|
-
|
220
|
-
def load_data(self, connector: DataConnector, source_name: str):
|
221
|
-
raise NotImplementedError
|
222
|
-
|
223
|
-
def load_file_to_source(self, filename: str, source_id: str, blocking=True) -> Job:
|
224
|
-
raise NotImplementedError
|
225
|
-
|
226
|
-
def delete_file_from_source(self, source_id: str, file_id: str) -> None:
|
227
|
-
raise NotImplementedError
|
228
|
-
|
229
|
-
def create_source(self, name: str, embedding_config: Optional[EmbeddingConfig] = None) -> Source:
|
230
|
-
raise NotImplementedError
|
231
|
-
|
232
|
-
def delete_source(self, source_id: str):
|
233
|
-
raise NotImplementedError
|
234
|
-
|
235
|
-
def get_source(self, source_id: str) -> Source:
|
236
|
-
raise NotImplementedError
|
237
|
-
|
238
|
-
def get_source_id(self, source_name: str) -> str:
|
239
|
-
raise NotImplementedError
|
240
|
-
|
241
|
-
def attach_source(self, agent_id: str, source_id: Optional[str] = None, source_name: Optional[str] = None) -> AgentState:
|
242
|
-
raise NotImplementedError
|
243
|
-
|
244
|
-
def detach_source(self, agent_id: str, source_id: Optional[str] = None, source_name: Optional[str] = None) -> AgentState:
|
245
|
-
raise NotImplementedError
|
246
|
-
|
247
|
-
def list_sources(self) -> List[Source]:
|
248
|
-
raise NotImplementedError
|
249
|
-
|
250
|
-
def list_attached_sources(self, agent_id: str) -> List[Source]:
|
251
|
-
raise NotImplementedError
|
252
|
-
|
253
|
-
def list_files_from_source(self, source_id: str, limit: int = 1000, after: Optional[str] = None) -> List[FileMetadata]:
|
254
|
-
raise NotImplementedError
|
255
|
-
|
256
|
-
def update_source(self, source_id: str, name: Optional[str] = None) -> Source:
|
257
|
-
raise NotImplementedError
|
258
|
-
|
259
|
-
def insert_archival_memory(self, agent_id: str, memory: str) -> List[Passage]:
|
260
|
-
raise NotImplementedError
|
261
|
-
|
262
|
-
def delete_archival_memory(self, agent_id: str, memory_id: str):
|
263
|
-
raise NotImplementedError
|
264
|
-
|
265
|
-
def get_archival_memory(
|
266
|
-
self, agent_id: str, after: Optional[str] = None, before: Optional[str] = None, limit: Optional[int] = 1000
|
267
|
-
) -> List[Passage]:
|
268
|
-
raise NotImplementedError
|
269
|
-
|
270
|
-
def get_messages(
|
271
|
-
self, agent_id: str, after: Optional[str] = None, before: Optional[str] = None, limit: Optional[int] = 1000
|
272
|
-
) -> List[LettaMessage]:
|
273
|
-
raise NotImplementedError
|
274
|
-
|
275
|
-
def list_model_configs(self) -> List[LLMConfig]:
|
276
|
-
raise NotImplementedError
|
277
|
-
|
278
|
-
def list_embedding_configs(self) -> List[EmbeddingConfig]:
|
279
|
-
raise NotImplementedError
|
280
|
-
|
281
|
-
def create_org(self, name: Optional[str] = None) -> Organization:
|
282
|
-
raise NotImplementedError
|
283
|
-
|
284
|
-
def list_orgs(self, after: Optional[str] = None, limit: Optional[int] = 50) -> List[Organization]:
|
285
|
-
raise NotImplementedError
|
286
|
-
|
287
|
-
def delete_org(self, org_id: str) -> Organization:
|
288
|
-
raise NotImplementedError
|
289
|
-
|
290
|
-
def create_sandbox_config(self, config: Union[LocalSandboxConfig, E2BSandboxConfig]) -> SandboxConfig:
|
291
|
-
"""
|
292
|
-
Create a new sandbox configuration.
|
293
|
-
|
294
|
-
Args:
|
295
|
-
config (Union[LocalSandboxConfig, E2BSandboxConfig]): The sandbox settings.
|
296
|
-
|
297
|
-
Returns:
|
298
|
-
SandboxConfig: The created sandbox configuration.
|
299
|
-
"""
|
300
|
-
raise NotImplementedError
|
301
|
-
|
302
|
-
def update_sandbox_config(self, sandbox_config_id: str, config: Union[LocalSandboxConfig, E2BSandboxConfig]) -> SandboxConfig:
|
303
|
-
"""
|
304
|
-
Update an existing sandbox configuration.
|
305
|
-
|
306
|
-
Args:
|
307
|
-
sandbox_config_id (str): The ID of the sandbox configuration to update.
|
308
|
-
config (Union[LocalSandboxConfig, E2BSandboxConfig]): The updated sandbox settings.
|
309
|
-
|
310
|
-
Returns:
|
311
|
-
SandboxConfig: The updated sandbox configuration.
|
312
|
-
"""
|
313
|
-
raise NotImplementedError
|
314
|
-
|
315
|
-
def delete_sandbox_config(self, sandbox_config_id: str) -> None:
|
316
|
-
"""
|
317
|
-
Delete a sandbox configuration.
|
318
|
-
|
319
|
-
Args:
|
320
|
-
sandbox_config_id (str): The ID of the sandbox configuration to delete.
|
321
|
-
"""
|
322
|
-
raise NotImplementedError
|
323
|
-
|
324
|
-
def list_sandbox_configs(self, limit: int = 50, after: Optional[str] = None) -> List[SandboxConfig]:
|
325
|
-
"""
|
326
|
-
List all sandbox configurations.
|
327
|
-
|
328
|
-
Args:
|
329
|
-
limit (int, optional): The maximum number of sandbox configurations to return. Defaults to 50.
|
330
|
-
after (Optional[str], optional): The pagination cursor for retrieving the next set of results.
|
331
|
-
|
332
|
-
Returns:
|
333
|
-
List[SandboxConfig]: A list of sandbox configurations.
|
334
|
-
"""
|
335
|
-
raise NotImplementedError
|
336
|
-
|
337
|
-
def create_sandbox_env_var(
|
338
|
-
self, sandbox_config_id: str, key: str, value: str, description: Optional[str] = None
|
339
|
-
) -> SandboxEnvironmentVariable:
|
340
|
-
"""
|
341
|
-
Create a new environment variable for a sandbox configuration.
|
342
|
-
|
343
|
-
Args:
|
344
|
-
sandbox_config_id (str): The ID of the sandbox configuration to associate the environment variable with.
|
345
|
-
key (str): The name of the environment variable.
|
346
|
-
value (str): The value of the environment variable.
|
347
|
-
description (Optional[str], optional): A description of the environment variable. Defaults to None.
|
348
|
-
|
349
|
-
Returns:
|
350
|
-
SandboxEnvironmentVariable: The created environment variable.
|
351
|
-
"""
|
352
|
-
raise NotImplementedError
|
353
|
-
|
354
|
-
def update_sandbox_env_var(
|
355
|
-
self, env_var_id: str, key: Optional[str] = None, value: Optional[str] = None, description: Optional[str] = None
|
356
|
-
) -> SandboxEnvironmentVariable:
|
357
|
-
"""
|
358
|
-
Update an existing environment variable.
|
359
|
-
|
360
|
-
Args:
|
361
|
-
env_var_id (str): The ID of the environment variable to update.
|
362
|
-
key (Optional[str], optional): The updated name of the environment variable. Defaults to None.
|
363
|
-
value (Optional[str], optional): The updated value of the environment variable. Defaults to None.
|
364
|
-
description (Optional[str], optional): The updated description of the environment variable. Defaults to None.
|
365
|
-
|
366
|
-
Returns:
|
367
|
-
SandboxEnvironmentVariable: The updated environment variable.
|
368
|
-
"""
|
369
|
-
raise NotImplementedError
|
370
|
-
|
371
|
-
def delete_sandbox_env_var(self, env_var_id: str) -> None:
|
372
|
-
"""
|
373
|
-
Delete an environment variable by its ID.
|
374
|
-
|
375
|
-
Args:
|
376
|
-
env_var_id (str): The ID of the environment variable to delete.
|
377
|
-
"""
|
378
|
-
raise NotImplementedError
|
379
|
-
|
380
|
-
def list_sandbox_env_vars(
|
381
|
-
self, sandbox_config_id: str, limit: int = 50, after: Optional[str] = None
|
382
|
-
) -> List[SandboxEnvironmentVariable]:
|
383
|
-
"""
|
384
|
-
List all environment variables associated with a sandbox configuration.
|
385
|
-
|
386
|
-
Args:
|
387
|
-
sandbox_config_id (str): The ID of the sandbox configuration to retrieve environment variables for.
|
388
|
-
limit (int, optional): The maximum number of environment variables to return. Defaults to 50.
|
389
|
-
after (Optional[str], optional): The pagination cursor for retrieving the next set of results.
|
390
|
-
|
391
|
-
Returns:
|
392
|
-
List[SandboxEnvironmentVariable]: A list of environment variables.
|
393
|
-
"""
|
394
|
-
raise NotImplementedError
|
395
|
-
|
396
|
-
def attach_block(self, agent_id: str, block_id: str) -> AgentState:
|
397
|
-
"""
|
398
|
-
Attach a block to an agent.
|
399
|
-
|
400
|
-
Args:
|
401
|
-
agent_id (str): ID of the agent
|
402
|
-
block_id (str): ID of the block to attach
|
403
|
-
"""
|
404
|
-
raise NotImplementedError
|
405
|
-
|
406
|
-
def detach_block(self, agent_id: str, block_id: str) -> AgentState:
|
407
|
-
"""
|
408
|
-
Detach a block from an agent.
|
409
|
-
|
410
|
-
Args:
|
411
|
-
agent_id (str): ID of the agent
|
412
|
-
block_id (str): ID of the block to detach
|
413
|
-
"""
|
414
|
-
raise NotImplementedError
|
415
|
-
|
416
|
-
|
417
|
-
class RESTClient(AbstractClient):
|
418
|
-
"""
|
419
|
-
REST client for Letta
|
420
|
-
|
421
|
-
Attributes:
|
422
|
-
base_url (str): Base URL of the REST API
|
423
|
-
headers (Dict): Headers for the REST API (includes token)
|
424
|
-
"""
|
425
|
-
|
426
|
-
def __init__(
|
427
|
-
self,
|
428
|
-
base_url: str,
|
429
|
-
token: Optional[str] = None,
|
430
|
-
password: Optional[str] = None,
|
431
|
-
api_prefix: str = "v1",
|
432
|
-
debug: bool = False,
|
433
|
-
default_llm_config: Optional[LLMConfig] = None,
|
434
|
-
default_embedding_config: Optional[EmbeddingConfig] = None,
|
435
|
-
headers: Optional[Dict] = None,
|
436
|
-
):
|
437
|
-
"""
|
438
|
-
Initializes a new instance of Client class.
|
439
|
-
|
440
|
-
Args:
|
441
|
-
user_id (str): The user ID.
|
442
|
-
debug (bool): Whether to print debug information.
|
443
|
-
default_llm_config (Optional[LLMConfig]): The default LLM configuration.
|
444
|
-
default_embedding_config (Optional[EmbeddingConfig]): The default embedding configuration.
|
445
|
-
headers (Optional[Dict]): The additional headers for the REST API.
|
446
|
-
token (Optional[str]): The token for the REST API when using managed letta service.
|
447
|
-
password (Optional[str]): The password for the REST API when using self hosted letta service.
|
448
|
-
"""
|
449
|
-
super().__init__(debug=debug)
|
450
|
-
self.base_url = base_url
|
451
|
-
self.api_prefix = api_prefix
|
452
|
-
if token:
|
453
|
-
self.headers = {"accept": "application/json", "Authorization": f"Bearer {token}"}
|
454
|
-
elif password:
|
455
|
-
self.headers = {"accept": "application/json", "Authorization": f"Bearer {password}"}
|
456
|
-
else:
|
457
|
-
self.headers = {"accept": "application/json"}
|
458
|
-
if headers:
|
459
|
-
self.headers.update(headers)
|
460
|
-
self._default_llm_config = default_llm_config
|
461
|
-
self._default_embedding_config = default_embedding_config
|
462
|
-
|
463
|
-
def list_agents(
|
464
|
-
self,
|
465
|
-
tags: Optional[List[str]] = None,
|
466
|
-
query_text: Optional[str] = None,
|
467
|
-
limit: int = 50,
|
468
|
-
before: Optional[str] = None,
|
469
|
-
after: Optional[str] = None,
|
470
|
-
) -> List[AgentState]:
|
471
|
-
params = {"limit": limit}
|
472
|
-
if tags:
|
473
|
-
params["tags"] = tags
|
474
|
-
params["match_all_tags"] = False
|
475
|
-
|
476
|
-
if query_text:
|
477
|
-
params["query_text"] = query_text
|
478
|
-
|
479
|
-
if before:
|
480
|
-
params["before"] = before
|
481
|
-
|
482
|
-
if after:
|
483
|
-
params["after"] = after
|
484
|
-
|
485
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/agents", headers=self.headers, params=params)
|
486
|
-
return [AgentState(**agent) for agent in response.json()]
|
487
|
-
|
488
|
-
def agent_exists(self, agent_id: str) -> bool:
|
489
|
-
"""
|
490
|
-
Check if an agent exists
|
491
|
-
|
492
|
-
Args:
|
493
|
-
agent_id (str): ID of the agent
|
494
|
-
agent_name (str): Name of the agent
|
495
|
-
|
496
|
-
Returns:
|
497
|
-
exists (bool): `True` if the agent exists, `False` otherwise
|
498
|
-
"""
|
499
|
-
|
500
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}", headers=self.headers)
|
501
|
-
if response.status_code == 404:
|
502
|
-
# not found error
|
503
|
-
return False
|
504
|
-
elif response.status_code == 200:
|
505
|
-
return True
|
506
|
-
else:
|
507
|
-
raise ValueError(f"Failed to check if agent exists: {response.text}")
|
508
|
-
|
509
|
-
def create_agent(
|
510
|
-
self,
|
511
|
-
name: Optional[str] = None,
|
512
|
-
# agent config
|
513
|
-
agent_type: Optional[AgentType] = AgentType.memgpt_agent,
|
514
|
-
# model configs
|
515
|
-
embedding_config: EmbeddingConfig = None,
|
516
|
-
llm_config: LLMConfig = None,
|
517
|
-
# memory
|
518
|
-
memory: Memory = ChatMemory(human=get_human_text(DEFAULT_HUMAN), persona=get_persona_text(DEFAULT_PERSONA)),
|
519
|
-
# Existing blocks
|
520
|
-
block_ids: Optional[List[str]] = None,
|
521
|
-
# system
|
522
|
-
system: Optional[str] = None,
|
523
|
-
# tools
|
524
|
-
tool_ids: Optional[List[str]] = None,
|
525
|
-
tool_rules: Optional[List[BaseToolRule]] = None,
|
526
|
-
include_base_tools: Optional[bool] = True,
|
527
|
-
include_multi_agent_tools: Optional[bool] = False,
|
528
|
-
# metadata
|
529
|
-
metadata: Optional[Dict] = {"human:": DEFAULT_HUMAN, "persona": DEFAULT_PERSONA},
|
530
|
-
description: Optional[str] = None,
|
531
|
-
initial_message_sequence: Optional[List[Message]] = None,
|
532
|
-
tags: Optional[List[str]] = None,
|
533
|
-
message_buffer_autoclear: bool = False,
|
534
|
-
response_format: Optional[ResponseFormatUnion] = None,
|
535
|
-
) -> AgentState:
|
536
|
-
"""Create an agent
|
537
|
-
|
538
|
-
Args:
|
539
|
-
name (str): Name of the agent
|
540
|
-
embedding_config (EmbeddingConfig): Embedding configuration
|
541
|
-
llm_config (LLMConfig): LLM configuration
|
542
|
-
memory (Memory): Memory configuration
|
543
|
-
system (str): System configuration
|
544
|
-
tool_ids (List[str]): List of tool ids
|
545
|
-
include_base_tools (bool): Include base tools
|
546
|
-
metadata (Dict): Metadata
|
547
|
-
description (str): Description
|
548
|
-
tags (List[str]): Tags for filtering agents
|
549
|
-
|
550
|
-
Returns:
|
551
|
-
agent_state (AgentState): State of the created agent
|
552
|
-
"""
|
553
|
-
tool_ids = tool_ids or []
|
554
|
-
tool_names = []
|
555
|
-
if include_base_tools:
|
556
|
-
tool_names += BASE_TOOLS
|
557
|
-
tool_names += BASE_MEMORY_TOOLS
|
558
|
-
tool_ids += [self.get_tool_id(tool_name=name) for name in tool_names]
|
559
|
-
|
560
|
-
assert embedding_config or self._default_embedding_config, "Embedding config must be provided"
|
561
|
-
assert llm_config or self._default_llm_config, "LLM config must be provided"
|
562
|
-
|
563
|
-
# TODO: This should not happen here, we need to have clear separation between create/add blocks
|
564
|
-
# TODO: This is insanely hacky and a result of allowing free-floating blocks
|
565
|
-
# TODO: When we create the block, it gets it's own block ID
|
566
|
-
blocks = []
|
567
|
-
for block in memory.get_blocks():
|
568
|
-
blocks.append(
|
569
|
-
self.create_block(
|
570
|
-
label=block.label,
|
571
|
-
value=block.value,
|
572
|
-
limit=block.limit,
|
573
|
-
template_name=block.template_name,
|
574
|
-
is_template=block.is_template,
|
575
|
-
)
|
576
|
-
)
|
577
|
-
memory.blocks = blocks
|
578
|
-
block_ids = block_ids or []
|
579
|
-
|
580
|
-
# create agent
|
581
|
-
create_params = {
|
582
|
-
"description": description,
|
583
|
-
"metadata": metadata,
|
584
|
-
"memory_blocks": [],
|
585
|
-
"block_ids": [b.id for b in memory.get_blocks()] + block_ids,
|
586
|
-
"tool_ids": tool_ids,
|
587
|
-
"tool_rules": tool_rules,
|
588
|
-
"system": system,
|
589
|
-
"agent_type": agent_type,
|
590
|
-
"llm_config": llm_config if llm_config else self._default_llm_config,
|
591
|
-
"embedding_config": embedding_config if embedding_config else self._default_embedding_config,
|
592
|
-
"initial_message_sequence": initial_message_sequence,
|
593
|
-
"tags": tags,
|
594
|
-
"include_base_tools": include_base_tools,
|
595
|
-
"message_buffer_autoclear": message_buffer_autoclear,
|
596
|
-
"include_multi_agent_tools": include_multi_agent_tools,
|
597
|
-
"response_format": response_format,
|
598
|
-
}
|
599
|
-
|
600
|
-
# Only add name if it's not None
|
601
|
-
if name is not None:
|
602
|
-
create_params["name"] = name
|
603
|
-
|
604
|
-
request = CreateAgent(**create_params)
|
605
|
-
|
606
|
-
# Use model_dump_json() instead of model_dump()
|
607
|
-
# If we use model_dump(), the datetime objects will not be serialized correctly
|
608
|
-
# response = requests.post(f"{self.base_url}/{self.api_prefix}/agents", json=request.model_dump(), headers=self.headers)
|
609
|
-
response = requests.post(
|
610
|
-
f"{self.base_url}/{self.api_prefix}/agents",
|
611
|
-
data=request.model_dump_json(), # Use model_dump_json() instead of json=model_dump()
|
612
|
-
headers={"Content-Type": "application/json", **self.headers},
|
613
|
-
)
|
614
|
-
|
615
|
-
if response.status_code != 200:
|
616
|
-
raise ValueError(f"Status {response.status_code} - Failed to create agent: {response.text}")
|
617
|
-
|
618
|
-
# gather agent state
|
619
|
-
agent_state = AgentState(**response.json())
|
620
|
-
|
621
|
-
# refresh and return agent
|
622
|
-
return self.get_agent(agent_state.id)
|
623
|
-
|
624
|
-
def update_agent(
|
625
|
-
self,
|
626
|
-
agent_id: str,
|
627
|
-
name: Optional[str] = None,
|
628
|
-
description: Optional[str] = None,
|
629
|
-
system: Optional[str] = None,
|
630
|
-
tool_ids: Optional[List[str]] = None,
|
631
|
-
metadata: Optional[Dict] = None,
|
632
|
-
llm_config: Optional[LLMConfig] = None,
|
633
|
-
embedding_config: Optional[EmbeddingConfig] = None,
|
634
|
-
message_ids: Optional[List[str]] = None,
|
635
|
-
tags: Optional[List[str]] = None,
|
636
|
-
response_format: Optional[ResponseFormatUnion] = None,
|
637
|
-
) -> AgentState:
|
638
|
-
"""
|
639
|
-
Update an existing agent
|
640
|
-
|
641
|
-
Args:
|
642
|
-
agent_id (str): ID of the agent
|
643
|
-
name (str): Name of the agent
|
644
|
-
description (str): Description of the agent
|
645
|
-
system (str): System configuration
|
646
|
-
tool_ids (List[str]): List of tools
|
647
|
-
metadata (Dict): Metadata
|
648
|
-
llm_config (LLMConfig): LLM configuration
|
649
|
-
embedding_config (EmbeddingConfig): Embedding configuration
|
650
|
-
message_ids (List[str]): List of message IDs
|
651
|
-
tags (List[str]): Tags for filtering agents
|
652
|
-
|
653
|
-
Returns:
|
654
|
-
agent_state (AgentState): State of the updated agent
|
655
|
-
"""
|
656
|
-
request = UpdateAgent(
|
657
|
-
name=name,
|
658
|
-
system=system,
|
659
|
-
tool_ids=tool_ids,
|
660
|
-
tags=tags,
|
661
|
-
description=description,
|
662
|
-
metadata=metadata,
|
663
|
-
llm_config=llm_config,
|
664
|
-
embedding_config=embedding_config,
|
665
|
-
message_ids=message_ids,
|
666
|
-
response_format=response_format,
|
667
|
-
)
|
668
|
-
response = requests.patch(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}", json=request.model_dump(), headers=self.headers)
|
669
|
-
if response.status_code != 200:
|
670
|
-
raise ValueError(f"Failed to update agent: {response.text}")
|
671
|
-
return AgentState(**response.json())
|
672
|
-
|
673
|
-
def get_tools_from_agent(self, agent_id: str) -> List[Tool]:
|
674
|
-
"""
|
675
|
-
Get tools to an existing agent
|
676
|
-
|
677
|
-
Args:
|
678
|
-
agent_id (str): ID of the agent
|
679
|
-
|
680
|
-
Returns:
|
681
|
-
List[Tool]: A List of Tool objs
|
682
|
-
"""
|
683
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/tools", headers=self.headers)
|
684
|
-
if response.status_code != 200:
|
685
|
-
raise ValueError(f"Failed to get tools from agents: {response.text}")
|
686
|
-
return [Tool(**tool) for tool in response.json()]
|
687
|
-
|
688
|
-
def attach_tool(self, agent_id: str, tool_id: str) -> AgentState:
|
689
|
-
"""
|
690
|
-
Add tool to an existing agent
|
691
|
-
|
692
|
-
Args:
|
693
|
-
agent_id (str): ID of the agent
|
694
|
-
tool_id (str): A tool id
|
695
|
-
|
696
|
-
Returns:
|
697
|
-
agent_state (AgentState): State of the updated agent
|
698
|
-
"""
|
699
|
-
response = requests.patch(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/tools/attach/{tool_id}", headers=self.headers)
|
700
|
-
if response.status_code != 200:
|
701
|
-
raise ValueError(f"Failed to update agent: {response.text}")
|
702
|
-
return AgentState(**response.json())
|
703
|
-
|
704
|
-
def detach_tool(self, agent_id: str, tool_id: str) -> AgentState:
|
705
|
-
"""
|
706
|
-
Removes tools from an existing agent
|
707
|
-
|
708
|
-
Args:
|
709
|
-
agent_id (str): ID of the agent
|
710
|
-
tool_id (str): The tool id
|
711
|
-
|
712
|
-
Returns:
|
713
|
-
agent_state (AgentState): State of the updated agent
|
714
|
-
"""
|
715
|
-
|
716
|
-
response = requests.patch(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/tools/detach/{tool_id}", headers=self.headers)
|
717
|
-
if response.status_code != 200:
|
718
|
-
raise ValueError(f"Failed to update agent: {response.text}")
|
719
|
-
return AgentState(**response.json())
|
720
|
-
|
721
|
-
def rename_agent(self, agent_id: str, new_name: str) -> AgentState:
|
722
|
-
"""
|
723
|
-
Rename an agent
|
724
|
-
|
725
|
-
Args:
|
726
|
-
agent_id (str): ID of the agent
|
727
|
-
new_name (str): New name for the agent
|
728
|
-
|
729
|
-
Returns:
|
730
|
-
agent_state (AgentState): State of the updated agent
|
731
|
-
"""
|
732
|
-
return self.update_agent(agent_id, name=new_name)
|
733
|
-
|
734
|
-
def delete_agent(self, agent_id: str) -> None:
|
735
|
-
"""
|
736
|
-
Delete an agent
|
737
|
-
|
738
|
-
Args:
|
739
|
-
agent_id (str): ID of the agent to delete
|
740
|
-
"""
|
741
|
-
response = requests.delete(f"{self.base_url}/{self.api_prefix}/agents/{str(agent_id)}", headers=self.headers)
|
742
|
-
assert response.status_code == 200, f"Failed to delete agent: {response.text}"
|
743
|
-
|
744
|
-
def get_agent(self, agent_id: Optional[str] = None, agent_name: Optional[str] = None) -> AgentState:
|
745
|
-
"""
|
746
|
-
Get an agent's state by it's ID.
|
747
|
-
|
748
|
-
Args:
|
749
|
-
agent_id (str): ID of the agent
|
750
|
-
|
751
|
-
Returns:
|
752
|
-
agent_state (AgentState): State representation of the agent
|
753
|
-
"""
|
754
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}", headers=self.headers)
|
755
|
-
assert response.status_code == 200, f"Failed to get agent: {response.text}"
|
756
|
-
return AgentState(**response.json())
|
757
|
-
|
758
|
-
def get_agent_id(self, agent_name: str) -> AgentState:
|
759
|
-
"""
|
760
|
-
Get the ID of an agent by name (names are unique per user)
|
761
|
-
|
762
|
-
Args:
|
763
|
-
agent_name (str): Name of the agent
|
764
|
-
|
765
|
-
Returns:
|
766
|
-
agent_id (str): ID of the agent
|
767
|
-
"""
|
768
|
-
# TODO: implement this
|
769
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/agents", headers=self.headers, params={"name": agent_name})
|
770
|
-
agents = [AgentState(**agent) for agent in response.json()]
|
771
|
-
if len(agents) == 0:
|
772
|
-
return None
|
773
|
-
agents = [agents[0]] # TODO: @matt monkeypatched
|
774
|
-
assert len(agents) == 1, f"Multiple agents with the same name: {[(agents.name, agents.id) for agents in agents]}"
|
775
|
-
return agents[0].id
|
776
|
-
|
777
|
-
# memory
|
778
|
-
def get_in_context_memory(self, agent_id: str) -> Memory:
|
779
|
-
"""
|
780
|
-
Get the in-contxt (i.e. core) memory of an agent
|
781
|
-
|
782
|
-
Args:
|
783
|
-
agent_id (str): ID of the agent
|
784
|
-
|
785
|
-
Returns:
|
786
|
-
memory (Memory): In-context memory of the agent
|
787
|
-
"""
|
788
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core-memory", headers=self.headers)
|
789
|
-
if response.status_code != 200:
|
790
|
-
raise ValueError(f"Failed to get in-context memory: {response.text}")
|
791
|
-
return Memory(**response.json())
|
792
|
-
|
793
|
-
def get_core_memory(self, agent_id: str) -> Memory:
|
794
|
-
return self.get_in_context_memory(agent_id)
|
795
|
-
|
796
|
-
def update_in_context_memory(self, agent_id: str, section: str, value: Union[List[str], str]) -> Memory:
|
797
|
-
"""
|
798
|
-
Update the in-context memory of an agent
|
799
|
-
|
800
|
-
Args:
|
801
|
-
agent_id (str): ID of the agent
|
802
|
-
|
803
|
-
Returns:
|
804
|
-
memory (Memory): The updated in-context memory of the agent
|
805
|
-
|
806
|
-
"""
|
807
|
-
memory_update_dict = {section: value}
|
808
|
-
response = requests.patch(
|
809
|
-
f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core-memory", json=memory_update_dict, headers=self.headers
|
810
|
-
)
|
811
|
-
if response.status_code != 200:
|
812
|
-
raise ValueError(f"Failed to update in-context memory: {response.text}")
|
813
|
-
return Memory(**response.json())
|
814
|
-
|
815
|
-
def get_archival_memory_summary(self, agent_id: str) -> ArchivalMemorySummary:
|
816
|
-
"""
|
817
|
-
Get a summary of the archival memory of an agent
|
818
|
-
|
819
|
-
Args:
|
820
|
-
agent_id (str): ID of the agent
|
821
|
-
|
822
|
-
Returns:
|
823
|
-
summary (ArchivalMemorySummary): Summary of the archival memory
|
824
|
-
|
825
|
-
"""
|
826
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/context", headers=self.headers)
|
827
|
-
if response.status_code != 200:
|
828
|
-
raise ValueError(f"Failed to get archival memory summary: {response.text}")
|
829
|
-
return ArchivalMemorySummary(size=response.json().get("num_archival_memory", 0))
|
830
|
-
|
831
|
-
def get_recall_memory_summary(self, agent_id: str) -> RecallMemorySummary:
|
832
|
-
"""
|
833
|
-
Get a summary of the recall memory of an agent
|
834
|
-
|
835
|
-
Args:
|
836
|
-
agent_id (str): ID of the agent
|
837
|
-
|
838
|
-
Returns:
|
839
|
-
summary (RecallMemorySummary): Summary of the recall memory
|
840
|
-
"""
|
841
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/context", headers=self.headers)
|
842
|
-
if response.status_code != 200:
|
843
|
-
raise ValueError(f"Failed to get recall memory summary: {response.text}")
|
844
|
-
return RecallMemorySummary(size=response.json().get("num_recall_memory", 0))
|
845
|
-
|
846
|
-
def get_in_context_messages(self, agent_id: str) -> List[Message]:
|
847
|
-
"""
|
848
|
-
Get in-context messages of an agent
|
849
|
-
|
850
|
-
Args:
|
851
|
-
agent_id (str): ID of the agent
|
852
|
-
|
853
|
-
Returns:
|
854
|
-
messages (List[Message]): List of in-context messages
|
855
|
-
"""
|
856
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/context", headers=self.headers)
|
857
|
-
if response.status_code != 200:
|
858
|
-
raise ValueError(f"Failed to get recall memory summary: {response.text}")
|
859
|
-
return [Message(**message) for message in response.json().get("messages", "")]
|
860
|
-
|
861
|
-
# agent interactions
|
862
|
-
|
863
|
-
def user_message(self, agent_id: str, message: str) -> LettaResponse:
|
864
|
-
"""
|
865
|
-
Send a message to an agent as a user
|
866
|
-
|
867
|
-
Args:
|
868
|
-
agent_id (str): ID of the agent
|
869
|
-
message (str): Message to send
|
870
|
-
|
871
|
-
Returns:
|
872
|
-
response (LettaResponse): Response from the agent
|
873
|
-
"""
|
874
|
-
return self.send_message(agent_id=agent_id, message=message, role="user")
|
875
|
-
|
876
|
-
def save(self):
|
877
|
-
raise NotImplementedError
|
878
|
-
|
879
|
-
# archival memory
|
880
|
-
|
881
|
-
def get_archival_memory(
|
882
|
-
self, agent_id: str, before: Optional[str] = None, after: Optional[str] = None, limit: Optional[int] = 1000
|
883
|
-
) -> List[Passage]:
|
884
|
-
"""
|
885
|
-
Get archival memory from an agent with pagination.
|
886
|
-
|
887
|
-
Args:
|
888
|
-
agent_id (str): ID of the agent
|
889
|
-
before (str): Get memories before a certain time
|
890
|
-
after (str): Get memories after a certain time
|
891
|
-
limit (int): Limit number of memories
|
892
|
-
|
893
|
-
Returns:
|
894
|
-
passages (List[Passage]): List of passages
|
895
|
-
"""
|
896
|
-
params = {"limit": limit}
|
897
|
-
if before:
|
898
|
-
params["before"] = str(before)
|
899
|
-
if after:
|
900
|
-
params["after"] = str(after)
|
901
|
-
response = requests.get(
|
902
|
-
f"{self.base_url}/{self.api_prefix}/agents/{str(agent_id)}/archival-memory", params=params, headers=self.headers
|
903
|
-
)
|
904
|
-
assert response.status_code == 200, f"Failed to get archival memory: {response.text}"
|
905
|
-
return [Passage(**passage) for passage in response.json()]
|
906
|
-
|
907
|
-
def insert_archival_memory(self, agent_id: str, memory: str) -> List[Passage]:
|
908
|
-
"""
|
909
|
-
Insert archival memory into an agent
|
910
|
-
|
911
|
-
Args:
|
912
|
-
agent_id (str): ID of the agent
|
913
|
-
memory (str): Memory string to insert
|
914
|
-
|
915
|
-
Returns:
|
916
|
-
passages (List[Passage]): List of inserted passages
|
917
|
-
"""
|
918
|
-
request = CreateArchivalMemory(text=memory)
|
919
|
-
response = requests.post(
|
920
|
-
f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/archival-memory", headers=self.headers, json=request.model_dump()
|
921
|
-
)
|
922
|
-
if response.status_code != 200:
|
923
|
-
raise ValueError(f"Failed to insert archival memory: {response.text}")
|
924
|
-
return [Passage(**passage) for passage in response.json()]
|
925
|
-
|
926
|
-
def delete_archival_memory(self, agent_id: str, memory_id: str):
|
927
|
-
"""
|
928
|
-
Delete archival memory from an agent
|
929
|
-
|
930
|
-
Args:
|
931
|
-
agent_id (str): ID of the agent
|
932
|
-
memory_id (str): ID of the memory
|
933
|
-
"""
|
934
|
-
response = requests.delete(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/archival-memory/{memory_id}", headers=self.headers)
|
935
|
-
assert response.status_code == 200, f"Failed to delete archival memory: {response.text}"
|
936
|
-
|
937
|
-
# messages (recall memory)
|
938
|
-
|
939
|
-
def get_messages(
|
940
|
-
self, agent_id: str, before: Optional[str] = None, after: Optional[str] = None, limit: Optional[int] = 1000
|
941
|
-
) -> List[LettaMessage]:
|
942
|
-
"""
|
943
|
-
Get messages from an agent with pagination.
|
944
|
-
|
945
|
-
Args:
|
946
|
-
agent_id (str): ID of the agent
|
947
|
-
before (str): Get messages before a certain time
|
948
|
-
after (str): Get messages after a certain time
|
949
|
-
limit (int): Limit number of messages
|
950
|
-
|
951
|
-
Returns:
|
952
|
-
messages (List[Message]): List of messages
|
953
|
-
"""
|
954
|
-
|
955
|
-
params = {"before": before, "after": after, "limit": limit, "msg_object": True}
|
956
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/messages", params=params, headers=self.headers)
|
957
|
-
if response.status_code != 200:
|
958
|
-
raise ValueError(f"Failed to get messages: {response.text}")
|
959
|
-
return [LettaMessage(**message) for message in response.json()]
|
960
|
-
|
961
|
-
def send_message(
|
962
|
-
self,
|
963
|
-
message: str,
|
964
|
-
role: str,
|
965
|
-
agent_id: Optional[str] = None,
|
966
|
-
name: Optional[str] = None,
|
967
|
-
stream: Optional[bool] = False,
|
968
|
-
stream_steps: bool = False,
|
969
|
-
stream_tokens: bool = False,
|
970
|
-
max_steps: Optional[int] = 10,
|
971
|
-
) -> LettaResponse:
|
972
|
-
"""
|
973
|
-
Send a message to an agent
|
974
|
-
|
975
|
-
Args:
|
976
|
-
message (str): Message to send
|
977
|
-
role (str): Role of the message
|
978
|
-
agent_id (str): ID of the agent
|
979
|
-
name(str): Name of the sender
|
980
|
-
stream (bool): Stream the response (default: `False`)
|
981
|
-
stream_tokens (bool): Stream tokens (default: `False`)
|
982
|
-
max_steps (int): Maximum number of steps the agent should take (default: 10)
|
983
|
-
|
984
|
-
Returns:
|
985
|
-
response (LettaResponse): Response from the agent
|
986
|
-
"""
|
987
|
-
# TODO: implement include_full_message
|
988
|
-
messages = [MessageCreate(role=MessageRole(role), content=message, name=name)]
|
989
|
-
# TODO: figure out how to handle stream_steps and stream_tokens
|
990
|
-
|
991
|
-
# When streaming steps is True, stream_tokens must be False
|
992
|
-
if stream_tokens or stream_steps:
|
993
|
-
from letta.client.streaming import _sse_post
|
994
|
-
|
995
|
-
request = LettaStreamingRequest(messages=messages, stream_tokens=stream_tokens)
|
996
|
-
return _sse_post(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/messages/stream", request.model_dump(), self.headers)
|
997
|
-
else:
|
998
|
-
request = LettaRequest(messages=messages)
|
999
|
-
response = requests.post(
|
1000
|
-
f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/messages", json=request.model_dump(), headers=self.headers
|
1001
|
-
)
|
1002
|
-
if response.status_code != 200:
|
1003
|
-
raise ValueError(f"Failed to send message: {response.text}")
|
1004
|
-
response = LettaResponse(**response.json())
|
1005
|
-
|
1006
|
-
# simplify messages
|
1007
|
-
# if not include_full_message:
|
1008
|
-
# messages = []
|
1009
|
-
# for m in response.messages:
|
1010
|
-
# assert isinstance(m, Message)
|
1011
|
-
# messages += m.to_letta_messages()
|
1012
|
-
# response.messages = messages
|
1013
|
-
|
1014
|
-
return response
|
1015
|
-
|
1016
|
-
def send_message_async(
|
1017
|
-
self,
|
1018
|
-
message: str,
|
1019
|
-
role: str,
|
1020
|
-
agent_id: Optional[str] = None,
|
1021
|
-
name: Optional[str] = None,
|
1022
|
-
) -> Run:
|
1023
|
-
"""
|
1024
|
-
Send a message to an agent (async, returns a job)
|
1025
|
-
|
1026
|
-
Args:
|
1027
|
-
message (str): Message to send
|
1028
|
-
role (str): Role of the message
|
1029
|
-
agent_id (str): ID of the agent
|
1030
|
-
name(str): Name of the sender
|
1031
|
-
|
1032
|
-
Returns:
|
1033
|
-
job (Job): Information about the async job
|
1034
|
-
"""
|
1035
|
-
messages = [MessageCreate(role=MessageRole(role), content=message, name=name)]
|
1036
|
-
|
1037
|
-
request = LettaRequest(messages=messages)
|
1038
|
-
response = requests.post(
|
1039
|
-
f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/messages/async",
|
1040
|
-
json=request.model_dump(),
|
1041
|
-
headers=self.headers,
|
1042
|
-
)
|
1043
|
-
if response.status_code != 200:
|
1044
|
-
raise ValueError(f"Failed to send message: {response.text}")
|
1045
|
-
response = Run(**response.json())
|
1046
|
-
|
1047
|
-
return response
|
1048
|
-
|
1049
|
-
# humans / personas
|
1050
|
-
|
1051
|
-
def list_blocks(self, label: Optional[str] = None, templates_only: Optional[bool] = True) -> List[Block]:
|
1052
|
-
params = {"label": label, "templates_only": templates_only}
|
1053
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/blocks", params=params, headers=self.headers)
|
1054
|
-
if response.status_code != 200:
|
1055
|
-
raise ValueError(f"Failed to list blocks: {response.text}")
|
1056
|
-
|
1057
|
-
if label == "human":
|
1058
|
-
return [Human(**human) for human in response.json()]
|
1059
|
-
elif label == "persona":
|
1060
|
-
return [Persona(**persona) for persona in response.json()]
|
1061
|
-
else:
|
1062
|
-
return [Block(**block) for block in response.json()]
|
1063
|
-
|
1064
|
-
def create_block(
|
1065
|
-
self, label: str, value: str, limit: Optional[int] = None, template_name: Optional[str] = None, is_template: bool = False
|
1066
|
-
) -> Block: #
|
1067
|
-
request_kwargs = dict(label=label, value=value, template=is_template, template_name=template_name)
|
1068
|
-
if limit:
|
1069
|
-
request_kwargs["limit"] = limit
|
1070
|
-
request = CreateBlock(**request_kwargs)
|
1071
|
-
response = requests.post(f"{self.base_url}/{self.api_prefix}/blocks", json=request.model_dump(), headers=self.headers)
|
1072
|
-
if response.status_code != 200:
|
1073
|
-
raise ValueError(f"Failed to create block: {response.text}")
|
1074
|
-
if request.label == "human":
|
1075
|
-
return Human(**response.json())
|
1076
|
-
elif request.label == "persona":
|
1077
|
-
return Persona(**response.json())
|
1078
|
-
else:
|
1079
|
-
return Block(**response.json())
|
1080
|
-
|
1081
|
-
def update_block(self, block_id: str, name: Optional[str] = None, text: Optional[str] = None, limit: Optional[int] = None) -> Block:
|
1082
|
-
request = BlockUpdate(id=block_id, template_name=name, value=text, limit=limit if limit else self.get_block(block_id).limit)
|
1083
|
-
response = requests.post(f"{self.base_url}/{self.api_prefix}/blocks/{block_id}", json=request.model_dump(), headers=self.headers)
|
1084
|
-
if response.status_code != 200:
|
1085
|
-
raise ValueError(f"Failed to update block: {response.text}")
|
1086
|
-
return Block(**response.json())
|
1087
|
-
|
1088
|
-
def get_block(self, block_id: str) -> Optional[Block]:
|
1089
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/blocks/{block_id}", headers=self.headers)
|
1090
|
-
if response.status_code == 404:
|
1091
|
-
return None
|
1092
|
-
elif response.status_code != 200:
|
1093
|
-
raise ValueError(f"Failed to get block: {response.text}")
|
1094
|
-
return Block(**response.json())
|
1095
|
-
|
1096
|
-
def get_block_id(self, name: str, label: str) -> str:
|
1097
|
-
params = {"name": name, "label": label}
|
1098
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/blocks", params=params, headers=self.headers)
|
1099
|
-
if response.status_code != 200:
|
1100
|
-
raise ValueError(f"Failed to get block ID: {response.text}")
|
1101
|
-
blocks = [Block(**block) for block in response.json()]
|
1102
|
-
if len(blocks) == 0:
|
1103
|
-
return None
|
1104
|
-
elif len(blocks) > 1:
|
1105
|
-
raise ValueError(f"Multiple blocks found with name {name}")
|
1106
|
-
return blocks[0].id
|
1107
|
-
|
1108
|
-
def delete_block(self, id: str) -> Block:
|
1109
|
-
response = requests.delete(f"{self.base_url}/{self.api_prefix}/blocks/{id}", headers=self.headers)
|
1110
|
-
assert response.status_code == 200, f"Failed to delete block: {response.text}"
|
1111
|
-
if response.status_code != 200:
|
1112
|
-
raise ValueError(f"Failed to delete block: {response.text}")
|
1113
|
-
return Block(**response.json())
|
1114
|
-
|
1115
|
-
def list_humans(self):
|
1116
|
-
"""
|
1117
|
-
List available human block templates
|
1118
|
-
|
1119
|
-
Returns:
|
1120
|
-
humans (List[Human]): List of human blocks
|
1121
|
-
"""
|
1122
|
-
blocks = self.list_blocks(label="human")
|
1123
|
-
return [Human(**block.model_dump()) for block in blocks]
|
1124
|
-
|
1125
|
-
def create_human(self, name: str, text: str) -> Human:
|
1126
|
-
"""
|
1127
|
-
Create a human block template (saved human string to pre-fill `ChatMemory`)
|
1128
|
-
|
1129
|
-
Args:
|
1130
|
-
name (str): Name of the human block template
|
1131
|
-
text (str): Text of the human block template
|
1132
|
-
|
1133
|
-
Returns:
|
1134
|
-
human (Human): Human block
|
1135
|
-
"""
|
1136
|
-
return self.create_block(label="human", template_name=name, value=text, is_template=True)
|
1137
|
-
|
1138
|
-
def update_human(self, human_id: str, name: Optional[str] = None, text: Optional[str] = None) -> Human:
|
1139
|
-
"""
|
1140
|
-
Update a human block template
|
1141
|
-
|
1142
|
-
Args:
|
1143
|
-
human_id (str): ID of the human block
|
1144
|
-
text (str): Text of the human block
|
1145
|
-
|
1146
|
-
Returns:
|
1147
|
-
human (Human): Updated human block
|
1148
|
-
"""
|
1149
|
-
request = UpdateHuman(id=human_id, template_name=name, value=text)
|
1150
|
-
response = requests.post(f"{self.base_url}/{self.api_prefix}/blocks/{human_id}", json=request.model_dump(), headers=self.headers)
|
1151
|
-
if response.status_code != 200:
|
1152
|
-
raise ValueError(f"Failed to update human: {response.text}")
|
1153
|
-
return Human(**response.json())
|
1154
|
-
|
1155
|
-
def list_personas(self):
|
1156
|
-
"""
|
1157
|
-
List available persona block templates
|
1158
|
-
|
1159
|
-
Returns:
|
1160
|
-
personas (List[Persona]): List of persona blocks
|
1161
|
-
"""
|
1162
|
-
blocks = self.list_blocks(label="persona")
|
1163
|
-
return [Persona(**block.model_dump()) for block in blocks]
|
1164
|
-
|
1165
|
-
def create_persona(self, name: str, text: str) -> Persona:
|
1166
|
-
"""
|
1167
|
-
Create a persona block template (saved persona string to pre-fill `ChatMemory`)
|
1168
|
-
|
1169
|
-
Args:
|
1170
|
-
name (str): Name of the persona block
|
1171
|
-
text (str): Text of the persona block
|
1172
|
-
|
1173
|
-
Returns:
|
1174
|
-
persona (Persona): Persona block
|
1175
|
-
"""
|
1176
|
-
return self.create_block(label="persona", template_name=name, value=text, is_template=True)
|
1177
|
-
|
1178
|
-
def update_persona(self, persona_id: str, name: Optional[str] = None, text: Optional[str] = None) -> Persona:
|
1179
|
-
"""
|
1180
|
-
Update a persona block template
|
1181
|
-
|
1182
|
-
Args:
|
1183
|
-
persona_id (str): ID of the persona block
|
1184
|
-
text (str): Text of the persona block
|
1185
|
-
|
1186
|
-
Returns:
|
1187
|
-
persona (Persona): Updated persona block
|
1188
|
-
"""
|
1189
|
-
request = UpdatePersona(id=persona_id, template_name=name, value=text)
|
1190
|
-
response = requests.post(f"{self.base_url}/{self.api_prefix}/blocks/{persona_id}", json=request.model_dump(), headers=self.headers)
|
1191
|
-
if response.status_code != 200:
|
1192
|
-
raise ValueError(f"Failed to update persona: {response.text}")
|
1193
|
-
return Persona(**response.json())
|
1194
|
-
|
1195
|
-
def get_persona(self, persona_id: str) -> Persona:
|
1196
|
-
"""
|
1197
|
-
Get a persona block template
|
1198
|
-
|
1199
|
-
Args:
|
1200
|
-
id (str): ID of the persona block
|
1201
|
-
|
1202
|
-
Returns:
|
1203
|
-
persona (Persona): Persona block
|
1204
|
-
"""
|
1205
|
-
return self.get_block(persona_id)
|
1206
|
-
|
1207
|
-
def get_persona_id(self, name: str) -> str:
|
1208
|
-
"""
|
1209
|
-
Get the ID of a persona block template
|
1210
|
-
|
1211
|
-
Args:
|
1212
|
-
name (str): Name of the persona block
|
1213
|
-
|
1214
|
-
Returns:
|
1215
|
-
id (str): ID of the persona block
|
1216
|
-
"""
|
1217
|
-
return self.get_block_id(name, "persona")
|
1218
|
-
|
1219
|
-
def delete_persona(self, persona_id: str) -> Persona:
|
1220
|
-
"""
|
1221
|
-
Delete a persona block template
|
1222
|
-
|
1223
|
-
Args:
|
1224
|
-
id (str): ID of the persona block
|
1225
|
-
"""
|
1226
|
-
return self.delete_block(persona_id)
|
1227
|
-
|
1228
|
-
def get_human(self, human_id: str) -> Human:
|
1229
|
-
"""
|
1230
|
-
Get a human block template
|
1231
|
-
|
1232
|
-
Args:
|
1233
|
-
id (str): ID of the human block
|
1234
|
-
|
1235
|
-
Returns:
|
1236
|
-
human (Human): Human block
|
1237
|
-
"""
|
1238
|
-
return self.get_block(human_id)
|
1239
|
-
|
1240
|
-
def get_human_id(self, name: str) -> str:
|
1241
|
-
"""
|
1242
|
-
Get the ID of a human block template
|
1243
|
-
|
1244
|
-
Args:
|
1245
|
-
name (str): Name of the human block
|
1246
|
-
|
1247
|
-
Returns:
|
1248
|
-
id (str): ID of the human block
|
1249
|
-
"""
|
1250
|
-
return self.get_block_id(name, "human")
|
1251
|
-
|
1252
|
-
def delete_human(self, human_id: str) -> Human:
|
1253
|
-
"""
|
1254
|
-
Delete a human block template
|
1255
|
-
|
1256
|
-
Args:
|
1257
|
-
id (str): ID of the human block
|
1258
|
-
"""
|
1259
|
-
return self.delete_block(human_id)
|
1260
|
-
|
1261
|
-
# sources
|
1262
|
-
|
1263
|
-
def get_source(self, source_id: str) -> Source:
|
1264
|
-
"""
|
1265
|
-
Get a source given the ID.
|
1266
|
-
|
1267
|
-
Args:
|
1268
|
-
source_id (str): ID of the source
|
1269
|
-
|
1270
|
-
Returns:
|
1271
|
-
source (Source): Source
|
1272
|
-
"""
|
1273
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/sources/{source_id}", headers=self.headers)
|
1274
|
-
if response.status_code != 200:
|
1275
|
-
raise ValueError(f"Failed to get source: {response.text}")
|
1276
|
-
return Source(**response.json())
|
1277
|
-
|
1278
|
-
def get_source_id(self, source_name: str) -> str:
|
1279
|
-
"""
|
1280
|
-
Get the ID of a source
|
1281
|
-
|
1282
|
-
Args:
|
1283
|
-
source_name (str): Name of the source
|
1284
|
-
|
1285
|
-
Returns:
|
1286
|
-
source_id (str): ID of the source
|
1287
|
-
"""
|
1288
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/sources/name/{source_name}", headers=self.headers)
|
1289
|
-
if response.status_code != 200:
|
1290
|
-
raise ValueError(f"Failed to get source ID: {response.text}")
|
1291
|
-
return response.json()
|
1292
|
-
|
1293
|
-
def list_sources(self) -> List[Source]:
|
1294
|
-
"""
|
1295
|
-
List available sources
|
1296
|
-
|
1297
|
-
Returns:
|
1298
|
-
sources (List[Source]): List of sources
|
1299
|
-
"""
|
1300
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/sources", headers=self.headers)
|
1301
|
-
if response.status_code != 200:
|
1302
|
-
raise ValueError(f"Failed to list sources: {response.text}")
|
1303
|
-
return [Source(**source) for source in response.json()]
|
1304
|
-
|
1305
|
-
def delete_source(self, source_id: str):
|
1306
|
-
"""
|
1307
|
-
Delete a source
|
1308
|
-
|
1309
|
-
Args:
|
1310
|
-
source_id (str): ID of the source
|
1311
|
-
"""
|
1312
|
-
response = requests.delete(f"{self.base_url}/{self.api_prefix}/sources/{str(source_id)}", headers=self.headers)
|
1313
|
-
assert response.status_code == 200, f"Failed to delete source: {response.text}"
|
1314
|
-
|
1315
|
-
def get_job(self, job_id: str) -> Job:
|
1316
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/jobs/{job_id}", headers=self.headers)
|
1317
|
-
if response.status_code != 200:
|
1318
|
-
raise ValueError(f"Failed to get job: {response.text}")
|
1319
|
-
return Job(**response.json())
|
1320
|
-
|
1321
|
-
def delete_job(self, job_id: str) -> Job:
|
1322
|
-
response = requests.delete(f"{self.base_url}/{self.api_prefix}/jobs/{job_id}", headers=self.headers)
|
1323
|
-
if response.status_code != 200:
|
1324
|
-
raise ValueError(f"Failed to delete job: {response.text}")
|
1325
|
-
return Job(**response.json())
|
1326
|
-
|
1327
|
-
def list_jobs(self):
|
1328
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/jobs", headers=self.headers)
|
1329
|
-
return [Job(**job) for job in response.json()]
|
1330
|
-
|
1331
|
-
def list_active_jobs(self):
|
1332
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/jobs/active", headers=self.headers)
|
1333
|
-
return [Job(**job) for job in response.json()]
|
1334
|
-
|
1335
|
-
def load_data(self, connector: DataConnector, source_name: str):
|
1336
|
-
raise NotImplementedError
|
1337
|
-
|
1338
|
-
def load_file_to_source(self, filename: str, source_id: str, blocking=True) -> Job:
|
1339
|
-
"""
|
1340
|
-
Load a file into a source
|
1341
|
-
|
1342
|
-
Args:
|
1343
|
-
filename (str): Name of the file
|
1344
|
-
source_id (str): ID of the source
|
1345
|
-
blocking (bool): Block until the job is complete
|
1346
|
-
|
1347
|
-
Returns:
|
1348
|
-
job (Job): Data loading job including job status and metadata
|
1349
|
-
"""
|
1350
|
-
files = {"file": open(filename, "rb")}
|
1351
|
-
|
1352
|
-
# create job
|
1353
|
-
response = requests.post(f"{self.base_url}/{self.api_prefix}/sources/{source_id}/upload", files=files, headers=self.headers)
|
1354
|
-
if response.status_code != 200:
|
1355
|
-
raise ValueError(f"Failed to upload file to source: {response.text}")
|
1356
|
-
|
1357
|
-
job = Job(**response.json())
|
1358
|
-
if blocking:
|
1359
|
-
# wait until job is completed
|
1360
|
-
while True:
|
1361
|
-
job = self.get_job(job.id)
|
1362
|
-
if job.status == JobStatus.completed:
|
1363
|
-
break
|
1364
|
-
elif job.status == JobStatus.failed:
|
1365
|
-
raise ValueError(f"Job failed: {job.metadata}")
|
1366
|
-
time.sleep(1)
|
1367
|
-
return job
|
1368
|
-
|
1369
|
-
def delete_file_from_source(self, source_id: str, file_id: str) -> None:
|
1370
|
-
response = requests.delete(f"{self.base_url}/{self.api_prefix}/sources/{source_id}/{file_id}", headers=self.headers)
|
1371
|
-
if response.status_code not in [200, 204]:
|
1372
|
-
raise ValueError(f"Failed to delete tool: {response.text}")
|
1373
|
-
|
1374
|
-
def create_source(self, name: str, embedding_config: Optional[EmbeddingConfig] = None) -> Source:
|
1375
|
-
"""
|
1376
|
-
Create a source
|
1377
|
-
|
1378
|
-
Args:
|
1379
|
-
name (str): Name of the source
|
1380
|
-
|
1381
|
-
Returns:
|
1382
|
-
source (Source): Created source
|
1383
|
-
"""
|
1384
|
-
assert embedding_config or self._default_embedding_config, "Must specify embedding_config for source"
|
1385
|
-
source_create = SourceCreate(name=name, embedding_config=embedding_config or self._default_embedding_config)
|
1386
|
-
payload = source_create.model_dump()
|
1387
|
-
response = requests.post(f"{self.base_url}/{self.api_prefix}/sources", json=payload, headers=self.headers)
|
1388
|
-
response_json = response.json()
|
1389
|
-
return Source(**response_json)
|
1390
|
-
|
1391
|
-
def list_attached_sources(self, agent_id: str) -> List[Source]:
|
1392
|
-
"""
|
1393
|
-
List sources attached to an agent
|
1394
|
-
|
1395
|
-
Args:
|
1396
|
-
agent_id (str): ID of the agent
|
1397
|
-
|
1398
|
-
Returns:
|
1399
|
-
sources (List[Source]): List of sources
|
1400
|
-
"""
|
1401
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/sources", headers=self.headers)
|
1402
|
-
if response.status_code != 200:
|
1403
|
-
raise ValueError(f"Failed to list attached sources: {response.text}")
|
1404
|
-
return [Source(**source) for source in response.json()]
|
1405
|
-
|
1406
|
-
def list_files_from_source(self, source_id: str, limit: int = 1000, after: Optional[str] = None) -> List[FileMetadata]:
|
1407
|
-
"""
|
1408
|
-
List files from source with pagination support.
|
1409
|
-
|
1410
|
-
Args:
|
1411
|
-
source_id (str): ID of the source
|
1412
|
-
limit (int): Number of files to return
|
1413
|
-
after (str): Get files after a certain time
|
1414
|
-
|
1415
|
-
Returns:
|
1416
|
-
List[FileMetadata]: List of files
|
1417
|
-
"""
|
1418
|
-
# Prepare query parameters for pagination
|
1419
|
-
params = {"limit": limit, "after": after}
|
1420
|
-
|
1421
|
-
# Make the request to the FastAPI endpoint
|
1422
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/sources/{source_id}/files", headers=self.headers, params=params)
|
1423
|
-
|
1424
|
-
if response.status_code != 200:
|
1425
|
-
raise ValueError(f"Failed to list files with source id {source_id}: [{response.status_code}] {response.text}")
|
1426
|
-
|
1427
|
-
# Parse the JSON response
|
1428
|
-
return [FileMetadata(**metadata) for metadata in response.json()]
|
1429
|
-
|
1430
|
-
def update_source(self, source_id: str, name: Optional[str] = None) -> Source:
|
1431
|
-
"""
|
1432
|
-
Update a source
|
1433
|
-
|
1434
|
-
Args:
|
1435
|
-
source_id (str): ID of the source
|
1436
|
-
name (str): Name of the source
|
1437
|
-
|
1438
|
-
Returns:
|
1439
|
-
source (Source): Updated source
|
1440
|
-
"""
|
1441
|
-
request = SourceUpdate(name=name)
|
1442
|
-
response = requests.patch(f"{self.base_url}/{self.api_prefix}/sources/{source_id}", json=request.model_dump(), headers=self.headers)
|
1443
|
-
if response.status_code != 200:
|
1444
|
-
raise ValueError(f"Failed to update source: {response.text}")
|
1445
|
-
return Source(**response.json())
|
1446
|
-
|
1447
|
-
def attach_source(self, source_id: str, agent_id: str) -> AgentState:
|
1448
|
-
"""
|
1449
|
-
Attach a source to an agent
|
1450
|
-
|
1451
|
-
Args:
|
1452
|
-
agent_id (str): ID of the agent
|
1453
|
-
source_id (str): ID of the source
|
1454
|
-
source_name (str): Name of the source
|
1455
|
-
"""
|
1456
|
-
params = {"agent_id": agent_id}
|
1457
|
-
response = requests.patch(
|
1458
|
-
f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/sources/attach/{source_id}", params=params, headers=self.headers
|
1459
|
-
)
|
1460
|
-
assert response.status_code == 200, f"Failed to attach source to agent: {response.text}"
|
1461
|
-
return AgentState(**response.json())
|
1462
|
-
|
1463
|
-
def detach_source(self, source_id: str, agent_id: str) -> AgentState:
|
1464
|
-
"""Detach a source from an agent"""
|
1465
|
-
params = {"agent_id": str(agent_id)}
|
1466
|
-
response = requests.patch(
|
1467
|
-
f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/sources/detach/{source_id}", params=params, headers=self.headers
|
1468
|
-
)
|
1469
|
-
assert response.status_code == 200, f"Failed to detach source from agent: {response.text}"
|
1470
|
-
return AgentState(**response.json())
|
1471
|
-
|
1472
|
-
# tools
|
1473
|
-
|
1474
|
-
def get_tool_id(self, tool_name: str):
|
1475
|
-
"""
|
1476
|
-
Get the ID of a tool
|
1477
|
-
|
1478
|
-
Args:
|
1479
|
-
name (str): Name of the tool
|
1480
|
-
|
1481
|
-
Returns:
|
1482
|
-
id (str): ID of the tool (`None` if not found)
|
1483
|
-
"""
|
1484
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/tools", headers=self.headers)
|
1485
|
-
if response.status_code != 200:
|
1486
|
-
raise ValueError(f"Failed to get tool: {response.text}")
|
1487
|
-
|
1488
|
-
tools = [tool for tool in [Tool(**tool) for tool in response.json()] if tool.name == tool_name]
|
1489
|
-
if not tools:
|
1490
|
-
return None
|
1491
|
-
return tools[0].id
|
1492
|
-
|
1493
|
-
def list_attached_tools(self, agent_id: str) -> List[Tool]:
|
1494
|
-
"""
|
1495
|
-
List all tools attached to an agent.
|
1496
|
-
|
1497
|
-
Args:
|
1498
|
-
agent_id (str): ID of the agent
|
1499
|
-
|
1500
|
-
Returns:
|
1501
|
-
List[Tool]: A list of attached tools
|
1502
|
-
"""
|
1503
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/tools", headers=self.headers)
|
1504
|
-
if response.status_code != 200:
|
1505
|
-
raise ValueError(f"Failed to list attached tools: {response.text}")
|
1506
|
-
return [Tool(**tool) for tool in response.json()]
|
1507
|
-
|
1508
|
-
def upsert_base_tools(self) -> List[Tool]:
|
1509
|
-
response = requests.post(f"{self.base_url}/{self.api_prefix}/tools/add-base-tools/", headers=self.headers)
|
1510
|
-
if response.status_code != 200:
|
1511
|
-
raise ValueError(f"Failed to add base tools: {response.text}")
|
1512
|
-
|
1513
|
-
return [Tool(**tool) for tool in response.json()]
|
1514
|
-
|
1515
|
-
def create_tool(
|
1516
|
-
self,
|
1517
|
-
func: Callable,
|
1518
|
-
tags: Optional[List[str]] = None,
|
1519
|
-
return_char_limit: int = FUNCTION_RETURN_CHAR_LIMIT,
|
1520
|
-
) -> Tool:
|
1521
|
-
"""
|
1522
|
-
Create a tool. This stores the source code of function on the server, so that the server can execute the function and generate an OpenAI JSON schemas for it when using with an agent.
|
1523
|
-
|
1524
|
-
Args:
|
1525
|
-
func (callable): The function to create a tool for.
|
1526
|
-
name: (str): Name of the tool (must be unique per-user.)
|
1527
|
-
tags (Optional[List[str]], optional): Tags for the tool. Defaults to None.
|
1528
|
-
return_char_limit (int): The character limit for the tool's return value. Defaults to FUNCTION_RETURN_CHAR_LIMIT.
|
1529
|
-
|
1530
|
-
Returns:
|
1531
|
-
tool (Tool): The created tool.
|
1532
|
-
"""
|
1533
|
-
source_code = parse_source_code(func)
|
1534
|
-
source_type = "python"
|
1535
|
-
|
1536
|
-
# call server function
|
1537
|
-
request = ToolCreate(source_type=source_type, source_code=source_code, return_char_limit=return_char_limit)
|
1538
|
-
if tags:
|
1539
|
-
request.tags = tags
|
1540
|
-
response = requests.post(f"{self.base_url}/{self.api_prefix}/tools", json=request.model_dump(), headers=self.headers)
|
1541
|
-
if response.status_code != 200:
|
1542
|
-
raise ValueError(f"Failed to create tool: {response.text}")
|
1543
|
-
return Tool(**response.json())
|
1544
|
-
|
1545
|
-
def create_or_update_tool(
|
1546
|
-
self,
|
1547
|
-
func: Callable,
|
1548
|
-
tags: Optional[List[str]] = None,
|
1549
|
-
return_char_limit: int = FUNCTION_RETURN_CHAR_LIMIT,
|
1550
|
-
) -> Tool:
|
1551
|
-
"""
|
1552
|
-
Creates or updates a tool. This stores the source code of function on the server, so that the server can execute the function and generate an OpenAI JSON schemas for it when using with an agent.
|
1553
|
-
|
1554
|
-
Args:
|
1555
|
-
func (callable): The function to create a tool for.
|
1556
|
-
name: (str): Name of the tool (must be unique per-user.)
|
1557
|
-
tags (Optional[List[str]], optional): Tags for the tool. Defaults to None.
|
1558
|
-
return_char_limit (int): The character limit for the tool's return value. Defaults to FUNCTION_RETURN_CHAR_LIMIT.
|
1559
|
-
|
1560
|
-
Returns:
|
1561
|
-
tool (Tool): The created tool.
|
1562
|
-
"""
|
1563
|
-
source_code = parse_source_code(func)
|
1564
|
-
source_type = "python"
|
1565
|
-
|
1566
|
-
# call server function
|
1567
|
-
request = ToolCreate(source_type=source_type, source_code=source_code, return_char_limit=return_char_limit)
|
1568
|
-
if tags:
|
1569
|
-
request.tags = tags
|
1570
|
-
response = requests.put(f"{self.base_url}/{self.api_prefix}/tools", json=request.model_dump(), headers=self.headers)
|
1571
|
-
if response.status_code != 200:
|
1572
|
-
raise ValueError(f"Failed to create tool: {response.text}")
|
1573
|
-
return Tool(**response.json())
|
1574
|
-
|
1575
|
-
def update_tool(
|
1576
|
-
self,
|
1577
|
-
id: str,
|
1578
|
-
description: Optional[str] = None,
|
1579
|
-
func: Optional[Callable] = None,
|
1580
|
-
tags: Optional[List[str]] = None,
|
1581
|
-
return_char_limit: int = FUNCTION_RETURN_CHAR_LIMIT,
|
1582
|
-
) -> Tool:
|
1583
|
-
"""
|
1584
|
-
Update a tool with provided parameters (name, func, tags)
|
1585
|
-
|
1586
|
-
Args:
|
1587
|
-
id (str): ID of the tool
|
1588
|
-
name (str): Name of the tool
|
1589
|
-
func (callable): Function to wrap in a tool
|
1590
|
-
tags (List[str]): Tags for the tool
|
1591
|
-
return_char_limit (int): The character limit for the tool's return value. Defaults to FUNCTION_RETURN_CHAR_LIMIT.
|
1592
|
-
|
1593
|
-
Returns:
|
1594
|
-
tool (Tool): Updated tool
|
1595
|
-
"""
|
1596
|
-
if func:
|
1597
|
-
source_code = parse_source_code(func)
|
1598
|
-
else:
|
1599
|
-
source_code = None
|
1600
|
-
|
1601
|
-
source_type = "python"
|
1602
|
-
|
1603
|
-
request = ToolUpdate(
|
1604
|
-
description=description,
|
1605
|
-
source_type=source_type,
|
1606
|
-
source_code=source_code,
|
1607
|
-
tags=tags,
|
1608
|
-
return_char_limit=return_char_limit,
|
1609
|
-
)
|
1610
|
-
response = requests.patch(f"{self.base_url}/{self.api_prefix}/tools/{id}", json=request.model_dump(), headers=self.headers)
|
1611
|
-
if response.status_code != 200:
|
1612
|
-
raise ValueError(f"Failed to update tool: {response.text}")
|
1613
|
-
return Tool(**response.json())
|
1614
|
-
|
1615
|
-
def list_tools(self, after: Optional[str] = None, limit: Optional[int] = 50) -> List[Tool]:
|
1616
|
-
"""
|
1617
|
-
List available tools for the user.
|
1618
|
-
|
1619
|
-
Returns:
|
1620
|
-
tools (List[Tool]): List of tools
|
1621
|
-
"""
|
1622
|
-
params = {}
|
1623
|
-
if after:
|
1624
|
-
params["after"] = after
|
1625
|
-
if limit:
|
1626
|
-
params["limit"] = limit
|
1627
|
-
|
1628
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/tools", params=params, headers=self.headers)
|
1629
|
-
if response.status_code != 200:
|
1630
|
-
raise ValueError(f"Failed to list tools: {response.text}")
|
1631
|
-
return [Tool(**tool) for tool in response.json()]
|
1632
|
-
|
1633
|
-
def delete_tool(self, name: str):
|
1634
|
-
"""
|
1635
|
-
Delete a tool given the ID.
|
1636
|
-
|
1637
|
-
Args:
|
1638
|
-
id (str): ID of the tool
|
1639
|
-
"""
|
1640
|
-
response = requests.delete(f"{self.base_url}/{self.api_prefix}/tools/{name}", headers=self.headers)
|
1641
|
-
if response.status_code != 200:
|
1642
|
-
raise ValueError(f"Failed to delete tool: {response.text}")
|
1643
|
-
|
1644
|
-
def get_tool(self, id: str) -> Optional[Tool]:
|
1645
|
-
"""
|
1646
|
-
Get a tool give its ID.
|
1647
|
-
|
1648
|
-
Args:
|
1649
|
-
id (str): ID of the tool
|
1650
|
-
|
1651
|
-
Returns:
|
1652
|
-
tool (Tool): Tool
|
1653
|
-
"""
|
1654
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/tools/{id}", headers=self.headers)
|
1655
|
-
if response.status_code == 404:
|
1656
|
-
return None
|
1657
|
-
elif response.status_code != 200:
|
1658
|
-
raise ValueError(f"Failed to get tool: {response.text}")
|
1659
|
-
return Tool(**response.json())
|
1660
|
-
|
1661
|
-
def set_default_llm_config(self, llm_config: LLMConfig):
|
1662
|
-
"""
|
1663
|
-
Set the default LLM configuration
|
1664
|
-
|
1665
|
-
Args:
|
1666
|
-
llm_config (LLMConfig): LLM configuration
|
1667
|
-
"""
|
1668
|
-
self._default_llm_config = llm_config
|
1669
|
-
|
1670
|
-
def set_default_embedding_config(self, embedding_config: EmbeddingConfig):
|
1671
|
-
"""
|
1672
|
-
Set the default embedding configuration
|
1673
|
-
|
1674
|
-
Args:
|
1675
|
-
embedding_config (EmbeddingConfig): Embedding configuration
|
1676
|
-
"""
|
1677
|
-
self._default_embedding_config = embedding_config
|
1678
|
-
|
1679
|
-
def list_llm_configs(self) -> List[LLMConfig]:
|
1680
|
-
"""
|
1681
|
-
List available LLM configurations
|
1682
|
-
|
1683
|
-
Returns:
|
1684
|
-
configs (List[LLMConfig]): List of LLM configurations
|
1685
|
-
"""
|
1686
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/models", headers=self.headers)
|
1687
|
-
if response.status_code != 200:
|
1688
|
-
raise ValueError(f"Failed to list LLM configs: {response.text}")
|
1689
|
-
return [LLMConfig(**config) for config in response.json()]
|
1690
|
-
|
1691
|
-
def list_embedding_configs(self) -> List[EmbeddingConfig]:
|
1692
|
-
"""
|
1693
|
-
List available embedding configurations
|
1694
|
-
|
1695
|
-
Returns:
|
1696
|
-
configs (List[EmbeddingConfig]): List of embedding configurations
|
1697
|
-
"""
|
1698
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/models/embedding", headers=self.headers)
|
1699
|
-
if response.status_code != 200:
|
1700
|
-
raise ValueError(f"Failed to list embedding configs: {response.text}")
|
1701
|
-
return [EmbeddingConfig(**config) for config in response.json()]
|
1702
|
-
|
1703
|
-
def list_orgs(self, after: Optional[str] = None, limit: Optional[int] = 50) -> List[Organization]:
|
1704
|
-
"""
|
1705
|
-
Retrieves a list of all organizations in the database, with optional pagination.
|
1706
|
-
|
1707
|
-
@param after: the pagination cursor, if any
|
1708
|
-
@param limit: the maximum number of organizations to retrieve
|
1709
|
-
@return: a list of Organization objects
|
1710
|
-
"""
|
1711
|
-
params = {"after": after, "limit": limit}
|
1712
|
-
response = requests.get(f"{self.base_url}/{ADMIN_PREFIX}/orgs", headers=self.headers, params=params)
|
1713
|
-
if response.status_code != 200:
|
1714
|
-
raise ValueError(f"Failed to retrieve organizations: {response.text}")
|
1715
|
-
return [Organization(**org_data) for org_data in response.json()]
|
1716
|
-
|
1717
|
-
def create_org(self, name: Optional[str] = None) -> Organization:
|
1718
|
-
"""
|
1719
|
-
Creates an organization with the given name. If not provided, we generate a random one.
|
1720
|
-
|
1721
|
-
@param name: the name of the organization
|
1722
|
-
@return: the created Organization
|
1723
|
-
"""
|
1724
|
-
payload = {"name": name}
|
1725
|
-
response = requests.post(f"{self.base_url}/{ADMIN_PREFIX}/orgs", headers=self.headers, json=payload)
|
1726
|
-
if response.status_code != 200:
|
1727
|
-
raise ValueError(f"Failed to create org: {response.text}")
|
1728
|
-
return Organization(**response.json())
|
1729
|
-
|
1730
|
-
def delete_org(self, org_id: str) -> Organization:
|
1731
|
-
"""
|
1732
|
-
Deletes an organization by its ID.
|
1733
|
-
|
1734
|
-
@param org_id: the ID of the organization to delete
|
1735
|
-
@return: the deleted Organization object
|
1736
|
-
"""
|
1737
|
-
# Define query parameters with org_id
|
1738
|
-
params = {"org_id": org_id}
|
1739
|
-
|
1740
|
-
# Make the DELETE request with query parameters
|
1741
|
-
response = requests.delete(f"{self.base_url}/{ADMIN_PREFIX}/orgs", headers=self.headers, params=params)
|
1742
|
-
|
1743
|
-
if response.status_code == 404:
|
1744
|
-
raise ValueError(f"Organization with ID '{org_id}' does not exist")
|
1745
|
-
elif response.status_code != 200:
|
1746
|
-
raise ValueError(f"Failed to delete organization: {response.text}")
|
1747
|
-
|
1748
|
-
# Parse and return the deleted organization
|
1749
|
-
return Organization(**response.json())
|
1750
|
-
|
1751
|
-
def create_sandbox_config(self, config: Union[LocalSandboxConfig, E2BSandboxConfig]) -> SandboxConfig:
|
1752
|
-
"""
|
1753
|
-
Create a new sandbox configuration.
|
1754
|
-
|
1755
|
-
Args:
|
1756
|
-
config (Union[LocalSandboxConfig, E2BSandboxConfig]): The sandbox settings.
|
1757
|
-
|
1758
|
-
Returns:
|
1759
|
-
SandboxConfig: The created sandbox configuration.
|
1760
|
-
"""
|
1761
|
-
payload = {
|
1762
|
-
"config": config.model_dump(),
|
1763
|
-
}
|
1764
|
-
response = requests.post(f"{self.base_url}/{self.api_prefix}/sandbox-config", headers=self.headers, json=payload)
|
1765
|
-
if response.status_code != 200:
|
1766
|
-
raise ValueError(f"Failed to create sandbox config: {response.text}")
|
1767
|
-
return SandboxConfig(**response.json())
|
1768
|
-
|
1769
|
-
def update_sandbox_config(self, sandbox_config_id: str, config: Union[LocalSandboxConfig, E2BSandboxConfig]) -> SandboxConfig:
|
1770
|
-
"""
|
1771
|
-
Update an existing sandbox configuration.
|
1772
|
-
|
1773
|
-
Args:
|
1774
|
-
sandbox_config_id (str): The ID of the sandbox configuration to update.
|
1775
|
-
config (Union[LocalSandboxConfig, E2BSandboxConfig]): The updated sandbox settings.
|
1776
|
-
|
1777
|
-
Returns:
|
1778
|
-
SandboxConfig: The updated sandbox configuration.
|
1779
|
-
"""
|
1780
|
-
payload = {
|
1781
|
-
"config": config.model_dump(),
|
1782
|
-
}
|
1783
|
-
response = requests.patch(
|
1784
|
-
f"{self.base_url}/{self.api_prefix}/sandbox-config/{sandbox_config_id}",
|
1785
|
-
headers=self.headers,
|
1786
|
-
json=payload,
|
1787
|
-
)
|
1788
|
-
if response.status_code != 200:
|
1789
|
-
raise ValueError(f"Failed to update sandbox config with ID '{sandbox_config_id}': {response.text}")
|
1790
|
-
return SandboxConfig(**response.json())
|
1791
|
-
|
1792
|
-
def delete_sandbox_config(self, sandbox_config_id: str) -> None:
|
1793
|
-
"""
|
1794
|
-
Delete a sandbox configuration.
|
1795
|
-
|
1796
|
-
Args:
|
1797
|
-
sandbox_config_id (str): The ID of the sandbox configuration to delete.
|
1798
|
-
"""
|
1799
|
-
response = requests.delete(f"{self.base_url}/{self.api_prefix}/sandbox-config/{sandbox_config_id}", headers=self.headers)
|
1800
|
-
if response.status_code == 404:
|
1801
|
-
raise ValueError(f"Sandbox config with ID '{sandbox_config_id}' does not exist")
|
1802
|
-
elif response.status_code != 204:
|
1803
|
-
raise ValueError(f"Failed to delete sandbox config with ID '{sandbox_config_id}': {response.text}")
|
1804
|
-
|
1805
|
-
def list_sandbox_configs(self, limit: int = 50, after: Optional[str] = None) -> List[SandboxConfig]:
|
1806
|
-
"""
|
1807
|
-
List all sandbox configurations.
|
1808
|
-
|
1809
|
-
Args:
|
1810
|
-
limit (int, optional): The maximum number of sandbox configurations to return. Defaults to 50.
|
1811
|
-
after (Optional[str], optional): The pagination cursor for retrieving the next set of results.
|
1812
|
-
|
1813
|
-
Returns:
|
1814
|
-
List[SandboxConfig]: A list of sandbox configurations.
|
1815
|
-
"""
|
1816
|
-
params = {"limit": limit, "after": after}
|
1817
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/sandbox-config", headers=self.headers, params=params)
|
1818
|
-
if response.status_code != 200:
|
1819
|
-
raise ValueError(f"Failed to list sandbox configs: {response.text}")
|
1820
|
-
return [SandboxConfig(**config_data) for config_data in response.json()]
|
1821
|
-
|
1822
|
-
def create_sandbox_env_var(
|
1823
|
-
self, sandbox_config_id: str, key: str, value: str, description: Optional[str] = None
|
1824
|
-
) -> SandboxEnvironmentVariable:
|
1825
|
-
"""
|
1826
|
-
Create a new environment variable for a sandbox configuration.
|
1827
|
-
|
1828
|
-
Args:
|
1829
|
-
sandbox_config_id (str): The ID of the sandbox configuration to associate the environment variable with.
|
1830
|
-
key (str): The name of the environment variable.
|
1831
|
-
value (str): The value of the environment variable.
|
1832
|
-
description (Optional[str], optional): A description of the environment variable. Defaults to None.
|
1833
|
-
|
1834
|
-
Returns:
|
1835
|
-
SandboxEnvironmentVariable: The created environment variable.
|
1836
|
-
"""
|
1837
|
-
payload = {"key": key, "value": value, "description": description}
|
1838
|
-
response = requests.post(
|
1839
|
-
f"{self.base_url}/{self.api_prefix}/sandbox-config/{sandbox_config_id}/environment-variable",
|
1840
|
-
headers=self.headers,
|
1841
|
-
json=payload,
|
1842
|
-
)
|
1843
|
-
if response.status_code != 200:
|
1844
|
-
raise ValueError(f"Failed to create environment variable for sandbox config ID '{sandbox_config_id}': {response.text}")
|
1845
|
-
return SandboxEnvironmentVariable(**response.json())
|
1846
|
-
|
1847
|
-
def update_sandbox_env_var(
|
1848
|
-
self, env_var_id: str, key: Optional[str] = None, value: Optional[str] = None, description: Optional[str] = None
|
1849
|
-
) -> SandboxEnvironmentVariable:
|
1850
|
-
"""
|
1851
|
-
Update an existing environment variable.
|
1852
|
-
|
1853
|
-
Args:
|
1854
|
-
env_var_id (str): The ID of the environment variable to update.
|
1855
|
-
key (Optional[str], optional): The updated name of the environment variable. Defaults to None.
|
1856
|
-
value (Optional[str], optional): The updated value of the environment variable. Defaults to None.
|
1857
|
-
description (Optional[str], optional): The updated description of the environment variable. Defaults to None.
|
1858
|
-
|
1859
|
-
Returns:
|
1860
|
-
SandboxEnvironmentVariable: The updated environment variable.
|
1861
|
-
"""
|
1862
|
-
payload = {k: v for k, v in {"key": key, "value": value, "description": description}.items() if v is not None}
|
1863
|
-
response = requests.patch(
|
1864
|
-
f"{self.base_url}/{self.api_prefix}/sandbox-config/environment-variable/{env_var_id}",
|
1865
|
-
headers=self.headers,
|
1866
|
-
json=payload,
|
1867
|
-
)
|
1868
|
-
if response.status_code != 200:
|
1869
|
-
raise ValueError(f"Failed to update environment variable with ID '{env_var_id}': {response.text}")
|
1870
|
-
return SandboxEnvironmentVariable(**response.json())
|
1871
|
-
|
1872
|
-
def delete_sandbox_env_var(self, env_var_id: str) -> None:
|
1873
|
-
"""
|
1874
|
-
Delete an environment variable by its ID.
|
1875
|
-
|
1876
|
-
Args:
|
1877
|
-
env_var_id (str): The ID of the environment variable to delete.
|
1878
|
-
"""
|
1879
|
-
response = requests.delete(
|
1880
|
-
f"{self.base_url}/{self.api_prefix}/sandbox-config/environment-variable/{env_var_id}", headers=self.headers
|
1881
|
-
)
|
1882
|
-
if response.status_code == 404:
|
1883
|
-
raise ValueError(f"Environment variable with ID '{env_var_id}' does not exist")
|
1884
|
-
elif response.status_code != 204:
|
1885
|
-
raise ValueError(f"Failed to delete environment variable with ID '{env_var_id}': {response.text}")
|
1886
|
-
|
1887
|
-
def list_sandbox_env_vars(
|
1888
|
-
self, sandbox_config_id: str, limit: int = 50, after: Optional[str] = None
|
1889
|
-
) -> List[SandboxEnvironmentVariable]:
|
1890
|
-
"""
|
1891
|
-
List all environment variables associated with a sandbox configuration.
|
1892
|
-
|
1893
|
-
Args:
|
1894
|
-
sandbox_config_id (str): The ID of the sandbox configuration to retrieve environment variables for.
|
1895
|
-
limit (int, optional): The maximum number of environment variables to return. Defaults to 50.
|
1896
|
-
after (Optional[str], optional): The pagination cursor for retrieving the next set of results.
|
1897
|
-
|
1898
|
-
Returns:
|
1899
|
-
List[SandboxEnvironmentVariable]: A list of environment variables.
|
1900
|
-
"""
|
1901
|
-
params = {"limit": limit, "after": after}
|
1902
|
-
response = requests.get(
|
1903
|
-
f"{self.base_url}/{self.api_prefix}/sandbox-config/{sandbox_config_id}/environment-variable",
|
1904
|
-
headers=self.headers,
|
1905
|
-
params=params,
|
1906
|
-
)
|
1907
|
-
if response.status_code != 200:
|
1908
|
-
raise ValueError(f"Failed to list environment variables for sandbox config ID '{sandbox_config_id}': {response.text}")
|
1909
|
-
return [SandboxEnvironmentVariable(**var_data) for var_data in response.json()]
|
1910
|
-
|
1911
|
-
def update_agent_memory_block_label(self, agent_id: str, current_label: str, new_label: str) -> Memory:
|
1912
|
-
"""Rename a block in the agent's core memory
|
1913
|
-
|
1914
|
-
Args:
|
1915
|
-
agent_id (str): The agent ID
|
1916
|
-
current_label (str): The current label of the block
|
1917
|
-
new_label (str): The new label of the block
|
1918
|
-
|
1919
|
-
Returns:
|
1920
|
-
memory (Memory): The updated memory
|
1921
|
-
"""
|
1922
|
-
block = self.get_agent_memory_block(agent_id, current_label)
|
1923
|
-
return self.update_block(block.id, label=new_label)
|
1924
|
-
|
1925
|
-
def attach_block(self, agent_id: str, block_id: str) -> AgentState:
|
1926
|
-
"""
|
1927
|
-
Attach a block to an agent.
|
1928
|
-
|
1929
|
-
Args:
|
1930
|
-
agent_id (str): ID of the agent
|
1931
|
-
block_id (str): ID of the block to attach
|
1932
|
-
"""
|
1933
|
-
response = requests.patch(
|
1934
|
-
f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core-memory/blocks/attach/{block_id}",
|
1935
|
-
headers=self.headers,
|
1936
|
-
)
|
1937
|
-
if response.status_code != 200:
|
1938
|
-
raise ValueError(f"Failed to attach block to agent: {response.text}")
|
1939
|
-
return AgentState(**response.json())
|
1940
|
-
|
1941
|
-
def detach_block(self, agent_id: str, block_id: str) -> AgentState:
|
1942
|
-
"""
|
1943
|
-
Detach a block from an agent.
|
1944
|
-
|
1945
|
-
Args:
|
1946
|
-
agent_id (str): ID of the agent
|
1947
|
-
block_id (str): ID of the block to detach
|
1948
|
-
"""
|
1949
|
-
response = requests.patch(
|
1950
|
-
f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core-memory/blocks/detach/{block_id}", headers=self.headers
|
1951
|
-
)
|
1952
|
-
if response.status_code != 200:
|
1953
|
-
raise ValueError(f"Failed to detach block from agent: {response.text}")
|
1954
|
-
return AgentState(**response.json())
|
1955
|
-
|
1956
|
-
def list_agent_memory_blocks(self, agent_id: str) -> List[Block]:
|
1957
|
-
"""
|
1958
|
-
Get all the blocks in the agent's core memory
|
1959
|
-
|
1960
|
-
Args:
|
1961
|
-
agent_id (str): The agent ID
|
1962
|
-
|
1963
|
-
Returns:
|
1964
|
-
blocks (List[Block]): The blocks in the agent's core memory
|
1965
|
-
"""
|
1966
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core-memory/blocks", headers=self.headers)
|
1967
|
-
if response.status_code != 200:
|
1968
|
-
raise ValueError(f"Failed to get agent memory blocks: {response.text}")
|
1969
|
-
return [Block(**block) for block in response.json()]
|
1970
|
-
|
1971
|
-
def get_agent_memory_block(self, agent_id: str, label: str) -> Block:
|
1972
|
-
"""
|
1973
|
-
Get a block in the agent's core memory by its label
|
1974
|
-
|
1975
|
-
Args:
|
1976
|
-
agent_id (str): The agent ID
|
1977
|
-
label (str): The label in the agent's core memory
|
1978
|
-
|
1979
|
-
Returns:
|
1980
|
-
block (Block): The block corresponding to the label
|
1981
|
-
"""
|
1982
|
-
response = requests.get(
|
1983
|
-
f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core-memory/blocks/{label}",
|
1984
|
-
headers=self.headers,
|
1985
|
-
)
|
1986
|
-
if response.status_code != 200:
|
1987
|
-
raise ValueError(f"Failed to get agent memory block: {response.text}")
|
1988
|
-
return Block(**response.json())
|
1989
|
-
|
1990
|
-
def update_agent_memory_block(
|
1991
|
-
self,
|
1992
|
-
agent_id: str,
|
1993
|
-
label: str,
|
1994
|
-
value: Optional[str] = None,
|
1995
|
-
limit: Optional[int] = None,
|
1996
|
-
):
|
1997
|
-
"""
|
1998
|
-
Update a block in the agent's core memory by specifying its label
|
1999
|
-
|
2000
|
-
Args:
|
2001
|
-
agent_id (str): The agent ID
|
2002
|
-
label (str): The label of the block
|
2003
|
-
value (str): The new value of the block
|
2004
|
-
limit (int): The new limit of the block
|
2005
|
-
|
2006
|
-
Returns:
|
2007
|
-
block (Block): The updated block
|
2008
|
-
"""
|
2009
|
-
# setup data
|
2010
|
-
data = {}
|
2011
|
-
if value:
|
2012
|
-
data["value"] = value
|
2013
|
-
if limit:
|
2014
|
-
data["limit"] = limit
|
2015
|
-
response = requests.patch(
|
2016
|
-
f"{self.base_url}/{self.api_prefix}/agents/{agent_id}/core-memory/blocks/{label}",
|
2017
|
-
headers=self.headers,
|
2018
|
-
json=data,
|
2019
|
-
)
|
2020
|
-
if response.status_code != 200:
|
2021
|
-
raise ValueError(f"Failed to update agent memory block: {response.text}")
|
2022
|
-
return Block(**response.json())
|
2023
|
-
|
2024
|
-
def update_block(
|
2025
|
-
self,
|
2026
|
-
block_id: str,
|
2027
|
-
label: Optional[str] = None,
|
2028
|
-
value: Optional[str] = None,
|
2029
|
-
limit: Optional[int] = None,
|
2030
|
-
):
|
2031
|
-
"""
|
2032
|
-
Update a block given the ID with the provided fields
|
2033
|
-
|
2034
|
-
Args:
|
2035
|
-
block_id (str): ID of the block
|
2036
|
-
label (str): Label to assign to the block
|
2037
|
-
value (str): Value to assign to the block
|
2038
|
-
limit (int): Token limit to assign to the block
|
2039
|
-
|
2040
|
-
Returns:
|
2041
|
-
block (Block): Updated block
|
2042
|
-
"""
|
2043
|
-
data = {}
|
2044
|
-
if value:
|
2045
|
-
data["value"] = value
|
2046
|
-
if limit:
|
2047
|
-
data["limit"] = limit
|
2048
|
-
if label:
|
2049
|
-
data["label"] = label
|
2050
|
-
response = requests.patch(
|
2051
|
-
f"{self.base_url}/{self.api_prefix}/blocks/{block_id}",
|
2052
|
-
headers=self.headers,
|
2053
|
-
json=data,
|
2054
|
-
)
|
2055
|
-
if response.status_code != 200:
|
2056
|
-
raise ValueError(f"Failed to update block: {response.text}")
|
2057
|
-
return Block(**response.json())
|
2058
|
-
|
2059
|
-
def get_run_messages(
|
2060
|
-
self,
|
2061
|
-
run_id: str,
|
2062
|
-
before: Optional[str] = None,
|
2063
|
-
after: Optional[str] = None,
|
2064
|
-
limit: Optional[int] = 100,
|
2065
|
-
ascending: bool = True,
|
2066
|
-
role: Optional[MessageRole] = None,
|
2067
|
-
) -> List[LettaMessageUnion]:
|
2068
|
-
"""
|
2069
|
-
Get messages associated with a job with filtering options.
|
2070
|
-
|
2071
|
-
Args:
|
2072
|
-
job_id: ID of the job
|
2073
|
-
before: Cursor for pagination
|
2074
|
-
after: Cursor for pagination
|
2075
|
-
limit: Maximum number of messages to return
|
2076
|
-
ascending: Sort order by creation time
|
2077
|
-
role: Filter by message role (user/assistant/system/tool)
|
2078
|
-
Returns:
|
2079
|
-
List of messages matching the filter criteria
|
2080
|
-
"""
|
2081
|
-
params = {
|
2082
|
-
"before": before,
|
2083
|
-
"after": after,
|
2084
|
-
"limit": limit,
|
2085
|
-
"ascending": ascending,
|
2086
|
-
"role": role,
|
2087
|
-
}
|
2088
|
-
# Remove None values
|
2089
|
-
params = {k: v for k, v in params.items() if v is not None}
|
2090
|
-
|
2091
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/runs/{run_id}/messages", params=params)
|
2092
|
-
if response.status_code != 200:
|
2093
|
-
raise ValueError(f"Failed to get run messages: {response.text}")
|
2094
|
-
return [LettaMessage(**message) for message in response.json()]
|
2095
|
-
|
2096
|
-
def get_run_usage(
|
2097
|
-
self,
|
2098
|
-
run_id: str,
|
2099
|
-
) -> List[UsageStatistics]:
|
2100
|
-
"""
|
2101
|
-
Get usage statistics associated with a job.
|
2102
|
-
|
2103
|
-
Args:
|
2104
|
-
job_id (str): ID of the job
|
2105
|
-
|
2106
|
-
Returns:
|
2107
|
-
List[UsageStatistics]: List of usage statistics associated with the job
|
2108
|
-
"""
|
2109
|
-
response = requests.get(
|
2110
|
-
f"{self.base_url}/{self.api_prefix}/runs/{run_id}/usage",
|
2111
|
-
headers=self.headers,
|
2112
|
-
)
|
2113
|
-
if response.status_code != 200:
|
2114
|
-
raise ValueError(f"Failed to get run usage statistics: {response.text}")
|
2115
|
-
return [UsageStatistics(**stat) for stat in [response.json()]]
|
2116
|
-
|
2117
|
-
def get_run(self, run_id: str) -> Run:
|
2118
|
-
"""
|
2119
|
-
Get a run by ID.
|
2120
|
-
|
2121
|
-
Args:
|
2122
|
-
run_id (str): ID of the run
|
2123
|
-
|
2124
|
-
Returns:
|
2125
|
-
run (Run): Run
|
2126
|
-
"""
|
2127
|
-
response = requests.get(
|
2128
|
-
f"{self.base_url}/{self.api_prefix}/runs/{run_id}",
|
2129
|
-
headers=self.headers,
|
2130
|
-
)
|
2131
|
-
if response.status_code != 200:
|
2132
|
-
raise ValueError(f"Failed to get run: {response.text}")
|
2133
|
-
return Run(**response.json())
|
2134
|
-
|
2135
|
-
def delete_run(self, run_id: str) -> None:
|
2136
|
-
"""
|
2137
|
-
Delete a run by ID.
|
2138
|
-
|
2139
|
-
Args:
|
2140
|
-
run_id (str): ID of the run
|
2141
|
-
"""
|
2142
|
-
response = requests.delete(
|
2143
|
-
f"{self.base_url}/{self.api_prefix}/runs/{run_id}",
|
2144
|
-
headers=self.headers,
|
2145
|
-
)
|
2146
|
-
if response.status_code != 200:
|
2147
|
-
raise ValueError(f"Failed to delete run: {response.text}")
|
2148
|
-
|
2149
|
-
def list_runs(self) -> List[Run]:
|
2150
|
-
"""
|
2151
|
-
List all runs.
|
2152
|
-
|
2153
|
-
Returns:
|
2154
|
-
runs (List[Run]): List of runs
|
2155
|
-
"""
|
2156
|
-
response = requests.get(
|
2157
|
-
f"{self.base_url}/{self.api_prefix}/runs",
|
2158
|
-
headers=self.headers,
|
2159
|
-
)
|
2160
|
-
if response.status_code != 200:
|
2161
|
-
raise ValueError(f"Failed to list runs: {response.text}")
|
2162
|
-
return [Run(**run) for run in response.json()]
|
2163
|
-
|
2164
|
-
def list_active_runs(self) -> List[Run]:
|
2165
|
-
"""
|
2166
|
-
List all active runs.
|
2167
|
-
|
2168
|
-
Returns:
|
2169
|
-
runs (List[Run]): List of active runs
|
2170
|
-
"""
|
2171
|
-
response = requests.get(
|
2172
|
-
f"{self.base_url}/{self.api_prefix}/runs/active",
|
2173
|
-
headers=self.headers,
|
2174
|
-
)
|
2175
|
-
if response.status_code != 200:
|
2176
|
-
raise ValueError(f"Failed to list active runs: {response.text}")
|
2177
|
-
return [Run(**run) for run in response.json()]
|
2178
|
-
|
2179
|
-
def get_tags(
|
2180
|
-
self,
|
2181
|
-
after: Optional[str] = None,
|
2182
|
-
limit: int = 100,
|
2183
|
-
query_text: Optional[str] = None,
|
2184
|
-
) -> List[str]:
|
2185
|
-
"""
|
2186
|
-
Get a list of all unique tags.
|
2187
|
-
|
2188
|
-
Args:
|
2189
|
-
after: Optional cursor for pagination (first tag seen)
|
2190
|
-
limit: Optional maximum number of tags to return
|
2191
|
-
query_text: Optional text to filter tags
|
2192
|
-
|
2193
|
-
Returns:
|
2194
|
-
List[str]: List of unique tags
|
2195
|
-
"""
|
2196
|
-
params = {}
|
2197
|
-
if after:
|
2198
|
-
params["after"] = after
|
2199
|
-
if limit:
|
2200
|
-
params["limit"] = limit
|
2201
|
-
if query_text:
|
2202
|
-
params["query_text"] = query_text
|
2203
|
-
|
2204
|
-
response = requests.get(f"{self.base_url}/{self.api_prefix}/tags", headers=self.headers, params=params)
|
2205
|
-
if response.status_code != 200:
|
2206
|
-
raise ValueError(f"Failed to get tags: {response.text}")
|
2207
|
-
return response.json()
|