fix: path validator had weird fastapi shared object memory bug (#5594)
* fix weird path param conflict * move to factory model * openapi * use type hinting and import annotations * re add after mc resolution
This commit is contained in:
@@ -2521,9 +2521,14 @@
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"minLength": 43,
|
||||
"maxLength": 43,
|
||||
"pattern": "^source-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'",
|
||||
"examples": ["source-123e4567-e89b-42d3-8456-426614174000"],
|
||||
"title": "Folder Id"
|
||||
}
|
||||
},
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -2561,9 +2566,14 @@
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"minLength": 43,
|
||||
"maxLength": 43,
|
||||
"pattern": "^source-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'",
|
||||
"examples": ["source-123e4567-e89b-42d3-8456-426614174000"],
|
||||
"title": "Folder Id"
|
||||
}
|
||||
},
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'"
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
@@ -2611,9 +2621,14 @@
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"minLength": 43,
|
||||
"maxLength": 43,
|
||||
"pattern": "^source-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'",
|
||||
"examples": ["source-123e4567-e89b-42d3-8456-426614174000"],
|
||||
"title": "Folder Id"
|
||||
}
|
||||
},
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -2910,9 +2925,14 @@
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"minLength": 43,
|
||||
"maxLength": 43,
|
||||
"pattern": "^source-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'",
|
||||
"examples": ["source-123e4567-e89b-42d3-8456-426614174000"],
|
||||
"title": "Folder Id"
|
||||
}
|
||||
},
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'"
|
||||
},
|
||||
{
|
||||
"name": "duplicate_handling",
|
||||
@@ -2991,9 +3011,14 @@
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"minLength": 43,
|
||||
"maxLength": 43,
|
||||
"pattern": "^source-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'",
|
||||
"examples": ["source-123e4567-e89b-42d3-8456-426614174000"],
|
||||
"title": "Folder Id"
|
||||
}
|
||||
},
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'"
|
||||
},
|
||||
{
|
||||
"name": "before",
|
||||
@@ -3118,9 +3143,14 @@
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"minLength": 43,
|
||||
"maxLength": 43,
|
||||
"pattern": "^source-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'",
|
||||
"examples": ["source-123e4567-e89b-42d3-8456-426614174000"],
|
||||
"title": "Folder Id"
|
||||
}
|
||||
},
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'"
|
||||
},
|
||||
{
|
||||
"name": "before",
|
||||
@@ -3245,9 +3275,14 @@
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"minLength": 43,
|
||||
"maxLength": 43,
|
||||
"pattern": "^source-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'",
|
||||
"examples": ["source-123e4567-e89b-42d3-8456-426614174000"],
|
||||
"title": "Folder Id"
|
||||
}
|
||||
},
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'"
|
||||
},
|
||||
{
|
||||
"name": "before",
|
||||
@@ -3384,9 +3419,14 @@
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"minLength": 43,
|
||||
"maxLength": 43,
|
||||
"pattern": "^source-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'",
|
||||
"examples": ["source-123e4567-e89b-42d3-8456-426614174000"],
|
||||
"title": "Folder Id"
|
||||
}
|
||||
},
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'"
|
||||
},
|
||||
{
|
||||
"name": "file_id",
|
||||
@@ -3394,9 +3434,14 @@
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"minLength": 41,
|
||||
"maxLength": 41,
|
||||
"pattern": "^file-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
|
||||
"description": "The ID of the file in the format 'file-<uuid4>'",
|
||||
"examples": ["file-123e4567-e89b-42d3-8456-426614174000"],
|
||||
"title": "File Id"
|
||||
}
|
||||
},
|
||||
"description": "The ID of the file in the format 'file-<uuid4>'"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -4587,7 +4632,7 @@
|
||||
"operationId": "attach_folder_to_agent",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "source_id",
|
||||
"name": "folder_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
@@ -4597,7 +4642,7 @@
|
||||
"pattern": "^source-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'",
|
||||
"examples": ["source-123e4567-e89b-42d3-8456-426614174000"],
|
||||
"title": "Source Id"
|
||||
"title": "Folder Id"
|
||||
},
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'"
|
||||
},
|
||||
@@ -4712,7 +4757,7 @@
|
||||
"operationId": "detach_folder_from_agent",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "source_id",
|
||||
"name": "folder_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
@@ -4722,7 +4767,7 @@
|
||||
"pattern": "^source-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'",
|
||||
"examples": ["source-123e4567-e89b-42d3-8456-426614174000"],
|
||||
"title": "Source Id"
|
||||
"title": "Folder Id"
|
||||
},
|
||||
"description": "The ID of the source in the format 'source-<uuid4>'"
|
||||
},
|
||||
|
||||
@@ -59,7 +59,7 @@ from letta.services.lettuce import LettuceClient
|
||||
from letta.services.run_manager import RunManager
|
||||
from letta.settings import settings
|
||||
from letta.utils import safe_create_shielded_task, safe_create_task, truncate_file_visible_content
|
||||
from letta.validators import PATH_VALIDATORS
|
||||
from letta.validators import AgentId, BlockId, FileId, MessageId, SourceId, ToolId
|
||||
|
||||
# These can be forward refs, but because Fastapi needs them at runtime the must be imported normally
|
||||
|
||||
@@ -170,7 +170,7 @@ class IndentedORJSONResponse(Response):
|
||||
|
||||
@router.get("/{agent_id}/export", response_class=IndentedORJSONResponse, operation_id="export_agent")
|
||||
async def export_agent(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: str = AgentId,
|
||||
max_steps: int = Query(100, deprecated=True),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -345,7 +345,7 @@ async def import_agent(
|
||||
|
||||
@router.get("/{agent_id}/context", response_model=ContextWindowOverview, operation_id="retrieve_agent_context_window", deprecated=True)
|
||||
async def retrieve_agent_context_window(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -385,7 +385,7 @@ async def create_agent(
|
||||
|
||||
@router.patch("/{agent_id}", response_model=AgentState, operation_id="modify_agent")
|
||||
async def modify_agent(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
update_agent: UpdateAgent = Body(...),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -397,7 +397,7 @@ async def modify_agent(
|
||||
|
||||
@router.get("/{agent_id}/tools", response_model=list[Tool], operation_id="list_tools_for_agent")
|
||||
async def list_tools_for_agent(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
before: Optional[str] = Query(
|
||||
@@ -426,8 +426,8 @@ async def list_tools_for_agent(
|
||||
|
||||
@router.patch("/{agent_id}/tools/attach/{tool_id}", response_model=AgentState, operation_id="attach_tool_to_agent")
|
||||
async def attach_tool_to_agent(
|
||||
tool_id: str = PATH_VALIDATORS[BaseTool.__id_prefix__],
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
tool_id: ToolId,
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -442,8 +442,8 @@ async def attach_tool_to_agent(
|
||||
|
||||
@router.patch("/{agent_id}/tools/detach/{tool_id}", response_model=AgentState, operation_id="detach_tool_from_agent")
|
||||
async def detach_tool_from_agent(
|
||||
tool_id: str = PATH_VALIDATORS[BaseTool.__id_prefix__],
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
tool_id: ToolId,
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -460,7 +460,7 @@ async def detach_tool_from_agent(
|
||||
async def modify_approval_for_tool(
|
||||
tool_name: str,
|
||||
requires_approval: bool,
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -477,8 +477,8 @@ async def modify_approval_for_tool(
|
||||
|
||||
@router.patch("/{agent_id}/sources/attach/{source_id}", response_model=AgentState, operation_id="attach_source_to_agent", deprecated=True)
|
||||
async def attach_source(
|
||||
source_id: str = PATH_VALIDATORS[BaseSource.__id_prefix__],
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
source_id: SourceId,
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -504,8 +504,8 @@ async def attach_source(
|
||||
|
||||
@router.patch("/{agent_id}/folders/attach/{folder_id}", response_model=AgentState, operation_id="attach_folder_to_agent")
|
||||
async def attach_folder_to_agent(
|
||||
folder_id: str = PATH_VALIDATORS[BaseSource.__id_prefix__],
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
folder_id: SourceId,
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -531,8 +531,8 @@ async def attach_folder_to_agent(
|
||||
|
||||
@router.patch("/{agent_id}/sources/detach/{source_id}", response_model=AgentState, operation_id="detach_source_from_agent", deprecated=True)
|
||||
async def detach_source(
|
||||
source_id: str = PATH_VALIDATORS[BaseSource.__id_prefix__],
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
source_id: SourceId,
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -561,8 +561,8 @@ async def detach_source(
|
||||
|
||||
@router.patch("/{agent_id}/folders/detach/{folder_id}", response_model=AgentState, operation_id="detach_folder_from_agent")
|
||||
async def detach_folder_from_agent(
|
||||
folder_id: str = PATH_VALIDATORS[BaseSource.__id_prefix__],
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
folder_id: SourceId,
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -591,7 +591,7 @@ async def detach_folder_from_agent(
|
||||
|
||||
@router.patch("/{agent_id}/files/close-all", response_model=List[str], operation_id="close_all_files_for_agent")
|
||||
async def close_all_files_for_agent(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -608,8 +608,8 @@ async def close_all_files_for_agent(
|
||||
|
||||
@router.patch("/{agent_id}/files/{file_id}/open", response_model=List[str], operation_id="open_file_for_agent")
|
||||
async def open_file_for_agent(
|
||||
file_id: str = PATH_VALIDATORS[FileMetadataBase.__id_prefix__],
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
file_id: FileId,
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -657,8 +657,8 @@ async def open_file_for_agent(
|
||||
|
||||
@router.patch("/{agent_id}/files/{file_id}/close", response_model=None, operation_id="close_file_for_agent")
|
||||
async def close_file_for_agent(
|
||||
file_id: str = PATH_VALIDATORS[FileMetadataBase.__id_prefix__],
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
file_id: FileId,
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -682,7 +682,7 @@ async def close_file_for_agent(
|
||||
|
||||
@router.get("/{agent_id}", response_model=AgentState, operation_id="retrieve_agent")
|
||||
async def retrieve_agent(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
include_relationships: list[str] | None = Query(
|
||||
None,
|
||||
description=(
|
||||
@@ -705,7 +705,7 @@ async def retrieve_agent(
|
||||
|
||||
@router.delete("/{agent_id}", response_model=None, operation_id="delete_agent")
|
||||
async def delete_agent(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -719,7 +719,7 @@ async def delete_agent(
|
||||
|
||||
@router.get("/{agent_id}/sources", response_model=list[Source], operation_id="list_agent_sources", deprecated=True)
|
||||
async def list_agent_sources(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
before: Optional[str] = Query(
|
||||
@@ -750,7 +750,7 @@ async def list_agent_sources(
|
||||
|
||||
@router.get("/{agent_id}/folders", response_model=list[Source], operation_id="list_folders_for_agent")
|
||||
async def list_folders_for_agent(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
before: Optional[str] = Query(
|
||||
@@ -781,7 +781,7 @@ async def list_folders_for_agent(
|
||||
|
||||
@router.get("/{agent_id}/files", response_model=PaginatedAgentFiles, operation_id="list_files_for_agent")
|
||||
async def list_files_for_agent(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
before: Optional[str] = Query(
|
||||
None, description="File ID cursor for pagination. Returns files that come before this file ID in the specified sort order"
|
||||
),
|
||||
@@ -846,7 +846,7 @@ async def list_files_for_agent(
|
||||
# TODO: remove? can also get with agent blocks
|
||||
@router.get("/{agent_id}/core-memory", response_model=Memory, operation_id="retrieve_agent_memory", deprecated=True)
|
||||
async def retrieve_agent_memory(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -862,7 +862,7 @@ async def retrieve_agent_memory(
|
||||
@router.get("/{agent_id}/core-memory/blocks/{block_label}", response_model=Block, operation_id="retrieve_core_memory_block")
|
||||
async def retrieve_block_for_agent(
|
||||
block_label: str,
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -876,7 +876,7 @@ async def retrieve_block_for_agent(
|
||||
|
||||
@router.get("/{agent_id}/core-memory/blocks", response_model=list[Block], operation_id="list_core_memory_blocks")
|
||||
async def list_blocks_for_agent(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
before: Optional[str] = Query(
|
||||
@@ -909,7 +909,7 @@ async def list_blocks_for_agent(
|
||||
@router.patch("/{agent_id}/core-memory/blocks/{block_label}", response_model=Block, operation_id="modify_core_memory_block")
|
||||
async def modify_block_for_agent(
|
||||
block_label: str,
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
block_update: BlockUpdate = Body(...),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -931,8 +931,8 @@ async def modify_block_for_agent(
|
||||
|
||||
@router.patch("/{agent_id}/core-memory/blocks/attach/{block_id}", response_model=AgentState, operation_id="attach_core_memory_block")
|
||||
async def attach_block_to_agent(
|
||||
block_id: str = PATH_VALIDATORS[BaseBlock.__id_prefix__],
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
block_id: BlockId,
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -945,8 +945,8 @@ async def attach_block_to_agent(
|
||||
|
||||
@router.patch("/{agent_id}/core-memory/blocks/detach/{block_id}", response_model=AgentState, operation_id="detach_core_memory_block")
|
||||
async def detach_block_from_agent(
|
||||
block_id: str = PATH_VALIDATORS[BaseBlock.__id_prefix__],
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
block_id: BlockId,
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -959,7 +959,7 @@ async def detach_block_from_agent(
|
||||
|
||||
@router.get("/{agent_id}/archival-memory", response_model=list[Passage], operation_id="list_passages", deprecated=True)
|
||||
async def list_passages(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
after: str | None = Query(None, description="Unique ID of the memory to start the query range at."),
|
||||
before: str | None = Query(None, description="Unique ID of the memory to end the query range at."),
|
||||
@@ -988,7 +988,7 @@ async def list_passages(
|
||||
|
||||
@router.post("/{agent_id}/archival-memory", response_model=list[Passage], operation_id="create_passage", deprecated=True)
|
||||
async def create_passage(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
request: CreateArchivalMemory = Body(...),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -1010,7 +1010,7 @@ async def create_passage(
|
||||
deprecated=True,
|
||||
)
|
||||
async def search_archival_memory(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
query: str = Query(..., description="String to search for using semantic similarity"),
|
||||
tags: Optional[List[str]] = Query(None, description="Optional list of tags to filter search results"),
|
||||
tag_match_mode: Literal["any", "all"] = Query(
|
||||
@@ -1058,7 +1058,7 @@ async def search_archival_memory(
|
||||
@router.delete("/{agent_id}/archival-memory/{memory_id}", response_model=None, operation_id="delete_passage", deprecated=True)
|
||||
async def delete_passage(
|
||||
memory_id: str,
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
# memory_id: str = Query(..., description="Unique ID of the memory to be deleted."),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -1079,7 +1079,7 @@ AgentMessagesResponse = Annotated[
|
||||
|
||||
@router.get("/{agent_id}/messages", response_model=AgentMessagesResponse, operation_id="list_messages")
|
||||
async def list_messages(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
before: Optional[str] = Query(
|
||||
None, description="Message ID cursor for pagination. Returns messages that come before this message ID in the specified sort order"
|
||||
@@ -1124,8 +1124,8 @@ async def list_messages(
|
||||
|
||||
@router.patch("/{agent_id}/messages/{message_id}", response_model=LettaMessageUnion, operation_id="modify_message")
|
||||
async def modify_message(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__], # backwards compatible. Consider removing for v1
|
||||
message_id: str = PATH_VALIDATORS[BaseMessage.__id_prefix__],
|
||||
agent_id: AgentId, # backwards compatible. Consider removing for v1
|
||||
message_id: MessageId,
|
||||
request: LettaMessageUpdateUnion = Body(...),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -1148,7 +1148,7 @@ async def modify_message(
|
||||
)
|
||||
async def send_message(
|
||||
request_obj: Request, # FastAPI Request
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
request: LettaRequest = Body(...),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -1275,7 +1275,7 @@ async def send_message(
|
||||
)
|
||||
async def send_message_streaming(
|
||||
request_obj: Request, # FastAPI Request
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
request: LettaStreamingRequest = Body(...),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -1308,7 +1308,7 @@ class CancelAgentRunRequest(BaseModel):
|
||||
|
||||
@router.post("/{agent_id}/messages/cancel", operation_id="cancel_message")
|
||||
async def cancel_message(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
request: CancelAgentRunRequest = Body(None),
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -1484,7 +1484,7 @@ async def _process_message_background(
|
||||
operation_id="create_agent_message_async",
|
||||
)
|
||||
async def send_message_async(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
request: LettaAsyncRequest = Body(...),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -1588,7 +1588,7 @@ async def send_message_async(
|
||||
|
||||
@router.patch("/{agent_id}/reset-messages", response_model=AgentState, operation_id="reset_messages")
|
||||
async def reset_messages(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
add_default_initial_messages: bool = Query(default=False, description="If true, adds the default initial messages after resetting."),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -1602,7 +1602,7 @@ async def reset_messages(
|
||||
|
||||
@router.get("/{agent_id}/groups", response_model=list[Group], operation_id="list_groups_for_agent")
|
||||
async def list_groups_for_agent(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
manager_type: str | None = Query(None, description="Manager type to filter groups by"),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -1638,7 +1638,7 @@ async def list_groups_for_agent(
|
||||
operation_id="preview_raw_payload",
|
||||
)
|
||||
async def preview_raw_payload(
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
request: Union[LettaRequest, LettaStreamingRequest] = Body(...),
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -1684,7 +1684,7 @@ async def preview_raw_payload(
|
||||
@router.post("/{agent_id}/summarize", status_code=204, operation_id="summarize_agent_conversation")
|
||||
async def summarize_agent_conversation(
|
||||
request_obj: Request, # FastAPI Request
|
||||
agent_id: str = PATH_VALIDATORS[AgentState.__id_prefix__],
|
||||
agent_id: AgentId,
|
||||
max_message_length: int = Query(..., description="Maximum number of messages to retain after summarization."),
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
|
||||
@@ -6,7 +6,7 @@ from pydantic import BaseModel
|
||||
from letta.schemas.archive import Archive as PydanticArchive, ArchiveBase
|
||||
from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
|
||||
from letta.server.server import SyncServer
|
||||
from letta.validators import PATH_VALIDATORS
|
||||
from letta.validators import ArchiveId
|
||||
|
||||
router = APIRouter(prefix="/archives", tags=["archives"])
|
||||
|
||||
@@ -85,8 +85,8 @@ async def list_archives(
|
||||
|
||||
@router.patch("/{archive_id}", response_model=PydanticArchive, operation_id="modify_archive")
|
||||
async def modify_archive(
|
||||
archive_id: ArchiveId,
|
||||
archive: ArchiveUpdateRequest = Body(...),
|
||||
archive_id: str = PATH_VALIDATORS[ArchiveBase.__id_prefix__],
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
|
||||
@@ -7,7 +7,7 @@ from letta.schemas.agent import AgentState
|
||||
from letta.schemas.block import BaseBlock, Block, BlockUpdate, CreateBlock
|
||||
from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
|
||||
from letta.server.server import SyncServer
|
||||
from letta.validators import PATH_VALIDATORS
|
||||
from letta.validators import BlockId
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
@@ -129,7 +129,7 @@ async def create_block(
|
||||
|
||||
@router.patch("/{block_id}", response_model=Block, operation_id="modify_block")
|
||||
async def modify_block(
|
||||
block_id: str = PATH_VALIDATORS[BaseBlock.__id_prefix__],
|
||||
block_id: BlockId,
|
||||
block_update: BlockUpdate = Body(...),
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -140,7 +140,7 @@ async def modify_block(
|
||||
|
||||
@router.delete("/{block_id}", operation_id="delete_block")
|
||||
async def delete_block(
|
||||
block_id: str = PATH_VALIDATORS[BaseBlock.__id_prefix__],
|
||||
block_id: BlockId,
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -150,7 +150,7 @@ async def delete_block(
|
||||
|
||||
@router.get("/{block_id}", response_model=Block, operation_id="retrieve_block")
|
||||
async def retrieve_block(
|
||||
block_id: str = PATH_VALIDATORS[BaseBlock.__id_prefix__],
|
||||
block_id: BlockId,
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -163,7 +163,7 @@ async def retrieve_block(
|
||||
|
||||
@router.get("/{block_id}/agents", response_model=List[AgentState], operation_id="list_agents_for_block")
|
||||
async def list_agents_for_block(
|
||||
block_id: str = PATH_VALIDATORS[BaseBlock.__id_prefix__],
|
||||
block_id: BlockId,
|
||||
before: Optional[str] = Query(
|
||||
None,
|
||||
description="Agent ID cursor for pagination. Returns agents that come before this agent ID in the specified sort order",
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import mimetypes
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from pathlib import Path as PathLibPath
|
||||
from typing import List, Literal, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Path as PathParam, Query, UploadFile
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, UploadFile
|
||||
from starlette import status
|
||||
from starlette.responses import Response
|
||||
|
||||
@@ -37,7 +37,7 @@ from letta.services.file_processor.parser.markitdown_parser import MarkitdownFil
|
||||
from letta.services.file_processor.parser.mistral_parser import MistralFileParser
|
||||
from letta.settings import settings
|
||||
from letta.utils import safe_create_file_processing_task, safe_create_task, sanitize_filename
|
||||
from letta.validators import PATH_VALIDATORS, PRIMITIVE_ID_PATTERNS
|
||||
from letta.validators import FileId, FolderId
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -62,7 +62,7 @@ async def count_folders(
|
||||
|
||||
@router.get("/{folder_id}", response_model=Folder, operation_id="retrieve_folder")
|
||||
async def retrieve_folder(
|
||||
folder_id: str = PathParam(..., pattern=PRIMITIVE_ID_PATTERNS[BaseFolder.__id_prefix__].pattern),
|
||||
folder_id: FolderId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -178,7 +178,7 @@ async def create_folder(
|
||||
@router.patch("/{folder_id}", response_model=Folder, operation_id="modify_folder")
|
||||
async def modify_folder(
|
||||
folder: SourceUpdate,
|
||||
folder_id: str = PathParam(..., pattern=PRIMITIVE_ID_PATTERNS[BaseFolder.__id_prefix__].pattern),
|
||||
folder_id: FolderId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -193,7 +193,7 @@ async def modify_folder(
|
||||
|
||||
@router.delete("/{folder_id}", response_model=None, operation_id="delete_folder")
|
||||
async def delete_folder(
|
||||
folder_id: str = PathParam(..., pattern=PRIMITIVE_ID_PATTERNS[BaseFolder.__id_prefix__].pattern),
|
||||
folder_id: FolderId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -229,7 +229,7 @@ async def delete_folder(
|
||||
@router.post("/{folder_id}/upload", response_model=FileMetadata, operation_id="upload_file_to_folder")
|
||||
async def upload_file_to_folder(
|
||||
file: UploadFile,
|
||||
folder_id: str = PathParam(..., pattern=PRIMITIVE_ID_PATTERNS[BaseFolder.__id_prefix__].pattern),
|
||||
folder_id: FolderId,
|
||||
duplicate_handling: DuplicateFileHandling = Query(DuplicateFileHandling.SUFFIX, description="How to handle duplicate filenames"),
|
||||
name: Optional[str] = Query(None, description="Optional custom name to override the uploaded file's name"),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
@@ -255,7 +255,7 @@ async def upload_file_to_folder(
|
||||
media_type = (guessed or "").lower()
|
||||
|
||||
if media_type not in allowed_media_types:
|
||||
ext = Path(file.filename).suffix.lower()
|
||||
ext = PathLibPath(file.filename).suffix.lower()
|
||||
ext_map = get_extension_to_mime_type_map()
|
||||
media_type = ext_map.get(ext, media_type)
|
||||
|
||||
@@ -344,7 +344,7 @@ async def upload_file_to_folder(
|
||||
|
||||
@router.get("/{folder_id}/agents", response_model=List[str], operation_id="list_agents_for_folder")
|
||||
async def list_agents_for_folder(
|
||||
folder_id: str = PathParam(..., pattern=PRIMITIVE_ID_PATTERNS[BaseFolder.__id_prefix__].pattern),
|
||||
folder_id: FolderId,
|
||||
before: Optional[str] = Query(
|
||||
None,
|
||||
description="Agent ID cursor for pagination. Returns agents that come before this agent ID in the specified sort order",
|
||||
@@ -377,7 +377,7 @@ async def list_agents_for_folder(
|
||||
|
||||
@router.get("/{folder_id}/passages", response_model=List[Passage], operation_id="list_folder_passages")
|
||||
async def list_folder_passages(
|
||||
folder_id: str = PathParam(..., pattern=PRIMITIVE_ID_PATTERNS[BaseFolder.__id_prefix__].pattern),
|
||||
folder_id: FolderId,
|
||||
before: Optional[str] = Query(
|
||||
None,
|
||||
description="Passage ID cursor for pagination. Returns passages that come before this passage ID in the specified sort order",
|
||||
@@ -410,7 +410,7 @@ async def list_folder_passages(
|
||||
|
||||
@router.get("/{folder_id}/files", response_model=List[FileMetadata], operation_id="list_folder_files")
|
||||
async def list_folder_files(
|
||||
folder_id: str = PathParam(..., pattern=PRIMITIVE_ID_PATTERNS[BaseFolder.__id_prefix__].pattern),
|
||||
folder_id: FolderId,
|
||||
before: Optional[str] = Query(
|
||||
None,
|
||||
description="File ID cursor for pagination. Returns files that come before this file ID in the specified sort order",
|
||||
@@ -497,8 +497,8 @@ async def list_folder_files(
|
||||
# it's still good practice to return a status indicating the success or failure of the deletion
|
||||
@router.delete("/{folder_id}/{file_id}", status_code=204, operation_id="delete_file_from_folder")
|
||||
async def delete_file_from_folder(
|
||||
folder_id: str = PathParam(..., pattern=PRIMITIVE_ID_PATTERNS[BaseFolder.__id_prefix__].pattern),
|
||||
file_id: str = PathParam(..., pattern=PRIMITIVE_ID_PATTERNS[FileMetadataBase.__id_prefix__].pattern),
|
||||
folder_id: FolderId,
|
||||
file_id: FileId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
|
||||
@@ -12,7 +12,7 @@ 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 PATH_VALIDATORS
|
||||
from letta.validators import GroupId, MessageId
|
||||
|
||||
router = APIRouter(prefix="/groups", tags=["groups"])
|
||||
|
||||
@@ -70,7 +70,7 @@ async def count_groups(
|
||||
|
||||
@router.get("/{group_id}", response_model=Group, operation_id="retrieve_group")
|
||||
async def retrieve_group(
|
||||
group_id: str = PATH_VALIDATORS[GroupBase.__id_prefix__],
|
||||
group_id: GroupId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -99,7 +99,7 @@ async def create_group(
|
||||
|
||||
@router.patch("/{group_id}", response_model=Group, operation_id="modify_group")
|
||||
async def modify_group(
|
||||
group_id: str = PATH_VALIDATORS[GroupBase.__id_prefix__],
|
||||
group_id: GroupId,
|
||||
group: GroupUpdate = Body(...),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -116,7 +116,7 @@ async def modify_group(
|
||||
|
||||
@router.delete("/{group_id}", response_model=None, operation_id="delete_group")
|
||||
async def delete_group(
|
||||
group_id: str = PATH_VALIDATORS[GroupBase.__id_prefix__],
|
||||
group_id: GroupId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -134,7 +134,7 @@ async def delete_group(
|
||||
operation_id="send_group_message",
|
||||
)
|
||||
async def send_group_message(
|
||||
group_id: str = PATH_VALIDATORS[GroupBase.__id_prefix__],
|
||||
group_id: GroupId,
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
request: LettaRequest = Body(...),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -172,7 +172,7 @@ async def send_group_message(
|
||||
},
|
||||
)
|
||||
async def send_group_message_streaming(
|
||||
group_id: str = PATH_VALIDATORS[GroupBase.__id_prefix__],
|
||||
group_id: GroupId,
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
request: LettaStreamingRequest = Body(...),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -204,8 +204,8 @@ GroupMessagesResponse = Annotated[
|
||||
|
||||
@router.patch("/{group_id}/messages/{message_id}", response_model=LettaMessageUnion, operation_id="modify_group_message")
|
||||
async def modify_group_message(
|
||||
group_id: str = PATH_VALIDATORS[GroupBase.__id_prefix__],
|
||||
message_id: str = PATH_VALIDATORS[BaseMessage.__id_prefix__],
|
||||
group_id: GroupId,
|
||||
message_id: MessageId,
|
||||
request: LettaMessageUpdateUnion = Body(...),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -220,7 +220,7 @@ async def modify_group_message(
|
||||
|
||||
@router.get("/{group_id}/messages", response_model=GroupMessagesResponse, operation_id="list_group_messages")
|
||||
async def list_group_messages(
|
||||
group_id: str = PATH_VALIDATORS[GroupBase.__id_prefix__],
|
||||
group_id: GroupId,
|
||||
before: Optional[str] = Query(
|
||||
None,
|
||||
description="Message ID cursor for pagination. Returns messages that come before this message ID in the specified sort order",
|
||||
@@ -275,7 +275,7 @@ async def list_group_messages(
|
||||
|
||||
@router.patch("/{group_id}/reset-messages", response_model=None, operation_id="reset_group_messages")
|
||||
async def reset_group_messages(
|
||||
group_id: str = PATH_VALIDATORS[GroupBase.__id_prefix__],
|
||||
group_id: GroupId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
|
||||
@@ -7,7 +7,7 @@ from letta.schemas.agent import AgentState
|
||||
from letta.schemas.block import Block
|
||||
from letta.schemas.identity import Identity, IdentityBase, IdentityCreate, IdentityProperty, IdentityType, IdentityUpdate, IdentityUpsert
|
||||
from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
|
||||
from letta.validators import PATH_VALIDATORS
|
||||
from letta.validators import IdentityId
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from letta.server.server import SyncServer
|
||||
@@ -73,7 +73,7 @@ async def count_identities(
|
||||
|
||||
@router.get("/{identity_id}", tags=["identities"], response_model=Identity, operation_id="retrieve_identity")
|
||||
async def retrieve_identity(
|
||||
identity_id: str = PATH_VALIDATORS[IdentityBase.__id_prefix__],
|
||||
identity_id: IdentityId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -109,7 +109,7 @@ async def upsert_identity(
|
||||
|
||||
@router.patch("/{identity_id}", tags=["identities"], response_model=Identity, operation_id="update_identity")
|
||||
async def modify_identity(
|
||||
identity_id: str = PATH_VALIDATORS[IdentityBase.__id_prefix__],
|
||||
identity_id: IdentityId,
|
||||
identity: IdentityUpdate = Body(...),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -120,7 +120,7 @@ async def modify_identity(
|
||||
|
||||
@router.put("/{identity_id}/properties", tags=["identities"], operation_id="upsert_identity_properties")
|
||||
async def upsert_identity_properties(
|
||||
identity_id: str = PATH_VALIDATORS[IdentityBase.__id_prefix__],
|
||||
identity_id: IdentityId,
|
||||
properties: List[IdentityProperty] = Body(...),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -131,7 +131,7 @@ async def upsert_identity_properties(
|
||||
|
||||
@router.delete("/{identity_id}", tags=["identities"], operation_id="delete_identity")
|
||||
async def delete_identity(
|
||||
identity_id: str = PATH_VALIDATORS[IdentityBase.__id_prefix__],
|
||||
identity_id: IdentityId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -144,7 +144,7 @@ async def delete_identity(
|
||||
|
||||
@router.get("/{identity_id}/agents", response_model=List[AgentState], operation_id="list_agents_for_identity")
|
||||
async def list_agents_for_identity(
|
||||
identity_id: str = PATH_VALIDATORS[IdentityBase.__id_prefix__],
|
||||
identity_id: IdentityId,
|
||||
before: Optional[str] = Query(
|
||||
None,
|
||||
description="Agent ID cursor for pagination. Returns agents that come before this agent ID in the specified sort order",
|
||||
@@ -177,7 +177,7 @@ async def list_agents_for_identity(
|
||||
|
||||
@router.get("/{identity_id}/blocks", response_model=List[Block], operation_id="list_blocks_for_identity")
|
||||
async def list_blocks_for_identity(
|
||||
identity_id: str = PATH_VALIDATORS[IdentityBase.__id_prefix__],
|
||||
identity_id: IdentityId,
|
||||
before: Optional[str] = Query(
|
||||
None,
|
||||
description="Block ID cursor for pagination. Returns blocks that come before this block ID in the specified sort order",
|
||||
|
||||
@@ -8,7 +8,7 @@ from letta.schemas.job import Job, JobBase
|
||||
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 PATH_VALIDATORS
|
||||
from letta.validators import JobId
|
||||
|
||||
router = APIRouter(prefix="/jobs", tags=["jobs"])
|
||||
|
||||
@@ -89,7 +89,7 @@ async def list_active_jobs(
|
||||
|
||||
@router.get("/{job_id}", response_model=Job, operation_id="retrieve_job")
|
||||
async def retrieve_job(
|
||||
job_id: str = PATH_VALIDATORS[JobBase.__id_prefix__],
|
||||
job_id: JobId,
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
):
|
||||
@@ -102,7 +102,7 @@ async def retrieve_job(
|
||||
|
||||
@router.patch("/{job_id}/cancel", response_model=Job, operation_id="cancel_job")
|
||||
async def cancel_job(
|
||||
job_id: str = PATH_VALIDATORS[JobBase.__id_prefix__],
|
||||
job_id: JobId,
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
):
|
||||
@@ -127,7 +127,7 @@ async def cancel_job(
|
||||
|
||||
@router.delete("/{job_id}", response_model=Job, operation_id="delete_job")
|
||||
async def delete_job(
|
||||
job_id: str = PATH_VALIDATORS[JobBase.__id_prefix__],
|
||||
job_id: JobId,
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
):
|
||||
|
||||
@@ -6,7 +6,7 @@ from fastapi.responses import JSONResponse
|
||||
from letta.schemas.enums import ProviderType
|
||||
from letta.schemas.providers import Provider, ProviderBase, ProviderCheck, ProviderCreate, ProviderUpdate
|
||||
from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
|
||||
from letta.validators import PATH_VALIDATORS
|
||||
from letta.validators import ProviderId
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from letta.server.server import SyncServer
|
||||
@@ -46,7 +46,7 @@ async def list_providers(
|
||||
|
||||
@router.get("/{provider_id}", response_model=Provider, operation_id="retrieve_provider")
|
||||
async def retrieve_provider(
|
||||
provider_id: str = PATH_VALIDATORS[ProviderBase.__id_prefix__],
|
||||
provider_id: ProviderId,
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
):
|
||||
@@ -80,8 +80,8 @@ async def create_provider(
|
||||
|
||||
@router.patch("/{provider_id}", response_model=Provider, operation_id="modify_provider")
|
||||
async def modify_provider(
|
||||
provider_id: ProviderId,
|
||||
request: ProviderUpdate = Body(...),
|
||||
provider_id: str = PATH_VALIDATORS[ProviderBase.__id_prefix__],
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
):
|
||||
@@ -111,7 +111,7 @@ async def check_provider(
|
||||
|
||||
@router.post("/{provider_id}/check", response_model=None, operation_id="check_existing_provider")
|
||||
async def check_existing_provider(
|
||||
provider_id: str = PATH_VALIDATORS[ProviderBase.__id_prefix__],
|
||||
provider_id: ProviderId,
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
):
|
||||
@@ -136,7 +136,7 @@ async def check_existing_provider(
|
||||
|
||||
@router.delete("/{provider_id}", response_model=None, operation_id="delete_provider")
|
||||
async def delete_provider(
|
||||
provider_id: str = PATH_VALIDATORS[ProviderBase.__id_prefix__],
|
||||
provider_id: ProviderId,
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
):
|
||||
|
||||
@@ -22,7 +22,7 @@ from letta.schemas.sandbox_config import (
|
||||
from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
|
||||
from letta.server.server import SyncServer
|
||||
from letta.services.helpers.tool_execution_helper import create_venv_for_local_sandbox, install_pip_requirements_for_sandbox
|
||||
from letta.validators import PATH_VALIDATORS
|
||||
from letta.validators import SandboxConfigId
|
||||
|
||||
router = APIRouter(prefix="/sandbox-config", tags=["sandbox-config"])
|
||||
|
||||
@@ -90,7 +90,7 @@ async def create_custom_local_sandbox_config(
|
||||
@router.patch("/{sandbox_config_id}", response_model=PydanticSandboxConfig)
|
||||
async def update_sandbox_config(
|
||||
config_update: SandboxConfigUpdate,
|
||||
sandbox_config_id: str = PATH_VALIDATORS[SandboxConfigBase.__id_prefix__],
|
||||
sandbox_config_id: SandboxConfigId,
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -100,7 +100,7 @@ async def update_sandbox_config(
|
||||
|
||||
@router.delete("/{sandbox_config_id}", status_code=204)
|
||||
async def delete_sandbox_config(
|
||||
sandbox_config_id: str = PATH_VALIDATORS[SandboxConfigBase.__id_prefix__],
|
||||
sandbox_config_id: SandboxConfigId,
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -160,7 +160,7 @@ async def force_recreate_local_sandbox_venv(
|
||||
@router.post("/{sandbox_config_id}/environment-variable", response_model=PydanticEnvVar)
|
||||
async def create_sandbox_env_var(
|
||||
env_var_create: SandboxEnvironmentVariableCreate,
|
||||
sandbox_config_id: str = PATH_VALIDATORS[SandboxConfigBase.__id_prefix__],
|
||||
sandbox_config_id: SandboxConfigId,
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -191,7 +191,7 @@ async def delete_sandbox_env_var(
|
||||
|
||||
@router.get("/{sandbox_config_id}/environment-variable", response_model=List[PydanticEnvVar])
|
||||
async def list_sandbox_env_vars(
|
||||
sandbox_config_id: str = PATH_VALIDATORS[SandboxConfigBase.__id_prefix__],
|
||||
sandbox_config_id: SandboxConfigId,
|
||||
limit: int = Query(1000, description="Number of results to return"),
|
||||
after: Optional[str] = Query(None, description="Pagination cursor to fetch the next set of results"),
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
|
||||
@@ -36,7 +36,7 @@ from letta.services.file_processor.parser.markitdown_parser import MarkitdownFil
|
||||
from letta.services.file_processor.parser.mistral_parser import MistralFileParser
|
||||
from letta.settings import settings
|
||||
from letta.utils import safe_create_file_processing_task, safe_create_task, sanitize_filename
|
||||
from letta.validators import PATH_VALIDATORS
|
||||
from letta.validators import FileId, SourceId
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -60,7 +60,7 @@ async def count_sources(
|
||||
|
||||
@router.get("/{source_id}", response_model=Source, operation_id="retrieve_source", deprecated=True)
|
||||
async def retrieve_source(
|
||||
source_id: str = PATH_VALIDATORS[BaseSource.__id_prefix__],
|
||||
source_id: SourceId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -158,7 +158,7 @@ async def create_source(
|
||||
@router.patch("/{source_id}", response_model=Source, operation_id="modify_source", deprecated=True)
|
||||
async def modify_source(
|
||||
source: SourceUpdate,
|
||||
source_id: str = PATH_VALIDATORS[BaseSource.__id_prefix__],
|
||||
source_id: SourceId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -173,7 +173,7 @@ async def modify_source(
|
||||
|
||||
@router.delete("/{source_id}", response_model=None, operation_id="delete_source", deprecated=True)
|
||||
async def delete_source(
|
||||
source_id: str = PATH_VALIDATORS[BaseSource.__id_prefix__],
|
||||
source_id: SourceId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -209,7 +209,7 @@ async def delete_source(
|
||||
@router.post("/{source_id}/upload", response_model=FileMetadata, operation_id="upload_file_to_source", deprecated=True)
|
||||
async def upload_file_to_source(
|
||||
file: UploadFile,
|
||||
source_id: str = PATH_VALIDATORS[BaseSource.__id_prefix__],
|
||||
source_id: SourceId,
|
||||
duplicate_handling: DuplicateFileHandling = Query(DuplicateFileHandling.SUFFIX, description="How to handle duplicate filenames"),
|
||||
name: Optional[str] = Query(None, description="Optional custom name to override the uploaded file's name"),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
@@ -324,7 +324,7 @@ async def upload_file_to_source(
|
||||
|
||||
@router.get("/{source_id}/agents", response_model=List[str], operation_id="get_agents_for_source", deprecated=True)
|
||||
async def get_agents_for_source(
|
||||
source_id: str = PATH_VALIDATORS[BaseSource.__id_prefix__],
|
||||
source_id: SourceId,
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -337,7 +337,7 @@ async def get_agents_for_source(
|
||||
|
||||
@router.get("/{source_id}/passages", response_model=List[Passage], operation_id="list_source_passages", deprecated=True)
|
||||
async def list_source_passages(
|
||||
source_id: str = PATH_VALIDATORS[BaseSource.__id_prefix__],
|
||||
source_id: SourceId,
|
||||
after: Optional[str] = Query(None, description="Message after which to retrieve the returned messages."),
|
||||
before: Optional[str] = Query(None, description="Message before which to retrieve the returned messages."),
|
||||
limit: int = Query(100, description="Maximum number of messages to retrieve."),
|
||||
@@ -359,7 +359,7 @@ async def list_source_passages(
|
||||
|
||||
@router.get("/{source_id}/files", response_model=List[FileMetadata], operation_id="list_source_files", deprecated=True)
|
||||
async def list_source_files(
|
||||
source_id: str = PATH_VALIDATORS[BaseSource.__id_prefix__],
|
||||
source_id: SourceId,
|
||||
limit: int = Query(1000, description="Number of files to return"),
|
||||
after: Optional[str] = Query(None, description="Pagination cursor to fetch the next set of results"),
|
||||
include_content: bool = Query(False, description="Whether to include full file content"),
|
||||
@@ -387,8 +387,8 @@ async def list_source_files(
|
||||
|
||||
@router.get("/{source_id}/files/{file_id}", response_model=FileMetadata, operation_id="get_file_metadata", deprecated=True)
|
||||
async def get_file_metadata(
|
||||
source_id: str = PATH_VALIDATORS[BaseSource.__id_prefix__],
|
||||
file_id: str = PATH_VALIDATORS[FileMetadataBase.__id_prefix__],
|
||||
source_id: SourceId,
|
||||
file_id: FileId,
|
||||
include_content: bool = Query(False, description="Whether to include full file content"),
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
@@ -413,8 +413,8 @@ async def get_file_metadata(
|
||||
# it's still good practice to return a status indicating the success or failure of the deletion
|
||||
@router.delete("/{source_id}/{file_id}", status_code=204, operation_id="delete_file_from_source", deprecated=True)
|
||||
async def delete_file_from_source(
|
||||
source_id: str = PATH_VALIDATORS[BaseSource.__id_prefix__],
|
||||
file_id: str = PATH_VALIDATORS[FileMetadataBase.__id_prefix__],
|
||||
source_id: SourceId,
|
||||
file_id: FileId,
|
||||
server: "SyncServer" = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
|
||||
@@ -13,7 +13,7 @@ from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_le
|
||||
from letta.server.server import SyncServer
|
||||
from letta.services.step_manager import FeedbackType
|
||||
from letta.settings import settings
|
||||
from letta.validators import PATH_VALIDATORS
|
||||
from letta.validators import StepId
|
||||
|
||||
router = APIRouter(prefix="/steps", tags=["steps"])
|
||||
|
||||
@@ -70,7 +70,7 @@ async def list_steps(
|
||||
|
||||
@router.get("/{step_id}", response_model=Step, operation_id="retrieve_step")
|
||||
async def retrieve_step(
|
||||
step_id: str = PATH_VALIDATORS[StepBase.__id_prefix__],
|
||||
step_id: StepId,
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
):
|
||||
@@ -83,7 +83,7 @@ async def retrieve_step(
|
||||
|
||||
@router.get("/{step_id}/metrics", response_model=StepMetrics, operation_id="retrieve_metrics_for_step")
|
||||
async def retrieve_metrics_for_step(
|
||||
step_id: str = PATH_VALIDATORS[StepBase.__id_prefix__],
|
||||
step_id: StepId,
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
):
|
||||
@@ -96,7 +96,7 @@ async def retrieve_metrics_for_step(
|
||||
|
||||
@router.get("/{step_id}/trace", response_model=Optional[ProviderTrace], operation_id="retrieve_trace_for_step")
|
||||
async def retrieve_trace_for_step(
|
||||
step_id: str = PATH_VALIDATORS[StepBase.__id_prefix__],
|
||||
step_id: StepId,
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -119,8 +119,8 @@ class ModifyFeedbackRequest(BaseModel):
|
||||
|
||||
@router.patch("/{step_id}/feedback", response_model=Step, operation_id="modify_feedback_for_step")
|
||||
async def modify_feedback_for_step(
|
||||
step_id: StepId,
|
||||
request: ModifyFeedbackRequest = Body(...),
|
||||
step_id: str = PATH_VALIDATORS[StepBase.__id_prefix__],
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
):
|
||||
@@ -133,7 +133,7 @@ async def modify_feedback_for_step(
|
||||
|
||||
@router.get("/{step_id}/messages", response_model=List[LettaMessageUnion], operation_id="list_messages_for_step")
|
||||
async def list_messages_for_step(
|
||||
step_id: str = PATH_VALIDATORS[StepBase.__id_prefix__],
|
||||
step_id: StepId,
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
before: Optional[str] = Query(
|
||||
@@ -161,7 +161,7 @@ async def list_messages_for_step(
|
||||
@router.patch("/{step_id}/transaction/{transaction_id}", response_model=Step, operation_id="update_step_transaction_id")
|
||||
async def update_step_transaction_id(
|
||||
transaction_id: str,
|
||||
step_id: str = PATH_VALIDATORS[StepBase.__id_prefix__],
|
||||
step_id: StepId,
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
):
|
||||
|
||||
@@ -38,7 +38,7 @@ from letta.services.mcp.oauth_utils import MCPOAuthSession, drill_down_exception
|
||||
from letta.services.mcp.stdio_client import AsyncStdioMCPClient
|
||||
from letta.services.mcp.types import OauthStreamEvent
|
||||
from letta.settings import tool_settings
|
||||
from letta.validators import PATH_VALIDATORS
|
||||
from letta.validators import ToolId
|
||||
|
||||
router = APIRouter(prefix="/tools", tags=["tools"])
|
||||
|
||||
@@ -47,7 +47,7 @@ logger = get_logger(__name__)
|
||||
|
||||
@router.delete("/{tool_id}", operation_id="delete_tool")
|
||||
async def delete_tool(
|
||||
tool_id: str = PATH_VALIDATORS[BaseTool.__id_prefix__],
|
||||
tool_id: ToolId,
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -150,7 +150,7 @@ async def count_tools(
|
||||
|
||||
@router.get("/{tool_id}", response_model=Tool, operation_id="retrieve_tool")
|
||||
async def retrieve_tool(
|
||||
tool_id: str = PATH_VALIDATORS[BaseTool.__id_prefix__],
|
||||
tool_id: ToolId,
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
@@ -298,8 +298,8 @@ async def upsert_tool(
|
||||
|
||||
@router.patch("/{tool_id}", response_model=Tool, operation_id="modify_tool")
|
||||
async def modify_tool(
|
||||
tool_id: ToolId,
|
||||
request: ToolUpdate = Body(...),
|
||||
tool_id: str = PATH_VALIDATORS[BaseTool.__id_prefix__],
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
headers: HeaderParams = Depends(get_headers),
|
||||
):
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import re
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Path
|
||||
|
||||
@@ -44,17 +45,50 @@ PRIMITIVE_ID_PATTERNS = {
|
||||
for primitive in primitives
|
||||
}
|
||||
|
||||
PATH_VALIDATORS = {}
|
||||
for primitive in primitives:
|
||||
PATH_VALIDATORS[primitive] = Path(
|
||||
description=f"The ID of the {primitive} in the format '{primitive}-<uuid4>'",
|
||||
pattern=PRIMITIVE_ID_PATTERNS[primitive].pattern,
|
||||
examples=[f"{primitive}-123e4567-e89b-42d3-8456-426614174000"],
|
||||
# len(agent) + len("-") + len(uuid4)
|
||||
min_length=len(primitive) + 1 + 36,
|
||||
max_length=len(primitive) + 1 + 36,
|
||||
)
|
||||
|
||||
def _create_path_validator_factory(primitive: str):
|
||||
"""
|
||||
Creates a factory function that returns a fresh Path validator.
|
||||
|
||||
This avoids shared state issues when the same validator is used
|
||||
across multiple endpoints with different parameter names.
|
||||
"""
|
||||
|
||||
def factory():
|
||||
return Path(
|
||||
description=f"The ID of the {primitive} in the format '{primitive}-<uuid4>'",
|
||||
pattern=PRIMITIVE_ID_PATTERNS[primitive].pattern,
|
||||
examples=[f"{primitive}-123e4567-e89b-42d3-8456-426614174000"],
|
||||
min_length=len(primitive) + 1 + 36,
|
||||
max_length=len(primitive) + 1 + 36,
|
||||
)
|
||||
|
||||
return factory
|
||||
|
||||
|
||||
# PATH_VALIDATORS now contains factory functions, not Path objects
|
||||
# Usage: folder_id: str = PATH_VALIDATORS[BaseFolder.__id_prefix__]()
|
||||
PATH_VALIDATORS = {primitive: _create_path_validator_factory(primitive) for primitive in primitives}
|
||||
|
||||
|
||||
def is_valid_id(primitive: str, id: str) -> bool:
|
||||
return PRIMITIVE_ID_PATTERNS[primitive].match(id) is not None
|
||||
|
||||
|
||||
# Type aliases for common ID types
|
||||
# These can be used directly in route handler signatures for cleaner code
|
||||
AgentId = Annotated[str, PATH_VALIDATORS[AgentState.__id_prefix__]()]
|
||||
ToolId = Annotated[str, PATH_VALIDATORS[BaseTool.__id_prefix__]()]
|
||||
SourceId = Annotated[str, PATH_VALIDATORS[BaseSource.__id_prefix__]()]
|
||||
BlockId = Annotated[str, PATH_VALIDATORS[BaseBlock.__id_prefix__]()]
|
||||
MessageId = Annotated[str, PATH_VALIDATORS[BaseMessage.__id_prefix__]()]
|
||||
RunId = Annotated[str, PATH_VALIDATORS[RunBase.__id_prefix__]()]
|
||||
JobId = Annotated[str, PATH_VALIDATORS[JobBase.__id_prefix__]()]
|
||||
GroupId = Annotated[str, PATH_VALIDATORS[GroupBase.__id_prefix__]()]
|
||||
FileId = Annotated[str, PATH_VALIDATORS[FileMetadataBase.__id_prefix__]()]
|
||||
FolderId = Annotated[str, PATH_VALIDATORS[BaseFolder.__id_prefix__]()]
|
||||
ArchiveId = Annotated[str, PATH_VALIDATORS[ArchiveBase.__id_prefix__]()]
|
||||
ProviderId = Annotated[str, PATH_VALIDATORS[ProviderBase.__id_prefix__]()]
|
||||
SandboxConfigId = Annotated[str, PATH_VALIDATORS[SandboxConfigBase.__id_prefix__]()]
|
||||
StepId = Annotated[str, PATH_VALIDATORS[StepBase.__id_prefix__]()]
|
||||
IdentityId = Annotated[str, PATH_VALIDATORS[IdentityBase.__id_prefix__]()]
|
||||
|
||||
Reference in New Issue
Block a user