feat: new agent id query param for default convo (#9756)
* feat: new agent id query param for default convo * update stainless
This commit is contained in:
@@ -8629,7 +8629,7 @@
|
|||||||
"schema": {
|
"schema": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/CompactionRequest"
|
"$ref": "#/components/schemas/letta__server__rest_api__routers__v1__agents__CompactionRequest"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "null"
|
"type": "null"
|
||||||
@@ -8855,18 +8855,14 @@
|
|||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 1,
|
"minLength": 41,
|
||||||
"maxLength": 42,
|
"maxLength": 41,
|
||||||
"pattern": "^(default|conv-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|agent-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$",
|
"pattern": "^conv-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
|
||||||
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), an agent ID ('agent-<uuid4>') for agent-direct messaging, or 'default'.",
|
"description": "The ID of the conv in the format 'conv-<uuid4>'",
|
||||||
"examples": [
|
"examples": ["conv-123e4567-e89b-42d3-8456-426614174000"],
|
||||||
"default",
|
|
||||||
"conv-123e4567-e89b-42d3-8456-426614174000",
|
|
||||||
"agent-123e4567-e89b-42d3-8456-426614174000"
|
|
||||||
],
|
|
||||||
"title": "Conversation Id"
|
"title": "Conversation Id"
|
||||||
},
|
},
|
||||||
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), an agent ID ('agent-<uuid4>') for agent-direct messaging, or 'default'."
|
"description": "The ID of the conv in the format 'conv-<uuid4>'"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -8904,18 +8900,14 @@
|
|||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 1,
|
"minLength": 41,
|
||||||
"maxLength": 42,
|
"maxLength": 41,
|
||||||
"pattern": "^(default|conv-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|agent-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$",
|
"pattern": "^conv-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
|
||||||
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), an agent ID ('agent-<uuid4>') for agent-direct messaging, or 'default'.",
|
"description": "The ID of the conv in the format 'conv-<uuid4>'",
|
||||||
"examples": [
|
"examples": ["conv-123e4567-e89b-42d3-8456-426614174000"],
|
||||||
"default",
|
|
||||||
"conv-123e4567-e89b-42d3-8456-426614174000",
|
|
||||||
"agent-123e4567-e89b-42d3-8456-426614174000"
|
|
||||||
],
|
|
||||||
"title": "Conversation Id"
|
"title": "Conversation Id"
|
||||||
},
|
},
|
||||||
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), an agent ID ('agent-<uuid4>') for agent-direct messaging, or 'default'."
|
"description": "The ID of the conv in the format 'conv-<uuid4>'"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
@@ -8963,18 +8955,14 @@
|
|||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 1,
|
"minLength": 41,
|
||||||
"maxLength": 42,
|
"maxLength": 41,
|
||||||
"pattern": "^(default|conv-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|agent-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$",
|
"pattern": "^conv-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
|
||||||
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), an agent ID ('agent-<uuid4>') for agent-direct messaging, or 'default'.",
|
"description": "The ID of the conv in the format 'conv-<uuid4>'",
|
||||||
"examples": [
|
"examples": ["conv-123e4567-e89b-42d3-8456-426614174000"],
|
||||||
"default",
|
|
||||||
"conv-123e4567-e89b-42d3-8456-426614174000",
|
|
||||||
"agent-123e4567-e89b-42d3-8456-426614174000"
|
|
||||||
],
|
|
||||||
"title": "Conversation Id"
|
"title": "Conversation Id"
|
||||||
},
|
},
|
||||||
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), an agent ID ('agent-<uuid4>') for agent-direct messaging, or 'default'."
|
"description": "The ID of the conv in the format 'conv-<uuid4>'"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -9003,7 +8991,7 @@
|
|||||||
"get": {
|
"get": {
|
||||||
"tags": ["conversations"],
|
"tags": ["conversations"],
|
||||||
"summary": "List Conversation Messages",
|
"summary": "List Conversation Messages",
|
||||||
"description": "List all messages in a conversation.\n\nReturns LettaMessage objects (UserMessage, AssistantMessage, etc.) for all\nmessages in the conversation, with support for cursor-based pagination.\n\nIf conversation_id is an agent ID (starts with \"agent-\"), returns messages\nfrom the agent's default conversation (no conversation isolation).",
|
"description": "List all messages in a conversation.\n\nReturns LettaMessage objects (UserMessage, AssistantMessage, etc.) for all\nmessages in the conversation, with support for cursor-based pagination.\n\n**Agent-direct mode**: Pass conversation_id=\"default\" with agent_id parameter\nto list messages from the agent's default conversation.\n\n**Deprecated**: Passing an agent ID as conversation_id still works but will be removed.",
|
||||||
"operationId": "list_conversation_messages",
|
"operationId": "list_conversation_messages",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
@@ -9015,7 +9003,7 @@
|
|||||||
"minLength": 1,
|
"minLength": 1,
|
||||||
"maxLength": 42,
|
"maxLength": 42,
|
||||||
"pattern": "^(default|conv-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|agent-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$",
|
"pattern": "^(default|conv-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|agent-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$",
|
||||||
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), an agent ID ('agent-<uuid4>') for agent-direct messaging, or 'default'.",
|
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), 'default' for agent-direct mode (with agent_id parameter), or an agent ID ('agent-<uuid4>') for backwards compatibility (deprecated).",
|
||||||
"examples": [
|
"examples": [
|
||||||
"default",
|
"default",
|
||||||
"conv-123e4567-e89b-42d3-8456-426614174000",
|
"conv-123e4567-e89b-42d3-8456-426614174000",
|
||||||
@@ -9023,7 +9011,25 @@
|
|||||||
],
|
],
|
||||||
"title": "Conversation Id"
|
"title": "Conversation Id"
|
||||||
},
|
},
|
||||||
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), an agent ID ('agent-<uuid4>') for agent-direct messaging, or 'default'."
|
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), 'default' for agent-direct mode (with agent_id parameter), or an agent ID ('agent-<uuid4>') for backwards compatibility (deprecated)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "agent_id",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Agent ID for agent-direct mode with 'default' conversation",
|
||||||
|
"title": "Agent Id"
|
||||||
|
},
|
||||||
|
"description": "Agent ID for agent-direct mode with 'default' conversation"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "before",
|
"name": "before",
|
||||||
@@ -9173,7 +9179,7 @@
|
|||||||
"post": {
|
"post": {
|
||||||
"tags": ["conversations"],
|
"tags": ["conversations"],
|
||||||
"summary": "Send Conversation Message",
|
"summary": "Send Conversation Message",
|
||||||
"description": "Send a message to a conversation and get a response.\n\nThis endpoint sends a message to an existing conversation.\nBy default (streaming=true), returns a streaming response (Server-Sent Events).\nSet streaming=false to get a complete JSON response.\n\nIf conversation_id is an agent ID (starts with \"agent-\"), routes to agent-direct\nmode with locking but without conversation-specific features.",
|
"description": "Send a message to a conversation and get a response.\n\nThis endpoint sends a message to an existing conversation.\nBy default (streaming=true), returns a streaming response (Server-Sent Events).\nSet streaming=false to get a complete JSON response.\n\n**Agent-direct mode**: Pass conversation_id=\"default\" with agent_id in request body\nto send messages to the agent's default conversation with locking.\n\n**Deprecated**: Passing an agent ID as conversation_id still works but will be removed.",
|
||||||
"operationId": "send_conversation_message",
|
"operationId": "send_conversation_message",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
@@ -9185,7 +9191,7 @@
|
|||||||
"minLength": 1,
|
"minLength": 1,
|
||||||
"maxLength": 42,
|
"maxLength": 42,
|
||||||
"pattern": "^(default|conv-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|agent-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$",
|
"pattern": "^(default|conv-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|agent-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$",
|
||||||
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), an agent ID ('agent-<uuid4>') for agent-direct messaging, or 'default'.",
|
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), 'default' for agent-direct mode (with agent_id parameter), or an agent ID ('agent-<uuid4>') for backwards compatibility (deprecated).",
|
||||||
"examples": [
|
"examples": [
|
||||||
"default",
|
"default",
|
||||||
"conv-123e4567-e89b-42d3-8456-426614174000",
|
"conv-123e4567-e89b-42d3-8456-426614174000",
|
||||||
@@ -9193,7 +9199,7 @@
|
|||||||
],
|
],
|
||||||
"title": "Conversation Id"
|
"title": "Conversation Id"
|
||||||
},
|
},
|
||||||
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), an agent ID ('agent-<uuid4>') for agent-direct messaging, or 'default'."
|
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), 'default' for agent-direct mode (with agent_id parameter), or an agent ID ('agent-<uuid4>') for backwards compatibility (deprecated)."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
@@ -9238,7 +9244,7 @@
|
|||||||
"post": {
|
"post": {
|
||||||
"tags": ["conversations"],
|
"tags": ["conversations"],
|
||||||
"summary": "Retrieve Conversation Stream",
|
"summary": "Retrieve Conversation Stream",
|
||||||
"description": "Resume the stream for the most recent active run in a conversation.\n\nThis endpoint allows you to reconnect to an active background stream\nfor a conversation, enabling recovery from network interruptions.\n\nIf conversation_id is an agent ID (starts with \"agent-\"), retrieves the\nstream for the agent's most recent active run.",
|
"description": "Resume the stream for the most recent active run in a conversation.\n\nThis endpoint allows you to reconnect to an active background stream\nfor a conversation, enabling recovery from network interruptions.\n\n**Agent-direct mode**: Pass conversation_id=\"default\" with agent_id in request body\nto retrieve the stream for the agent's most recent active run.\n\n**Deprecated**: Passing an agent ID as conversation_id still works but will be removed.",
|
||||||
"operationId": "retrieve_conversation_stream",
|
"operationId": "retrieve_conversation_stream",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
@@ -9250,7 +9256,7 @@
|
|||||||
"minLength": 1,
|
"minLength": 1,
|
||||||
"maxLength": 42,
|
"maxLength": 42,
|
||||||
"pattern": "^(default|conv-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|agent-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$",
|
"pattern": "^(default|conv-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|agent-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$",
|
||||||
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), an agent ID ('agent-<uuid4>') for agent-direct messaging, or 'default'.",
|
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), 'default' for agent-direct mode (with agent_id parameter), or an agent ID ('agent-<uuid4>') for backwards compatibility (deprecated).",
|
||||||
"examples": [
|
"examples": [
|
||||||
"default",
|
"default",
|
||||||
"conv-123e4567-e89b-42d3-8456-426614174000",
|
"conv-123e4567-e89b-42d3-8456-426614174000",
|
||||||
@@ -9258,7 +9264,7 @@
|
|||||||
],
|
],
|
||||||
"title": "Conversation Id"
|
"title": "Conversation Id"
|
||||||
},
|
},
|
||||||
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), an agent ID ('agent-<uuid4>') for agent-direct messaging, or 'default'."
|
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), 'default' for agent-direct mode (with agent_id parameter), or an agent ID ('agent-<uuid4>') for backwards compatibility (deprecated)."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
@@ -9342,7 +9348,7 @@
|
|||||||
"post": {
|
"post": {
|
||||||
"tags": ["conversations"],
|
"tags": ["conversations"],
|
||||||
"summary": "Cancel Conversation",
|
"summary": "Cancel Conversation",
|
||||||
"description": "Cancel runs associated with a conversation.\n\nNote: To cancel active runs, Redis is required.\n\nIf conversation_id is an agent ID (starts with \"agent-\"), cancels runs\nfor the agent's default conversation.",
|
"description": "Cancel runs associated with a conversation.\n\nNote: To cancel active runs, Redis is required.\n\n**Agent-direct mode**: Pass conversation_id=\"default\" with agent_id query parameter\nto cancel runs for the agent's default conversation.\n\n**Deprecated**: Passing an agent ID as conversation_id still works but will be removed.",
|
||||||
"operationId": "cancel_conversation",
|
"operationId": "cancel_conversation",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
@@ -9354,7 +9360,7 @@
|
|||||||
"minLength": 1,
|
"minLength": 1,
|
||||||
"maxLength": 42,
|
"maxLength": 42,
|
||||||
"pattern": "^(default|conv-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|agent-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$",
|
"pattern": "^(default|conv-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|agent-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$",
|
||||||
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), an agent ID ('agent-<uuid4>') for agent-direct messaging, or 'default'.",
|
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), 'default' for agent-direct mode (with agent_id parameter), or an agent ID ('agent-<uuid4>') for backwards compatibility (deprecated).",
|
||||||
"examples": [
|
"examples": [
|
||||||
"default",
|
"default",
|
||||||
"conv-123e4567-e89b-42d3-8456-426614174000",
|
"conv-123e4567-e89b-42d3-8456-426614174000",
|
||||||
@@ -9362,7 +9368,25 @@
|
|||||||
],
|
],
|
||||||
"title": "Conversation Id"
|
"title": "Conversation Id"
|
||||||
},
|
},
|
||||||
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), an agent ID ('agent-<uuid4>') for agent-direct messaging, or 'default'."
|
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), 'default' for agent-direct mode (with agent_id parameter), or an agent ID ('agent-<uuid4>') for backwards compatibility (deprecated)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "agent_id",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Agent ID for agent-direct mode with 'default' conversation",
|
||||||
|
"title": "Agent Id"
|
||||||
|
},
|
||||||
|
"description": "Agent ID for agent-direct mode with 'default' conversation"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -9395,7 +9419,7 @@
|
|||||||
"post": {
|
"post": {
|
||||||
"tags": ["conversations"],
|
"tags": ["conversations"],
|
||||||
"summary": "Compact Conversation",
|
"summary": "Compact Conversation",
|
||||||
"description": "Compact (summarize) a conversation's message history.\n\nThis endpoint summarizes the in-context messages for a specific conversation,\nreducing the message count while preserving important context.\n\nIf conversation_id is an agent ID (starts with \"agent-\"), compacts the\nagent's default conversation messages.",
|
"description": "Compact (summarize) a conversation's message history.\n\nThis endpoint summarizes the in-context messages for a specific conversation,\nreducing the message count while preserving important context.\n\n**Agent-direct mode**: Pass conversation_id=\"default\" with agent_id in request body\nto compact the agent's default conversation messages.\n\n**Deprecated**: Passing an agent ID as conversation_id still works but will be removed.",
|
||||||
"operationId": "compact_conversation",
|
"operationId": "compact_conversation",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
@@ -9407,7 +9431,7 @@
|
|||||||
"minLength": 1,
|
"minLength": 1,
|
||||||
"maxLength": 42,
|
"maxLength": 42,
|
||||||
"pattern": "^(default|conv-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|agent-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$",
|
"pattern": "^(default|conv-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|agent-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$",
|
||||||
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), an agent ID ('agent-<uuid4>') for agent-direct messaging, or 'default'.",
|
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), 'default' for agent-direct mode (with agent_id parameter), or an agent ID ('agent-<uuid4>') for backwards compatibility (deprecated).",
|
||||||
"examples": [
|
"examples": [
|
||||||
"default",
|
"default",
|
||||||
"conv-123e4567-e89b-42d3-8456-426614174000",
|
"conv-123e4567-e89b-42d3-8456-426614174000",
|
||||||
@@ -9415,7 +9439,7 @@
|
|||||||
],
|
],
|
||||||
"title": "Conversation Id"
|
"title": "Conversation Id"
|
||||||
},
|
},
|
||||||
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), an agent ID ('agent-<uuid4>') for agent-direct messaging, or 'default'."
|
"description": "The conversation identifier. Can be a conversation ID ('conv-<uuid4>'), 'default' for agent-direct mode (with agent_id parameter), or an agent ID ('agent-<uuid4>') for backwards compatibility (deprecated)."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
@@ -9424,7 +9448,7 @@
|
|||||||
"schema": {
|
"schema": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/CompactionRequest"
|
"$ref": "#/components/schemas/letta__server__rest_api__routers__v1__conversations__CompactionRequest"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "null"
|
"type": "null"
|
||||||
@@ -31460,23 +31484,6 @@
|
|||||||
"required": ["code"],
|
"required": ["code"],
|
||||||
"title": "CodeInput"
|
"title": "CodeInput"
|
||||||
},
|
},
|
||||||
"CompactionRequest": {
|
|
||||||
"properties": {
|
|
||||||
"compaction_settings": {
|
|
||||||
"anyOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/CompactionSettings-Input"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "null"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Optional compaction settings to use for this summarization request. If not provided, the agent's default settings will be used."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "object",
|
|
||||||
"title": "CompactionRequest"
|
|
||||||
},
|
|
||||||
"CompactionResponse": {
|
"CompactionResponse": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"summary": {
|
"summary": {
|
||||||
@@ -32611,6 +32618,18 @@
|
|||||||
"description": "If True, returns token IDs and logprobs for ALL LLM generations in the agent step, not just the last one. Uses SGLang native /generate endpoint. Returns 'turns' field with TurnTokenData for each assistant/tool turn. Required for proper multi-turn RL training with loss masking.",
|
"description": "If True, returns token IDs and logprobs for ALL LLM generations in the agent step, not just the last one. Uses SGLang native /generate endpoint. Returns 'turns' field with TurnTokenData for each assistant/tool turn. Required for proper multi-turn RL training with loss masking.",
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
|
"agent_id": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Agent Id",
|
||||||
|
"description": "Agent ID for agent-direct mode with 'default' conversation. Use with conversation_id='default' in the URL path."
|
||||||
|
},
|
||||||
"streaming": {
|
"streaming": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"title": "Streaming",
|
"title": "Streaming",
|
||||||
@@ -43448,6 +43467,18 @@
|
|||||||
},
|
},
|
||||||
"RetrieveStreamRequest": {
|
"RetrieveStreamRequest": {
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"agent_id": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Agent Id",
|
||||||
|
"description": "Agent ID for agent-direct mode with 'default' conversation. Use with conversation_id='default' in the URL path."
|
||||||
|
},
|
||||||
"starting_after": {
|
"starting_after": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"title": "Starting After",
|
"title": "Starting After",
|
||||||
@@ -51563,6 +51594,52 @@
|
|||||||
],
|
],
|
||||||
"title": "ToolSchema"
|
"title": "ToolSchema"
|
||||||
},
|
},
|
||||||
|
"letta__server__rest_api__routers__v1__agents__CompactionRequest": {
|
||||||
|
"properties": {
|
||||||
|
"compaction_settings": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/CompactionSettings-Input"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Optional compaction settings to use for this summarization request. If not provided, the agent's default settings will be used."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
"title": "CompactionRequest"
|
||||||
|
},
|
||||||
|
"letta__server__rest_api__routers__v1__conversations__CompactionRequest": {
|
||||||
|
"properties": {
|
||||||
|
"agent_id": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Agent Id",
|
||||||
|
"description": "Agent ID for agent-direct mode with 'default' conversation. Use with conversation_id='default' in the URL path."
|
||||||
|
},
|
||||||
|
"compaction_settings": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/CompactionSettings-Input"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Optional compaction settings to use for this summarization request. If not provided, the agent's default settings will be used."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
"title": "CompactionRequest"
|
||||||
|
},
|
||||||
"letta__server__rest_api__routers__v1__tools__ToolExecuteRequest": {
|
"letta__server__rest_api__routers__v1__tools__ToolExecuteRequest": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"args": {
|
"args": {
|
||||||
|
|||||||
@@ -88,8 +88,7 @@ class LettaRequest(BaseModel):
|
|||||||
)
|
)
|
||||||
top_logprobs: Optional[int] = Field(
|
top_logprobs: Optional[int] = Field(
|
||||||
default=None,
|
default=None,
|
||||||
description="Number of most likely tokens to return at each position (0-20). "
|
description="Number of most likely tokens to return at each position (0-20). Requires return_logprobs=True.",
|
||||||
"Requires return_logprobs=True.",
|
|
||||||
)
|
)
|
||||||
return_token_ids: bool = Field(
|
return_token_ids: bool = Field(
|
||||||
default=False,
|
default=False,
|
||||||
@@ -155,6 +154,10 @@ class LettaStreamingRequest(LettaRequest):
|
|||||||
class ConversationMessageRequest(LettaRequest):
|
class ConversationMessageRequest(LettaRequest):
|
||||||
"""Request for sending messages to a conversation. Streams by default."""
|
"""Request for sending messages to a conversation. Streams by default."""
|
||||||
|
|
||||||
|
agent_id: Optional[str] = Field(
|
||||||
|
default=None,
|
||||||
|
description="Agent ID for agent-direct mode with 'default' conversation. Use with conversation_id='default' in the URL path.",
|
||||||
|
)
|
||||||
streaming: bool = Field(
|
streaming: bool = Field(
|
||||||
default=True,
|
default=True,
|
||||||
description="If True (default), returns a streaming response (Server-Sent Events). If False, returns a complete JSON response.",
|
description="If True (default), returns a streaming response (Server-Sent Events). If False, returns a complete JSON response.",
|
||||||
@@ -194,6 +197,10 @@ class CreateBatch(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class RetrieveStreamRequest(BaseModel):
|
class RetrieveStreamRequest(BaseModel):
|
||||||
|
agent_id: Optional[str] = Field(
|
||||||
|
default=None,
|
||||||
|
description="Agent ID for agent-direct mode with 'default' conversation. Use with conversation_id='default' in the URL path.",
|
||||||
|
)
|
||||||
starting_after: int = Field(
|
starting_after: int = Field(
|
||||||
0, description="Sequence id to use as a cursor for pagination. Response will start streaming after this chunk sequence id"
|
0, description="Sequence id to use as a cursor for pagination. Response will start streaming after this chunk sequence id"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ from letta.services.run_manager import RunManager
|
|||||||
from letta.services.streaming_service import StreamingService
|
from letta.services.streaming_service import StreamingService
|
||||||
from letta.services.summarizer.summarizer_config import CompactionSettings
|
from letta.services.summarizer.summarizer_config import CompactionSettings
|
||||||
from letta.settings import settings
|
from letta.settings import settings
|
||||||
from letta.validators import ConversationId
|
from letta.validators import ConversationId, ConversationIdOrDefault
|
||||||
|
|
||||||
router = APIRouter(prefix="/conversations", tags=["conversations"])
|
router = APIRouter(prefix="/conversations", tags=["conversations"])
|
||||||
|
|
||||||
@@ -150,7 +150,8 @@ ConversationMessagesResponse = Annotated[
|
|||||||
operation_id="list_conversation_messages",
|
operation_id="list_conversation_messages",
|
||||||
)
|
)
|
||||||
async def list_conversation_messages(
|
async def list_conversation_messages(
|
||||||
conversation_id: ConversationId,
|
conversation_id: ConversationIdOrDefault,
|
||||||
|
agent_id: Optional[str] = Query(None, description="Agent ID for agent-direct mode with 'default' conversation"),
|
||||||
server: SyncServer = Depends(get_letta_server),
|
server: SyncServer = Depends(get_letta_server),
|
||||||
headers: HeaderParams = Depends(get_headers),
|
headers: HeaderParams = Depends(get_headers),
|
||||||
before: Optional[str] = Query(
|
before: Optional[str] = Query(
|
||||||
@@ -175,15 +176,24 @@ async def list_conversation_messages(
|
|||||||
Returns LettaMessage objects (UserMessage, AssistantMessage, etc.) for all
|
Returns LettaMessage objects (UserMessage, AssistantMessage, etc.) for all
|
||||||
messages in the conversation, with support for cursor-based pagination.
|
messages in the conversation, with support for cursor-based pagination.
|
||||||
|
|
||||||
If conversation_id is an agent ID (starts with "agent-"), returns messages
|
**Agent-direct mode**: Pass conversation_id="default" with agent_id parameter
|
||||||
from the agent's default conversation (no conversation isolation).
|
to list messages from the agent's default conversation.
|
||||||
|
|
||||||
|
**Deprecated**: Passing an agent ID as conversation_id still works but will be removed.
|
||||||
"""
|
"""
|
||||||
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
||||||
|
|
||||||
# Agent-direct mode: list agent's default conversation messages
|
# Agent-direct mode: conversation_id="default" + agent_id param (preferred)
|
||||||
if conversation_id.startswith("agent-"):
|
# OR conversation_id="agent-*" (backwards compat, deprecated)
|
||||||
|
resolved_agent_id = None
|
||||||
|
if conversation_id == "default" and agent_id:
|
||||||
|
resolved_agent_id = agent_id
|
||||||
|
elif conversation_id.startswith("agent-"):
|
||||||
|
resolved_agent_id = conversation_id
|
||||||
|
|
||||||
|
if resolved_agent_id:
|
||||||
return await server.get_agent_recall_async(
|
return await server.get_agent_recall_async(
|
||||||
agent_id=conversation_id,
|
agent_id=resolved_agent_id,
|
||||||
after=after,
|
after=after,
|
||||||
before=before,
|
before=before,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
@@ -324,7 +334,7 @@ async def _send_agent_direct_message(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
async def send_conversation_message(
|
async def send_conversation_message(
|
||||||
conversation_id: ConversationId,
|
conversation_id: ConversationIdOrDefault,
|
||||||
request: ConversationMessageRequest = Body(...),
|
request: ConversationMessageRequest = Body(...),
|
||||||
server: SyncServer = Depends(get_letta_server),
|
server: SyncServer = Depends(get_letta_server),
|
||||||
headers: HeaderParams = Depends(get_headers),
|
headers: HeaderParams = Depends(get_headers),
|
||||||
@@ -336,22 +346,28 @@ async def send_conversation_message(
|
|||||||
By default (streaming=true), returns a streaming response (Server-Sent Events).
|
By default (streaming=true), returns a streaming response (Server-Sent Events).
|
||||||
Set streaming=false to get a complete JSON response.
|
Set streaming=false to get a complete JSON response.
|
||||||
|
|
||||||
If conversation_id is an agent ID (starts with "agent-"), routes to agent-direct
|
**Agent-direct mode**: Pass conversation_id="default" with agent_id in request body
|
||||||
mode with locking but without conversation-specific features.
|
to send messages to the agent's default conversation with locking.
|
||||||
|
|
||||||
|
**Deprecated**: Passing an agent ID as conversation_id still works but will be removed.
|
||||||
"""
|
"""
|
||||||
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
||||||
|
|
||||||
if not request.messages or len(request.messages) == 0:
|
if not request.messages or len(request.messages) == 0:
|
||||||
raise HTTPException(status_code=422, detail="Messages must not be empty")
|
raise HTTPException(status_code=422, detail="Messages must not be empty")
|
||||||
|
|
||||||
# Detect agent-direct mode: conversation_id is actually an agent ID
|
# Agent-direct mode: conversation_id="default" + agent_id in body (preferred)
|
||||||
is_agent_direct = conversation_id.startswith("agent-")
|
# OR conversation_id="agent-*" (backwards compat, deprecated)
|
||||||
|
resolved_agent_id = None
|
||||||
|
if conversation_id == "default" and request.agent_id:
|
||||||
|
resolved_agent_id = request.agent_id
|
||||||
|
elif conversation_id.startswith("agent-"):
|
||||||
|
resolved_agent_id = conversation_id
|
||||||
|
|
||||||
if is_agent_direct:
|
if resolved_agent_id:
|
||||||
# Agent-direct mode: use agent ID, enable locking, skip conversation features
|
# Agent-direct mode: use agent ID, enable locking, skip conversation features
|
||||||
agent_id = conversation_id
|
|
||||||
return await _send_agent_direct_message(
|
return await _send_agent_direct_message(
|
||||||
agent_id=agent_id,
|
agent_id=resolved_agent_id,
|
||||||
request=request,
|
request=request,
|
||||||
server=server,
|
server=server,
|
||||||
actor=actor,
|
actor=actor,
|
||||||
@@ -488,7 +504,7 @@ async def send_conversation_message(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
async def retrieve_conversation_stream(
|
async def retrieve_conversation_stream(
|
||||||
conversation_id: ConversationId,
|
conversation_id: ConversationIdOrDefault,
|
||||||
request: RetrieveStreamRequest = Body(None),
|
request: RetrieveStreamRequest = Body(None),
|
||||||
headers: HeaderParams = Depends(get_headers),
|
headers: HeaderParams = Depends(get_headers),
|
||||||
server: SyncServer = Depends(get_letta_server),
|
server: SyncServer = Depends(get_letta_server),
|
||||||
@@ -499,18 +515,28 @@ async def retrieve_conversation_stream(
|
|||||||
This endpoint allows you to reconnect to an active background stream
|
This endpoint allows you to reconnect to an active background stream
|
||||||
for a conversation, enabling recovery from network interruptions.
|
for a conversation, enabling recovery from network interruptions.
|
||||||
|
|
||||||
If conversation_id is an agent ID (starts with "agent-"), retrieves the
|
**Agent-direct mode**: Pass conversation_id="default" with agent_id in request body
|
||||||
stream for the agent's most recent active run.
|
to retrieve the stream for the agent's most recent active run.
|
||||||
|
|
||||||
|
**Deprecated**: Passing an agent ID as conversation_id still works but will be removed.
|
||||||
"""
|
"""
|
||||||
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
||||||
runs_manager = RunManager()
|
runs_manager = RunManager()
|
||||||
|
|
||||||
|
# Agent-direct mode: conversation_id="default" + agent_id in body (preferred)
|
||||||
|
# OR conversation_id="agent-*" (backwards compat, deprecated)
|
||||||
|
resolved_agent_id = None
|
||||||
|
if conversation_id == "default" and request and request.agent_id:
|
||||||
|
resolved_agent_id = request.agent_id
|
||||||
|
elif conversation_id.startswith("agent-"):
|
||||||
|
resolved_agent_id = conversation_id
|
||||||
|
|
||||||
# Find the most recent active run
|
# Find the most recent active run
|
||||||
if conversation_id.startswith("agent-"):
|
if resolved_agent_id:
|
||||||
# Agent-direct mode: find runs by agent_id
|
# Agent-direct mode: find runs by agent_id
|
||||||
active_runs = await runs_manager.list_runs(
|
active_runs = await runs_manager.list_runs(
|
||||||
actor=actor,
|
actor=actor,
|
||||||
agent_id=conversation_id,
|
agent_id=resolved_agent_id,
|
||||||
statuses=[RunStatus.created, RunStatus.running],
|
statuses=[RunStatus.created, RunStatus.running],
|
||||||
limit=1,
|
limit=1,
|
||||||
ascending=False,
|
ascending=False,
|
||||||
@@ -578,7 +604,8 @@ async def retrieve_conversation_stream(
|
|||||||
|
|
||||||
@router.post("/{conversation_id}/cancel", operation_id="cancel_conversation")
|
@router.post("/{conversation_id}/cancel", operation_id="cancel_conversation")
|
||||||
async def cancel_conversation(
|
async def cancel_conversation(
|
||||||
conversation_id: ConversationId,
|
conversation_id: ConversationIdOrDefault,
|
||||||
|
agent_id: Optional[str] = Query(None, description="Agent ID for agent-direct mode with 'default' conversation"),
|
||||||
server: SyncServer = Depends(get_letta_server),
|
server: SyncServer = Depends(get_letta_server),
|
||||||
headers: HeaderParams = Depends(get_headers),
|
headers: HeaderParams = Depends(get_headers),
|
||||||
) -> dict:
|
) -> dict:
|
||||||
@@ -587,8 +614,10 @@ async def cancel_conversation(
|
|||||||
|
|
||||||
Note: To cancel active runs, Redis is required.
|
Note: To cancel active runs, Redis is required.
|
||||||
|
|
||||||
If conversation_id is an agent ID (starts with "agent-"), cancels runs
|
**Agent-direct mode**: Pass conversation_id="default" with agent_id query parameter
|
||||||
for the agent's default conversation.
|
to cancel runs for the agent's default conversation.
|
||||||
|
|
||||||
|
**Deprecated**: Passing an agent ID as conversation_id still works but will be removed.
|
||||||
"""
|
"""
|
||||||
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
||||||
logger.info(
|
logger.info(
|
||||||
@@ -601,13 +630,20 @@ async def cancel_conversation(
|
|||||||
if not settings.track_agent_run:
|
if not settings.track_agent_run:
|
||||||
raise HTTPException(status_code=400, detail="Agent run tracking is disabled")
|
raise HTTPException(status_code=400, detail="Agent run tracking is disabled")
|
||||||
|
|
||||||
# Agent-direct mode: use agent_id directly, skip conversation lookup
|
# Agent-direct mode: conversation_id="default" + agent_id param (preferred)
|
||||||
if conversation_id.startswith("agent-"):
|
# OR conversation_id="agent-*" (backwards compat, deprecated)
|
||||||
agent_id = conversation_id
|
resolved_agent_id = None
|
||||||
|
if conversation_id == "default" and agent_id:
|
||||||
|
resolved_agent_id = agent_id
|
||||||
|
elif conversation_id.startswith("agent-"):
|
||||||
|
resolved_agent_id = conversation_id
|
||||||
|
|
||||||
|
if resolved_agent_id:
|
||||||
|
# Agent-direct mode: use agent_id directly, skip conversation lookup
|
||||||
# Find active runs for this agent (default conversation has conversation_id=None)
|
# Find active runs for this agent (default conversation has conversation_id=None)
|
||||||
runs = await server.run_manager.list_runs(
|
runs = await server.run_manager.list_runs(
|
||||||
actor=actor,
|
actor=actor,
|
||||||
agent_id=agent_id,
|
agent_id=resolved_agent_id,
|
||||||
statuses=[RunStatus.created, RunStatus.running],
|
statuses=[RunStatus.created, RunStatus.running],
|
||||||
ascending=False,
|
ascending=False,
|
||||||
limit=100,
|
limit=100,
|
||||||
@@ -657,6 +693,10 @@ async def cancel_conversation(
|
|||||||
|
|
||||||
|
|
||||||
class CompactionRequest(BaseModel):
|
class CompactionRequest(BaseModel):
|
||||||
|
agent_id: Optional[str] = Field(
|
||||||
|
default=None,
|
||||||
|
description="Agent ID for agent-direct mode with 'default' conversation. Use with conversation_id='default' in the URL path.",
|
||||||
|
)
|
||||||
compaction_settings: Optional[CompactionSettings] = Field(
|
compaction_settings: Optional[CompactionSettings] = Field(
|
||||||
default=None,
|
default=None,
|
||||||
description="Optional compaction settings to use for this summarization request. If not provided, the agent's default settings will be used.",
|
description="Optional compaction settings to use for this summarization request. If not provided, the agent's default settings will be used.",
|
||||||
@@ -671,7 +711,7 @@ class CompactionResponse(BaseModel):
|
|||||||
|
|
||||||
@router.post("/{conversation_id}/compact", response_model=CompactionResponse, operation_id="compact_conversation")
|
@router.post("/{conversation_id}/compact", response_model=CompactionResponse, operation_id="compact_conversation")
|
||||||
async def compact_conversation(
|
async def compact_conversation(
|
||||||
conversation_id: ConversationId,
|
conversation_id: ConversationIdOrDefault,
|
||||||
request: Optional[CompactionRequest] = Body(default=None),
|
request: Optional[CompactionRequest] = Body(default=None),
|
||||||
server: SyncServer = Depends(get_letta_server),
|
server: SyncServer = Depends(get_letta_server),
|
||||||
headers: HeaderParams = Depends(get_headers),
|
headers: HeaderParams = Depends(get_headers),
|
||||||
@@ -682,15 +722,24 @@ async def compact_conversation(
|
|||||||
This endpoint summarizes the in-context messages for a specific conversation,
|
This endpoint summarizes the in-context messages for a specific conversation,
|
||||||
reducing the message count while preserving important context.
|
reducing the message count while preserving important context.
|
||||||
|
|
||||||
If conversation_id is an agent ID (starts with "agent-"), compacts the
|
**Agent-direct mode**: Pass conversation_id="default" with agent_id in request body
|
||||||
agent's default conversation messages.
|
to compact the agent's default conversation messages.
|
||||||
|
|
||||||
|
**Deprecated**: Passing an agent ID as conversation_id still works but will be removed.
|
||||||
"""
|
"""
|
||||||
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
||||||
|
|
||||||
# Agent-direct mode: compact agent's default conversation
|
# Agent-direct mode: conversation_id="default" + agent_id in body (preferred)
|
||||||
if conversation_id.startswith("agent-"):
|
# OR conversation_id="agent-*" (backwards compat, deprecated)
|
||||||
agent_id = conversation_id
|
resolved_agent_id = None
|
||||||
agent = await server.agent_manager.get_agent_by_id_async(agent_id, actor, include_relationships=["multi_agent_group"])
|
if conversation_id == "default" and request and request.agent_id:
|
||||||
|
resolved_agent_id = request.agent_id
|
||||||
|
elif conversation_id.startswith("agent-"):
|
||||||
|
resolved_agent_id = conversation_id
|
||||||
|
|
||||||
|
if resolved_agent_id:
|
||||||
|
# Agent-direct mode: compact agent's default conversation
|
||||||
|
agent = await server.agent_manager.get_agent_by_id_async(resolved_agent_id, actor, include_relationships=["multi_agent_group"])
|
||||||
in_context_messages = await server.message_manager.get_messages_by_ids_async(message_ids=agent.message_ids, actor=actor)
|
in_context_messages = await server.message_manager.get_messages_by_ids_async(message_ids=agent.message_ids, actor=actor)
|
||||||
agent_loop = LettaAgentV3(agent_state=agent, actor=actor)
|
agent_loop = LettaAgentV3(agent_state=agent, actor=actor)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ PATH_VALIDATORS = {primitive_type.value: _create_path_validator_factory(primitiv
|
|||||||
|
|
||||||
|
|
||||||
def _create_conversation_id_or_default_path_validator_factory():
|
def _create_conversation_id_or_default_path_validator_factory():
|
||||||
"""Conversation IDs accept the usual primitive format, 'default', or an agent ID."""
|
"""Conversation IDs with support for 'default' and agent IDs (backwards compatibility)."""
|
||||||
|
|
||||||
conversation_primitive = PrimitiveType.CONVERSATION.value
|
conversation_primitive = PrimitiveType.CONVERSATION.value
|
||||||
agent_primitive = PrimitiveType.AGENT.value
|
agent_primitive = PrimitiveType.AGENT.value
|
||||||
@@ -59,7 +59,8 @@ def _create_conversation_id_or_default_path_validator_factory():
|
|||||||
return Path(
|
return Path(
|
||||||
description=(
|
description=(
|
||||||
f"The conversation identifier. Can be a conversation ID ('{conversation_primitive}-<uuid4>'), "
|
f"The conversation identifier. Can be a conversation ID ('{conversation_primitive}-<uuid4>'), "
|
||||||
f"an agent ID ('{agent_primitive}-<uuid4>') for agent-direct messaging, or 'default'."
|
f"'default' for agent-direct mode (with agent_id parameter), "
|
||||||
|
f"or an agent ID ('{agent_primitive}-<uuid4>') for backwards compatibility (deprecated)."
|
||||||
),
|
),
|
||||||
pattern=conversation_or_agent_or_default_pattern,
|
pattern=conversation_or_agent_or_default_pattern,
|
||||||
examples=[
|
examples=[
|
||||||
@@ -74,10 +75,6 @@ def _create_conversation_id_or_default_path_validator_factory():
|
|||||||
return factory
|
return factory
|
||||||
|
|
||||||
|
|
||||||
# Override conversation ID path validation to also allow 'default' and agent IDs.
|
|
||||||
PATH_VALIDATORS[PrimitiveType.CONVERSATION.value] = _create_conversation_id_or_default_path_validator_factory()
|
|
||||||
|
|
||||||
|
|
||||||
# Type aliases for common ID types
|
# Type aliases for common ID types
|
||||||
# These can be used directly in route handler signatures for cleaner code
|
# These can be used directly in route handler signatures for cleaner code
|
||||||
AgentId = Annotated[str, PATH_VALIDATORS[PrimitiveType.AGENT.value]()]
|
AgentId = Annotated[str, PATH_VALIDATORS[PrimitiveType.AGENT.value]()]
|
||||||
@@ -98,6 +95,10 @@ StepId = Annotated[str, PATH_VALIDATORS[PrimitiveType.STEP.value]()]
|
|||||||
IdentityId = Annotated[str, PATH_VALIDATORS[PrimitiveType.IDENTITY.value]()]
|
IdentityId = Annotated[str, PATH_VALIDATORS[PrimitiveType.IDENTITY.value]()]
|
||||||
ConversationId = Annotated[str, PATH_VALIDATORS[PrimitiveType.CONVERSATION.value]()]
|
ConversationId = Annotated[str, PATH_VALIDATORS[PrimitiveType.CONVERSATION.value]()]
|
||||||
|
|
||||||
|
# Conversation ID with support for 'default' and agent IDs (for agent-direct mode endpoints)
|
||||||
|
# Backwards compatible - agent-* will be deprecated in favor of conversation_id='default' + agent_id param
|
||||||
|
ConversationIdOrDefault = Annotated[str, _create_conversation_id_or_default_path_validator_factory()()]
|
||||||
|
|
||||||
# Infrastructure types
|
# Infrastructure types
|
||||||
McpServerId = Annotated[str, PATH_VALIDATORS[PrimitiveType.MCP_SERVER.value]()]
|
McpServerId = Annotated[str, PATH_VALIDATORS[PrimitiveType.MCP_SERVER.value]()]
|
||||||
McpOAuthId = Annotated[str, PATH_VALIDATORS[PrimitiveType.MCP_OAUTH.value]()]
|
McpOAuthId = Annotated[str, PATH_VALIDATORS[PrimitiveType.MCP_OAUTH.value]()]
|
||||||
|
|||||||
@@ -725,6 +725,132 @@ class TestConversationsSDK:
|
|||||||
if "No active runs" not in str(e):
|
if "No active runs" not in str(e):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
def test_backwards_compatibility_old_pattern(self, client: Letta, agent, server_url: str):
|
||||||
|
"""Test that the old pattern (agent_id as conversation_id) still works for backwards compatibility."""
|
||||||
|
# OLD PATTERN: conversation_id=agent.id (should still work)
|
||||||
|
# Use raw HTTP requests since SDK might not be up to date
|
||||||
|
|
||||||
|
# Test 1: Send message using old pattern
|
||||||
|
response = requests.post(
|
||||||
|
f"{server_url}/v1/conversations/{agent.id}/messages",
|
||||||
|
json={
|
||||||
|
"messages": [{"role": "user", "content": "Testing old pattern still works"}],
|
||||||
|
"streaming": False,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert response.status_code == 200, f"Old pattern should work for sending messages: {response.text}"
|
||||||
|
data = response.json()
|
||||||
|
assert "messages" in data, "Response should contain messages"
|
||||||
|
assert len(data["messages"]) > 0, "Should receive response messages"
|
||||||
|
|
||||||
|
# Test 2: List messages using old pattern
|
||||||
|
response = requests.get(f"{server_url}/v1/conversations/{agent.id}/messages")
|
||||||
|
assert response.status_code == 200, f"Old pattern should work for listing messages: {response.text}"
|
||||||
|
data = response.json()
|
||||||
|
# Response is a list of messages directly
|
||||||
|
assert isinstance(data, list), "Response should be a list of messages"
|
||||||
|
assert len(data) >= 3, "Should have at least system + user + assistant messages"
|
||||||
|
|
||||||
|
# Verify our message is there
|
||||||
|
user_messages = [m for m in data if m.get("message_type") == "user_message"]
|
||||||
|
assert any("Testing old pattern still works" in str(m.get("content", "")) for m in user_messages), "Should find our test message"
|
||||||
|
|
||||||
|
def test_new_pattern_send_message(self, client: Letta, agent, server_url: str):
|
||||||
|
"""Test sending messages using the new pattern: conversation_id='default' + agent_id in body."""
|
||||||
|
# NEW PATTERN: conversation_id='default' + agent_id in request body
|
||||||
|
response = requests.post(
|
||||||
|
f"{server_url}/v1/conversations/default/messages",
|
||||||
|
json={
|
||||||
|
"agent_id": agent.id,
|
||||||
|
"messages": [{"role": "user", "content": "Testing new pattern send message"}],
|
||||||
|
"streaming": False,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert response.status_code == 200, f"New pattern should work for sending messages: {response.text}"
|
||||||
|
data = response.json()
|
||||||
|
assert "messages" in data, "Response should contain messages"
|
||||||
|
assert len(data["messages"]) > 0, "Should receive response messages"
|
||||||
|
|
||||||
|
# Verify we got an assistant message
|
||||||
|
assistant_messages = [m for m in data["messages"] if m.get("message_type") == "assistant_message"]
|
||||||
|
assert len(assistant_messages) > 0, "Should receive at least one assistant message"
|
||||||
|
|
||||||
|
def test_new_pattern_list_messages(self, client: Letta, agent, server_url: str):
|
||||||
|
"""Test listing messages using the new pattern: conversation_id='default' + agent_id query param."""
|
||||||
|
# First send a message to populate the conversation
|
||||||
|
requests.post(
|
||||||
|
f"{server_url}/v1/conversations/{agent.id}/messages",
|
||||||
|
json={
|
||||||
|
"messages": [{"role": "user", "content": "Setup message for list test"}],
|
||||||
|
"streaming": False,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# NEW PATTERN: conversation_id='default' + agent_id as query param
|
||||||
|
response = requests.get(
|
||||||
|
f"{server_url}/v1/conversations/default/messages",
|
||||||
|
params={"agent_id": agent.id},
|
||||||
|
)
|
||||||
|
assert response.status_code == 200, f"New pattern should work for listing messages: {response.text}"
|
||||||
|
data = response.json()
|
||||||
|
# Response is a list of messages directly
|
||||||
|
assert isinstance(data, list), "Response should be a list of messages"
|
||||||
|
assert len(data) >= 3, "Should have at least system + user + assistant messages"
|
||||||
|
|
||||||
|
def test_new_pattern_cancel(self, client: Letta, agent, server_url: str):
|
||||||
|
"""Test canceling runs using the new pattern: conversation_id='default' + agent_id query param."""
|
||||||
|
from letta.settings import settings
|
||||||
|
|
||||||
|
if not settings.track_agent_run:
|
||||||
|
pytest.skip("Run tracking disabled - skipping cancel test")
|
||||||
|
|
||||||
|
# NEW PATTERN: conversation_id='default' + agent_id as query param
|
||||||
|
response = requests.post(
|
||||||
|
f"{server_url}/v1/conversations/default/cancel",
|
||||||
|
params={"agent_id": agent.id},
|
||||||
|
)
|
||||||
|
# Returns 200 with results if runs exist, or 409 if no active runs
|
||||||
|
assert response.status_code in [200, 409], f"New pattern should work for cancel: {response.text}"
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
assert isinstance(data, dict), "Cancel should return a dict"
|
||||||
|
|
||||||
|
def test_new_pattern_compact(self, client: Letta, agent, server_url: str):
|
||||||
|
"""Test compacting conversation using the new pattern: conversation_id='default' + agent_id in body."""
|
||||||
|
# Send many messages to have enough for compaction
|
||||||
|
for i in range(10):
|
||||||
|
requests.post(
|
||||||
|
f"{server_url}/v1/conversations/{agent.id}/messages",
|
||||||
|
json={
|
||||||
|
"messages": [{"role": "user", "content": f"Message {i} for compaction test"}],
|
||||||
|
"streaming": False,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# NEW PATTERN: conversation_id='default' + agent_id in request body
|
||||||
|
response = requests.post(
|
||||||
|
f"{server_url}/v1/conversations/default/compact",
|
||||||
|
json={"agent_id": agent.id},
|
||||||
|
)
|
||||||
|
# May return 200 (success) or 400 (not enough messages to compact)
|
||||||
|
assert response.status_code in [200, 400], f"New pattern should accept agent_id parameter: {response.text}"
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
assert "summary" in data, "Response should contain summary"
|
||||||
|
assert "num_messages_before" in data, "Response should contain num_messages_before"
|
||||||
|
assert "num_messages_after" in data, "Response should contain num_messages_after"
|
||||||
|
|
||||||
|
def test_new_pattern_stream_retrieve(self, client: Letta, agent, server_url: str):
|
||||||
|
"""Test retrieving stream using the new pattern: conversation_id='default' + agent_id in body."""
|
||||||
|
# NEW PATTERN: conversation_id='default' + agent_id in request body
|
||||||
|
# Note: This will likely return 400 if no active run exists, which is expected
|
||||||
|
response = requests.post(
|
||||||
|
f"{server_url}/v1/conversations/default/stream",
|
||||||
|
json={"agent_id": agent.id},
|
||||||
|
)
|
||||||
|
# Either 200 (if run exists) or 400 (no active run) are both acceptable
|
||||||
|
assert response.status_code in [200, 400], f"Stream retrieve should accept new pattern: {response.text}"
|
||||||
|
|
||||||
|
|
||||||
class TestConversationDelete:
|
class TestConversationDelete:
|
||||||
"""Tests for the conversation delete endpoint."""
|
"""Tests for the conversation delete endpoint."""
|
||||||
|
|||||||
Reference in New Issue
Block a user