diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/_channel_service_routes.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/_channel_service_routes.py index 3c7e5bcb..4aad06ef 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/_channel_service_routes.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/_channel_service_routes.py @@ -3,7 +3,7 @@ """Channel service route definitions (framework-agnostic logic).""" -from typing import Type +from typing import Type, TypeVar from microsoft_agents.activity import ( AgentsModel, @@ -16,6 +16,8 @@ from ._http_request_protocol import HttpRequestProtocol +AgentsModelT = TypeVar("AgentsModelT", bound=AgentsModel) + class ChannelServiceRoutes: """Defines the Channel Service API routes and their handlers. @@ -36,8 +38,8 @@ def __init__(self, handler: ChannelApiHandlerProtocol, base_url: str = ""): @staticmethod async def deserialize_from_body( - request: HttpRequestProtocol, target_model: Type[AgentsModel] - ) -> AgentsModel: + request: HttpRequestProtocol, target_model: Type[AgentsModelT] + ) -> AgentsModelT: """Deserialize request body to target model.""" content_type = request.headers.get("Content-Type", "") if "application/json" not in content_type: @@ -47,7 +49,9 @@ async def deserialize_from_body( return target_model.model_validate(body) @staticmethod - def serialize_model(model_or_list: AgentsModel | list[AgentsModel]) -> dict: + def serialize_model( + model_or_list: AgentsModelT | list[AgentsModelT], + ) -> dict | list[dict]: """Serialize model or list of models to JSON-compatible dict.""" if isinstance(model_or_list, AgentsModel): return model_or_list.model_dump( @@ -60,7 +64,9 @@ def serialize_model(model_or_list: AgentsModel | list[AgentsModel]) -> dict: ] # Route handler methods - async def send_to_conversation(self, request: HttpRequestProtocol) -> dict: + async def send_to_conversation( + self, request: HttpRequestProtocol + ) -> dict | list[dict]: """Handle POST /v3/conversations/{conversation_id}/activities.""" activity = await self.deserialize_from_body(request, Activity) conversation_id = request.get_path_param("conversation_id") @@ -71,7 +77,9 @@ async def send_to_conversation(self, request: HttpRequestProtocol) -> dict: ) return self.serialize_model(result) - async def reply_to_activity(self, request: HttpRequestProtocol) -> dict: + async def reply_to_activity( + self, request: HttpRequestProtocol + ) -> dict | list[dict]: """Handle POST /v3/conversations/{conversation_id}/activities/{activity_id}.""" activity = await self.deserialize_from_body(request, Activity) conversation_id = request.get_path_param("conversation_id") @@ -84,7 +92,7 @@ async def reply_to_activity(self, request: HttpRequestProtocol) -> dict: ) return self.serialize_model(result) - async def update_activity(self, request: HttpRequestProtocol) -> dict: + async def update_activity(self, request: HttpRequestProtocol) -> dict | list[dict]: """Handle PUT /v3/conversations/{conversation_id}/activities/{activity_id}.""" activity = await self.deserialize_from_body(request, Activity) conversation_id = request.get_path_param("conversation_id") @@ -107,7 +115,9 @@ async def delete_activity(self, request: HttpRequestProtocol) -> None: activity_id, ) - async def get_activity_members(self, request: HttpRequestProtocol) -> dict: + async def get_activity_members( + self, request: HttpRequestProtocol + ) -> dict | list[dict]: """Handle GET /v3/conversations/{conversation_id}/activities/{activity_id}/members.""" conversation_id = request.get_path_param("conversation_id") activity_id = request.get_path_param("activity_id") @@ -118,7 +128,9 @@ async def get_activity_members(self, request: HttpRequestProtocol) -> dict: ) return self.serialize_model(result) - async def create_conversation(self, request: HttpRequestProtocol) -> dict: + async def create_conversation( + self, request: HttpRequestProtocol + ) -> dict | list[dict]: """Handle POST /.""" conversation_parameters = await self.deserialize_from_body( request, ConversationParameters @@ -128,7 +140,9 @@ async def create_conversation(self, request: HttpRequestProtocol) -> dict: ) return self.serialize_model(result) - async def get_conversations(self, request: HttpRequestProtocol) -> dict: + async def get_conversations( + self, request: HttpRequestProtocol + ) -> dict | list[dict]: """Handle GET /.""" # TODO: continuation token? conversation_id? result = await self.handler.on_get_conversations( @@ -136,7 +150,9 @@ async def get_conversations(self, request: HttpRequestProtocol) -> dict: ) return self.serialize_model(result) - async def get_conversation_members(self, request: HttpRequestProtocol) -> dict: + async def get_conversation_members( + self, request: HttpRequestProtocol + ) -> dict | list[dict]: """Handle GET /v3/conversations/{conversation_id}/members.""" conversation_id = request.get_path_param("conversation_id") result = await self.handler.on_get_conversation_members( @@ -145,7 +161,9 @@ async def get_conversation_members(self, request: HttpRequestProtocol) -> dict: ) return self.serialize_model(result) - async def get_conversation_member(self, request: HttpRequestProtocol) -> dict: + async def get_conversation_member( + self, request: HttpRequestProtocol + ) -> dict | list[dict]: """Handle GET /v3/conversations/{conversation_id}/members/{member_id}.""" conversation_id = request.get_path_param("conversation_id") member_id = request.get_path_param("member_id") @@ -158,7 +176,7 @@ async def get_conversation_member(self, request: HttpRequestProtocol) -> dict: async def get_conversation_paged_members( self, request: HttpRequestProtocol - ) -> dict: + ) -> dict | list[dict]: """Handle GET /v3/conversations/{conversation_id}/pagedmembers.""" conversation_id = request.get_path_param("conversation_id") # TODO: continuation token? page size? @@ -168,18 +186,19 @@ async def get_conversation_paged_members( ) return self.serialize_model(result) - async def delete_conversation_member(self, request: HttpRequestProtocol) -> dict: + async def delete_conversation_member(self, request: HttpRequestProtocol) -> None: """Handle DELETE /v3/conversations/{conversation_id}/members/{member_id}.""" conversation_id = request.get_path_param("conversation_id") member_id = request.get_path_param("member_id") - result = await self.handler.on_delete_conversation_member( + await self.handler.on_delete_conversation_member( request.get_claims_identity(), conversation_id, member_id, ) - return self.serialize_model(result) - async def send_conversation_history(self, request: HttpRequestProtocol) -> dict: + async def send_conversation_history( + self, request: HttpRequestProtocol + ) -> dict | list[dict]: """Handle POST /v3/conversations/{conversation_id}/activities/history.""" conversation_id = request.get_path_param("conversation_id") transcript = await self.deserialize_from_body(request, Transcript) @@ -190,7 +209,9 @@ async def send_conversation_history(self, request: HttpRequestProtocol) -> dict: ) return self.serialize_model(result) - async def upload_attachment(self, request: HttpRequestProtocol) -> dict: + async def upload_attachment( + self, request: HttpRequestProtocol + ) -> dict | list[dict]: """Handle POST /v3/conversations/{conversation_id}/attachments.""" conversation_id = request.get_path_param("conversation_id") attachment_data = await self.deserialize_from_body(request, AttachmentData) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/_http_response.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/_http_response.py index 955ff2ad..efe68657 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/_http_response.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/_http_response.py @@ -28,7 +28,7 @@ def ok(body: Any = None) -> HttpResponse: @staticmethod def accepted() -> HttpResponse: """Create 202 Accepted response.""" - return HttpResponse(status_code=202) + return HttpResponse(status_code=202, content_type=None) @staticmethod def json(body: Any, status_code: int = 200) -> HttpResponse: