letta-nightly 0.5.5.dev20241122170833__py3-none-any.whl → 0.6.0.dev20241204051808__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of letta-nightly might be problematic. Click here for more details.

Files changed (70) hide show
  1. letta/__init__.py +2 -2
  2. letta/agent.py +155 -166
  3. letta/agent_store/chroma.py +2 -0
  4. letta/agent_store/db.py +1 -1
  5. letta/cli/cli.py +12 -8
  6. letta/cli/cli_config.py +1 -1
  7. letta/client/client.py +765 -137
  8. letta/config.py +2 -2
  9. letta/constants.py +10 -14
  10. letta/errors.py +12 -0
  11. letta/functions/function_sets/base.py +38 -1
  12. letta/functions/functions.py +40 -57
  13. letta/functions/helpers.py +0 -4
  14. letta/functions/schema_generator.py +279 -18
  15. letta/helpers/tool_rule_solver.py +6 -5
  16. letta/llm_api/helpers.py +99 -5
  17. letta/llm_api/openai.py +8 -2
  18. letta/local_llm/utils.py +13 -6
  19. letta/log.py +7 -9
  20. letta/main.py +1 -1
  21. letta/metadata.py +53 -38
  22. letta/o1_agent.py +1 -4
  23. letta/orm/__init__.py +2 -0
  24. letta/orm/block.py +7 -3
  25. letta/orm/blocks_agents.py +32 -0
  26. letta/orm/errors.py +8 -0
  27. letta/orm/mixins.py +8 -0
  28. letta/orm/organization.py +8 -1
  29. letta/orm/sandbox_config.py +56 -0
  30. letta/orm/sqlalchemy_base.py +68 -10
  31. letta/persistence_manager.py +1 -0
  32. letta/schemas/agent.py +57 -52
  33. letta/schemas/block.py +85 -26
  34. letta/schemas/blocks_agents.py +32 -0
  35. letta/schemas/enums.py +14 -0
  36. letta/schemas/letta_base.py +10 -1
  37. letta/schemas/letta_request.py +11 -23
  38. letta/schemas/letta_response.py +1 -2
  39. letta/schemas/memory.py +41 -76
  40. letta/schemas/message.py +3 -3
  41. letta/schemas/sandbox_config.py +114 -0
  42. letta/schemas/tool.py +37 -1
  43. letta/schemas/tool_rule.py +13 -5
  44. letta/server/rest_api/app.py +5 -4
  45. letta/server/rest_api/interface.py +12 -19
  46. letta/server/rest_api/routers/openai/assistants/threads.py +2 -3
  47. letta/server/rest_api/routers/openai/chat_completions/chat_completions.py +0 -2
  48. letta/server/rest_api/routers/v1/__init__.py +4 -9
  49. letta/server/rest_api/routers/v1/agents.py +145 -61
  50. letta/server/rest_api/routers/v1/blocks.py +50 -5
  51. letta/server/rest_api/routers/v1/sandbox_configs.py +127 -0
  52. letta/server/rest_api/routers/v1/sources.py +8 -1
  53. letta/server/rest_api/routers/v1/tools.py +139 -13
  54. letta/server/rest_api/utils.py +6 -0
  55. letta/server/server.py +397 -340
  56. letta/server/static_files/assets/index-9fa459a2.js +1 -1
  57. letta/services/block_manager.py +23 -2
  58. letta/services/blocks_agents_manager.py +106 -0
  59. letta/services/per_agent_lock_manager.py +18 -0
  60. letta/services/sandbox_config_manager.py +256 -0
  61. letta/services/tool_execution_sandbox.py +352 -0
  62. letta/services/tool_manager.py +16 -22
  63. letta/services/tool_sandbox_env/.gitkeep +0 -0
  64. letta/settings.py +4 -0
  65. letta/utils.py +0 -7
  66. {letta_nightly-0.5.5.dev20241122170833.dist-info → letta_nightly-0.6.0.dev20241204051808.dist-info}/METADATA +8 -6
  67. {letta_nightly-0.5.5.dev20241122170833.dist-info → letta_nightly-0.6.0.dev20241204051808.dist-info}/RECORD +70 -60
  68. {letta_nightly-0.5.5.dev20241122170833.dist-info → letta_nightly-0.6.0.dev20241204051808.dist-info}/LICENSE +0 -0
  69. {letta_nightly-0.5.5.dev20241122170833.dist-info → letta_nightly-0.6.0.dev20241204051808.dist-info}/WHEEL +0 -0
  70. {letta_nightly-0.5.5.dev20241122170833.dist-info → letta_nightly-0.6.0.dev20241204051808.dist-info}/entry_points.txt +0 -0
