Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def __init__(
self._stopped: bool = False
self._hook_registered: bool = False

async def __aenter__(self) -> "TypingIndicator":
async def __aenter__(self) -> TypingIndicator:
self.start()
return self

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,19 @@ async def send_activities(
raise ValueError("send_activities: activities list cannot be empty")

responses = []
buffered_reply_activities = []

for activity in activities:
activity.id = None

response = ResourceResponse()

buffer_replies = (
context.activity.delivery_mode == DeliveryModes.expect_replies
)
if buffer_replies:
buffered_reply_activities.append(activity)

if activity.type == ActivityTypes.invoke_response:
context.turn_state[self.INVOKE_RESPONSE_KEY] = activity
elif (
Expand All @@ -83,7 +90,7 @@ async def send_activities(
):
# no-op
pass
else:
elif not buffer_replies:
connector_client = context.services.get(ConnectorClientBase)
if not connector_client:
raise RuntimeError(
Expand All @@ -110,6 +117,8 @@ async def send_activities(

responses.append(response)

if buffered_reply_activities:
context.buffered_reply_activities.extend(buffered_reply_activities)
return responses

async def update_activity(self, context: TurnContext, activity: Activity):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
Activity,
ActivityTypes,
ConversationReference,
DeliveryModes,
InputHints,
Mention,
ResourceResponse,
Expand Down Expand Up @@ -47,9 +46,6 @@ def __call__(self) -> Awaitable[T]: ...


class TurnContext(TurnContextProtocol):
# Same constant as in the BF Adapter, duplicating here to avoid circular dependency
_INVOKE_RESPONSE_KEY = "TurnContext.InvokeResponse"

_activity: Activity

_on_send_activities: list[OnSendActivitiesHandler]
Expand Down Expand Up @@ -203,7 +199,6 @@ async def send_activities(
self, activities: list[Activity]
) -> list[ResourceResponse]:
sent_non_trace_activity = False
# TODO: Check activity serialization
ref = self.activity.get_conversation_reference()

with spans.TurnContextSendActivities(self):
Expand All @@ -229,23 +224,6 @@ def activity_validator(activity: Activity) -> Activity:
# send activities through adapter
async def logic() -> list[ResourceResponse]:
nonlocal sent_non_trace_activity

if self.activity.delivery_mode == DeliveryModes.expect_replies:
responses = []
for activity in output:
self.buffered_reply_activities.append(activity)
# Ensure the TurnState has the InvokeResponseKey, since this activity
# is not being sent through the adapter, where it would be added to TurnState.
if activity.type == ActivityTypes.invoke_response:
self.turn_state[TurnContext._INVOKE_RESPONSE_KEY] = activity

responses.append(ResourceResponse())

if sent_non_trace_activity:
self.responded = True

return responses

responses = await self.adapter.send_activities(self, output)
if sent_non_trace_activity:
self.responded = True
Expand Down
25 changes: 25 additions & 0 deletions tests/hosting_core/test_channel_service_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from microsoft_agents.activity import (
Activity,
ActivityTypes,
ConversationResourceResponse,
ConversationParameters,
DeliveryModes,
Expand Down Expand Up @@ -160,6 +161,30 @@ async def callback(context: TurnContext):
assert context_arg.services.get(UserTokenClientBase) is user_token_client
assert not context_arg.services.has(ConnectorClientBase)

@pytest.mark.asyncio
async def test_send_activities_buffers_expect_replies_without_connector(
self, adapter
):
context = TurnContext(
adapter,
Activity(
type=ActivityTypes.message,
conversation={"id": "conversation123"},
channel_id="channel_id",
delivery_mode=DeliveryModes.expect_replies,
),
)
activities = [
Activity(type=ActivityTypes.message, text="reply"),
Activity(type=ActivityTypes.typing),
]

responses = await adapter.send_activities(context, activities)

assert len(responses) == 2
assert context.buffered_reply_activities == activities
assert not context.services.has(ConnectorClientBase)

@pytest.mark.asyncio
async def test_process_activity_normal_no_service_url(
self, mocker, user_token_client, adapter
Expand Down