letta-nightly 0.8.15.dev20250719104256__py3-none-any.whl → 0.8.16.dev20250721070720__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 -1
- letta/agent.py +27 -11
- letta/agents/helpers.py +1 -1
- letta/agents/letta_agent.py +518 -322
- letta/agents/letta_agent_batch.py +1 -2
- letta/agents/voice_agent.py +15 -17
- letta/client/client.py +3 -3
- letta/constants.py +5 -0
- letta/embeddings.py +0 -2
- letta/errors.py +8 -0
- letta/functions/function_sets/base.py +3 -3
- letta/functions/helpers.py +2 -3
- letta/groups/sleeptime_multi_agent.py +0 -1
- letta/helpers/composio_helpers.py +2 -2
- letta/helpers/converters.py +1 -1
- letta/helpers/pinecone_utils.py +8 -0
- letta/helpers/tool_rule_solver.py +13 -18
- letta/llm_api/aws_bedrock.py +16 -2
- letta/llm_api/cohere.py +1 -1
- letta/llm_api/openai_client.py +1 -1
- letta/local_llm/grammars/gbnf_grammar_generator.py +1 -1
- letta/local_llm/llm_chat_completion_wrappers/zephyr.py +14 -14
- letta/local_llm/utils.py +1 -2
- letta/orm/agent.py +3 -3
- letta/orm/block.py +4 -4
- letta/orm/files_agents.py +0 -1
- letta/orm/identity.py +2 -0
- letta/orm/mcp_server.py +0 -2
- letta/orm/message.py +140 -14
- letta/orm/organization.py +5 -5
- letta/orm/passage.py +4 -4
- letta/orm/source.py +1 -1
- letta/orm/sqlalchemy_base.py +61 -39
- letta/orm/step.py +2 -0
- letta/otel/db_pool_monitoring.py +308 -0
- letta/otel/metric_registry.py +94 -1
- letta/otel/sqlalchemy_instrumentation.py +548 -0
- letta/otel/sqlalchemy_instrumentation_integration.py +124 -0
- letta/otel/tracing.py +37 -1
- letta/schemas/agent.py +0 -3
- letta/schemas/agent_file.py +283 -0
- letta/schemas/block.py +0 -3
- letta/schemas/file.py +28 -26
- letta/schemas/letta_message.py +15 -4
- letta/schemas/memory.py +1 -1
- letta/schemas/message.py +31 -26
- letta/schemas/openai/chat_completion_response.py +0 -1
- letta/schemas/providers.py +20 -0
- letta/schemas/source.py +11 -13
- letta/schemas/step.py +12 -0
- letta/schemas/tool.py +0 -4
- letta/serialize_schemas/marshmallow_agent.py +14 -1
- letta/serialize_schemas/marshmallow_block.py +23 -1
- letta/serialize_schemas/marshmallow_message.py +1 -3
- letta/serialize_schemas/marshmallow_tool.py +23 -1
- letta/server/db.py +110 -6
- letta/server/rest_api/app.py +85 -73
- letta/server/rest_api/routers/v1/agents.py +68 -53
- letta/server/rest_api/routers/v1/blocks.py +2 -2
- letta/server/rest_api/routers/v1/jobs.py +3 -0
- letta/server/rest_api/routers/v1/organizations.py +2 -2
- letta/server/rest_api/routers/v1/sources.py +18 -2
- letta/server/rest_api/routers/v1/tools.py +11 -12
- letta/server/rest_api/routers/v1/users.py +1 -1
- letta/server/rest_api/streaming_response.py +13 -5
- letta/server/rest_api/utils.py +8 -25
- letta/server/server.py +11 -4
- letta/server/ws_api/server.py +2 -2
- letta/services/agent_file_manager.py +616 -0
- letta/services/agent_manager.py +133 -46
- letta/services/block_manager.py +38 -17
- letta/services/file_manager.py +106 -21
- letta/services/file_processor/file_processor.py +93 -0
- letta/services/files_agents_manager.py +28 -0
- letta/services/group_manager.py +4 -5
- letta/services/helpers/agent_manager_helper.py +57 -9
- letta/services/identity_manager.py +22 -0
- letta/services/job_manager.py +210 -91
- letta/services/llm_batch_manager.py +9 -6
- letta/services/mcp/stdio_client.py +1 -2
- letta/services/mcp_manager.py +0 -1
- letta/services/message_manager.py +49 -26
- letta/services/passage_manager.py +0 -1
- letta/services/provider_manager.py +1 -1
- letta/services/source_manager.py +114 -5
- letta/services/step_manager.py +36 -4
- letta/services/telemetry_manager.py +9 -2
- letta/services/tool_executor/builtin_tool_executor.py +5 -1
- letta/services/tool_executor/core_tool_executor.py +3 -3
- letta/services/tool_manager.py +95 -20
- letta/services/user_manager.py +4 -12
- letta/settings.py +23 -6
- letta/system.py +1 -1
- letta/utils.py +26 -2
- {letta_nightly-0.8.15.dev20250719104256.dist-info → letta_nightly-0.8.16.dev20250721070720.dist-info}/METADATA +3 -2
- {letta_nightly-0.8.15.dev20250719104256.dist-info → letta_nightly-0.8.16.dev20250721070720.dist-info}/RECORD +99 -94
- {letta_nightly-0.8.15.dev20250719104256.dist-info → letta_nightly-0.8.16.dev20250721070720.dist-info}/LICENSE +0 -0
- {letta_nightly-0.8.15.dev20250719104256.dist-info → letta_nightly-0.8.16.dev20250721070720.dist-info}/WHEEL +0 -0
- {letta_nightly-0.8.15.dev20250719104256.dist-info → letta_nightly-0.8.16.dev20250721070720.dist-info}/entry_points.txt +0 -0
@@ -20,11 +20,12 @@ from letta.helpers.composio_helpers import get_composio_api_key
|
|
20
20
|
from letta.log import get_logger
|
21
21
|
from letta.orm.errors import UniqueConstraintViolationError
|
22
22
|
from letta.schemas.letta_message import ToolReturnMessage
|
23
|
-
from letta.schemas.mcp import UpdateSSEMCPServer, UpdateStreamableHTTPMCPServer
|
23
|
+
from letta.schemas.mcp import UpdateSSEMCPServer, UpdateStdioMCPServer, UpdateStreamableHTTPMCPServer
|
24
24
|
from letta.schemas.tool import Tool, ToolCreate, ToolRunFromSource, ToolUpdate
|
25
25
|
from letta.server.rest_api.utils import get_letta_server
|
26
26
|
from letta.server.server import SyncServer
|
27
27
|
from letta.services.mcp.sse_client import AsyncSSEMCPClient
|
28
|
+
from letta.services.mcp.stdio_client import AsyncStdioMCPClient
|
28
29
|
from letta.services.mcp.streamable_http_client import AsyncStreamableHTTPMCPClient
|
29
30
|
from letta.settings import tool_settings
|
30
31
|
|
@@ -136,7 +137,7 @@ async def create_tool(
|
|
136
137
|
except UniqueConstraintViolationError as e:
|
137
138
|
# Log or print the full exception here for debugging
|
138
139
|
print(f"Error occurred: {e}")
|
139
|
-
clean_error_message =
|
140
|
+
clean_error_message = "Tool with this name already exists."
|
140
141
|
raise HTTPException(status_code=409, detail=clean_error_message)
|
141
142
|
except LettaToolCreateError as e:
|
142
143
|
# HTTP 400 == Bad Request
|
@@ -234,6 +235,7 @@ async def run_tool_from_source(
|
|
234
235
|
tool_name=request.name,
|
235
236
|
tool_args_json_schema=request.args_json_schema,
|
236
237
|
tool_json_schema=request.json_schema,
|
238
|
+
pip_requirements=request.pip_requirements,
|
237
239
|
actor=actor,
|
238
240
|
)
|
239
241
|
except LettaToolCreateError as e:
|
@@ -262,7 +264,7 @@ def list_composio_apps(server: SyncServer = Depends(get_letta_server), user_id:
|
|
262
264
|
if not composio_api_key:
|
263
265
|
raise HTTPException(
|
264
266
|
status_code=400, # Bad Request
|
265
|
-
detail=
|
267
|
+
detail="No API keys found for Composio. Please add your Composio API Key as an environment variable for your sandbox configuration, or set it as environment variable COMPOSIO_API_KEY.",
|
266
268
|
)
|
267
269
|
return server.get_composio_apps(api_key=composio_api_key)
|
268
270
|
|
@@ -281,7 +283,7 @@ def list_composio_actions_by_app(
|
|
281
283
|
if not composio_api_key:
|
282
284
|
raise HTTPException(
|
283
285
|
status_code=400, # Bad Request
|
284
|
-
detail=
|
286
|
+
detail="No API keys found for Composio. Please add your Composio API Key as an environment variable for your sandbox configuration, or set it as environment variable COMPOSIO_API_KEY.",
|
285
287
|
)
|
286
288
|
return server.get_composio_actions_from_app_name(composio_app_name=composio_app_name, api_key=composio_api_key)
|
287
289
|
|
@@ -444,7 +446,6 @@ async def add_mcp_tool(
|
|
444
446
|
actor = server.user_manager.get_user_or_default(user_id=actor_id)
|
445
447
|
|
446
448
|
if tool_settings.mcp_read_from_config:
|
447
|
-
|
448
449
|
try:
|
449
450
|
available_tools = await server.get_tools_from_mcp_server(mcp_server_name=mcp_server_name)
|
450
451
|
except ValueError as e:
|
@@ -565,7 +566,7 @@ async def add_mcp_server_to_config(
|
|
565
566
|
)
|
566
567
|
async def update_mcp_server(
|
567
568
|
mcp_server_name: str,
|
568
|
-
request: Union[UpdateSSEMCPServer, UpdateStreamableHTTPMCPServer] = Body(...),
|
569
|
+
request: Union[UpdateStdioMCPServer, UpdateSSEMCPServer, UpdateStreamableHTTPMCPServer] = Body(...),
|
569
570
|
server: SyncServer = Depends(get_letta_server),
|
570
571
|
actor_id: Optional[str] = Header(None, alias="user_id"),
|
571
572
|
):
|
@@ -627,12 +628,6 @@ async def test_mcp_server(
|
|
627
628
|
"""
|
628
629
|
client = None
|
629
630
|
try:
|
630
|
-
if isinstance(request, StdioServerConfig):
|
631
|
-
raise HTTPException(
|
632
|
-
status_code=400,
|
633
|
-
detail="stdio is not supported currently for testing connection",
|
634
|
-
)
|
635
|
-
|
636
631
|
# create a temporary MCP client based on the server type
|
637
632
|
if request.type == MCPServerType.SSE:
|
638
633
|
if not isinstance(request, SSEServerConfig):
|
@@ -642,6 +637,10 @@ async def test_mcp_server(
|
|
642
637
|
if not isinstance(request, StreamableHTTPServerConfig):
|
643
638
|
request = StreamableHTTPServerConfig(**request.model_dump())
|
644
639
|
client = AsyncStreamableHTTPMCPClient(request)
|
640
|
+
elif request.type == MCPServerType.STDIO:
|
641
|
+
if not isinstance(request, StdioServerConfig):
|
642
|
+
request = StdioServerConfig(**request.model_dump())
|
643
|
+
client = AsyncStdioMCPClient(request)
|
645
644
|
else:
|
646
645
|
raise ValueError(f"Invalid MCP server type: {request.type}")
|
647
646
|
|
@@ -65,7 +65,7 @@ async def delete_user(
|
|
65
65
|
try:
|
66
66
|
user = await server.user_manager.get_actor_by_id_async(actor_id=user_id)
|
67
67
|
if user is None:
|
68
|
-
raise HTTPException(status_code=404, detail=
|
68
|
+
raise HTTPException(status_code=404, detail="User does not exist")
|
69
69
|
await server.user_manager.delete_actor_by_id_async(user_id=user_id)
|
70
70
|
except HTTPException:
|
71
71
|
raise
|
@@ -12,6 +12,7 @@ from starlette.types import Send
|
|
12
12
|
from letta.log import get_logger
|
13
13
|
from letta.schemas.enums import JobStatus
|
14
14
|
from letta.schemas.user import User
|
15
|
+
from letta.server.rest_api.utils import capture_sentry_exception
|
15
16
|
from letta.services.job_manager import JobManager
|
16
17
|
|
17
18
|
logger = get_logger(__name__)
|
@@ -92,6 +93,7 @@ class StreamingResponseWithStatusCode(StreamingResponse):
|
|
92
93
|
more_body = True
|
93
94
|
try:
|
94
95
|
first_chunk = await self.body_iterator.__anext__()
|
96
|
+
logger.debug("stream_response first chunk:", first_chunk)
|
95
97
|
if isinstance(first_chunk, tuple):
|
96
98
|
first_chunk_content, self.status_code = first_chunk
|
97
99
|
else:
|
@@ -130,7 +132,7 @@ class StreamingResponseWithStatusCode(StreamingResponse):
|
|
130
132
|
"more_body": more_body,
|
131
133
|
}
|
132
134
|
)
|
133
|
-
|
135
|
+
raise Exception(f"An exception occurred mid-stream with status code {status_code}", detail={"content": content})
|
134
136
|
else:
|
135
137
|
content = chunk
|
136
138
|
|
@@ -146,8 +148,8 @@ class StreamingResponseWithStatusCode(StreamingResponse):
|
|
146
148
|
)
|
147
149
|
|
148
150
|
# This should be handled properly upstream?
|
149
|
-
except asyncio.CancelledError:
|
150
|
-
logger.
|
151
|
+
except asyncio.CancelledError as exc:
|
152
|
+
logger.warning("Stream was cancelled by client or job cancellation")
|
151
153
|
# Handle cancellation gracefully
|
152
154
|
more_body = False
|
153
155
|
cancellation_resp = {"error": {"message": "Stream cancelled"}}
|
@@ -160,6 +162,7 @@ class StreamingResponseWithStatusCode(StreamingResponse):
|
|
160
162
|
"headers": self.raw_headers,
|
161
163
|
}
|
162
164
|
)
|
165
|
+
raise
|
163
166
|
await send(
|
164
167
|
{
|
165
168
|
"type": "http.response.body",
|
@@ -167,13 +170,15 @@ class StreamingResponseWithStatusCode(StreamingResponse):
|
|
167
170
|
"more_body": more_body,
|
168
171
|
}
|
169
172
|
)
|
173
|
+
capture_sentry_exception(exc)
|
170
174
|
return
|
171
175
|
|
172
|
-
except Exception:
|
173
|
-
logger.exception("
|
176
|
+
except Exception as exc:
|
177
|
+
logger.exception("Unhandled Streaming Error")
|
174
178
|
more_body = False
|
175
179
|
error_resp = {"error": {"message": "Internal Server Error"}}
|
176
180
|
error_event = f"event: error\ndata: {json.dumps(error_resp)}\n\n".encode(self.charset)
|
181
|
+
logger.debug("response_started:", self.response_started)
|
177
182
|
if not self.response_started:
|
178
183
|
await send(
|
179
184
|
{
|
@@ -182,6 +187,7 @@ class StreamingResponseWithStatusCode(StreamingResponse):
|
|
182
187
|
"headers": self.raw_headers,
|
183
188
|
}
|
184
189
|
)
|
190
|
+
raise
|
185
191
|
await send(
|
186
192
|
{
|
187
193
|
"type": "http.response.body",
|
@@ -189,5 +195,7 @@ class StreamingResponseWithStatusCode(StreamingResponse):
|
|
189
195
|
"more_body": more_body,
|
190
196
|
}
|
191
197
|
)
|
198
|
+
capture_sentry_exception(exc)
|
199
|
+
return
|
192
200
|
if more_body:
|
193
201
|
await send({"type": "http.response.body", "body": b"", "more_body": False})
|
letta/server/rest_api/utils.py
CHANGED
@@ -2,7 +2,6 @@ import asyncio
|
|
2
2
|
import json
|
3
3
|
import os
|
4
4
|
import uuid
|
5
|
-
import warnings
|
6
5
|
from enum import Enum
|
7
6
|
from typing import TYPE_CHECKING, AsyncGenerator, Dict, Iterable, List, Optional, Union, cast
|
8
7
|
|
@@ -34,12 +33,15 @@ from letta.schemas.message import Message, MessageCreate, ToolReturn
|
|
34
33
|
from letta.schemas.tool_execution_result import ToolExecutionResult
|
35
34
|
from letta.schemas.usage import LettaUsageStatistics
|
36
35
|
from letta.schemas.user import User
|
37
|
-
from letta.server.rest_api.interface import StreamingServerInterface
|
38
36
|
from letta.system import get_heartbeat, package_function_response
|
39
37
|
|
40
38
|
if TYPE_CHECKING:
|
41
39
|
from letta.server.server import SyncServer
|
42
40
|
|
41
|
+
SENTRY_ENABLED = bool(os.getenv("SENTRY_DSN"))
|
42
|
+
|
43
|
+
if SENTRY_ENABLED:
|
44
|
+
import sentry_sdk
|
43
45
|
|
44
46
|
SSE_PREFIX = "data: "
|
45
47
|
SSE_SUFFIX = "\n\n"
|
@@ -130,7 +132,7 @@ async def sse_async_generator(
|
|
130
132
|
except Exception as e:
|
131
133
|
log_error_to_sentry(e)
|
132
134
|
logger.error(f"Caught unexpected Exception: {e}")
|
133
|
-
yield sse_formatter({"error":
|
135
|
+
yield sse_formatter({"error": "Stream failed (internal error occurred)"})
|
134
136
|
|
135
137
|
except Exception as e:
|
136
138
|
log_error_to_sentry(e)
|
@@ -157,21 +159,9 @@ def get_user_id(user_id: Optional[str] = Header(None, alias="user_id")) -> Optio
|
|
157
159
|
return user_id
|
158
160
|
|
159
161
|
|
160
|
-
def
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
def log_error_to_sentry(e):
|
165
|
-
import traceback
|
166
|
-
|
167
|
-
traceback.print_exc()
|
168
|
-
warnings.warn(f"SSE stream generator failed: {e}")
|
169
|
-
|
170
|
-
# Log the error, since the exception handler upstack (in FastAPI) won't catch it, because this may be a 200 response
|
171
|
-
# Print the stack trace
|
172
|
-
if (os.getenv("SENTRY_DSN") is not None) and (os.getenv("SENTRY_DSN") != ""):
|
173
|
-
import sentry_sdk
|
174
|
-
|
162
|
+
def capture_sentry_exception(e: BaseException):
|
163
|
+
"""This will capture the exception in sentry, since the exception handler upstack (in FastAPI) won't catch it, because this may be a 200 response"""
|
164
|
+
if SENTRY_ENABLED:
|
175
165
|
sentry_sdk.capture_exception(e)
|
176
166
|
|
177
167
|
|
@@ -184,8 +174,6 @@ def create_input_messages(input_messages: List[MessageCreate], agent_id: str, ti
|
|
184
174
|
"""
|
185
175
|
|
186
176
|
messages = convert_message_creates_to_messages(input_messages, agent_id, timezone, wrap_user_message=False, wrap_system_message=False)
|
187
|
-
for message in messages:
|
188
|
-
message.organization_id = actor.organization_id
|
189
177
|
return messages
|
190
178
|
|
191
179
|
|
@@ -224,7 +212,6 @@ def create_letta_messages_from_llm_response(
|
|
224
212
|
assistant_message = Message(
|
225
213
|
role=MessageRole.assistant,
|
226
214
|
content=reasoning_content if reasoning_content else [],
|
227
|
-
organization_id=actor.organization_id,
|
228
215
|
agent_id=agent_id,
|
229
216
|
model=model,
|
230
217
|
tool_calls=[tool_call],
|
@@ -241,7 +228,6 @@ def create_letta_messages_from_llm_response(
|
|
241
228
|
tool_message = Message(
|
242
229
|
role=MessageRole.tool,
|
243
230
|
content=[TextContent(text=package_function_response(function_call_success, function_response, timezone))],
|
244
|
-
organization_id=actor.organization_id,
|
245
231
|
agent_id=agent_id,
|
246
232
|
model=model,
|
247
233
|
tool_calls=[],
|
@@ -294,7 +280,6 @@ def create_heartbeat_system_message(
|
|
294
280
|
heartbeat_system_message = Message(
|
295
281
|
role=MessageRole.user,
|
296
282
|
content=[TextContent(text=get_heartbeat(timezone, text_content))],
|
297
|
-
organization_id=actor.organization_id,
|
298
283
|
agent_id=agent_id,
|
299
284
|
model=model,
|
300
285
|
tool_calls=[],
|
@@ -370,7 +355,6 @@ def convert_in_context_letta_messages_to_openai(in_context_messages: List[Messag
|
|
370
355
|
id=msg.id,
|
371
356
|
role=msg.role,
|
372
357
|
content=[TextContent(text=extracted_text)],
|
373
|
-
organization_id=msg.organization_id,
|
374
358
|
agent_id=msg.agent_id,
|
375
359
|
model=msg.model,
|
376
360
|
name=msg.name,
|
@@ -399,7 +383,6 @@ def convert_in_context_letta_messages_to_openai(in_context_messages: List[Messag
|
|
399
383
|
id=msg.id,
|
400
384
|
role=msg.role,
|
401
385
|
content=[TextContent(text=actual_user_text)],
|
402
|
-
organization_id=msg.organization_id,
|
403
386
|
agent_id=msg.agent_id,
|
404
387
|
model=msg.model,
|
405
388
|
name=msg.name,
|
letta/server/server.py
CHANGED
@@ -53,6 +53,7 @@ from letta.schemas.llm_config import LLMConfig
|
|
53
53
|
from letta.schemas.memory import ArchivalMemorySummary, Memory, RecallMemorySummary
|
54
54
|
from letta.schemas.message import Message, MessageCreate, MessageUpdate
|
55
55
|
from letta.schemas.passage import Passage, PassageUpdate
|
56
|
+
from letta.schemas.pip_requirement import PipRequirement
|
56
57
|
from letta.schemas.providers import (
|
57
58
|
AnthropicProvider,
|
58
59
|
AzureProvider,
|
@@ -104,7 +105,8 @@ from letta.services.telemetry_manager import TelemetryManager
|
|
104
105
|
from letta.services.tool_executor.tool_execution_manager import ToolExecutionManager
|
105
106
|
from letta.services.tool_manager import ToolManager
|
106
107
|
from letta.services.user_manager import UserManager
|
107
|
-
from letta.settings import model_settings, settings, tool_settings
|
108
|
+
from letta.settings import DatabaseChoice, model_settings, settings, tool_settings
|
109
|
+
from letta.streaming_interface import AgentChunkStreamingInterface
|
108
110
|
from letta.utils import get_friendly_error_msg, get_persona_text, make_key
|
109
111
|
|
110
112
|
config = LettaConfig.load()
|
@@ -176,7 +178,7 @@ class SyncServer(Server):
|
|
176
178
|
self,
|
177
179
|
chaining: bool = True,
|
178
180
|
max_chaining_steps: Optional[int] = 100,
|
179
|
-
default_interface_factory: Callable[[],
|
181
|
+
default_interface_factory: Callable[[], AgentChunkStreamingInterface] = lambda: CLIInterface(),
|
180
182
|
init_with_default_org_and_user: bool = True,
|
181
183
|
# default_interface: AgentInterface = CLIInterface(),
|
182
184
|
# default_persistence_manager_cls: PersistenceManager = LocalStateManager,
|
@@ -195,7 +197,7 @@ class SyncServer(Server):
|
|
195
197
|
|
196
198
|
# Initialize the metadata store
|
197
199
|
config = LettaConfig.load()
|
198
|
-
if settings.
|
200
|
+
if settings.database_engine is DatabaseChoice.POSTGRES:
|
199
201
|
config.recall_storage_type = "postgres"
|
200
202
|
config.recall_storage_uri = settings.letta_pg_uri_no_default
|
201
203
|
config.archival_storage_type = "postgres"
|
@@ -1244,6 +1246,7 @@ class SyncServer(Server):
|
|
1244
1246
|
use_assistant_message: bool = True,
|
1245
1247
|
assistant_message_tool_name: str = constants.DEFAULT_MESSAGE_TOOL,
|
1246
1248
|
assistant_message_tool_kwarg: str = constants.DEFAULT_MESSAGE_TOOL_KWARG,
|
1249
|
+
include_err: Optional[bool] = None,
|
1247
1250
|
) -> Union[List[Message], List[LettaMessage]]:
|
1248
1251
|
records = await self.message_manager.list_messages_for_agent_async(
|
1249
1252
|
agent_id=agent_id,
|
@@ -1253,6 +1256,7 @@ class SyncServer(Server):
|
|
1253
1256
|
limit=limit,
|
1254
1257
|
ascending=not reverse,
|
1255
1258
|
group_id=group_id,
|
1259
|
+
include_err=include_err,
|
1256
1260
|
)
|
1257
1261
|
|
1258
1262
|
if not return_message_object:
|
@@ -1262,6 +1266,7 @@ class SyncServer(Server):
|
|
1262
1266
|
assistant_message_tool_name=assistant_message_tool_name,
|
1263
1267
|
assistant_message_tool_kwarg=assistant_message_tool_kwarg,
|
1264
1268
|
reverse=reverse,
|
1269
|
+
include_err=include_err,
|
1265
1270
|
)
|
1266
1271
|
|
1267
1272
|
if reverse:
|
@@ -2001,6 +2006,7 @@ class SyncServer(Server):
|
|
2001
2006
|
tool_name: Optional[str] = None,
|
2002
2007
|
tool_args_json_schema: Optional[Dict[str, Any]] = None,
|
2003
2008
|
tool_json_schema: Optional[Dict[str, Any]] = None,
|
2009
|
+
pip_requirements: Optional[List[PipRequirement]] = None,
|
2004
2010
|
) -> ToolReturnMessage:
|
2005
2011
|
"""Run a tool from source code"""
|
2006
2012
|
if tool_source_type is not None and tool_source_type != "python":
|
@@ -2008,13 +2014,14 @@ class SyncServer(Server):
|
|
2008
2014
|
|
2009
2015
|
# If tools_json_schema is explicitly passed in, override it on the created Tool object
|
2010
2016
|
if tool_json_schema:
|
2011
|
-
tool = Tool(name=tool_name, source_code=tool_source, json_schema=tool_json_schema)
|
2017
|
+
tool = Tool(name=tool_name, source_code=tool_source, json_schema=tool_json_schema, pip_requirements=pip_requirements)
|
2012
2018
|
else:
|
2013
2019
|
# NOTE: we're creating a floating Tool object and NOT persisting to DB
|
2014
2020
|
tool = Tool(
|
2015
2021
|
name=tool_name,
|
2016
2022
|
source_code=tool_source,
|
2017
2023
|
args_json_schema=tool_args_json_schema,
|
2024
|
+
pip_requirements=pip_requirements,
|
2018
2025
|
)
|
2019
2026
|
|
2020
2027
|
assert tool.name is not None, "Failed to create tool object"
|
letta/server/ws_api/server.py
CHANGED
@@ -21,7 +21,7 @@ class WebSocketServer:
|
|
21
21
|
def shutdown_server(self):
|
22
22
|
try:
|
23
23
|
self.interface.close()
|
24
|
-
print(
|
24
|
+
print("Closed the WS interface")
|
25
25
|
except Exception as e:
|
26
26
|
print(f"Closing the WS interface failed with: {e}")
|
27
27
|
|
@@ -100,7 +100,7 @@ class WebSocketServer:
|
|
100
100
|
await websocket.send(protocol.server_error(f"unrecognized client package data type: {data}"))
|
101
101
|
|
102
102
|
except websockets.exceptions.ConnectionClosed:
|
103
|
-
print(
|
103
|
+
print("[server] connection with client was closed")
|
104
104
|
finally:
|
105
105
|
self.interface.unregister_client(websocket)
|
106
106
|
|