diff --git a/libraries/Botbuilder/microsoft-agents-botbuilder/microsoft/agents/botbuilder/channel_service_adapter.py b/libraries/Botbuilder/microsoft-agents-botbuilder/microsoft/agents/botbuilder/channel_service_adapter.py index 8ccd81fb..bf6b018a 100644 --- a/libraries/Botbuilder/microsoft-agents-botbuilder/microsoft/agents/botbuilder/channel_service_adapter.py +++ b/libraries/Botbuilder/microsoft-agents-botbuilder/microsoft/agents/botbuilder/channel_service_adapter.py @@ -7,7 +7,7 @@ from abc import ABC from copy import Error from http import HTTPStatus -from typing import Awaitable, Callable, Protocol, cast +from typing import Awaitable, Callable, cast from uuid import uuid4 from microsoft.agents.core.models import ( @@ -25,7 +25,12 @@ InvokeResponse, ResourceResponse, ) -from microsoft.agents.connector import ConnectorClientBase, UserTokenClientBase +from microsoft.agents.connector import ( + ConnectorClientBase, + UserTokenClientBase, + ConnectorClient, + UserTokenClient, +) from microsoft.agents.authentication import AuthenticationConstants, ClaimsIdentity from .channel_service_client_factory_base import ChannelServiceClientFactoryBase from .channel_adapter import ChannelAdapter @@ -91,7 +96,8 @@ async def send_activities( else: response = ( await connector_client.conversations.send_to_conversation( - activity.conversation.id, activity + activity.conversation.id, + activity.model_dump(by_alias=True, exclude_unset=True), ) ) @@ -326,7 +332,7 @@ async def process_activity( use_anonymous_auth_callback = True # Create the connector client to use for outbound requests. - connector_client = ( + connector_client: ConnectorClient = ( await self._channel_service_client_factory.create_connector_client( claims_identity, activity.service_url, @@ -337,7 +343,7 @@ async def process_activity( ) # Create a UserTokenClient instance for the OAuth flow. - user_token_client = ( + user_token_client: UserTokenClient = ( await self._channel_service_client_factory.create_user_token_client( claims_identity, use_anonymous_auth_callback ) @@ -355,6 +361,9 @@ async def process_activity( await self.run_pipeline(context, callback) + await connector_client.close() + await user_token_client.close() + # If there are any results they will have been left on the TurnContext. return self._process_turn_results(context) diff --git a/libraries/Core/microsoft-agents-core/microsoft/agents/core/models/_agents_model.py b/libraries/Core/microsoft-agents-core/microsoft/agents/core/models/_agents_model.py index ba930b56..e6d8bf4f 100644 --- a/libraries/Core/microsoft-agents-core/microsoft/agents/core/models/_agents_model.py +++ b/libraries/Core/microsoft-agents-core/microsoft/agents/core/models/_agents_model.py @@ -5,6 +5,7 @@ class AgentsModel(BaseModel): class Config: alias_generator = to_camel + populate_by_name = True """ @model_serializer diff --git a/libraries/Core/microsoft-agents-core/microsoft/agents/core/models/activity.py b/libraries/Core/microsoft-agents-core/microsoft/agents/core/models/activity.py index d3ecf95c..836b94dd 100644 --- a/libraries/Core/microsoft-agents-core/microsoft/agents/core/models/activity.py +++ b/libraries/Core/microsoft-agents-core/microsoft/agents/core/models/activity.py @@ -491,7 +491,7 @@ def create_trace_activity( ) @staticmethod - def create_typing_activity(): + def create_typing_activity() -> "Activity": """ Creates an instance of the :class:`Activity` class as a TypingActivity object. @@ -499,30 +499,27 @@ def create_typing_activity(): """ return Activity(type=ActivityTypes.typing) - def get_conversation_reference(self): + def get_conversation_reference(self) -> ConversationReference: """ Creates a ConversationReference based on this activity. :returns: A conversation reference for the conversation that contains this activity. """ - # TODO: Fix serialization story - conversation_reference = { - "activityId": ( + + return ConversationReference( + activity_id=( self.id if self.type != ActivityTypes.conversation_update or self.channel_id not in ["directline", "webchat"] else None ), - "user": self.from_property.model_dump(by_alias=True, exclude_unset=True), - "bot": self.recipient.model_dump(by_alias=True, exclude_unset=True), - "conversation": self.conversation.model_dump( - by_alias=True, exclude_unset=True - ), - "channelId": self.channel_id, - "locale": self.locale, - "serviceUrl": self.service_url, - } - return ConversationReference.model_validate(conversation_reference) + user=copy(self.from_property), + bot=copy(self.recipient), + conversation=copy(self.conversation), + channel_id=self.channel_id, + locale=self.locale, + service_url=self.service_url, + ) def get_mentions(self) -> list[Mention]: """ diff --git a/libraries/Core/microsoft-agents-core/microsoft/agents/core/models/conversation_reference.py b/libraries/Core/microsoft-agents-core/microsoft/agents/core/models/conversation_reference.py index 44ccf1e6..91558845 100644 --- a/libraries/Core/microsoft-agents-core/microsoft/agents/core/models/conversation_reference.py +++ b/libraries/Core/microsoft-agents-core/microsoft/agents/core/models/conversation_reference.py @@ -1,4 +1,5 @@ from uuid import uuid4 as uuid +from typing import Optional from .channel_account import ChannelAccount from .conversation_account import ConversationAccount @@ -32,12 +33,13 @@ class ConversationReference(AgentsModel): :type service_url: str """ - activity_id: NonEmptyString = None + # optionals here are due to webchat + activity_id: Optional[NonEmptyString] = None user: ChannelAccount = None bot: ChannelAccount conversation: ConversationAccount channel_id: NonEmptyString - locale: NonEmptyString = None + locale: Optional[NonEmptyString] = None service_url: NonEmptyString def get_continuation_activity(self) -> "Activity": # type: ignore diff --git a/libraries/Hosting/microsoft-agents-hosting-aiohttp/microsoft/agents/hosting/aiohttp/cloud_adapter.py b/libraries/Hosting/microsoft-agents-hosting-aiohttp/microsoft/agents/hosting/aiohttp/cloud_adapter.py index b3d2cbfb..30e2ee0c 100644 --- a/libraries/Hosting/microsoft-agents-hosting-aiohttp/microsoft/agents/hosting/aiohttp/cloud_adapter.py +++ b/libraries/Hosting/microsoft-agents-hosting-aiohttp/microsoft/agents/hosting/aiohttp/cloud_adapter.py @@ -70,7 +70,6 @@ async def process(self, request: Request, bot: Bot) -> Optional[Response]: raise HTTPUnsupportedMediaType() activity: Activity = Activity.model_validate(body) - # TODO: Add the ability to pass in the ClaimsIdentity claims_identity: ClaimsIdentity = request.get("claims_identity") # A POST request must contain an Activity @@ -86,6 +85,7 @@ async def process(self, request: Request, bot: Bot) -> Optional[Response]: invoke_response = await self.process_activity( claims_identity, activity, bot.on_turn ) + if ( activity.type == "invoke" or activity.delivery_mode == DeliveryModes.expect_replies diff --git a/test_samples/echo_bot/app.py b/test_samples/echo_bot/app.py index db30f8ac..3c7d561b 100644 --- a/test_samples/echo_bot/app.py +++ b/test_samples/echo_bot/app.py @@ -1,8 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -from aiohttp import web -from aiohttp.web import Request, Response +from aiohttp.web import Application, Request, Response, run_app from microsoft.agents.botbuilder import RestChannelServiceClientFactory from microsoft.agents.hosting.aiohttp import CloudAdapter, jwt_authorization_middleware @@ -41,13 +40,13 @@ async def messages(req: Request) -> Response: return await adapter.process(req, BOT) -APP = web.Application(middlewares=[jwt_authorization_middleware]) +APP = Application(middlewares=[jwt_authorization_middleware]) APP.router.add_post("/api/messages", messages) APP["bot_configuration"] = CONFIG APP["adapter"] = ADAPTER if __name__ == "__main__": try: - web.run_app(APP, host="localhost", port=CONFIG.PORT) + run_app(APP, host="localhost", port=CONFIG.PORT) except Exception as error: raise error \ No newline at end of file