From 25d54dd896064c054e39a89ff163e2be7a014eae Mon Sep 17 00:00:00 2001 From: Kian Jones <11655409+kianjones9@users.noreply.github.com> Date: Tue, 17 Feb 2026 10:07:40 -0800 Subject: [PATCH] chore: enable F821, F401, W293 (#9503) * auto fixes * auto fix pt2 and transitive deps and undefined var checking locals() * manual fixes (ignored or letta-code fixed) * fix circular import --- .github/scripts/model-sweep/conftest.py | 1 - .github/scripts/model-sweep/model_sweep.py | 4 - ..._add_cascades_to_blocks_agents_fks_set_.py | 2 - ...34_add_agent_id_index_to_mapping_tables.py | 2 - ...145c45d_add_project_constraint_on_tools.py | 2 - ...reate_new_runs_table_and_remove_legacy_.py | 2 - ...54dec07619c4_divide_passage_table_into_.py | 1 + .../57bcea83af3f_add_various_indexes.py | 2 - ...b595051e48_replace_composite_runs_index.py | 2 - ...d4e5f6_add_index_to_step_metrics_run_id.py | 2 - ...43_add_tool_indexes_for_organization_id.py | 2 - ...4f5a6_drop_unused_and_redundant_indexes.py | 2 - ...1b151f_add_vector_db_provider_to_source.py | 2 +- ..._add_and_migrate_encrypted_columns_for_.py | 2 - ...8609d65ff_add_index_on_messages_step_id.py | 2 - ...5fd2b_add_query_optimizing_runs_listing.py | 2 - letta/__init__.py | 42 ++-- letta/adapters/letta_llm_adapter.py | 2 +- letta/adapters/letta_llm_request_adapter.py | 2 +- letta/adapters/letta_llm_stream_adapter.py | 1 - letta/adapters/sglang_native_adapter.py | 200 +++++++++--------- letta/adapters/simple_llm_stream_adapter.py | 1 - letta/agents/helpers.py | 5 +- letta/agents/letta_agent.py | 19 +- letta/agents/letta_agent_v2.py | 16 +- letta/agents/letta_agent_v3.py | 21 +- letta/agents/voice_agent.py | 2 +- letta/agents/voice_sleeptime_agent.py | 4 +- letta/cli/cli.py | 1 - letta/data_sources/connectors.py | 2 +- letta/errors.py | 6 +- letta/functions/function_sets/base.py | 10 +- letta/functions/function_sets/multi_agent.py | 11 +- letta/functions/function_sets/voice.py | 8 +- letta/functions/helpers.py | 26 ++- letta/groups/dynamic_multi_agent.py | 2 +- letta/groups/sleeptime_multi_agent_v2.py | 1 - letta/groups/sleeptime_multi_agent_v3.py | 1 - letta/groups/sleeptime_multi_agent_v4.py | 4 +- letta/groups/supervisor_multi_agent.py | 12 +- letta/helpers/__init__.py | 2 +- letta/helpers/converters.py | 4 +- letta/helpers/tool_execution_helper.py | 2 +- letta/helpers/tpuf_client.py | 35 ++- ..._parallel_tool_call_streaming_interface.py | 6 +- .../anthropic_streaming_interface.py | 12 +- .../interfaces/gemini_streaming_interface.py | 6 +- .../interfaces/openai_streaming_interface.py | 18 +- letta/llm_api/anthropic_client.py | 2 - letta/llm_api/chatgpt_oauth_client.py | 8 +- letta/llm_api/helpers.py | 8 +- letta/llm_api/openai_client.py | 2 - letta/llm_api/sglang_native_client.py | 24 +-- letta/local_llm/constants.py | 2 +- letta/local_llm/settings/settings.py | 2 +- letta/main.py | 2 - letta/orm/__init__.py | 92 ++++---- letta/orm/agent.py | 9 +- letta/orm/agents_tags.py | 2 +- letta/orm/archives_agents.py | 4 +- letta/orm/block.py | 5 +- letta/orm/blocks_tags.py | 2 +- letta/orm/files_agents.py | 2 +- letta/orm/group.py | 8 +- letta/orm/identity.py | 6 +- letta/orm/llm_batch_items.py | 6 +- letta/orm/llm_batch_job.py | 4 +- letta/orm/mcp_server.py | 1 - letta/orm/message.py | 8 +- letta/orm/passage.py | 2 +- letta/orm/provider_trace.py | 2 +- letta/orm/provider_trace_metadata.py | 2 +- letta/orm/run.py | 2 +- letta/orm/run_metrics.py | 3 +- letta/orm/sqlalchemy_base.py | 36 ++-- letta/otel/tracing.py | 1 - letta/plugins/defaults.py | 3 - letta/prompts/prompt_generator.py | 7 + letta/schemas/agent.py | 2 - letta/schemas/letta_message_content.py | 1 - letta/schemas/letta_response.py | 29 +-- letta/schemas/llm_config.py | 2 +- letta/schemas/llm_trace.py | 1 - letta/schemas/mcp.py | 1 - letta/schemas/mcp_server.py | 5 - letta/schemas/memory.py | 6 +- letta/schemas/message.py | 4 +- .../schemas/openai/chat_completion_request.py | 2 +- letta/schemas/provider_model.py | 1 - letta/schemas/providers/base.py | 2 - letta/schemas/providers/together.py | 2 - letta/schemas/source.py | 1 - letta/schemas/tool.py | 2 +- letta/schemas/usage.py | 4 +- letta/serialize_schemas/__init__.py | 2 +- letta/server/rest_api/app.py | 4 +- letta/server/rest_api/proxy_helpers.py | 1 - letta/server/rest_api/routers/v1/agents.py | 19 +- letta/server/rest_api/routers/v1/anthropic.py | 1 - letta/server/rest_api/routers/v1/archives.py | 6 +- letta/server/rest_api/routers/v1/blocks.py | 4 +- .../rest_api/routers/v1/conversations.py | 3 +- letta/server/rest_api/routers/v1/folders.py | 6 +- letta/server/rest_api/routers/v1/groups.py | 4 +- .../server/rest_api/routers/v1/identities.py | 6 +- .../rest_api/routers/v1/internal_runs.py | 1 - letta/server/rest_api/routers/v1/jobs.py | 2 +- .../server/rest_api/routers/v1/mcp_servers.py | 6 +- letta/server/rest_api/routers/v1/messages.py | 2 +- letta/server/rest_api/routers/v1/providers.py | 2 +- letta/server/rest_api/routers/v1/runs.py | 1 - .../rest_api/routers/v1/sandbox_configs.py | 1 - letta/server/rest_api/routers/v1/sources.py | 7 +- letta/server/rest_api/routers/v1/steps.py | 2 +- letta/server/rest_api/routers/v1/tools.py | 13 +- letta/server/rest_api/routers/v1/zai.py | 1 - letta/server/rest_api/utils.py | 2 +- letta/server/server.py | 43 +--- letta/server/ws_api/example_client.py | 1 + letta/server/ws_api/server.py | 2 +- .../agent_generate_completion_manager.py | 4 +- letta/services/agent_manager.py | 17 +- letta/services/agent_serialization_manager.py | 13 +- letta/services/archive_manager.py | 1 - letta/services/block_manager.py | 1 - letta/services/block_manager_git.py | 1 - .../token_counter.py | 3 +- letta/services/file_manager.py | 1 - letta/services/identity_manager.py | 1 - letta/services/job_manager.py | 7 +- letta/services/mcp_manager.py | 5 +- letta/services/mcp_server_manager.py | 3 +- letta/services/memory_repo/git_operations.py | 3 +- .../services/memory_repo/memfs_client_base.py | 2 +- letta/services/memory_repo/storage/base.py | 2 +- letta/services/message_manager.py | 2 +- letta/services/passage_manager.py | 8 +- letta/services/provider_manager.py | 1 - .../provider_trace_backends/clickhouse.py | 1 - letta/services/run_manager.py | 9 +- letta/services/source_manager.py | 1 - letta/services/step_manager.py | 2 +- letta/services/summarizer/summarizer.py | 8 +- .../summarizer/summarizer_sliding_window.py | 9 +- .../tool_executor/core_tool_executor.py | 4 +- .../multi_agent_tool_executor.py | 1 - .../tool_executor/sandbox_tool_executor.py | 2 +- .../tool_executor/tool_execution_sandbox.py | 6 +- letta/services/tool_manager.py | 5 +- letta/services/tool_sandbox/modal_sandbox.py | 9 +- letta/services/webhook_service.py | 1 - letta/types/__init__.py | 2 +- letta/utils.py | 2 - pyproject.toml | 3 - ...letta_llm_stream_adapter_error_handling.py | 1 - tests/conftest.py | 1 - tests/integration_test_agent_tool_graph.py | 4 +- tests/integration_test_async_tool_sandbox.py | 4 +- tests/integration_test_chat_completions.py | 2 - tests/integration_test_conversations_sdk.py | 1 - tests/integration_test_human_in_the_loop.py | 2 - tests/integration_test_mcp.py | 1 - tests/integration_test_modal.py | 1 - ...tegration_test_multi_modal_tool_returns.py | 2 +- tests/integration_test_override_model.py | 2 +- tests/integration_test_send_message.py | 4 +- tests/integration_test_sleeptime_agent.py | 1 - tests/integration_test_summarizer.py | 3 +- tests/integration_test_turbopuffer.py | 2 +- ..._test_typescript_tool_execution_sandbox.py | 1 - tests/integration_test_usage_tracking.py | 2 +- tests/managers/conftest.py | 3 +- tests/managers/test_agent_manager.py | 81 +------ tests/managers/test_agent_tag_manager.py | 90 +------- tests/managers/test_archive_manager.py | 90 +------- tests/managers/test_block_manager.py | 81 +------ tests/managers/test_cancellation.py | 5 +- tests/managers/test_file_manager.py | 93 +------- tests/managers/test_group_manager.py | 96 --------- tests/managers/test_identity_manager.py | 95 +-------- tests/managers/test_job_manager.py | 96 +-------- tests/managers/test_mcp_manager.py | 95 +-------- tests/managers/test_message_manager.py | 92 +------- tests/managers/test_organization_manager.py | 96 +-------- tests/managers/test_passage_manager.py | 87 +------- tests/managers/test_provider_manager.py | 11 +- tests/managers/test_run_manager.py | 92 +------- tests/managers/test_sandbox_manager.py | 84 +------- tests/managers/test_source_manager.py | 80 +------ tests/managers/test_tool_manager.py | 78 +------ tests/managers/test_user_manager.py | 92 +------- tests/mcp_tests/test_mcp_schema_validation.py | 2 - tests/mock_mcp_server.py | 4 +- .../test_agent_mass_creation.py | 2 +- .../test_agent_mass_update.py | 2 +- tests/test_crypto_utils.py | 2 - tests/test_exception_logging.py | 6 +- tests/test_internal_agents_count.py | 1 - tests/test_log_context.py | 2 - tests/test_mcp_encryption.py | 7 +- tests/test_prompt_caching.py | 2 - tests/test_provider_trace.py | 1 - tests/test_provider_trace_backends.py | 6 +- tests/test_provider_trace_summarization.py | 1 - tests/test_sdk_client.py | 2 - tests/test_secret.py | 3 +- tests/test_server.py | 23 +- tests/test_server_providers.py | 11 +- tests/test_sources.py | 3 +- tests/test_temporal_metrics_local.py | 4 +- tests/test_tool_schema_parsing.py | 2 +- 211 files changed, 534 insertions(+), 2243 deletions(-) diff --git a/.github/scripts/model-sweep/conftest.py b/.github/scripts/model-sweep/conftest.py index 146bf058..edc0ae34 100644 --- a/.github/scripts/model-sweep/conftest.py +++ b/.github/scripts/model-sweep/conftest.py @@ -16,7 +16,6 @@ from letta.schemas.agent import AgentState from letta.schemas.llm_config import LLMConfig from letta.services.organization_manager import OrganizationManager from letta.services.user_manager import UserManager -from letta.settings import tool_settings def pytest_configure(config): diff --git a/.github/scripts/model-sweep/model_sweep.py b/.github/scripts/model-sweep/model_sweep.py index 97a19306..ec61e936 100644 --- a/.github/scripts/model-sweep/model_sweep.py +++ b/.github/scripts/model-sweep/model_sweep.py @@ -1,16 +1,12 @@ import base64 import json import os -import socket -import threading import time import uuid from typing import Any, Dict, List import httpx import pytest -import requests -from dotenv import load_dotenv from letta_client import Letta, MessageCreate, Run from letta_client.core.api_error import ApiError from letta_client.types import ( diff --git a/alembic/versions/038e68cdf0df_add_cascades_to_blocks_agents_fks_set_.py b/alembic/versions/038e68cdf0df_add_cascades_to_blocks_agents_fks_set_.py index 83406ef5..81f0e7d1 100644 --- a/alembic/versions/038e68cdf0df_add_cascades_to_blocks_agents_fks_set_.py +++ b/alembic/versions/038e68cdf0df_add_cascades_to_blocks_agents_fks_set_.py @@ -8,8 +8,6 @@ Create Date: 2025-10-07 13:01:17.872405 from typing import Sequence, Union -import sqlalchemy as sa - from alembic import op # revision identifiers, used by Alembic. diff --git a/alembic/versions/18ff61fbc034_add_agent_id_index_to_mapping_tables.py b/alembic/versions/18ff61fbc034_add_agent_id_index_to_mapping_tables.py index 825ead37..29e1c65d 100644 --- a/alembic/versions/18ff61fbc034_add_agent_id_index_to_mapping_tables.py +++ b/alembic/versions/18ff61fbc034_add_agent_id_index_to_mapping_tables.py @@ -8,8 +8,6 @@ Create Date: 2025-09-10 19:16:39.118760 from typing import Sequence, Union -import sqlalchemy as sa - from alembic import op # revision identifiers, used by Alembic. diff --git a/alembic/versions/39577145c45d_add_project_constraint_on_tools.py b/alembic/versions/39577145c45d_add_project_constraint_on_tools.py index 98a6b6be..b9d4e866 100644 --- a/alembic/versions/39577145c45d_add_project_constraint_on_tools.py +++ b/alembic/versions/39577145c45d_add_project_constraint_on_tools.py @@ -8,8 +8,6 @@ Create Date: 2025-12-17 15:46:06.184858 from typing import Sequence, Union -import sqlalchemy as sa - from alembic import op # revision identifiers, used by Alembic. diff --git a/alembic/versions/3bc3c031fbe4_create_new_runs_table_and_remove_legacy_.py b/alembic/versions/3bc3c031fbe4_create_new_runs_table_and_remove_legacy_.py index 5339801b..3a194649 100644 --- a/alembic/versions/3bc3c031fbe4_create_new_runs_table_and_remove_legacy_.py +++ b/alembic/versions/3bc3c031fbe4_create_new_runs_table_and_remove_legacy_.py @@ -8,8 +8,6 @@ Create Date: 2025-10-03 12:10:51.065067 from typing import Sequence, Union -import sqlalchemy as sa - from alembic import op # revision identifiers, used by Alembic. diff --git a/alembic/versions/54dec07619c4_divide_passage_table_into_.py b/alembic/versions/54dec07619c4_divide_passage_table_into_.py index e58a490a..e8c85fef 100644 --- a/alembic/versions/54dec07619c4_divide_passage_table_into_.py +++ b/alembic/versions/54dec07619c4_divide_passage_table_into_.py @@ -9,6 +9,7 @@ Create Date: 2024-12-14 17:23:08.772554 from typing import Sequence, Union import sqlalchemy as sa +from pgvector.sqlalchemy import Vector from sqlalchemy.dialects import postgresql from alembic import op diff --git a/alembic/versions/57bcea83af3f_add_various_indexes.py b/alembic/versions/57bcea83af3f_add_various_indexes.py index a2e71cd4..14a2f091 100644 --- a/alembic/versions/57bcea83af3f_add_various_indexes.py +++ b/alembic/versions/57bcea83af3f_add_various_indexes.py @@ -8,8 +8,6 @@ Create Date: 2025-09-19 10:58:19.658106 from typing import Sequence, Union -import sqlalchemy as sa - from alembic import op # revision identifiers, used by Alembic. diff --git a/alembic/versions/89b595051e48_replace_composite_runs_index.py b/alembic/versions/89b595051e48_replace_composite_runs_index.py index 8988a376..ea5494de 100644 --- a/alembic/versions/89b595051e48_replace_composite_runs_index.py +++ b/alembic/versions/89b595051e48_replace_composite_runs_index.py @@ -8,8 +8,6 @@ Create Date: 2025-10-06 13:17:09.918439 from typing import Sequence, Union -import sqlalchemy as sa - from alembic import op # revision identifiers, used by Alembic. diff --git a/alembic/versions/a1b2c3d4e5f6_add_index_to_step_metrics_run_id.py b/alembic/versions/a1b2c3d4e5f6_add_index_to_step_metrics_run_id.py index d2a43356..6970a70a 100644 --- a/alembic/versions/a1b2c3d4e5f6_add_index_to_step_metrics_run_id.py +++ b/alembic/versions/a1b2c3d4e5f6_add_index_to_step_metrics_run_id.py @@ -8,8 +8,6 @@ Create Date: 2025-11-11 19:16:00.000000 from typing import Sequence, Union -import sqlalchemy as sa - from alembic import op from letta.settings import settings diff --git a/alembic/versions/af842aa6f743_add_tool_indexes_for_organization_id.py b/alembic/versions/af842aa6f743_add_tool_indexes_for_organization_id.py index 4295ae72..967532d5 100644 --- a/alembic/versions/af842aa6f743_add_tool_indexes_for_organization_id.py +++ b/alembic/versions/af842aa6f743_add_tool_indexes_for_organization_id.py @@ -8,8 +8,6 @@ Create Date: 2025-12-07 15:30:43.407495 from typing import Sequence, Union -import sqlalchemy as sa - from alembic import op # revision identifiers, used by Alembic. diff --git a/alembic/versions/b1c2d3e4f5a6_drop_unused_and_redundant_indexes.py b/alembic/versions/b1c2d3e4f5a6_drop_unused_and_redundant_indexes.py index e1c72dd1..6909f61f 100644 --- a/alembic/versions/b1c2d3e4f5a6_drop_unused_and_redundant_indexes.py +++ b/alembic/versions/b1c2d3e4f5a6_drop_unused_and_redundant_indexes.py @@ -8,8 +8,6 @@ Create Date: 2025-11-11 21:16:00.000000 from typing import Sequence, Union -import sqlalchemy as sa - from alembic import op from letta.settings import settings diff --git a/alembic/versions/b888f21b151f_add_vector_db_provider_to_source.py b/alembic/versions/b888f21b151f_add_vector_db_provider_to_source.py index 8b909295..ba91726f 100644 --- a/alembic/versions/b888f21b151f_add_vector_db_provider_to_source.py +++ b/alembic/versions/b888f21b151f_add_vector_db_provider_to_source.py @@ -23,7 +23,7 @@ depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: # determine backfill value based on current pinecone settings try: - from pinecone import IndexEmbed, PineconeAsyncio + from pinecone import IndexEmbed, PineconeAsyncio # noqa: F401 pinecone_available = True except ImportError: diff --git a/alembic/versions/d06594144ef3_add_and_migrate_encrypted_columns_for_.py b/alembic/versions/d06594144ef3_add_and_migrate_encrypted_columns_for_.py index 9fa5fec5..f2d54413 100644 --- a/alembic/versions/d06594144ef3_add_and_migrate_encrypted_columns_for_.py +++ b/alembic/versions/d06594144ef3_add_and_migrate_encrypted_columns_for_.py @@ -10,8 +10,6 @@ import json import os # Add the app directory to path to import our crypto utils -import sys -from pathlib import Path from typing import Sequence, Union import sqlalchemy as sa diff --git a/alembic/versions/d798609d65ff_add_index_on_messages_step_id.py b/alembic/versions/d798609d65ff_add_index_on_messages_step_id.py index a289a62a..091a9636 100644 --- a/alembic/versions/d798609d65ff_add_index_on_messages_step_id.py +++ b/alembic/versions/d798609d65ff_add_index_on_messages_step_id.py @@ -8,8 +8,6 @@ Create Date: 2025-11-07 15:43:59.446292 from typing import Sequence, Union -import sqlalchemy as sa - from alembic import op from letta.settings import settings diff --git a/alembic/versions/f9ad1c25fd2b_add_query_optimizing_runs_listing.py b/alembic/versions/f9ad1c25fd2b_add_query_optimizing_runs_listing.py index d4a265b6..61ba9ac1 100644 --- a/alembic/versions/f9ad1c25fd2b_add_query_optimizing_runs_listing.py +++ b/alembic/versions/f9ad1c25fd2b_add_query_optimizing_runs_listing.py @@ -8,8 +8,6 @@ Create Date: 2025-10-04 00:44:06.663817 from typing import Sequence, Union -import sqlalchemy as sa - from alembic import op # revision identifiers, used by Alembic. diff --git a/letta/__init__.py b/letta/__init__.py index 5019bbad..bb343695 100644 --- a/letta/__init__.py +++ b/letta/__init__.py @@ -16,26 +16,32 @@ try: from letta.settings import DatabaseChoice, settings if settings.database_engine == DatabaseChoice.SQLITE: - from letta.orm import sqlite_functions + from letta.orm import sqlite_functions # noqa: F401 except ImportError: # If sqlite_vec is not installed, it's fine for client usage pass # # imports for easier access -from letta.schemas.agent import AgentState -from letta.schemas.block import Block -from letta.schemas.embedding_config import EmbeddingConfig -from letta.schemas.enums import JobStatus -from letta.schemas.file import FileMetadata -from letta.schemas.job import Job -from letta.schemas.letta_message import LettaMessage, LettaPing -from letta.schemas.letta_stop_reason import LettaStopReason -from letta.schemas.llm_config import LLMConfig -from letta.schemas.memory import ArchivalMemorySummary, BasicBlockMemory, ChatMemory, Memory, RecallMemorySummary -from letta.schemas.message import Message -from letta.schemas.organization import Organization -from letta.schemas.passage import Passage -from letta.schemas.source import Source -from letta.schemas.tool import Tool -from letta.schemas.usage import LettaUsageStatistics -from letta.schemas.user import User +from letta.schemas.agent import AgentState as AgentState +from letta.schemas.block import Block as Block +from letta.schemas.embedding_config import EmbeddingConfig as EmbeddingConfig +from letta.schemas.enums import JobStatus as JobStatus +from letta.schemas.file import FileMetadata as FileMetadata +from letta.schemas.job import Job as Job +from letta.schemas.letta_message import LettaErrorMessage as LettaErrorMessage, LettaMessage as LettaMessage, LettaPing as LettaPing +from letta.schemas.letta_stop_reason import LettaStopReason as LettaStopReason +from letta.schemas.llm_config import LLMConfig as LLMConfig +from letta.schemas.memory import ( + ArchivalMemorySummary as ArchivalMemorySummary, + BasicBlockMemory as BasicBlockMemory, + ChatMemory as ChatMemory, + Memory as Memory, + RecallMemorySummary as RecallMemorySummary, +) +from letta.schemas.message import Message as Message +from letta.schemas.organization import Organization as Organization +from letta.schemas.passage import Passage as Passage +from letta.schemas.source import Source as Source +from letta.schemas.tool import Tool as Tool +from letta.schemas.usage import LettaUsageStatistics as LettaUsageStatistics +from letta.schemas.user import User as User diff --git a/letta/adapters/letta_llm_adapter.py b/letta/adapters/letta_llm_adapter.py index ba14b3d4..d75c2367 100644 --- a/letta/adapters/letta_llm_adapter.py +++ b/letta/adapters/letta_llm_adapter.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import AsyncGenerator, Optional +from typing import AsyncGenerator from letta.llm_api.llm_client_base import LLMClientBase from letta.schemas.enums import LLMCallType diff --git a/letta/adapters/letta_llm_request_adapter.py b/letta/adapters/letta_llm_request_adapter.py index 21bc543d..221e2a76 100644 --- a/letta/adapters/letta_llm_request_adapter.py +++ b/letta/adapters/letta_llm_request_adapter.py @@ -2,7 +2,7 @@ from typing import AsyncGenerator from letta.adapters.letta_llm_adapter import LettaLLMAdapter from letta.helpers.datetime_helpers import get_utc_timestamp_ns -from letta.otel.tracing import log_attributes, log_event, safe_json_dumps, trace_method +from letta.otel.tracing import log_attributes, safe_json_dumps, trace_method from letta.schemas.letta_message import LettaMessage from letta.schemas.letta_message_content import OmittedReasoningContent, ReasoningContent, TextContent from letta.schemas.provider_trace import ProviderTrace diff --git a/letta/adapters/letta_llm_stream_adapter.py b/letta/adapters/letta_llm_stream_adapter.py index c345ee74..de1e47e4 100644 --- a/letta/adapters/letta_llm_stream_adapter.py +++ b/letta/adapters/letta_llm_stream_adapter.py @@ -11,7 +11,6 @@ from letta.schemas.enums import LLMCallType, ProviderType from letta.schemas.letta_message import LettaMessage from letta.schemas.llm_config import LLMConfig from letta.schemas.provider_trace import ProviderTrace -from letta.schemas.usage import LettaUsageStatistics from letta.schemas.user import User from letta.settings import settings from letta.utils import safe_create_task diff --git a/letta/adapters/sglang_native_adapter.py b/letta/adapters/sglang_native_adapter.py index fbaa07e0..ad0f0e88 100644 --- a/letta/adapters/sglang_native_adapter.py +++ b/letta/adapters/sglang_native_adapter.py @@ -19,18 +19,17 @@ from letta.helpers.datetime_helpers import get_utc_timestamp_ns from letta.llm_api.sglang_native_client import SGLangNativeClient from letta.log import get_logger from letta.schemas.letta_message import LettaMessage -from letta.schemas.letta_message_content import OmittedReasoningContent, ReasoningContent, TextContent +from letta.schemas.letta_message_content import TextContent from letta.schemas.openai.chat_completion_response import ( ChatCompletionResponse, + ChatCompletionTokenLogprob, Choice, ChoiceLogprobs, - ChatCompletionTokenLogprob, FunctionCall, Message as ChoiceMessage, ToolCall, UsageStatistics, ) -from letta.schemas.usage import normalize_cache_tokens, normalize_reasoning_tokens logger = get_logger(__name__) @@ -41,37 +40,38 @@ _tokenizer_cache: dict[str, Any] = {} class SGLangNativeAdapter(SimpleLLMRequestAdapter): """ Adapter that uses SGLang's native /generate endpoint for multi-turn RL training. - + Key differences from SimpleLLMRequestAdapter: - Uses /generate instead of /v1/chat/completions - Returns output_ids (token IDs) in addition to text - Returns output_token_logprobs with [logprob, token_id] pairs - Formats tools into prompt and parses tool calls from response - + These are essential for building accurate loss masks in multi-turn training. """ - + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._sglang_client: Optional[SGLangNativeClient] = None self._tokenizer: Any = None - + def _get_tokenizer(self) -> Any: """Get or create tokenizer for the model.""" global _tokenizer_cache - + # Get model name from llm_config model_name = self.llm_config.model if not model_name: logger.warning("No model name in llm_config, cannot load tokenizer") return None - + # Check cache if model_name in _tokenizer_cache: return _tokenizer_cache[model_name] - + try: from transformers import AutoTokenizer + logger.info(f"Loading tokenizer for model: {model_name}") tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True) _tokenizer_cache[model_name] = tokenizer @@ -82,7 +82,7 @@ class SGLangNativeAdapter(SimpleLLMRequestAdapter): except Exception as e: logger.warning(f"Failed to load tokenizer: {e}, falling back to manual formatting") return None - + def _get_sglang_client(self) -> SGLangNativeClient: """Get or create SGLang native client.""" if self._sglang_client is None: @@ -94,17 +94,17 @@ class SGLangNativeAdapter(SimpleLLMRequestAdapter): api_key=None, ) return self._sglang_client - + def _format_tools_for_prompt(self, tools: list) -> str: """ Format tools in Qwen3 chat template format for the system prompt. - + This matches the exact format produced by Qwen3's tokenizer.apply_chat_template() with tools parameter. """ if not tools: return "" - + # Format each tool as JSON (matching Qwen3 template exactly) tool_jsons = [] for tool in tools: @@ -120,84 +120,85 @@ class SGLangNativeAdapter(SimpleLLMRequestAdapter): "name": getattr(getattr(tool, "function", tool), "name", ""), "description": getattr(getattr(tool, "function", tool), "description", ""), "parameters": getattr(getattr(tool, "function", tool), "parameters", {}), - } + }, } tool_jsons.append(json.dumps(tool_dict)) - + # Use exact Qwen3 format tools_section = ( "\n\n# Tools\n\n" "You may call one or more functions to assist with the user query.\n\n" "You are provided with function signatures within XML tags:\n" - "\n" - + "\n".join(tool_jsons) + "\n" + "\n" + "\n".join(tool_jsons) + "\n" "\n\n" "For each function call, return a json object with function name and arguments within XML tags:\n" "\n" '{"name": , "arguments": }\n' "" ) - + return tools_section - + def _convert_messages_to_openai_format(self, messages: list) -> list[dict]: """Convert Letta Message objects to OpenAI-style message dicts.""" openai_messages = [] - + for msg in messages: # Handle both dict and Pydantic Message objects - if hasattr(msg, 'role'): + if hasattr(msg, "role"): role = msg.role - content = msg.content if hasattr(msg, 'content') else "" + content = msg.content if hasattr(msg, "content") else "" # Handle content that might be a list of content parts if isinstance(content, list): - content = " ".join([c.text if hasattr(c, 'text') else str(c) for c in content]) + content = " ".join([c.text if hasattr(c, "text") else str(c) for c in content]) elif content is None: content = "" - tool_calls = getattr(msg, 'tool_calls', None) - tool_call_id = getattr(msg, 'tool_call_id', None) - name = getattr(msg, 'name', None) + tool_calls = getattr(msg, "tool_calls", None) + tool_call_id = getattr(msg, "tool_call_id", None) + name = getattr(msg, "name", None) else: role = msg.get("role", "user") content = msg.get("content", "") tool_calls = msg.get("tool_calls", None) tool_call_id = msg.get("tool_call_id", None) name = msg.get("name", None) - + openai_msg = {"role": role, "content": content} - + if tool_calls: # Convert tool calls to OpenAI format openai_tool_calls = [] for tc in tool_calls: - if hasattr(tc, 'function'): + if hasattr(tc, "function"): tc_dict = { - "id": getattr(tc, 'id', f"call_{uuid.uuid4().hex[:8]}"), + "id": getattr(tc, "id", f"call_{uuid.uuid4().hex[:8]}"), "type": "function", "function": { "name": tc.function.name, - "arguments": tc.function.arguments if isinstance(tc.function.arguments, str) else json.dumps(tc.function.arguments) - } + "arguments": tc.function.arguments + if isinstance(tc.function.arguments, str) + else json.dumps(tc.function.arguments), + }, } else: tc_dict = { "id": tc.get("id", f"call_{uuid.uuid4().hex[:8]}"), "type": "function", - "function": tc.get("function", {}) + "function": tc.get("function", {}), } openai_tool_calls.append(tc_dict) openai_msg["tool_calls"] = openai_tool_calls - + if tool_call_id: openai_msg["tool_call_id"] = tool_call_id - + if name and role == "tool": openai_msg["name"] = name - + openai_messages.append(openai_msg) - + return openai_messages - + def _convert_tools_to_openai_format(self, tools: list) -> list[dict]: """Convert tools to OpenAI format for tokenizer.""" openai_tools = [] @@ -218,24 +219,24 @@ class SGLangNativeAdapter(SimpleLLMRequestAdapter): "name": getattr(func, "name", ""), "description": getattr(func, "description", ""), "parameters": getattr(func, "parameters", {}), - } + }, } openai_tools.append(tool_dict) return openai_tools - + def _format_messages_to_text(self, messages: list, tools: list) -> str: """ Format messages to text using tokenizer's apply_chat_template if available. - + Falls back to manual formatting if tokenizer is not available. """ tokenizer = self._get_tokenizer() - + if tokenizer is not None: # Use tokenizer's apply_chat_template for proper formatting openai_messages = self._convert_messages_to_openai_format(messages) openai_tools = self._convert_tools_to_openai_format(tools) if tools else None - + try: formatted = tokenizer.apply_chat_template( openai_messages, @@ -247,30 +248,30 @@ class SGLangNativeAdapter(SimpleLLMRequestAdapter): return formatted except Exception as e: logger.warning(f"apply_chat_template failed: {e}, falling back to manual formatting") - + # Fallback to manual formatting return self._format_messages_to_text_manual(messages, tools) - + def _format_messages_to_text_manual(self, messages: list, tools: list) -> str: """Manual fallback formatting for when tokenizer is not available.""" formatted_parts = [] tools_section = self._format_tools_for_prompt(tools) - + for msg in messages: # Handle both dict and Pydantic Message objects - if hasattr(msg, 'role'): + if hasattr(msg, "role"): role = msg.role - content = msg.content if hasattr(msg, 'content') else "" + content = msg.content if hasattr(msg, "content") else "" if isinstance(content, list): - content = " ".join([c.text if hasattr(c, 'text') else str(c) for c in content]) + content = " ".join([c.text if hasattr(c, "text") else str(c) for c in content]) elif content is None: content = "" - tool_calls = getattr(msg, 'tool_calls', None) + tool_calls = getattr(msg, "tool_calls", None) else: role = msg.get("role", "user") content = msg.get("content", "") tool_calls = msg.get("tool_calls", None) - + if role == "system": system_content = content + tools_section if tools_section else content formatted_parts.append(f"<|im_start|>system\n{system_content}<|im_end|>") @@ -281,62 +282,55 @@ class SGLangNativeAdapter(SimpleLLMRequestAdapter): if tool_calls: tc_parts = [] for tc in tool_calls: - if hasattr(tc, 'function'): + if hasattr(tc, "function"): tc_name = tc.function.name tc_args = tc.function.arguments else: tc_name = tc.get("function", {}).get("name", "") tc_args = tc.get("function", {}).get("arguments", "{}") - + if isinstance(tc_args, str): try: tc_args = json.loads(tc_args) except: pass - - tc_parts.append( - f"\n" - f'{{"name": "{tc_name}", "arguments": {json.dumps(tc_args)}}}\n' - f"" - ) - + + tc_parts.append(f'\n{{"name": "{tc_name}", "arguments": {json.dumps(tc_args)}}}\n') + assistant_content = content + "\n" + "\n".join(tc_parts) if content else "\n".join(tc_parts) formatted_parts.append(f"<|im_start|>assistant\n{assistant_content}<|im_end|>") elif content: formatted_parts.append(f"<|im_start|>assistant\n{content}<|im_end|>") elif role == "tool": - formatted_parts.append( - f"<|im_start|>user\n" - f"\n{content}\n<|im_end|>" - ) - + formatted_parts.append(f"<|im_start|>user\n\n{content}\n<|im_end|>") + formatted_parts.append("<|im_start|>assistant\n") return "\n".join(formatted_parts) - + def _parse_tool_calls(self, text: str) -> list[ToolCall]: """ Parse tool calls from response text. - + Looks for patterns like: {"name": "tool_name", "arguments": {...}} """ tool_calls = [] - + # Find all tool_call blocks - pattern = r'\s*(\{.*?\})\s*' + pattern = r"\s*(\{.*?\})\s*" matches = re.findall(pattern, text, re.DOTALL) - + for match in matches: try: tc_data = json.loads(match) name = tc_data.get("name", "") arguments = tc_data.get("arguments", {}) - + if isinstance(arguments, dict): arguments = json.dumps(arguments) - + tool_call = ToolCall( id=f"call_{uuid.uuid4().hex[:8]}", type="function", @@ -349,17 +343,17 @@ class SGLangNativeAdapter(SimpleLLMRequestAdapter): except json.JSONDecodeError as e: logger.warning(f"Failed to parse tool call JSON: {e}") continue - + return tool_calls - + def _extract_content_without_tool_calls(self, text: str) -> str: """Extract content from response, removing tool_call blocks.""" # Remove tool_call blocks - cleaned = re.sub(r'.*?', '', text, flags=re.DOTALL) + cleaned = re.sub(r".*?", "", text, flags=re.DOTALL) # Clean up whitespace cleaned = cleaned.strip() return cleaned - + async def invoke_llm( self, request_data: dict, @@ -372,7 +366,7 @@ class SGLangNativeAdapter(SimpleLLMRequestAdapter): ) -> AsyncGenerator[LettaMessage | None, None]: """ Execute LLM request using SGLang native endpoint. - + This method: 1. Formats messages and tools to text using chat template 2. Calls SGLang native /generate endpoint @@ -381,20 +375,20 @@ class SGLangNativeAdapter(SimpleLLMRequestAdapter): 5. Converts response to standard format """ self.request_data = request_data - + # Get sampling params from request_data sampling_params = { "temperature": request_data.get("temperature", 0.7), "max_new_tokens": request_data.get("max_tokens", 4096), "top_p": request_data.get("top_p", 0.9), } - + # Format messages to text (includes tools in prompt) text_input = self._format_messages_to_text(messages, tools) - + # Call SGLang native endpoint client = self._get_sglang_client() - + try: response = await client.generate( text=text_input, @@ -404,31 +398,31 @@ class SGLangNativeAdapter(SimpleLLMRequestAdapter): except Exception as e: logger.error(f"SGLang native endpoint error: {e}") raise - + self.llm_request_finish_timestamp_ns = get_utc_timestamp_ns() - + # Store native response data self.response_data = response - + # Extract SGLang native data self.output_ids = response.get("output_ids") # output_token_logprobs is inside meta_info meta_info = response.get("meta_info", {}) self.output_token_logprobs = meta_info.get("output_token_logprobs") - + # Extract text response text_response = response.get("text", "") - + # Remove trailing end token if present if text_response.endswith("<|im_end|>"): text_response = text_response[:-10] - + # Parse tool calls from response parsed_tool_calls = self._parse_tool_calls(text_response) - + # Extract content (text without tool_call blocks) content_text = self._extract_content_without_tool_calls(text_response) - + # Determine finish reason meta_info = response.get("meta_info", {}) finish_reason_info = meta_info.get("finish_reason", {}) @@ -436,11 +430,11 @@ class SGLangNativeAdapter(SimpleLLMRequestAdapter): finish_reason = finish_reason_info.get("type", "stop") else: finish_reason = "stop" - + # If we have tool calls, set finish_reason to tool_calls if parsed_tool_calls: finish_reason = "tool_calls" - + # Convert to standard ChatCompletionResponse format for compatibility # Build logprobs in OpenAI format from SGLang format logprobs_content = None @@ -458,13 +452,13 @@ class SGLangNativeAdapter(SimpleLLMRequestAdapter): top_logprobs=[], ) ) - + choice_logprobs = ChoiceLogprobs(content=logprobs_content) if logprobs_content else None - + # Build chat completion response prompt_tokens = meta_info.get("prompt_tokens", 0) completion_tokens = len(self.output_ids) if self.output_ids else 0 - + self.chat_completions_response = ChatCompletionResponse( id=meta_info.get("id", "sglang-native"), created=int(time.time()), @@ -486,36 +480,36 @@ class SGLangNativeAdapter(SimpleLLMRequestAdapter): total_tokens=prompt_tokens + completion_tokens, ), ) - + # Extract content if content_text: self.content = [TextContent(text=content_text)] else: self.content = None - + # No reasoning content from native endpoint self.reasoning_content = None - + # Set tool calls self.tool_calls = parsed_tool_calls self.tool_call = parsed_tool_calls[0] if parsed_tool_calls else None - + # Set logprobs self.logprobs = choice_logprobs - + # Extract usage statistics self.usage.step_count = 1 self.usage.completion_tokens = completion_tokens self.usage.prompt_tokens = prompt_tokens self.usage.total_tokens = prompt_tokens + completion_tokens - + self.log_provider_trace(step_id=step_id, actor=actor) - + logger.info( f"SGLang native response: {len(self.output_ids or [])} tokens, " f"{len(self.output_token_logprobs or [])} logprobs, " f"{len(parsed_tool_calls)} tool calls" ) - + yield None return diff --git a/letta/adapters/simple_llm_stream_adapter.py b/letta/adapters/simple_llm_stream_adapter.py index f09fcaea..492260d8 100644 --- a/letta/adapters/simple_llm_stream_adapter.py +++ b/letta/adapters/simple_llm_stream_adapter.py @@ -1,4 +1,3 @@ -import json from typing import AsyncGenerator, List from letta.adapters.letta_llm_stream_adapter import LettaLLMStreamAdapter diff --git a/letta/agents/helpers.py b/letta/agents/helpers.py index b99c8d44..7f53aa69 100644 --- a/letta/agents/helpers.py +++ b/letta/agents/helpers.py @@ -1,5 +1,4 @@ import json -import uuid import xml.etree.ElementTree as ET from typing import Any, Dict, List, Optional, Tuple from uuid import UUID, uuid4 @@ -15,7 +14,7 @@ from letta.schemas.letta_message import MessageType from letta.schemas.letta_message_content import TextContent from letta.schemas.letta_response import LettaResponse from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.message import ApprovalCreate, Message, MessageCreate, MessageCreateBase, ToolReturnCreate +from letta.schemas.message import ApprovalCreate, Message, MessageCreate, MessageCreateBase from letta.schemas.tool_execution_result import ToolExecutionResult from letta.schemas.usage import LettaUsageStatistics from letta.schemas.user import User @@ -463,7 +462,7 @@ def _schema_accepts_value(prop_schema: Dict[str, Any], value: Any) -> bool: return True -def merge_and_validate_prefilled_args(tool: "Tool", llm_args: Dict[str, Any], prefilled_args: Dict[str, Any]) -> Dict[str, Any]: +def merge_and_validate_prefilled_args(tool: "Tool", llm_args: Dict[str, Any], prefilled_args: Dict[str, Any]) -> Dict[str, Any]: # noqa: F821 """Merge LLM-provided args with prefilled args from tool rules. - Overlapping keys are replaced by prefilled values (prefilled wins). diff --git a/letta/agents/letta_agent.py b/letta/agents/letta_agent.py index 0001e59a..a9fa5dea 100644 --- a/letta/agents/letta_agent.py +++ b/letta/agents/letta_agent.py @@ -13,7 +13,6 @@ from letta.agents.ephemeral_summary_agent import EphemeralSummaryAgent from letta.agents.helpers import ( _build_rule_violation_result, _create_letta_response, - _load_last_function_response, _pop_heartbeat, _prepare_in_context_messages_no_persist_async, _safe_load_tool_call_str, @@ -293,6 +292,7 @@ class LettaAgent(BaseAgent): agent_step_span.set_attributes({"step_id": step_id}) step_progression = StepProgression.START + caught_exception = None should_continue = False step_metrics = StepMetrics(id=step_id) # Initialize metrics tracking @@ -439,6 +439,7 @@ class LettaAgent(BaseAgent): ) except Exception as e: + caught_exception = e # Handle any unexpected errors during step processing self.logger.error(f"Error during step processing: {e}") job_update_metadata = {"error": str(e)} @@ -485,8 +486,8 @@ class LettaAgent(BaseAgent): await self.step_manager.update_step_error_async( actor=self.actor, step_id=step_id, # Use original step_id for telemetry - error_type=type(e).__name__ if "e" in locals() else "Unknown", - error_message=str(e) if "e" in locals() else "Unknown error", + error_type=type(caught_exception).__name__ if caught_exception is not None else "Unknown", + error_message=str(caught_exception) if caught_exception is not None else "Unknown error", error_traceback=traceback.format_exc(), stop_reason=stop_reason, ) @@ -632,6 +633,7 @@ class LettaAgent(BaseAgent): agent_step_span.set_attributes({"step_id": step_id}) step_progression = StepProgression.START + caught_exception = None should_continue = False step_metrics = StepMetrics(id=step_id) # Initialize metrics tracking @@ -768,6 +770,7 @@ class LettaAgent(BaseAgent): ) except Exception as e: + caught_exception = e # Handle any unexpected errors during step processing self.logger.error(f"Error during step processing: {e}") job_update_metadata = {"error": str(e)} @@ -810,8 +813,8 @@ class LettaAgent(BaseAgent): await self.step_manager.update_step_error_async( actor=self.actor, step_id=step_id, # Use original step_id for telemetry - error_type=type(e).__name__ if "e" in locals() else "Unknown", - error_message=str(e) if "e" in locals() else "Unknown error", + error_type=type(caught_exception).__name__ if caught_exception is not None else "Unknown", + error_message=str(caught_exception) if caught_exception is not None else "Unknown error", error_traceback=traceback.format_exc(), stop_reason=stop_reason, ) @@ -973,6 +976,7 @@ class LettaAgent(BaseAgent): agent_step_span.set_attributes({"step_id": step_id}) step_progression = StepProgression.START + caught_exception = None should_continue = False step_metrics = StepMetrics(id=step_id) # Initialize metrics tracking @@ -1228,6 +1232,7 @@ class LettaAgent(BaseAgent): self.logger.warning(f"Failed to record step metrics: {metrics_error}") except Exception as e: + caught_exception = e # Handle any unexpected errors during step processing self.logger.error(f"Error during step processing: {e}") job_update_metadata = {"error": str(e)} @@ -1274,8 +1279,8 @@ class LettaAgent(BaseAgent): await self.step_manager.update_step_error_async( actor=self.actor, step_id=step_id, # Use original step_id for telemetry - error_type=type(e).__name__ if "e" in locals() else "Unknown", - error_message=str(e) if "e" in locals() else "Unknown error", + error_type=type(caught_exception).__name__ if caught_exception is not None else "Unknown", + error_message=str(caught_exception) if caught_exception is not None else "Unknown error", error_traceback=traceback.format_exc(), stop_reason=stop_reason, ) diff --git a/letta/agents/letta_agent_v2.py b/letta/agents/letta_agent_v2.py index e5bdec0f..ba41c123 100644 --- a/letta/agents/letta_agent_v2.py +++ b/letta/agents/letta_agent_v2.py @@ -9,7 +9,6 @@ from letta.adapters.letta_llm_adapter import LettaLLMAdapter from letta.adapters.letta_llm_request_adapter import LettaLLMRequestAdapter from letta.adapters.letta_llm_stream_adapter import LettaLLMStreamAdapter from letta.agents.base_agent_v2 import BaseAgentV2 -from letta.agents.ephemeral_summary_agent import EphemeralSummaryAgent from letta.agents.helpers import ( _build_rule_violation_result, _load_last_function_response, @@ -68,7 +67,7 @@ from letta.services.summarizer.enums import SummarizationMode from letta.services.summarizer.summarizer import Summarizer from letta.services.telemetry_manager import TelemetryManager from letta.services.tool_executor.tool_execution_manager import ToolExecutionManager -from letta.settings import model_settings, settings, summarizer_settings +from letta.settings import settings, summarizer_settings from letta.system import package_function_response from letta.types import JsonDict from letta.utils import log_telemetry, safe_create_task, safe_create_task_with_return, united_diff, validate_function_response @@ -455,6 +454,7 @@ class LettaAgentV2(BaseAgentV2): raise AssertionError("run_id is required when enforce_run_id_set is True") step_progression = StepProgression.START + caught_exception = None # TODO(@caren): clean this up tool_call, reasoning_content, agent_step_span, first_chunk, step_id, logged_step, step_start_ns, step_metrics = ( None, @@ -615,6 +615,7 @@ class LettaAgentV2(BaseAgentV2): ) step_progression, step_metrics = await self._step_checkpoint_finish(step_metrics, agent_step_span, logged_step) except Exception as e: + caught_exception = e self.logger.warning(f"Error during step processing: {e}") self.job_update_metadata = {"error": str(e)} @@ -650,8 +651,8 @@ class LettaAgentV2(BaseAgentV2): await self.step_manager.update_step_error_async( actor=self.actor, step_id=step_id, # Use original step_id for telemetry - error_type=type(e).__name__ if "e" in locals() else "Unknown", - error_message=str(e) if "e" in locals() else "Unknown error", + error_type=type(caught_exception).__name__ if caught_exception is not None else "Unknown", + error_message=str(caught_exception) if caught_exception is not None else "Unknown error", error_traceback=traceback.format_exc(), stop_reason=self.stop_reason, ) @@ -705,14 +706,11 @@ class LettaAgentV2(BaseAgentV2): async def _check_credits(self) -> bool: """Check if the organization still has credits. Returns True if OK or not configured.""" try: - await self.credit_verification_service.verify_credits( - self.actor.organization_id, self.agent_state.id - ) + await self.credit_verification_service.verify_credits(self.actor.organization_id, self.agent_state.id) return True except InsufficientCreditsError: self.logger.warning( - f"Insufficient credits for organization {self.actor.organization_id}, " - f"agent {self.agent_state.id}, stopping agent loop" + f"Insufficient credits for organization {self.actor.organization_id}, agent {self.agent_state.id}, stopping agent loop" ) return False diff --git a/letta/agents/letta_agent_v3.py b/letta/agents/letta_agent_v3.py index 746fbb33..8665f4e1 100644 --- a/letta/agents/letta_agent_v3.py +++ b/letta/agents/letta_agent_v3.py @@ -1,7 +1,7 @@ import asyncio import json import uuid -from typing import Any, AsyncGenerator, Dict, Literal, Optional +from typing import Any, AsyncGenerator, Dict, Optional from opentelemetry.trace import Span @@ -20,16 +20,15 @@ from letta.agents.helpers import ( merge_and_validate_prefilled_args, ) from letta.agents.letta_agent_v2 import LettaAgentV2 -from letta.constants import DEFAULT_MAX_STEPS, NON_USER_MSG_PREFIX, REQUEST_HEARTBEAT_PARAM, SUMMARIZATION_TRIGGER_MULTIPLIER +from letta.constants import DEFAULT_MAX_STEPS, NON_USER_MSG_PREFIX, REQUEST_HEARTBEAT_PARAM from letta.errors import ContextWindowExceededError, LLMError, SystemPromptTokenExceededError from letta.helpers import ToolRulesSolver from letta.helpers.datetime_helpers import get_utc_time, get_utc_timestamp_ns -from letta.helpers.message_helper import convert_message_creates_to_messages from letta.helpers.tool_execution_helper import enable_strict_mode from letta.local_llm.constants import INNER_THOUGHTS_KWARG from letta.otel.tracing import trace_method from letta.schemas.agent import AgentState -from letta.schemas.enums import LLMCallType, MessageRole +from letta.schemas.enums import LLMCallType from letta.schemas.letta_message import ( ApprovalReturn, CompactionStats, @@ -44,13 +43,11 @@ from letta.schemas.letta_message_content import OmittedReasoningContent, Reasoni from letta.schemas.letta_request import ClientToolSchema from letta.schemas.letta_response import LettaResponse, TurnTokenData from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.llm_config import LLMConfig from letta.schemas.message import Message, MessageCreate, ToolReturn -from letta.schemas.openai.chat_completion_response import ChoiceLogprobs, FunctionCall, ToolCall, ToolCallDenial, UsageStatistics +from letta.schemas.openai.chat_completion_response import ChoiceLogprobs, ToolCall, ToolCallDenial, UsageStatistics from letta.schemas.step import StepProgression from letta.schemas.step_metrics import StepMetrics from letta.schemas.tool_execution_result import ToolExecutionResult -from letta.schemas.usage import LettaUsageStatistics from letta.schemas.user import User from letta.server.rest_api.utils import ( create_approval_request_message_from_llm_response, @@ -64,8 +61,8 @@ from letta.services.summarizer.compact import compact_messages from letta.services.summarizer.summarizer_config import CompactionSettings from letta.services.summarizer.summarizer_sliding_window import count_tokens from letta.settings import settings, summarizer_settings -from letta.system import package_function_response, package_summarize_message_no_counts -from letta.utils import log_telemetry, safe_create_task_with_return, validate_function_response +from letta.system import package_function_response +from letta.utils import safe_create_task_with_return, validate_function_response def extract_compaction_stats_from_message(message: Message) -> CompactionStats | None: @@ -800,6 +797,7 @@ class LettaAgentV3(LettaAgentV2): self.logger.warning("Context token estimate is not set") step_progression = StepProgression.START + caught_exception = None # TODO(@caren): clean this up tool_calls, content, agent_step_span, first_chunk, step_id, logged_step, step_start_ns, step_metrics = ( None, @@ -1272,6 +1270,7 @@ class LettaAgentV3(LettaAgentV2): raise except Exception as e: + caught_exception = e # NOTE: message persistence does not happen in the case of an exception (rollback to previous state) # Use repr() if str() is empty (happens with Exception() with no args) error_detail = str(e) or repr(e) @@ -1322,8 +1321,8 @@ class LettaAgentV3(LettaAgentV2): await self.step_manager.update_step_error_async( actor=self.actor, step_id=step_id, # Use original step_id for telemetry - error_type=type(e).__name__ if "e" in locals() else "Unknown", - error_message=str(e) if "e" in locals() else "Unknown error", + error_type=type(caught_exception).__name__ if caught_exception is not None else "Unknown", + error_message=str(caught_exception) if caught_exception is not None else "Unknown error", error_traceback=traceback.format_exc(), stop_reason=self.stop_reason, ) diff --git a/letta/agents/voice_agent.py b/letta/agents/voice_agent.py index 068d6f3e..4f26aaa7 100644 --- a/letta/agents/voice_agent.py +++ b/letta/agents/voice_agent.py @@ -414,7 +414,7 @@ class VoiceAgent(BaseAgent): for t in tools ] - async def _execute_tool(self, user_query: str, tool_name: str, tool_args: dict, agent_state: AgentState) -> "ToolExecutionResult": + async def _execute_tool(self, user_query: str, tool_name: str, tool_args: dict, agent_state: AgentState) -> "ToolExecutionResult": # noqa: F821 """ Executes a tool and returns the ToolExecutionResult. """ diff --git a/letta/agents/voice_sleeptime_agent.py b/letta/agents/voice_sleeptime_agent.py index fbd5d145..264b4204 100644 --- a/letta/agents/voice_sleeptime_agent.py +++ b/letta/agents/voice_sleeptime_agent.py @@ -110,9 +110,9 @@ class VoiceSleeptimeAgent(LettaAgent): tool_name: str, tool_args: JsonDict, agent_state: AgentState, - agent_step_span: Optional["Span"] = None, + agent_step_span: Optional["Span"] = None, # noqa: F821 step_id: str | None = None, - ) -> "ToolExecutionResult": + ) -> "ToolExecutionResult": # noqa: F821 """ Executes a tool and returns the ToolExecutionResult """ diff --git a/letta/cli/cli.py b/letta/cli/cli.py index 47e86509..e566ca62 100644 --- a/letta/cli/cli.py +++ b/letta/cli/cli.py @@ -5,7 +5,6 @@ from typing import Annotated, Optional import typer from letta.log import get_logger -from letta.streaming_interface import StreamingRefreshCLIInterface as interface # for printing to terminal logger = get_logger(__name__) diff --git a/letta/data_sources/connectors.py b/letta/data_sources/connectors.py index cfafe2a2..52a7b73f 100644 --- a/letta/data_sources/connectors.py +++ b/letta/data_sources/connectors.py @@ -37,7 +37,7 @@ class DataConnector: """ -async def load_data(connector: DataConnector, source: Source, passage_manager: PassageManager, file_manager: FileManager, actor: "User"): +async def load_data(connector: DataConnector, source: Source, passage_manager: PassageManager, file_manager: FileManager, actor: "User"): # noqa: F821 from letta.llm_api.llm_client import LLMClient """Load data from a connector (generates file and passages) into a specified source_id, associated with a user_id.""" diff --git a/letta/errors.py b/letta/errors.py index 5795ef69..2d2dfe05 100644 --- a/letta/errors.py +++ b/letta/errors.py @@ -362,16 +362,16 @@ class RateLimitExceededError(LettaError): class LettaMessageError(LettaError): """Base error class for handling message-related errors.""" - messages: List[Union["Message", "LettaMessage"]] + messages: List[Union["Message", "LettaMessage"]] # noqa: F821 default_error_message: str = "An error occurred with the message." - def __init__(self, *, messages: List[Union["Message", "LettaMessage"]], explanation: Optional[str] = None) -> None: + def __init__(self, *, messages: List[Union["Message", "LettaMessage"]], explanation: Optional[str] = None) -> None: # noqa: F821 error_msg = self.construct_error_message(messages, self.default_error_message, explanation) super().__init__(error_msg) self.messages = messages @staticmethod - def construct_error_message(messages: List[Union["Message", "LettaMessage"]], error_msg: str, explanation: Optional[str] = None) -> str: + def construct_error_message(messages: List[Union["Message", "LettaMessage"]], error_msg: str, explanation: Optional[str] = None) -> str: # noqa: F821 """Helper method to construct a clean and formatted error message.""" if explanation: error_msg += f" (Explanation: {explanation})" diff --git a/letta/functions/function_sets/base.py b/letta/functions/function_sets/base.py index 8e76942a..28926f17 100644 --- a/letta/functions/function_sets/base.py +++ b/letta/functions/function_sets/base.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, List, Literal, Optional +from typing import TYPE_CHECKING, List, Literal, Optional from letta.constants import CORE_MEMORY_LINE_NUMBER_WARNING @@ -67,7 +67,7 @@ def memory( raise NotImplementedError("This should never be invoked directly. Contact Letta if you see this error message.") -def send_message(self: "Agent", message: str) -> Optional[str]: +def send_message(self: "Agent", message: str) -> Optional[str]: # noqa: F821 """ Sends a message to the human user. @@ -84,7 +84,7 @@ def send_message(self: "Agent", message: str) -> Optional[str]: def conversation_search( - self: "Agent", + self: "Agent", # noqa: F821 query: Optional[str] = None, roles: Optional[List[Literal["assistant", "user", "tool"]]] = None, limit: Optional[int] = None, @@ -160,7 +160,7 @@ def conversation_search( return results_str -async def archival_memory_insert(self: "Agent", content: str, tags: Optional[list[str]] = None) -> Optional[str]: +async def archival_memory_insert(self: "Agent", content: str, tags: Optional[list[str]] = None) -> Optional[str]: # noqa: F821 """ Add information to long-term archival memory for later retrieval. @@ -191,7 +191,7 @@ async def archival_memory_insert(self: "Agent", content: str, tags: Optional[lis async def archival_memory_search( - self: "Agent", + self: "Agent", # noqa: F821 query: str, tags: Optional[list[str]] = None, tag_match_mode: Literal["any", "all"] = "any", diff --git a/letta/functions/function_sets/multi_agent.py b/letta/functions/function_sets/multi_agent.py index b1690ed9..b1dbda98 100644 --- a/letta/functions/function_sets/multi_agent.py +++ b/letta/functions/function_sets/multi_agent.py @@ -1,5 +1,5 @@ import asyncio -from typing import TYPE_CHECKING, List +from typing import List from letta.functions.helpers import ( _send_message_to_agents_matching_tags_async, @@ -10,9 +10,10 @@ from letta.functions.helpers import ( from letta.schemas.enums import MessageRole from letta.schemas.message import MessageCreate from letta.server.rest_api.dependencies import get_letta_server +from letta.settings import settings -def send_message_to_agent_and_wait_for_reply(self: "Agent", message: str, other_agent_id: str) -> str: +def send_message_to_agent_and_wait_for_reply(self: "Agent", message: str, other_agent_id: str) -> str: # noqa: F821 """ Sends a message to a specific Letta agent within the same organization and waits for a response. The sender's identity is automatically included, so no explicit introduction is needed in the message. This function is designed for two-way communication where a reply is expected. @@ -36,7 +37,7 @@ def send_message_to_agent_and_wait_for_reply(self: "Agent", message: str, other_ ) -def send_message_to_agents_matching_tags(self: "Agent", message: str, match_all: List[str], match_some: List[str]) -> List[str]: +def send_message_to_agents_matching_tags(self: "Agent", message: str, match_all: List[str], match_some: List[str]) -> List[str]: # noqa: F821 """ Sends a message to all agents within the same organization that match the specified tag criteria. Agents must possess *all* of the tags in `match_all` and *at least one* of the tags in `match_some` to receive the message. @@ -65,7 +66,7 @@ def send_message_to_agents_matching_tags(self: "Agent", message: str, match_all: return asyncio.run(_send_message_to_agents_matching_tags_async(self, server, messages, matching_agents)) -def send_message_to_all_agents_in_group(self: "Agent", message: str) -> List[str]: +def send_message_to_all_agents_in_group(self: "Agent", message: str) -> List[str]: # noqa: F821 """ Sends a message to all agents within the same multi-agent group. @@ -81,7 +82,7 @@ def send_message_to_all_agents_in_group(self: "Agent", message: str) -> List[str return asyncio.run(_send_message_to_all_agents_in_group_async(self, message)) -def send_message_to_agent_async(self: "Agent", message: str, other_agent_id: str) -> str: +def send_message_to_agent_async(self: "Agent", message: str, other_agent_id: str) -> str: # noqa: F821 """ Sends a message to a specific Letta agent within the same organization. The sender's identity is automatically included, so no explicit introduction is required in the message. This function does not expect a response from the target agent, making it suitable for notifications or one-way communication. Args: diff --git a/letta/functions/function_sets/voice.py b/letta/functions/function_sets/voice.py index dbe16993..ee67965b 100644 --- a/letta/functions/function_sets/voice.py +++ b/letta/functions/function_sets/voice.py @@ -4,7 +4,7 @@ from typing import List, Optional from pydantic import BaseModel, Field -def rethink_user_memory(agent_state: "AgentState", new_memory: str) -> None: +def rethink_user_memory(agent_state: "AgentState", new_memory: str) -> None: # noqa: F821 """ Rewrite memory block for the main agent, new_memory should contain all current information from the block that is not outdated or inconsistent, integrating any new information, resulting in a new memory block that is organized, readable, and comprehensive. @@ -18,7 +18,7 @@ def rethink_user_memory(agent_state: "AgentState", new_memory: str) -> None: return None -def finish_rethinking_memory(agent_state: "AgentState") -> None: # type: ignore +def finish_rethinking_memory(agent_state: "AgentState") -> None: # type: ignore # noqa: F821 """ This function is called when the agent is done rethinking the memory. @@ -43,7 +43,7 @@ class MemoryChunk(BaseModel): ) -def store_memories(agent_state: "AgentState", chunks: List[MemoryChunk]) -> None: +def store_memories(agent_state: "AgentState", chunks: List[MemoryChunk]) -> None: # noqa: F821 """ Persist dialogue that is about to fall out of the agent’s context window. @@ -59,7 +59,7 @@ def store_memories(agent_state: "AgentState", chunks: List[MemoryChunk]) -> None def search_memory( - agent_state: "AgentState", + agent_state: "AgentState", # noqa: F821 convo_keyword_queries: Optional[List[str]], start_minutes_ago: Optional[int], end_minutes_ago: Optional[int], diff --git a/letta/functions/helpers.py b/letta/functions/helpers.py index 3e041b92..b74cc070 100644 --- a/letta/functions/helpers.py +++ b/letta/functions/helpers.py @@ -36,7 +36,8 @@ def {mcp_tool_name}(**kwargs): def generate_langchain_tool_wrapper( - tool: "LangChainBaseTool", additional_imports_module_attr_map: dict[str, str] = None + tool: "LangChainBaseTool", # noqa: F821 + additional_imports_module_attr_map: dict[str, str] = None, ) -> tuple[str, str]: tool_name = tool.__class__.__name__ import_statement = f"from langchain_community.tools import {tool_name}" @@ -72,7 +73,7 @@ def _assert_code_gen_compilable(code_str): print(f"Syntax error in code: {e}") -def _assert_all_classes_are_imported(tool: Union["LangChainBaseTool"], additional_imports_module_attr_map: dict[str, str]) -> None: +def _assert_all_classes_are_imported(tool: Union["LangChainBaseTool"], additional_imports_module_attr_map: dict[str, str]) -> None: # noqa: F821 # Safety check that user has passed in all required imports: tool_name = tool.__class__.__name__ current_class_imports = {tool_name} @@ -86,7 +87,7 @@ def _assert_all_classes_are_imported(tool: Union["LangChainBaseTool"], additiona raise RuntimeError(err_msg) -def _find_required_class_names_for_import(obj: Union["LangChainBaseTool", BaseModel]) -> list[str]: +def _find_required_class_names_for_import(obj: Union["LangChainBaseTool", BaseModel]) -> list[str]: # noqa: F821 """ Finds all the class names for required imports when instantiating the `obj`. NOTE: This does not return the full import path, only the class name. @@ -224,7 +225,7 @@ def _parse_letta_response_for_assistant_message( async def async_execute_send_message_to_agent( - sender_agent: "Agent", + sender_agent: "Agent", # noqa: F821 messages: List[MessageCreate], other_agent_id: str, log_prefix: str, @@ -255,7 +256,7 @@ async def async_execute_send_message_to_agent( def execute_send_message_to_agent( - sender_agent: "Agent", + sender_agent: "Agent", # noqa: F821 messages: List[MessageCreate], other_agent_id: str, log_prefix: str, @@ -268,7 +269,7 @@ def execute_send_message_to_agent( async def _send_message_to_agent_no_stream( - server: "SyncServer", + server: "SyncServer", # noqa: F821 agent_id: str, actor: User, messages: List[MessageCreate], @@ -301,8 +302,8 @@ async def _send_message_to_agent_no_stream( async def _async_send_message_with_retries( - server: "SyncServer", - sender_agent: "Agent", + server: "SyncServer", # noqa: F821 + sender_agent: "Agent", # noqa: F821 target_agent_id: str, messages: List[MessageCreate], max_retries: int, @@ -352,7 +353,7 @@ async def _async_send_message_with_retries( def fire_and_forget_send_to_agent( - sender_agent: "Agent", + sender_agent: "Agent", # noqa: F821 messages: List[MessageCreate], other_agent_id: str, log_prefix: str, @@ -436,7 +437,10 @@ def fire_and_forget_send_to_agent( async def _send_message_to_agents_matching_tags_async( - sender_agent: "Agent", server: "SyncServer", messages: List[MessageCreate], matching_agents: List["AgentState"] + sender_agent: "Agent", # noqa: F821 + server: "SyncServer", # noqa: F821 + messages: List[MessageCreate], + matching_agents: List["AgentState"], # noqa: F821 ) -> List[str]: async def _send_single(agent_state): return await _async_send_message_with_retries( @@ -460,7 +464,7 @@ async def _send_message_to_agents_matching_tags_async( return final -async def _send_message_to_all_agents_in_group_async(sender_agent: "Agent", message: str) -> List[str]: +async def _send_message_to_all_agents_in_group_async(sender_agent: "Agent", message: str) -> List[str]: # noqa: F821 server = get_letta_server() augmented_message = ( diff --git a/letta/groups/dynamic_multi_agent.py b/letta/groups/dynamic_multi_agent.py index 4f0a09ab..926d9eff 100644 --- a/letta/groups/dynamic_multi_agent.py +++ b/letta/groups/dynamic_multi_agent.py @@ -177,7 +177,7 @@ class DynamicMultiAgent(BaseAgent): return LettaUsageStatistics(**total_usage.model_dump(), step_count=step_count) - def load_manager_agent(self) -> Agent: + def load_manager_agent(self) -> Agent: # noqa: F821 for participant_agent_id in self.agent_ids: participant_agent_state = self.agent_manager.get_agent_by_id(agent_id=participant_agent_id, actor=self.user) participant_persona_block = participant_agent_state.memory.get_block(label="persona") diff --git a/letta/groups/sleeptime_multi_agent_v2.py b/letta/groups/sleeptime_multi_agent_v2.py index 563600b7..936136a5 100644 --- a/letta/groups/sleeptime_multi_agent_v2.py +++ b/letta/groups/sleeptime_multi_agent_v2.py @@ -1,4 +1,3 @@ -import asyncio from collections.abc import AsyncGenerator from datetime import datetime, timezone diff --git a/letta/groups/sleeptime_multi_agent_v3.py b/letta/groups/sleeptime_multi_agent_v3.py index 080f3abe..d1c8c302 100644 --- a/letta/groups/sleeptime_multi_agent_v3.py +++ b/letta/groups/sleeptime_multi_agent_v3.py @@ -9,7 +9,6 @@ from letta.otel.tracing import trace_method from letta.schemas.agent import AgentState from letta.schemas.enums import RunStatus from letta.schemas.group import Group, ManagerType -from letta.schemas.job import JobUpdate from letta.schemas.letta_message import MessageType from letta.schemas.letta_message_content import TextContent from letta.schemas.letta_request import ClientToolSchema diff --git a/letta/groups/sleeptime_multi_agent_v4.py b/letta/groups/sleeptime_multi_agent_v4.py index 58a161cc..9995ee15 100644 --- a/letta/groups/sleeptime_multi_agent_v4.py +++ b/letta/groups/sleeptime_multi_agent_v4.py @@ -1,4 +1,3 @@ -import asyncio from collections.abc import AsyncGenerator from datetime import datetime, timezone @@ -7,9 +6,8 @@ from letta.constants import DEFAULT_MAX_STEPS from letta.groups.helpers import stringify_message from letta.otel.tracing import trace_method from letta.schemas.agent import AgentState -from letta.schemas.enums import JobStatus, RunStatus +from letta.schemas.enums import RunStatus from letta.schemas.group import Group, ManagerType -from letta.schemas.job import JobUpdate from letta.schemas.letta_message import MessageType from letta.schemas.letta_message_content import TextContent from letta.schemas.letta_request import ClientToolSchema diff --git a/letta/groups/supervisor_multi_agent.py b/letta/groups/supervisor_multi_agent.py index 1a87aa67..d95afde6 100644 --- a/letta/groups/supervisor_multi_agent.py +++ b/letta/groups/supervisor_multi_agent.py @@ -1,19 +1,9 @@ -from typing import List, Optional +from typing import List from letta.agents.base_agent import BaseAgent -from letta.constants import DEFAULT_MESSAGE_TOOL -from letta.functions.function_sets.multi_agent import send_message_to_all_agents_in_group -from letta.functions.functions import parse_source_code -from letta.functions.schema_generator import generate_schema from letta.interface import AgentInterface from letta.orm import User from letta.schemas.agent import AgentState -from letta.schemas.enums import ToolType -from letta.schemas.letta_message_content import TextContent -from letta.schemas.message import MessageCreate -from letta.schemas.tool import Tool -from letta.schemas.tool_rule import ChildToolRule, InitToolRule, TerminalToolRule -from letta.schemas.usage import LettaUsageStatistics from letta.services.agent_manager import AgentManager from letta.services.tool_manager import ToolManager diff --git a/letta/helpers/__init__.py b/letta/helpers/__init__.py index 62e8d709..9c9cd242 100644 --- a/letta/helpers/__init__.py +++ b/letta/helpers/__init__.py @@ -1 +1 @@ -from letta.helpers.tool_rule_solver import ToolRulesSolver +from letta.helpers.tool_rule_solver import ToolRulesSolver as ToolRulesSolver diff --git a/letta/helpers/converters.py b/letta/helpers/converters.py index 24cc2ab1..a3e7a581 100644 --- a/letta/helpers/converters.py +++ b/letta/helpers/converters.py @@ -113,7 +113,7 @@ def deserialize_embedding_config(data: Optional[Dict]) -> Optional[EmbeddingConf # -------------------------- -def serialize_compaction_settings(config: Union[Optional["CompactionSettings"], Dict]) -> Optional[Dict]: +def serialize_compaction_settings(config: Union[Optional["CompactionSettings"], Dict]) -> Optional[Dict]: # noqa: F821 """Convert a CompactionSettings object into a JSON-serializable dictionary.""" if config: # Import here to avoid circular dependency @@ -124,7 +124,7 @@ def serialize_compaction_settings(config: Union[Optional["CompactionSettings"], return config -def deserialize_compaction_settings(data: Optional[Dict]) -> Optional["CompactionSettings"]: +def deserialize_compaction_settings(data: Optional[Dict]) -> Optional["CompactionSettings"]: # noqa: F821 """Convert a dictionary back into a CompactionSettings object.""" if data: # Import here to avoid circular dependency diff --git a/letta/helpers/tool_execution_helper.py b/letta/helpers/tool_execution_helper.py index 1058e2f3..a32cd0a7 100644 --- a/letta/helpers/tool_execution_helper.py +++ b/letta/helpers/tool_execution_helper.py @@ -1,6 +1,6 @@ import copy from collections import OrderedDict -from typing import Any, Dict, List, Optional +from typing import Any, Dict, Optional from letta.constants import PRE_EXECUTION_MESSAGE_ARG from letta.schemas.tool import MCP_TOOL_METADATA_SCHEMA_STATUS, MCP_TOOL_METADATA_SCHEMA_WARNINGS diff --git a/letta/helpers/tpuf_client.py b/letta/helpers/tpuf_client.py index 6c2fb6cc..ab6414a4 100644 --- a/letta/helpers/tpuf_client.py +++ b/letta/helpers/tpuf_client.py @@ -244,7 +244,7 @@ class TurbopufferClient: raise ValueError("Turbopuffer API key not provided") @trace_method - async def _generate_embeddings(self, texts: List[str], actor: "PydanticUser") -> List[List[float]]: + async def _generate_embeddings(self, texts: List[str], actor: "PydanticUser") -> List[List[float]]: # noqa: F821 """Generate embeddings using the default embedding configuration. Args: @@ -311,7 +311,7 @@ class TurbopufferClient: return namespace_name - def _extract_tool_text(self, tool: "PydanticTool") -> str: + def _extract_tool_text(self, tool: "PydanticTool") -> str: # noqa: F821 """Extract searchable text from a tool for embedding. Combines name, description, and JSON schema into a structured format @@ -361,9 +361,9 @@ class TurbopufferClient: @async_retry_with_backoff() async def insert_tools( self, - tools: List["PydanticTool"], + tools: List["PydanticTool"], # noqa: F821 organization_id: str, - actor: "PydanticUser", + actor: "PydanticUser", # noqa: F821 ) -> bool: """Insert tools into Turbopuffer. @@ -375,7 +375,6 @@ class TurbopufferClient: Returns: True if successful """ - from turbopuffer import AsyncTurbopuffer if not tools: return True @@ -457,7 +456,7 @@ class TurbopufferClient: text_chunks: List[str], passage_ids: List[str], organization_id: str, - actor: "PydanticUser", + actor: "PydanticUser", # noqa: F821 tags: Optional[List[str]] = None, created_at: Optional[datetime] = None, embeddings: Optional[List[List[float]]] = None, @@ -477,7 +476,6 @@ class TurbopufferClient: Returns: List of PydanticPassage objects that were inserted """ - from turbopuffer import AsyncTurbopuffer # filter out empty text chunks filtered_chunks = [(i, text) for i, text in enumerate(text_chunks) if text.strip()] @@ -609,7 +607,7 @@ class TurbopufferClient: message_texts: List[str], message_ids: List[str], organization_id: str, - actor: "PydanticUser", + actor: "PydanticUser", # noqa: F821 roles: List[MessageRole], created_ats: List[datetime], project_id: Optional[str] = None, @@ -633,7 +631,6 @@ class TurbopufferClient: Returns: True if successful """ - from turbopuffer import AsyncTurbopuffer # filter out empty message texts filtered_messages = [(i, text) for i, text in enumerate(message_texts) if text.strip()] @@ -870,7 +867,7 @@ class TurbopufferClient: async def query_passages( self, archive_id: str, - actor: "PydanticUser", + actor: "PydanticUser", # noqa: F821 query_text: Optional[str] = None, search_mode: str = "vector", # "vector", "fts", "hybrid" top_k: int = 10, @@ -1015,7 +1012,7 @@ class TurbopufferClient: self, agent_id: str, organization_id: str, - actor: "PydanticUser", + actor: "PydanticUser", # noqa: F821 query_text: Optional[str] = None, search_mode: str = "vector", # "vector", "fts", "hybrid", "timestamp" top_k: int = 10, @@ -1191,7 +1188,7 @@ class TurbopufferClient: async def query_messages_by_org_id( self, organization_id: str, - actor: "PydanticUser", + actor: "PydanticUser", # noqa: F821 query_text: Optional[str] = None, search_mode: str = "hybrid", # "vector", "fts", "hybrid" top_k: int = 10, @@ -1520,7 +1517,6 @@ class TurbopufferClient: @async_retry_with_backoff() async def delete_passage(self, archive_id: str, passage_id: str) -> bool: """Delete a passage from Turbopuffer.""" - from turbopuffer import AsyncTurbopuffer namespace_name = await self._get_archive_namespace_name(archive_id) @@ -1543,7 +1539,6 @@ class TurbopufferClient: @async_retry_with_backoff() async def delete_passages(self, archive_id: str, passage_ids: List[str]) -> bool: """Delete multiple passages from Turbopuffer.""" - from turbopuffer import AsyncTurbopuffer if not passage_ids: return True @@ -1588,7 +1583,6 @@ class TurbopufferClient: @async_retry_with_backoff() async def delete_messages(self, agent_id: str, organization_id: str, message_ids: List[str]) -> bool: """Delete multiple messages from Turbopuffer.""" - from turbopuffer import AsyncTurbopuffer if not message_ids: return True @@ -1614,7 +1608,6 @@ class TurbopufferClient: @async_retry_with_backoff() async def delete_all_messages(self, agent_id: str, organization_id: str) -> bool: """Delete all messages for an agent from Turbopuffer.""" - from turbopuffer import AsyncTurbopuffer namespace_name = await self._get_message_namespace_name(organization_id) @@ -1661,7 +1654,7 @@ class TurbopufferClient: file_id: str, text_chunks: List[str], organization_id: str, - actor: "PydanticUser", + actor: "PydanticUser", # noqa: F821 created_at: Optional[datetime] = None, ) -> List[PydanticPassage]: """Insert file passages into Turbopuffer using org-scoped namespace. @@ -1677,7 +1670,6 @@ class TurbopufferClient: Returns: List of PydanticPassage objects that were inserted """ - from turbopuffer import AsyncTurbopuffer if not text_chunks: return [] @@ -1775,7 +1767,7 @@ class TurbopufferClient: self, source_ids: List[str], organization_id: str, - actor: "PydanticUser", + actor: "PydanticUser", # noqa: F821 query_text: Optional[str] = None, search_mode: str = "vector", # "vector", "fts", "hybrid" top_k: int = 10, @@ -1914,7 +1906,6 @@ class TurbopufferClient: @async_retry_with_backoff() async def delete_file_passages(self, source_id: str, file_id: str, organization_id: str) -> bool: """Delete all passages for a specific file from Turbopuffer.""" - from turbopuffer import AsyncTurbopuffer namespace_name = await self._get_file_passages_namespace_name(organization_id) @@ -1943,7 +1934,6 @@ class TurbopufferClient: @async_retry_with_backoff() async def delete_source_passages(self, source_id: str, organization_id: str) -> bool: """Delete all passages for a source from Turbopuffer.""" - from turbopuffer import AsyncTurbopuffer namespace_name = await self._get_file_passages_namespace_name(organization_id) @@ -1976,7 +1966,6 @@ class TurbopufferClient: Returns: True if successful """ - from turbopuffer import AsyncTurbopuffer if not tool_ids: return True @@ -2002,7 +1991,7 @@ class TurbopufferClient: async def query_tools( self, organization_id: str, - actor: "PydanticUser", + actor: "PydanticUser", # noqa: F821 query_text: Optional[str] = None, search_mode: str = "hybrid", # "vector", "fts", "hybrid", "timestamp" top_k: int = 50, diff --git a/letta/interfaces/anthropic_parallel_tool_call_streaming_interface.py b/letta/interfaces/anthropic_parallel_tool_call_streaming_interface.py index 648007f2..14b9cd10 100644 --- a/letta/interfaces/anthropic_parallel_tool_call_streaming_interface.py +++ b/letta/interfaces/anthropic_parallel_tool_call_streaming_interface.py @@ -146,7 +146,7 @@ class SimpleAnthropicStreamingInterface: return tool_calls[0] return None - def get_usage_statistics(self) -> "LettaUsageStatistics": + def get_usage_statistics(self) -> "LettaUsageStatistics": # noqa: F821 """Extract usage statistics from accumulated streaming data. Returns: @@ -232,7 +232,7 @@ class SimpleAnthropicStreamingInterface: async def process( self, stream: AsyncStream[BetaRawMessageStreamEvent], - ttft_span: Optional["Span"] = None, + ttft_span: Optional["Span"] = None, # noqa: F821 ) -> AsyncGenerator[LettaMessage | LettaStopReason, None]: prev_message_type = None message_index = 0 @@ -287,7 +287,7 @@ class SimpleAnthropicStreamingInterface: async def _process_event( self, event: BetaRawMessageStreamEvent, - ttft_span: Optional["Span"] = None, + ttft_span: Optional["Span"] = None, # noqa: F821 prev_message_type: Optional[str] = None, message_index: int = 0, ) -> AsyncGenerator[LettaMessage | LettaStopReason, None]: diff --git a/letta/interfaces/anthropic_streaming_interface.py b/letta/interfaces/anthropic_streaming_interface.py index b5ffe9b5..a4101bbd 100644 --- a/letta/interfaces/anthropic_streaming_interface.py +++ b/letta/interfaces/anthropic_streaming_interface.py @@ -128,7 +128,7 @@ class AnthropicStreamingInterface: arguments = str(json.dumps(tool_input, indent=2)) return ToolCall(id=self.tool_call_id, function=FunctionCall(arguments=arguments, name=self.tool_call_name)) - def get_usage_statistics(self) -> "LettaUsageStatistics": + def get_usage_statistics(self) -> "LettaUsageStatistics": # noqa: F821 """Extract usage statistics from accumulated streaming data. Returns: @@ -222,7 +222,7 @@ class AnthropicStreamingInterface: async def process( self, stream: AsyncStream[BetaRawMessageStreamEvent], - ttft_span: Optional["Span"] = None, + ttft_span: Optional["Span"] = None, # noqa: F821 ) -> AsyncGenerator[LettaMessage | LettaStopReason, None]: prev_message_type = None message_index = 0 @@ -276,7 +276,7 @@ class AnthropicStreamingInterface: async def _process_event( self, event: BetaRawMessageStreamEvent, - ttft_span: Optional["Span"] = None, + ttft_span: Optional["Span"] = None, # noqa: F821 prev_message_type: Optional[str] = None, message_index: int = 0, ) -> AsyncGenerator[LettaMessage | LettaStopReason, None]: @@ -662,7 +662,7 @@ class SimpleAnthropicStreamingInterface: arguments = str(json.dumps(tool_input, indent=2)) return ToolCall(id=self.tool_call_id, function=FunctionCall(arguments=arguments, name=self.tool_call_name)) - def get_usage_statistics(self) -> "LettaUsageStatistics": + def get_usage_statistics(self) -> "LettaUsageStatistics": # noqa: F821 """Extract usage statistics from accumulated streaming data. Returns: @@ -754,7 +754,7 @@ class SimpleAnthropicStreamingInterface: async def process( self, stream: AsyncStream[BetaRawMessageStreamEvent], - ttft_span: Optional["Span"] = None, + ttft_span: Optional["Span"] = None, # noqa: F821 ) -> AsyncGenerator[LettaMessage | LettaStopReason, None]: prev_message_type = None message_index = 0 @@ -803,7 +803,7 @@ class SimpleAnthropicStreamingInterface: async def _process_event( self, event: BetaRawMessageStreamEvent, - ttft_span: Optional["Span"] = None, + ttft_span: Optional["Span"] = None, # noqa: F821 prev_message_type: Optional[str] = None, message_index: int = 0, ) -> AsyncGenerator[LettaMessage | LettaStopReason, None]: diff --git a/letta/interfaces/gemini_streaming_interface.py b/letta/interfaces/gemini_streaming_interface.py index e7f45040..9c3ef633 100644 --- a/letta/interfaces/gemini_streaming_interface.py +++ b/letta/interfaces/gemini_streaming_interface.py @@ -124,7 +124,7 @@ class SimpleGeminiStreamingInterface: """Return all finalized tool calls collected during this message (parallel supported).""" return list(self.collected_tool_calls) - def get_usage_statistics(self) -> "LettaUsageStatistics": + def get_usage_statistics(self) -> "LettaUsageStatistics": # noqa: F821 """Extract usage statistics from accumulated streaming data. Returns: @@ -148,7 +148,7 @@ class SimpleGeminiStreamingInterface: async def process( self, stream: AsyncIterator[GenerateContentResponse], - ttft_span: Optional["Span"] = None, + ttft_span: Optional["Span"] = None, # noqa: F821 ) -> AsyncGenerator[LettaMessage | LettaStopReason, None]: """ Iterates over the Gemini stream, yielding SSE events. @@ -202,7 +202,7 @@ class SimpleGeminiStreamingInterface: async def _process_event( self, event: GenerateContentResponse, - ttft_span: Optional["Span"] = None, + ttft_span: Optional["Span"] = None, # noqa: F821 prev_message_type: Optional[str] = None, message_index: int = 0, ) -> AsyncGenerator[LettaMessage | LettaStopReason, None]: diff --git a/letta/interfaces/openai_streaming_interface.py b/letta/interfaces/openai_streaming_interface.py index bf0a2938..69abde67 100644 --- a/letta/interfaces/openai_streaming_interface.py +++ b/letta/interfaces/openai_streaming_interface.py @@ -194,7 +194,7 @@ class OpenAIStreamingInterface: function=FunctionCall(arguments=self._get_current_function_arguments(), name=function_name), ) - def get_usage_statistics(self) -> "LettaUsageStatistics": + def get_usage_statistics(self) -> "LettaUsageStatistics": # noqa: F821 """Extract usage statistics from accumulated streaming data. Returns: @@ -219,7 +219,7 @@ class OpenAIStreamingInterface: async def process( self, stream: AsyncStream[ChatCompletionChunk], - ttft_span: Optional["Span"] = None, + ttft_span: Optional["Span"] = None, # noqa: F821 ) -> AsyncGenerator[LettaMessage | LettaStopReason, None]: """ Iterates over the OpenAI stream, yielding SSE events. @@ -307,7 +307,7 @@ class OpenAIStreamingInterface: async def _process_chunk( self, chunk: ChatCompletionChunk, - ttft_span: Optional["Span"] = None, + ttft_span: Optional["Span"] = None, # noqa: F821 prev_message_type: Optional[str] = None, message_index: int = 0, ) -> AsyncGenerator[LettaMessage | LettaStopReason, None]: @@ -694,7 +694,7 @@ class SimpleOpenAIStreamingInterface: raise ValueError("No tool calls available") return calls[0] - def get_usage_statistics(self) -> "LettaUsageStatistics": + def get_usage_statistics(self) -> "LettaUsageStatistics": # noqa: F821 """Extract usage statistics from accumulated streaming data. Returns: @@ -719,7 +719,7 @@ class SimpleOpenAIStreamingInterface: async def process( self, stream: AsyncStream[ChatCompletionChunk], - ttft_span: Optional["Span"] = None, + ttft_span: Optional["Span"] = None, # noqa: F821 ) -> AsyncGenerator[LettaMessage | LettaStopReason, None]: """ Iterates over the OpenAI stream, yielding SSE events. @@ -833,7 +833,7 @@ class SimpleOpenAIStreamingInterface: async def _process_chunk( self, chunk: ChatCompletionChunk, - ttft_span: Optional["Span"] = None, + ttft_span: Optional["Span"] = None, # noqa: F821 prev_message_type: Optional[str] = None, message_index: int = 0, ) -> AsyncGenerator[LettaMessage | LettaStopReason, None]: @@ -1120,7 +1120,7 @@ class SimpleOpenAIResponsesStreamingInterface: raise ValueError("No tool calls available") return calls[0] - def get_usage_statistics(self) -> "LettaUsageStatistics": + def get_usage_statistics(self) -> "LettaUsageStatistics": # noqa: F821 """Extract usage statistics from accumulated streaming data. Returns: @@ -1141,7 +1141,7 @@ class SimpleOpenAIResponsesStreamingInterface: async def process( self, stream: AsyncStream[ResponseStreamEvent], - ttft_span: Optional["Span"] = None, + ttft_span: Optional["Span"] = None, # noqa: F821 ) -> AsyncGenerator[LettaMessage | LettaStopReason, None]: """ Iterates over the OpenAI stream, yielding SSE events. @@ -1227,7 +1227,7 @@ class SimpleOpenAIResponsesStreamingInterface: async def _process_event( self, event: ResponseStreamEvent, - ttft_span: Optional["Span"] = None, + ttft_span: Optional["Span"] = None, # noqa: F821 prev_message_type: Optional[str] = None, message_index: int = 0, ) -> AsyncGenerator[LettaMessage | LettaStopReason, None]: diff --git a/letta/llm_api/anthropic_client.py b/letta/llm_api/anthropic_client.py index c6516663..ece74740 100644 --- a/letta/llm_api/anthropic_client.py +++ b/letta/llm_api/anthropic_client.py @@ -49,9 +49,7 @@ from letta.schemas.openai.chat_completion_response import ( FunctionCall, Message as ChoiceMessage, ToolCall, - UsageStatistics, ) -from letta.schemas.response_format import JsonSchemaResponseFormat from letta.schemas.usage import LettaUsageStatistics from letta.settings import model_settings diff --git a/letta/llm_api/chatgpt_oauth_client.py b/letta/llm_api/chatgpt_oauth_client.py index e392355f..e5c8f4c0 100644 --- a/letta/llm_api/chatgpt_oauth_client.py +++ b/letta/llm_api/chatgpt_oauth_client.py @@ -2,10 +2,9 @@ import asyncio import json -from typing import Any, AsyncIterator, Callable, Dict, List, Optional, Union +from typing import Any, AsyncIterator, Dict, List, Optional import httpx -from openai.types.chat.chat_completion_chunk import ChatCompletionChunk from openai.types.responses import ( Response, ResponseCompletedEvent, @@ -50,11 +49,6 @@ from letta.schemas.llm_config import LLMConfig from letta.schemas.message import Message as PydanticMessage from letta.schemas.openai.chat_completion_response import ( ChatCompletionResponse, - Choice, - FunctionCall, - Message as ChoiceMessage, - ToolCall, - UsageStatistics, ) from letta.schemas.providers.chatgpt_oauth import ChatGPTOAuthCredentials, ChatGPTOAuthProvider from letta.schemas.usage import LettaUsageStatistics diff --git a/letta/llm_api/helpers.py b/letta/llm_api/helpers.py index aee2b73b..85695a48 100644 --- a/letta/llm_api/helpers.py +++ b/letta/llm_api/helpers.py @@ -1,23 +1,17 @@ import copy import json -import logging from collections import OrderedDict -from typing import Any, List, Optional, Union +from typing import List, Optional -from letta.constants import OPENAI_CONTEXT_WINDOW_ERROR_SUBSTRING from letta.helpers.json_helpers import json_dumps from letta.log import get_logger -from letta.schemas.message import Message from letta.schemas.openai.chat_completion_response import ChatCompletionResponse, Choice from letta.schemas.response_format import ( JsonObjectResponseFormat, JsonSchemaResponseFormat, - ResponseFormatType, ResponseFormatUnion, TextResponseFormat, ) -from letta.settings import summarizer_settings -from letta.utils import printd logger = get_logger(__name__) diff --git a/letta/llm_api/openai_client.py b/letta/llm_api/openai_client.py index e4b78736..b0a79d71 100644 --- a/letta/llm_api/openai_client.py +++ b/letta/llm_api/openai_client.py @@ -48,7 +48,6 @@ from letta.schemas.message import Message as PydanticMessage from letta.schemas.openai.chat_completion_request import ( ChatCompletionRequest, FunctionCall as ToolFunctionChoiceFunctionCall, - FunctionSchema, Tool as OpenAITool, ToolFunctionChoice, cast_message_to_subtype, @@ -59,7 +58,6 @@ from letta.schemas.openai.chat_completion_response import ( FunctionCall, Message as ChoiceMessage, ToolCall, - UsageStatistics, ) from letta.schemas.openai.responses_request import ResponsesRequest from letta.schemas.response_format import JsonSchemaResponseFormat diff --git a/letta/llm_api/sglang_native_client.py b/letta/llm_api/sglang_native_client.py index 341be2c5..ade9adc3 100644 --- a/letta/llm_api/sglang_native_client.py +++ b/letta/llm_api/sglang_native_client.py @@ -9,7 +9,7 @@ The OpenAI-compatible endpoint only returns token strings, not IDs, making it impossible to accurately reconstruct the token sequence for training. """ -from typing import Any, Dict, List, Optional +from typing import Any, Dict, Optional import httpx @@ -20,18 +20,18 @@ logger = get_logger(__name__) class SGLangNativeClient: """Client for SGLang's native /generate endpoint. - + Unlike the OpenAI-compatible endpoint, this returns: - output_ids: List of token IDs - output_token_logprobs: List of [logprob, token_id, top_logprob] tuples - + This is essential for RL training where we need exact token IDs, not re-tokenized text. """ - + def __init__(self, base_url: str, api_key: Optional[str] = None): """ Initialize the SGLang native client. - + Args: base_url: Base URL for SGLang server (e.g., http://localhost:30000) api_key: Optional API key for authentication @@ -41,7 +41,7 @@ class SGLangNativeClient: if self.base_url.endswith("/v1"): self.base_url = self.base_url[:-3] self.api_key = api_key - + async def generate( self, text: str, @@ -50,19 +50,19 @@ class SGLangNativeClient: ) -> Dict[str, Any]: """ Call SGLang's native /generate endpoint. - + Args: text: The formatted prompt text (with chat template applied) sampling_params: Sampling parameters (temperature, max_new_tokens, etc.) return_logprob: Whether to return logprobs (default True for RL training) - + Returns: Response dict with: - text: Generated text - output_ids: List of token IDs - output_token_logprobs: List of [logprob, token_id, top_logprob] tuples - meta_info: Metadata including finish_reason, prompt_tokens, etc. - + Example response: { "text": "Hello! How can I help?", @@ -82,13 +82,13 @@ class SGLangNativeClient: headers = {"Content-Type": "application/json"} if self.api_key: headers["Authorization"] = f"Bearer {self.api_key}" - + payload = { "text": text, "sampling_params": sampling_params or {}, "return_logprob": return_logprob, } - + async with httpx.AsyncClient(timeout=300.0) as client: response = await client.post( f"{self.base_url}/generate", @@ -97,7 +97,7 @@ class SGLangNativeClient: ) response.raise_for_status() return response.json() - + async def health_check(self) -> bool: """Check if the SGLang server is healthy.""" try: diff --git a/letta/local_llm/constants.py b/letta/local_llm/constants.py index 19fce8e8..c4973717 100644 --- a/letta/local_llm/constants.py +++ b/letta/local_llm/constants.py @@ -2,7 +2,7 @@ # (settings.py imports from this module indirectly through log.py) # Import this here to avoid circular dependency at module level from letta.local_llm.llm_chat_completion_wrappers.chatml import ChatMLInnerMonologueWrapper -from letta.settings import DEFAULT_WRAPPER_NAME, INNER_THOUGHTS_KWARG +from letta.settings import INNER_THOUGHTS_KWARG DEFAULT_WRAPPER = ChatMLInnerMonologueWrapper INNER_THOUGHTS_KWARG_VERTEX = "thinking" diff --git a/letta/local_llm/settings/settings.py b/letta/local_llm/settings/settings.py index 3671e30b..18ecb6e7 100644 --- a/letta/local_llm/settings/settings.py +++ b/letta/local_llm/settings/settings.py @@ -46,7 +46,7 @@ def get_completions_settings(defaults="simple") -> dict: with open(settings_file, "r", encoding="utf-8") as file: user_settings = json.load(file) if len(user_settings) > 0: - printd(f"Updating base settings with the following user settings:\n{json_dumps(user_settings, indent=2)}") + printd(f"Updating base settings with the following user settings:\n{json.dumps(user_settings, indent=2)}") settings.update(user_settings) else: printd(f"'{settings_file}' was empty, ignoring...") diff --git a/letta/main.py b/letta/main.py index 5c156571..204f0c6e 100644 --- a/letta/main.py +++ b/letta/main.py @@ -1,5 +1,3 @@ -import os - import typer from letta.cli.cli import server diff --git a/letta/orm/__init__.py b/letta/orm/__init__.py index 72e1112a..04fa066a 100644 --- a/letta/orm/__init__.py +++ b/letta/orm/__init__.py @@ -1,44 +1,48 @@ -from letta.orm.agent import Agent -from letta.orm.agents_tags import AgentsTags -from letta.orm.archive import Archive -from letta.orm.archives_agents import ArchivesAgents -from letta.orm.base import Base -from letta.orm.block import Block -from letta.orm.block_history import BlockHistory -from letta.orm.blocks_agents import BlocksAgents -from letta.orm.blocks_conversations import BlocksConversations -from letta.orm.blocks_tags import BlocksTags -from letta.orm.conversation import Conversation -from letta.orm.conversation_messages import ConversationMessage -from letta.orm.file import FileMetadata -from letta.orm.files_agents import FileAgent -from letta.orm.group import Group -from letta.orm.groups_agents import GroupsAgents -from letta.orm.groups_blocks import GroupsBlocks -from letta.orm.identities_agents import IdentitiesAgents -from letta.orm.identities_blocks import IdentitiesBlocks -from letta.orm.identity import Identity -from letta.orm.job import Job -from letta.orm.llm_batch_items import LLMBatchItem -from letta.orm.llm_batch_job import LLMBatchJob -from letta.orm.mcp_oauth import MCPOAuth -from letta.orm.mcp_server import MCPServer -from letta.orm.message import Message -from letta.orm.organization import Organization -from letta.orm.passage import ArchivalPassage, BasePassage, SourcePassage -from letta.orm.passage_tag import PassageTag -from letta.orm.prompt import Prompt -from letta.orm.provider import Provider -from letta.orm.provider_model import ProviderModel -from letta.orm.provider_trace import ProviderTrace -from letta.orm.provider_trace_metadata import ProviderTraceMetadata -from letta.orm.run import Run -from letta.orm.run_metrics import RunMetrics -from letta.orm.sandbox_config import AgentEnvironmentVariable, SandboxConfig, SandboxEnvironmentVariable -from letta.orm.source import Source -from letta.orm.sources_agents import SourcesAgents -from letta.orm.step import Step -from letta.orm.step_metrics import StepMetrics -from letta.orm.tool import Tool -from letta.orm.tools_agents import ToolsAgents -from letta.orm.user import User +from letta.orm.agent import Agent as Agent +from letta.orm.agents_tags import AgentsTags as AgentsTags +from letta.orm.archive import Archive as Archive +from letta.orm.archives_agents import ArchivesAgents as ArchivesAgents +from letta.orm.base import Base as Base +from letta.orm.block import Block as Block +from letta.orm.block_history import BlockHistory as BlockHistory +from letta.orm.blocks_agents import BlocksAgents as BlocksAgents +from letta.orm.blocks_conversations import BlocksConversations as BlocksConversations +from letta.orm.blocks_tags import BlocksTags as BlocksTags +from letta.orm.conversation import Conversation as Conversation +from letta.orm.conversation_messages import ConversationMessage as ConversationMessage +from letta.orm.file import FileMetadata as FileMetadata +from letta.orm.files_agents import FileAgent as FileAgent +from letta.orm.group import Group as Group +from letta.orm.groups_agents import GroupsAgents as GroupsAgents +from letta.orm.groups_blocks import GroupsBlocks as GroupsBlocks +from letta.orm.identities_agents import IdentitiesAgents as IdentitiesAgents +from letta.orm.identities_blocks import IdentitiesBlocks as IdentitiesBlocks +from letta.orm.identity import Identity as Identity +from letta.orm.job import Job as Job +from letta.orm.llm_batch_items import LLMBatchItem as LLMBatchItem +from letta.orm.llm_batch_job import LLMBatchJob as LLMBatchJob +from letta.orm.mcp_oauth import MCPOAuth as MCPOAuth +from letta.orm.mcp_server import MCPServer as MCPServer +from letta.orm.message import Message as Message +from letta.orm.organization import Organization as Organization +from letta.orm.passage import ArchivalPassage as ArchivalPassage, BasePassage as BasePassage, SourcePassage as SourcePassage +from letta.orm.passage_tag import PassageTag as PassageTag +from letta.orm.prompt import Prompt as Prompt +from letta.orm.provider import Provider as Provider +from letta.orm.provider_model import ProviderModel as ProviderModel +from letta.orm.provider_trace import ProviderTrace as ProviderTrace +from letta.orm.provider_trace_metadata import ProviderTraceMetadata as ProviderTraceMetadata +from letta.orm.run import Run as Run +from letta.orm.run_metrics import RunMetrics as RunMetrics +from letta.orm.sandbox_config import ( + AgentEnvironmentVariable as AgentEnvironmentVariable, + SandboxConfig as SandboxConfig, + SandboxEnvironmentVariable as SandboxEnvironmentVariable, +) +from letta.orm.source import Source as Source +from letta.orm.sources_agents import SourcesAgents as SourcesAgents +from letta.orm.step import Step as Step +from letta.orm.step_metrics import StepMetrics as StepMetrics +from letta.orm.tool import Tool as Tool +from letta.orm.tools_agents import ToolsAgents as ToolsAgents +from letta.orm.user import User as User diff --git a/letta/orm/agent.py b/letta/orm/agent.py index 6fad20dd..86540196 100644 --- a/letta/orm/agent.py +++ b/letta/orm/agent.py @@ -37,7 +37,6 @@ if TYPE_CHECKING: from letta.orm.run import Run from letta.orm.source import Source from letta.orm.tool import Tool - from letta.services.summarizer.summarizer_config import CompactionSettings class Agent(SqlalchemyBase, OrganizationMixin, ProjectMixin, TemplateEntityMixin, TemplateMixin, AsyncAttrs): @@ -123,7 +122,7 @@ class Agent(SqlalchemyBase, OrganizationMixin, ProjectMixin, TemplateEntityMixin # relationships organization: Mapped["Organization"] = relationship("Organization", back_populates="agents", lazy="raise") - tool_exec_environment_variables: Mapped[List["AgentEnvironmentVariable"]] = relationship( + tool_exec_environment_variables: Mapped[List["AgentEnvironmentVariable"]] = relationship( # noqa: F821 "AgentEnvironmentVariable", back_populates="agent", cascade="all, delete-orphan", @@ -161,14 +160,14 @@ class Agent(SqlalchemyBase, OrganizationMixin, ProjectMixin, TemplateEntityMixin back_populates="agents", passive_deletes=True, ) - groups: Mapped[List["Group"]] = relationship( + groups: Mapped[List["Group"]] = relationship( # noqa: F821 "Group", secondary="groups_agents", lazy="raise", back_populates="agents", passive_deletes=True, ) - multi_agent_group: Mapped["Group"] = relationship( + multi_agent_group: Mapped["Group"] = relationship( # noqa: F821 "Group", lazy="selectin", viewonly=True, @@ -176,7 +175,7 @@ class Agent(SqlalchemyBase, OrganizationMixin, ProjectMixin, TemplateEntityMixin foreign_keys="[Group.manager_agent_id]", uselist=False, ) - batch_items: Mapped[List["LLMBatchItem"]] = relationship("LLMBatchItem", back_populates="agent", lazy="raise") + batch_items: Mapped[List["LLMBatchItem"]] = relationship("LLMBatchItem", back_populates="agent", lazy="raise") # noqa: F821 file_agents: Mapped[List["FileAgent"]] = relationship( "FileAgent", back_populates="agent", diff --git a/letta/orm/agents_tags.py b/letta/orm/agents_tags.py index d7177083..0507a10f 100644 --- a/letta/orm/agents_tags.py +++ b/letta/orm/agents_tags.py @@ -21,4 +21,4 @@ class AgentsTags(Base): tag: Mapped[str] = mapped_column(String, doc="The name of the tag associated with the agent.", primary_key=True) # Relationships - agent: Mapped["Agent"] = relationship("Agent", back_populates="tags") + agent: Mapped["Agent"] = relationship("Agent", back_populates="tags") # noqa: F821 diff --git a/letta/orm/archives_agents.py b/letta/orm/archives_agents.py index 06c63a5e..c39e98df 100644 --- a/letta/orm/archives_agents.py +++ b/letta/orm/archives_agents.py @@ -23,5 +23,5 @@ class ArchivesAgents(Base): is_owner: Mapped[bool] = mapped_column(Boolean, default=False, doc="Whether this agent created/owns the archive") # relationships - agent: Mapped["Agent"] = relationship("Agent", back_populates="archives_agents") - archive: Mapped["Archive"] = relationship("Archive", back_populates="archives_agents") + agent: Mapped["Agent"] = relationship("Agent", back_populates="archives_agents") # noqa: F821 + archive: Mapped["Archive"] = relationship("Archive", back_populates="archives_agents") # noqa: F821 diff --git a/letta/orm/block.py b/letta/orm/block.py index e9440730..a9faca50 100644 --- a/letta/orm/block.py +++ b/letta/orm/block.py @@ -5,7 +5,6 @@ from sqlalchemy.orm import Mapped, declared_attr, mapped_column, relationship from letta.constants import CORE_MEMORY_BLOCK_CHAR_LIMIT from letta.orm.block_history import BlockHistory -from letta.orm.blocks_agents import BlocksAgents from letta.orm.mixins import OrganizationMixin, ProjectMixin, TemplateEntityMixin, TemplateMixin from letta.orm.sqlalchemy_base import SqlalchemyBase from letta.schemas.block import Block as PydanticBlock, Human, Persona @@ -61,7 +60,7 @@ class Block(OrganizationMixin, SqlalchemyBase, ProjectMixin, TemplateEntityMixin # relationships organization: Mapped[Optional["Organization"]] = relationship("Organization", lazy="raise") - agents: Mapped[List["Agent"]] = relationship( + agents: Mapped[List["Agent"]] = relationship( # noqa: F821 "Agent", secondary="blocks_agents", lazy="raise", @@ -76,7 +75,7 @@ class Block(OrganizationMixin, SqlalchemyBase, ProjectMixin, TemplateEntityMixin back_populates="blocks", passive_deletes=True, ) - groups: Mapped[List["Group"]] = relationship( + groups: Mapped[List["Group"]] = relationship( # noqa: F821 "Group", secondary="groups_blocks", lazy="raise", diff --git a/letta/orm/blocks_tags.py b/letta/orm/blocks_tags.py index 23412df8..90f79678 100644 --- a/letta/orm/blocks_tags.py +++ b/letta/orm/blocks_tags.py @@ -34,4 +34,4 @@ class BlocksTags(Base): _last_updated_by_id: Mapped[Optional[str]] = mapped_column(String, nullable=True) # Relationships - block: Mapped["Block"] = relationship("Block", back_populates="tags") + block: Mapped["Block"] = relationship("Block", back_populates="tags") # noqa: F821 diff --git a/letta/orm/files_agents.py b/letta/orm/files_agents.py index 1c768711..9757cd2e 100644 --- a/letta/orm/files_agents.py +++ b/letta/orm/files_agents.py @@ -85,7 +85,7 @@ class FileAgent(SqlalchemyBase, OrganizationMixin): ) # relationships - agent: Mapped["Agent"] = relationship( + agent: Mapped["Agent"] = relationship( # noqa: F821 "Agent", back_populates="file_agents", lazy="selectin", diff --git a/letta/orm/group.py b/letta/orm/group.py index 5b2c7e57..28653882 100644 --- a/letta/orm/group.py +++ b/letta/orm/group.py @@ -27,12 +27,12 @@ class Group(SqlalchemyBase, OrganizationMixin, ProjectMixin, TemplateMixin): hidden: Mapped[Optional[bool]] = mapped_column(nullable=True, doc="If set to True, the group will be hidden.") # relationships - organization: Mapped["Organization"] = relationship("Organization", back_populates="groups") + organization: Mapped["Organization"] = relationship("Organization", back_populates="groups") # noqa: F821 agent_ids: Mapped[List[str]] = mapped_column(JSON, nullable=False, doc="Ordered list of agent IDs in this group") - agents: Mapped[List["Agent"]] = relationship( + agents: Mapped[List["Agent"]] = relationship( # noqa: F821 "Agent", secondary="groups_agents", lazy="selectin", passive_deletes=True, back_populates="groups" ) - shared_blocks: Mapped[List["Block"]] = relationship( + shared_blocks: Mapped[List["Block"]] = relationship( # noqa: F821 "Block", secondary="groups_blocks", lazy="selectin", passive_deletes=True, back_populates="groups" ) - manager_agent: Mapped["Agent"] = relationship("Agent", lazy="joined", back_populates="multi_agent_group") + manager_agent: Mapped["Agent"] = relationship("Agent", lazy="joined", back_populates="multi_agent_group") # noqa: F821 diff --git a/letta/orm/identity.py b/letta/orm/identity.py index dd7ae51c..b0058a69 100644 --- a/letta/orm/identity.py +++ b/letta/orm/identity.py @@ -36,11 +36,11 @@ class Identity(SqlalchemyBase, OrganizationMixin, ProjectMixin): ) # relationships - organization: Mapped["Organization"] = relationship("Organization", back_populates="identities") - agents: Mapped[List["Agent"]] = relationship( + organization: Mapped["Organization"] = relationship("Organization", back_populates="identities") # noqa: F821 + agents: Mapped[List["Agent"]] = relationship( # noqa: F821 "Agent", secondary="identities_agents", lazy="selectin", passive_deletes=True, back_populates="identities" ) - blocks: Mapped[List["Block"]] = relationship( + blocks: Mapped[List["Block"]] = relationship( # noqa: F821 "Block", secondary="identities_blocks", lazy="selectin", passive_deletes=True, back_populates="identities" ) diff --git a/letta/orm/llm_batch_items.py b/letta/orm/llm_batch_items.py index b4f08cb0..71d3c255 100644 --- a/letta/orm/llm_batch_items.py +++ b/letta/orm/llm_batch_items.py @@ -49,6 +49,6 @@ class LLMBatchItem(SqlalchemyBase, OrganizationMixin, AgentMixin): ) # relationships - organization: Mapped["Organization"] = relationship("Organization", back_populates="llm_batch_items") - batch: Mapped["LLMBatchJob"] = relationship("LLMBatchJob", back_populates="items", lazy="selectin") - agent: Mapped["Agent"] = relationship("Agent", back_populates="batch_items", lazy="selectin") + organization: Mapped["Organization"] = relationship("Organization", back_populates="llm_batch_items") # noqa: F821 + batch: Mapped["LLMBatchJob"] = relationship("LLMBatchJob", back_populates="items", lazy="selectin") # noqa: F821 + agent: Mapped["Agent"] = relationship("Agent", back_populates="batch_items", lazy="selectin") # noqa: F821 diff --git a/letta/orm/llm_batch_job.py b/letta/orm/llm_batch_job.py index db085dc7..cf67e4d1 100644 --- a/letta/orm/llm_batch_job.py +++ b/letta/orm/llm_batch_job.py @@ -47,5 +47,5 @@ class LLMBatchJob(SqlalchemyBase, OrganizationMixin): String, ForeignKey("jobs.id", ondelete="CASCADE"), nullable=False, doc="ID of the Letta batch job" ) - organization: Mapped["Organization"] = relationship("Organization", back_populates="llm_batch_jobs") - items: Mapped[List["LLMBatchItem"]] = relationship("LLMBatchItem", back_populates="batch", lazy="selectin") + organization: Mapped["Organization"] = relationship("Organization", back_populates="llm_batch_jobs") # noqa: F821 + items: Mapped[List["LLMBatchItem"]] = relationship("LLMBatchItem", back_populates="batch", lazy="selectin") # noqa: F821 diff --git a/letta/orm/mcp_server.py b/letta/orm/mcp_server.py index a62ff1d6..955a9059 100644 --- a/letta/orm/mcp_server.py +++ b/letta/orm/mcp_server.py @@ -1,4 +1,3 @@ -import json from typing import TYPE_CHECKING, Optional from sqlalchemy import JSON, String, Text, UniqueConstraint diff --git a/letta/orm/message.py b/letta/orm/message.py index 578c7120..f3c8bb33 100644 --- a/letta/orm/message.py +++ b/letta/orm/message.py @@ -83,12 +83,12 @@ class Message(SqlalchemyBase, OrganizationMixin, AgentMixin): ) # Relationships - organization: Mapped["Organization"] = relationship("Organization", back_populates="messages", lazy="raise") - step: Mapped["Step"] = relationship("Step", back_populates="messages", lazy="selectin") - run: Mapped["Run"] = relationship("Run", back_populates="messages", lazy="selectin") + organization: Mapped["Organization"] = relationship("Organization", back_populates="messages", lazy="raise") # noqa: F821 + step: Mapped["Step"] = relationship("Step", back_populates="messages", lazy="selectin") # noqa: F821 + run: Mapped["Run"] = relationship("Run", back_populates="messages", lazy="selectin") # noqa: F821 @property - def job(self) -> Optional["Job"]: + def job(self) -> Optional["Job"]: # noqa: F821 """Get the job associated with this message, if any.""" return self.job_message.job if self.job_message else None diff --git a/letta/orm/passage.py b/letta/orm/passage.py index 4bbe9509..457a8ec6 100644 --- a/letta/orm/passage.py +++ b/letta/orm/passage.py @@ -78,7 +78,7 @@ class ArchivalPassage(BasePassage, ArchiveMixin): __tablename__ = "archival_passages" # junction table for efficient tag queries (complements json column above) - passage_tags: Mapped[List["PassageTag"]] = relationship( + passage_tags: Mapped[List["PassageTag"]] = relationship( # noqa: F821 "PassageTag", back_populates="passage", cascade="all, delete-orphan", lazy="noload" ) diff --git a/letta/orm/provider_trace.py b/letta/orm/provider_trace.py index 90399b5d..9a3875f3 100644 --- a/letta/orm/provider_trace.py +++ b/letta/orm/provider_trace.py @@ -43,4 +43,4 @@ class ProviderTrace(SqlalchemyBase, OrganizationMixin): ) # Relationships - organization: Mapped["Organization"] = relationship("Organization", lazy="selectin") + organization: Mapped["Organization"] = relationship("Organization", lazy="selectin") # noqa: F821 diff --git a/letta/orm/provider_trace_metadata.py b/letta/orm/provider_trace_metadata.py index 5d8fecf7..55d4b0ab 100644 --- a/letta/orm/provider_trace_metadata.py +++ b/letta/orm/provider_trace_metadata.py @@ -42,4 +42,4 @@ class ProviderTraceMetadata(SqlalchemyBase, OrganizationMixin): user_id: Mapped[Optional[str]] = mapped_column(String, nullable=True, doc="ID of the user who initiated the request") # Relationships - organization: Mapped["Organization"] = relationship("Organization", lazy="selectin") + organization: Mapped["Organization"] = relationship("Organization", lazy="selectin") # noqa: F821 diff --git a/letta/orm/run.py b/letta/orm/run.py index b2444e54..947a68bf 100644 --- a/letta/orm/run.py +++ b/letta/orm/run.py @@ -2,7 +2,7 @@ import uuid from datetime import datetime from typing import TYPE_CHECKING, List, Optional -from sqlalchemy import JSON, BigInteger, Boolean, DateTime, ForeignKey, Index, String +from sqlalchemy import JSON, BigInteger, Boolean, ForeignKey, Index, String from sqlalchemy.orm import Mapped, mapped_column, relationship from letta.orm.mixins import OrganizationMixin, ProjectMixin, TemplateMixin diff --git a/letta/orm/run_metrics.py b/letta/orm/run_metrics.py index 22c5d8e7..8cc4d79a 100644 --- a/letta/orm/run_metrics.py +++ b/letta/orm/run_metrics.py @@ -1,7 +1,7 @@ from datetime import datetime, timezone from typing import TYPE_CHECKING, List, Optional -from sqlalchemy import JSON, BigInteger, ForeignKey, Integer, String +from sqlalchemy import JSON, BigInteger, ForeignKey, Integer from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import Mapped, Session, mapped_column, relationship @@ -14,7 +14,6 @@ from letta.settings import DatabaseChoice, settings if TYPE_CHECKING: from letta.orm.agent import Agent from letta.orm.run import Run - from letta.orm.step import Step class RunMetrics(SqlalchemyBase, ProjectMixin, AgentMixin, OrganizationMixin, TemplateMixin): diff --git a/letta/orm/sqlalchemy_base.py b/letta/orm/sqlalchemy_base.py index 03684236..ba85cbf2 100644 --- a/letta/orm/sqlalchemy_base.py +++ b/letta/orm/sqlalchemy_base.py @@ -122,7 +122,7 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base): query_text: Optional[str] = None, query_embedding: Optional[List[float]] = None, ascending: bool = True, - actor: Optional["User"] = None, + actor: Optional["User"] = None, # noqa: F821 access: Optional[List[Literal["read", "write", "admin"]]] = ["read"], access_type: AccessType = AccessType.ORGANIZATION, join_model: Optional[Base] = None, @@ -222,7 +222,7 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base): query_text: Optional[str] = None, query_embedding: Optional[List[float]] = None, ascending: bool = True, - actor: Optional["User"] = None, + actor: Optional["User"] = None, # noqa: F821 access: Optional[List[Literal["read", "write", "admin"]]] = ["read"], access_type: AccessType = AccessType.ORGANIZATION, join_model: Optional[Base] = None, @@ -415,7 +415,7 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base): cls, db_session: "AsyncSession", identifier: Optional[str] = None, - actor: Optional["User"] = None, + actor: Optional["User"] = None, # noqa: F821 access: Optional[List[Literal["read", "write", "admin"]]] = ["read"], access_type: AccessType = AccessType.ORGANIZATION, check_is_deleted: bool = False, @@ -451,7 +451,7 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base): cls, db_session: "AsyncSession", identifiers: List[str] = [], - actor: Optional["User"] = None, + actor: Optional["User"] = None, # noqa: F821 access: Optional[List[Literal["read", "write", "admin"]]] = ["read"], access_type: AccessType = AccessType.ORGANIZATION, check_is_deleted: bool = False, @@ -471,7 +471,7 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base): def _read_multiple_preprocess( cls, identifiers: List[str], - actor: Optional["User"], + actor: Optional["User"], # noqa: F821 access: Optional[List[Literal["read", "write", "admin"]]], access_type: AccessType, check_is_deleted: bool, @@ -543,7 +543,7 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base): async def create_async( self, db_session: "AsyncSession", - actor: Optional["User"] = None, + actor: Optional["User"] = None, # noqa: F821 no_commit: bool = False, no_refresh: bool = False, ignore_conflicts: bool = False, @@ -599,7 +599,7 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base): cls, items: List["SqlalchemyBase"], db_session: "AsyncSession", - actor: Optional["User"] = None, + actor: Optional["User"] = None, # noqa: F821 no_commit: bool = False, no_refresh: bool = False, ) -> List["SqlalchemyBase"]: @@ -654,7 +654,7 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base): cls._handle_dbapi_error(e) @handle_db_timeout - async def delete_async(self, db_session: "AsyncSession", actor: Optional["User"] = None) -> "SqlalchemyBase": + async def delete_async(self, db_session: "AsyncSession", actor: Optional["User"] = None) -> "SqlalchemyBase": # noqa: F821 """Soft delete a record asynchronously (mark as deleted).""" logger.debug(f"Soft deleting {self.__class__.__name__} with ID: {self.id} with actor={actor} (async)") @@ -665,7 +665,7 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base): return await self.update_async(db_session) @handle_db_timeout - async def hard_delete_async(self, db_session: "AsyncSession", actor: Optional["User"] = None) -> None: + async def hard_delete_async(self, db_session: "AsyncSession", actor: Optional["User"] = None) -> None: # noqa: F821 """Permanently removes the record from the database asynchronously.""" obj_id = self.id obj_class = self.__class__.__name__ @@ -694,7 +694,7 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base): cls, db_session: "AsyncSession", identifiers: List[str], - actor: Optional["User"], + actor: Optional["User"], # noqa: F821 access: Optional[List[Literal["read", "write", "admin"]]] = ["write"], access_type: AccessType = AccessType.ORGANIZATION, ) -> None: @@ -729,7 +729,11 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base): @handle_db_timeout async def update_async( - self, db_session: "AsyncSession", actor: Optional["User"] = None, no_commit: bool = False, no_refresh: bool = False + self, + db_session: "AsyncSession", + actor: Optional["User"] = None, # noqa: F821 + no_commit: bool = False, + no_refresh: bool = False, ) -> "SqlalchemyBase": """Async version of update function""" logger.debug(f"Updating {self.__class__.__name__} with ID: {self.id} with actor={actor}") @@ -774,7 +778,7 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base): cls, *, db_session: "Session", - actor: Optional["User"] = None, + actor: Optional["User"] = None, # noqa: F821 access: Optional[List[Literal["read", "write", "admin"]]] = ["read"], access_type: AccessType = AccessType.ORGANIZATION, check_is_deleted: bool = False, @@ -814,7 +818,7 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base): cls, *, db_session: "AsyncSession", - actor: Optional["User"] = None, + actor: Optional["User"] = None, # noqa: F821 access: Optional[List[Literal["read", "write", "admin"]]] = ["read"], access_type: AccessType = AccessType.ORGANIZATION, check_is_deleted: bool = False, @@ -850,11 +854,11 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base): @classmethod def apply_access_predicate( cls, - query: "Select", - actor: "User", + query: "Select", # noqa: F821 + actor: "User", # noqa: F821 access: List[Literal["read", "write", "admin"]], access_type: AccessType = AccessType.ORGANIZATION, - ) -> "Select": + ) -> "Select": # noqa: F821 """applies a WHERE clause restricting results to the given actor and access level Args: query: The initial sqlalchemy select statement diff --git a/letta/otel/tracing.py b/letta/otel/tracing.py index a2b8c86a..78144911 100644 --- a/letta/otel/tracing.py +++ b/letta/otel/tracing.py @@ -4,7 +4,6 @@ import itertools import json import re import time -import traceback from functools import wraps from typing import Any, Dict, List, Optional diff --git a/letta/plugins/defaults.py b/letta/plugins/defaults.py index f3032ad2..7d105b6b 100644 --- a/letta/plugins/defaults.py +++ b/letta/plugins/defaults.py @@ -1,6 +1,3 @@ -from letta.settings import settings - - def is_experimental_enabled(feature_name: str, **kwargs) -> bool: # if feature_name in ("async_agent_loop", "summarize"): # if not (kwargs.get("eligibility", False) and settings.use_experimental): diff --git a/letta/prompts/prompt_generator.py b/letta/prompts/prompt_generator.py index ab8e7208..e3cedcac 100644 --- a/letta/prompts/prompt_generator.py +++ b/letta/prompts/prompt_generator.py @@ -12,6 +12,13 @@ from letta.otel.tracing import trace_method from letta.schemas.memory import Memory +class PreserveMapping(dict): + """Used to preserve (do not modify) undefined variables in the system prompt""" + + def __missing__(self, key): + return "{" + key + "}" + + class PromptGenerator: # TODO: This code is kind of wonky and deserves a rewrite @trace_method diff --git a/letta/schemas/agent.py b/letta/schemas/agent.py index 6aa43f88..666aaf60 100644 --- a/letta/schemas/agent.py +++ b/letta/schemas/agent.py @@ -5,7 +5,6 @@ from typing import Dict, List, Literal, Optional from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator from letta.constants import ( - CORE_MEMORY_LINE_NUMBER_WARNING, DEFAULT_EMBEDDING_CHUNK_SIZE, MAX_FILES_OPEN_LIMIT, MAX_PER_FILE_VIEW_WINDOW_CHAR_LIMIT, @@ -15,7 +14,6 @@ from letta.schemas.block import Block, CreateBlock from letta.schemas.embedding_config import EmbeddingConfig from letta.schemas.enums import PrimitiveType from letta.schemas.environment_variables import AgentEnvironmentVariable -from letta.schemas.file import FileStatus from letta.schemas.group import Group from letta.schemas.identity import Identity from letta.schemas.letta_base import OrmMetadataBase diff --git a/letta/schemas/letta_message_content.py b/letta/schemas/letta_message_content.py index 7c62ebd3..6ea9c1cd 100644 --- a/letta/schemas/letta_message_content.py +++ b/letta/schemas/letta_message_content.py @@ -1,7 +1,6 @@ from enum import Enum from typing import Annotated, List, Literal, Optional, Union -from openai.types import Reasoning from pydantic import BaseModel, Field diff --git a/letta/schemas/letta_response.py b/letta/schemas/letta_response.py index a964ab99..b211d722 100644 --- a/letta/schemas/letta_response.py +++ b/letta/schemas/letta_response.py @@ -7,15 +7,13 @@ from typing import Any, List, Literal, Optional, Union from pydantic import BaseModel, Field, RootModel from letta.helpers.json_helpers import json_dumps -from letta.schemas.enums import JobStatus, MessageStreamStatus -from letta.schemas.openai.chat_completion_response import ChoiceLogprobs +from letta.schemas.enums import JobStatus from letta.schemas.letta_message import ( ApprovalRequestMessage, ApprovalResponseMessage, AssistantMessage, HiddenReasoningMessage, LettaErrorMessage, - LettaMessage, LettaMessageUnion, LettaPing, ReasoningMessage, @@ -26,6 +24,7 @@ from letta.schemas.letta_message import ( ) from letta.schemas.letta_stop_reason import LettaStopReason from letta.schemas.message import Message +from letta.schemas.openai.chat_completion_response import ChoiceLogprobs from letta.schemas.usage import LettaUsageStatistics # TODO: consider moving into own file @@ -33,31 +32,21 @@ from letta.schemas.usage import LettaUsageStatistics class TurnTokenData(BaseModel): """Token data for a single LLM generation turn in a multi-turn agent interaction. - + Used for RL training to track token IDs and logprobs across all LLM calls, not just the final one. Tool results are included so the client can tokenize them with loss_mask=0 (non-trainable). """ + role: Literal["assistant", "tool"] = Field( - ..., - description="Role of this turn: 'assistant' for LLM generations (trainable), 'tool' for tool results (non-trainable)." - ) - output_ids: Optional[List[int]] = Field( - None, - description="Token IDs from SGLang native endpoint. Only present for assistant turns." + ..., description="Role of this turn: 'assistant' for LLM generations (trainable), 'tool' for tool results (non-trainable)." ) + output_ids: Optional[List[int]] = Field(None, description="Token IDs from SGLang native endpoint. Only present for assistant turns.") output_token_logprobs: Optional[List[List[Any]]] = Field( - None, - description="Logprobs from SGLang: [[logprob, token_id, top_logprob_or_null], ...]. Only present for assistant turns." - ) - content: Optional[str] = Field( - None, - description="Text content. For tool turns, client tokenizes this with loss_mask=0." - ) - tool_name: Optional[str] = Field( - None, - description="Name of the tool called. Only present for tool turns." + None, description="Logprobs from SGLang: [[logprob, token_id, top_logprob_or_null], ...]. Only present for assistant turns." ) + content: Optional[str] = Field(None, description="Text content. For tool turns, client tokenizes this with loss_mask=0.") + tool_name: Optional[str] = Field(None, description="Name of the tool called. Only present for tool turns.") class LettaResponse(BaseModel): diff --git a/letta/schemas/llm_config.py b/letta/schemas/llm_config.py index 9639a2a2..6955b9a9 100644 --- a/letta/schemas/llm_config.py +++ b/letta/schemas/llm_config.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Annotated, Literal, Optional, Union +from typing import TYPE_CHECKING, Literal, Optional from pydantic import BaseModel, ConfigDict, Field, model_validator diff --git a/letta/schemas/llm_trace.py b/letta/schemas/llm_trace.py index 537148b5..13cbb806 100644 --- a/letta/schemas/llm_trace.py +++ b/letta/schemas/llm_trace.py @@ -2,7 +2,6 @@ from __future__ import annotations -import uuid from datetime import datetime from typing import Optional diff --git a/letta/schemas/mcp.py b/letta/schemas/mcp.py index 06190ed1..614f2df9 100644 --- a/letta/schemas/mcp.py +++ b/letta/schemas/mcp.py @@ -20,7 +20,6 @@ from letta.orm.mcp_oauth import OAuthSessionStatus from letta.schemas.enums import PrimitiveType from letta.schemas.letta_base import LettaBase from letta.schemas.secret import Secret -from letta.settings import settings class BaseMCPServer(LettaBase): diff --git a/letta/schemas/mcp_server.py b/letta/schemas/mcp_server.py index a671467c..7ec807e6 100644 --- a/letta/schemas/mcp_server.py +++ b/letta/schemas/mcp_server.py @@ -1,4 +1,3 @@ -import json from datetime import datetime from typing import Annotated, Any, Dict, List, Literal, Optional, Union from urllib.parse import urlparse @@ -6,12 +5,8 @@ from urllib.parse import urlparse from pydantic import Field, field_validator from letta.functions.mcp_client.types import ( - MCP_AUTH_HEADER_AUTHORIZATION, MCP_AUTH_TOKEN_BEARER_PREFIX, MCPServerType, - SSEServerConfig, - StdioServerConfig, - StreamableHTTPServerConfig, ) from letta.orm.mcp_oauth import OAuthSessionStatus from letta.schemas.enums import PrimitiveType diff --git a/letta/schemas/memory.py b/letta/schemas/memory.py index c96c2f67..3c17f557 100644 --- a/letta/schemas/memory.py +++ b/letta/schemas/memory.py @@ -2,7 +2,7 @@ import asyncio import logging from datetime import datetime from io import StringIO -from typing import TYPE_CHECKING, List, Optional, Union +from typing import List, Optional, Union from letta.log import get_logger @@ -535,7 +535,7 @@ class BasicBlockMemory(Memory): """ super().__init__(blocks=blocks) - def core_memory_append(agent_state: "AgentState", label: str, content: str) -> Optional[str]: # type: ignore + def core_memory_append(agent_state: "AgentState", label: str, content: str) -> Optional[str]: # type: ignore # noqa: F821 """ Append to the contents of core memory. @@ -551,7 +551,7 @@ class BasicBlockMemory(Memory): agent_state.memory.update_block_value(label=label, value=new_value) return None - def core_memory_replace(agent_state: "AgentState", label: str, old_content: str, new_content: str) -> Optional[str]: # type: ignore + def core_memory_replace(agent_state: "AgentState", label: str, old_content: str, new_content: str) -> Optional[str]: # type: ignore # noqa: F821 """ Replace the contents of core memory. To delete memories, use an empty string for new_content. diff --git a/letta/schemas/message.py b/letta/schemas/message.py index e6f21675..f9c63829 100644 --- a/letta/schemas/message.py +++ b/letta/schemas/message.py @@ -11,10 +11,9 @@ import uuid from collections import OrderedDict from datetime import datetime, timezone from enum import Enum -from typing import Annotated, Any, Dict, List, Literal, Optional, Union +from typing import Any, Dict, List, Literal, Optional, Union from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction -from openai.types.responses import ResponseReasoningItem from pydantic import BaseModel, Field, field_validator, model_validator from letta.constants import DEFAULT_MESSAGE_TOOL, DEFAULT_MESSAGE_TOOL_KWARG, REQUEST_HEARTBEAT_PARAM, TOOL_CALL_ID_MAX_LEN @@ -30,7 +29,6 @@ from letta.schemas.letta_message import ( ApprovalReturn, AssistantMessage, AssistantMessageListResult, - CompactionStats, HiddenReasoningMessage, LettaMessage, LettaMessageReturnUnion, diff --git a/letta/schemas/openai/chat_completion_request.py b/letta/schemas/openai/chat_completion_request.py index c0939257..9188eb93 100644 --- a/letta/schemas/openai/chat_completion_request.py +++ b/letta/schemas/openai/chat_completion_request.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List, Literal, Optional, Union -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel, field_validator class SystemMessage(BaseModel): diff --git a/letta/schemas/provider_model.py b/letta/schemas/provider_model.py index fd948fd8..0caf889d 100644 --- a/letta/schemas/provider_model.py +++ b/letta/schemas/provider_model.py @@ -1,4 +1,3 @@ -from datetime import datetime from typing import Optional from pydantic import Field diff --git a/letta/schemas/providers/base.py b/letta/schemas/providers/base.py index bad77164..d527d4d5 100644 --- a/letta/schemas/providers/base.py +++ b/letta/schemas/providers/base.py @@ -90,7 +90,6 @@ class Provider(ProviderBase): def list_llm_models(self) -> list[LLMConfig]: """List available LLM models (deprecated: use list_llm_models_async)""" import asyncio - import warnings logger.warning("list_llm_models is deprecated, use list_llm_models_async instead", stacklevel=2) @@ -115,7 +114,6 @@ class Provider(ProviderBase): def list_embedding_models(self) -> list[EmbeddingConfig]: """List available embedding models (deprecated: use list_embedding_models_async)""" import asyncio - import warnings logger.warning("list_embedding_models is deprecated, use list_embedding_models_async instead", stacklevel=2) diff --git a/letta/schemas/providers/together.py b/letta/schemas/providers/together.py index 00dee3dc..f86636b1 100644 --- a/letta/schemas/providers/together.py +++ b/letta/schemas/providers/together.py @@ -35,8 +35,6 @@ class TogetherProvider(OpenAIProvider): return self._list_llm_models(models) async def list_embedding_models_async(self) -> list[EmbeddingConfig]: - import warnings - logger.warning( "Letta does not currently support listing embedding models for Together. Please " "contact support or reach out via GitHub or Discord to get support." diff --git a/letta/schemas/source.py b/letta/schemas/source.py index 26a533e8..3c874ad9 100644 --- a/letta/schemas/source.py +++ b/letta/schemas/source.py @@ -3,7 +3,6 @@ from typing import Optional from pydantic import Field -from letta.helpers.tpuf_client import should_use_tpuf from letta.schemas.embedding_config import EmbeddingConfig from letta.schemas.enums import PrimitiveType, VectorDBProvider from letta.schemas.letta_base import LettaBase diff --git a/letta/schemas/tool.py b/letta/schemas/tool.py index 9b94f82b..3f8726d2 100644 --- a/letta/schemas/tool.py +++ b/letta/schemas/tool.py @@ -20,7 +20,7 @@ from letta.functions.functions import get_json_schema_from_module from letta.functions.mcp_client.types import MCPTool from letta.functions.schema_generator import generate_tool_schema_for_mcp from letta.log import get_logger -from letta.schemas.enums import ToolSourceType, ToolType +from letta.schemas.enums import ToolType from letta.schemas.letta_base import LettaBase from letta.schemas.npm_requirement import NpmRequirement from letta.schemas.pip_requirement import PipRequirement diff --git a/letta/schemas/usage.py b/letta/schemas/usage.py index c066423f..e67ab13d 100644 --- a/letta/schemas/usage.py +++ b/letta/schemas/usage.py @@ -2,8 +2,6 @@ from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Tuple, Uni from pydantic import BaseModel, Field -from letta.schemas.message import Message - if TYPE_CHECKING: from letta.schemas.openai.chat_completion_response import ( UsageStatisticsCompletionTokenDetails, @@ -133,7 +131,7 @@ class LettaUsageStatistics(BaseModel): description="Estimate of tokens currently in the context window.", ) - def to_usage(self, provider_type: Optional["ProviderType"] = None) -> "UsageStatistics": + def to_usage(self, provider_type: Optional["ProviderType"] = None) -> "UsageStatistics": # noqa: F821 # noqa: F821 """Convert to UsageStatistics (OpenAI-compatible format). Args: diff --git a/letta/serialize_schemas/__init__.py b/letta/serialize_schemas/__init__.py index 1f6be200..b2082c2f 100644 --- a/letta/serialize_schemas/__init__.py +++ b/letta/serialize_schemas/__init__.py @@ -1 +1 @@ -from letta.serialize_schemas.marshmallow_agent import MarshmallowAgentSchema +from letta.serialize_schemas.marshmallow_agent import MarshmallowAgentSchema as MarshmallowAgentSchema diff --git a/letta/server/rest_api/app.py b/letta/server/rest_api/app.py index c464590b..6c062aa6 100644 --- a/letta/server/rest_api/app.py +++ b/letta/server/rest_api/app.py @@ -5,7 +5,6 @@ import logging import os import platform import sys -import threading from contextlib import asynccontextmanager from functools import partial from pathlib import Path @@ -27,7 +26,7 @@ from starlette.middleware.cors import CORSMiddleware from letta.__init__ import __version__ as letta_version from letta.agents.exceptions import IncompatibleAgentType -from letta.constants import ADMIN_PREFIX, API_PREFIX, OPENAI_API_PREFIX +from letta.constants import ADMIN_PREFIX, API_PREFIX from letta.errors import ( AgentExportIdMappingError, AgentExportProcessingError, @@ -108,7 +107,6 @@ class SafeORJSONResponse(ORJSONResponse): ) -from letta.server.db import db_registry from letta.server.global_exception_handler import setup_global_exception_handlers # NOTE(charles): these are extra routes that are not part of v1 but we still need to mount to pass tests diff --git a/letta/server/rest_api/proxy_helpers.py b/letta/server/rest_api/proxy_helpers.py index b8627fe8..d6e2ca4a 100644 --- a/letta/server/rest_api/proxy_helpers.py +++ b/letta/server/rest_api/proxy_helpers.py @@ -4,7 +4,6 @@ Shared helper functions for Anthropic-compatible proxy endpoints. These helpers are used by both the Anthropic and Z.ai proxy routers to reduce code duplication. """ -import asyncio import json from fastapi import Request diff --git a/letta/server/rest_api/routers/v1/agents.py b/letta/server/rest_api/routers/v1/agents.py index 0fb1b695..da71a349 100644 --- a/letta/server/rest_api/routers/v1/agents.py +++ b/letta/server/rest_api/routers/v1/agents.py @@ -1,29 +1,21 @@ import asyncio import json -import traceback -from datetime import datetime, timezone +from datetime import datetime from typing import Annotated, Any, Dict, List, Literal, Optional, Union from fastapi import APIRouter, Body, Depends, File, Form, Header, HTTPException, Query, Request, UploadFile, status from fastapi.responses import JSONResponse -from marshmallow import ValidationError from orjson import orjson from pydantic import BaseModel, ConfigDict, Field, field_validator -from sqlalchemy.exc import IntegrityError, OperationalError from starlette.responses import Response, StreamingResponse from letta.agents.agent_loop import AgentLoop from letta.agents.base_agent_v2 import BaseAgentV2 from letta.agents.letta_agent import LettaAgent -from letta.agents.letta_agent_v2 import LettaAgentV2 from letta.agents.letta_agent_v3 import LettaAgentV3 from letta.constants import DEFAULT_MAX_STEPS, DEFAULT_MESSAGE_TOOL, DEFAULT_MESSAGE_TOOL_KWARG, REDIS_RUN_ID_PREFIX from letta.data_sources.redis_client import get_redis_client from letta.errors import ( - AgentExportIdMappingError, - AgentExportProcessingError, - AgentFileImportError, - AgentNotFoundForExportError, HandleNotFoundError, LLMError, NoActiveRunsToCancelError, @@ -31,16 +23,15 @@ from letta.errors import ( ) from letta.groups.sleeptime_multi_agent_v4 import SleeptimeMultiAgentV4 from letta.helpers.datetime_helpers import get_utc_time, get_utc_timestamp_ns -from letta.llm_api.llm_client import LLMClient from letta.log import get_logger from letta.orm.errors import NoResultFound from letta.otel.context import get_ctx_attributes from letta.otel.metric_registry import MetricRegistry from letta.schemas.agent import AgentRelationships, AgentState, CreateAgent, UpdateAgent from letta.schemas.agent_file import AgentFileSchema, SkillSchema -from letta.schemas.block import BaseBlock, Block, BlockResponse, BlockUpdate +from letta.schemas.block import BlockResponse, BlockUpdate from letta.schemas.enums import AgentType, MessageRole, RunStatus -from letta.schemas.file import AgentFileAttachment, FileMetadataBase, PaginatedAgentFiles +from letta.schemas.file import AgentFileAttachment, PaginatedAgentFiles from letta.schemas.group import Group from letta.schemas.job import LettaRequestConfig from letta.schemas.letta_message import LettaMessageUnion, LettaMessageUpdateUnion, MessageType @@ -59,8 +50,8 @@ from letta.schemas.memory import ( from letta.schemas.message import Message, MessageCreate, MessageCreateType, MessageSearchRequest, MessageSearchResult from letta.schemas.passage import Passage from letta.schemas.run import Run as PydanticRun, RunUpdate -from letta.schemas.source import BaseSource, Source -from letta.schemas.tool import BaseTool, Tool +from letta.schemas.source import Source +from letta.schemas.tool import Tool from letta.schemas.tool_execution_result import ToolExecutionResult from letta.schemas.usage import LettaUsageStatistics from letta.schemas.user import User diff --git a/letta/server/rest_api/routers/v1/anthropic.py b/letta/server/rest_api/routers/v1/anthropic.py index 4aa271ba..62357e75 100644 --- a/letta/server/rest_api/routers/v1/anthropic.py +++ b/letta/server/rest_api/routers/v1/anthropic.py @@ -1,5 +1,4 @@ import asyncio -import json import httpx from fastapi import APIRouter, Depends, Request diff --git a/letta/server/rest_api/routers/v1/archives.py b/letta/server/rest_api/routers/v1/archives.py index 1313bf8d..9076bcc9 100644 --- a/letta/server/rest_api/routers/v1/archives.py +++ b/letta/server/rest_api/routers/v1/archives.py @@ -1,19 +1,17 @@ -from datetime import datetime from typing import Dict, List, Literal, Optional from fastapi import APIRouter, Body, Depends, Query from pydantic import BaseModel, Field from letta import AgentState -from letta.errors import LettaInvalidArgumentError from letta.schemas.agent import AgentRelationships -from letta.schemas.archive import Archive as PydanticArchive, ArchiveBase +from letta.schemas.archive import Archive as PydanticArchive from letta.schemas.embedding_config import EmbeddingConfig from letta.schemas.passage import Passage from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server from letta.server.server import SyncServer from letta.settings import settings -from letta.validators import AgentId, ArchiveId, PassageId +from letta.validators import ArchiveId, PassageId router = APIRouter(prefix="/archives", tags=["archives"]) diff --git a/letta/server/rest_api/routers/v1/blocks.py b/letta/server/rest_api/routers/v1/blocks.py index d297ab7a..a4c5d539 100644 --- a/letta/server/rest_api/routers/v1/blocks.py +++ b/letta/server/rest_api/routers/v1/blocks.py @@ -1,10 +1,10 @@ from typing import TYPE_CHECKING, List, Literal, Optional -from fastapi import APIRouter, Body, Depends, HTTPException, Query +from fastapi import APIRouter, Body, Depends, Query from letta.orm.errors import NoResultFound from letta.schemas.agent import AgentRelationships, AgentState -from letta.schemas.block import BaseBlock, Block, BlockResponse, BlockUpdate, CreateBlock +from letta.schemas.block import Block, BlockResponse, BlockUpdate, CreateBlock from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server from letta.server.server import SyncServer from letta.utils import is_1_0_sdk_version diff --git a/letta/server/rest_api/routers/v1/conversations.py b/letta/server/rest_api/routers/v1/conversations.py index 97a224d7..c0d691ca 100644 --- a/letta/server/rest_api/routers/v1/conversations.py +++ b/letta/server/rest_api/routers/v1/conversations.py @@ -17,14 +17,13 @@ from letta.schemas.enums import RunStatus from letta.schemas.job import LettaRequestConfig from letta.schemas.letta_message import LettaMessageUnion from letta.schemas.letta_request import ConversationMessageRequest, LettaStreamingRequest, RetrieveStreamRequest -from letta.schemas.letta_response import LettaResponse, LettaStreamingResponse +from letta.schemas.letta_response import LettaResponse from letta.schemas.run import Run as PydanticRun from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server from letta.server.rest_api.redis_stream_manager import redis_sse_stream_generator from letta.server.rest_api.streaming_response import ( StreamingResponseWithStatusCode, add_keepalive_to_stream, - cancellation_aware_stream_wrapper, ) from letta.server.server import SyncServer from letta.services.conversation_manager import ConversationManager diff --git a/letta/server/rest_api/routers/v1/folders.py b/letta/server/rest_api/routers/v1/folders.py index 908004ac..7449783e 100644 --- a/letta/server/rest_api/routers/v1/folders.py +++ b/letta/server/rest_api/routers/v1/folders.py @@ -22,10 +22,10 @@ from letta.otel.tracing import trace_method from letta.schemas.agent import AgentState from letta.schemas.embedding_config import EmbeddingConfig from letta.schemas.enums import DuplicateFileHandling, FileProcessingStatus -from letta.schemas.file import FileMetadata, FileMetadataBase -from letta.schemas.folder import BaseFolder, Folder +from letta.schemas.file import FileMetadata +from letta.schemas.folder import Folder from letta.schemas.passage import Passage -from letta.schemas.source import BaseSource, Source, SourceCreate, SourceUpdate +from letta.schemas.source import Source, SourceCreate, SourceUpdate from letta.schemas.source_metadata import OrganizationSourcesStats from letta.schemas.user import User from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server diff --git a/letta/server/rest_api/routers/v1/groups.py b/letta/server/rest_api/routers/v1/groups.py index 9117e4e5..29edd0b6 100644 --- a/letta/server/rest_api/routers/v1/groups.py +++ b/letta/server/rest_api/routers/v1/groups.py @@ -5,10 +5,8 @@ from fastapi.responses import JSONResponse from pydantic import Field from letta.constants import DEFAULT_MESSAGE_TOOL, DEFAULT_MESSAGE_TOOL_KWARG -from letta.schemas.group import Group, GroupBase, GroupCreate, GroupUpdate, ManagerType +from letta.schemas.group import Group, GroupCreate, GroupUpdate, ManagerType from letta.schemas.letta_message import LettaMessageUnion, LettaMessageUpdateUnion -from letta.schemas.letta_response import LettaResponse -from letta.schemas.message import BaseMessage from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server from letta.server.server import SyncServer from letta.validators import GroupId, MessageId diff --git a/letta/server/rest_api/routers/v1/identities.py b/letta/server/rest_api/routers/v1/identities.py index 2fca5a7a..d91a8b06 100644 --- a/letta/server/rest_api/routers/v1/identities.py +++ b/letta/server/rest_api/routers/v1/identities.py @@ -1,10 +1,10 @@ -from typing import TYPE_CHECKING, List, Literal, Optional, Union +from typing import TYPE_CHECKING, List, Literal, Optional from fastapi import APIRouter, Body, Depends, Header, Query -from letta.orm.errors import NoResultFound, UniqueConstraintViolationError +from letta.orm.errors import NoResultFound from letta.schemas.agent import AgentRelationships, AgentState -from letta.schemas.block import Block, BlockResponse +from letta.schemas.block import BlockResponse from letta.schemas.identity import ( Identity, IdentityCreate, diff --git a/letta/server/rest_api/routers/v1/internal_runs.py b/letta/server/rest_api/routers/v1/internal_runs.py index 75c2efb6..d9cba0b9 100644 --- a/letta/server/rest_api/routers/v1/internal_runs.py +++ b/letta/server/rest_api/routers/v1/internal_runs.py @@ -8,7 +8,6 @@ from letta.schemas.letta_stop_reason import StopReasonType from letta.schemas.run import Run from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server from letta.server.server import SyncServer -from letta.services.run_manager import RunManager router = APIRouter(prefix="/_internal_runs", tags=["_internal_runs"]) diff --git a/letta/server/rest_api/routers/v1/jobs.py b/letta/server/rest_api/routers/v1/jobs.py index be8b57e9..c6603458 100644 --- a/letta/server/rest_api/routers/v1/jobs.py +++ b/letta/server/rest_api/routers/v1/jobs.py @@ -4,7 +4,7 @@ from fastapi import APIRouter, Depends, Query from letta.errors import LettaInvalidArgumentError from letta.schemas.enums import JobStatus -from letta.schemas.job import Job, JobBase +from letta.schemas.job import Job from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server from letta.server.server import SyncServer from letta.settings import settings diff --git a/letta/server/rest_api/routers/v1/mcp_servers.py b/letta/server/rest_api/routers/v1/mcp_servers.py index 0b037777..537660de 100644 --- a/letta/server/rest_api/routers/v1/mcp_servers.py +++ b/letta/server/rest_api/routers/v1/mcp_servers.py @@ -1,13 +1,12 @@ -from typing import Any, AsyncGenerator, Dict, List, Optional, Union +from typing import AsyncGenerator, List, Optional, Union -from fastapi import APIRouter, Body, Depends, HTTPException, Request +from fastapi import APIRouter, Body, Depends, Request from httpx import HTTPStatusError from starlette.responses import StreamingResponse from letta.errors import LettaMCPConnectionError from letta.functions.mcp_client.types import SSEServerConfig, StdioServerConfig, StreamableHTTPServerConfig from letta.log import get_logger -from letta.schemas.letta_message import ToolReturnMessage from letta.schemas.mcp_server import ( CreateMCPServerRequest, MCPServerUnion, @@ -28,7 +27,6 @@ from letta.server.server import SyncServer from letta.services.mcp.oauth_utils import drill_down_exception, oauth_stream_event from letta.services.mcp.stdio_client import AsyncStdioMCPClient from letta.services.mcp.types import OauthStreamEvent -from letta.settings import tool_settings router = APIRouter(prefix="/mcp-servers", tags=["mcp-servers"]) diff --git a/letta/server/rest_api/routers/v1/messages.py b/letta/server/rest_api/routers/v1/messages.py index e695d292..7dfcda59 100644 --- a/letta/server/rest_api/routers/v1/messages.py +++ b/letta/server/rest_api/routers/v1/messages.py @@ -11,7 +11,7 @@ from letta.schemas.job import BatchJob, JobStatus, JobType, JobUpdate from letta.schemas.letta_message import LettaMessageSearchResult, LettaMessageUnion from letta.schemas.letta_request import CreateBatch from letta.schemas.letta_response import LettaBatchMessages -from letta.schemas.message import Message, MessageSearchRequest, MessageSearchResult, SearchAllMessagesRequest +from letta.schemas.message import Message, SearchAllMessagesRequest from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server from letta.server.server import SyncServer from letta.settings import settings diff --git a/letta/server/rest_api/routers/v1/providers.py b/letta/server/rest_api/routers/v1/providers.py index 5c0ae926..d20512a3 100644 --- a/letta/server/rest_api/routers/v1/providers.py +++ b/letta/server/rest_api/routers/v1/providers.py @@ -4,7 +4,7 @@ from fastapi import APIRouter, Body, Depends, HTTPException, Query, status from fastapi.responses import JSONResponse from letta.schemas.enums import ProviderCategory, ProviderType -from letta.schemas.providers import Provider, ProviderBase, ProviderCheck, ProviderCreate, ProviderUpdate +from letta.schemas.providers import Provider, ProviderCheck, ProviderCreate, ProviderUpdate from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server from letta.validators import ProviderId diff --git a/letta/server/rest_api/routers/v1/runs.py b/letta/server/rest_api/routers/v1/runs.py index b4c3973d..c454f7d9 100644 --- a/letta/server/rest_api/routers/v1/runs.py +++ b/letta/server/rest_api/routers/v1/runs.py @@ -20,7 +20,6 @@ from letta.server.rest_api.redis_stream_manager import redis_sse_stream_generato from letta.server.rest_api.streaming_response import ( StreamingResponseWithStatusCode, add_keepalive_to_stream, - cancellation_aware_stream_wrapper, ) from letta.server.server import SyncServer from letta.services.clickhouse_otel_traces import ClickhouseOtelTracesReader diff --git a/letta/server/rest_api/routers/v1/sandbox_configs.py b/letta/server/rest_api/routers/v1/sandbox_configs.py index 5e51fa33..d59181c0 100644 --- a/letta/server/rest_api/routers/v1/sandbox_configs.py +++ b/letta/server/rest_api/routers/v1/sandbox_configs.py @@ -15,7 +15,6 @@ from letta.schemas.environment_variables import ( from letta.schemas.sandbox_config import ( LocalSandboxConfig, SandboxConfig as PydanticSandboxConfig, - SandboxConfigBase, SandboxConfigCreate, SandboxConfigUpdate, ) diff --git a/letta/server/rest_api/routers/v1/sources.py b/letta/server/rest_api/routers/v1/sources.py index d5a38a9c..edacc64b 100644 --- a/letta/server/rest_api/routers/v1/sources.py +++ b/letta/server/rest_api/routers/v1/sources.py @@ -5,8 +5,7 @@ import tempfile from pathlib import Path from typing import List, Optional -from fastapi import APIRouter, Depends, HTTPException, Query, UploadFile -from starlette import status +from fastapi import APIRouter, Depends, Query, UploadFile from starlette.responses import Response import letta.constants as constants @@ -22,9 +21,9 @@ from letta.otel.tracing import trace_method from letta.schemas.agent import AgentState from letta.schemas.embedding_config import EmbeddingConfig from letta.schemas.enums import DuplicateFileHandling, FileProcessingStatus -from letta.schemas.file import FileMetadata, FileMetadataBase +from letta.schemas.file import FileMetadata from letta.schemas.passage import Passage -from letta.schemas.source import BaseSource, Source, SourceCreate, SourceUpdate +from letta.schemas.source import Source, SourceCreate, SourceUpdate from letta.schemas.source_metadata import OrganizationSourcesStats from letta.schemas.user import User from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server diff --git a/letta/server/rest_api/routers/v1/steps.py b/letta/server/rest_api/routers/v1/steps.py index 0b28a949..d34f1de0 100644 --- a/letta/server/rest_api/routers/v1/steps.py +++ b/letta/server/rest_api/routers/v1/steps.py @@ -7,7 +7,7 @@ from pydantic import BaseModel, Field from letta.schemas.letta_message import LettaMessageUnion from letta.schemas.message import Message from letta.schemas.provider_trace import ProviderTrace -from letta.schemas.step import Step, StepBase +from letta.schemas.step import Step from letta.schemas.step_metrics import StepMetrics from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server from letta.server.server import SyncServer diff --git a/letta/server/rest_api/routers/v1/tools.py b/letta/server/rest_api/routers/v1/tools.py index 0960ca46..f7ba2c81 100644 --- a/letta/server/rest_api/routers/v1/tools.py +++ b/letta/server/rest_api/routers/v1/tools.py @@ -1,22 +1,20 @@ +import asyncio import json +import traceback from collections.abc import AsyncGenerator from typing import Any, Dict, List, Literal, Optional, Union from fastapi import APIRouter, Body, Depends, HTTPException, Query, Request -from fastmcp.exceptions import ToolError as FastMCPToolError from httpx import ConnectError, HTTPStatusError from mcp.shared.exceptions import McpError from pydantic import BaseModel, Field from starlette.responses import StreamingResponse -from letta.constants import DEFAULT_GENERATE_TOOL_MODEL_HANDLE, MAX_TOOL_NAME_LENGTH +from letta.constants import DEFAULT_GENERATE_TOOL_MODEL_HANDLE from letta.errors import ( LettaInvalidArgumentError, - LettaInvalidMCPSchemaError, LettaMCPConnectionError, LettaMCPTimeoutError, - LettaToolCreateError, - LettaToolNameConflictError, LLMError, ) from letta.functions.functions import derive_openai_json_schema @@ -25,7 +23,6 @@ from letta.functions.mcp_client.types import MCPTool, SSEServerConfig, StdioServ from letta.helpers.decorators import deprecated from letta.llm_api.llm_client import LLMClient from letta.log import get_logger -from letta.orm.errors import UniqueConstraintViolationError from letta.orm.mcp_oauth import OAuthSessionStatus from letta.prompts.gpt_system import get_system_text from letta.schemas.enums import AgentType, LLMCallType, MessageRole, ToolType @@ -34,16 +31,14 @@ from letta.schemas.letta_message_content import TextContent from letta.schemas.mcp import UpdateSSEMCPServer, UpdateStdioMCPServer, UpdateStreamableHTTPMCPServer from letta.schemas.message import Message from letta.schemas.pip_requirement import PipRequirement -from letta.schemas.tool import BaseTool, Tool, ToolCreate, ToolRunFromSource, ToolSearchRequest, ToolSearchResult, ToolUpdate +from letta.schemas.tool import Tool, ToolCreate, ToolRunFromSource, ToolSearchRequest, ToolSearchResult, ToolUpdate from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server from letta.server.rest_api.streaming_response import StreamingResponseWithStatusCode from letta.server.server import SyncServer from letta.services.mcp.oauth_utils import MCPOAuthSession, drill_down_exception, oauth_stream_event from letta.services.mcp.stdio_client import AsyncStdioMCPClient from letta.services.mcp.types import OauthStreamEvent -from letta.services.summarizer.summarizer import traceback from letta.settings import tool_settings -from letta.utils import asyncio from letta.validators import ToolId router = APIRouter(prefix="/tools", tags=["tools"]) diff --git a/letta/server/rest_api/routers/v1/zai.py b/letta/server/rest_api/routers/v1/zai.py index 9a674b8b..b8035cee 100644 --- a/letta/server/rest_api/routers/v1/zai.py +++ b/letta/server/rest_api/routers/v1/zai.py @@ -1,5 +1,4 @@ import asyncio -import json import httpx from fastapi import APIRouter, Depends, Request diff --git a/letta/server/rest_api/utils.py b/letta/server/rest_api/utils.py index 66e15572..dee13a01 100644 --- a/letta/server/rest_api/utils.py +++ b/letta/server/rest_api/utils.py @@ -5,7 +5,7 @@ import uuid from enum import Enum from typing import Any, AsyncGenerator, Dict, Iterable, List, Optional, Union, cast -from fastapi import Header, HTTPException +from fastapi import HTTPException from openai.types.chat import ChatCompletionMessageParam from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction from openai.types.chat.completion_create_params import CompletionCreateParams diff --git a/letta/server/server.py b/letta/server/server.py index f17c04d1..175c8819 100644 --- a/letta/server/server.py +++ b/letta/server/server.py @@ -2,19 +2,15 @@ import asyncio import json import os import traceback -from abc import abstractmethod from datetime import datetime from pathlib import Path from typing import Any, Callable, Dict, List, Optional, Tuple, Union import httpx from anthropic import AsyncAnthropic -from fastapi import HTTPException -from fastapi.responses import StreamingResponse import letta.constants as constants import letta.server.utils as server_utils -import letta.system as system from letta.config import LettaConfig from letta.constants import LETTA_TOOL_EXECUTION_DIR from letta.data_sources.connectors import DataConnector, load_data @@ -22,17 +18,13 @@ from letta.errors import ( HandleNotFoundError, LettaInvalidArgumentError, LettaMCPConnectionError, - LettaMCPTimeoutError, ) from letta.functions.mcp_client.types import MCPServerType, MCPTool, MCPToolHealth, SSEServerConfig, StdioServerConfig from letta.functions.schema_validator import validate_complete_json_schema -from letta.groups.helpers import load_multi_agent from letta.helpers.datetime_helpers import get_utc_time -from letta.helpers.json_helpers import json_dumps, json_loads # TODO use custom interface from letta.interface import ( - AgentInterface, # abstract CLIInterface, # for printing to terminal ) from letta.log import get_logger @@ -44,17 +36,13 @@ from letta.schemas.block import Block, BlockUpdate, CreateBlock from letta.schemas.embedding_config import EmbeddingConfig # openai schemas -from letta.schemas.enums import AgentType, JobStatus, MessageStreamStatus, ProviderCategory, ProviderType, SandboxType, ToolSourceType -from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate -from letta.schemas.group import GroupCreate, ManagerType, SleeptimeManager, VoiceSleeptimeManager +from letta.schemas.enums import AgentType, JobStatus, ProviderCategory, ProviderType, ToolSourceType +from letta.schemas.group import GroupCreate, SleeptimeManager, VoiceSleeptimeManager from letta.schemas.job import Job, JobUpdate -from letta.schemas.letta_message import LegacyLettaMessage, LettaMessage, MessageType, ToolReturnMessage -from letta.schemas.letta_message_content import TextContent -from letta.schemas.letta_response import LettaResponse -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType +from letta.schemas.letta_message import LettaMessage, ToolReturnMessage from letta.schemas.llm_config import LLMConfig -from letta.schemas.memory import ArchivalMemorySummary, Memory, RecallMemorySummary -from letta.schemas.message import Message, MessageCreate, MessageUpdate +from letta.schemas.memory import Memory +from letta.schemas.message import Message from letta.schemas.passage import Passage from letta.schemas.pip_requirement import PipRequirement from letta.schemas.providers import ( @@ -82,11 +70,7 @@ from letta.schemas.sandbox_config import LocalSandboxConfig, SandboxConfigCreate from letta.schemas.secret import Secret from letta.schemas.source import Source from letta.schemas.tool import Tool -from letta.schemas.usage import LettaUsageStatistics from letta.schemas.user import User -from letta.server.rest_api.chat_completions_interface import ChatCompletionsStreamingInterface -from letta.server.rest_api.interface import StreamingServerInterface -from letta.server.rest_api.utils import sse_async_generator from letta.services.agent_manager import AgentManager from letta.services.agent_serialization_manager import AgentSerializationManager from letta.services.archive_manager import ArchiveManager @@ -121,7 +105,7 @@ from letta.services.tool_manager import ToolManager from letta.services.user_manager import UserManager from letta.settings import DatabaseChoice, model_settings, settings, tool_settings from letta.streaming_interface import AgentChunkStreamingInterface -from letta.utils import get_friendly_error_msg, get_persona_text, safe_create_task +from letta.utils import get_friendly_error_msg, get_persona_text config = LettaConfig.load() logger = get_logger(__name__) @@ -1338,10 +1322,7 @@ class SyncServer(object): # ChatGPT OAuth uses a hardcoded model list. If that list changes, # backfill already-synced providers that are missing new handles. - if ( - provider.provider_type == ProviderType.chatgpt_oauth - and not should_sync_models - ): + if provider.provider_type == ProviderType.chatgpt_oauth and not should_sync_models: expected_models = await typed_provider.list_llm_models_async() expected_handles = {model.handle for model in expected_models} provider_llm_models = await self.provider_manager.list_models_async( @@ -1350,12 +1331,8 @@ class SyncServer(object): provider_id=provider.id, enabled=True, ) - existing_handles = { - model.handle for model in provider_llm_models - } - should_sync_models = not expected_handles.issubset( - existing_handles - ) + existing_handles = {model.handle for model in provider_llm_models} + should_sync_models = not expected_handles.issubset(existing_handles) if should_sync_models: models = await typed_provider.list_llm_models_async() @@ -1606,7 +1583,7 @@ class SyncServer(object): ) -> ToolReturnMessage: """Run a tool from source code""" - from letta.services.tool_schema_generator import generate_schema_for_tool_creation, generate_schema_for_tool_update + from letta.services.tool_schema_generator import generate_schema_for_tool_creation if tool_source_type not in (None, ToolSourceType.python, ToolSourceType.typescript): raise LettaInvalidArgumentError( diff --git a/letta/server/ws_api/example_client.py b/letta/server/ws_api/example_client.py index a7fc57b5..447600d3 100644 --- a/letta/server/ws_api/example_client.py +++ b/letta/server/ws_api/example_client.py @@ -3,6 +3,7 @@ import asyncio import websockets import letta.server.ws_api.protocol as protocol +from letta.helpers.json_helpers import json_dumps, json_loads from letta.server.constants import WS_CLIENT_TIMEOUT, WS_DEFAULT_PORT from letta.server.utils import condition_to_stop_receiving, print_server_response diff --git a/letta/server/ws_api/server.py b/letta/server/ws_api/server.py index 85edf515..67cadb0a 100644 --- a/letta/server/ws_api/server.py +++ b/letta/server/ws_api/server.py @@ -1,11 +1,11 @@ import asyncio import signal import sys -import traceback import websockets import letta.server.ws_api.protocol as protocol +from letta.helpers.json_helpers import json_loads from letta.log import get_logger from letta.server.constants import WS_DEFAULT_PORT from letta.server.server import SyncServer diff --git a/letta/services/agent_generate_completion_manager.py b/letta/services/agent_generate_completion_manager.py index 9f5fd1f4..c01d6936 100644 --- a/letta/services/agent_generate_completion_manager.py +++ b/letta/services/agent_generate_completion_manager.py @@ -1,12 +1,10 @@ """Manager for handling direct LLM completions using agent configuration.""" -import json from typing import TYPE_CHECKING, Any, Dict, Optional -from letta.errors import HandleNotFoundError, LLMError +from letta.errors import LLMError from letta.llm_api.llm_client import LLMClient from letta.log import get_logger -from letta.orm.errors import NoResultFound from letta.schemas.enums import AgentType, MessageRole from letta.schemas.letta_message_content import TextContent from letta.schemas.message import Message diff --git a/letta/services/agent_manager.py b/letta/services/agent_manager.py index 3c68df3c..5e4d2366 100644 --- a/letta/services/agent_manager.py +++ b/letta/services/agent_manager.py @@ -19,16 +19,13 @@ from letta.constants import ( DEFAULT_CORE_MEMORY_SOURCE_CHAR_LIMIT, DEFAULT_MAX_FILES_OPEN, DEFAULT_TIMEZONE, - DEPRECATED_LETTA_TOOLS, EXCLUDE_MODEL_KEYWORDS_FROM_BASE_TOOL_RULES, FILES_TOOLS, INCLUDE_MODEL_KEYWORDS_BASE_TOOL_RULES, RETRIEVAL_QUERY_DEFAULT_PAGE_SIZE, ) -from letta.errors import LettaAgentNotFoundError, LettaInvalidArgumentError from letta.helpers import ToolRulesSolver from letta.helpers.datetime_helpers import get_utc_time -from letta.llm_api.llm_client import LLMClient from letta.log import get_logger from letta.orm import ( Agent as AgentModel, @@ -47,12 +44,11 @@ from letta.orm import ( ToolsAgents, ) from letta.orm.errors import NoResultFound -from letta.orm.sandbox_config import AgentEnvironmentVariable, AgentEnvironmentVariable as AgentEnvironmentVariableModel +from letta.orm.sandbox_config import AgentEnvironmentVariable from letta.orm.sqlalchemy_base import AccessType from letta.otel.tracing import trace_method from letta.prompts.prompt_generator import PromptGenerator from letta.schemas.agent import ( - AgentRelationships, AgentState as PydanticAgentState, CreateAgent, InternalTemplateAgentCreate, @@ -60,7 +56,7 @@ from letta.schemas.agent import ( ) from letta.schemas.block import DEFAULT_BLOCKS, Block as PydanticBlock, BlockUpdate from letta.schemas.embedding_config import EmbeddingConfig -from letta.schemas.enums import AgentType, PrimitiveType, ProviderType, TagMatchMode, ToolType, VectorDBProvider +from letta.schemas.enums import AgentType, PrimitiveType, TagMatchMode, ToolType, VectorDBProvider from letta.schemas.environment_variables import AgentEnvironmentVariable as PydanticAgentEnvVar from letta.schemas.file import FileMetadata as PydanticFileMetadata from letta.schemas.group import Group as PydanticGroup, ManagerType @@ -74,10 +70,6 @@ from letta.schemas.source import Source as PydanticSource from letta.schemas.tool import Tool as PydanticTool from letta.schemas.tool_rule import ContinueToolRule, RequiresApprovalToolRule, TerminalToolRule from letta.schemas.user import User as PydanticUser -from letta.serialize_schemas import MarshmallowAgentSchema -from letta.serialize_schemas.marshmallow_message import SerializedMessageSchema -from letta.serialize_schemas.marshmallow_tool import SerializedToolSchema -from letta.serialize_schemas.pydantic_agent_schema import AgentSchema from letta.server.db import db_registry from letta.services.archive_manager import ArchiveManager from letta.services.block_manager import BlockManager, validate_block_limit_constraint @@ -89,11 +81,9 @@ from letta.services.files_agents_manager import FileAgentManager from letta.services.helpers.agent_manager_helper import ( _apply_filters, _apply_identity_filters, - _apply_pagination, _apply_pagination_async, _apply_relationship_filters, _apply_tag_filter, - _process_relationship, _process_relationship_async, build_agent_passage_query, build_passage_query, @@ -113,7 +103,7 @@ from letta.services.message_manager import MessageManager from letta.services.passage_manager import PassageManager from letta.services.source_manager import SourceManager from letta.services.tool_manager import ToolManager -from letta.settings import DatabaseChoice, model_settings, settings +from letta.settings import DatabaseChoice, settings from letta.utils import ( bounded_gather, calculate_file_defaults_based_on_context_window, @@ -2216,7 +2206,6 @@ class AgentManager: Lists all passages attached to an agent (combines both source and agent passages). """ - import warnings logger.warning( "list_passages_async is deprecated. Use query_source_passages_async or query_agent_passages_async instead.", diff --git a/letta/services/agent_serialization_manager.py b/letta/services/agent_serialization_manager.py index 718be37f..a9996b76 100644 --- a/letta/services/agent_serialization_manager.py +++ b/letta/services/agent_serialization_manager.py @@ -1,4 +1,3 @@ -import asyncio import uuid from datetime import datetime, timezone from typing import Any, Dict, List, Optional @@ -31,7 +30,7 @@ from letta.schemas.agent_file import ( ) from letta.schemas.block import Block from letta.schemas.embedding_config import EmbeddingConfig -from letta.schemas.enums import FileProcessingStatus, VectorDBProvider +from letta.schemas.enums import FileProcessingStatus from letta.schemas.file import FileMetadata from letta.schemas.group import Group, GroupCreate from letta.schemas.llm_config import LLMConfig @@ -190,9 +189,9 @@ class AgentSerializationManager: return sources, files async def _convert_agent_state_to_schema( - self, - agent_state: AgentState, - actor: User, + self, + agent_state: AgentState, + actor: User, files_agents_cache: dict = None, scrub_messages: bool = False, ) -> AgentSchema: @@ -460,8 +459,8 @@ class AgentSerializationManager: # Convert to schemas with ID remapping (reusing cached file-agent data) agent_schemas = [ await self._convert_agent_state_to_schema( - agent_state, - actor=actor, + agent_state, + actor=actor, files_agents_cache=files_agents_cache, scrub_messages=scrub_messages, ) diff --git a/letta/services/archive_manager.py b/letta/services/archive_manager.py index 28c3322a..f1a26159 100644 --- a/letta/services/archive_manager.py +++ b/letta/services/archive_manager.py @@ -1,4 +1,3 @@ -import asyncio from datetime import datetime from typing import Dict, List, Optional diff --git a/letta/services/block_manager.py b/letta/services/block_manager.py index 561657c0..ca525efa 100644 --- a/letta/services/block_manager.py +++ b/letta/services/block_manager.py @@ -1,4 +1,3 @@ -import asyncio from datetime import datetime from typing import Dict, List, Optional diff --git a/letta/services/block_manager_git.py b/letta/services/block_manager_git.py index 1fc4424a..fa22418f 100644 --- a/letta/services/block_manager_git.py +++ b/letta/services/block_manager_git.py @@ -7,7 +7,6 @@ When an agent has the GIT_MEMORY_ENABLED_TAG tag, block operations: This provides full version history while maintaining fast reads from PostgreSQL. """ -import json import time from typing import List, Optional diff --git a/letta/services/context_window_calculator/token_counter.py b/letta/services/context_window_calculator/token_counter.py index 775ea804..7cbbd6a9 100644 --- a/letta/services/context_window_calculator/token_counter.py +++ b/letta/services/context_window_calculator/token_counter.py @@ -13,7 +13,6 @@ from letta.schemas.message import Message from letta.schemas.openai.chat_completion_request import Tool as OpenAITool if TYPE_CHECKING: - from letta.schemas.llm_config import LLMConfig from letta.schemas.user import User logger = get_logger(__name__) @@ -279,7 +278,7 @@ def create_token_counter( The appropriate TokenCounter instance """ from letta.llm_api.llm_client import LLMClient - from letta.settings import model_settings, settings + from letta.settings import settings # Use Gemini token counter for Google Vertex and Google AI use_gemini = model_endpoint_type in ("google_vertex", "google_ai") diff --git a/letta/services/file_manager.py b/letta/services/file_manager.py index ee3db939..9b1fc0ab 100644 --- a/letta/services/file_manager.py +++ b/letta/services/file_manager.py @@ -1,4 +1,3 @@ -import asyncio import os from datetime import datetime, timedelta, timezone from typing import List, Optional diff --git a/letta/services/identity_manager.py b/letta/services/identity_manager.py index fc416094..5c510408 100644 --- a/letta/services/identity_manager.py +++ b/letta/services/identity_manager.py @@ -1,4 +1,3 @@ -import asyncio from typing import List, Optional from fastapi import HTTPException diff --git a/letta/services/job_manager.py b/letta/services/job_manager.py index 0b39ece1..eaabce32 100644 --- a/letta/services/job_manager.py +++ b/letta/services/job_manager.py @@ -1,5 +1,4 @@ -from functools import partial, reduce -from operator import add +from functools import partial from typing import List, Literal, Optional, Union from httpx import AsyncClient, post @@ -10,9 +9,8 @@ from letta.helpers.datetime_helpers import get_utc_time from letta.log import get_logger from letta.orm.errors import NoResultFound from letta.orm.job import Job as JobModel -from letta.orm.message import Message as MessageModel from letta.orm.sqlalchemy_base import AccessType -from letta.orm.step import Step, Step as StepModel +from letta.orm.step import Step as StepModel from letta.otel.tracing import log_event, trace_method from letta.schemas.enums import JobStatus, JobType, MessageRole, PrimitiveType from letta.schemas.job import BatchJob as PydanticBatchJob, Job as PydanticJob, JobUpdate, LettaRequestConfig @@ -21,7 +19,6 @@ from letta.schemas.letta_stop_reason import StopReasonType from letta.schemas.message import Message as PydanticMessage from letta.schemas.run import Run as PydanticRun from letta.schemas.step import Step as PydanticStep -from letta.schemas.usage import LettaUsageStatistics from letta.schemas.user import User as PydanticUser from letta.server.db import db_registry from letta.services.helpers.agent_manager_helper import validate_agent_exists_async diff --git a/letta/services/mcp_manager.py b/letta/services/mcp_manager.py index 0f2ad4a8..32b91ca8 100644 --- a/letta/services/mcp_manager.py +++ b/letta/services/mcp_manager.py @@ -7,7 +7,7 @@ from datetime import datetime, timedelta from typing import Any, Dict, List, Optional, Tuple, Union from fastapi import HTTPException -from sqlalchemy import delete, desc, null, select +from sqlalchemy import delete, desc, select from starlette.requests import Request import letta.constants as constants @@ -48,7 +48,7 @@ from letta.services.mcp.server_side_oauth import ServerSideOAuth from letta.services.mcp.sse_client import MCP_CONFIG_TOPLEVEL_KEY from letta.services.mcp.stdio_client import AsyncStdioMCPClient from letta.services.tool_manager import ToolManager -from letta.settings import settings, tool_settings +from letta.settings import tool_settings from letta.utils import enforce_types, printd, safe_create_task_with_return from letta.validators import raise_on_invalid_id @@ -483,7 +483,6 @@ class MCPManager: 2. Attempts to connect and fetch tools 3. Persists valid tools in parallel (best-effort) """ - import asyncio # First, create the MCP server created_server = await self.create_mcp_server(pydantic_mcp_server, actor) diff --git a/letta/services/mcp_server_manager.py b/letta/services/mcp_server_manager.py index f1981a03..462da216 100644 --- a/letta/services/mcp_server_manager.py +++ b/letta/services/mcp_server_manager.py @@ -46,7 +46,7 @@ from letta.services.mcp.server_side_oauth import ServerSideOAuth from letta.services.mcp.sse_client import MCP_CONFIG_TOPLEVEL_KEY from letta.services.mcp.stdio_client import AsyncStdioMCPClient from letta.services.tool_manager import ToolManager -from letta.settings import settings, tool_settings +from letta.settings import tool_settings from letta.utils import enforce_types, printd, safe_create_task logger = get_logger(__name__) @@ -607,7 +607,6 @@ class MCPServerManager: 2. Attempts to connect and fetch tools 3. Persists valid tools in parallel (best-effort) """ - import asyncio # First, create the MCP server created_server = await self.create_mcp_server(pydantic_mcp_server, actor) diff --git a/letta/services/memory_repo/git_operations.py b/letta/services/memory_repo/git_operations.py index 75ce5946..a3102200 100644 --- a/letta/services/memory_repo/git_operations.py +++ b/letta/services/memory_repo/git_operations.py @@ -8,14 +8,13 @@ stored in object storage (GCS/S3). """ import asyncio -import io import os import shutil import tempfile import time import uuid from datetime import datetime, timezone -from typing import Dict, List, Optional, Tuple +from typing import Dict, List, Optional from letta.data_sources.redis_client import get_redis_client from letta.log import get_logger diff --git a/letta/services/memory_repo/memfs_client_base.py b/letta/services/memory_repo/memfs_client_base.py index 08f61da6..6a7a9e32 100644 --- a/letta/services/memory_repo/memfs_client_base.py +++ b/letta/services/memory_repo/memfs_client_base.py @@ -11,7 +11,7 @@ HTTP service instead. import hashlib import os import uuid -from typing import Dict, List, Optional +from typing import List, Optional from letta.constants import CORE_MEMORY_BLOCK_CHAR_LIMIT from letta.log import get_logger diff --git a/letta/services/memory_repo/storage/base.py b/letta/services/memory_repo/storage/base.py index 2fd97dc6..1f7e4150 100644 --- a/letta/services/memory_repo/storage/base.py +++ b/letta/services/memory_repo/storage/base.py @@ -1,7 +1,7 @@ """Abstract base class for storage backends.""" from abc import ABC, abstractmethod -from typing import AsyncIterator, List, Optional +from typing import List class StorageBackend(ABC): diff --git a/letta/services/message_manager.py b/letta/services/message_manager.py index 1da5ea8a..e4a6ca0c 100644 --- a/letta/services/message_manager.py +++ b/letta/services/message_manager.py @@ -13,7 +13,7 @@ from letta.orm.message import Message as MessageModel from letta.otel.tracing import trace_method from letta.schemas.enums import MessageRole, PrimitiveType from letta.schemas.letta_message import LettaMessageUpdateUnion -from letta.schemas.letta_message_content import ImageSourceType, LettaImage, MessageContentType, TextContent +from letta.schemas.letta_message_content import ImageSourceType, LettaImage, MessageContentType from letta.schemas.message import Message as PydanticMessage, MessageSearchResult, MessageUpdate from letta.schemas.user import User as PydanticUser from letta.server.db import db_registry diff --git a/letta/services/passage_manager.py b/letta/services/passage_manager.py index 0a69e70e..55ba29f3 100644 --- a/letta/services/passage_manager.py +++ b/letta/services/passage_manager.py @@ -23,7 +23,6 @@ from letta.schemas.passage import Passage as PydanticPassage from letta.schemas.user import User as PydanticUser from letta.server.db import db_registry from letta.services.archive_manager import ArchiveManager -from letta.settings import settings from letta.utils import enforce_types logger = get_logger(__name__) @@ -351,7 +350,7 @@ class PassageManager: return passage.to_pydantic() @trace_method - def _preprocess_passage_for_creation(self, pydantic_passage: PydanticPassage) -> "SqlAlchemyBase": + def _preprocess_passage_for_creation(self, pydantic_passage: PydanticPassage) -> "SqlAlchemyBase": # noqa: F821 data = pydantic_passage.model_dump(to_orm=True) common_fields = { "id": data.get("id"), @@ -496,7 +495,6 @@ class PassageManager: @trace_method def create_many_passages(self, passages: List[PydanticPassage], actor: PydanticUser) -> List[PydanticPassage]: """DEPRECATED: Use create_many_agent_passages() or create_many_source_passages() instead.""" - import warnings logger.warning( "create_many_passages is deprecated. Use create_many_agent_passages() or create_many_source_passages() instead.", @@ -508,7 +506,6 @@ class PassageManager: @trace_method async def create_many_passages_async(self, passages: List[PydanticPassage], actor: PydanticUser) -> List[PydanticPassage]: """DEPRECATED: Use create_many_agent_passages_async() or create_many_source_passages_async() instead.""" - import warnings logger.warning( "create_many_passages_async is deprecated. Use create_many_agent_passages_async() or create_many_source_passages_async() instead.", @@ -814,7 +811,6 @@ class PassageManager: @trace_method async def delete_passage_by_id_async(self, passage_id: str, actor: PydanticUser) -> bool: """DEPRECATED: Use delete_agent_passage_by_id_async() or delete_source_passage_by_id_async() instead.""" - import warnings logger.warning( "delete_passage_by_id_async is deprecated. Use delete_agent_passage_by_id_async() or delete_source_passage_by_id_async() instead.", @@ -927,7 +923,6 @@ class PassageManager: passages: List[PydanticPassage], ) -> bool: """DEPRECATED: Use delete_agent_passages() or delete_source_passages() instead.""" - import warnings logger.warning( "delete_passages is deprecated. Use delete_agent_passages() or delete_source_passages() instead.", @@ -948,7 +943,6 @@ class PassageManager: agent_id: Optional[str] = None, ) -> int: """DEPRECATED: Use agent_passage_size() instead (this only counted agent passages anyway).""" - import warnings logger.warning("size is deprecated. Use agent_passage_size() instead.", stacklevel=2) return self.agent_passage_size(actor=actor, agent_id=agent_id) diff --git a/letta/services/provider_manager.py b/letta/services/provider_manager.py index c8a3a1ae..453fcf1a 100644 --- a/letta/services/provider_manager.py +++ b/letta/services/provider_manager.py @@ -3,7 +3,6 @@ from typing import List, Optional, Tuple, Union from sqlalchemy import and_, select from letta.log import get_logger -from letta.orm.errors import UniqueConstraintViolationError from letta.orm.provider import Provider as ProviderModel from letta.orm.provider_model import ProviderModel as ProviderModelORM from letta.otel.tracing import trace_method diff --git a/letta/services/provider_trace_backends/clickhouse.py b/letta/services/provider_trace_backends/clickhouse.py index 77c8cf80..3ba84772 100644 --- a/letta/services/provider_trace_backends/clickhouse.py +++ b/letta/services/provider_trace_backends/clickhouse.py @@ -42,7 +42,6 @@ class ClickhouseProviderTraceBackend(ProviderTraceBackendClient): ) try: - from letta.schemas.llm_trace import LLMTrace from letta.services.llm_trace_writer import get_llm_trace_writer trace = self._convert_to_trace(actor, provider_trace) diff --git a/letta/services/run_manager.py b/letta/services/run_manager.py index c841a62d..26650b71 100644 --- a/letta/services/run_manager.py +++ b/letta/services/run_manager.py @@ -1,26 +1,21 @@ from datetime import datetime -from multiprocessing import Value -from pickletools import pyunicode from typing import List, Literal, Optional from httpx import AsyncClient from letta.data_sources.redis_client import get_redis_client -from letta.errors import LettaInvalidArgumentError from letta.helpers.datetime_helpers import get_utc_time from letta.log import get_logger from letta.log_context import update_log_context from letta.orm.agent import Agent as AgentModel from letta.orm.errors import NoResultFound -from letta.orm.message import Message as MessageModel from letta.orm.run import Run as RunModel from letta.orm.run_metrics import RunMetrics as RunMetricsModel from letta.orm.sqlalchemy_base import AccessType -from letta.orm.step import Step as StepModel from letta.otel.tracing import log_event, trace_method from letta.schemas.enums import AgentType, ComparisonOperator, MessageRole, PrimitiveType, RunStatus from letta.schemas.job import LettaRequestConfig -from letta.schemas.letta_message import LettaMessage, LettaMessageUnion +from letta.schemas.letta_message import LettaMessage from letta.schemas.letta_response import LettaResponse from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType from letta.schemas.message import Message as PydanticMessage @@ -162,7 +157,7 @@ class RunManager: ) -> List[PydanticRun]: """List runs with filtering options.""" async with db_registry.async_session() as session: - from sqlalchemy import func, or_, select + from sqlalchemy import func, select # Always join with run_metrics to get duration data query = ( diff --git a/letta/services/source_manager.py b/letta/services/source_manager.py index 6f1891e7..825353cf 100644 --- a/letta/services/source_manager.py +++ b/letta/services/source_manager.py @@ -1,4 +1,3 @@ -import asyncio from typing import List, Optional, Union from sqlalchemy import and_, exists, select diff --git a/letta/services/step_manager.py b/letta/services/step_manager.py index 003604dd..d89b21f1 100644 --- a/letta/services/step_manager.py +++ b/letta/services/step_manager.py @@ -140,7 +140,7 @@ class StepManager: "provider_name": provider_name, "provider_category": provider_category, "model": model, - "model_handle": model_handle, + "model_handle": None, "model_endpoint": model_endpoint, "context_window_limit": context_window_limit, "completion_tokens": usage.completion_tokens, diff --git a/letta/services/summarizer/summarizer.py b/letta/services/summarizer/summarizer.py index b5a11e02..4206e6cd 100644 --- a/letta/services/summarizer/summarizer.py +++ b/letta/services/summarizer/summarizer.py @@ -1,6 +1,4 @@ -import asyncio import json -import traceback from typing import List, Optional, Tuple, Union from letta.agents.ephemeral_summary_agent import EphemeralSummaryAgent @@ -17,7 +15,7 @@ from letta.log import get_logger from letta.otel.tracing import trace_method from letta.prompts import gpt_summarize from letta.schemas.enums import AgentType, LLMCallType, MessageRole, ProviderType -from letta.schemas.letta_message_content import TextContent +from letta.schemas.letta_message_content import ImageContent, TextContent from letta.schemas.llm_config import LLMConfig from letta.schemas.message import Message, MessageCreate from letta.schemas.user import User @@ -41,7 +39,7 @@ class Summarizer: def __init__( self, mode: SummarizationMode, - summarizer_agent: Optional[Union[EphemeralSummaryAgent, "VoiceSleeptimeAgent"]] = None, + summarizer_agent: Optional[Union[EphemeralSummaryAgent, "VoiceSleeptimeAgent"]] = None, # noqa: F821 message_buffer_limit: int = 10, message_buffer_min: int = 3, partial_evict_summarizer_percentage: float = 0.30, @@ -453,7 +451,7 @@ async def simple_summary( actor: User, include_ack: bool = True, prompt: str | None = None, - telemetry_manager: "TelemetryManager | None" = None, + telemetry_manager: "TelemetryManager | None" = None, # noqa: F821 agent_id: str | None = None, agent_tags: List[str] | None = None, run_id: str | None = None, diff --git a/letta/services/summarizer/summarizer_sliding_window.py b/letta/services/summarizer/summarizer_sliding_window.py index ea650a59..d1ff5186 100644 --- a/letta/services/summarizer/summarizer_sliding_window.py +++ b/letta/services/summarizer/summarizer_sliding_window.py @@ -1,19 +1,14 @@ from typing import List, Optional, Tuple -from letta.helpers.message_helper import convert_message_creates_to_messages from letta.log import get_logger from letta.otel.tracing import trace_method -from letta.schemas.agent import AgentState from letta.schemas.enums import MessageRole -from letta.schemas.letta_message_content import TextContent from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import Message, MessageCreate +from letta.schemas.message import Message from letta.schemas.user import User from letta.services.context_window_calculator.token_counter import create_token_counter -from letta.services.message_manager import MessageManager from letta.services.summarizer.summarizer import simple_summary from letta.services.summarizer.summarizer_config import CompactionSettings -from letta.system import package_summarize_message_no_counts logger = get_logger(__name__) @@ -46,7 +41,7 @@ async def count_tokens_with_tools( actor: User, llm_config: LLMConfig, messages: List[Message], - tools: Optional[List["Tool"]] = None, + tools: Optional[List["Tool"]] = None, # noqa: F821 ) -> int: """Count tokens in messages AND tool definitions. diff --git a/letta/services/tool_executor/core_tool_executor.py b/letta/services/tool_executor/core_tool_executor.py index 46818911..f46c0d9b 100644 --- a/letta/services/tool_executor/core_tool_executor.py +++ b/letta/services/tool_executor/core_tool_executor.py @@ -8,13 +8,11 @@ from letta.constants import ( READ_ONLY_BLOCK_EDIT_ERROR, RETRIEVAL_QUERY_DEFAULT_PAGE_SIZE, ) -from letta.helpers.json_helpers import json_dumps -from letta.helpers.tpuf_client import should_use_tpuf_for_messages from letta.log import get_logger from letta.orm.errors import NoResultFound from letta.schemas.agent import AgentState from letta.schemas.block import BlockUpdate -from letta.schemas.enums import MessageRole, TagMatchMode +from letta.schemas.enums import MessageRole from letta.schemas.sandbox_config import SandboxConfig from letta.schemas.tool import Tool from letta.schemas.tool_execution_result import ToolExecutionResult diff --git a/letta/services/tool_executor/multi_agent_tool_executor.py b/letta/services/tool_executor/multi_agent_tool_executor.py index 8ecfc569..cdb55116 100644 --- a/letta/services/tool_executor/multi_agent_tool_executor.py +++ b/letta/services/tool_executor/multi_agent_tool_executor.py @@ -1,4 +1,3 @@ -import asyncio from typing import Any, Dict, List, Optional from letta.log import get_logger diff --git a/letta/services/tool_executor/sandbox_tool_executor.py b/letta/services/tool_executor/sandbox_tool_executor.py index ea5af641..2f903d8b 100644 --- a/letta/services/tool_executor/sandbox_tool_executor.py +++ b/letta/services/tool_executor/sandbox_tool_executor.py @@ -5,7 +5,7 @@ from letta.functions.ast_parsers import coerce_dict_args_by_annotations, get_fun from letta.log import get_logger from letta.otel.tracing import trace_method from letta.schemas.agent import AgentState -from letta.schemas.enums import SandboxType, ToolSourceType +from letta.schemas.enums import SandboxType from letta.schemas.sandbox_config import SandboxConfig from letta.schemas.tool import Tool from letta.schemas.tool_execution_result import ToolExecutionResult diff --git a/letta/services/tool_executor/tool_execution_sandbox.py b/letta/services/tool_executor/tool_execution_sandbox.py index 48b52fe8..c619274e 100644 --- a/letta/services/tool_executor/tool_execution_sandbox.py +++ b/letta/services/tool_executor/tool_execution_sandbox.py @@ -409,13 +409,13 @@ class ToolExecutionSandbox: sandbox_config_fingerprint=sbx_config.fingerprint(), ) - def parse_exception_from_e2b_execution(self, e2b_execution: "Execution") -> Exception: + def parse_exception_from_e2b_execution(self, e2b_execution: "Execution") -> Exception: # noqa: F821 builtins_dict = __builtins__ if isinstance(__builtins__, dict) else vars(__builtins__) # Dynamically fetch the exception class from builtins, defaulting to Exception if not found exception_class = builtins_dict.get(e2b_execution.error.name, Exception) return exception_class(e2b_execution.error.value) - def get_running_e2b_sandbox_with_same_state(self, sandbox_config: SandboxConfig) -> Optional["Sandbox"]: + def get_running_e2b_sandbox_with_same_state(self, sandbox_config: SandboxConfig) -> Optional["Sandbox"]: # noqa: F821 from e2b_code_interpreter import Sandbox # List running sandboxes and access metadata. @@ -430,7 +430,7 @@ class ToolExecutionSandbox: return None @trace_method - def create_e2b_sandbox_with_metadata_hash(self, sandbox_config: SandboxConfig) -> "Sandbox": + def create_e2b_sandbox_with_metadata_hash(self, sandbox_config: SandboxConfig) -> "Sandbox": # noqa: F821 from e2b_code_interpreter import Sandbox state_hash = sandbox_config.fingerprint() diff --git a/letta/services/tool_manager.py b/letta/services/tool_manager.py index 0b46d2e9..2613c99b 100644 --- a/letta/services/tool_manager.py +++ b/letta/services/tool_manager.py @@ -24,7 +24,7 @@ from letta.constants import ( MODAL_SAFE_IMPORT_MODULES, ) from letta.errors import LettaInvalidArgumentError, LettaToolNameConflictError, LettaToolNameSchemaMismatchError -from letta.functions.functions import derive_openai_json_schema, load_function_set +from letta.functions.functions import load_function_set from letta.helpers.tool_helpers import compute_tool_hash, generate_modal_function_name from letta.log import get_logger @@ -32,7 +32,6 @@ from letta.log import get_logger from letta.orm.errors import NoResultFound from letta.orm.tool import Tool as ToolModel from letta.otel.tracing import trace_method, tracer -from letta.schemas.agent import AgentState from letta.schemas.enums import PrimitiveType, SandboxType, ToolType from letta.schemas.tool import Tool as PydanticTool, ToolCreate, ToolUpdate from letta.schemas.user import User as PydanticUser @@ -57,7 +56,6 @@ def modal_tool_wrapper(tool: PydanticTool, actor: PydanticUser, sandbox_env_vars from typing import Optional import modal - from letta_client import Letta packages = [str(req) for req in tool.pip_requirements] if tool.pip_requirements else [] for package in MODAL_SAFE_IMPORT_MODULES: @@ -1297,7 +1295,6 @@ class ToolManager: @trace_method async def create_or_update_modal_app(self, tool: PydanticTool, actor: PydanticUser): """Create a Modal app with the tool function registered""" - import time import modal diff --git a/letta/services/tool_sandbox/modal_sandbox.py b/letta/services/tool_sandbox/modal_sandbox.py index 4fd1cd6e..ba779f01 100644 --- a/letta/services/tool_sandbox/modal_sandbox.py +++ b/letta/services/tool_sandbox/modal_sandbox.py @@ -4,10 +4,6 @@ Model sandbox implementation, which configures on Modal App per tool. from typing import TYPE_CHECKING, Any, Dict, Optional -import modal -from e2b.sandbox.commands.command_handle import CommandExitException -from e2b_code_interpreter import AsyncSandbox - from letta.constants import MODAL_DEFAULT_TOOL_NAME from letta.log import get_logger from letta.otel.tracing import log_event, trace_method @@ -16,16 +12,13 @@ from letta.schemas.enums import SandboxType from letta.schemas.sandbox_config import SandboxConfig from letta.schemas.tool import Tool from letta.schemas.tool_execution_result import ToolExecutionResult -from letta.services.helpers.tool_parser_helper import parse_function_arguments, parse_stdout_best_effort -from letta.services.tool_manager import ToolManager from letta.services.tool_sandbox.base import AsyncToolSandboxBase from letta.types import JsonDict -from letta.utils import get_friendly_error_msg logger = get_logger(__name__) if TYPE_CHECKING: - from e2b_code_interpreter import Execution + pass class AsyncToolSandboxModal(AsyncToolSandboxBase): diff --git a/letta/services/webhook_service.py b/letta/services/webhook_service.py index 56509289..2f1e0544 100644 --- a/letta/services/webhook_service.py +++ b/letta/services/webhook_service.py @@ -1,6 +1,5 @@ import logging import os -from typing import Optional import httpx diff --git a/letta/types/__init__.py b/letta/types/__init__.py index b0f83c65..2ff19c97 100644 --- a/letta/types/__init__.py +++ b/letta/types/__init__.py @@ -1,4 +1,4 @@ -from typing import Any, TypeAlias +from typing import TypeAlias from pydantic import JsonValue diff --git a/letta/utils.py b/letta/utils.py index 62fe459a..85587e1a 100644 --- a/letta/utils.py +++ b/letta/utils.py @@ -21,7 +21,6 @@ from typing import Any, Callable, Coroutine, Optional, Union, _GenericAlias, get from urllib.parse import urljoin, urlparse import demjson3 as demjson -import tiktoken from pathvalidate import sanitize_filename as pathvalidate_sanitize_filename from sqlalchemy import text @@ -1384,7 +1383,6 @@ def fire_and_forget(coro, task_name: Optional[str] = None, error_callback: Optio Returns: The created asyncio Task object """ - import traceback task = asyncio.create_task(coro) diff --git a/pyproject.toml b/pyproject.toml index af3c70a5..da83dceb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -194,11 +194,8 @@ ignore = [ "E712", # true-false-comparison "E722", # bare except "E721", # type comparison - "F401", # unused import - "F821", # undefined name "F811", # redefined while unused "F841", # local variable assigned but never used - "W293", # blank line contains whitespace ] [tool.ruff.lint.isort] diff --git a/tests/adapters/test_letta_llm_stream_adapter_error_handling.py b/tests/adapters/test_letta_llm_stream_adapter_error_handling.py index fcdf562d..a7d7e7e5 100644 --- a/tests/adapters/test_letta_llm_stream_adapter_error_handling.py +++ b/tests/adapters/test_letta_llm_stream_adapter_error_handling.py @@ -9,7 +9,6 @@ from letta.errors import ( ContextWindowExceededError, LLMBadRequestError, LLMConnectionError, - LLMError, LLMInsufficientCreditsError, LLMServerError, ) diff --git a/tests/conftest.py b/tests/conftest.py index 452e682f..8bb49443 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -14,7 +14,6 @@ from letta_client import Letta from letta.server.db import db_registry from letta.services.organization_manager import OrganizationManager from letta.services.user_manager import UserManager -from letta.settings import tool_settings def pytest_configure(config): diff --git a/tests/integration_test_agent_tool_graph.py b/tests/integration_test_agent_tool_graph.py index 9a25d8d2..89a5f840 100644 --- a/tests/integration_test_agent_tool_graph.py +++ b/tests/integration_test_agent_tool_graph.py @@ -4,11 +4,9 @@ import uuid import pytest -from letta.agents.letta_agent_v2 import LettaAgentV2 from letta.agents.letta_agent_v3 import LettaAgentV3 from letta.config import LettaConfig from letta.schemas.letta_message import ToolCallMessage -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType from letta.schemas.message import MessageCreate from letta.schemas.run import Run from letta.schemas.tool_rule import ( @@ -282,7 +280,7 @@ async def complex_child_tool(server): Returns: str: Summary string encoding the provided inputs. """ - return f"ok:{text}:{num}:{flag}:{len(arr)}:{len(obj)}" + return f"ok:{text}:{num}:{flag}:{len(arr)}" actor = await server.user_manager.get_actor_or_default_async() tool = await server.tool_manager.create_or_update_tool_async(create_tool_from_func(func=complex_child), actor=actor) diff --git a/tests/integration_test_async_tool_sandbox.py b/tests/integration_test_async_tool_sandbox.py index de690f12..2e3cd9f8 100644 --- a/tests/integration_test_async_tool_sandbox.py +++ b/tests/integration_test_async_tool_sandbox.py @@ -13,7 +13,6 @@ from dotenv import load_dotenv from letta_client import Letta from sqlalchemy import delete -from letta.config import LettaConfig from letta.functions.function_sets.base import core_memory_append, core_memory_replace from letta.orm.sandbox_config import SandboxConfig, SandboxEnvironmentVariable from letta.schemas.agent import AgentState, CreateAgent @@ -23,7 +22,6 @@ from letta.schemas.organization import Organization from letta.schemas.pip_requirement import PipRequirement from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfigCreate from letta.schemas.user import User -from letta.server.db import db_registry from letta.services.organization_manager import OrganizationManager from letta.services.sandbox_config_manager import SandboxConfigManager from letta.services.tool_manager import ToolManager @@ -382,7 +380,7 @@ async def tool_with_broken_pip_requirements(test_user): str: Should not reach here due to pip install failure. """ try: - import some_nonexistent_package # This will fail during pip install + import some_nonexistent_package # noqa: F401 return "This should not execute" except ImportError as e: diff --git a/tests/integration_test_chat_completions.py b/tests/integration_test_chat_completions.py index 89669350..86bae323 100644 --- a/tests/integration_test_chat_completions.py +++ b/tests/integration_test_chat_completions.py @@ -1,7 +1,6 @@ import os import threading import uuid -from typing import List import pytest from dotenv import load_dotenv @@ -12,7 +11,6 @@ from openai.types.chat.chat_completion_chunk import ChatCompletionChunk from letta.schemas.embedding_config import EmbeddingConfig from letta.schemas.enums import AgentType, MessageStreamStatus from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import MessageCreate from letta.schemas.openai.chat_completion_request import ChatCompletionRequest, UserMessage as OpenAIUserMessage from letta.schemas.usage import LettaUsageStatistics from tests.utils import wait_for_server diff --git a/tests/integration_test_conversations_sdk.py b/tests/integration_test_conversations_sdk.py index 2b863e33..8ba154b4 100644 --- a/tests/integration_test_conversations_sdk.py +++ b/tests/integration_test_conversations_sdk.py @@ -3,7 +3,6 @@ Integration tests for the Conversations API using the SDK. """ import uuid -from time import sleep import pytest import requests diff --git a/tests/integration_test_human_in_the_loop.py b/tests/integration_test_human_in_the_loop.py index 6024660f..269060a3 100644 --- a/tests/integration_test_human_in_the_loop.py +++ b/tests/integration_test_human_in_the_loop.py @@ -1,4 +1,3 @@ -import asyncio import logging import uuid from typing import Any, List @@ -7,7 +6,6 @@ from unittest.mock import patch import pytest from letta_client import APIError, Letta from letta_client.types import AgentState, MessageCreateParam, Tool -from letta_client.types.agents import ApprovalCreateParam from letta.adapters.simple_llm_stream_adapter import SimpleLLMStreamAdapter diff --git a/tests/integration_test_mcp.py b/tests/integration_test_mcp.py index 7ac138d4..06531772 100644 --- a/tests/integration_test_mcp.py +++ b/tests/integration_test_mcp.py @@ -16,7 +16,6 @@ from letta_client.types.tool_return_message import ToolReturnMessage from letta.functions.mcp_client.types import StdioServerConfig from letta.schemas.agent import AgentState from letta.schemas.embedding_config import EmbeddingConfig -from letta.schemas.letta_message_content import TextContent from letta.schemas.llm_config import LLMConfig # ------------------------------ diff --git a/tests/integration_test_modal.py b/tests/integration_test_modal.py index 8a2bb3cb..ae49b855 100644 --- a/tests/integration_test_modal.py +++ b/tests/integration_test_modal.py @@ -18,7 +18,6 @@ from letta.schemas.organization import Organization from letta.schemas.pip_requirement import PipRequirement from letta.schemas.sandbox_config import LocalSandboxConfig, ModalSandboxConfig, SandboxConfigCreate from letta.schemas.user import User -from letta.server.db import db_registry from letta.server.server import SyncServer from letta.services.organization_manager import OrganizationManager from letta.services.sandbox_config_manager import SandboxConfigManager diff --git a/tests/integration_test_multi_modal_tool_returns.py b/tests/integration_test_multi_modal_tool_returns.py index 831913e6..6304e539 100644 --- a/tests/integration_test_multi_modal_tool_returns.py +++ b/tests/integration_test_multi_modal_tool_returns.py @@ -15,7 +15,7 @@ import uuid import pytest from letta_client import Letta -from letta_client.types.agents import ApprovalRequestMessage, AssistantMessage, ToolCallMessage +from letta_client.types.agents import ApprovalRequestMessage, AssistantMessage # ------------------------------ # Constants diff --git a/tests/integration_test_override_model.py b/tests/integration_test_override_model.py index e5a484a0..4a897a57 100644 --- a/tests/integration_test_override_model.py +++ b/tests/integration_test_override_model.py @@ -13,7 +13,7 @@ import os import threading import time import uuid -from typing import Any, Generator, List +from typing import Generator, List import pytest import requests diff --git a/tests/integration_test_send_message.py b/tests/integration_test_send_message.py index 4c1d71b4..404965f2 100644 --- a/tests/integration_test_send_message.py +++ b/tests/integration_test_send_message.py @@ -29,7 +29,7 @@ from letta_client.types.agents.letta_streaming_response import LettaPing, LettaS from letta_client.types.agents.text_content_param import TextContentParam from letta.errors import LLMError -from letta.helpers.reasoning_helper import is_reasoning_completely_disabled +from letta.helpers.reasoning_helper import is_reasoning_completely_disabled # noqa: F401 from letta.llm_api.openai_client import is_openai_reasoning_model logger = logging.getLogger(__name__) @@ -2557,7 +2557,7 @@ def test_inner_thoughts_toggle_interleaved( # ) # Test our helper functions - assert is_reasoning_completely_disabled(adjusted_llm_config), "Reasoning should be completely disabled" + # assert is_reasoning_completely_disabled(adjusted_llm_config), "Reasoning should be completely disabled" # Verify that assistant messages with tool calls have been scrubbed of inner thoughts # Branch assertions based on model endpoint type diff --git a/tests/integration_test_sleeptime_agent.py b/tests/integration_test_sleeptime_agent.py index dce649f7..1f26550c 100644 --- a/tests/integration_test_sleeptime_agent.py +++ b/tests/integration_test_sleeptime_agent.py @@ -325,7 +325,6 @@ async def test_sleeptime_agent_new_block_attachment(client): assert main_agent.id in [agent.id for agent in agents] # 4. Create a new block after agent creation - from letta.schemas.block import Block as PydanticBlock new_block = client.blocks.create( label="preferences", diff --git a/tests/integration_test_summarizer.py b/tests/integration_test_summarizer.py index 22a98995..5e437197 100644 --- a/tests/integration_test_summarizer.py +++ b/tests/integration_test_summarizer.py @@ -13,14 +13,13 @@ from typing import List, Literal import pytest -from letta.agents.letta_agent_v2 import LettaAgentV2 from letta.agents.letta_agent_v3 import LettaAgentV3 from letta.config import LettaConfig from letta.schemas.agent import CreateAgent, UpdateAgent from letta.schemas.block import BlockUpdate, CreateBlock from letta.schemas.embedding_config import EmbeddingConfig from letta.schemas.enums import MessageRole -from letta.schemas.letta_message import EventMessage, LettaMessage, SummaryMessage +from letta.schemas.letta_message import EventMessage, SummaryMessage from letta.schemas.letta_message_content import TextContent, ToolCallContent, ToolReturnContent from letta.schemas.llm_config import LLMConfig from letta.schemas.message import Message as PydanticMessage, MessageCreate diff --git a/tests/integration_test_turbopuffer.py b/tests/integration_test_turbopuffer.py index 31fa21be..ca90d2f4 100644 --- a/tests/integration_test_turbopuffer.py +++ b/tests/integration_test_turbopuffer.py @@ -2492,7 +2492,7 @@ async def test_query_messages_by_org_id_with_missing_conversation_id_schema(enab @pytest.mark.asyncio async def test_system_messages_not_embedded_during_agent_creation(server, default_user, enable_message_embedding): """Test that system messages are filtered out before being passed to the embedding pipeline during agent creation""" - from unittest.mock import AsyncMock, patch + from unittest.mock import patch from letta.schemas.agent import CreateAgent from letta.schemas.llm_config import LLMConfig diff --git a/tests/integration_test_typescript_tool_execution_sandbox.py b/tests/integration_test_typescript_tool_execution_sandbox.py index 97528214..7a39eea1 100644 --- a/tests/integration_test_typescript_tool_execution_sandbox.py +++ b/tests/integration_test_typescript_tool_execution_sandbox.py @@ -14,7 +14,6 @@ from letta.schemas.tool import Tool as PydanticTool, ToolCreate from letta.schemas.user import User from letta.server.server import SyncServer from letta.services.organization_manager import OrganizationManager -from letta.services.tool_executor.tool_execution_sandbox import ToolExecutionSandbox from letta.services.tool_manager import ToolManager from letta.services.tool_sandbox.e2b_sandbox import AsyncToolSandboxE2B from letta.services.user_manager import UserManager diff --git a/tests/integration_test_usage_tracking.py b/tests/integration_test_usage_tracking.py index 018fb312..c6887da9 100644 --- a/tests/integration_test_usage_tracking.py +++ b/tests/integration_test_usage_tracking.py @@ -17,7 +17,7 @@ import json import logging import os import uuid -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, List, Optional, Tuple import pytest from dotenv import load_dotenv diff --git a/tests/managers/conftest.py b/tests/managers/conftest.py index 6e56cdcd..f6dcf9ac 100644 --- a/tests/managers/conftest.py +++ b/tests/managers/conftest.py @@ -6,7 +6,6 @@ This conftest.py makes fixtures available to all test files in the tests/manager import os import time -import uuid from typing import Tuple import pytest @@ -23,7 +22,7 @@ from letta.schemas.agent import CreateAgent from letta.schemas.block import Block as PydanticBlock, CreateBlock from letta.schemas.embedding_config import EmbeddingConfig from letta.schemas.enums import JobStatus, MessageRole, RunStatus -from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate +from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate from letta.schemas.file import FileMetadata as PydanticFileMetadata from letta.schemas.job import BatchJob, Job as PydanticJob from letta.schemas.letta_message_content import TextContent diff --git a/tests/managers/test_agent_manager.py b/tests/managers/test_agent_manager.py index dcf2ddb3..0eaedc05 100644 --- a/tests/managers/test_agent_manager.py +++ b/tests/managers/test_agent_manager.py @@ -1,19 +1,9 @@ -import json -import logging -import os -import random -import re -import string import time import uuid -from datetime import datetime, timedelta, timezone -from typing import List -from unittest.mock import AsyncMock, Mock, patch +from datetime import datetime, timezone +from unittest.mock import patch import pytest -from _pytest.python_api import approx -from anthropic.types.beta import BetaMessage -from anthropic.types.beta.messages import BetaMessageBatchIndividualResponse, BetaMessageBatchSucceededResult # Import shared fixtures and constants from conftest from conftest import ( @@ -21,87 +11,33 @@ from conftest import ( DEFAULT_EMBEDDING_CONFIG, USING_SQLITE, ) -from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction from sqlalchemy import func, select -from sqlalchemy.exc import IntegrityError, InvalidRequestError -from sqlalchemy.orm.exc import StaleDataError -from letta.config import LettaConfig from letta.constants import ( - BASE_MEMORY_TOOLS, - BASE_SLEEPTIME_TOOLS, - BASE_TOOLS, - BASE_VOICE_SLEEPTIME_CHAT_TOOLS, - BASE_VOICE_SLEEPTIME_TOOLS, - BUILTIN_TOOLS, - DEFAULT_ORG_ID, - DEFAULT_ORG_NAME, - FILES_TOOLS, - LETTA_TOOL_EXECUTION_DIR, - LETTA_TOOL_SET, LOCAL_ONLY_MULTI_AGENT_TOOLS, - MCP_TOOL_TAG_NAME_PREFIX, MULTI_AGENT_TOOLS, ) -from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client from letta.errors import LettaAgentNotFoundError -from letta.functions.functions import derive_openai_json_schema, parse_source_code -from letta.functions.mcp_client.types import MCPTool -from letta.helpers import ToolRulesSolver -from letta.helpers.datetime_helpers import AsyncTimer -from letta.jobs.types import ItemUpdateInfo, RequestStatusUpdateInfo, StepStatusUpdateInfo -from letta.orm import Base, Block -from letta.orm.block_history import BlockHistory -from letta.orm.errors import NoResultFound, UniqueConstraintViolationError -from letta.orm.file import FileContent as FileContentModel, FileMetadata as FileMetadataModel +from letta.orm.file import FileContent as FileContentModel from letta.schemas.agent import CreateAgent, InternalTemplateAgentCreate, UpdateAgent -from letta.schemas.block import Block as PydanticBlock, BlockUpdate, CreateBlock +from letta.schemas.block import CreateBlock from letta.schemas.embedding_config import EmbeddingConfig from letta.schemas.enums import ( - ActorType, - AgentStepStatus, - FileProcessingStatus, - JobStatus, - JobType, MessageRole, - ProviderType, - SandboxType, - StepStatus, - TagMatchMode, - ToolType, - VectorDBProvider, ) -from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate -from letta.schemas.file import FileMetadata, FileMetadata as PydanticFileMetadata -from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityPropertyType, IdentityType, IdentityUpdate, IdentityUpsert -from letta.schemas.job import BatchJob, Job, Job as PydanticJob, JobUpdate, LettaRequestConfig -from letta.schemas.letta_message import UpdateAssistantMessage, UpdateReasoningMessage, UpdateSystemMessage, UpdateUserMessage from letta.schemas.letta_message_content import TextContent -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.llm_batch_job import AgentStepState, LLMBatchItem +from letta.schemas.letta_stop_reason import StopReasonType from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import Message as PydanticMessage, MessageCreate, MessageUpdate -from letta.schemas.model import ModelSettings -from letta.schemas.openai.chat_completion_response import UsageStatistics -from letta.schemas.organization import Organization, Organization as PydanticOrganization, OrganizationUpdate -from letta.schemas.passage import Passage as PydanticPassage -from letta.schemas.pip_requirement import PipRequirement -from letta.schemas.run import Run as PydanticRun -from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfigCreate, SandboxConfigUpdate -from letta.schemas.source import Source as PydanticSource, SourceUpdate -from letta.schemas.tool import Tool as PydanticTool, ToolCreate, ToolUpdate +from letta.schemas.message import MessageCreate +from letta.schemas.source import Source as PydanticSource from letta.schemas.tool_rule import InitToolRule -from letta.schemas.user import User as PydanticUser, UserUpdate from letta.server.db import db_registry from letta.server.server import SyncServer -from letta.services.block_manager import BlockManager from letta.services.helpers.agent_manager_helper import calculate_base_tools, calculate_multi_agent_tools, validate_agent_exists_async -from letta.services.step_manager import FeedbackType from letta.services.summarizer.summarizer_config import CompactionSettings -from letta.settings import settings, tool_settings +from letta.settings import settings from letta.utils import calculate_file_defaults_based_on_context_window from tests.helpers.utils import comprehensive_agent_checks, validate_context_window_overview -from tests.utils import random_string # ====================================================================================================================== # Helper Functions @@ -1579,7 +1515,6 @@ async def test_agent_state_schema_unchanged(server: SyncServer): from letta.schemas.response_format import ResponseFormatUnion from letta.schemas.source import Source from letta.schemas.tool import Tool - from letta.schemas.tool_rule import ToolRule from letta.services.summarizer.summarizer_config import CompactionSettings # Define the expected schema structure diff --git a/tests/managers/test_agent_tag_manager.py b/tests/managers/test_agent_tag_manager.py index de67136c..d0c9dd75 100644 --- a/tests/managers/test_agent_tag_manager.py +++ b/tests/managers/test_agent_tag_manager.py @@ -1,106 +1,20 @@ import asyncio -import json -import logging -import os -import random -import re -import string import time -import uuid -from datetime import datetime, timedelta, timezone -from typing import List -from unittest.mock import AsyncMock, Mock, patch import pytest -from _pytest.python_api import approx -from anthropic.types.beta import BetaMessage -from anthropic.types.beta.messages import BetaMessageBatchIndividualResponse, BetaMessageBatchSucceededResult # Import shared fixtures and constants from conftest from conftest import ( CREATE_DELAY_SQLITE, - DEFAULT_EMBEDDING_CONFIG, USING_SQLITE, ) -from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction -from sqlalchemy import func, select -from sqlalchemy.exc import IntegrityError, InvalidRequestError -from sqlalchemy.orm.exc import StaleDataError -from letta.config import LettaConfig -from letta.constants import ( - BASE_MEMORY_TOOLS, - BASE_SLEEPTIME_TOOLS, - BASE_TOOLS, - BASE_VOICE_SLEEPTIME_CHAT_TOOLS, - BASE_VOICE_SLEEPTIME_TOOLS, - BUILTIN_TOOLS, - DEFAULT_ORG_ID, - DEFAULT_ORG_NAME, - FILES_TOOLS, - LETTA_TOOL_EXECUTION_DIR, - LETTA_TOOL_SET, - LOCAL_ONLY_MULTI_AGENT_TOOLS, - MCP_TOOL_TAG_NAME_PREFIX, - MULTI_AGENT_TOOLS, -) -from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client -from letta.errors import LettaAgentNotFoundError -from letta.functions.functions import derive_openai_json_schema, parse_source_code -from letta.functions.mcp_client.types import MCPTool -from letta.helpers import ToolRulesSolver -from letta.helpers.datetime_helpers import AsyncTimer -from letta.jobs.types import ItemUpdateInfo, RequestStatusUpdateInfo, StepStatusUpdateInfo -from letta.orm import Base, Block -from letta.orm.block_history import BlockHistory -from letta.orm.errors import NoResultFound, UniqueConstraintViolationError -from letta.orm.file import FileContent as FileContentModel, FileMetadata as FileMetadataModel from letta.schemas.agent import CreateAgent, UpdateAgent -from letta.schemas.block import Block as PydanticBlock, BlockUpdate, CreateBlock from letta.schemas.embedding_config import EmbeddingConfig -from letta.schemas.enums import ( - ActorType, - AgentStepStatus, - FileProcessingStatus, - JobStatus, - JobType, - MessageRole, - ProviderType, - SandboxType, - StepStatus, - TagMatchMode, - ToolType, - VectorDBProvider, -) -from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate -from letta.schemas.file import FileMetadata, FileMetadata as PydanticFileMetadata -from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityPropertyType, IdentityType, IdentityUpdate, IdentityUpsert -from letta.schemas.job import BatchJob, Job, Job as PydanticJob, JobUpdate, LettaRequestConfig -from letta.schemas.letta_message import UpdateAssistantMessage, UpdateReasoningMessage, UpdateSystemMessage, UpdateUserMessage -from letta.schemas.letta_message_content import TextContent -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.llm_batch_job import AgentStepState, LLMBatchItem from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import Message as PydanticMessage, MessageCreate, MessageUpdate -from letta.schemas.openai.chat_completion_response import UsageStatistics -from letta.schemas.organization import Organization, Organization as PydanticOrganization, OrganizationUpdate -from letta.schemas.passage import Passage as PydanticPassage -from letta.schemas.pip_requirement import PipRequirement -from letta.schemas.run import Run as PydanticRun -from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfigCreate, SandboxConfigUpdate -from letta.schemas.source import Source as PydanticSource, SourceUpdate -from letta.schemas.tool import Tool as PydanticTool, ToolCreate, ToolUpdate -from letta.schemas.tool_rule import InitToolRule -from letta.schemas.user import User as PydanticUser, UserUpdate -from letta.server.db import db_registry +from letta.schemas.organization import Organization as PydanticOrganization +from letta.schemas.user import User as PydanticUser from letta.server.server import SyncServer -from letta.services.block_manager import BlockManager -from letta.services.helpers.agent_manager_helper import calculate_base_tools, calculate_multi_agent_tools, validate_agent_exists_async -from letta.services.step_manager import FeedbackType -from letta.settings import settings, tool_settings -from letta.utils import calculate_file_defaults_based_on_context_window -from tests.helpers.utils import comprehensive_agent_checks, validate_context_window_overview -from tests.utils import random_string # ====================================================================================================================== # AgentManager Tests - Tags Relationship diff --git a/tests/managers/test_archive_manager.py b/tests/managers/test_archive_manager.py index 33241f08..d61c337c 100644 --- a/tests/managers/test_archive_manager.py +++ b/tests/managers/test_archive_manager.py @@ -1,105 +1,19 @@ -import json -import logging -import os -import random -import re -import string -import time import uuid -from datetime import datetime, timedelta, timezone -from typing import List -from unittest.mock import AsyncMock, Mock, patch import pytest -from _pytest.python_api import approx -from anthropic.types.beta import BetaMessage -from anthropic.types.beta.messages import BetaMessageBatchIndividualResponse, BetaMessageBatchSucceededResult # Import shared fixtures and constants from conftest from conftest import ( - CREATE_DELAY_SQLITE, DEFAULT_EMBEDDING_CONFIG, - USING_SQLITE, ) -from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction -from sqlalchemy import func, select -from sqlalchemy.exc import IntegrityError, InvalidRequestError -from sqlalchemy.orm.exc import StaleDataError -from letta.config import LettaConfig -from letta.constants import ( - BASE_MEMORY_TOOLS, - BASE_SLEEPTIME_TOOLS, - BASE_TOOLS, - BASE_VOICE_SLEEPTIME_CHAT_TOOLS, - BASE_VOICE_SLEEPTIME_TOOLS, - BUILTIN_TOOLS, - DEFAULT_ORG_ID, - DEFAULT_ORG_NAME, - FILES_TOOLS, - LETTA_TOOL_EXECUTION_DIR, - LETTA_TOOL_SET, - LOCAL_ONLY_MULTI_AGENT_TOOLS, - MCP_TOOL_TAG_NAME_PREFIX, - MULTI_AGENT_TOOLS, -) -from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client from letta.errors import LettaAgentNotFoundError -from letta.functions.functions import derive_openai_json_schema, parse_source_code -from letta.functions.mcp_client.types import MCPTool -from letta.helpers import ToolRulesSolver -from letta.helpers.datetime_helpers import AsyncTimer -from letta.jobs.types import ItemUpdateInfo, RequestStatusUpdateInfo, StepStatusUpdateInfo -from letta.orm import Base, Block -from letta.orm.block_history import BlockHistory -from letta.orm.errors import NoResultFound, UniqueConstraintViolationError -from letta.orm.file import FileContent as FileContentModel, FileMetadata as FileMetadataModel -from letta.schemas.agent import AgentRelationships, AgentState, CreateAgent, UpdateAgent -from letta.schemas.block import Block as PydanticBlock, BlockUpdate, CreateBlock +from letta.orm.errors import NoResultFound +from letta.schemas.agent import CreateAgent from letta.schemas.embedding_config import EmbeddingConfig -from letta.schemas.enums import ( - ActorType, - AgentStepStatus, - FileProcessingStatus, - JobStatus, - JobType, - MessageRole, - ProviderType, - SandboxType, - StepStatus, - TagMatchMode, - ToolType, - VectorDBProvider, -) -from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate -from letta.schemas.file import FileMetadata, FileMetadata as PydanticFileMetadata -from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityPropertyType, IdentityType, IdentityUpdate, IdentityUpsert -from letta.schemas.job import BatchJob, Job, Job as PydanticJob, JobUpdate, LettaRequestConfig -from letta.schemas.letta_message import UpdateAssistantMessage, UpdateReasoningMessage, UpdateSystemMessage, UpdateUserMessage -from letta.schemas.letta_message_content import TextContent -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.llm_batch_job import AgentStepState, LLMBatchItem from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import Message as PydanticMessage, MessageCreate, MessageUpdate -from letta.schemas.openai.chat_completion_response import UsageStatistics -from letta.schemas.organization import Organization, Organization as PydanticOrganization, OrganizationUpdate from letta.schemas.passage import Passage as PydanticPassage -from letta.schemas.pip_requirement import PipRequirement -from letta.schemas.run import Run as PydanticRun -from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfigCreate, SandboxConfigUpdate -from letta.schemas.source import Source as PydanticSource, SourceUpdate -from letta.schemas.tool import Tool as PydanticTool, ToolCreate, ToolUpdate -from letta.schemas.tool_rule import InitToolRule -from letta.schemas.user import User as PydanticUser, UserUpdate -from letta.server.db import db_registry from letta.server.server import SyncServer -from letta.services.block_manager import BlockManager -from letta.services.helpers.agent_manager_helper import calculate_base_tools, calculate_multi_agent_tools, validate_agent_exists_async -from letta.services.step_manager import FeedbackType -from letta.settings import settings, tool_settings -from letta.utils import calculate_file_defaults_based_on_context_window -from tests.helpers.utils import comprehensive_agent_checks, validate_context_window_overview -from tests.utils import random_string # ====================================================================================================================== diff --git a/tests/managers/test_block_manager.py b/tests/managers/test_block_manager.py index b815428a..8f6408cf 100644 --- a/tests/managers/test_block_manager.py +++ b/tests/managers/test_block_manager.py @@ -1,104 +1,33 @@ -import json import logging -import os import random -import re import string import time import uuid -from datetime import datetime, timedelta, timezone -from typing import List -from unittest.mock import AsyncMock, Mock, patch import pytest -from _pytest.python_api import approx -from anthropic.types.beta import BetaMessage -from anthropic.types.beta.messages import BetaMessageBatchIndividualResponse, BetaMessageBatchSucceededResult # Import shared fixtures and constants from conftest from conftest import ( CREATE_DELAY_SQLITE, - DEFAULT_EMBEDDING_CONFIG, USING_SQLITE, ) -from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction -from sqlalchemy import func, select -from sqlalchemy.exc import IntegrityError, InvalidRequestError from sqlalchemy.orm.exc import StaleDataError -from letta.config import LettaConfig -from letta.constants import ( - BASE_MEMORY_TOOLS, - BASE_SLEEPTIME_TOOLS, - BASE_TOOLS, - BASE_VOICE_SLEEPTIME_CHAT_TOOLS, - BASE_VOICE_SLEEPTIME_TOOLS, - BUILTIN_TOOLS, - DEFAULT_ORG_ID, - DEFAULT_ORG_NAME, - FILES_TOOLS, - LETTA_TOOL_EXECUTION_DIR, - LETTA_TOOL_SET, - LOCAL_ONLY_MULTI_AGENT_TOOLS, - MCP_TOOL_TAG_NAME_PREFIX, - MULTI_AGENT_TOOLS, -) -from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client -from letta.errors import LettaAgentNotFoundError, LettaInvalidArgumentError -from letta.functions.functions import derive_openai_json_schema, parse_source_code -from letta.functions.mcp_client.types import MCPTool -from letta.helpers import ToolRulesSolver -from letta.helpers.datetime_helpers import AsyncTimer -from letta.jobs.types import ItemUpdateInfo, RequestStatusUpdateInfo, StepStatusUpdateInfo -from letta.orm import Base, Block +from letta.errors import LettaInvalidArgumentError +from letta.orm import Block from letta.orm.block_history import BlockHistory from letta.orm.errors import NoResultFound, UniqueConstraintViolationError -from letta.orm.file import FileContent as FileContentModel, FileMetadata as FileMetadataModel -from letta.schemas.agent import CreateAgent, UpdateAgent -from letta.schemas.block import Block as PydanticBlock, BlockUpdate, CreateBlock +from letta.schemas.agent import CreateAgent +from letta.schemas.block import Block as PydanticBlock, BlockUpdate from letta.schemas.embedding_config import EmbeddingConfig from letta.schemas.enums import ( ActorType, - AgentStepStatus, - FileProcessingStatus, - JobStatus, - JobType, - MessageRole, - ProviderType, - SandboxType, - StepStatus, - TagMatchMode, - ToolType, - VectorDBProvider, ) -from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate -from letta.schemas.file import FileMetadata, FileMetadata as PydanticFileMetadata -from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityPropertyType, IdentityType, IdentityUpdate, IdentityUpsert -from letta.schemas.job import BatchJob, Job, Job as PydanticJob, JobUpdate, LettaRequestConfig -from letta.schemas.letta_message import UpdateAssistantMessage, UpdateReasoningMessage, UpdateSystemMessage, UpdateUserMessage -from letta.schemas.letta_message_content import TextContent -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.llm_batch_job import AgentStepState, LLMBatchItem from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import Message as PydanticMessage, MessageCreate, MessageUpdate -from letta.schemas.openai.chat_completion_response import UsageStatistics -from letta.schemas.organization import Organization, Organization as PydanticOrganization, OrganizationUpdate -from letta.schemas.passage import Passage as PydanticPassage -from letta.schemas.pip_requirement import PipRequirement -from letta.schemas.run import Run as PydanticRun -from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfigCreate, SandboxConfigUpdate -from letta.schemas.source import Source as PydanticSource, SourceUpdate -from letta.schemas.tool import Tool as PydanticTool, ToolCreate, ToolUpdate -from letta.schemas.tool_rule import InitToolRule -from letta.schemas.user import User as PydanticUser, UserUpdate +from letta.schemas.user import User as PydanticUser from letta.server.db import db_registry from letta.server.server import SyncServer from letta.services.block_manager import BlockManager -from letta.services.helpers.agent_manager_helper import calculate_base_tools, calculate_multi_agent_tools, validate_agent_exists_async -from letta.services.step_manager import FeedbackType -from letta.settings import settings, tool_settings -from letta.utils import calculate_file_defaults_based_on_context_window -from tests.helpers.utils import comprehensive_agent_checks, validate_context_window_overview from tests.utils import random_string # ====================================================================================================================== diff --git a/tests/managers/test_cancellation.py b/tests/managers/test_cancellation.py index 804fd4ee..e154150d 100644 --- a/tests/managers/test_cancellation.py +++ b/tests/managers/test_cancellation.py @@ -6,8 +6,6 @@ points in the agent execution flow, covering all the issues documented in CANCEL """ import asyncio -from typing import AsyncGenerator -from unittest.mock import AsyncMock, MagicMock, patch import pytest @@ -19,8 +17,7 @@ from letta.schemas.enums import MessageRole, RunStatus from letta.schemas.letta_request import LettaStreamingRequest from letta.schemas.llm_config import LLMConfig from letta.schemas.message import MessageCreate -from letta.schemas.model import ModelSettings -from letta.schemas.run import Run as PydanticRun, RunUpdate +from letta.schemas.run import Run as PydanticRun from letta.server.server import SyncServer from letta.services.streaming_service import StreamingService diff --git a/tests/managers/test_file_manager.py b/tests/managers/test_file_manager.py index 7a1284b8..e77fbe75 100644 --- a/tests/managers/test_file_manager.py +++ b/tests/managers/test_file_manager.py @@ -1,106 +1,15 @@ import asyncio -import json -import logging -import os -import random -import re -import string import time -import uuid -from datetime import datetime, timedelta, timezone -from typing import List -from unittest.mock import AsyncMock, Mock, patch import pytest -from _pytest.python_api import approx -from anthropic.types.beta import BetaMessage -from anthropic.types.beta.messages import BetaMessageBatchIndividualResponse, BetaMessageBatchSucceededResult # Import shared fixtures and constants from conftest from conftest import ( CREATE_DELAY_SQLITE, - DEFAULT_EMBEDDING_CONFIG, USING_SQLITE, ) -from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction -from sqlalchemy import func, select -from sqlalchemy.exc import IntegrityError, InvalidRequestError -from sqlalchemy.orm.exc import StaleDataError -from letta.config import LettaConfig -from letta.constants import ( - BASE_MEMORY_TOOLS, - BASE_SLEEPTIME_TOOLS, - BASE_TOOLS, - BASE_VOICE_SLEEPTIME_CHAT_TOOLS, - BASE_VOICE_SLEEPTIME_TOOLS, - BUILTIN_TOOLS, - DEFAULT_ORG_ID, - DEFAULT_ORG_NAME, - FILES_TOOLS, - LETTA_TOOL_EXECUTION_DIR, - LETTA_TOOL_SET, - LOCAL_ONLY_MULTI_AGENT_TOOLS, - MCP_TOOL_TAG_NAME_PREFIX, - MULTI_AGENT_TOOLS, -) -from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client -from letta.errors import LettaAgentNotFoundError -from letta.functions.functions import derive_openai_json_schema, parse_source_code -from letta.functions.mcp_client.types import MCPTool -from letta.helpers import ToolRulesSolver -from letta.helpers.datetime_helpers import AsyncTimer -from letta.jobs.types import ItemUpdateInfo, RequestStatusUpdateInfo, StepStatusUpdateInfo -from letta.orm import Base, Block -from letta.orm.block_history import BlockHistory -from letta.orm.errors import NoResultFound, UniqueConstraintViolationError -from letta.orm.file import FileContent as FileContentModel, FileMetadata as FileMetadataModel -from letta.schemas.agent import CreateAgent, UpdateAgent -from letta.schemas.block import Block as PydanticBlock, BlockUpdate, CreateBlock -from letta.schemas.embedding_config import EmbeddingConfig -from letta.schemas.enums import ( - ActorType, - AgentStepStatus, - FileProcessingStatus, - JobStatus, - JobType, - MessageRole, - ProviderType, - SandboxType, - StepStatus, - TagMatchMode, - ToolType, - VectorDBProvider, -) -from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate -from letta.schemas.file import FileMetadata, FileMetadata as PydanticFileMetadata -from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityPropertyType, IdentityType, IdentityUpdate, IdentityUpsert -from letta.schemas.job import BatchJob, Job, Job as PydanticJob, JobUpdate, LettaRequestConfig -from letta.schemas.letta_message import UpdateAssistantMessage, UpdateReasoningMessage, UpdateSystemMessage, UpdateUserMessage -from letta.schemas.letta_message_content import TextContent -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.llm_batch_job import AgentStepState, LLMBatchItem -from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import Message as PydanticMessage, MessageCreate, MessageUpdate -from letta.schemas.openai.chat_completion_response import UsageStatistics -from letta.schemas.organization import Organization, Organization as PydanticOrganization, OrganizationUpdate -from letta.schemas.passage import Passage as PydanticPassage -from letta.schemas.pip_requirement import PipRequirement -from letta.schemas.run import Run as PydanticRun -from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfigCreate, SandboxConfigUpdate -from letta.schemas.source import Source as PydanticSource, SourceUpdate -from letta.schemas.tool import Tool as PydanticTool, ToolCreate, ToolUpdate -from letta.schemas.tool_rule import InitToolRule -from letta.schemas.user import User as PydanticUser, UserUpdate -from letta.server.db import db_registry -from letta.server.server import SyncServer -from letta.services.block_manager import BlockManager -from letta.services.helpers.agent_manager_helper import calculate_base_tools, calculate_multi_agent_tools, validate_agent_exists_async -from letta.services.step_manager import FeedbackType -from letta.settings import settings, tool_settings -from letta.utils import calculate_file_defaults_based_on_context_window -from tests.helpers.utils import comprehensive_agent_checks, validate_context_window_overview -from tests.utils import random_string +from letta.schemas.file import FileMetadata as PydanticFileMetadata # ====================================================================================================================== # FileAgent Tests diff --git a/tests/managers/test_group_manager.py b/tests/managers/test_group_manager.py index 47a94df3..d6f59d5a 100644 --- a/tests/managers/test_group_manager.py +++ b/tests/managers/test_group_manager.py @@ -1,105 +1,9 @@ -import json -import logging -import os -import random -import re -import string -import time -import uuid -from datetime import datetime, timedelta, timezone -from typing import List -from unittest.mock import AsyncMock, Mock, patch - import pytest -from _pytest.python_api import approx -from anthropic.types.beta import BetaMessage -from anthropic.types.beta.messages import BetaMessageBatchIndividualResponse, BetaMessageBatchSucceededResult # Import shared fixtures and constants from conftest -from conftest import ( - CREATE_DELAY_SQLITE, - DEFAULT_EMBEDDING_CONFIG, - USING_SQLITE, -) -from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction -from sqlalchemy import func, select -from sqlalchemy.exc import IntegrityError, InvalidRequestError -from sqlalchemy.orm.exc import StaleDataError - -from letta.config import LettaConfig -from letta.constants import ( - BASE_MEMORY_TOOLS, - BASE_SLEEPTIME_TOOLS, - BASE_TOOLS, - BASE_VOICE_SLEEPTIME_CHAT_TOOLS, - BASE_VOICE_SLEEPTIME_TOOLS, - BUILTIN_TOOLS, - DEFAULT_ORG_ID, - DEFAULT_ORG_NAME, - FILES_TOOLS, - LETTA_TOOL_EXECUTION_DIR, - LETTA_TOOL_SET, - LOCAL_ONLY_MULTI_AGENT_TOOLS, - MCP_TOOL_TAG_NAME_PREFIX, - MULTI_AGENT_TOOLS, -) -from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client -from letta.errors import LettaAgentNotFoundError -from letta.functions.functions import derive_openai_json_schema, parse_source_code -from letta.functions.mcp_client.types import MCPTool -from letta.helpers import ToolRulesSolver -from letta.helpers.datetime_helpers import AsyncTimer -from letta.jobs.types import ItemUpdateInfo, RequestStatusUpdateInfo, StepStatusUpdateInfo -from letta.orm import Base, Block -from letta.orm.block_history import BlockHistory -from letta.orm.errors import NoResultFound, UniqueConstraintViolationError -from letta.orm.file import FileContent as FileContentModel, FileMetadata as FileMetadataModel -from letta.schemas.agent import CreateAgent, UpdateAgent -from letta.schemas.block import Block as PydanticBlock, BlockUpdate, CreateBlock from letta.schemas.embedding_config import EmbeddingConfig -from letta.schemas.enums import ( - ActorType, - AgentStepStatus, - FileProcessingStatus, - JobStatus, - JobType, - MessageRole, - ProviderType, - SandboxType, - StepStatus, - TagMatchMode, - ToolType, - VectorDBProvider, -) -from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate -from letta.schemas.file import FileMetadata, FileMetadata as PydanticFileMetadata -from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityPropertyType, IdentityType, IdentityUpdate, IdentityUpsert -from letta.schemas.job import BatchJob, Job, Job as PydanticJob, JobUpdate, LettaRequestConfig -from letta.schemas.letta_message import UpdateAssistantMessage, UpdateReasoningMessage, UpdateSystemMessage, UpdateUserMessage -from letta.schemas.letta_message_content import TextContent -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.llm_batch_job import AgentStepState, LLMBatchItem from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import Message as PydanticMessage, MessageCreate, MessageUpdate -from letta.schemas.openai.chat_completion_response import UsageStatistics -from letta.schemas.organization import Organization, Organization as PydanticOrganization, OrganizationUpdate -from letta.schemas.passage import Passage as PydanticPassage -from letta.schemas.pip_requirement import PipRequirement -from letta.schemas.run import Run as PydanticRun -from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfigCreate, SandboxConfigUpdate -from letta.schemas.source import Source as PydanticSource, SourceUpdate -from letta.schemas.tool import Tool as PydanticTool, ToolCreate, ToolUpdate -from letta.schemas.tool_rule import InitToolRule -from letta.schemas.user import User as PydanticUser, UserUpdate -from letta.server.db import db_registry from letta.server.server import SyncServer -from letta.services.block_manager import BlockManager -from letta.services.helpers.agent_manager_helper import calculate_base_tools, calculate_multi_agent_tools, validate_agent_exists_async -from letta.services.step_manager import FeedbackType -from letta.settings import settings, tool_settings -from letta.utils import calculate_file_defaults_based_on_context_window -from tests.helpers.utils import comprehensive_agent_checks, validate_context_window_overview -from tests.utils import random_string @pytest.mark.asyncio diff --git a/tests/managers/test_identity_manager.py b/tests/managers/test_identity_manager.py index 9534e306..278cc151 100644 --- a/tests/managers/test_identity_manager.py +++ b/tests/managers/test_identity_manager.py @@ -1,105 +1,14 @@ -import json -import logging -import os -import random -import re -import string -import time -import uuid -from datetime import datetime, timedelta, timezone -from typing import List -from unittest.mock import AsyncMock, Mock, patch - import pytest -from _pytest.python_api import approx -from anthropic.types.beta import BetaMessage -from anthropic.types.beta.messages import BetaMessageBatchIndividualResponse, BetaMessageBatchSucceededResult # Import shared fixtures and constants from conftest -from conftest import ( - CREATE_DELAY_SQLITE, - DEFAULT_EMBEDDING_CONFIG, - USING_SQLITE, -) -from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction -from sqlalchemy import func, select -from sqlalchemy.exc import IntegrityError, InvalidRequestError -from sqlalchemy.orm.exc import StaleDataError - -from letta.config import LettaConfig -from letta.constants import ( - BASE_MEMORY_TOOLS, - BASE_SLEEPTIME_TOOLS, - BASE_TOOLS, - BASE_VOICE_SLEEPTIME_CHAT_TOOLS, - BASE_VOICE_SLEEPTIME_TOOLS, - BUILTIN_TOOLS, - DEFAULT_ORG_ID, - DEFAULT_ORG_NAME, - FILES_TOOLS, - LETTA_TOOL_EXECUTION_DIR, - LETTA_TOOL_SET, - LOCAL_ONLY_MULTI_AGENT_TOOLS, - MCP_TOOL_TAG_NAME_PREFIX, - MULTI_AGENT_TOOLS, -) -from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client -from letta.errors import LettaAgentNotFoundError -from letta.functions.functions import derive_openai_json_schema, parse_source_code -from letta.functions.mcp_client.types import MCPTool -from letta.helpers import ToolRulesSolver -from letta.helpers.datetime_helpers import AsyncTimer -from letta.jobs.types import ItemUpdateInfo, RequestStatusUpdateInfo, StepStatusUpdateInfo -from letta.orm import Base, Block -from letta.orm.block_history import BlockHistory -from letta.orm.errors import NoResultFound, UniqueConstraintViolationError -from letta.orm.file import FileContent as FileContentModel, FileMetadata as FileMetadataModel +from letta.orm.errors import UniqueConstraintViolationError from letta.schemas.agent import CreateAgent, UpdateAgent -from letta.schemas.block import Block as PydanticBlock, BlockUpdate, CreateBlock +from letta.schemas.block import Block as PydanticBlock from letta.schemas.embedding_config import EmbeddingConfig -from letta.schemas.enums import ( - ActorType, - AgentStepStatus, - FileProcessingStatus, - JobStatus, - JobType, - MessageRole, - ProviderType, - SandboxType, - StepStatus, - TagMatchMode, - ToolType, - VectorDBProvider, -) -from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate -from letta.schemas.file import FileMetadata, FileMetadata as PydanticFileMetadata from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityPropertyType, IdentityType, IdentityUpdate, IdentityUpsert -from letta.schemas.job import BatchJob, Job, Job as PydanticJob, JobUpdate, LettaRequestConfig -from letta.schemas.letta_message import UpdateAssistantMessage, UpdateReasoningMessage, UpdateSystemMessage, UpdateUserMessage -from letta.schemas.letta_message_content import TextContent -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.llm_batch_job import AgentStepState, LLMBatchItem from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import Message as PydanticMessage, MessageCreate, MessageUpdate -from letta.schemas.openai.chat_completion_response import UsageStatistics -from letta.schemas.organization import Organization, Organization as PydanticOrganization, OrganizationUpdate -from letta.schemas.passage import Passage as PydanticPassage -from letta.schemas.pip_requirement import PipRequirement -from letta.schemas.run import Run as PydanticRun -from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfigCreate, SandboxConfigUpdate -from letta.schemas.source import Source as PydanticSource, SourceUpdate -from letta.schemas.tool import Tool as PydanticTool, ToolCreate, ToolUpdate -from letta.schemas.tool_rule import InitToolRule -from letta.schemas.user import User as PydanticUser, UserUpdate -from letta.server.db import db_registry from letta.server.server import SyncServer from letta.services.block_manager import BlockManager -from letta.services.helpers.agent_manager_helper import calculate_base_tools, calculate_multi_agent_tools, validate_agent_exists_async -from letta.services.step_manager import FeedbackType -from letta.settings import settings, tool_settings -from letta.utils import calculate_file_defaults_based_on_context_window -from tests.helpers.utils import comprehensive_agent_checks, validate_context_window_overview -from tests.utils import random_string # ====================================================================================================================== # Identity Manager Tests diff --git a/tests/managers/test_job_manager.py b/tests/managers/test_job_manager.py index 9c0d8d59..aa55a0cb 100644 --- a/tests/managers/test_job_manager.py +++ b/tests/managers/test_job_manager.py @@ -1,105 +1,15 @@ -import json -import logging -import os -import random -import re -import string -import time -import uuid -from datetime import datetime, timedelta, timezone -from typing import List -from unittest.mock import AsyncMock, Mock, patch +from datetime import datetime import pytest -from _pytest.python_api import approx -from anthropic.types.beta import BetaMessage -from anthropic.types.beta.messages import BetaMessageBatchIndividualResponse, BetaMessageBatchSucceededResult # Import shared fixtures and constants from conftest -from conftest import ( - CREATE_DELAY_SQLITE, - DEFAULT_EMBEDDING_CONFIG, - USING_SQLITE, -) -from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction -from sqlalchemy import func, select -from sqlalchemy.exc import IntegrityError, InvalidRequestError -from sqlalchemy.orm.exc import StaleDataError - -from letta.config import LettaConfig -from letta.constants import ( - BASE_MEMORY_TOOLS, - BASE_SLEEPTIME_TOOLS, - BASE_TOOLS, - BASE_VOICE_SLEEPTIME_CHAT_TOOLS, - BASE_VOICE_SLEEPTIME_TOOLS, - BUILTIN_TOOLS, - DEFAULT_ORG_ID, - DEFAULT_ORG_NAME, - FILES_TOOLS, - LETTA_TOOL_EXECUTION_DIR, - LETTA_TOOL_SET, - LOCAL_ONLY_MULTI_AGENT_TOOLS, - MCP_TOOL_TAG_NAME_PREFIX, - MULTI_AGENT_TOOLS, -) -from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client -from letta.errors import LettaAgentNotFoundError, LettaInvalidArgumentError -from letta.functions.functions import derive_openai_json_schema, parse_source_code -from letta.functions.mcp_client.types import MCPTool -from letta.helpers import ToolRulesSolver -from letta.helpers.datetime_helpers import AsyncTimer -from letta.jobs.types import ItemUpdateInfo, RequestStatusUpdateInfo, StepStatusUpdateInfo -from letta.orm import Base, Block -from letta.orm.block_history import BlockHistory -from letta.orm.errors import NoResultFound, UniqueConstraintViolationError -from letta.orm.file import FileContent as FileContentModel, FileMetadata as FileMetadataModel -from letta.schemas.agent import CreateAgent, UpdateAgent -from letta.schemas.block import Block as PydanticBlock, BlockUpdate, CreateBlock -from letta.schemas.embedding_config import EmbeddingConfig +from letta.errors import LettaInvalidArgumentError from letta.schemas.enums import ( - ActorType, - AgentStepStatus, - FileProcessingStatus, JobStatus, JobType, - MessageRole, - ProviderType, - SandboxType, - StepStatus, - TagMatchMode, - ToolType, - VectorDBProvider, ) -from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate -from letta.schemas.file import FileMetadata, FileMetadata as PydanticFileMetadata -from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityPropertyType, IdentityType, IdentityUpdate, IdentityUpsert -from letta.schemas.job import BatchJob, Job, Job as PydanticJob, JobUpdate, LettaRequestConfig -from letta.schemas.letta_message import UpdateAssistantMessage, UpdateReasoningMessage, UpdateSystemMessage, UpdateUserMessage -from letta.schemas.letta_message_content import TextContent -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.llm_batch_job import AgentStepState, LLMBatchItem -from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import Message as PydanticMessage, MessageCreate, MessageUpdate -from letta.schemas.openai.chat_completion_response import UsageStatistics -from letta.schemas.organization import Organization, Organization as PydanticOrganization, OrganizationUpdate -from letta.schemas.passage import Passage as PydanticPassage -from letta.schemas.pip_requirement import PipRequirement -from letta.schemas.run import Run as PydanticRun -from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfigCreate, SandboxConfigUpdate -from letta.schemas.source import Source as PydanticSource, SourceUpdate -from letta.schemas.tool import Tool as PydanticTool, ToolCreate, ToolUpdate -from letta.schemas.tool_rule import InitToolRule -from letta.schemas.user import User as PydanticUser, UserUpdate -from letta.server.db import db_registry +from letta.schemas.job import Job as PydanticJob, JobUpdate from letta.server.server import SyncServer -from letta.services.block_manager import BlockManager -from letta.services.helpers.agent_manager_helper import calculate_base_tools, calculate_multi_agent_tools, validate_agent_exists_async -from letta.services.step_manager import FeedbackType -from letta.settings import settings, tool_settings -from letta.utils import calculate_file_defaults_based_on_context_window -from tests.helpers.utils import comprehensive_agent_checks, validate_context_window_overview -from tests.utils import random_string # ====================================================================================================================== # JobManager Tests diff --git a/tests/managers/test_mcp_manager.py b/tests/managers/test_mcp_manager.py index ba36820d..9eab0dc3 100644 --- a/tests/managers/test_mcp_manager.py +++ b/tests/managers/test_mcp_manager.py @@ -1,105 +1,18 @@ -import json -import logging -import os -import random -import re -import string -import time import uuid -from datetime import datetime, timedelta, timezone -from typing import List -from unittest.mock import AsyncMock, Mock, patch +from unittest.mock import AsyncMock, patch import pytest -from _pytest.python_api import approx -from anthropic.types.beta import BetaMessage -from anthropic.types.beta.messages import BetaMessageBatchIndividualResponse, BetaMessageBatchSucceededResult # Import shared fixtures and constants from conftest -from conftest import ( - CREATE_DELAY_SQLITE, - DEFAULT_EMBEDDING_CONFIG, - USING_SQLITE, -) -from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction -from sqlalchemy import func, select -from sqlalchemy.exc import IntegrityError, InvalidRequestError -from sqlalchemy.orm.exc import StaleDataError - -from letta.config import LettaConfig from letta.constants import ( - BASE_MEMORY_TOOLS, - BASE_SLEEPTIME_TOOLS, - BASE_TOOLS, - BASE_VOICE_SLEEPTIME_CHAT_TOOLS, - BASE_VOICE_SLEEPTIME_TOOLS, - BUILTIN_TOOLS, - DEFAULT_ORG_ID, - DEFAULT_ORG_NAME, - FILES_TOOLS, - LETTA_TOOL_EXECUTION_DIR, - LETTA_TOOL_SET, - LOCAL_ONLY_MULTI_AGENT_TOOLS, MCP_TOOL_TAG_NAME_PREFIX, - MULTI_AGENT_TOOLS, ) -from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client -from letta.errors import LettaAgentNotFoundError -from letta.functions.functions import derive_openai_json_schema, parse_source_code from letta.functions.mcp_client.types import MCPTool -from letta.helpers import ToolRulesSolver -from letta.helpers.datetime_helpers import AsyncTimer -from letta.jobs.types import ItemUpdateInfo, RequestStatusUpdateInfo, StepStatusUpdateInfo -from letta.orm import Base, Block -from letta.orm.block_history import BlockHistory -from letta.orm.errors import NoResultFound, UniqueConstraintViolationError -from letta.orm.file import FileContent as FileContentModel, FileMetadata as FileMetadataModel -from letta.schemas.agent import CreateAgent, UpdateAgent -from letta.schemas.block import Block as PydanticBlock, BlockUpdate, CreateBlock -from letta.schemas.embedding_config import EmbeddingConfig from letta.schemas.enums import ( - ActorType, - AgentStepStatus, - FileProcessingStatus, - JobStatus, - JobType, - MessageRole, - ProviderType, - SandboxType, - StepStatus, - TagMatchMode, ToolType, - VectorDBProvider, ) -from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate -from letta.schemas.file import FileMetadata, FileMetadata as PydanticFileMetadata -from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityPropertyType, IdentityType, IdentityUpdate, IdentityUpsert -from letta.schemas.job import BatchJob, Job, Job as PydanticJob, JobUpdate, LettaRequestConfig -from letta.schemas.letta_message import UpdateAssistantMessage, UpdateReasoningMessage, UpdateSystemMessage, UpdateUserMessage -from letta.schemas.letta_message_content import TextContent -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.llm_batch_job import AgentStepState, LLMBatchItem -from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import Message as PydanticMessage, MessageCreate, MessageUpdate -from letta.schemas.openai.chat_completion_response import UsageStatistics -from letta.schemas.organization import Organization, Organization as PydanticOrganization, OrganizationUpdate -from letta.schemas.passage import Passage as PydanticPassage -from letta.schemas.pip_requirement import PipRequirement -from letta.schemas.run import Run as PydanticRun -from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfigCreate, SandboxConfigUpdate -from letta.schemas.source import Source as PydanticSource, SourceUpdate -from letta.schemas.tool import Tool as PydanticTool, ToolCreate, ToolUpdate -from letta.schemas.tool_rule import InitToolRule -from letta.schemas.user import User as PydanticUser, UserUpdate from letta.server.db import db_registry -from letta.server.server import SyncServer -from letta.services.block_manager import BlockManager -from letta.services.helpers.agent_manager_helper import calculate_base_tools, calculate_multi_agent_tools, validate_agent_exists_async -from letta.services.step_manager import FeedbackType -from letta.settings import settings, tool_settings -from letta.utils import calculate_file_defaults_based_on_context_window -from tests.helpers.utils import comprehensive_agent_checks, validate_context_window_overview -from tests.utils import random_string +from letta.settings import settings # ====================================================================================================================== # MCPManager Tests @@ -200,7 +113,7 @@ async def test_create_mcp_server(mock_get_client, server, default_user): async def test_create_mcp_server_with_tools(mock_get_client, server, default_user): """Test that creating an MCP server automatically syncs and persists its tools.""" from letta.functions.mcp_client.types import MCPToolHealth - from letta.schemas.mcp import MCPServer, MCPServerType, SSEServerConfig + from letta.schemas.mcp import MCPServer, MCPServerType from letta.settings import tool_settings if tool_settings.mcp_read_from_config: @@ -795,7 +708,7 @@ async def test_mcp_server_delete_removes_all_sessions_for_url_and_user(server, d @pytest.mark.asyncio async def test_mcp_server_resync_tools(server, default_user, default_organization): """Test that resyncing MCP server tools correctly handles added, deleted, and updated tools.""" - from unittest.mock import AsyncMock, MagicMock, patch + from unittest.mock import AsyncMock, patch from letta.functions.mcp_client.types import MCPTool, MCPToolHealth from letta.schemas.mcp import MCPServer as PydanticMCPServer, MCPServerType diff --git a/tests/managers/test_message_manager.py b/tests/managers/test_message_manager.py index 8bea9347..593c64d4 100644 --- a/tests/managers/test_message_manager.py +++ b/tests/managers/test_message_manager.py @@ -1,105 +1,18 @@ -import json -import logging -import os -import random -import re -import string -import time import uuid -from datetime import datetime, timedelta, timezone -from typing import List -from unittest.mock import AsyncMock, Mock, patch import pytest -from _pytest.python_api import approx -from anthropic.types.beta import BetaMessage -from anthropic.types.beta.messages import BetaMessageBatchIndividualResponse, BetaMessageBatchSucceededResult # Import shared fixtures and constants from conftest -from conftest import ( - CREATE_DELAY_SQLITE, - DEFAULT_EMBEDDING_CONFIG, - USING_SQLITE, -) from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction -from sqlalchemy import func, select -from sqlalchemy.exc import IntegrityError, InvalidRequestError -from sqlalchemy.orm.exc import StaleDataError -from letta.config import LettaConfig -from letta.constants import ( - BASE_MEMORY_TOOLS, - BASE_SLEEPTIME_TOOLS, - BASE_TOOLS, - BASE_VOICE_SLEEPTIME_CHAT_TOOLS, - BASE_VOICE_SLEEPTIME_TOOLS, - BUILTIN_TOOLS, - DEFAULT_ORG_ID, - DEFAULT_ORG_NAME, - FILES_TOOLS, - LETTA_TOOL_EXECUTION_DIR, - LETTA_TOOL_SET, - LOCAL_ONLY_MULTI_AGENT_TOOLS, - MCP_TOOL_TAG_NAME_PREFIX, - MULTI_AGENT_TOOLS, -) -from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client -from letta.errors import LettaAgentNotFoundError -from letta.functions.functions import derive_openai_json_schema, parse_source_code -from letta.functions.mcp_client.types import MCPTool -from letta.helpers import ToolRulesSolver -from letta.helpers.datetime_helpers import AsyncTimer -from letta.jobs.types import ItemUpdateInfo, RequestStatusUpdateInfo, StepStatusUpdateInfo -from letta.orm import Base, Block -from letta.orm.block_history import BlockHistory -from letta.orm.errors import NoResultFound, UniqueConstraintViolationError -from letta.orm.file import FileContent as FileContentModel, FileMetadata as FileMetadataModel -from letta.schemas.agent import CreateAgent, UpdateAgent -from letta.schemas.block import Block as PydanticBlock, BlockUpdate, CreateBlock -from letta.schemas.embedding_config import EmbeddingConfig +from letta.orm.errors import UniqueConstraintViolationError from letta.schemas.enums import ( - ActorType, - AgentStepStatus, - FileProcessingStatus, - JobStatus, - JobType, MessageRole, - ProviderType, - SandboxType, - StepStatus, - TagMatchMode, - ToolType, - VectorDBProvider, ) -from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate -from letta.schemas.file import FileMetadata, FileMetadata as PydanticFileMetadata -from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityPropertyType, IdentityType, IdentityUpdate, IdentityUpsert -from letta.schemas.job import BatchJob, Job, Job as PydanticJob, JobUpdate, LettaRequestConfig from letta.schemas.letta_message import UpdateAssistantMessage, UpdateReasoningMessage, UpdateSystemMessage, UpdateUserMessage from letta.schemas.letta_message_content import TextContent -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.llm_batch_job import AgentStepState, LLMBatchItem -from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import Message as PydanticMessage, MessageCreate, MessageUpdate -from letta.schemas.openai.chat_completion_response import UsageStatistics -from letta.schemas.organization import Organization, Organization as PydanticOrganization, OrganizationUpdate -from letta.schemas.passage import Passage as PydanticPassage -from letta.schemas.pip_requirement import PipRequirement -from letta.schemas.run import Run as PydanticRun -from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfigCreate, SandboxConfigUpdate -from letta.schemas.source import Source as PydanticSource, SourceUpdate -from letta.schemas.tool import Tool as PydanticTool, ToolCreate, ToolUpdate -from letta.schemas.tool_rule import InitToolRule -from letta.schemas.user import User as PydanticUser, UserUpdate -from letta.server.db import db_registry +from letta.schemas.message import Message as PydanticMessage, MessageUpdate from letta.server.server import SyncServer -from letta.services.block_manager import BlockManager -from letta.services.helpers.agent_manager_helper import calculate_base_tools, calculate_multi_agent_tools, validate_agent_exists_async -from letta.services.step_manager import FeedbackType -from letta.settings import settings, tool_settings -from letta.utils import calculate_file_defaults_based_on_context_window -from tests.helpers.utils import comprehensive_agent_checks, validate_context_window_overview -from tests.utils import random_string # ====================================================================================================================== # AgentManager Tests - Messages Relationship @@ -849,7 +762,6 @@ async def test_create_many_messages_async_with_turbopuffer(server: SyncServer, s @pytest.mark.asyncio async def test_convert_tool_call_messages_no_assistant_mode(server: SyncServer, sarah_agent, default_user): """Test that when assistant mode is off, all tool calls go into a single ToolCallMessage""" - from letta.schemas.letta_message import ToolCall # create a message with multiple tool calls tool_calls = [ diff --git a/tests/managers/test_organization_manager.py b/tests/managers/test_organization_manager.py index d21d7f9e..0178ee12 100644 --- a/tests/managers/test_organization_manager.py +++ b/tests/managers/test_organization_manager.py @@ -1,105 +1,11 @@ -import json -import logging -import os -import random -import re -import string -import time -import uuid -from datetime import datetime, timedelta, timezone -from typing import List -from unittest.mock import AsyncMock, Mock, patch - import pytest -from _pytest.python_api import approx -from anthropic.types.beta import BetaMessage -from anthropic.types.beta.messages import BetaMessageBatchIndividualResponse, BetaMessageBatchSucceededResult # Import shared fixtures and constants from conftest -from conftest import ( - CREATE_DELAY_SQLITE, - DEFAULT_EMBEDDING_CONFIG, - USING_SQLITE, -) -from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction -from sqlalchemy import func, select -from sqlalchemy.exc import IntegrityError, InvalidRequestError -from sqlalchemy.orm.exc import StaleDataError - -from letta.config import LettaConfig from letta.constants import ( - BASE_MEMORY_TOOLS, - BASE_SLEEPTIME_TOOLS, - BASE_TOOLS, - BASE_VOICE_SLEEPTIME_CHAT_TOOLS, - BASE_VOICE_SLEEPTIME_TOOLS, - BUILTIN_TOOLS, - DEFAULT_ORG_ID, DEFAULT_ORG_NAME, - FILES_TOOLS, - LETTA_TOOL_EXECUTION_DIR, - LETTA_TOOL_SET, - LOCAL_ONLY_MULTI_AGENT_TOOLS, - MCP_TOOL_TAG_NAME_PREFIX, - MULTI_AGENT_TOOLS, ) -from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client -from letta.errors import LettaAgentNotFoundError -from letta.functions.functions import derive_openai_json_schema, parse_source_code -from letta.functions.mcp_client.types import MCPTool -from letta.helpers import ToolRulesSolver -from letta.helpers.datetime_helpers import AsyncTimer -from letta.jobs.types import ItemUpdateInfo, RequestStatusUpdateInfo, StepStatusUpdateInfo -from letta.orm import Base, Block -from letta.orm.block_history import BlockHistory -from letta.orm.errors import NoResultFound, UniqueConstraintViolationError -from letta.orm.file import FileContent as FileContentModel, FileMetadata as FileMetadataModel -from letta.schemas.agent import CreateAgent, UpdateAgent -from letta.schemas.block import Block as PydanticBlock, BlockUpdate, CreateBlock -from letta.schemas.embedding_config import EmbeddingConfig -from letta.schemas.enums import ( - ActorType, - AgentStepStatus, - FileProcessingStatus, - JobStatus, - JobType, - MessageRole, - ProviderType, - SandboxType, - StepStatus, - TagMatchMode, - ToolType, - VectorDBProvider, -) -from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate -from letta.schemas.file import FileMetadata, FileMetadata as PydanticFileMetadata -from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityPropertyType, IdentityType, IdentityUpdate, IdentityUpsert -from letta.schemas.job import BatchJob, Job, Job as PydanticJob, JobUpdate, LettaRequestConfig -from letta.schemas.letta_message import UpdateAssistantMessage, UpdateReasoningMessage, UpdateSystemMessage, UpdateUserMessage -from letta.schemas.letta_message_content import TextContent -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.llm_batch_job import AgentStepState, LLMBatchItem -from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import Message as PydanticMessage, MessageCreate, MessageUpdate -from letta.schemas.openai.chat_completion_response import UsageStatistics -from letta.schemas.organization import Organization, Organization as PydanticOrganization, OrganizationUpdate -from letta.schemas.passage import Passage as PydanticPassage -from letta.schemas.pip_requirement import PipRequirement -from letta.schemas.run import Run as PydanticRun -from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfigCreate, SandboxConfigUpdate -from letta.schemas.source import Source as PydanticSource, SourceUpdate -from letta.schemas.tool import Tool as PydanticTool, ToolCreate, ToolUpdate -from letta.schemas.tool_rule import InitToolRule -from letta.schemas.user import User as PydanticUser, UserUpdate -from letta.server.db import db_registry +from letta.schemas.organization import Organization as PydanticOrganization, OrganizationUpdate from letta.server.server import SyncServer -from letta.services.block_manager import BlockManager -from letta.services.helpers.agent_manager_helper import calculate_base_tools, calculate_multi_agent_tools, validate_agent_exists_async -from letta.services.step_manager import FeedbackType -from letta.settings import settings, tool_settings -from letta.utils import calculate_file_defaults_based_on_context_window -from tests.helpers.utils import comprehensive_agent_checks, validate_context_window_overview -from tests.utils import random_string # ====================================================================================================================== diff --git a/tests/managers/test_passage_manager.py b/tests/managers/test_passage_manager.py index e5020dea..d763995d 100644 --- a/tests/managers/test_passage_manager.py +++ b/tests/managers/test_passage_manager.py @@ -1,105 +1,22 @@ import json -import logging import os -import random -import re -import string -import time -import uuid from datetime import datetime, timedelta, timezone -from typing import List -from unittest.mock import AsyncMock, Mock, patch +from unittest.mock import Mock import pytest from _pytest.python_api import approx -from anthropic.types.beta import BetaMessage -from anthropic.types.beta.messages import BetaMessageBatchIndividualResponse, BetaMessageBatchSucceededResult # Import shared fixtures and constants from conftest from conftest import ( - CREATE_DELAY_SQLITE, DEFAULT_EMBEDDING_CONFIG, - USING_SQLITE, ) -from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction -from sqlalchemy import func, select -from sqlalchemy.exc import IntegrityError, InvalidRequestError -from sqlalchemy.orm.exc import StaleDataError -from letta.config import LettaConfig -from letta.constants import ( - BASE_MEMORY_TOOLS, - BASE_SLEEPTIME_TOOLS, - BASE_TOOLS, - BASE_VOICE_SLEEPTIME_CHAT_TOOLS, - BASE_VOICE_SLEEPTIME_TOOLS, - BUILTIN_TOOLS, - DEFAULT_ORG_ID, - DEFAULT_ORG_NAME, - FILES_TOOLS, - LETTA_TOOL_EXECUTION_DIR, - LETTA_TOOL_SET, - LOCAL_ONLY_MULTI_AGENT_TOOLS, - MCP_TOOL_TAG_NAME_PREFIX, - MULTI_AGENT_TOOLS, -) -from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client -from letta.errors import LettaAgentNotFoundError -from letta.functions.functions import derive_openai_json_schema, parse_source_code -from letta.functions.mcp_client.types import MCPTool -from letta.helpers import ToolRulesSolver -from letta.helpers.datetime_helpers import AsyncTimer -from letta.jobs.types import ItemUpdateInfo, RequestStatusUpdateInfo, StepStatusUpdateInfo -from letta.orm import Base, Block -from letta.orm.block_history import BlockHistory -from letta.orm.errors import NoResultFound, UniqueConstraintViolationError -from letta.orm.file import FileContent as FileContentModel, FileMetadata as FileMetadataModel -from letta.schemas.agent import AgentState, CreateAgent, UpdateAgent -from letta.schemas.block import Block as PydanticBlock, BlockUpdate, CreateBlock -from letta.schemas.embedding_config import EmbeddingConfig +from letta.orm.errors import NoResultFound from letta.schemas.enums import ( - ActorType, - AgentStepStatus, - FileProcessingStatus, - JobStatus, - JobType, - MessageRole, - ProviderType, - SandboxType, - StepStatus, TagMatchMode, - ToolType, - VectorDBProvider, ) -from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate -from letta.schemas.file import FileMetadata, FileMetadata as PydanticFileMetadata -from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityPropertyType, IdentityType, IdentityUpdate, IdentityUpsert -from letta.schemas.job import BatchJob, Job, Job as PydanticJob, JobUpdate, LettaRequestConfig -from letta.schemas.letta_message import UpdateAssistantMessage, UpdateReasoningMessage, UpdateSystemMessage, UpdateUserMessage -from letta.schemas.letta_message_content import TextContent -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.llm_batch_job import AgentStepState, LLMBatchItem -from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import Message as PydanticMessage, MessageCreate, MessageUpdate -from letta.schemas.openai.chat_completion_response import UsageStatistics -from letta.schemas.organization import Organization, Organization as PydanticOrganization, OrganizationUpdate from letta.schemas.passage import Passage as PydanticPassage -from letta.schemas.pip_requirement import PipRequirement -from letta.schemas.run import Run as PydanticRun -from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfigCreate, SandboxConfigUpdate -from letta.schemas.source import Source as PydanticSource, SourceUpdate -from letta.schemas.tool import Tool as PydanticTool, ToolCreate, ToolUpdate -from letta.schemas.tool_rule import InitToolRule -from letta.schemas.user import User as PydanticUser, UserUpdate -from letta.server.db import db_registry from letta.server.server import SyncServer -from letta.services.block_manager import BlockManager -from letta.services.helpers.agent_manager_helper import calculate_base_tools, calculate_multi_agent_tools, validate_agent_exists_async -from letta.services.step_manager import FeedbackType -from letta.settings import settings, tool_settings -from letta.utils import calculate_file_defaults_based_on_context_window -from tests.helpers.utils import comprehensive_agent_checks, validate_context_window_overview -from tests.utils import random_string # ====================================================================================================================== # Agent Manager - Passages Tests diff --git a/tests/managers/test_provider_manager.py b/tests/managers/test_provider_manager.py index fb135be8..d6ae398a 100644 --- a/tests/managers/test_provider_manager.py +++ b/tests/managers/test_provider_manager.py @@ -1,12 +1,10 @@ """Tests for ProviderManager encryption/decryption logic.""" -import os - import pytest from letta.orm.provider import Provider as ProviderModel from letta.schemas.enums import ProviderCategory, ProviderType -from letta.schemas.providers import Provider, ProviderCreate, ProviderUpdate +from letta.schemas.providers import ProviderCreate, ProviderUpdate from letta.schemas.secret import Secret from letta.server.db import db_registry from letta.services.organization_manager import OrganizationManager @@ -501,11 +499,7 @@ async def test_server_startup_syncs_base_providers(default_user, default_organiz 3. Models are properly persisted to the database with correct metadata 4. Models can be retrieved using handles """ - from unittest.mock import AsyncMock - from letta.schemas.embedding_config import EmbeddingConfig - from letta.schemas.llm_config import LLMConfig - from letta.schemas.providers import AnthropicProvider, OpenAIProvider from letta.server.server import SyncServer # Mock OpenAI API responses @@ -745,7 +739,7 @@ async def test_server_startup_handles_disabled_providers(default_user, default_o 2. BYOK providers that are no longer enabled are NOT deleted (user-created) 3. The sync process handles providers gracefully when API calls fail """ - from letta.schemas.providers import OpenAIProvider, ProviderCreate + from letta.schemas.providers import ProviderCreate from letta.server.server import SyncServer # First, manually create providers in the database @@ -833,7 +827,6 @@ async def test_server_startup_handles_api_errors_gracefully(default_user, defaul 2. Other providers can still sync successfully 3. The server startup completes without crashing """ - from letta.schemas.providers import AnthropicProvider, OpenAIProvider from letta.server.server import SyncServer # Mock OpenAI to fail diff --git a/tests/managers/test_run_manager.py b/tests/managers/test_run_manager.py index bde73cfb..94934799 100644 --- a/tests/managers/test_run_manager.py +++ b/tests/managers/test_run_manager.py @@ -1,105 +1,27 @@ -import json -import logging -import os -import random -import re -import string -import time import uuid from datetime import datetime, timedelta, timezone -from typing import List -from unittest.mock import AsyncMock, Mock, patch +from unittest.mock import AsyncMock, patch import pytest -from _pytest.python_api import approx -from anthropic.types.beta import BetaMessage -from anthropic.types.beta.messages import BetaMessageBatchIndividualResponse, BetaMessageBatchSucceededResult # Import shared fixtures and constants from conftest -from conftest import ( - CREATE_DELAY_SQLITE, - DEFAULT_EMBEDDING_CONFIG, - USING_SQLITE, -) from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction -from sqlalchemy import func, select -from sqlalchemy.exc import IntegrityError, InvalidRequestError -from sqlalchemy.orm.exc import StaleDataError -from letta.config import LettaConfig -from letta.constants import ( - BASE_MEMORY_TOOLS, - BASE_SLEEPTIME_TOOLS, - BASE_TOOLS, - BASE_VOICE_SLEEPTIME_CHAT_TOOLS, - BASE_VOICE_SLEEPTIME_TOOLS, - BUILTIN_TOOLS, - DEFAULT_ORG_ID, - DEFAULT_ORG_NAME, - FILES_TOOLS, - LETTA_TOOL_EXECUTION_DIR, - LETTA_TOOL_SET, - LOCAL_ONLY_MULTI_AGENT_TOOLS, - MCP_TOOL_TAG_NAME_PREFIX, - MULTI_AGENT_TOOLS, -) -from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client -from letta.errors import LettaAgentNotFoundError, LettaInvalidArgumentError -from letta.functions.functions import derive_openai_json_schema, parse_source_code -from letta.functions.mcp_client.types import MCPTool -from letta.helpers import ToolRulesSolver -from letta.helpers.datetime_helpers import AsyncTimer -from letta.jobs.types import ItemUpdateInfo, RequestStatusUpdateInfo, StepStatusUpdateInfo -from letta.orm import Base, Block -from letta.orm.block_history import BlockHistory -from letta.orm.errors import NoResultFound, UniqueConstraintViolationError -from letta.orm.file import FileContent as FileContentModel, FileMetadata as FileMetadataModel -from letta.schemas.agent import CreateAgent, UpdateAgent -from letta.schemas.block import Block as PydanticBlock, BlockUpdate, CreateBlock -from letta.schemas.embedding_config import EmbeddingConfig +from letta.errors import LettaInvalidArgumentError +from letta.orm.errors import NoResultFound from letta.schemas.enums import ( - ActorType, - AgentStepStatus, - FileProcessingStatus, - JobStatus, - JobType, MessageRole, - ProviderType, RunStatus, - SandboxType, - StepStatus, - TagMatchMode, - ToolType, - VectorDBProvider, ) -from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate -from letta.schemas.file import FileMetadata, FileMetadata as PydanticFileMetadata -from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityPropertyType, IdentityType, IdentityUpdate, IdentityUpsert -from letta.schemas.job import Job as PydanticJob, LettaRequestConfig -from letta.schemas.letta_message import UpdateAssistantMessage, UpdateReasoningMessage, UpdateSystemMessage, UpdateUserMessage +from letta.schemas.job import LettaRequestConfig from letta.schemas.letta_message_content import TextContent -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import Message, Message as PydanticMessage, MessageCreate, MessageUpdate, ToolReturn +from letta.schemas.letta_stop_reason import StopReasonType +from letta.schemas.message import Message, Message as PydanticMessage, ToolReturn from letta.schemas.openai.chat_completion_response import UsageStatistics -from letta.schemas.organization import Organization, Organization as PydanticOrganization, OrganizationUpdate -from letta.schemas.passage import Passage as PydanticPassage -from letta.schemas.pip_requirement import PipRequirement from letta.schemas.run import Run as PydanticRun, RunUpdate -from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfigCreate, SandboxConfigUpdate -from letta.schemas.source import Source as PydanticSource, SourceUpdate -from letta.schemas.tool import Tool as PydanticTool, ToolCreate, ToolUpdate -from letta.schemas.tool_rule import InitToolRule -from letta.schemas.user import User as PydanticUser, UserUpdate -from letta.server.db import db_registry +from letta.schemas.user import User as PydanticUser from letta.server.server import SyncServer -from letta.services.block_manager import BlockManager -from letta.services.helpers.agent_manager_helper import calculate_base_tools, calculate_multi_agent_tools, validate_agent_exists_async from letta.services.step_manager import FeedbackType -from letta.settings import settings, tool_settings -from letta.utils import calculate_file_defaults_based_on_context_window -from tests.helpers.utils import comprehensive_agent_checks, validate_context_window_overview -from tests.utils import random_string # ====================================================================================================================== # RunManager Tests diff --git a/tests/managers/test_sandbox_manager.py b/tests/managers/test_sandbox_manager.py index e52a7d78..f1f66af3 100644 --- a/tests/managers/test_sandbox_manager.py +++ b/tests/managers/test_sandbox_manager.py @@ -1,105 +1,23 @@ -import json -import logging -import os -import random -import re -import string import time -import uuid -from datetime import datetime, timedelta, timezone -from typing import List -from unittest.mock import AsyncMock, Mock, patch import pytest -from _pytest.python_api import approx -from anthropic.types.beta import BetaMessage -from anthropic.types.beta.messages import BetaMessageBatchIndividualResponse, BetaMessageBatchSucceededResult # Import shared fixtures and constants from conftest from conftest import ( CREATE_DELAY_SQLITE, - DEFAULT_EMBEDDING_CONFIG, USING_SQLITE, ) -from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction -from sqlalchemy import func, select -from sqlalchemy.exc import IntegrityError, InvalidRequestError -from sqlalchemy.orm.exc import StaleDataError -from letta.config import LettaConfig from letta.constants import ( - BASE_MEMORY_TOOLS, - BASE_SLEEPTIME_TOOLS, - BASE_TOOLS, - BASE_VOICE_SLEEPTIME_CHAT_TOOLS, - BASE_VOICE_SLEEPTIME_TOOLS, - BUILTIN_TOOLS, - DEFAULT_ORG_ID, - DEFAULT_ORG_NAME, - FILES_TOOLS, LETTA_TOOL_EXECUTION_DIR, - LETTA_TOOL_SET, - LOCAL_ONLY_MULTI_AGENT_TOOLS, - MCP_TOOL_TAG_NAME_PREFIX, - MULTI_AGENT_TOOLS, ) -from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client -from letta.errors import LettaAgentNotFoundError -from letta.functions.functions import derive_openai_json_schema, parse_source_code -from letta.functions.mcp_client.types import MCPTool -from letta.helpers import ToolRulesSolver -from letta.helpers.datetime_helpers import AsyncTimer -from letta.jobs.types import ItemUpdateInfo, RequestStatusUpdateInfo, StepStatusUpdateInfo -from letta.orm import Base, Block -from letta.orm.block_history import BlockHistory -from letta.orm.errors import NoResultFound, UniqueConstraintViolationError -from letta.orm.file import FileContent as FileContentModel, FileMetadata as FileMetadataModel -from letta.schemas.agent import CreateAgent, UpdateAgent -from letta.schemas.block import Block as PydanticBlock, BlockUpdate, CreateBlock -from letta.schemas.embedding_config import EmbeddingConfig from letta.schemas.enums import ( - ActorType, - AgentStepStatus, - FileProcessingStatus, - JobStatus, - JobType, - MessageRole, - ProviderType, SandboxType, - StepStatus, - TagMatchMode, - ToolType, - VectorDBProvider, ) from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate -from letta.schemas.file import FileMetadata, FileMetadata as PydanticFileMetadata -from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityPropertyType, IdentityType, IdentityUpdate, IdentityUpsert -from letta.schemas.job import BatchJob, Job, Job as PydanticJob, JobUpdate, LettaRequestConfig -from letta.schemas.letta_message import UpdateAssistantMessage, UpdateReasoningMessage, UpdateSystemMessage, UpdateUserMessage -from letta.schemas.letta_message_content import TextContent -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.llm_batch_job import AgentStepState, LLMBatchItem -from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import Message as PydanticMessage, MessageCreate, MessageUpdate -from letta.schemas.openai.chat_completion_response import UsageStatistics -from letta.schemas.organization import Organization, Organization as PydanticOrganization, OrganizationUpdate -from letta.schemas.passage import Passage as PydanticPassage -from letta.schemas.pip_requirement import PipRequirement -from letta.schemas.run import Run as PydanticRun from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfigCreate, SandboxConfigUpdate -from letta.schemas.source import Source as PydanticSource, SourceUpdate -from letta.schemas.tool import Tool as PydanticTool, ToolCreate, ToolUpdate -from letta.schemas.tool_rule import InitToolRule -from letta.schemas.user import User as PydanticUser, UserUpdate -from letta.server.db import db_registry from letta.server.server import SyncServer -from letta.services.block_manager import BlockManager -from letta.services.helpers.agent_manager_helper import calculate_base_tools, calculate_multi_agent_tools, validate_agent_exists_async -from letta.services.step_manager import FeedbackType -from letta.settings import settings, tool_settings -from letta.utils import calculate_file_defaults_based_on_context_window -from tests.helpers.utils import comprehensive_agent_checks, validate_context_window_overview -from tests.utils import random_string +from letta.settings import tool_settings # ====================================================================================================================== # SandboxConfigManager Tests - Sandbox Configs diff --git a/tests/managers/test_source_manager.py b/tests/managers/test_source_manager.py index b3a2418e..05af9959 100644 --- a/tests/managers/test_source_manager.py +++ b/tests/managers/test_source_manager.py @@ -1,19 +1,8 @@ -import json -import logging -import os -import random -import re -import string import time import uuid -from datetime import datetime, timedelta, timezone -from typing import List -from unittest.mock import AsyncMock, Mock, patch +from unittest.mock import patch import pytest -from _pytest.python_api import approx -from anthropic.types.beta import BetaMessage -from anthropic.types.beta.messages import BetaMessageBatchIndividualResponse, BetaMessageBatchSucceededResult # Import shared fixtures and constants from conftest from conftest import ( @@ -21,85 +10,22 @@ from conftest import ( DEFAULT_EMBEDDING_CONFIG, USING_SQLITE, ) -from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction from sqlalchemy import func, select -from sqlalchemy.exc import IntegrityError, InvalidRequestError -from sqlalchemy.orm.exc import StaleDataError +from sqlalchemy.exc import InvalidRequestError -from letta.config import LettaConfig -from letta.constants import ( - BASE_MEMORY_TOOLS, - BASE_SLEEPTIME_TOOLS, - BASE_TOOLS, - BASE_VOICE_SLEEPTIME_CHAT_TOOLS, - BASE_VOICE_SLEEPTIME_TOOLS, - BUILTIN_TOOLS, - DEFAULT_ORG_ID, - DEFAULT_ORG_NAME, - FILES_TOOLS, - LETTA_TOOL_EXECUTION_DIR, - LETTA_TOOL_SET, - LOCAL_ONLY_MULTI_AGENT_TOOLS, - MCP_TOOL_TAG_NAME_PREFIX, - MULTI_AGENT_TOOLS, -) -from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client from letta.errors import LettaAgentNotFoundError, LettaInvalidArgumentError -from letta.functions.functions import derive_openai_json_schema, parse_source_code -from letta.functions.mcp_client.types import MCPTool -from letta.helpers import ToolRulesSolver -from letta.helpers.datetime_helpers import AsyncTimer -from letta.jobs.types import ItemUpdateInfo, RequestStatusUpdateInfo, StepStatusUpdateInfo -from letta.orm import Base, Block -from letta.orm.block_history import BlockHistory from letta.orm.errors import NoResultFound, UniqueConstraintViolationError from letta.orm.file import FileContent as FileContentModel, FileMetadata as FileMetadataModel -from letta.schemas.agent import CreateAgent, UpdateAgent -from letta.schemas.block import Block as PydanticBlock, BlockUpdate, CreateBlock +from letta.schemas.agent import CreateAgent from letta.schemas.embedding_config import EmbeddingConfig from letta.schemas.enums import ( - ActorType, - AgentStepStatus, FileProcessingStatus, - JobStatus, - JobType, - MessageRole, - ProviderType, - SandboxType, - StepStatus, - TagMatchMode, - ToolType, VectorDBProvider, ) -from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate from letta.schemas.file import FileMetadata, FileMetadata as PydanticFileMetadata -from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityPropertyType, IdentityType, IdentityUpdate, IdentityUpsert -from letta.schemas.job import BatchJob, Job, Job as PydanticJob, JobUpdate, LettaRequestConfig -from letta.schemas.letta_message import UpdateAssistantMessage, UpdateReasoningMessage, UpdateSystemMessage, UpdateUserMessage -from letta.schemas.letta_message_content import TextContent -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.llm_batch_job import AgentStepState, LLMBatchItem from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import Message as PydanticMessage, MessageCreate, MessageUpdate -from letta.schemas.openai.chat_completion_response import UsageStatistics -from letta.schemas.organization import Organization, Organization as PydanticOrganization, OrganizationUpdate -from letta.schemas.passage import Passage as PydanticPassage -from letta.schemas.pip_requirement import PipRequirement -from letta.schemas.run import Run as PydanticRun -from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfigCreate, SandboxConfigUpdate from letta.schemas.source import Source as PydanticSource, SourceUpdate -from letta.schemas.tool import Tool as PydanticTool, ToolCreate, ToolUpdate -from letta.schemas.tool_rule import InitToolRule -from letta.schemas.user import User as PydanticUser, UserUpdate -from letta.server.db import db_registry from letta.server.server import SyncServer -from letta.services.block_manager import BlockManager -from letta.services.helpers.agent_manager_helper import calculate_base_tools, calculate_multi_agent_tools, validate_agent_exists_async -from letta.services.step_manager import FeedbackType -from letta.settings import settings, tool_settings -from letta.utils import calculate_file_defaults_based_on_context_window -from tests.helpers.utils import comprehensive_agent_checks, validate_context_window_overview -from tests.utils import random_string # Helper function for file content tests diff --git a/tests/managers/test_tool_manager.py b/tests/managers/test_tool_manager.py index 4658acb8..68a09541 100644 --- a/tests/managers/test_tool_manager.py +++ b/tests/managers/test_tool_manager.py @@ -1,32 +1,8 @@ -import json -import logging -import os -import random -import re -import string -import time import uuid -from datetime import datetime, timedelta, timezone -from typing import List -from unittest.mock import AsyncMock, Mock, patch import pytest -from _pytest.python_api import approx -from anthropic.types.beta import BetaMessage -from anthropic.types.beta.messages import BetaMessageBatchIndividualResponse, BetaMessageBatchSucceededResult # Import shared fixtures and constants from conftest -from conftest import ( - CREATE_DELAY_SQLITE, - DEFAULT_EMBEDDING_CONFIG, - USING_SQLITE, -) -from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction -from sqlalchemy import func, select -from sqlalchemy.exc import IntegrityError, InvalidRequestError -from sqlalchemy.orm.exc import StaleDataError - -from letta.config import LettaConfig from letta.constants import ( BASE_MEMORY_TOOLS, BASE_SLEEPTIME_TOOLS, @@ -34,73 +10,27 @@ from letta.constants import ( BASE_VOICE_SLEEPTIME_CHAT_TOOLS, BASE_VOICE_SLEEPTIME_TOOLS, BUILTIN_TOOLS, - DEFAULT_ORG_ID, - DEFAULT_ORG_NAME, FILES_TOOLS, - LETTA_TOOL_EXECUTION_DIR, LETTA_TOOL_SET, LOCAL_ONLY_MULTI_AGENT_TOOLS, MCP_TOOL_TAG_NAME_PREFIX, MULTI_AGENT_TOOLS, ) -from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client from letta.errors import LettaAgentNotFoundError -from letta.functions.functions import derive_openai_json_schema, parse_source_code -from letta.functions.mcp_client.types import MCPTool -from letta.helpers import ToolRulesSolver -from letta.helpers.datetime_helpers import AsyncTimer -from letta.jobs.types import ItemUpdateInfo, RequestStatusUpdateInfo, StepStatusUpdateInfo -from letta.orm import Base, Block -from letta.orm.block_history import BlockHistory +from letta.functions.functions import parse_source_code from letta.orm.errors import NoResultFound, UniqueConstraintViolationError -from letta.orm.file import FileContent as FileContentModel, FileMetadata as FileMetadataModel -from letta.schemas.agent import CreateAgent, UpdateAgent -from letta.schemas.block import Block as PydanticBlock, BlockUpdate, CreateBlock +from letta.schemas.agent import CreateAgent from letta.schemas.embedding_config import EmbeddingConfig from letta.schemas.enums import ( - ActorType, - AgentStepStatus, - FileProcessingStatus, - JobStatus, - JobType, - MessageRole, - ProviderType, - SandboxType, - StepStatus, - TagMatchMode, ToolType, - VectorDBProvider, ) -from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate -from letta.schemas.file import FileMetadata, FileMetadata as PydanticFileMetadata -from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityPropertyType, IdentityType, IdentityUpdate, IdentityUpsert -from letta.schemas.job import BatchJob, Job, Job as PydanticJob, JobUpdate, LettaRequestConfig -from letta.schemas.letta_message import UpdateAssistantMessage, UpdateReasoningMessage, UpdateSystemMessage, UpdateUserMessage -from letta.schemas.letta_message_content import TextContent -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.llm_batch_job import AgentStepState, LLMBatchItem from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import Message as PydanticMessage, MessageCreate, MessageUpdate -from letta.schemas.openai.chat_completion_response import UsageStatistics -from letta.schemas.organization import Organization, Organization as PydanticOrganization, OrganizationUpdate -from letta.schemas.passage import Passage as PydanticPassage from letta.schemas.pip_requirement import PipRequirement -from letta.schemas.run import Run as PydanticRun -from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfigCreate, SandboxConfigUpdate -from letta.schemas.source import Source as PydanticSource, SourceUpdate -from letta.schemas.tool import Tool as PydanticTool, ToolCreate, ToolUpdate -from letta.schemas.tool_rule import InitToolRule -from letta.schemas.user import User as PydanticUser, UserUpdate +from letta.schemas.tool import Tool as PydanticTool, ToolUpdate from letta.server.db import db_registry from letta.server.server import SyncServer -from letta.services.block_manager import BlockManager -from letta.services.helpers.agent_manager_helper import calculate_base_tools, calculate_multi_agent_tools, validate_agent_exists_async -from letta.services.step_manager import FeedbackType from letta.services.tool_schema_generator import generate_schema_for_tool_creation -from letta.settings import settings, tool_settings -from letta.utils import calculate_file_defaults_based_on_context_window -from tests.helpers.utils import comprehensive_agent_checks, validate_context_window_overview -from tests.utils import random_string +from letta.settings import settings # ====================================================================================================================== # AgentManager Tests - Tools Relationship diff --git a/tests/managers/test_user_manager.py b/tests/managers/test_user_manager.py index eac55f19..6c1e7f82 100644 --- a/tests/managers/test_user_manager.py +++ b/tests/managers/test_user_manager.py @@ -1,104 +1,14 @@ -import logging -import os -import random -import re -import string -import time -import uuid -from datetime import datetime, timedelta, timezone -from typing import List -from unittest.mock import AsyncMock, Mock, patch - import pytest -from _pytest.python_api import approx -from anthropic.types.beta import BetaMessage -from anthropic.types.beta.messages import BetaMessageBatchIndividualResponse, BetaMessageBatchSucceededResult # Import shared fixtures and constants from conftest -from conftest import ( - CREATE_DELAY_SQLITE, - DEFAULT_EMBEDDING_CONFIG, - USING_SQLITE, -) -from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall, Function as OpenAIFunction -from sqlalchemy import func, select -from sqlalchemy.exc import IntegrityError, InvalidRequestError -from sqlalchemy.orm.exc import StaleDataError - -from letta.config import LettaConfig from letta.constants import ( - BASE_MEMORY_TOOLS, - BASE_SLEEPTIME_TOOLS, - BASE_TOOLS, - BASE_VOICE_SLEEPTIME_CHAT_TOOLS, - BASE_VOICE_SLEEPTIME_TOOLS, - BUILTIN_TOOLS, DEFAULT_ORG_ID, - DEFAULT_ORG_NAME, - FILES_TOOLS, - LETTA_TOOL_EXECUTION_DIR, - LETTA_TOOL_SET, - LOCAL_ONLY_MULTI_AGENT_TOOLS, - MCP_TOOL_TAG_NAME_PREFIX, - MULTI_AGENT_TOOLS, ) from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client -from letta.errors import LettaAgentNotFoundError -from letta.functions.functions import derive_openai_json_schema, parse_source_code -from letta.functions.mcp_client.types import MCPTool -from letta.helpers import ToolRulesSolver from letta.helpers.datetime_helpers import AsyncTimer -from letta.jobs.types import ItemUpdateInfo, RequestStatusUpdateInfo, StepStatusUpdateInfo -from letta.orm import Base, Block -from letta.orm.block_history import BlockHistory -from letta.orm.errors import NoResultFound, UniqueConstraintViolationError -from letta.orm.file import FileContent as FileContentModel, FileMetadata as FileMetadataModel -from letta.schemas.agent import CreateAgent, UpdateAgent -from letta.schemas.block import Block as PydanticBlock, BlockUpdate, CreateBlock -from letta.schemas.embedding_config import EmbeddingConfig -from letta.schemas.enums import ( - ActorType, - AgentStepStatus, - FileProcessingStatus, - JobStatus, - JobType, - MessageRole, - ProviderType, - SandboxType, - StepStatus, - TagMatchMode, - ToolType, - VectorDBProvider, -) -from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate -from letta.schemas.file import FileMetadata, FileMetadata as PydanticFileMetadata -from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityPropertyType, IdentityType, IdentityUpdate, IdentityUpsert -from letta.schemas.job import BatchJob, Job, Job as PydanticJob, JobUpdate, LettaRequestConfig -from letta.schemas.letta_message import UpdateAssistantMessage, UpdateReasoningMessage, UpdateSystemMessage, UpdateUserMessage -from letta.schemas.letta_message_content import TextContent -from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType -from letta.schemas.llm_batch_job import AgentStepState, LLMBatchItem -from letta.schemas.llm_config import LLMConfig -from letta.schemas.message import Message as PydanticMessage, MessageCreate, MessageUpdate -from letta.schemas.openai.chat_completion_response import UsageStatistics -from letta.schemas.organization import Organization, Organization as PydanticOrganization, OrganizationUpdate -from letta.schemas.passage import Passage as PydanticPassage -from letta.schemas.pip_requirement import PipRequirement -from letta.schemas.run import Run as PydanticRun -from letta.schemas.sandbox_config import E2BSandboxConfig, LocalSandboxConfig, SandboxConfigCreate, SandboxConfigUpdate -from letta.schemas.source import Source as PydanticSource, SourceUpdate -from letta.schemas.tool import Tool as PydanticTool, ToolCreate, ToolUpdate -from letta.schemas.tool_rule import InitToolRule +from letta.schemas.organization import Organization as PydanticOrganization from letta.schemas.user import User as PydanticUser, UserUpdate -from letta.server.db import db_registry from letta.server.server import SyncServer -from letta.services.block_manager import BlockManager -from letta.services.helpers.agent_manager_helper import calculate_base_tools, calculate_multi_agent_tools, validate_agent_exists_async -from letta.services.step_manager import FeedbackType -from letta.settings import settings, tool_settings -from letta.utils import calculate_file_defaults_based_on_context_window -from tests.helpers.utils import comprehensive_agent_checks, validate_context_window_overview -from tests.utils import random_string # ====================================================================================================================== diff --git a/tests/mcp_tests/test_mcp_schema_validation.py b/tests/mcp_tests/test_mcp_schema_validation.py index 93eb021b..b049d33c 100644 --- a/tests/mcp_tests/test_mcp_schema_validation.py +++ b/tests/mcp_tests/test_mcp_schema_validation.py @@ -138,7 +138,6 @@ async def test_add_mcp_tool_accepts_non_strict_schemas(): @pytest.mark.asyncio async def test_add_mcp_tool_rejects_invalid_schemas(): """Test that adding MCP tools with invalid schemas is rejected.""" - from fastapi import HTTPException from letta.server.rest_api.routers.v1.tools import add_mcp_tool from letta.settings import tool_settings @@ -465,7 +464,6 @@ def test_mcp_schema_with_uuid_format_required_field(): def test_mcp_schema_complex_nested_with_defs(): """Test generating exact schema with nested Pydantic-like models using $defs.""" - import json from letta.functions.mcp_client.types import MCPToolHealth diff --git a/tests/mock_mcp_server.py b/tests/mock_mcp_server.py index b3381720..a98bfa27 100755 --- a/tests/mock_mcp_server.py +++ b/tests/mock_mcp_server.py @@ -5,10 +5,10 @@ Simple MCP test server with basic and complex tools for testing purposes. import json import logging -from typing import List, Optional, Union +from typing import List, Optional from mcp.server.fastmcp import FastMCP -from pydantic import BaseModel, ConfigDict, Field +from pydantic import BaseModel, Field # Configure logging to stderr (not stdout for STDIO servers) logging.basicConfig(level=logging.INFO) diff --git a/tests/performance_tests/test_agent_mass_creation.py b/tests/performance_tests/test_agent_mass_creation.py index f9dc57db..7888899f 100644 --- a/tests/performance_tests/test_agent_mass_creation.py +++ b/tests/performance_tests/test_agent_mass_creation.py @@ -73,7 +73,7 @@ def roll_dice_tool(client): @pytest.fixture() def rethink_tool(client): - def rethink_memory(agent_state: "AgentState", new_memory: str, target_block_label: str) -> str: # type: ignore + def rethink_memory(agent_state: "AgentState", new_memory: str, target_block_label: str) -> str: # type: ignore # noqa: F821 """ Re-evaluate the memory in block_name, integrating new and updated facts. Replace outdated information with the most likely truths, avoiding redundancy with original memories. diff --git a/tests/performance_tests/test_agent_mass_update.py b/tests/performance_tests/test_agent_mass_update.py index 841462ef..0c84cdd8 100644 --- a/tests/performance_tests/test_agent_mass_update.py +++ b/tests/performance_tests/test_agent_mass_update.py @@ -72,7 +72,7 @@ def roll_dice_tool(client): @pytest.fixture() def rethink_tool(client): - def rethink_memory(agent_state: "AgentState", new_memory: str, target_block_label: str) -> str: # type: ignore + def rethink_memory(agent_state: "AgentState", new_memory: str, target_block_label: str) -> str: # type: ignore # noqa: F821 """ Re-evaluate the memory in block_name, integrating new and updated facts. Replace outdated information with the most likely truths, avoiding redundancy with original memories. diff --git a/tests/test_crypto_utils.py b/tests/test_crypto_utils.py index 6ceabdd5..259548be 100644 --- a/tests/test_crypto_utils.py +++ b/tests/test_crypto_utils.py @@ -1,7 +1,5 @@ import base64 import json -import os -from unittest.mock import patch import pytest diff --git a/tests/test_exception_logging.py b/tests/test_exception_logging.py index 2fa952cc..6907ec7d 100644 --- a/tests/test_exception_logging.py +++ b/tests/test_exception_logging.py @@ -3,13 +3,11 @@ Tests for global exception logging system. """ import asyncio -import logging -from unittest.mock import MagicMock, patch +from unittest.mock import patch import pytest -from fastapi import FastAPI, Request +from fastapi import FastAPI from fastapi.testclient import TestClient -from starlette.middleware.base import BaseHTTPMiddleware from letta.exceptions.logging import add_exception_context, log_and_raise, log_exception from letta.server.rest_api.middleware.logging import LoggingMiddleware diff --git a/tests/test_internal_agents_count.py b/tests/test_internal_agents_count.py index 534296a0..8223990f 100644 --- a/tests/test_internal_agents_count.py +++ b/tests/test_internal_agents_count.py @@ -1,4 +1,3 @@ -import os from typing import List import httpx diff --git a/tests/test_log_context.py b/tests/test_log_context.py index 45ae3a93..3af18f84 100644 --- a/tests/test_log_context.py +++ b/tests/test_log_context.py @@ -2,8 +2,6 @@ import json import logging from io import StringIO -import pytest - from letta.log import JSONFormatter, LogContextFilter from letta.log_context import clear_log_context, get_log_context, remove_log_context, set_log_context, update_log_context diff --git a/tests/test_mcp_encryption.py b/tests/test_mcp_encryption.py index e8c9c0f1..d37d6931 100644 --- a/tests/test_mcp_encryption.py +++ b/tests/test_mcp_encryption.py @@ -4,9 +4,8 @@ Tests the end-to-end encryption functionality in the MCP manager. """ import json -import os from datetime import datetime, timezone -from unittest.mock import AsyncMock, Mock, patch +from unittest.mock import AsyncMock, patch from uuid import uuid4 import pytest @@ -20,13 +19,9 @@ from letta.schemas.mcp import ( MCPOAuthSessionUpdate, MCPServer as PydanticMCPServer, MCPServerType, - SSEServerConfig, - StdioServerConfig, ) -from letta.schemas.secret import Secret from letta.server.db import db_registry from letta.server.server import SyncServer -from letta.services.mcp_manager import MCPManager from letta.settings import settings diff --git a/tests/test_prompt_caching.py b/tests/test_prompt_caching.py index 01b3fc95..a64f0b12 100644 --- a/tests/test_prompt_caching.py +++ b/tests/test_prompt_caching.py @@ -702,8 +702,6 @@ async def test_anthropic_inspect_raw_request(async_client: AsyncLetta): agent = await create_agent_with_large_memory(async_client, model, {}, "anthropic-debug") try: - import json - # Message 1 response1 = await async_client.agents.messages.create( agent_id=agent.id, diff --git a/tests/test_provider_trace.py b/tests/test_provider_trace.py index d2fc4f47..537d6b83 100644 --- a/tests/test_provider_trace.py +++ b/tests/test_provider_trace.py @@ -14,7 +14,6 @@ import os import threading import time import uuid -from unittest.mock import patch import pytest from dotenv import load_dotenv diff --git a/tests/test_provider_trace_backends.py b/tests/test_provider_trace_backends.py index 83547c1d..34238102 100644 --- a/tests/test_provider_trace_backends.py +++ b/tests/test_provider_trace_backends.py @@ -1,18 +1,17 @@ """Unit tests for provider trace backends.""" -import asyncio import json import os import socket import tempfile import threading -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import patch import pytest from letta.schemas.provider_trace import ProviderTrace from letta.schemas.user import User -from letta.services.provider_trace_backends.base import ProviderTraceBackend, ProviderTraceBackendClient +from letta.services.provider_trace_backends.base import ProviderTraceBackend from letta.services.provider_trace_backends.socket import SocketProviderTraceBackend @@ -341,7 +340,6 @@ class TestBackendFactory: def test_get_multiple_backends(self): """Test getting multiple backends via environment.""" - import os from letta.services.provider_trace_backends.factory import ( get_provider_trace_backends, diff --git a/tests/test_provider_trace_summarization.py b/tests/test_provider_trace_summarization.py index c21e1e02..c1bedbc0 100644 --- a/tests/test_provider_trace_summarization.py +++ b/tests/test_provider_trace_summarization.py @@ -11,7 +11,6 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest from letta.schemas.agent import AgentState -from letta.schemas.block import Block from letta.schemas.embedding_config import EmbeddingConfig from letta.schemas.enums import MessageRole from letta.schemas.llm_config import LLMConfig diff --git a/tests/test_sdk_client.py b/tests/test_sdk_client.py index 91e340b8..9414d9c1 100644 --- a/tests/test_sdk_client.py +++ b/tests/test_sdk_client.py @@ -24,14 +24,12 @@ from letta_client.types import ( TerminalToolRule, ToolReturnMessage, ) -from letta_client.types.agents.text_content_param import TextContentParam from letta_client.types.tool import BaseTool from pydantic import BaseModel, Field from letta.config import LettaConfig from letta.jobs.llm_batch_job_polling import poll_running_llm_batches from letta.server.server import SyncServer -from tests.helpers.utils import upload_file_and_wait from tests.utils import wait_for_server # Constants diff --git a/tests/test_secret.py b/tests/test_secret.py index 0dbc80d9..cd4d6390 100644 --- a/tests/test_secret.py +++ b/tests/test_secret.py @@ -1,5 +1,4 @@ -import json -from unittest.mock import MagicMock, patch +from unittest.mock import patch import pytest diff --git a/tests/test_server.py b/tests/test_server.py index 490463e2..67b3726c 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -1,39 +1,20 @@ -import json import os -import shutil -import uuid -import warnings -from typing import List, Tuple -from unittest.mock import patch import pytest -from sqlalchemy import delete import letta.utils as utils from letta.agents.agent_loop import AgentLoop -from letta.constants import BASE_MEMORY_TOOLS, BASE_TOOLS, LETTA_DIR, LETTA_TOOL_EXECUTION_DIR -from letta.orm import Provider, Step -from letta.schemas.block import CreateBlock from letta.schemas.enums import MessageRole, ProviderType -from letta.schemas.letta_message import LettaMessage, ReasoningMessage, SystemMessage, ToolCallMessage, ToolReturnMessage, UserMessage -from letta.schemas.llm_config import LLMConfig from letta.schemas.providers import Provider as PydanticProvider, ProviderCreate -from letta.schemas.sandbox_config import SandboxType from letta.schemas.user import User utils.DEBUG = True from letta.config import LettaConfig from letta.orm.errors import NoResultFound -from letta.schemas.agent import CreateAgent, UpdateAgent -from letta.schemas.embedding_config import EmbeddingConfig -from letta.schemas.job import Job as PydanticJob -from letta.schemas.message import Message, MessageCreate +from letta.schemas.agent import CreateAgent +from letta.schemas.message import MessageCreate from letta.schemas.run import Run as PydanticRun -from letta.schemas.source import Source as PydanticSource from letta.server.server import SyncServer -from letta.system import unpack_message - -from .utils import DummyDataConnector @pytest.fixture diff --git a/tests/test_server_providers.py b/tests/test_server_providers.py index b3116303..57dc256f 100644 --- a/tests/test_server_providers.py +++ b/tests/test_server_providers.py @@ -2600,7 +2600,6 @@ async def test_byok_provider_last_synced_triggers_sync_when_null(default_user, p @pytest.mark.asyncio async def test_byok_provider_last_synced_skips_sync_when_set(default_user, provider_manager): """Test that BYOK providers with last_synced set skip sync and read from DB.""" - from datetime import datetime, timezone from letta.schemas.providers import Provider from letta.server.server import SyncServer @@ -2662,9 +2661,7 @@ async def test_byok_provider_last_synced_skips_sync_when_set(default_user, provi @pytest.mark.asyncio -async def test_chatgpt_oauth_byok_resyncs_when_allowlist_expands( - default_user, provider_manager -): +async def test_chatgpt_oauth_byok_resyncs_when_allowlist_expands(default_user, provider_manager): """ChatGPT OAuth providers should backfill newly added hardcoded models.""" test_id = generate_test_id() provider_name = f"test-chatgpt-oauth-{test_id}" @@ -2706,9 +2703,7 @@ async def test_chatgpt_oauth_byok_resyncs_when_allowlist_expands( embedding_models=[], organization_id=default_user.organization_id, ) - await provider_manager.update_provider_last_synced_async( - byok_provider.id, actor=default_user - ) + await provider_manager.update_provider_last_synced_async(byok_provider.id, actor=default_user) server = SyncServer(init_with_default_org_and_user=False) server.default_user = default_user @@ -2728,7 +2723,6 @@ async def test_chatgpt_oauth_byok_resyncs_when_allowlist_expands( @pytest.mark.asyncio async def test_base_provider_updates_last_synced_on_sync(default_user, provider_manager): """Test that base provider sync updates the last_synced timestamp.""" - from letta.server.server import SyncServer test_id = generate_test_id() @@ -3224,7 +3218,6 @@ async def test_byok_provider_uses_schema_default_base_url(default_user, provider """ from letta.orm.provider import Provider as ProviderORM from letta.schemas.providers import Provider as PydanticProvider - from letta.schemas.providers.zai import ZAIProvider from letta.server.db import db_registry test_id = generate_test_id() diff --git a/tests/test_sources.py b/tests/test_sources.py index cff833fe..8cac5a46 100644 --- a/tests/test_sources.py +++ b/tests/test_sources.py @@ -532,7 +532,8 @@ def test_agent_uses_search_files_correctly(disable_pinecone, disable_turbopuffer # Check it returned successfully tool_returns = [msg for msg in search_files_response.messages if msg.message_type == "tool_return_message"] assert len(tool_returns) > 0, "No tool returns found" - assert all(tr.status == "success" for tr in tool_returns), f"Tool call failed {tr}" + failed_returns = [tr for tr in tool_returns if tr.status != "success"] + assert len(failed_returns) == 0, f"Tool call failed: {failed_returns}" def test_agent_uses_grep_correctly_basic(disable_pinecone, disable_turbopuffer, client: LettaSDKClient, agent_state: AgentState): diff --git a/tests/test_temporal_metrics_local.py b/tests/test_temporal_metrics_local.py index 75d98231..9d4f1d6d 100644 --- a/tests/test_temporal_metrics_local.py +++ b/tests/test_temporal_metrics_local.py @@ -3,16 +3,14 @@ Local test for temporal metrics. Run with: uv run pytest tests/test_temporal_metrics_local.py -v -s """ -import asyncio import os -from unittest.mock import MagicMock, patch +from unittest.mock import patch import pytest from letta.agents.temporal.metrics import ( ActivityMetrics, TemporalMetrics, - WorkerMetrics, WorkflowMetrics, ) diff --git a/tests/test_tool_schema_parsing.py b/tests/test_tool_schema_parsing.py index 90ed1ca7..1ea9865f 100644 --- a/tests/test_tool_schema_parsing.py +++ b/tests/test_tool_schema_parsing.py @@ -14,7 +14,7 @@ from letta.functions.functions import derive_openai_json_schema from letta.functions.schema_generator import validate_google_style_docstring from letta.helpers.tool_execution_helper import enable_strict_mode from letta.llm_api.helpers import convert_to_structured_output -from letta.schemas.tool import MCP_TOOL_METADATA_SCHEMA_STATUS, Tool, ToolCreate +from letta.schemas.tool import MCP_TOOL_METADATA_SCHEMA_STATUS, Tool def _clean_diff(d1, d2):