Skip to content

Commit efc4f4b

Browse files
authored
Http framework integration refactor (#221)
* Http framework integration refactor WIP * Revert "Http framework integration refactor WIP" This reverts commit 318a0ce. * HTTP Refactor WIP: merge conflicts pending * Cleaning unused references * Fix: minor comments * Fix: minor comments
1 parent f7ae951 commit efc4f4b

18 files changed

Lines changed: 1275 additions & 422 deletions

File tree

libraries/microsoft-agents-hosting-aiohttp/microsoft_agents/hosting/aiohttp/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
jwt_authorization_middleware,
77
jwt_authorization_decorator,
88
)
9-
from .app.streaming import (
9+
10+
# Import streaming utilities from core for backward compatibility
11+
from microsoft_agents.hosting.core.app.streaming import (
1012
Citation,
1113
CitationUtil,
1214
StreamingResponse,

libraries/microsoft-agents-hosting-aiohttp/microsoft_agents/hosting/aiohttp/app/streaming/streaming_response.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33

44
import asyncio
55
import logging
6-
from typing import List, Optional, Callable, Literal, TYPE_CHECKING
7-
from dataclasses import dataclass
6+
from typing import List, Optional, Callable, Literal
87

98
from microsoft_agents.activity import (
109
Activity,
Lines changed: 65 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,194 +1,143 @@
11
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT License.
33
import json
4-
from typing import List, Union, Type
54

65
from aiohttp.web import RouteTableDef, Request, Response
76

8-
from microsoft_agents.activity import (
9-
AgentsModel,
10-
Activity,
11-
AttachmentData,
12-
ConversationParameters,
13-
Transcript,
14-
)
157
from microsoft_agents.hosting.core import ChannelApiHandlerProtocol
8+
from microsoft_agents.hosting.core.http import ChannelServiceRoutes
169

1710

18-
async def deserialize_from_body(
19-
request: Request, target_model: Type[AgentsModel]
20-
) -> Activity:
21-
if "application/json" in request.headers["Content-Type"]:
22-
body = await request.json()
23-
else:
24-
return Response(status=415)
11+
class AiohttpRequestAdapter:
12+
"""Adapter for aiohttp requests to use with ChannelServiceRoutes."""
2513

26-
return target_model.model_validate(body)
14+
def __init__(self, request: Request):
15+
self._request = request
2716

17+
@property
18+
def method(self) -> str:
19+
return self._request.method
2820

29-
def get_serialized_response(
30-
model_or_list: Union[AgentsModel, List[AgentsModel]],
31-
) -> Response:
32-
if isinstance(model_or_list, AgentsModel):
33-
json_obj = model_or_list.model_dump(
34-
mode="json", exclude_unset=True, by_alias=True
35-
)
36-
else:
37-
json_obj = [
38-
model.model_dump(mode="json", exclude_unset=True, by_alias=True)
39-
for model in model_or_list
40-
]
21+
@property
22+
def headers(self):
23+
return self._request.headers
24+
25+
async def json(self):
26+
return await self._request.json()
27+
28+
def get_claims_identity(self):
29+
return self._request.get("claims_identity")
4130

42-
return Response(body=json.dumps(json_obj), content_type="application/json")
31+
def get_path_param(self, name: str) -> str:
32+
return self._request.match_info[name]
4333

4434

4535
def channel_service_route_table(
4636
handler: ChannelApiHandlerProtocol, base_url: str = ""
4737
) -> RouteTableDef:
48-
# pylint: disable=unused-variable
38+
"""Create aiohttp route table for Channel Service API.
39+
40+
Args:
41+
handler: The handler that implements the Channel API protocol.
42+
base_url: Optional base URL prefix for all routes.
43+
44+
Returns:
45+
RouteTableDef with all channel service routes.
46+
"""
4947
routes = RouteTableDef()
48+
service_routes = ChannelServiceRoutes(handler, base_url)
49+
50+
def json_response(data: dict) -> Response:
51+
return Response(body=json.dumps(data), content_type="application/json")
5052

5153
@routes.post(base_url + "/v3/conversations/{conversation_id}/activities")
5254
async def send_to_conversation(request: Request):
53-
activity = await deserialize_from_body(request, Activity)
54-
result = await handler.on_send_to_conversation(
55-
request.get("claims_identity"),
56-
request.match_info["conversation_id"],
57-
activity,
55+
result = await service_routes.send_to_conversation(
56+
AiohttpRequestAdapter(request)
5857
)
59-
60-
return get_serialized_response(result)
58+
return json_response(result)
6159

6260
@routes.post(
6361
base_url + "/v3/conversations/{conversation_id}/activities/{activity_id}"
6462
)
6563
async def reply_to_activity(request: Request):
66-
activity = await deserialize_from_body(request, Activity)
67-
result = await handler.on_reply_to_activity(
68-
request.get("claims_identity"),
69-
request.match_info["conversation_id"],
70-
request.match_info["activity_id"],
71-
activity,
72-
)
73-
74-
return get_serialized_response(result)
64+
result = await service_routes.reply_to_activity(AiohttpRequestAdapter(request))
65+
return json_response(result)
7566

7667
@routes.put(
7768
base_url + "/v3/conversations/{conversation_id}/activities/{activity_id}"
7869
)
7970
async def update_activity(request: Request):
80-
activity = await deserialize_from_body(request, Activity)
81-
result = await handler.on_update_activity(
82-
request.get("claims_identity"),
83-
request.match_info["conversation_id"],
84-
request.match_info["activity_id"],
85-
activity,
86-
)
87-
88-
return get_serialized_response(result)
71+
result = await service_routes.update_activity(AiohttpRequestAdapter(request))
72+
return json_response(result)
8973

9074
@routes.delete(
9175
base_url + "/v3/conversations/{conversation_id}/activities/{activity_id}"
9276
)
9377
async def delete_activity(request: Request):
94-
await handler.on_delete_activity(
95-
request.get("claims_identity"),
96-
request.match_info["conversation_id"],
97-
request.match_info["activity_id"],
98-
)
99-
78+
await service_routes.delete_activity(AiohttpRequestAdapter(request))
10079
return Response()
10180

10281
@routes.get(
10382
base_url
10483
+ "/v3/conversations/{conversation_id}/activities/{activity_id}/members"
10584
)
10685
async def get_activity_members(request: Request):
107-
result = await handler.on_get_activity_members(
108-
request.get("claims_identity"),
109-
request.match_info["conversation_id"],
110-
request.match_info["activity_id"],
86+
result = await service_routes.get_activity_members(
87+
AiohttpRequestAdapter(request)
11188
)
112-
113-
return get_serialized_response(result)
89+
return json_response(result)
11490

11591
@routes.post(base_url + "/")
11692
async def create_conversation(request: Request):
117-
conversation_parameters = deserialize_from_body(request, ConversationParameters)
118-
result = await handler.on_create_conversation(
119-
request.get("claims_identity"), conversation_parameters
93+
result = await service_routes.create_conversation(
94+
AiohttpRequestAdapter(request)
12095
)
121-
122-
return get_serialized_response(result)
96+
return json_response(result)
12397

12498
@routes.get(base_url + "/")
12599
async def get_conversation(request: Request):
126-
# TODO: continuation token? conversation_id?
127-
result = await handler.on_get_conversations(
128-
request.get("claims_identity"), None
129-
)
130-
131-
return get_serialized_response(result)
100+
result = await service_routes.get_conversations(AiohttpRequestAdapter(request))
101+
return json_response(result)
132102

133103
@routes.get(base_url + "/v3/conversations/{conversation_id}/members")
134104
async def get_conversation_members(request: Request):
135-
result = await handler.on_get_conversation_members(
136-
request.get("claims_identity"),
137-
request.match_info["conversation_id"],
105+
result = await service_routes.get_conversation_members(
106+
AiohttpRequestAdapter(request)
138107
)
139-
140-
return get_serialized_response(result)
108+
return json_response(result)
141109

142110
@routes.get(base_url + "/v3/conversations/{conversation_id}/members/{member_id}")
143111
async def get_conversation_member(request: Request):
144-
result = await handler.on_get_conversation_member(
145-
request.get("claims_identity"),
146-
request.match_info["member_id"],
147-
request.match_info["conversation_id"],
112+
result = await service_routes.get_conversation_member(
113+
AiohttpRequestAdapter(request)
148114
)
149-
150-
return get_serialized_response(result)
115+
return json_response(result)
151116

152117
@routes.get(base_url + "/v3/conversations/{conversation_id}/pagedmembers")
153118
async def get_conversation_paged_members(request: Request):
154-
# TODO: continuation token? page size?
155-
result = await handler.on_get_conversation_paged_members(
156-
request.get("claims_identity"),
157-
request.match_info["conversation_id"],
119+
result = await service_routes.get_conversation_paged_members(
120+
AiohttpRequestAdapter(request)
158121
)
159-
160-
return get_serialized_response(result)
122+
return json_response(result)
161123

162124
@routes.delete(base_url + "/v3/conversations/{conversation_id}/members/{member_id}")
163125
async def delete_conversation_member(request: Request):
164-
result = await handler.on_delete_conversation_member(
165-
request.get("claims_identity"),
166-
request.match_info["conversation_id"],
167-
request.match_info["member_id"],
126+
result = await service_routes.delete_conversation_member(
127+
AiohttpRequestAdapter(request)
168128
)
169-
170-
return get_serialized_response(result)
129+
return json_response(result)
171130

172131
@routes.post(base_url + "/v3/conversations/{conversation_id}/activities/history")
173132
async def send_conversation_history(request: Request):
174-
transcript = deserialize_from_body(request, Transcript)
175-
result = await handler.on_send_conversation_history(
176-
request.get("claims_identity"),
177-
request.match_info["conversation_id"],
178-
transcript,
133+
result = await service_routes.send_conversation_history(
134+
AiohttpRequestAdapter(request)
179135
)
180-
181-
return get_serialized_response(result)
136+
return json_response(result)
182137

183138
@routes.post(base_url + "/v3/conversations/{conversation_id}/attachments")
184139
async def upload_attachment(request: Request):
185-
attachment_data = deserialize_from_body(request, AttachmentData)
186-
result = await handler.on_upload_attachment(
187-
request.get("claims_identity"),
188-
request.match_info["conversation_id"],
189-
attachment_data,
190-
)
191-
192-
return get_serialized_response(result)
140+
result = await service_routes.upload_attachment(AiohttpRequestAdapter(request))
141+
return json_response(result)
193142

194143
return routes

0 commit comments

Comments
 (0)