@@ -1,9 +1,12 @@
1
1
  from typing import List, Optional
2
2
 
3
+ from composio.client.collections import ActionModel, AppModel
3
4
  from fastapi import APIRouter, Body, Depends, Header, HTTPException
4
5
 
5
- from letta.orm.errors import NoResultFound
6
- from letta.schemas.tool import Tool, ToolCreate, ToolUpdate
6
+ from letta.errors import LettaToolCreateError
7
+ from letta.orm.errors import UniqueConstraintViolationError
8
+ from letta.schemas.letta_message import FunctionReturn
9
+ from letta.schemas.tool import Tool, ToolCreate, ToolRunFromSource, ToolUpdate
7
10
  from letta.server.rest_api.utils import get_letta_server
8
11
  from letta.server.server import SyncServer
9
12
 
@@ -14,12 +17,13 @@ router = APIRouter(prefix="/tools", tags=["tools"])
14
17
  def delete_tool(
15
18
  tool_id: str,
16
19
  server: SyncServer = Depends(get_letta_server),
20
+ user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
17
21
  ):
18
22
  """
19
23
  Delete a tool by name
20
24
  """
21
- # actor = server.get_user_or_default(user_id=user_id)
22
- server.tool_manager.delete_tool(tool_id=tool_id)
25
+ actor = server.get_user_or_default(user_id=user_id)
26
+ server.tool_manager.delete_tool_by_id(tool_id=tool_id, actor=actor)
23
27
 
24
28
 
25
29
  @router.get("/{tool_id}", response_model=Tool, operation_id="get_tool")
@@ -49,11 +53,10 @@ def get_tool_id(
49
53
  Get a tool ID by name
50
54
  """
51
55
  actor = server.get_user_or_default(user_id=user_id)
52
-
53
- try:
54
- tool = server.tool_manager.get_tool_by_name(tool_name=tool_name, actor=actor)
56
+ tool = server.tool_manager.get_tool_by_name(tool_name=tool_name, actor=actor)
57
+ if tool:
55
58
  return tool.id
56
- except NoResultFound:
59
+ else:
57
60
  raise HTTPException(status_code=404, detail=f"Tool with name {tool_name} and organization id {actor.organization_id} not found.")
58
61
 
59
62
 
@@ -85,12 +88,50 @@ def create_tool(
85
88
  """
86
89
  Create a new tool
87
90
  """
88
- # Derive user and org id from actor
89
- actor = server.get_user_or_default(user_id=user_id)
91
+ try:
92
+ actor = server.get_user_or_default(user_id=user_id)
93
+ tool = Tool(**request.model_dump())
94
+ return server.tool_manager.create_tool(pydantic_tool=tool, actor=actor)
95
+ except UniqueConstraintViolationError as e:
96
+ # Log or print the full exception here for debugging
97
+ print(f"Error occurred: {e}")
98
+ clean_error_message = f"Tool with name {request.name} already exists."
99
+ raise HTTPException(status_code=409, detail=clean_error_message)
100
+ except LettaToolCreateError as e:
101
+ # HTTP 400 == Bad Request
102
+ print(f"Error occurred during tool creation: {e}")
103
+ # print the full stack trace
104
+ import traceback
105
+
106
+ print(traceback.format_exc())
107
+ raise HTTPException(status_code=400, detail=str(e))
108
+ except Exception as e:
109
+ # Catch other unexpected errors and raise an internal server error
110
+ print(f"Unexpected error occurred: {e}")
111
+ raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}")
90
112
 
91
- # Send request to create the tool
92
- tool = Tool(**request.model_dump())
93
- return server.tool_manager.create_or_update_tool(pydantic_tool=tool, actor=actor)
113
+
114
+ @router.put("/", response_model=Tool, operation_id="upsert_tool")
115
+ def upsert_tool(
116
+ request: ToolCreate = Body(...),
117
+ server: SyncServer = Depends(get_letta_server),
118
+ user_id: Optional[str] = Header(None, alias="user_id"),
119
+ ):
120
+ """
121
+ Create or update a tool
122
+ """
123
+ try:
124
+ actor = server.get_user_or_default(user_id=user_id)
125
+ tool = server.tool_manager.create_or_update_tool(pydantic_tool=Tool(**request.model_dump()), actor=actor)
126
+ return tool
127
+ except UniqueConstraintViolationError as e:
128
+ # Log the error and raise a conflict exception
129
+ print(f"Unique constraint violation occurred: {e}")
130
+ raise HTTPException(status_code=409, detail=str(e))
131
+ except Exception as e:
132
+ # Catch other unexpected errors and raise an internal server error
133
+ print(f"Unexpected error occurred: {e}")
134
+ raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}")
94
135
 
95
136
 
96
137
  @router.patch("/{tool_id}", response_model=Tool, operation_id="update_tool")
@@ -117,3 +158,88 @@ def add_base_tools(
117
158
  """
118
159
  actor = server.get_user_or_default(user_id=user_id)
119
160
  return server.tool_manager.add_base_tools(actor=actor)
161
+
162
+
163
+ # NOTE: can re-enable if needed
164
+ # @router.post("/{tool_id}/run", response_model=FunctionReturn, operation_id="run_tool")
165
+ # def run_tool(
166
+ # server: SyncServer = Depends(get_letta_server),
167
+ # request: ToolRun = Body(...),
168
+ # user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
169
+ # ):
170
+ # """
171
+ # Run an existing tool on provided arguments
172
+ # """
173
+ # actor = server.get_user_or_default(user_id=user_id)
174
+
175
+ # return server.run_tool(tool_id=request.tool_id, tool_args=request.tool_args, user_id=actor.id)
176
+
177
+
178
+ @router.post("/run", response_model=FunctionReturn, operation_id="run_tool_from_source")
179
+ def run_tool_from_source(
180
+ server: SyncServer = Depends(get_letta_server),
181
+ request: ToolRunFromSource = Body(...),
182
+ user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
183
+ ):
184
+ """
185
+ Attempt to build a tool from source, then run it on the provided arguments
186
+ """
187
+ actor = server.get_user_or_default(user_id=user_id)
188
+
189
+ try:
190
+ return server.run_tool_from_source(
191
+ tool_source=request.source_code,
192
+ tool_source_type=request.source_type,
193
+ tool_args=request.args,
194
+ tool_name=request.name,
195
+ user_id=actor.id,
196
+ )
197
+ except LettaToolCreateError as e:
198
+ # HTTP 400 == Bad Request
199
+ print(f"Error occurred during tool creation: {e}")
200
+ # print the full stack trace
201
+ import traceback
202
+
203
+ print(traceback.format_exc())
204
+ raise HTTPException(status_code=400, detail=str(e))
205
+
206
+ except Exception as e:
207
+ # Catch other unexpected errors and raise an internal server error
208
+ print(f"Unexpected error occurred: {e}")
209
+ raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}")
210
+
211
+
212
+ # Specific routes for Composio
213
+
214
+
215
+ @router.get("/composio/apps", response_model=List[AppModel], operation_id="list_composio_apps")
216
+ def list_composio_apps(server: SyncServer = Depends(get_letta_server)):
217
+ """
218
+ Get a list of all Composio apps
219
+ """
220
+ return server.get_composio_apps()
221
+
222
+
223
+ @router.get("/composio/apps/{composio_app_name}/actions", response_model=List[ActionModel], operation_id="list_composio_actions_by_app")
224
+ def list_composio_actions_by_app(
225
+ composio_app_name: str,
226
+ server: SyncServer = Depends(get_letta_server),
227
+ ):
228
+ """
229
+ Get a list of all Composio actions for a specific app
230
+ """
231
+ return server.get_composio_actions_from_app_name(composio_app_name=composio_app_name)
232
+
233
+
234
+ @router.post("/composio/{composio_action_name}", response_model=Tool, operation_id="add_composio_tool")
235
+ def add_composio_tool(
236
+ composio_action_name: str,
237
+ server: SyncServer = Depends(get_letta_server),
238
+ user_id: Optional[str] = Header(None, alias="user_id"),
239
+ ):
240
+ """
241
+ Add a new Composio tool by action name (Composio refers to each tool as an `Action`)
242
+ """
243
+ actor = server.get_user_or_default(user_id=user_id)
244
+ tool_create = ToolCreate.from_composio(action=composio_action_name)
245
+ return server.tool_manager.create_or_update_tool(pydantic_tool=Tool(**tool_create.model_dump()), actor=actor)
@@ -5,6 +5,7 @@ import warnings
5
5
  from enum import Enum
6
6
  from typing import AsyncGenerator, Optional, Union
7
7
 
8
+ from fastapi import Header
8
9
  from pydantic import BaseModel
9
10
 
10
11
  from letta.schemas.usage import LettaUsageStatistics
@@ -84,5 +85,10 @@ def get_letta_server() -> SyncServer:
84
85
  return server
85
86
 
86
87
 
88
+ # Dependency to get user_id from headers
89
+ def get_user_id(user_id: Optional[str] = Header(None, alias="user_id")) -> Optional[str]:
90
+ return user_id
91
+
92
+
87
93
  def get_current_interface() -> StreamingServerInterface:
88
94
  return StreamingServerInterface