-
Notifications
You must be signed in to change notification settings - Fork 1k
Python: Streaming sample for azurefunctions #3057
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
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
1f8f016
Streaming sample for azurefunctions
gavin-aguiar 5f67638
Fixed links and sample name
gavin-aguiar 2844a38
Addressed feedback
gavin-aguiar 91b229d
Addressed feedback
gavin-aguiar b0abe59
Fixed integration tests
gavin-aguiar 3c53038
Merge branch 'main' into gaaguiar/streaming_sample
gavin-aguiar 847c54e
Merge branch 'main' into gaaguiar/streaming_sample
gavin-aguiar f1e77ef
Merge branch 'main' into gaaguiar/streaming_sample
gavin-aguiar 0c2b5ab
Merge branch 'main' into gaaguiar/streaming_sample
gavin-aguiar c6ee47e
Updated test
gavin-aguiar 19bc73a
Merge branch 'gaaguiar/streaming_sample' of https://github.com/micros…
gavin-aguiar d6827e1
Merge branch 'main' into gaaguiar/streaming_sample
gavin-aguiar 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
102 changes: 0 additions & 102 deletions
102
python/packages/azurefunctions/tests/integration_tests/test_03_callbacks.py
This file was deleted.
Oops, something went wrong.
125 changes: 125 additions & 0 deletions
125
python/packages/azurefunctions/tests/integration_tests/test_03_reliable_streaming.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,125 @@ | ||
| # Copyright (c) Microsoft. All rights reserved. | ||
| """ | ||
| Integration Tests for Reliable Streaming Sample | ||
|
|
||
| Tests the reliable streaming sample using Redis Streams for persistent message delivery. | ||
|
|
||
| The function app is automatically started by the test fixture. | ||
|
|
||
| Prerequisites: | ||
| - Azure OpenAI credentials configured (see packages/azurefunctions/tests/integration_tests/.env.example) | ||
| - Azurite or Azure Storage account configured | ||
| - Redis running (docker run -d --name redis -p 6379:6379 redis:latest) | ||
|
|
||
| Usage: | ||
| uv run pytest packages/azurefunctions/tests/integration_tests/test_03_reliable_streaming.py -v | ||
| """ | ||
|
|
||
| import time | ||
|
|
||
| import pytest | ||
| import requests | ||
|
|
||
| from .testutils import ( | ||
| SampleTestHelper, | ||
| skip_if_azure_functions_integration_tests_disabled, | ||
| ) | ||
|
|
||
| # Module-level markers - applied to all tests in this file | ||
| pytestmark = [ | ||
| pytest.mark.sample("03_reliable_streaming"), | ||
| pytest.mark.usefixtures("function_app_for_test"), | ||
| skip_if_azure_functions_integration_tests_disabled, | ||
| ] | ||
|
|
||
|
|
||
| class TestSampleReliableStreaming: | ||
| """Tests for 03_reliable_streaming sample.""" | ||
|
|
||
| @pytest.fixture(autouse=True) | ||
| def _set_base_url(self, base_url: str) -> None: | ||
| """Provide the base URL for each test.""" | ||
| self.base_url = base_url | ||
| self.agent_url = f"{base_url}/api/agents/TravelPlanner" | ||
| self.stream_url = f"{base_url}/api/agent/stream" | ||
|
|
||
| def test_agent_run_and_stream(self) -> None: | ||
| """Test agent execution with Redis streaming.""" | ||
| # Start agent run | ||
| response = SampleTestHelper.post_json( | ||
| f"{self.agent_url}/run", | ||
| {"message": "Plan a 1-day trip to Seattle in 1 sentence", "wait_for_response": False}, | ||
| ) | ||
| assert response.status_code == 202 | ||
| data = response.json() | ||
|
|
||
| thread_id = data.get("thread_id") | ||
|
|
||
| # Wait a moment for the agent to start writing to Redis | ||
| time.sleep(2) | ||
|
|
||
| # Stream response from Redis with shorter timeout | ||
| # Note: We use text/plain to avoid SSE parsing complexity | ||
| stream_response = requests.get( | ||
| f"{self.stream_url}/{thread_id}", | ||
| headers={"Accept": "text/plain"}, | ||
| timeout=30, # Shorter timeout for test | ||
| ) | ||
| assert stream_response.status_code == 200 | ||
|
|
||
| def test_stream_with_sse_format(self) -> None: | ||
| """Test streaming with Server-Sent Events format.""" | ||
| # Start agent run | ||
| response = SampleTestHelper.post_json( | ||
| f"{self.agent_url}/run", | ||
| {"message": "What's the weather like?", "wait_for_response": False}, | ||
| ) | ||
| assert response.status_code == 202 | ||
| data = response.json() | ||
| thread_id = data.get("thread_id") | ||
|
|
||
| # Wait for agent to start writing | ||
| time.sleep(2) | ||
|
|
||
| # Stream with SSE format | ||
| stream_response = requests.get( | ||
| f"{self.stream_url}/{thread_id}", | ||
| headers={"Accept": "text/event-stream"}, | ||
| timeout=30, # Shorter timeout | ||
| ) | ||
| assert stream_response.status_code == 200 | ||
| content_type = stream_response.headers.get("content-type", "") | ||
| assert "text/event-stream" in content_type | ||
|
|
||
| # Check for SSE event markers if we got content | ||
| content = stream_response.text | ||
| if content: | ||
| assert "event:" in content or "data:" in content | ||
|
|
||
| def test_stream_nonexistent_conversation(self) -> None: | ||
| """Test streaming from a non-existent conversation. | ||
|
|
||
| The endpoint will wait for data in Redis, but since the conversation | ||
| doesn't exist, it will timeout. This is expected behavior. | ||
| """ | ||
| fake_id = "nonexistent-conversation-12345" | ||
|
|
||
| # Should timeout since the conversation doesn't exist | ||
| with pytest.raises(requests.exceptions.ReadTimeout): | ||
| requests.get( | ||
| f"{self.stream_url}/{fake_id}", | ||
| headers={"Accept": "text/plain"}, | ||
| timeout=10, # Short timeout for non-existent ID | ||
| ) | ||
|
|
||
| def test_health_endpoint(self) -> None: | ||
| """Test health check endpoint.""" | ||
| response = SampleTestHelper.get(f"{self.base_url}/api/health") | ||
| assert response.status_code == 200 | ||
| data = response.json() | ||
| assert data["status"] == "healthy" | ||
| assert "agents" in data | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| pytest.main([__file__, "-v"]) | ||
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
83 changes: 0 additions & 83 deletions
83
python/samples/getting_started/azure_functions/03_callbacks/README.md
This file was deleted.
Oops, something went wrong.
30 changes: 0 additions & 30 deletions
30
python/samples/getting_started/azure_functions/03_callbacks/demo.http
This file was deleted.
Oops, something went wrong.
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.