-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Add async test for file search tool and add test recordings #44316
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
9 commits
Select commit
Hold shift + click to select a range
0fd4ac8
First
dargilco ccf7a4a
Add async tests
dargilco 863b807
update test asserts
dargilco e4047e1
Merge remote-tracking branch 'origin/main' into dargilco/more-test-re…
dargilco ad38be5
update test asserts after merge from Main
dargilco ed7d032
Disable some sample tests
dargilco 1788101
Merge branch 'main' into dargilco/more-test-recordings-for-agent-tools
dargilco 4bf9c63
Add comment on how to run a test
dargilco 1e8a607
update test assets
dargilco 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
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
222 changes: 222 additions & 0 deletions
222
sdk/ai/azure-ai-projects/tests/agents/tools/test_agent_file_search_async.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,222 @@ | ||
| # pylint: disable=too-many-lines,line-too-long,useless-suppression | ||
| # ------------------------------------ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # Licensed under the MIT License. | ||
| # ------------------------------------ | ||
| # cSpell:disable | ||
|
|
||
| import os | ||
| from io import BytesIO | ||
| from test_base import TestBase, servicePreparer | ||
| from devtools_testutils.aio import recorded_by_proxy_async | ||
| from devtools_testutils import RecordedTransport | ||
| from azure.ai.projects.models import PromptAgentDefinition, FileSearchTool | ||
|
|
||
|
|
||
| class TestAgentFileSearchAsync(TestBase): | ||
|
|
||
| @servicePreparer() | ||
| @recorded_by_proxy_async(RecordedTransport.AZURE_CORE, RecordedTransport.HTTPX) | ||
| async def test_agent_file_search_async(self, **kwargs): | ||
|
|
||
| model = self.test_agents_params["model_deployment_name"] | ||
|
|
||
| async with ( | ||
| self.create_async_client(operation_group="agents", **kwargs) as project_client, | ||
| project_client.get_openai_client() as openai_client, | ||
| ): | ||
| # Get the path to the test file | ||
| asset_file_path = os.path.abspath( | ||
| os.path.join(os.path.dirname(__file__), "../../../samples/agents/assets/product_info.md") | ||
| ) | ||
|
|
||
| assert os.path.exists(asset_file_path), f"Test file not found at: {asset_file_path}" | ||
| print(f"Using test file: {asset_file_path}") | ||
|
|
||
| # Create vector store for file search | ||
| vector_store = await openai_client.vector_stores.create(name="ProductInfoStore") | ||
| print(f"Vector store created (id: {vector_store.id})") | ||
| assert vector_store.id | ||
|
|
||
| # Upload file to vector store | ||
| with open(asset_file_path, "rb") as f: | ||
| file = await openai_client.vector_stores.files.upload_and_poll( | ||
| vector_store_id=vector_store.id, | ||
| file=f, | ||
| ) | ||
|
|
||
| print(f"File uploaded (id: {file.id}, status: {file.status})") | ||
| assert file.id | ||
| assert file.status == "completed", f"Expected file status 'completed', got '{file.status}'" | ||
|
|
||
| # Create agent with file search tool | ||
| agent_name = "file-search-agent" | ||
| agent = await project_client.agents.create_version( | ||
| agent_name=agent_name, | ||
| definition=PromptAgentDefinition( | ||
| model=model, | ||
| instructions="You are a helpful assistant that can search through uploaded documents to answer questions.", | ||
| tools=[FileSearchTool(vector_store_ids=[vector_store.id])], | ||
| ), | ||
| description="Agent for testing file search capabilities.", | ||
| ) | ||
| self._validate_agent_version(agent, expected_name=agent_name) | ||
|
|
||
| # Ask a question about the uploaded document | ||
| print("\nAsking agent about the product information...") | ||
|
|
||
| response = await openai_client.responses.create( | ||
| input="What products are mentioned in the document?", | ||
| extra_body={"agent": {"name": agent.name, "type": "agent_reference"}}, | ||
| ) | ||
|
|
||
| print(f"Response completed (id: {response.id})") | ||
| assert response.id | ||
| assert response.output is not None | ||
| assert len(response.output) > 0 | ||
|
|
||
| # Get the response text | ||
| response_text = response.output_text | ||
| print(f"\nAgent's response: {response_text[:300]}...") | ||
|
|
||
| # Verify we got a meaningful response | ||
| assert len(response_text) > 50, "Expected a substantial response from the agent" | ||
|
|
||
| # The response should mention finding information (indicating file search was used) | ||
| # We can't assert exact product names without knowing the file content, | ||
| # but we can verify the agent provided an answer | ||
| print("\n✓ Agent successfully used file search tool to answer question from uploaded document") | ||
|
|
||
| # Teardown | ||
| print("\nCleaning up...") | ||
| await project_client.agents.delete_version(agent_name=agent.name, agent_version=agent.version) | ||
| print("Agent deleted") | ||
|
|
||
| await openai_client.vector_stores.delete(vector_store.id) | ||
| print("Vector store deleted") | ||
|
|
||
| @servicePreparer() | ||
| @recorded_by_proxy_async(RecordedTransport.AZURE_CORE, RecordedTransport.HTTPX) | ||
| async def test_agent_file_search_multi_turn_conversation_async(self, **kwargs): | ||
| """ | ||
| Test multi-turn conversation with File Search (async version). | ||
|
|
||
| This test verifies that an agent can maintain context across multiple turns | ||
| while using File Search to answer follow-up questions. | ||
| """ | ||
|
|
||
| model = self.test_agents_params["model_deployment_name"] | ||
|
|
||
| async with ( | ||
| self.create_async_client(operation_group="agents", **kwargs) as project_client, | ||
| project_client.get_openai_client() as openai_client, | ||
| ): | ||
| # Create a document with information about products | ||
| product_info = """Product Catalog: | ||
|
|
||
| Widget A: | ||
| - Price: $150 | ||
| - Category: Electronics | ||
| - Stock: 50 units | ||
| - Rating: 4.5/5 stars | ||
|
|
||
| Widget B: | ||
| - Price: $220 | ||
| - Category: Electronics | ||
| - Stock: 30 units | ||
| - Rating: 4.8/5 stars | ||
|
|
||
| Widget C: | ||
| - Price: $95 | ||
| - Category: Home & Garden | ||
| - Stock: 100 units | ||
| - Rating: 4.2/5 stars | ||
| """ | ||
|
|
||
| # Create vector store and upload document | ||
| vector_store = await openai_client.vector_stores.create(name="ProductCatalog") | ||
| print(f"Vector store created: {vector_store.id}") | ||
|
|
||
| product_file = BytesIO(product_info.encode("utf-8")) | ||
| product_file.name = "products.txt" | ||
|
|
||
| file = await openai_client.vector_stores.files.upload_and_poll( | ||
| vector_store_id=vector_store.id, | ||
| file=product_file, | ||
| ) | ||
| print(f"Product catalog uploaded: {file.id}") | ||
|
|
||
| # Create agent with File Search | ||
| agent = await project_client.agents.create_version( | ||
| agent_name="product-catalog-agent", | ||
| definition=PromptAgentDefinition( | ||
| model=model, | ||
| instructions="You are a product information assistant. Use file search to answer questions about products.", | ||
| tools=[FileSearchTool(vector_store_ids=[vector_store.id])], | ||
| ), | ||
| description="Agent for multi-turn product queries.", | ||
| ) | ||
| print(f"Agent created: {agent.id}") | ||
|
|
||
| # Turn 1: Ask about price | ||
| print("\n--- Turn 1: Initial query ---") | ||
| response_1 = await openai_client.responses.create( | ||
| input="What is the price of Widget B?", | ||
| extra_body={"agent": {"name": agent.name, "type": "agent_reference"}}, | ||
| ) | ||
|
|
||
| response_1_text = response_1.output_text | ||
| print(f"Response 1: {response_1_text[:200]}...") | ||
| assert "$220" in response_1_text or "220" in response_1_text, "Response should mention Widget B's price" | ||
|
|
||
| # Turn 2: Follow-up question (requires context from turn 1) | ||
| print("\n--- Turn 2: Follow-up query (testing context retention) ---") | ||
| response_2 = await openai_client.responses.create( | ||
| input="What about its stock level?", | ||
| previous_response_id=response_1.id, | ||
| extra_body={"agent": {"name": agent.name, "type": "agent_reference"}}, | ||
| ) | ||
|
|
||
| response_2_text = response_2.output_text | ||
| print(f"Response 2: {response_2_text[:200]}...") | ||
| assert ( | ||
| "30" in response_2_text or "thirty" in response_2_text.lower() | ||
| ), "Response should mention Widget B's stock (30 units)" | ||
|
|
||
| # Turn 3: Another follow-up (compare with different product) | ||
| print("\n--- Turn 3: Comparison query ---") | ||
| response_3 = await openai_client.responses.create( | ||
| input="How does that compare to Widget A's stock?", | ||
| previous_response_id=response_2.id, | ||
| extra_body={"agent": {"name": agent.name, "type": "agent_reference"}}, | ||
| ) | ||
|
|
||
| response_3_text = response_3.output_text | ||
| print(f"Response 3: {response_3_text[:200]}...") | ||
| assert ( | ||
| "50" in response_3_text or "fifty" in response_3_text.lower() | ||
| ), "Response should mention Widget A's stock (50 units)" | ||
|
|
||
| # Turn 4: New topic (testing topic switching) | ||
| print("\n--- Turn 4: Topic switch ---") | ||
| response_4 = await openai_client.responses.create( | ||
| input="Which widget has the highest rating?", | ||
| previous_response_id=response_3.id, | ||
| extra_body={"agent": {"name": agent.name, "type": "agent_reference"}}, | ||
| ) | ||
|
|
||
| response_4_text = response_4.output_text | ||
| print(f"Response 4: {response_4_text[:200]}...") | ||
| assert ( | ||
| "widget b" in response_4_text.lower() or "4.8" in response_4_text | ||
| ), "Response should identify Widget B as highest rated (4.8/5)" | ||
|
|
||
| print("\n✓ Multi-turn conversation successful!") | ||
| print(" - Context maintained across turns") | ||
| print(" - Follow-up questions handled correctly") | ||
| print(" - Topic switching works") | ||
|
|
||
| # Cleanup | ||
| await project_client.agents.delete_version(agent_name=agent.name, agent_version=agent.version) | ||
| await openai_client.vector_stores.delete(vector_store.id) | ||
| print("Cleanup completed") | ||
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.
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.