chore: strings lint cleanup (#3374)

This commit is contained in:
Andy Li
2025-07-18 09:20:45 -07:00
committed by GitHub
parent 7b7465382b
commit 904d9ba5a2
43 changed files with 71 additions and 86 deletions

View File

@@ -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"))

View File

@@ -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]

View File

@@ -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)

View File

@@ -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,

View File

@@ -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."
),
},
},

View File

@@ -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)

View File

@@ -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)

View File

@@ -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:

View File

@@ -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(

View File

@@ -20,7 +20,6 @@ from letta.services.message_manager import MessageManager
class SleeptimeMultiAgent(Agent):
def __init__(
self,
interface: AgentInterface,

View File

@@ -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:

View File

@@ -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)

View File

@@ -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:

View File

@@ -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

View File

@@ -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":

View File

@@ -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{"

View File

@@ -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)

View File

@@ -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},
)

View File

@@ -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:

View File

@@ -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:

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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})")

View File

@@ -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

View File

@@ -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:

View File

@@ -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:

View File

@@ -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

View File

@@ -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}',
},
}
],

View File

@@ -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())

View File

@@ -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}")

View File

@@ -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",

View File

@@ -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

View File

@@ -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

View File

@@ -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'}}."))],
)
],
)

View File

@@ -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"],

View File

@@ -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"

View File

@@ -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 ---------------------- #