Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
225 changes: 225 additions & 0 deletions docs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,52 @@
}
}
},
"/v1/tools": {
"get": {
"tags": [
"tools"
],
"summary": "Tools Endpoint Handler",
"description": "Handle requests to the /tools endpoint.\n\nProcess GET requests to the /tools endpoint, returning a consolidated list of\navailable tools from all configured MCP servers.\n\nRaises:\n HTTPException: If unable to connect to the Llama Stack server or if\n tool retrieval fails for any reason.\n\nReturns:\n ToolsResponse: An object containing the consolidated list of available tools\n with metadata including tool name, description, parameters, and server source.",
"operationId": "tools_endpoint_handler_v1_tools_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ToolsResponse"
},
"example": {
"tools": [
{
"identifier": "",
"description": "",
"parameters": [
{
"name": "",
"description": "",
"parameter_type": "",
"required": "True/False",
"default": "null"
}
],
"provider_id": "",
"toolgroup_id": "",
"server_source": "",
"type": "tool"
}
]
}
}
}
},
"500": {
"description": "Connection to Llama Stack is broken or MCP server error"
}
}
}
},
"/v1/shields": {
"get": {
"tags": [
Expand Down Expand Up @@ -945,6 +991,87 @@
}
}
}
},
"put": {
"tags": [
"conversations_v2"
],
"summary": "Update Conversation Endpoint Handler",
"description": "Handle request to update a conversation topic summary by ID.",
"operationId": "update_conversation_endpoint_handler_v2_conversations__conversation_id__put",
"parameters": [
{
"name": "conversation_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"title": "Conversation Id"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ConversationUpdateRequest"
}
}
}
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ConversationUpdateResponse"
}
}
},
"conversation_id": "123e4567-e89b-12d3-a456-426614174000",
"success": true,
"message": "Topic summary updated successfully"
},
"400": {
"description": "Missing or invalid credentials provided by client",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedResponse"
}
}
}
},
"401": {
"description": "Unauthorized: Invalid or missing Bearer token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedResponse"
}
}
}
},
"404": {
"detail": {
"response": "Conversation not found",
"cause": "The specified conversation ID does not exist."
},
"description": "Not Found"
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/readiness": {
Expand Down Expand Up @@ -1114,8 +1241,10 @@
"get_conversation",
"list_conversations",
"delete_conversation",
"update_conversation",
"feedback",
"get_models",
"get_tools",
"get_shields",
"get_metrics",
"get_config",
Expand Down Expand Up @@ -1693,6 +1822,63 @@
}
]
},
"ConversationUpdateRequest": {
"properties": {
"topic_summary": {
"type": "string",
"maxLength": 1000,
"minLength": 1,
"title": "Topic Summary",
"description": "The new topic summary for the conversation",
"examples": [
"Discussion about machine learning algorithms"
]
}
},
"additionalProperties": false,
"type": "object",
"required": [
"topic_summary"
],
"title": "ConversationUpdateRequest",
"description": "Model representing a request to update a conversation topic summary.\n\nAttributes:\n topic_summary: The new topic summary for the conversation.\n\nExample:\n ```python\n update_request = ConversationUpdateRequest(\n topic_summary=\"Discussion about machine learning algorithms\"\n )\n ```"
},
"ConversationUpdateResponse": {
"properties": {
"conversation_id": {
"type": "string",
"title": "Conversation Id",
"description": "The conversation ID (UUID) that was updated",
"examples": [
"123e4567-e89b-12d3-a456-426614174000"
]
},
"success": {
"type": "boolean",
"title": "Success",
"description": "Whether the update was successful",
"examples": [
true
]
},
"message": {
"type": "string",
"title": "Message",
"description": "A message about the update result",
"examples": [
"Topic summary updated successfully"
]
}
},
"type": "object",
"required": [
"conversation_id",
"success",
"message"
],
"title": "ConversationUpdateResponse",
"description": "Model representing a response for updating a conversation topic summary.\n\nAttributes:\n conversation_id: The conversation ID (UUID) that was updated.\n success: Whether the update was successful.\n message: A message about the update result.\n\nExample:\n ```python\n update_response = ConversationUpdateResponse(\n conversation_id=\"123e4567-e89b-12d3-a456-426614174000\",\n success=True,\n message=\"Topic summary updated successfully\",\n )\n ```"
},
"ConversationsListResponse": {
"properties": {
"conversations": {
Expand Down Expand Up @@ -3170,6 +3356,45 @@
"title": "ToolCall",
"description": "Model representing a tool call made during response generation."
},
"ToolsResponse": {
"properties": {
"tools": {
"items": {
"additionalProperties": true,
"type": "object"
},
"type": "array",
"title": "Tools",
"description": "List of tools available from all configured MCP servers and built-in toolgroups",
"examples": [
[
{
"description": "Read contents of a file from the filesystem",
"identifier": "filesystem_read",
"parameters": [
{
"description": "Path to the file to read",
"name": "path",
"parameter_type": "string",
"required": true
}
],
"provider_id": "model-context-protocol",
"server_source": "http://localhost:3000",
"toolgroup_id": "filesystem-tools",
"type": "tool"
}
]
]
}
},
"type": "object",
"required": [
"tools"
],
"title": "ToolsResponse",
"description": "Model representing a response to tools request."
},
"UnauthorizedResponse": {
"properties": {
"detail": {
Expand Down
74 changes: 74 additions & 0 deletions src/app/endpoints/conversations_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
from configuration import configuration
from models.cache_entry import CacheEntry
from models.config import Action
from models.requests import ConversationUpdateRequest
from models.responses import (
ConversationDeleteResponse,
ConversationResponse,
ConversationUpdateResponse,
ConversationsListResponseV2,
UnauthorizedResponse,
)
Expand Down Expand Up @@ -89,6 +91,28 @@
}
}

conversation_update_responses: dict[int | str, dict[str, Any]] = {
200: {
"conversation_id": "123e4567-e89b-12d3-a456-426614174000",
"success": True,
"message": "Topic summary updated successfully",
},
400: {
"description": "Missing or invalid credentials provided by client",
"model": UnauthorizedResponse,
},
401: {
"description": "Unauthorized: Invalid or missing Bearer token",
"model": UnauthorizedResponse,
},
404: {
"detail": {
"response": "Conversation not found",
"cause": "The specified conversation ID does not exist.",
}
},
}


@router.get("/conversations", responses=conversations_list_responses)
@authorize(Action.LIST_CONVERSATIONS)
Expand Down Expand Up @@ -207,6 +231,56 @@ async def delete_conversation_endpoint_handler(
)


@router.put("/conversations/{conversation_id}", responses=conversation_update_responses)
@authorize(Action.UPDATE_CONVERSATION)
async def update_conversation_endpoint_handler(
conversation_id: str,
update_request: ConversationUpdateRequest,
auth: Any = Depends(get_auth_dependency()),
) -> ConversationUpdateResponse:
"""Handle request to update a conversation topic summary by ID."""
check_configuration_loaded(configuration)
check_valid_conversation_id(conversation_id)

user_id = auth[0]
logger.info(
"Updating topic summary for conversation %s for user %s",
conversation_id,
user_id,
)

skip_userid_check = auth[2]

if configuration.conversation_cache is None:
logger.warning("Conversation cache is not configured")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail={
"response": "Conversation cache is not configured",
"cause": "Conversation cache is not configured",
},
)

check_conversation_existence(user_id, conversation_id)

# Update the topic summary in the cache
configuration.conversation_cache.set_topic_summary(
user_id, conversation_id, update_request.topic_summary, skip_userid_check
)

logger.info(
"Successfully updated topic summary for conversation %s for user %s",
conversation_id,
user_id,
)

return ConversationUpdateResponse(
conversation_id=conversation_id,
success=True,
message="Topic summary updated successfully",
)


def check_valid_conversation_id(conversation_id: str) -> None:
"""Check validity of conversation ID format."""
if not check_suid(conversation_id):
Expand Down
3 changes: 3 additions & 0 deletions src/models/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,9 @@ class Action(str, Enum):

# Access the conversation delete endpoint
DELETE_CONVERSATION = "delete_conversation"

# Access the conversation update endpoint
UPDATE_CONVERSATION = "update_conversation"
FEEDBACK = "feedback"
GET_MODELS = "get_models"
GET_TOOLS = "get_tools"
Expand Down
26 changes: 26 additions & 0 deletions src/models/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,3 +410,29 @@ class FeedbackStatusUpdateRequest(BaseModel):
def get_value(self) -> bool:
"""Return the value of the status attribute."""
return self.status


class ConversationUpdateRequest(BaseModel):
"""Model representing a request to update a conversation topic summary.

Attributes:
topic_summary: The new topic summary for the conversation.

Example:
```python
update_request = ConversationUpdateRequest(
topic_summary="Discussion about machine learning algorithms"
)
```
"""

topic_summary: str = Field(
...,
description="The new topic summary for the conversation",
examples=["Discussion about machine learning algorithms"],
min_length=1,
max_length=1000,
)

# Reject unknown fields
model_config = {"extra": "forbid"}
Loading
Loading