diff --git a/alembic/env.py b/alembic/env.py index 8b60f2d4..a566f92c 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -16,7 +16,7 @@ config = context.config if settings.letta_pg_uri_no_default: config.set_main_option("sqlalchemy.url", settings.letta_pg_uri) - print(f"Using database: ", settings.letta_pg_uri) + print("Using database: ", settings.letta_pg_uri) else: config.set_main_option("sqlalchemy.url", "sqlite:///" + os.path.join(letta_config.recall_storage_path, "sqlite.db")) diff --git a/letta/agent.py b/letta/agent.py index bc40f12b..c68a0891 100644 --- a/letta/agent.py +++ b/letta/agent.py @@ -1641,7 +1641,7 @@ class Agent(BaseAgent): # Get the MCPClient from the server's handle # TODO these don't get raised properly if not self.mcp_clients: - raise ValueError(f"No MCP client available to use") + raise ValueError("No MCP client available to use") if server_name not in self.mcp_clients: raise ValueError(f"Unknown MCP server name: {server_name}") mcp_client = self.mcp_clients[server_name] diff --git a/letta/agents/helpers.py b/letta/agents/helpers.py index b49303e6..42257436 100644 --- a/letta/agents/helpers.py +++ b/letta/agents/helpers.py @@ -235,5 +235,5 @@ def _pop_heartbeat(tool_args: dict) -> bool: def _build_rule_violation_result(tool_name: str, valid: list[str], solver: ToolRulesSolver) -> ToolExecutionResult: hint_lines = solver.guess_rule_violation(tool_name) hint_txt = ("\n** Hint: Possible rules that were violated:\n" + "\n".join(f"\t- {h}" for h in hint_lines)) if hint_lines else "" - msg = f"[ToolConstraintError] Cannot call {tool_name}, " f"valid tools include: {valid}.{hint_txt}" + msg = f"[ToolConstraintError] Cannot call {tool_name}, valid tools include: {valid}.{hint_txt}" return ToolExecutionResult(status="error", func_return=msg) diff --git a/letta/agents/letta_agent_batch.py b/letta/agents/letta_agent_batch.py index 456633d4..0e9afb07 100644 --- a/letta/agents/letta_agent_batch.py +++ b/letta/agents/letta_agent_batch.py @@ -100,7 +100,6 @@ async def execute_tool_wrapper(params: ToolExecutionParams) -> tuple[str, ToolEx # TODO: Limitations -> # TODO: Only works with anthropic for now class LettaAgentBatch(BaseAgent): - def __init__( self, message_manager: MessageManager, diff --git a/letta/agents/voice_agent.py b/letta/agents/voice_agent.py index 18e930d3..93ced5cb 100644 --- a/letta/agents/voice_agent.py +++ b/letta/agents/voice_agent.py @@ -377,19 +377,19 @@ class VoiceAgent(BaseAgent): "type": ["array", "null"], "items": {"type": "string"}, "description": ( - "Extra keywords (e.g., order ID, place name). " "Use *null* when the utterance is already specific." + "Extra keywords (e.g., order ID, place name). Use *null* when the utterance is already specific." ), }, "start_minutes_ago": { "type": ["integer", "null"], "description": ( - "Newer bound of the time window, in minutes ago. " "Use *null* if no lower bound is needed." + "Newer bound of the time window, in minutes ago. Use *null* if no lower bound is needed." ), }, "end_minutes_ago": { "type": ["integer", "null"], "description": ( - "Older bound of the time window, in minutes ago. " "Use *null* if no upper bound is needed." + "Older bound of the time window, in minutes ago. Use *null* if no upper bound is needed." ), }, }, diff --git a/letta/client/client.py b/letta/client/client.py index d2aadcbc..1d5c81e7 100644 --- a/letta/client/client.py +++ b/letta/client/client.py @@ -568,8 +568,8 @@ class RESTClient(AbstractClient): tool_names += BASE_MEMORY_TOOLS tool_ids += [self.get_tool_id(tool_name=name) for name in tool_names] - assert embedding_config or self._default_embedding_config, f"Embedding config must be provided" - assert llm_config or self._default_llm_config, f"LLM config must be provided" + assert embedding_config or self._default_embedding_config, "Embedding config must be provided" + assert llm_config or self._default_llm_config, "LLM config must be provided" # TODO: This should not happen here, we need to have clear separation between create/add blocks # TODO: This is insanely hacky and a result of allowing free-floating blocks @@ -1392,7 +1392,7 @@ class RESTClient(AbstractClient): Returns: source (Source): Created source """ - assert embedding_config or self._default_embedding_config, f"Must specify embedding_config for source" + assert embedding_config or self._default_embedding_config, "Must specify embedding_config for source" source_create = SourceCreate(name=name, embedding_config=embedding_config or self._default_embedding_config) payload = source_create.model_dump() response = requests.post(f"{self.base_url}/{self.api_prefix}/sources", json=payload, headers=self.headers) diff --git a/letta/embeddings.py b/letta/embeddings.py index 776671b5..31d73095 100644 --- a/letta/embeddings.py +++ b/letta/embeddings.py @@ -190,7 +190,6 @@ class GoogleEmbeddings: class GoogleVertexEmbeddings: - def __init__(self, model: str, project_id: str, region: str): from google import genai @@ -203,7 +202,6 @@ class GoogleVertexEmbeddings: class OpenAIEmbeddings: - def __init__(self, api_key: str, model: str, base_url: str): if base_url: self.client = OpenAI(api_key=api_key, base_url=base_url) diff --git a/letta/functions/function_sets/base.py b/letta/functions/function_sets/base.py index 948c9a11..3ecbf42e 100644 --- a/letta/functions/function_sets/base.py +++ b/letta/functions/function_sets/base.py @@ -42,7 +42,7 @@ def conversation_search(self: "Agent", query: str, page: Optional[int] = 0) -> O try: page = int(page) except: - raise ValueError(f"'page' argument must be an integer") + raise ValueError("'page' argument must be an integer") count = RETRIEVAL_QUERY_DEFAULT_PAGE_SIZE # TODO: add paging by page number. currently cursor only works with strings. # original: start=page * count @@ -55,7 +55,7 @@ def conversation_search(self: "Agent", query: str, page: Optional[int] = 0) -> O total = len(messages) num_pages = math.ceil(total / count) - 1 # 0 index if len(messages) == 0: - results_str = f"No results found." + results_str = "No results found." else: results_pref = f"Showing {len(messages)} of {total} results (page {page}/{num_pages}):" results_formatted = [message.content[0].text for message in messages] @@ -103,7 +103,7 @@ def archival_memory_search(self: "Agent", query: str, page: Optional[int] = 0, s try: page = int(page) except: - raise ValueError(f"'page' argument must be an integer") + raise ValueError("'page' argument must be an integer") count = RETRIEVAL_QUERY_DEFAULT_PAGE_SIZE try: diff --git a/letta/functions/helpers.py b/letta/functions/helpers.py index 161bef96..dc1a3b0b 100644 --- a/letta/functions/helpers.py +++ b/letta/functions/helpers.py @@ -23,7 +23,6 @@ from letta.settings import settings # TODO needed? def generate_mcp_tool_wrapper(mcp_tool_name: str) -> tuple[str, str]: - wrapper_function_str = f"""\ def {mcp_tool_name}(**kwargs): raise RuntimeError("Something went wrong - we should never be using the persisted source code for MCP. Please reach out to Letta team") @@ -46,7 +45,7 @@ def generate_langchain_tool_wrapper( _assert_all_classes_are_imported(tool, additional_imports_module_attr_map) tool_instantiation = f"tool = {generate_imported_tool_instantiation_call_str(tool)}" - run_call = f"return tool._run(**kwargs)" + run_call = "return tool._run(**kwargs)" func_name = humps.decamelize(tool_name) # Combine all parts into the wrapper function @@ -240,7 +239,7 @@ async def async_execute_send_message_to_agent( try: server.agent_manager.get_agent_by_id(agent_id=other_agent_id, actor=sender_agent.user) except NoResultFound: - raise ValueError(f"Target agent {other_agent_id} either does not exist or is not in org " f"({sender_agent.user.organization_id}).") + raise ValueError(f"Target agent {other_agent_id} either does not exist or is not in org ({sender_agent.user.organization_id}).") # 2. Use your async retry logic return await _async_send_message_with_retries( diff --git a/letta/groups/sleeptime_multi_agent.py b/letta/groups/sleeptime_multi_agent.py index 3ab0adc2..b207219c 100644 --- a/letta/groups/sleeptime_multi_agent.py +++ b/letta/groups/sleeptime_multi_agent.py @@ -20,7 +20,6 @@ from letta.services.message_manager import MessageManager class SleeptimeMultiAgent(Agent): - def __init__( self, interface: AgentInterface, diff --git a/letta/helpers/composio_helpers.py b/letta/helpers/composio_helpers.py index 7a142f7c..1e6e31d6 100644 --- a/letta/helpers/composio_helpers.py +++ b/letta/helpers/composio_helpers.py @@ -10,7 +10,7 @@ def get_composio_api_key(actor: User, logger: Optional[Logger] = None) -> Option api_keys = SandboxConfigManager().list_sandbox_env_vars_by_key(key="COMPOSIO_API_KEY", actor=actor) if not api_keys: if logger: - logger.debug(f"No API keys found for Composio. Defaulting to the environment variable...") + logger.debug("No API keys found for Composio. Defaulting to the environment variable...") if tool_settings.composio_api_key: return tool_settings.composio_api_key else: @@ -26,7 +26,7 @@ async def get_composio_api_key_async(actor: User, logger: Optional[Logger] = Non api_keys = await SandboxConfigManager().list_sandbox_env_vars_by_key_async(key="COMPOSIO_API_KEY", actor=actor) if not api_keys: if logger: - logger.debug(f"No API keys found for Composio. Defaulting to the environment variable...") + logger.debug("No API keys found for Composio. Defaulting to the environment variable...") if tool_settings.composio_api_key: return tool_settings.composio_api_key else: diff --git a/letta/helpers/converters.py b/letta/helpers/converters.py index 53ee1565..b855001f 100644 --- a/letta/helpers/converters.py +++ b/letta/helpers/converters.py @@ -245,7 +245,7 @@ def deserialize_message_content(data: Optional[List[Dict]]) -> List[MessageConte if content_type == MessageContentType.text: content = TextContent(**item) elif content_type == MessageContentType.image: - assert item["source"]["type"] == ImageSourceType.letta, f'Invalid image source type: {item["source"]["type"]}' + assert item["source"]["type"] == ImageSourceType.letta, f"Invalid image source type: {item['source']['type']}" content = ImageContent(**item) elif content_type == MessageContentType.tool_call: content = ToolCallContent(**item) diff --git a/letta/llm_api/cohere.py b/letta/llm_api/cohere.py index 4a30d796..eeabae7a 100644 --- a/letta/llm_api/cohere.py +++ b/letta/llm_api/cohere.py @@ -307,7 +307,7 @@ def cohere_chat_completions_request( data = chat_completion_request.model_dump(exclude_none=True) if "functions" in data: - raise ValueError(f"'functions' unexpected in Anthropic API payload") + raise ValueError("'functions' unexpected in Anthropic API payload") # If tools == None, strip from the payload if "tools" in data and data["tools"] is None: diff --git a/letta/llm_api/openai_client.py b/letta/llm_api/openai_client.py index 3b12095f..c9561cc2 100644 --- a/letta/llm_api/openai_client.py +++ b/letta/llm_api/openai_client.py @@ -54,7 +54,7 @@ def accepts_developer_role(model: str) -> bool: See: https://community.openai.com/t/developer-role-not-accepted-for-o1-o1-mini-o3-mini/1110750/7 """ - if is_openai_reasoning_model(model) and not "o1-mini" in model or "o1-preview" in model: + if is_openai_reasoning_model(model) and "o1-mini" not in model or "o1-preview" in model: return True else: return False diff --git a/letta/local_llm/grammars/gbnf_grammar_generator.py b/letta/local_llm/grammars/gbnf_grammar_generator.py index 8daeda5c..536bf8e2 100644 --- a/letta/local_llm/grammars/gbnf_grammar_generator.py +++ b/letta/local_llm/grammars/gbnf_grammar_generator.py @@ -697,7 +697,7 @@ def generate_markdown_documentation( # Indenting the fields section documentation += f" {fields_prefix}:\n" else: - documentation += f" attributes:\n" + documentation += " attributes:\n" if isclass(model) and issubclass(model, BaseModel): for name, field_type in model.__annotations__.items(): # if name == "markdown_code_block": diff --git a/letta/local_llm/llm_chat_completion_wrappers/zephyr.py b/letta/local_llm/llm_chat_completion_wrappers/zephyr.py index 3b7fb72d..8ee733aa 100644 --- a/letta/local_llm/llm_chat_completion_wrappers/zephyr.py +++ b/letta/local_llm/llm_chat_completion_wrappers/zephyr.py @@ -43,7 +43,7 @@ class ZephyrMistralWrapper(LLMChatCompletionWrapper): # System instructions go first assert messages[0]["role"] == "system" - prompt += f"<|system|>" + prompt += "<|system|>" prompt += f"\n{messages[0]['content']}" # Next is the functions preamble @@ -52,7 +52,7 @@ class ZephyrMistralWrapper(LLMChatCompletionWrapper): func_str = "" func_str += f"{schema['name']}:" func_str += f"\n description: {schema['description']}" - func_str += f"\n params:" + func_str += "\n params:" for param_k, param_v in schema["parameters"]["properties"].items(): # TODO we're ignoring type func_str += f"\n {param_k}: {param_v['description']}" @@ -60,8 +60,8 @@ class ZephyrMistralWrapper(LLMChatCompletionWrapper): return func_str # prompt += f"\nPlease select the most suitable function and parameters from the list of available functions below, based on the user's input. Provide your response in JSON format." - prompt += f"\nPlease select the most suitable function and parameters from the list of available functions below, based on the ongoing conversation. Provide your response in JSON format." - prompt += f"\nAvailable functions:" + prompt += "\nPlease select the most suitable function and parameters from the list of available functions below, based on the ongoing conversation. Provide your response in JSON format." + prompt += "\nAvailable functions:" if function_documentation is not None: prompt += f"\n{function_documentation}" else: @@ -92,7 +92,7 @@ class ZephyrMistralWrapper(LLMChatCompletionWrapper): prompt += f"\n<|user|>\n{message['content']}{IM_END_TOKEN}" # prompt += f"\nUSER: {message['content']}" elif message["role"] == "assistant": - prompt += f"\n<|assistant|>" + prompt += "\n<|assistant|>" if message["content"] is not None: prompt += f"\n{message['content']}" # prompt += f"\nASSISTANT: {message['content']}" @@ -103,7 +103,7 @@ class ZephyrMistralWrapper(LLMChatCompletionWrapper): elif message["role"] in ["function", "tool"]: # TODO find a good way to add this # prompt += f"\nASSISTANT: (function return) {message['content']}" - prompt += f"\n<|assistant|>" + prompt += "\n<|assistant|>" prompt += f"\nFUNCTION RETURN: {message['content']}" # prompt += f"\nFUNCTION RETURN: {message['content']}" continue @@ -116,7 +116,7 @@ class ZephyrMistralWrapper(LLMChatCompletionWrapper): if self.include_assistant_prefix: # prompt += f"\nASSISTANT:" - prompt += f"\n<|assistant|>" + prompt += "\n<|assistant|>" if self.include_opening_brance_in_prefix: prompt += "\n{" @@ -214,9 +214,9 @@ class ZephyrMistralInnerMonologueWrapper(ZephyrMistralWrapper): func_str = "" func_str += f"{schema['name']}:" func_str += f"\n description: {schema['description']}" - func_str += f"\n params:" + func_str += "\n params:" if add_inner_thoughts: - func_str += f"\n inner_thoughts: Deep inner monologue private to you only." + func_str += "\n inner_thoughts: Deep inner monologue private to you only." for param_k, param_v in schema["parameters"]["properties"].items(): # TODO we're ignoring type func_str += f"\n {param_k}: {param_v['description']}" @@ -224,8 +224,8 @@ class ZephyrMistralInnerMonologueWrapper(ZephyrMistralWrapper): return func_str # prompt += f"\nPlease select the most suitable function and parameters from the list of available functions below, based on the user's input. Provide your response in JSON format." - prompt += f"\nPlease select the most suitable function and parameters from the list of available functions below, based on the ongoing conversation. Provide your response in JSON format." - prompt += f"\nAvailable functions:" + prompt += "\nPlease select the most suitable function and parameters from the list of available functions below, based on the ongoing conversation. Provide your response in JSON format." + prompt += "\nAvailable functions:" if function_documentation is not None: prompt += f"\n{function_documentation}" else: @@ -259,10 +259,10 @@ class ZephyrMistralInnerMonologueWrapper(ZephyrMistralWrapper): except: prompt += f"\n<|user|>\n{message['content']}{IM_END_TOKEN}" elif message["role"] == "assistant": - prompt += f"\n<|assistant|>" + prompt += "\n<|assistant|>" # need to add the function call if there was one inner_thoughts = message["content"] - if "function_call" in message and message["function_call"]: + if message.get("function_call"): prompt += f"\n{create_function_call(message['function_call'], inner_thoughts=inner_thoughts)}" elif message["role"] in ["function", "tool"]: # TODO find a good way to add this @@ -277,7 +277,7 @@ class ZephyrMistralInnerMonologueWrapper(ZephyrMistralWrapper): # prompt += "\n### RESPONSE" if self.include_assistant_prefix: - prompt += f"\n<|assistant|>" + prompt += "\n<|assistant|>" if self.include_opening_brance_in_prefix: prompt += "\n{" diff --git a/letta/local_llm/utils.py b/letta/local_llm/utils.py index 09177c3e..6027484d 100644 --- a/letta/local_llm/utils.py +++ b/letta/local_llm/utils.py @@ -76,7 +76,7 @@ def num_tokens_from_functions(functions: List[dict], model: str = "gpt-4"): except KeyError: from letta.utils import printd - printd(f"Warning: model not found. Using cl100k_base encoding.") + printd("Warning: model not found. Using cl100k_base encoding.") encoding = tiktoken.get_encoding("cl100k_base") num_tokens = 0 @@ -238,7 +238,6 @@ def num_tokens_from_messages(messages: List[dict], model: str = "gpt-4") -> int: num_tokens += tokens_per_message for key, value in message.items(): try: - if isinstance(value, list) and key == "tool_calls": num_tokens += num_tokens_from_tool_calls(tool_calls=value, model=model) # special case for tool calling (list) diff --git a/letta/orm/source.py b/letta/orm/source.py index f23c61e5..ff34462d 100644 --- a/letta/orm/source.py +++ b/letta/orm/source.py @@ -20,7 +20,7 @@ class Source(SqlalchemyBase, OrganizationMixin): __pydantic_model__ = PydanticSource __table_args__ = ( - Index(f"source_created_at_id_idx", "created_at", "id"), + Index("source_created_at_id_idx", "created_at", "id"), UniqueConstraint("name", "organization_id", name="uq_source_name_organization"), {"extend_existing": True}, ) diff --git a/letta/schemas/agent.py b/letta/schemas/agent.py index 8bceac98..2db5e2bf 100644 --- a/letta/schemas/agent.py +++ b/letta/schemas/agent.py @@ -309,7 +309,6 @@ class AgentStepResponse(BaseModel): def get_prompt_template_for_agent_type(agent_type: Optional[AgentType] = None): - # Workflow agents and ReAct agents don't use memory blocks # However, they still allow files to be injected into the context if agent_type == AgentType.react_agent or agent_type == AgentType.workflow_agent: diff --git a/letta/schemas/memory.py b/letta/schemas/memory.py index eac33ae7..829bf737 100644 --- a/letta/schemas/memory.py +++ b/letta/schemas/memory.py @@ -174,7 +174,7 @@ class Memory(BaseModel, validate_assignment=True): def update_block_value(self, label: str, value: str): """Update the value of a block""" if not isinstance(value, str): - raise ValueError(f"Provided value must be a string") + raise ValueError("Provided value must be a string") for block in self.blocks: if block.label == label: diff --git a/letta/schemas/openai/chat_completion_response.py b/letta/schemas/openai/chat_completion_response.py index 77f8b991..63224cc4 100644 --- a/letta/schemas/openai/chat_completion_response.py +++ b/letta/schemas/openai/chat_completion_response.py @@ -106,7 +106,6 @@ class UsageStatistics(BaseModel): completion_tokens_details: Optional[UsageStatisticsCompletionTokenDetails] = None def __add__(self, other: "UsageStatistics") -> "UsageStatistics": - if self.prompt_tokens_details is None and other.prompt_tokens_details is None: total_prompt_tokens_details = None elif self.prompt_tokens_details is None: diff --git a/letta/server/rest_api/app.py b/letta/server/rest_api/app.py index 286e94e3..68b21934 100644 --- a/letta/server/rest_api/app.py +++ b/letta/server/rest_api/app.py @@ -408,7 +408,7 @@ def start_server( if (os.getenv("LOCAL_HTTPS") == "true") or "--localhttps" in sys.argv: print(f"▶ Server running at: https://{host or 'localhost'}:{port or REST_DEFAULT_PORT}") - print(f"▶ View using ADE at: https://app.letta.com/development-servers/local/dashboard\n") + print("▶ View using ADE at: https://app.letta.com/development-servers/local/dashboard\n") if importlib.util.find_spec("granian") is not None and settings.use_granian: from granian import Granian diff --git a/letta/server/rest_api/routers/v1/organizations.py b/letta/server/rest_api/routers/v1/organizations.py index 04b69e94..7f52a79d 100644 --- a/letta/server/rest_api/routers/v1/organizations.py +++ b/letta/server/rest_api/routers/v1/organizations.py @@ -52,7 +52,7 @@ async def delete_org( try: org = await server.organization_manager.get_organization_by_id_async(org_id=org_id) if org is None: - raise HTTPException(status_code=404, detail=f"Organization does not exist") + raise HTTPException(status_code=404, detail="Organization does not exist") await server.organization_manager.delete_organization_by_id_async(org_id=org_id) except HTTPException: raise @@ -70,7 +70,7 @@ async def update_org( try: org = await server.organization_manager.get_organization_by_id_async(org_id=org_id) if org is None: - raise HTTPException(status_code=404, detail=f"Organization does not exist") + raise HTTPException(status_code=404, detail="Organization does not exist") org = await server.organization_manager.update_organization_async(org_id=org_id, name=request.name) except HTTPException: raise diff --git a/letta/server/rest_api/routers/v1/tools.py b/letta/server/rest_api/routers/v1/tools.py index 4d094e78..9312b363 100644 --- a/letta/server/rest_api/routers/v1/tools.py +++ b/letta/server/rest_api/routers/v1/tools.py @@ -122,7 +122,7 @@ async def create_tool( except UniqueConstraintViolationError as e: # Log or print the full exception here for debugging print(f"Error occurred: {e}") - clean_error_message = f"Tool with this name already exists." + clean_error_message = "Tool with this name already exists." raise HTTPException(status_code=409, detail=clean_error_message) except LettaToolCreateError as e: # HTTP 400 == Bad Request @@ -248,7 +248,7 @@ def list_composio_apps(server: SyncServer = Depends(get_letta_server), user_id: if not composio_api_key: raise HTTPException( status_code=400, # Bad Request - 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.", + 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.", ) return server.get_composio_apps(api_key=composio_api_key) @@ -267,7 +267,7 @@ def list_composio_actions_by_app( if not composio_api_key: raise HTTPException( status_code=400, # Bad Request - 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.", + 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.", ) return server.get_composio_actions_from_app_name(composio_app_name=composio_app_name, api_key=composio_api_key) @@ -430,7 +430,6 @@ async def add_mcp_tool( actor = server.user_manager.get_user_or_default(user_id=actor_id) if tool_settings.mcp_read_from_config: - try: available_tools = await server.get_tools_from_mcp_server(mcp_server_name=mcp_server_name) except ValueError as e: diff --git a/letta/server/rest_api/routers/v1/users.py b/letta/server/rest_api/routers/v1/users.py index 4b4bfd91..d12315cd 100644 --- a/letta/server/rest_api/routers/v1/users.py +++ b/letta/server/rest_api/routers/v1/users.py @@ -65,7 +65,7 @@ async def delete_user( try: user = await server.user_manager.get_actor_by_id_async(actor_id=user_id) if user is None: - raise HTTPException(status_code=404, detail=f"User does not exist") + raise HTTPException(status_code=404, detail="User does not exist") await server.user_manager.delete_actor_by_id_async(user_id=user_id) except HTTPException: raise diff --git a/letta/server/rest_api/utils.py b/letta/server/rest_api/utils.py index 394193e3..91f75012 100644 --- a/letta/server/rest_api/utils.py +++ b/letta/server/rest_api/utils.py @@ -130,7 +130,7 @@ async def sse_async_generator( except Exception as e: log_error_to_sentry(e) logger.error(f"Caught unexpected Exception: {e}") - yield sse_formatter({"error": f"Stream failed (internal error occurred)"}) + yield sse_formatter({"error": "Stream failed (internal error occurred)"}) except Exception as e: log_error_to_sentry(e) diff --git a/letta/server/ws_api/server.py b/letta/server/ws_api/server.py index e2408dda..75b18aab 100644 --- a/letta/server/ws_api/server.py +++ b/letta/server/ws_api/server.py @@ -21,7 +21,7 @@ class WebSocketServer: def shutdown_server(self): try: self.interface.close() - print(f"Closed the WS interface") + print("Closed the WS interface") except Exception as e: print(f"Closing the WS interface failed with: {e}") @@ -100,7 +100,7 @@ class WebSocketServer: await websocket.send(protocol.server_error(f"unrecognized client package data type: {data}")) except websockets.exceptions.ConnectionClosed: - print(f"[server] connection with client was closed") + print("[server] connection with client was closed") finally: self.interface.unregister_client(websocket) diff --git a/letta/services/block_manager.py b/letta/services/block_manager.py index 0c0203bd..79a4bcdc 100644 --- a/letta/services/block_manager.py +++ b/letta/services/block_manager.py @@ -531,7 +531,7 @@ class BlockManager: for block in blocks: new_val = updates[block.id] if len(new_val) > block.limit: - logger.warning(f"Value length ({len(new_val)}) exceeds limit " f"({block.limit}) for block {block.id!r}, truncating...") + logger.warning(f"Value length ({len(new_val)}) exceeds limit ({block.limit}) for block {block.id!r}, truncating...") new_val = new_val[: block.limit] block.value = new_val diff --git a/letta/services/group_manager.py b/letta/services/group_manager.py index 2be87789..96684271 100644 --- a/letta/services/group_manager.py +++ b/letta/services/group_manager.py @@ -18,7 +18,6 @@ from letta.utils import enforce_types class GroupManager: - @enforce_types @trace_method def list_groups( @@ -164,7 +163,7 @@ class GroupManager: manager_agent_id = None if group_update.manager_config: if group_update.manager_config.manager_type != group.manager_type: - raise ValueError(f"Cannot change group pattern after creation") + raise ValueError("Cannot change group pattern after creation") match group_update.manager_config.manager_type: case ManagerType.round_robin: max_turns = group_update.manager_config.max_turns @@ -473,7 +472,7 @@ class GroupManager: # 1) require both-or-none if (max_value is None) != (min_value is None): raise ValueError( - f"Both '{max_name}' and '{min_name}' must be provided together " f"(got {max_name}={max_value}, {min_name}={min_value})" + f"Both '{max_name}' and '{min_name}' must be provided together (got {max_name}={max_value}, {min_name}={min_value})" ) # no further checks if neither is provided @@ -488,9 +487,9 @@ class GroupManager: ) if max_value <= 4 or min_value <= 4: raise ValueError( - f"Both '{max_name}' and '{min_name}' must be greater than 4 " f"(got {max_name}={max_value}, {min_name}={min_value})" + f"Both '{max_name}' and '{min_name}' must be greater than 4 (got {max_name}={max_value}, {min_name}={min_value})" ) # 3) ordering if max_value <= min_value: - raise ValueError(f"'{max_name}' must be greater than '{min_name}' " f"(got {max_name}={max_value} <= {min_name}={min_value})") + raise ValueError(f"'{max_name}' must be greater than '{min_name}' (got {max_name}={max_value} <= {min_name}={min_value})") diff --git a/letta/services/llm_batch_manager.py b/letta/services/llm_batch_manager.py index b2241e44..6f7c58f3 100644 --- a/letta/services/llm_batch_manager.py +++ b/letta/services/llm_batch_manager.py @@ -403,7 +403,7 @@ class LLMBatchManager: missing = requested - found if missing: raise ValueError( - f"Cannot bulk-update batch items: no records for the following " f"(llm_batch_id, agent_id) pairs: {missing}" + f"Cannot bulk-update batch items: no records for the following (llm_batch_id, agent_id) pairs: {missing}" ) # Build mappings, skipping any missing when strict=False diff --git a/letta/services/mcp_manager.py b/letta/services/mcp_manager.py index 63a825bd..b542e22b 100644 --- a/letta/services/mcp_manager.py +++ b/letta/services/mcp_manager.py @@ -274,7 +274,6 @@ class MCPManager: mcp_config_path = os.path.join(constants.LETTA_DIR, constants.MCP_CONFIG_NAME) if os.path.exists(mcp_config_path): with open(mcp_config_path, "r") as f: - try: mcp_config = json.load(f) except Exception as e: diff --git a/letta/services/passage_manager.py b/letta/services/passage_manager.py index 8206f048..67580776 100644 --- a/letta/services/passage_manager.py +++ b/letta/services/passage_manager.py @@ -476,7 +476,6 @@ class PassageManager: try: # breakup string into passages for text in parse_and_chunk_text(text, embedding_chunk_size): - if agent_state.embedding_config.embedding_endpoint_type != "openai": embedding = embed_model.get_text_embedding(text) else: diff --git a/letta/services/tool_executor/core_tool_executor.py b/letta/services/tool_executor/core_tool_executor.py index 8883890f..bff2545d 100644 --- a/letta/services/tool_executor/core_tool_executor.py +++ b/letta/services/tool_executor/core_tool_executor.py @@ -96,7 +96,7 @@ class LettaCoreToolExecutor(ToolExecutor): try: page = int(page) except: - raise ValueError(f"'page' argument must be an integer") + raise ValueError("'page' argument must be an integer") count = RETRIEVAL_QUERY_DEFAULT_PAGE_SIZE messages = await MessageManager().list_user_messages_for_agent_async( @@ -110,7 +110,7 @@ class LettaCoreToolExecutor(ToolExecutor): num_pages = math.ceil(total / count) - 1 # 0 index if len(messages) == 0: - results_str = f"No results found." + results_str = "No results found." else: results_pref = f"Showing {len(messages)} of {total} results (page {page}/{num_pages}):" results_formatted = [message.content[0].text for message in messages] @@ -137,7 +137,7 @@ class LettaCoreToolExecutor(ToolExecutor): try: page = int(page) except: - raise ValueError(f"'page' argument must be an integer") + raise ValueError("'page' argument must be an integer") count = RETRIEVAL_QUERY_DEFAULT_PAGE_SIZE diff --git a/letta/system.py b/letta/system.py index e4031bcc..888cc304 100644 --- a/letta/system.py +++ b/letta/system.py @@ -66,7 +66,7 @@ def get_initial_boot_messages(version, timezone): "type": "function", "function": { "name": "send_message", - "arguments": '{\n "message": "' + f"Hi, is anyone there?" + '"\n}', + "arguments": '{\n "message": "' + "Hi, is anyone there?" + '"\n}', }, } ], diff --git a/letta/utils.py b/letta/utils.py index 91218cd4..29699bd6 100644 --- a/letta/utils.py +++ b/letta/utils.py @@ -583,7 +583,7 @@ def annotate_message_json_list_with_tool_calls(messages: list[dict], allow_tool_ # If we find a function call w/o a tool call ID annotation, annotate it if message["role"] == "assistant" and "function_call" in message: if "tool_call_id" in message and message["tool_call_id"] is not None: - printd(f"Message already has tool_call_id") + printd("Message already has tool_call_id") tool_call_id = message["tool_call_id"] else: tool_call_id = str(uuid.uuid4()) @@ -625,7 +625,7 @@ def annotate_message_json_list_with_tool_calls(messages: list[dict], allow_tool_ assistant_tool_call = message["tool_calls"][0] if "id" in assistant_tool_call and assistant_tool_call["id"] is not None: - printd(f"Message already has id (tool_call_id)") + printd("Message already has id (tool_call_id)") tool_call_id = assistant_tool_call["id"] else: tool_call_id = str(uuid.uuid4()) diff --git a/performance_tests/test_insert_archival_memory.py b/performance_tests/test_insert_archival_memory.py index 4ce29664..93deedce 100644 --- a/performance_tests/test_insert_archival_memory.py +++ b/performance_tests/test_insert_archival_memory.py @@ -97,7 +97,7 @@ async def test_insert_archival_memories_concurrent(client): durs = np.array([t[2] for t in timeline]) start_offset = starts - starts.min() - print(f"Latency stats (s): min={durs.min():.3f}, mean={durs.mean():.3f}, " f"max={durs.max():.3f}, std={durs.std():.3f}") + print(f"Latency stats (s): min={durs.min():.3f}, mean={durs.mean():.3f}, max={durs.max():.3f}, std={durs.std():.3f}") # 4) Generate improved plots # Helper: concurrency over time @@ -182,4 +182,4 @@ async def test_insert_large_archival_memory(client): await client.agents.passages.create(agent_id=agent.id, text=text) t1 = time.perf_counter() - print(f"Total time: {t1-t0}") + print(f"Total time: {t1 - t0}") diff --git a/tests/integration_test_batch_sdk.py b/tests/integration_test_batch_sdk.py index d36c5422..e70435de 100644 --- a/tests/integration_test_batch_sdk.py +++ b/tests/integration_test_batch_sdk.py @@ -70,7 +70,6 @@ def client(server_url): @pytest.mark.asyncio async def test_create_batch(client: Letta, server: SyncServer): - # create agents agent1 = client.agents.create( name="agent1_batch", diff --git a/tests/integration_test_summarizer.py b/tests/integration_test_summarizer.py index 7143f9ac..2f0965c9 100644 --- a/tests/integration_test_summarizer.py +++ b/tests/integration_test_summarizer.py @@ -228,7 +228,7 @@ def test_auto_summarize(server, default_user): actor=default_user, ) - def summarize_message_exists(messages: List[Message]) -> bool: + def summarize_message_exists(messages: list[Message]) -> bool: for message in messages: if message.content[0].text and "The following is a summary of the previous" in message.content[0].text: return True diff --git a/tests/integration_test_voice_agent.py b/tests/integration_test_voice_agent.py index 49149cbe..4390e978 100644 --- a/tests/integration_test_voice_agent.py +++ b/tests/integration_test_voice_agent.py @@ -492,7 +492,6 @@ async def test_voice_sleeptime_agent(disable_e2b_api_key, voice_agent): @pytest.mark.asyncio(loop_scope="module") async def test_init_voice_convo_agent(voice_agent, server, actor): - assert voice_agent.enable_sleeptime == True main_agent_tools = [tool.name for tool in voice_agent.tools] assert len(main_agent_tools) == 4 diff --git a/tests/mcp/test_mcp.py b/tests/mcp/test_mcp.py index d17b9f38..6d8edaca 100644 --- a/tests/mcp/test_mcp.py +++ b/tests/mcp/test_mcp.py @@ -192,7 +192,7 @@ def test_stdio_mcp_server(client, agent_state): messages=[ MessageCreate( role="user", - content=[TextContent(text=(f"Use the `{letta_tool.name}` tool with these arguments: " f"{{'state': 'CA'}}."))], + content=[TextContent(text=(f"Use the `{letta_tool.name}` tool with these arguments: {{'state': 'CA'}}."))], ) ], ) diff --git a/tests/test_letta_agent_batch.py b/tests/test_letta_agent_batch.py index 8e798cb6..40d68170 100644 --- a/tests/test_letta_agent_batch.py +++ b/tests/test_letta_agent_batch.py @@ -350,7 +350,7 @@ async def test_rethink_tool_modify_agent_state(disable_e2b_api_key, server, defa actor = server.user_manager.get_user_or_default() agent = await server.create_agent_async( request=CreateAgent( - name=f"test_agent_rethink", + name="test_agent_rethink", include_base_tools=True, model=MODELS["sonnet"], tags=["test_agents"], diff --git a/tests/test_timezone_formatting.py b/tests/test_timezone_formatting.py index ca259e89..878799a1 100644 --- a/tests/test_timezone_formatting.py +++ b/tests/test_timezone_formatting.py @@ -68,9 +68,9 @@ class TestTimezoneFormatting: # Check that times are within tolerance time_diff = abs((parsed_time - current_time_in_tz).total_seconds()) - assert time_diff <= tolerance_minutes * 60, ( - f"Time difference too large: {time_diff}s. " f"Parsed: {parsed_time}, Expected timezone: {current_time_in_tz}" - ) + assert ( + time_diff <= tolerance_minutes * 60 + ), f"Time difference too large: {time_diff}s. Parsed: {parsed_time}, Expected timezone: {current_time_in_tz}" # Verify timezone info exists and format looks reasonable assert parsed_time.tzinfo is not None, "Parsed time should have timezone info" diff --git a/tests/test_utils.py b/tests/test_utils.py index f0ef20a9..23658b84 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -359,13 +359,12 @@ def test_basic_sanitization_no_suffix(): def test_formatter(): - # Example system prompt that has no vars NO_VARS = """ THIS IS A SYSTEM PROMPT WITH NO VARS """ - assert NO_VARS == safe_format(NO_VARS, VARS_DICT) + assert safe_format(NO_VARS, VARS_DICT) == NO_VARS # Example system prompt that has {CORE_MEMORY} CORE_MEMORY_VAR = """ @@ -378,7 +377,7 @@ def test_formatter(): My core memory is that I like to eat bananas """ - assert CORE_MEMORY_VAR_SOL == safe_format(CORE_MEMORY_VAR, VARS_DICT) + assert safe_format(CORE_MEMORY_VAR, VARS_DICT) == CORE_MEMORY_VAR_SOL # Example system prompt that has {CORE_MEMORY} and {USER_MEMORY} (latter doesn't exist) UNUSED_VAR = """ @@ -393,7 +392,7 @@ def test_formatter(): My core memory is that I like to eat bananas """ - assert UNUSED_VAR_SOL == safe_format(UNUSED_VAR, VARS_DICT) + assert safe_format(UNUSED_VAR, VARS_DICT) == UNUSED_VAR_SOL # Example system prompt that has {CORE_MEMORY} and {USER_MEMORY} (latter doesn't exist), AND an empty {} UNUSED_AND_EMPRY_VAR = """ @@ -410,7 +409,7 @@ def test_formatter(): My core memory is that I like to eat bananas """ - assert UNUSED_AND_EMPRY_VAR_SOL == safe_format(UNUSED_AND_EMPRY_VAR, VARS_DICT) + assert safe_format(UNUSED_AND_EMPRY_VAR, VARS_DICT) == UNUSED_AND_EMPRY_VAR_SOL # ---------------------- LineChunker TESTS ---------------------- #