letta-nightly 0.13.0.dev20251031104146__py3-none-any.whl → 0.13.1.dev20251031234110__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of letta-nightly might be problematic. Click here for more details.
- letta/__init__.py +1 -1
- letta/adapters/simple_llm_stream_adapter.py +1 -0
- letta/agents/letta_agent_v2.py +8 -0
- letta/agents/letta_agent_v3.py +120 -27
- letta/agents/temporal/activities/__init__.py +25 -0
- letta/agents/temporal/activities/create_messages.py +26 -0
- letta/agents/temporal/activities/create_step.py +57 -0
- letta/agents/temporal/activities/example_activity.py +9 -0
- letta/agents/temporal/activities/execute_tool.py +130 -0
- letta/agents/temporal/activities/llm_request.py +114 -0
- letta/agents/temporal/activities/prepare_messages.py +27 -0
- letta/agents/temporal/activities/refresh_context.py +160 -0
- letta/agents/temporal/activities/summarize_conversation_history.py +77 -0
- letta/agents/temporal/activities/update_message_ids.py +25 -0
- letta/agents/temporal/activities/update_run.py +43 -0
- letta/agents/temporal/constants.py +59 -0
- letta/agents/temporal/temporal_agent_workflow.py +704 -0
- letta/agents/temporal/types.py +275 -0
- letta/constants.py +8 -0
- letta/errors.py +4 -0
- letta/functions/function_sets/base.py +0 -11
- letta/groups/helpers.py +7 -1
- letta/groups/sleeptime_multi_agent_v4.py +4 -3
- letta/interfaces/anthropic_streaming_interface.py +0 -1
- letta/interfaces/openai_streaming_interface.py +103 -100
- letta/llm_api/anthropic_client.py +57 -12
- letta/llm_api/bedrock_client.py +1 -0
- letta/llm_api/deepseek_client.py +3 -2
- letta/llm_api/google_vertex_client.py +1 -0
- letta/llm_api/groq_client.py +1 -0
- letta/llm_api/llm_client_base.py +15 -1
- letta/llm_api/openai.py +2 -2
- letta/llm_api/openai_client.py +17 -3
- letta/llm_api/xai_client.py +1 -0
- letta/orm/organization.py +4 -0
- letta/orm/sqlalchemy_base.py +7 -0
- letta/otel/tracing.py +131 -4
- letta/schemas/agent_file.py +10 -10
- letta/schemas/block.py +22 -3
- letta/schemas/enums.py +21 -0
- letta/schemas/environment_variables.py +3 -2
- letta/schemas/group.py +3 -3
- letta/schemas/letta_response.py +36 -4
- letta/schemas/llm_batch_job.py +3 -3
- letta/schemas/llm_config.py +27 -3
- letta/schemas/mcp.py +3 -2
- letta/schemas/mcp_server.py +3 -2
- letta/schemas/message.py +167 -49
- letta/schemas/organization.py +2 -1
- letta/schemas/passage.py +2 -1
- letta/schemas/provider_trace.py +2 -1
- letta/schemas/providers/openrouter.py +1 -2
- letta/schemas/run_metrics.py +2 -1
- letta/schemas/sandbox_config.py +3 -1
- letta/schemas/step_metrics.py +2 -1
- letta/schemas/tool_rule.py +2 -2
- letta/schemas/user.py +2 -1
- letta/server/rest_api/app.py +5 -1
- letta/server/rest_api/routers/v1/__init__.py +4 -0
- letta/server/rest_api/routers/v1/agents.py +71 -9
- letta/server/rest_api/routers/v1/blocks.py +7 -7
- letta/server/rest_api/routers/v1/groups.py +40 -0
- letta/server/rest_api/routers/v1/identities.py +2 -2
- letta/server/rest_api/routers/v1/internal_agents.py +31 -0
- letta/server/rest_api/routers/v1/internal_blocks.py +177 -0
- letta/server/rest_api/routers/v1/internal_runs.py +25 -1
- letta/server/rest_api/routers/v1/runs.py +2 -22
- letta/server/rest_api/routers/v1/tools.py +10 -0
- letta/server/server.py +5 -2
- letta/services/agent_manager.py +4 -4
- letta/services/archive_manager.py +16 -0
- letta/services/group_manager.py +44 -0
- letta/services/helpers/run_manager_helper.py +2 -2
- letta/services/lettuce/lettuce_client.py +148 -0
- letta/services/mcp/base_client.py +9 -3
- letta/services/run_manager.py +148 -37
- letta/services/source_manager.py +91 -3
- letta/services/step_manager.py +2 -3
- letta/services/streaming_service.py +52 -13
- letta/services/summarizer/summarizer.py +28 -2
- letta/services/tool_executor/builtin_tool_executor.py +1 -1
- letta/services/tool_executor/core_tool_executor.py +2 -117
- letta/services/tool_schema_generator.py +2 -2
- letta/validators.py +21 -0
- {letta_nightly-0.13.0.dev20251031104146.dist-info → letta_nightly-0.13.1.dev20251031234110.dist-info}/METADATA +1 -1
- {letta_nightly-0.13.0.dev20251031104146.dist-info → letta_nightly-0.13.1.dev20251031234110.dist-info}/RECORD +89 -84
- letta/agent.py +0 -1758
- letta/cli/cli_load.py +0 -16
- letta/client/__init__.py +0 -0
- letta/client/streaming.py +0 -95
- letta/client/utils.py +0 -78
- letta/functions/async_composio_toolset.py +0 -109
- letta/functions/composio_helpers.py +0 -96
- letta/helpers/composio_helpers.py +0 -38
- letta/orm/job_messages.py +0 -33
- letta/schemas/providers.py +0 -1617
- letta/server/rest_api/routers/openai/chat_completions/chat_completions.py +0 -132
- letta/services/tool_executor/composio_tool_executor.py +0 -57
- {letta_nightly-0.13.0.dev20251031104146.dist-info → letta_nightly-0.13.1.dev20251031234110.dist-info}/WHEEL +0 -0
- {letta_nightly-0.13.0.dev20251031104146.dist-info → letta_nightly-0.13.1.dev20251031234110.dist-info}/entry_points.txt +0 -0
- {letta_nightly-0.13.0.dev20251031104146.dist-info → letta_nightly-0.13.1.dev20251031234110.dist-info}/licenses/LICENSE +0 -0
|
@@ -21,7 +21,6 @@ class OpenRouterProvider(OpenAIProvider):
|
|
|
21
21
|
provider_category: ProviderCategory = Field(ProviderCategory.base, description="The category of the provider (base or byok)")
|
|
22
22
|
api_key: str = Field(..., description="API key for the OpenRouter API.")
|
|
23
23
|
base_url: str = Field("https://openrouter.ai/api/v1", description="Base URL for the OpenRouter API.")
|
|
24
|
-
handle_base: str | None = Field(None, description="Custom handle base name for model handles (e.g., 'custom' instead of 'openrouter').")
|
|
25
24
|
|
|
26
25
|
def _list_llm_models(self, data: list[dict]) -> list[LLMConfig]:
|
|
27
26
|
"""
|
|
@@ -34,7 +33,7 @@ class OpenRouterProvider(OpenAIProvider):
|
|
|
34
33
|
continue
|
|
35
34
|
model_name, context_window_size = check
|
|
36
35
|
|
|
37
|
-
handle = self.get_handle(model_name
|
|
36
|
+
handle = self.get_handle(model_name)
|
|
38
37
|
|
|
39
38
|
config = LLMConfig(
|
|
40
39
|
model=model_name,
|
letta/schemas/run_metrics.py
CHANGED
|
@@ -2,11 +2,12 @@ from typing import List, Optional
|
|
|
2
2
|
|
|
3
3
|
from pydantic import Field
|
|
4
4
|
|
|
5
|
+
from letta.schemas.enums import PrimitiveType
|
|
5
6
|
from letta.schemas.letta_base import LettaBase
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class RunMetricsBase(LettaBase):
|
|
9
|
-
__id_prefix__ =
|
|
10
|
+
__id_prefix__ = PrimitiveType.RUN.value
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class RunMetrics(RunMetricsBase):
|
letta/schemas/sandbox_config.py
CHANGED
|
@@ -102,7 +102,9 @@ class SandboxConfig(SandboxConfigBase):
|
|
|
102
102
|
config: Dict = Field(default_factory=lambda: {}, description="The JSON sandbox settings data.")
|
|
103
103
|
|
|
104
104
|
def get_e2b_config(self) -> E2BSandboxConfig:
|
|
105
|
-
|
|
105
|
+
config_dict = self.config.copy()
|
|
106
|
+
config_dict["template"] = tool_settings.e2b_sandbox_template_id
|
|
107
|
+
return E2BSandboxConfig(**config_dict)
|
|
106
108
|
|
|
107
109
|
def get_local_config(self) -> LocalSandboxConfig:
|
|
108
110
|
return LocalSandboxConfig(**self.config)
|
letta/schemas/step_metrics.py
CHANGED
|
@@ -2,11 +2,12 @@ from typing import Optional
|
|
|
2
2
|
|
|
3
3
|
from pydantic import Field
|
|
4
4
|
|
|
5
|
+
from letta.schemas.enums import PrimitiveType
|
|
5
6
|
from letta.schemas.letta_base import LettaBase
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class StepMetricsBase(LettaBase):
|
|
9
|
-
__id_prefix__ =
|
|
10
|
+
__id_prefix__ = PrimitiveType.STEP.value
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class StepMetrics(StepMetricsBase):
|
letta/schemas/tool_rule.py
CHANGED
|
@@ -4,14 +4,14 @@ from typing import Annotated, Any, Dict, List, Literal, Optional, Set, Union
|
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
6
6
|
|
|
7
|
-
from letta.schemas.enums import ToolRuleType
|
|
7
|
+
from letta.schemas.enums import PrimitiveType, ToolRuleType
|
|
8
8
|
from letta.schemas.letta_base import LettaBase
|
|
9
9
|
|
|
10
10
|
logger = logging.getLogger(__name__)
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class BaseToolRule(LettaBase):
|
|
14
|
-
__id_prefix__ =
|
|
14
|
+
__id_prefix__ = PrimitiveType.TOOL_RULE.value
|
|
15
15
|
tool_name: str = Field(..., description="The name of the tool. Must exist in the database for the user's organization.")
|
|
16
16
|
type: ToolRuleType = Field(..., description="The type of the message.")
|
|
17
17
|
prompt_template: Optional[str] = Field(
|
letta/schemas/user.py
CHANGED
|
@@ -4,11 +4,12 @@ from typing import Optional
|
|
|
4
4
|
from pydantic import Field
|
|
5
5
|
|
|
6
6
|
from letta.constants import DEFAULT_ORG_ID
|
|
7
|
+
from letta.schemas.enums import PrimitiveType
|
|
7
8
|
from letta.schemas.letta_base import LettaBase
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class UserBase(LettaBase):
|
|
11
|
-
__id_prefix__ =
|
|
12
|
+
__id_prefix__ = PrimitiveType.USER.value
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class User(UserBase):
|
letta/server/rest_api/app.py
CHANGED
|
@@ -26,6 +26,7 @@ from letta.errors import (
|
|
|
26
26
|
AgentFileImportError,
|
|
27
27
|
AgentNotFoundForExportError,
|
|
28
28
|
BedrockPermissionError,
|
|
29
|
+
HandleNotFoundError,
|
|
29
30
|
LettaAgentNotFoundError,
|
|
30
31
|
LettaExpiredError,
|
|
31
32
|
LettaInvalidArgumentError,
|
|
@@ -39,6 +40,7 @@ from letta.errors import (
|
|
|
39
40
|
LettaUserNotFoundError,
|
|
40
41
|
LLMAuthenticationError,
|
|
41
42
|
LLMError,
|
|
43
|
+
LLMProviderOverloaded,
|
|
42
44
|
LLMRateLimitError,
|
|
43
45
|
LLMTimeoutError,
|
|
44
46
|
PendingApprovalError,
|
|
@@ -270,7 +272,7 @@ def create_application() -> "FastAPI":
|
|
|
270
272
|
return JSONResponse(
|
|
271
273
|
status_code=500,
|
|
272
274
|
content={
|
|
273
|
-
"detail": "An
|
|
275
|
+
"detail": "An unknown error occurred",
|
|
274
276
|
# Only include error details in debug/development mode
|
|
275
277
|
# "debug_info": str(exc) if settings.debug else None
|
|
276
278
|
},
|
|
@@ -369,6 +371,7 @@ def create_application() -> "FastAPI":
|
|
|
369
371
|
app.add_exception_handler(LettaAgentNotFoundError, _error_handler_404_agent)
|
|
370
372
|
app.add_exception_handler(LettaUserNotFoundError, _error_handler_404_user)
|
|
371
373
|
app.add_exception_handler(AgentNotFoundForExportError, _error_handler_404)
|
|
374
|
+
app.add_exception_handler(HandleNotFoundError, _error_handler_404)
|
|
372
375
|
|
|
373
376
|
# 410 Expired errors
|
|
374
377
|
app.add_exception_handler(LettaExpiredError, _error_handler_410)
|
|
@@ -396,6 +399,7 @@ def create_application() -> "FastAPI":
|
|
|
396
399
|
# 503 Service Unavailable errors
|
|
397
400
|
app.add_exception_handler(OperationalError, _error_handler_503)
|
|
398
401
|
app.add_exception_handler(LettaServiceUnavailableError, _error_handler_503)
|
|
402
|
+
app.add_exception_handler(LLMProviderOverloaded, _error_handler_503)
|
|
399
403
|
|
|
400
404
|
@app.exception_handler(IncompatibleAgentType)
|
|
401
405
|
async def handle_incompatible_agent_type(request: Request, exc: IncompatibleAgentType):
|
|
@@ -7,6 +7,8 @@ from letta.server.rest_api.routers.v1.folders import router as folders_router
|
|
|
7
7
|
from letta.server.rest_api.routers.v1.groups import router as groups_router
|
|
8
8
|
from letta.server.rest_api.routers.v1.health import router as health_router
|
|
9
9
|
from letta.server.rest_api.routers.v1.identities import router as identities_router
|
|
10
|
+
from letta.server.rest_api.routers.v1.internal_agents import router as internal_agents_router
|
|
11
|
+
from letta.server.rest_api.routers.v1.internal_blocks import router as internal_blocks_router
|
|
10
12
|
from letta.server.rest_api.routers.v1.internal_runs import router as internal_runs_router
|
|
11
13
|
from letta.server.rest_api.routers.v1.internal_templates import router as internal_templates_router
|
|
12
14
|
from letta.server.rest_api.routers.v1.jobs import router as jobs_router
|
|
@@ -32,6 +34,8 @@ ROUTERS = [
|
|
|
32
34
|
chat_completions_router,
|
|
33
35
|
groups_router,
|
|
34
36
|
identities_router,
|
|
37
|
+
internal_agents_router,
|
|
38
|
+
internal_blocks_router,
|
|
35
39
|
internal_runs_router,
|
|
36
40
|
internal_templates_router,
|
|
37
41
|
llm_router,
|
|
@@ -25,21 +25,23 @@ from letta.errors import (
|
|
|
25
25
|
AgentNotFoundForExportError,
|
|
26
26
|
PendingApprovalError,
|
|
27
27
|
)
|
|
28
|
-
from letta.
|
|
28
|
+
from letta.groups.sleeptime_multi_agent_v4 import SleeptimeMultiAgentV4
|
|
29
|
+
from letta.helpers.datetime_helpers import get_utc_time, get_utc_timestamp_ns
|
|
29
30
|
from letta.log import get_logger
|
|
30
31
|
from letta.orm.errors import NoResultFound
|
|
31
32
|
from letta.otel.context import get_ctx_attributes
|
|
32
33
|
from letta.otel.metric_registry import MetricRegistry
|
|
33
34
|
from letta.schemas.agent import AgentRelationships, AgentState, CreateAgent, UpdateAgent
|
|
34
35
|
from letta.schemas.agent_file import AgentFileSchema
|
|
35
|
-
from letta.schemas.block import BaseBlock, Block, BlockUpdate
|
|
36
|
-
from letta.schemas.enums import AgentType, RunStatus
|
|
36
|
+
from letta.schemas.block import BaseBlock, Block, BlockResponse, BlockUpdate
|
|
37
|
+
from letta.schemas.enums import AgentType, MessageRole, RunStatus
|
|
37
38
|
from letta.schemas.file import AgentFileAttachment, FileMetadataBase, PaginatedAgentFiles
|
|
38
39
|
from letta.schemas.group import Group
|
|
39
40
|
from letta.schemas.job import LettaRequestConfig
|
|
40
41
|
from letta.schemas.letta_message import LettaMessageUnion, LettaMessageUpdateUnion, MessageType
|
|
42
|
+
from letta.schemas.letta_message_content import TextContent
|
|
41
43
|
from letta.schemas.letta_request import LettaAsyncRequest, LettaRequest, LettaStreamingRequest
|
|
42
|
-
from letta.schemas.letta_response import LettaResponse
|
|
44
|
+
from letta.schemas.letta_response import LettaResponse, LettaStreamingResponse
|
|
43
45
|
from letta.schemas.letta_stop_reason import StopReasonType
|
|
44
46
|
from letta.schemas.memory import (
|
|
45
47
|
ArchivalMemorySearchResponse,
|
|
@@ -48,7 +50,7 @@ from letta.schemas.memory import (
|
|
|
48
50
|
CreateArchivalMemory,
|
|
49
51
|
Memory,
|
|
50
52
|
)
|
|
51
|
-
from letta.schemas.message import
|
|
53
|
+
from letta.schemas.message import Message, MessageCreate, MessageCreateType, MessageSearchRequest, MessageSearchResult
|
|
52
54
|
from letta.schemas.passage import Passage
|
|
53
55
|
from letta.schemas.run import Run as PydanticRun, RunUpdate
|
|
54
56
|
from letta.schemas.source import BaseSource, Source
|
|
@@ -915,7 +917,7 @@ async def retrieve_agent_memory(
|
|
|
915
917
|
return await server.get_agent_memory_async(agent_id=agent_id, actor=actor)
|
|
916
918
|
|
|
917
919
|
|
|
918
|
-
@router.get("/{agent_id}/core-memory/blocks/{block_label}", response_model=
|
|
920
|
+
@router.get("/{agent_id}/core-memory/blocks/{block_label}", response_model=BlockResponse, operation_id="retrieve_core_memory_block")
|
|
919
921
|
async def retrieve_block_for_agent(
|
|
920
922
|
block_label: str,
|
|
921
923
|
agent_id: AgentId,
|
|
@@ -930,7 +932,7 @@ async def retrieve_block_for_agent(
|
|
|
930
932
|
return await server.agent_manager.get_block_with_label_async(agent_id=agent_id, block_label=block_label, actor=actor)
|
|
931
933
|
|
|
932
934
|
|
|
933
|
-
@router.get("/{agent_id}/core-memory/blocks", response_model=list[
|
|
935
|
+
@router.get("/{agent_id}/core-memory/blocks", response_model=list[BlockResponse], operation_id="list_core_memory_blocks")
|
|
934
936
|
async def list_blocks_for_agent(
|
|
935
937
|
agent_id: AgentId,
|
|
936
938
|
server: "SyncServer" = Depends(get_letta_server),
|
|
@@ -962,7 +964,7 @@ async def list_blocks_for_agent(
|
|
|
962
964
|
)
|
|
963
965
|
|
|
964
966
|
|
|
965
|
-
@router.patch("/{agent_id}/core-memory/blocks/{block_label}", response_model=
|
|
967
|
+
@router.patch("/{agent_id}/core-memory/blocks/{block_label}", response_model=BlockResponse, operation_id="modify_core_memory_block")
|
|
966
968
|
async def modify_block_for_agent(
|
|
967
969
|
block_label: str,
|
|
968
970
|
agent_id: AgentId,
|
|
@@ -1394,7 +1396,7 @@ async def send_message(
|
|
|
1394
1396
|
# noinspection PyInconsistentReturns
|
|
1395
1397
|
@router.post(
|
|
1396
1398
|
"/{agent_id}/messages/stream",
|
|
1397
|
-
response_model=
|
|
1399
|
+
response_model=LettaStreamingResponse,
|
|
1398
1400
|
operation_id="create_agent_message_stream",
|
|
1399
1401
|
responses={
|
|
1400
1402
|
200: {
|
|
@@ -1902,3 +1904,63 @@ async def summarize_messages(
|
|
|
1902
1904
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
1903
1905
|
detail="Summarization is not currently supported for this agent configuration. Please contact Letta support.",
|
|
1904
1906
|
)
|
|
1907
|
+
|
|
1908
|
+
|
|
1909
|
+
class CaptureMessagesRequest(BaseModel):
|
|
1910
|
+
provider: str
|
|
1911
|
+
model: str
|
|
1912
|
+
request_messages: list[dict[str, Any]]
|
|
1913
|
+
response_dict: dict[str, Any]
|
|
1914
|
+
|
|
1915
|
+
|
|
1916
|
+
@router.post("/{agent_id}/messages/capture", response_model=str, operation_id="capture_messages", include_in_schema=False)
|
|
1917
|
+
async def capture_messages(
|
|
1918
|
+
agent_id: AgentId,
|
|
1919
|
+
request: CaptureMessagesRequest = Body(...),
|
|
1920
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
1921
|
+
headers: HeaderParams = Depends(get_headers),
|
|
1922
|
+
):
|
|
1923
|
+
"""
|
|
1924
|
+
Capture a list of messages for an agent.
|
|
1925
|
+
"""
|
|
1926
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
|
1927
|
+
agent = await server.agent_manager.get_agent_by_id_async(agent_id, actor, include_relationships=["multi_agent_group"])
|
|
1928
|
+
|
|
1929
|
+
messages_to_persist = []
|
|
1930
|
+
|
|
1931
|
+
# Input user messages
|
|
1932
|
+
for message in request.request_messages:
|
|
1933
|
+
if message["role"] == "user":
|
|
1934
|
+
messages_to_persist.append(
|
|
1935
|
+
Message(
|
|
1936
|
+
role=MessageRole.user,
|
|
1937
|
+
content=[(TextContent(text=message["content"]))],
|
|
1938
|
+
agent_id=agent_id,
|
|
1939
|
+
tool_calls=None,
|
|
1940
|
+
tool_call_id=None,
|
|
1941
|
+
created_at=get_utc_time(),
|
|
1942
|
+
)
|
|
1943
|
+
)
|
|
1944
|
+
|
|
1945
|
+
# Assistant response
|
|
1946
|
+
messages_to_persist.append(
|
|
1947
|
+
Message(
|
|
1948
|
+
role=MessageRole.assistant,
|
|
1949
|
+
content=[(TextContent(text=request.response_dict["content"]))],
|
|
1950
|
+
agent_id=agent_id,
|
|
1951
|
+
model=request.model,
|
|
1952
|
+
tool_calls=None,
|
|
1953
|
+
tool_call_id=None,
|
|
1954
|
+
created_at=get_utc_time(),
|
|
1955
|
+
)
|
|
1956
|
+
)
|
|
1957
|
+
|
|
1958
|
+
response_messages = await server.message_manager.create_many_messages_async(messages_to_persist, actor=actor)
|
|
1959
|
+
|
|
1960
|
+
sleeptime_group = agent.multi_agent_group if agent.multi_agent_group and agent.multi_agent_group.manager_type == "sleeptime" else None
|
|
1961
|
+
if sleeptime_group:
|
|
1962
|
+
sleeptime_agent_loop = SleeptimeMultiAgentV4(agent_state=agent, actor=actor, group=sleeptime_group)
|
|
1963
|
+
sleeptime_agent_loop.response_messages = response_messages
|
|
1964
|
+
run_ids = await sleeptime_agent_loop.run_sleeptime_agents()
|
|
1965
|
+
|
|
1966
|
+
return JSONResponse({"success": True, "messages_created": len(response_messages), "run_ids": run_ids})
|
|
@@ -4,7 +4,7 @@ from fastapi import APIRouter, Body, Depends, HTTPException, Query
|
|
|
4
4
|
|
|
5
5
|
from letta.orm.errors import NoResultFound
|
|
6
6
|
from letta.schemas.agent import AgentRelationships, AgentState
|
|
7
|
-
from letta.schemas.block import BaseBlock, Block, BlockUpdate, CreateBlock
|
|
7
|
+
from letta.schemas.block import BaseBlock, Block, BlockResponse, BlockUpdate, CreateBlock
|
|
8
8
|
from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
|
|
9
9
|
from letta.server.server import SyncServer
|
|
10
10
|
from letta.utils import is_1_0_sdk_version
|
|
@@ -16,7 +16,7 @@ if TYPE_CHECKING:
|
|
|
16
16
|
router = APIRouter(prefix="/blocks", tags=["blocks"])
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
@router.get("/", response_model=List[
|
|
19
|
+
@router.get("/", response_model=List[BlockResponse], operation_id="list_blocks")
|
|
20
20
|
async def list_blocks(
|
|
21
21
|
# query parameters
|
|
22
22
|
label: Optional[str] = Query(None, description="Labels to include (e.g. human, persona)"),
|
|
@@ -117,7 +117,7 @@ async def count_blocks(
|
|
|
117
117
|
return await server.block_manager.size_async(actor=actor)
|
|
118
118
|
|
|
119
119
|
|
|
120
|
-
@router.post("/", response_model=
|
|
120
|
+
@router.post("/", response_model=BlockResponse, operation_id="create_block")
|
|
121
121
|
async def create_block(
|
|
122
122
|
create_block: CreateBlock = Body(...),
|
|
123
123
|
server: SyncServer = Depends(get_letta_server),
|
|
@@ -128,7 +128,7 @@ async def create_block(
|
|
|
128
128
|
return await server.block_manager.create_or_update_block_async(actor=actor, block=block)
|
|
129
129
|
|
|
130
130
|
|
|
131
|
-
@router.patch("/{block_id}", response_model=
|
|
131
|
+
@router.patch("/{block_id}", response_model=BlockResponse, operation_id="modify_block")
|
|
132
132
|
async def modify_block(
|
|
133
133
|
block_id: BlockId,
|
|
134
134
|
block_update: BlockUpdate = Body(...),
|
|
@@ -149,7 +149,7 @@ async def delete_block(
|
|
|
149
149
|
await server.block_manager.delete_block_async(block_id=block_id, actor=actor)
|
|
150
150
|
|
|
151
151
|
|
|
152
|
-
@router.get("/{block_id}", response_model=
|
|
152
|
+
@router.get("/{block_id}", response_model=BlockResponse, operation_id="retrieve_block")
|
|
153
153
|
async def retrieve_block(
|
|
154
154
|
block_id: BlockId,
|
|
155
155
|
server: SyncServer = Depends(get_letta_server),
|
|
@@ -214,7 +214,7 @@ async def list_agents_for_block(
|
|
|
214
214
|
return agents
|
|
215
215
|
|
|
216
216
|
|
|
217
|
-
@router.patch("/{block_id}/identities/attach/{identity_id}", response_model=
|
|
217
|
+
@router.patch("/{block_id}/identities/attach/{identity_id}", response_model=BlockResponse, operation_id="attach_identity_to_block")
|
|
218
218
|
async def attach_identity_to_block(
|
|
219
219
|
identity_id: str,
|
|
220
220
|
block_id: BlockId,
|
|
@@ -233,7 +233,7 @@ async def attach_identity_to_block(
|
|
|
233
233
|
return await server.block_manager.get_block_by_id_async(block_id=block_id, actor=actor)
|
|
234
234
|
|
|
235
235
|
|
|
236
|
-
@router.patch("/{block_id}/identities/detach/{identity_id}", response_model=
|
|
236
|
+
@router.patch("/{block_id}/identities/detach/{identity_id}", response_model=BlockResponse, operation_id="detach_identity_from_block")
|
|
237
237
|
async def detach_identity_from_block(
|
|
238
238
|
identity_id: str,
|
|
239
239
|
block_id: BlockId,
|
|
@@ -284,3 +284,43 @@ async def reset_group_messages(
|
|
|
284
284
|
"""
|
|
285
285
|
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
|
286
286
|
await server.group_manager.reset_messages_async(group_id=group_id, actor=actor)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
@router.patch("/{group_id}/blocks/attach/{block_id}", response_model=None, operation_id="attach_block_to_group")
|
|
290
|
+
async def attach_block_to_group(
|
|
291
|
+
block_id: str,
|
|
292
|
+
group_id: GroupId,
|
|
293
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
294
|
+
headers: HeaderParams = Depends(get_headers),
|
|
295
|
+
):
|
|
296
|
+
"""
|
|
297
|
+
Attach a block to a group.
|
|
298
|
+
This will add the block to the group and all agents within the group.
|
|
299
|
+
"""
|
|
300
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
|
301
|
+
await server.group_manager.attach_block_async(
|
|
302
|
+
group_id=group_id,
|
|
303
|
+
block_id=block_id,
|
|
304
|
+
actor=actor,
|
|
305
|
+
)
|
|
306
|
+
return None
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
@router.patch("/{group_id}/blocks/detach/{block_id}", response_model=None, operation_id="detach_block_from_group")
|
|
310
|
+
async def detach_block_from_group(
|
|
311
|
+
block_id: str,
|
|
312
|
+
group_id: GroupId,
|
|
313
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
314
|
+
headers: HeaderParams = Depends(get_headers),
|
|
315
|
+
):
|
|
316
|
+
"""
|
|
317
|
+
Detach a block from a group.
|
|
318
|
+
This will remove the block from the group and all agents within the group.
|
|
319
|
+
"""
|
|
320
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
|
321
|
+
await server.group_manager.detach_block_async(
|
|
322
|
+
group_id=group_id,
|
|
323
|
+
block_id=block_id,
|
|
324
|
+
actor=actor,
|
|
325
|
+
)
|
|
326
|
+
return None
|
|
@@ -4,7 +4,7 @@ from fastapi import APIRouter, Body, Depends, Header, Query
|
|
|
4
4
|
|
|
5
5
|
from letta.orm.errors import NoResultFound, UniqueConstraintViolationError
|
|
6
6
|
from letta.schemas.agent import AgentRelationships, AgentState
|
|
7
|
-
from letta.schemas.block import Block
|
|
7
|
+
from letta.schemas.block import Block, BlockResponse
|
|
8
8
|
from letta.schemas.identity import (
|
|
9
9
|
Identity,
|
|
10
10
|
IdentityCreate,
|
|
@@ -188,7 +188,7 @@ async def list_agents_for_identity(
|
|
|
188
188
|
)
|
|
189
189
|
|
|
190
190
|
|
|
191
|
-
@router.get("/{identity_id}/blocks", response_model=List[
|
|
191
|
+
@router.get("/{identity_id}/blocks", response_model=List[BlockResponse], operation_id="list_blocks_for_identity")
|
|
192
192
|
async def list_blocks_for_identity(
|
|
193
193
|
identity_id: IdentityId,
|
|
194
194
|
before: Optional[str] = Query(
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from fastapi import APIRouter, Body, Depends
|
|
2
|
+
|
|
3
|
+
from letta.schemas.block import Block, BlockUpdate
|
|
4
|
+
from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
|
|
5
|
+
from letta.server.server import SyncServer
|
|
6
|
+
from letta.validators import AgentId
|
|
7
|
+
|
|
8
|
+
router = APIRouter(prefix="/_internal_agents", tags=["_internal_agents"])
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@router.patch("/{agent_id}/core-memory/blocks/{block_label}", response_model=Block, operation_id="modify_internal_core_memory_block")
|
|
12
|
+
async def modify_block_for_agent(
|
|
13
|
+
block_label: str,
|
|
14
|
+
agent_id: AgentId,
|
|
15
|
+
block_update: BlockUpdate = Body(...),
|
|
16
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
17
|
+
headers: HeaderParams = Depends(get_headers),
|
|
18
|
+
):
|
|
19
|
+
"""
|
|
20
|
+
Updates a core memory block of an agent.
|
|
21
|
+
"""
|
|
22
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
|
23
|
+
|
|
24
|
+
block = await server.agent_manager.modify_block_by_label_async(
|
|
25
|
+
agent_id=agent_id, block_label=block_label, block_update=block_update, actor=actor
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# This should also trigger a system prompt change in the agent
|
|
29
|
+
await server.agent_manager.rebuild_system_prompt_async(agent_id=agent_id, actor=actor, force=True, update_timestamp=False)
|
|
30
|
+
|
|
31
|
+
return block
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, List, Literal, Optional
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, Body, Depends, Query
|
|
4
|
+
|
|
5
|
+
from letta.schemas.agent import AgentState
|
|
6
|
+
from letta.schemas.block import Block, CreateBlock
|
|
7
|
+
from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
|
|
8
|
+
from letta.server.server import SyncServer
|
|
9
|
+
from letta.utils import is_1_0_sdk_version
|
|
10
|
+
from letta.validators import BlockId
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
router = APIRouter(prefix="/_internal_blocks", tags=["_internal_blocks"])
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@router.get("/", response_model=List[Block], operation_id="list_internal_blocks")
|
|
19
|
+
async def list_blocks(
|
|
20
|
+
# query parameters
|
|
21
|
+
label: Optional[str] = Query(None, description="Labels to include (e.g. human, persona)"),
|
|
22
|
+
templates_only: bool = Query(False, description="Whether to include only templates"),
|
|
23
|
+
name: Optional[str] = Query(None, description="Name of the block"),
|
|
24
|
+
identity_id: Optional[str] = Query(None, description="Search agents by identifier id"),
|
|
25
|
+
identifier_keys: Optional[List[str]] = Query(None, description="Search agents by identifier keys"),
|
|
26
|
+
project_id: Optional[str] = Query(None, description="Search blocks by project id"),
|
|
27
|
+
limit: Optional[int] = Query(50, description="Number of blocks to return"),
|
|
28
|
+
before: Optional[str] = Query(
|
|
29
|
+
None,
|
|
30
|
+
description="Block ID cursor for pagination. Returns blocks that come before this block ID in the specified sort order",
|
|
31
|
+
),
|
|
32
|
+
after: Optional[str] = Query(
|
|
33
|
+
None,
|
|
34
|
+
description="Block ID cursor for pagination. Returns blocks that come after this block ID in the specified sort order",
|
|
35
|
+
),
|
|
36
|
+
order: Literal["asc", "desc"] = Query(
|
|
37
|
+
"asc", description="Sort order for blocks by creation time. 'asc' for oldest first, 'desc' for newest first"
|
|
38
|
+
),
|
|
39
|
+
order_by: Literal["created_at"] = Query("created_at", description="Field to sort by"),
|
|
40
|
+
label_search: Optional[str] = Query(
|
|
41
|
+
None,
|
|
42
|
+
description=("Search blocks by label. If provided, returns blocks that match this label. This is a full-text search on labels."),
|
|
43
|
+
),
|
|
44
|
+
description_search: Optional[str] = Query(
|
|
45
|
+
None,
|
|
46
|
+
description=(
|
|
47
|
+
"Search blocks by description. If provided, returns blocks that match this description. "
|
|
48
|
+
"This is a full-text search on block descriptions."
|
|
49
|
+
),
|
|
50
|
+
),
|
|
51
|
+
value_search: Optional[str] = Query(
|
|
52
|
+
None,
|
|
53
|
+
description=("Search blocks by value. If provided, returns blocks that match this value."),
|
|
54
|
+
),
|
|
55
|
+
connected_to_agents_count_gt: Optional[int] = Query(
|
|
56
|
+
None,
|
|
57
|
+
description=(
|
|
58
|
+
"Filter blocks by the number of connected agents. "
|
|
59
|
+
"If provided, returns blocks that have more than this number of connected agents."
|
|
60
|
+
),
|
|
61
|
+
),
|
|
62
|
+
connected_to_agents_count_lt: Optional[int] = Query(
|
|
63
|
+
None,
|
|
64
|
+
description=(
|
|
65
|
+
"Filter blocks by the number of connected agents. "
|
|
66
|
+
"If provided, returns blocks that have less than this number of connected agents."
|
|
67
|
+
),
|
|
68
|
+
),
|
|
69
|
+
connected_to_agents_count_eq: Optional[List[int]] = Query(
|
|
70
|
+
None,
|
|
71
|
+
description=(
|
|
72
|
+
"Filter blocks by the exact number of connected agents. "
|
|
73
|
+
"If provided, returns blocks that have exactly this number of connected agents."
|
|
74
|
+
),
|
|
75
|
+
),
|
|
76
|
+
show_hidden_blocks: bool | None = Query(
|
|
77
|
+
False,
|
|
78
|
+
include_in_schema=False,
|
|
79
|
+
description="If set to True, include blocks marked as hidden in the results.",
|
|
80
|
+
),
|
|
81
|
+
server: SyncServer = Depends(get_letta_server),
|
|
82
|
+
headers: HeaderParams = Depends(get_headers),
|
|
83
|
+
):
|
|
84
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
|
85
|
+
return await server.block_manager.get_blocks_async(
|
|
86
|
+
actor=actor,
|
|
87
|
+
label=label,
|
|
88
|
+
is_template=templates_only,
|
|
89
|
+
value_search=value_search,
|
|
90
|
+
label_search=label_search,
|
|
91
|
+
description_search=description_search,
|
|
92
|
+
template_name=name,
|
|
93
|
+
identity_id=identity_id,
|
|
94
|
+
identifier_keys=identifier_keys,
|
|
95
|
+
project_id=project_id,
|
|
96
|
+
before=before,
|
|
97
|
+
connected_to_agents_count_gt=connected_to_agents_count_gt,
|
|
98
|
+
connected_to_agents_count_lt=connected_to_agents_count_lt,
|
|
99
|
+
connected_to_agents_count_eq=connected_to_agents_count_eq,
|
|
100
|
+
limit=limit,
|
|
101
|
+
after=after,
|
|
102
|
+
ascending=(order == "asc"),
|
|
103
|
+
show_hidden_blocks=show_hidden_blocks,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@router.post("/", response_model=Block, operation_id="create_internal_block")
|
|
108
|
+
async def create_block(
|
|
109
|
+
create_block: CreateBlock = Body(...),
|
|
110
|
+
server: SyncServer = Depends(get_letta_server),
|
|
111
|
+
headers: HeaderParams = Depends(get_headers),
|
|
112
|
+
):
|
|
113
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
|
114
|
+
block = Block(**create_block.model_dump())
|
|
115
|
+
return await server.block_manager.create_or_update_block_async(actor=actor, block=block)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@router.delete("/{block_id}", operation_id="delete_internal_block")
|
|
119
|
+
async def delete_block(
|
|
120
|
+
block_id: BlockId,
|
|
121
|
+
server: SyncServer = Depends(get_letta_server),
|
|
122
|
+
headers: HeaderParams = Depends(get_headers),
|
|
123
|
+
):
|
|
124
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
|
125
|
+
await server.block_manager.delete_block_async(block_id=block_id, actor=actor)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@router.get("/{block_id}/agents", response_model=List[AgentState], operation_id="list_agents_for_internal_block")
|
|
129
|
+
async def list_agents_for_block(
|
|
130
|
+
block_id: BlockId,
|
|
131
|
+
before: Optional[str] = Query(
|
|
132
|
+
None,
|
|
133
|
+
description="Agent ID cursor for pagination. Returns agents that come before this agent ID in the specified sort order",
|
|
134
|
+
),
|
|
135
|
+
after: Optional[str] = Query(
|
|
136
|
+
None,
|
|
137
|
+
description="Agent ID cursor for pagination. Returns agents that come after this agent ID in the specified sort order",
|
|
138
|
+
),
|
|
139
|
+
limit: Optional[int] = Query(50, description="Maximum number of agents to return"),
|
|
140
|
+
order: Literal["asc", "desc"] = Query(
|
|
141
|
+
"desc", description="Sort order for agents by creation time. 'asc' for oldest first, 'desc' for newest first"
|
|
142
|
+
),
|
|
143
|
+
order_by: Literal["created_at"] = Query("created_at", description="Field to sort by"),
|
|
144
|
+
include_relationships: list[str] | None = Query(
|
|
145
|
+
None,
|
|
146
|
+
description=(
|
|
147
|
+
"Specify which relational fields (e.g., 'tools', 'sources', 'memory') to include in the response. "
|
|
148
|
+
"If not provided, all relationships are loaded by default. "
|
|
149
|
+
"Using this can optimize performance by reducing unnecessary joins."
|
|
150
|
+
"This is a legacy parameter, and no longer supported after 1.0.0 SDK versions."
|
|
151
|
+
),
|
|
152
|
+
),
|
|
153
|
+
include: List[str] = Query(
|
|
154
|
+
[],
|
|
155
|
+
description=("Specify which relational fields to include in the response. No relationships are included by default."),
|
|
156
|
+
),
|
|
157
|
+
server: SyncServer = Depends(get_letta_server),
|
|
158
|
+
headers: HeaderParams = Depends(get_headers),
|
|
159
|
+
):
|
|
160
|
+
"""
|
|
161
|
+
Retrieves all agents associated with the specified block.
|
|
162
|
+
Raises a 404 if the block does not exist.
|
|
163
|
+
"""
|
|
164
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
|
165
|
+
if include_relationships is None and is_1_0_sdk_version(headers):
|
|
166
|
+
include_relationships = [] # don't default include all if using new SDK version
|
|
167
|
+
agents = await server.block_manager.get_agents_for_block_async(
|
|
168
|
+
block_id=block_id,
|
|
169
|
+
before=before,
|
|
170
|
+
after=after,
|
|
171
|
+
limit=limit,
|
|
172
|
+
ascending=(order == "asc"),
|
|
173
|
+
include_relationships=include_relationships,
|
|
174
|
+
include=include,
|
|
175
|
+
actor=actor,
|
|
176
|
+
)
|
|
177
|
+
return agents
|