From 697be58b780d380a3377f32a8aa5c4447f7f1066 Mon Sep 17 00:00:00 2001 From: cthomas Date: Fri, 29 Aug 2025 15:22:34 -0700 Subject: [PATCH] feat: make new message create type field optional [LET-4116] (#4319) feat: make new message create type field optional --- letta/schemas/letta_request.py | 13 ++++++++++--- letta/schemas/message.py | 25 ++++--------------------- letta/server/rest_api/app.py | 2 -- 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/letta/schemas/letta_request.py b/letta/schemas/letta_request.py index f02d1635..f8d07772 100644 --- a/letta/schemas/letta_request.py +++ b/letta/schemas/letta_request.py @@ -39,11 +39,18 @@ class LettaRequest(BaseModel): @field_validator("messages", mode="before") @classmethod def add_default_type_to_messages(cls, v): - """Add default 'message' type for backwards compatibility with older versions of SDK clients that don't send it""" + """Handle union without discriminator - default to 'message' type if not specified""" if isinstance(v, list): for item in v: - if isinstance(item, dict) and "type" not in item: - item["type"] = "message" + if isinstance(item, dict): + # If type is not present, determine based on fields + if "type" not in item: + # If it has approval-specific fields, it's an approval + if "approval_request_id" in item or "approve" in item: + item["type"] = "approval" + else: + # Default to message + item["type"] = "message" return v diff --git a/letta/schemas/message.py b/letta/schemas/message.py index badd095a..41c692c5 100644 --- a/letta/schemas/message.py +++ b/letta/schemas/message.py @@ -78,7 +78,9 @@ class MessageCreateBase(BaseModel): class MessageCreate(MessageCreateBase): """Request to create a message""" - type: Literal[MessageCreateType.message] = Field(default=MessageCreateType.message, description="The message type to be created.") + type: Optional[Literal[MessageCreateType.message]] = Field( + default=MessageCreateType.message, description="The message type to be created." + ) # In the simplified format, only allow simple roles role: Literal[ MessageRole.user, @@ -113,26 +115,7 @@ class ApprovalCreate(MessageCreateBase): reason: Optional[str] = Field(None, description="An optional explanation for the provided approval status") -MessageCreateUnion = Annotated[ - Union[MessageCreate, ApprovalCreate], - Field(discriminator="type"), -] - - -def create_message_create_union_schema(): - return { - "oneOf": [ - {"$ref": "#/components/schemas/MessageCreate"}, - {"$ref": "#/components/schemas/ApprovalCreate"}, - ], - "discriminator": { - "propertyName": "type", - "mapping": { - "message": "#/components/schemas/MessageCreate", - "approval": "#/components/schemas/ApprovalCreate", - }, - }, - } +MessageCreateUnion = Union[MessageCreate, ApprovalCreate] class MessageUpdate(BaseModel): diff --git a/letta/server/rest_api/app.py b/letta/server/rest_api/app.py index dcdddd20..853b8096 100644 --- a/letta/server/rest_api/app.py +++ b/letta/server/rest_api/app.py @@ -29,7 +29,6 @@ from letta.schemas.letta_message_content import ( create_letta_user_message_content_union_schema, ) from letta.schemas.letta_ping import create_letta_ping_schema -from letta.schemas.message import create_message_create_union_schema from letta.server.constants import REST_DEFAULT_PORT from letta.server.db import db_registry @@ -66,7 +65,6 @@ def generate_openapi_schema(app: FastAPI): letta_docs["paths"] = {k: v for k, v in letta_docs["paths"].items() if not k.startswith("/openai")} letta_docs["info"]["title"] = "Letta API" letta_docs["components"]["schemas"]["LettaMessageUnion"] = create_letta_message_union_schema() - letta_docs["components"]["schemas"]["MessageCreateUnion"] = create_message_create_union_schema() letta_docs["components"]["schemas"]["LettaMessageContentUnion"] = create_letta_message_content_union_schema() letta_docs["components"]["schemas"]["LettaAssistantMessageContentUnion"] = create_letta_assistant_message_content_union_schema() letta_docs["components"]["schemas"]["LettaUserMessageContentUnion"] = create_letta_user_message_content_union_schema()