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.
Files changed (99) hide show
  1. letta/__init__.py +1 -1
  2. letta/agent.py +27 -11
  3. letta/agents/helpers.py +1 -1
  4. letta/agents/letta_agent.py +518 -322
  5. letta/agents/letta_agent_batch.py +1 -2
  6. letta/agents/voice_agent.py +15 -17
  7. letta/client/client.py +3 -3
  8. letta/constants.py +5 -0
  9. letta/embeddings.py +0 -2
  10. letta/errors.py +8 -0
  11. letta/functions/function_sets/base.py +3 -3
  12. letta/functions/helpers.py +2 -3
  13. letta/groups/sleeptime_multi_agent.py +0 -1
  14. letta/helpers/composio_helpers.py +2 -2
  15. letta/helpers/converters.py +1 -1
  16. letta/helpers/pinecone_utils.py +8 -0
  17. letta/helpers/tool_rule_solver.py +13 -18
  18. letta/llm_api/aws_bedrock.py +16 -2
  19. letta/llm_api/cohere.py +1 -1
  20. letta/llm_api/openai_client.py +1 -1
  21. letta/local_llm/grammars/gbnf_grammar_generator.py +1 -1
  22. letta/local_llm/llm_chat_completion_wrappers/zephyr.py +14 -14
  23. letta/local_llm/utils.py +1 -2
  24. letta/orm/agent.py +3 -3
  25. letta/orm/block.py +4 -4
  26. letta/orm/files_agents.py +0 -1
  27. letta/orm/identity.py +2 -0
  28. letta/orm/mcp_server.py +0 -2
  29. letta/orm/message.py +140 -14
  30. letta/orm/organization.py +5 -5
  31. letta/orm/passage.py +4 -4
  32. letta/orm/source.py +1 -1
  33. letta/orm/sqlalchemy_base.py +61 -39
  34. letta/orm/step.py +2 -0
  35. letta/otel/db_pool_monitoring.py +308 -0
  36. letta/otel/metric_registry.py +94 -1
  37. letta/otel/sqlalchemy_instrumentation.py +548 -0
  38. letta/otel/sqlalchemy_instrumentation_integration.py +124 -0
  39. letta/otel/tracing.py +37 -1
  40. letta/schemas/agent.py +0 -3
  41. letta/schemas/agent_file.py +283 -0
  42. letta/schemas/block.py +0 -3
  43. letta/schemas/file.py +28 -26
  44. letta/schemas/letta_message.py +15 -4
  45. letta/schemas/memory.py +1 -1
  46. letta/schemas/message.py +31 -26
  47. letta/schemas/openai/chat_completion_response.py +0 -1
  48. letta/schemas/providers.py +20 -0
  49. letta/schemas/source.py +11 -13
  50. letta/schemas/step.py +12 -0
  51. letta/schemas/tool.py +0 -4
  52. letta/serialize_schemas/marshmallow_agent.py +14 -1
  53. letta/serialize_schemas/marshmallow_block.py +23 -1
  54. letta/serialize_schemas/marshmallow_message.py +1 -3
  55. letta/serialize_schemas/marshmallow_tool.py +23 -1
  56. letta/server/db.py +110 -6
  57. letta/server/rest_api/app.py +85 -73
  58. letta/server/rest_api/routers/v1/agents.py +68 -53
  59. letta/server/rest_api/routers/v1/blocks.py +2 -2
  60. letta/server/rest_api/routers/v1/jobs.py +3 -0
  61. letta/server/rest_api/routers/v1/organizations.py +2 -2
  62. letta/server/rest_api/routers/v1/sources.py +18 -2
  63. letta/server/rest_api/routers/v1/tools.py +11 -12
  64. letta/server/rest_api/routers/v1/users.py +1 -1
  65. letta/server/rest_api/streaming_response.py +13 -5
  66. letta/server/rest_api/utils.py +8 -25
  67. letta/server/server.py +11 -4
  68. letta/server/ws_api/server.py +2 -2
  69. letta/services/agent_file_manager.py +616 -0
  70. letta/services/agent_manager.py +133 -46
  71. letta/services/block_manager.py +38 -17
  72. letta/services/file_manager.py +106 -21
  73. letta/services/file_processor/file_processor.py +93 -0
  74. letta/services/files_agents_manager.py +28 -0
  75. letta/services/group_manager.py +4 -5
  76. letta/services/helpers/agent_manager_helper.py +57 -9
  77. letta/services/identity_manager.py +22 -0
  78. letta/services/job_manager.py +210 -91
  79. letta/services/llm_batch_manager.py +9 -6
  80. letta/services/mcp/stdio_client.py +1 -2
  81. letta/services/mcp_manager.py +0 -1
  82. letta/services/message_manager.py +49 -26
  83. letta/services/passage_manager.py +0 -1
  84. letta/services/provider_manager.py +1 -1
  85. letta/services/source_manager.py +114 -5
  86. letta/services/step_manager.py +36 -4
  87. letta/services/telemetry_manager.py +9 -2
  88. letta/services/tool_executor/builtin_tool_executor.py +5 -1
  89. letta/services/tool_executor/core_tool_executor.py +3 -3
  90. letta/services/tool_manager.py +95 -20
  91. letta/services/user_manager.py +4 -12
  92. letta/settings.py +23 -6
  93. letta/system.py +1 -1
  94. letta/utils.py +26 -2
  95. {letta_nightly-0.8.15.dev20250719104256.dist-info → letta_nightly-0.8.16.dev20250721070720.dist-info}/METADATA +3 -2
  96. {letta_nightly-0.8.15.dev20250719104256.dist-info → letta_nightly-0.8.16.dev20250721070720.dist-info}/RECORD +99 -94
  97. {letta_nightly-0.8.15.dev20250719104256.dist-info → letta_nightly-0.8.16.dev20250721070720.dist-info}/LICENSE +0 -0
  98. {letta_nightly-0.8.15.dev20250719104256.dist-info → letta_nightly-0.8.16.dev20250721070720.dist-info}/WHEEL +0 -0
  99. {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 = f"Tool with this name already exists."
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=f"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.",
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=f"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.",
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=f"User does not exist")
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
- return
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.info("Stream was cancelled by client or job cancellation")
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("unhandled_streaming_error")
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})
@@ -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": f"Stream failed (internal error occurred)"})
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 get_current_interface() -> StreamingServerInterface:
161
- return StreamingServerInterface
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[[], AgentInterface] = lambda: CLIInterface(),
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.letta_pg_uri_no_default:
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"
@@ -21,7 +21,7 @@ class WebSocketServer:
21
21
  def shutdown_server(self):
22
22
  try:
23
23
  self.interface.close()
24
- print(f"Closed the WS interface")
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(f"[server] connection with client was closed")
103
+ print("[server] connection with client was closed")
104
104
  finally:
105
105
  self.interface.unregister_client(websocket)
106
106