-
Notifications
You must be signed in to change notification settings - Fork 60
Consolidating StreamingResponse definitions and tweaking streaming entities' serialization #327
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
rodrigobr-msft
wants to merge
29
commits into
main
from
users/robrandao/streaming-response-consolidation
Closed
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
d3652ff
Consolidating StreamingResponse definitions and slight fixes
rodrigobr-msft e8aa9b2
Improving StreamingResponse tests and formatting
rodrigobr-msft a374ced
Merge branch 'main' into users/robrandao/streaming-response-consolida…
rodrigobr-msft c730a89
Adding tests for StreamingResponse
rodrigobr-msft ead6403
Adding basic StreamingResponse tests
rodrigobr-msft 03ed8f7
Adding integration tests for streaming
rodrigobr-msft 634b608
Finalized basic end-to-end streaming tests
rodrigobr-msft 13dda2a
Addressing Copilot PR review
rodrigobr-msft c121834
Validating channel_id before accessing parent channel
rodrigobr-msft a230b9a
Removing duplicate sample
rodrigobr-msft ee5016c
Another commit
rodrigobr-msft 86686ae
Adding '@type' aliases for AIEntity-related classes
rodrigobr-msft a6a58f8
Fixing further linting issues
rodrigobr-msft df23e5c
Removing exclude_unset=True usagei n reply_to_activity to enable prop…
rodrigobr-msft 904557f
Adding improved serialization for AIEntity and related classes
rodrigobr-msft bb2980c
Readded exclude_unset=True usage in ConversationsOperations
rodrigobr-msft b976eb9
Formatting
rodrigobr-msft e634a91
Removing unused imports
rodrigobr-msft eedfd6c
Small fixes
rodrigobr-msft 8d1b300
Removing unnecessary dummy constructors
rodrigobr-msft f8639c3
Fixing tests
rodrigobr-msft 478651d
Potential fix for pull request finding
rodrigobr-msft c9b25e9
Adding formatting and comment
rodrigobr-msft d77f479
Potential fix for pull request finding
rodrigobr-msft e07bed3
Merge branch 'main' into users/robrandao/streaming-response-consolida…
rodrigobr-msft ea765fd
Reformatting
rodrigobr-msft fe605f3
Merge branch 'users/robrandao/streaming-response-consolidation' of ht…
rodrigobr-msft 7757e09
Potential fix for pull request finding
rodrigobr-msft 20a903f
Potential fix for pull request finding
rodrigobr-msft File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
404 changes: 0 additions & 404 deletions
404
dev/microsoft-agents-testing/microsoft_agents/testing/core/fluent/activity.py
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| import pytest | ||
| import asyncio | ||
|
|
||
| from microsoft_agents.activity import ( | ||
| Activity, | ||
| ActivityTypes, | ||
| Channels, | ||
| Entity | ||
| ) | ||
|
|
||
| from microsoft_agents.hosting.core import ( | ||
| TurnContext, | ||
| TurnState, | ||
| ) | ||
|
|
||
| from microsoft_agents.testing import ( | ||
| AgentClient, | ||
| AgentEnvironment, | ||
| AiohttpScenario, | ||
| ) | ||
|
|
||
| FULL_TEXT = "This is a streaming response." | ||
| CHUNKS = FULL_TEXT.split() | ||
|
|
||
| def get_streaminfo(activity: Activity) -> Entity: | ||
| for entity in activity.entities: | ||
| if isinstance(entity, dict) and entity.get("type") == "streaminfo": | ||
| return Entity.model_validate(entity) | ||
| elif isinstance(entity, Entity) and entity.type == "streaminfo": | ||
rodrigobr-msft marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return entity | ||
| raise ValueError("No streaminfo entity found") | ||
|
|
||
| async def init_agent(env: AgentEnvironment): | ||
|
|
||
| app = env.agent_application | ||
|
|
||
| @app.message("/stream") | ||
| async def stream_handler(context: TurnContext, state: TurnState): | ||
|
|
||
| assert context.streaming_response is not None | ||
|
|
||
| context.streaming_response.queue_informative_update("Starting stream...") | ||
| await asyncio.sleep(1.0) # Simulate delay before starting stream | ||
|
|
||
| for chunk in CHUNKS[:-1]: | ||
| context.streaming_response.queue_text_chunk(chunk) | ||
| await asyncio.sleep(1.0) # Simulate delay between chunks | ||
|
|
||
rodrigobr-msft marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| context.streaming_response.queue_text_chunk(CHUNKS[-1]) | ||
| await context.streaming_response.end_stream() | ||
rodrigobr-msft marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| _SCENARIO = AiohttpScenario(init_agent=init_agent, use_jwt_middleware=False) | ||
|
|
||
| @pytest.mark.asyncio | ||
| @pytest.mark.agent_test(_SCENARIO) | ||
| async def test_basic_streaming_response_non_streaming_channel(agent_client: AgentClient): | ||
|
|
||
| expected_len = len(FULL_TEXT.split()) | ||
|
|
||
| agent_client.template = agent_client.template.with_updates(channel_id=Channels.emulator) | ||
|
|
||
| # give enough time for all the activities to send | ||
| await agent_client.send("/stream", wait=expected_len * 2.0) | ||
|
|
||
| stream_activities = agent_client.select().where( | ||
| entities=lambda x: any(e["type"] == "streaminfo" for e in x) | ||
| ).get() | ||
rodrigobr-msft marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| assert len(stream_activities) == 1 | ||
|
|
||
| final_streaminfo = get_streaminfo(stream_activities[0]) | ||
|
|
||
| assert final_streaminfo.stream_sequence == 1 | ||
| assert final_streaminfo.stream_type == "final" | ||
| assert stream_activities[0].text == FULL_TEXT.replace(" ", "") | ||
|
|
||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| @pytest.mark.agent_test(_SCENARIO) | ||
| async def test_basic_streaming_response_streaming_channel(agent_client: AgentClient): | ||
|
|
||
| expected_len = len(FULL_TEXT.split()) | ||
|
|
||
| agent_client.template = agent_client.template.with_updates(channel_id=Channels.webchat) | ||
|
|
||
| # give enough time for all the activities to send | ||
| await agent_client.send("/stream", wait=expected_len * 2.0) | ||
|
|
||
| stream_activities = agent_client.select().where( | ||
| entities=lambda x: any(e["type"] == "streaminfo" for e in x) | ||
| ).get() | ||
rodrigobr-msft marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| assert len(stream_activities) == len(CHUNKS) + 1 | ||
|
|
||
| informative = stream_activities[0] | ||
| informative_streaminfo = get_streaminfo(informative) | ||
|
|
||
| assert informative_streaminfo.stream_type == "informative" | ||
| assert informative_streaminfo.stream_sequence == 1 | ||
| assert informative.text == "Starting stream..." | ||
| assert informative.type == ActivityTypes.typing | ||
|
|
||
| t = "" | ||
| for i, chunk in enumerate(CHUNKS[:-1]): | ||
| t += chunk | ||
|
|
||
| j = i + 1 | ||
|
|
||
| streaminfo = get_streaminfo(stream_activities[j]) | ||
|
|
||
| assert stream_activities[j].text == t | ||
| assert stream_activities[j].type == ActivityTypes.typing | ||
| assert streaminfo.stream_type == "streaming" | ||
| assert streaminfo.stream_sequence == j + 1 | ||
|
|
||
| final_streaminfo = get_streaminfo(stream_activities[-1]) | ||
|
|
||
| assert final_streaminfo.stream_sequence == len(stream_activities) | ||
| assert final_streaminfo.stream_type == "final" | ||
| assert stream_activities[-1].text == FULL_TEXT.replace(" ", "") | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
libraries/microsoft-agents-activity/microsoft_agents/activity/entity/_schema_mixin.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # Copyright (c) Microsoft Corporation. All rights reserved. | ||
| # Licensed under the MIT License. | ||
|
|
||
| from typing import Any | ||
| from pydantic import model_serializer, SerializerFunctionWrapHandler | ||
|
|
||
|
|
||
| class _SchemaMixin: | ||
|
|
||
| at_type: Any | ||
|
|
||
| @model_serializer(mode="wrap") | ||
| def serialize_model( | ||
| self, handler: SerializerFunctionWrapHandler | ||
| ) -> dict[str, object]: | ||
| serialized = handler(self) | ||
| serialized["@type"] = self.at_type | ||
| if hasattr(self, "at_context"): | ||
| serialized["@context"] = getattr(self, "at_context") | ||
rodrigobr-msft marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return serialized | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.