|
7 | 7 | from typing import Any, Optional |
8 | 8 |
|
9 | 9 | import os |
10 | | -import datetime |
11 | 10 | import json |
12 | | -import logging |
13 | 11 | import tempfile |
14 | | -import sys |
15 | 12 | import time |
16 | 13 | import pytest |
17 | | -import functools |
18 | 14 | import io |
19 | 15 | import user_functions |
20 | 16 |
|
21 | 17 | from azure.ai.agents import AgentsClient |
22 | 18 | from azure.core.exceptions import HttpResponseError |
23 | 19 | from devtools_testutils import ( |
24 | | - AzureRecordedTestCase, |
25 | | - EnvironmentVariableLoader, |
26 | 20 | recorded_by_proxy, |
27 | 21 | ) |
28 | 22 | from azure.ai.agents.models import ( |
|
32 | 26 | AzureAISearchTool, |
33 | 27 | AzureFunctionStorageQueue, |
34 | 28 | AzureFunctionTool, |
| 29 | + BrowserAutomationTool, |
35 | 30 | CodeInterpreterTool, |
36 | 31 | CodeInterpreterToolResource, |
37 | 32 | ConnectedAgentTool, |
|
49 | 44 | ResponseFormatJsonSchema, |
50 | 45 | ResponseFormatJsonSchemaType, |
51 | 46 | RunAdditionalFieldList, |
| 47 | + RunStepBrowserAutomationToolCall, |
52 | 48 | RunStepConnectedAgentToolCall, |
53 | 49 | RunStepDeepResearchToolCall, |
54 | 50 | RunStepDeltaChunk, |
|
70 | 66 | VectorStoreDataSource, |
71 | 67 | VectorStoreDataSourceAssetType, |
72 | 68 | ) |
73 | | -from devtools_testutils.azure_testcase import is_live |
74 | 69 | from azure.ai.agents.models._models import RunStepDeltaConnectedAgentToolCall |
75 | | - |
76 | | -# Set to True to enable SDK logging |
77 | | -LOGGING_ENABLED = True |
78 | | - |
79 | | -if LOGGING_ENABLED: |
80 | | - # Create a logger for the 'azure' SDK |
81 | | - # See https://docs.python.org/3/library/logging.html |
82 | | - logger = logging.getLogger("azure") |
83 | | - logger.setLevel(logging.DEBUG) # INFO or DEBUG |
84 | | - |
85 | | - # Configure a console output |
86 | | - handler = logging.StreamHandler(stream=sys.stdout) |
87 | | - logger.addHandler(handler) |
88 | | - |
89 | | -agentClientPreparer = functools.partial( |
90 | | - EnvironmentVariableLoader, |
91 | | - "azure_ai_agents", |
92 | | - # TODO: uncomment this endpoint when re running with 1DP |
93 | | - # azure_ai_agents_tests_project_endpoint="https://aiservices-id.services.ai.azure.com/api/projects/project-name", |
94 | | - # TODO: remove this endpoint when re running with 1DP |
95 | | - azure_ai_agents_tests_project_connection_string="https://Sanitized.api.azureml.ms/agents/v1.0/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/00000/providers/Microsoft.MachineLearningServices/workspaces/00000/", |
96 | | - azure_ai_agents_tests_project_endpoint="https://Sanitized.services.ai.azure.com/api/projects/00000", |
97 | | - azure_ai_agents_tests_data_path="azureml://subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/rg-resour-cegr-oupfoo1/workspaces/abcd-abcdabcdabcda-abcdefghijklm/datastores/workspaceblobstore/paths/LocalUpload/000000000000/product_info_1.md", |
98 | | - azure_ai_agents_tests_storage_queue="https://foobar.queue.core.windows.net", |
99 | | - azure_ai_agents_tests_search_index_name="sample_index", |
100 | | - azure_ai_agents_tests_search_connection_id="/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/00000/providers/Microsoft.MachineLearningServices/workspaces/00000/connections/someindex", |
101 | | - azure_ai_agents_tests_bing_connection_id="/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/00000/providers/Microsoft.CognitiveServices/accounts/00000/projects/00000/connections/00000", |
102 | | - azure_ai_agents_tests_deep_research_model="gpt-4o-deep-research", |
103 | | - azure_ai_agents_tests_is_test_run="True", |
104 | | -) |
105 | | - |
106 | | - |
107 | | -# create tool for agent use |
108 | | -def fetch_current_datetime_live(): |
109 | | - """ |
110 | | - Get the current time as a JSON string. |
111 | | -
|
112 | | - :return: Static time string so that test recordings work. |
113 | | - :rtype: str |
114 | | - """ |
115 | | - current_datetime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") |
116 | | - time_json = json.dumps({"current_time": current_datetime}) |
117 | | - return time_json |
118 | | - |
119 | | - |
120 | | -# create tool for agent use |
121 | | -def fetch_current_datetime_recordings(): |
122 | | - """ |
123 | | - Get the current time as a JSON string. |
124 | | -
|
125 | | - :return: Static time string so that test recordings work. |
126 | | - :rtype: str |
127 | | - """ |
128 | | - time_json = json.dumps({"current_time": "2024-10-10 12:30:19"}) |
129 | | - return time_json |
130 | | - |
| 70 | +from test_agents_client_base import TestAgentClientBase, agentClientPreparer, fetch_current_datetime_recordings, fetch_current_datetime_live |
131 | 71 |
|
132 | 72 | # Statically defined user functions for fast reference |
133 | 73 | user_functions_recording = {fetch_current_datetime_recordings} |
134 | 74 | user_functions_live = {fetch_current_datetime_live} |
135 | 75 |
|
136 | 76 |
|
137 | 77 | # The test class name needs to start with "Test" to get collected by pytest |
138 | | -class TestAgentClient(AzureRecordedTestCase): |
| 78 | +class TestAgentClient(TestAgentClientBase): |
139 | 79 |
|
140 | 80 | # helper function: create client using environment variables |
141 | 81 | def create_client(self, by_endpoint=False, **kwargs) -> AgentsClient: |
@@ -3152,6 +3092,38 @@ def test_azure_function_call(self, **kwargs): |
3152 | 3092 | specific_message_text="bar", |
3153 | 3093 | ) |
3154 | 3094 |
|
| 3095 | + @agentClientPreparer() |
| 3096 | + @recorded_by_proxy |
| 3097 | + def test_browser_automation_tool(self, **kwargs): |
| 3098 | + connection_id = kwargs["azure_ai_agents_tests_playwright_connection_id"] |
| 3099 | + with self.create_client(by_endpoint=True, **kwargs) as client: |
| 3100 | + browser_automation_tool = BrowserAutomationTool(connection_id=connection_id) |
| 3101 | + self._do_test_tool( |
| 3102 | + client=client, |
| 3103 | + model_name="gpt-4o", |
| 3104 | + tool_to_test=browser_automation_tool, |
| 3105 | + instructions=""" |
| 3106 | + You are an Agent helping with browser automation tasks. |
| 3107 | + You can answer questions, provide information, and assist with various tasks |
| 3108 | + related to web browsing using the Browser Automation tool available to you. |
| 3109 | + """, |
| 3110 | + prompt=""" |
| 3111 | + Your goal is to report the percent of Microsoft year-to-date stock price change. |
| 3112 | + To do that, go to the website finance.yahoo.com. |
| 3113 | + At the top of the page, you will find a search bar. |
| 3114 | + Enter the value 'MSFT', to get information about the Microsoft stock price. |
| 3115 | + At the top of the resulting page you will see a default chart of Microsoft stock price. |
| 3116 | + Click on 'YTD' at the top of that chart, and read the value that shows up right underneath it. |
| 3117 | + Report your result using exactly the following sentence, followed by the value you found: |
| 3118 | + `The year-to-date (YTD) stock price change for Microsoft (MSFT) is` |
| 3119 | + """, |
| 3120 | + # The tool is very slow to run, since the Microsoft Playwright Workspace service needs to |
| 3121 | + # load a VM and open a browser. Use a large polling interval to avoid tons of REST API calls in test recordings. |
| 3122 | + polling_interval=60, |
| 3123 | + expected_class=RunStepBrowserAutomationToolCall, |
| 3124 | + specific_message_text="the year-to-date (ytd) stock price change for microsoft (msft) is", |
| 3125 | + ) |
| 3126 | + |
3155 | 3127 | @agentClientPreparer() |
3156 | 3128 | @recorded_by_proxy |
3157 | 3129 | def test_deep_research_tool(self, **kwargs): |
@@ -3300,10 +3272,12 @@ def _do_test_tool( |
3300 | 3272 | :param tool_to_test: The pre created tool to be used. |
3301 | 3273 | :param instructions: The instructions, given to an agent. |
3302 | 3274 | :param prompt: The prompt, given in the first user message. |
| 3275 | + :param expected_class: If a tool call is expected, the name of the class derived from RunStepToolCall |
| 3276 | + corresponding to the expected tool call (e.g. RunStepBrowserAutomationToolCall). |
3303 | 3277 | :param headers: The headers used to call the agents. |
3304 | 3278 | For example: {"x-ms-enable-preview": "true"} |
3305 | 3279 | :param polling_interval: The polling interval (useful, when we need to wait longer times). |
3306 | | - :param specific_message_text: The specific text to search in the messages. |
| 3280 | + :param specific_message_text: The specific text to search in the messages. Must be all lower-case. |
3307 | 3281 | """ |
3308 | 3282 | if headers is None: |
3309 | 3283 | headers = {} |
@@ -3361,6 +3335,8 @@ def _do_test_tool( |
3361 | 3335 | found_step = True |
3362 | 3336 | if "connected_agent_name" in kwargs: |
3363 | 3337 | assert tool_call.connected_agent.name == kwargs["connected_agent_name"] |
| 3338 | + if isinstance(tool_call, RunStepBrowserAutomationToolCall): |
| 3339 | + self._validate_run_step_browser_automation_tool_call(tool_call) |
3364 | 3340 | assert found_step, f"The {expected_class} was not found." |
3365 | 3341 | finally: |
3366 | 3342 | client.delete_agent(agent.id) |
@@ -3443,6 +3419,4 @@ def _do_test_tool_streaming( |
3443 | 3419 | client.delete_agent(agent.id) |
3444 | 3420 | client.threads.delete(thread.id) |
3445 | 3421 |
|
3446 | | - def _sleep_time(self, sleep: int = 1) -> int: |
3447 | | - """Return sleep or zero if we are running the recording.""" |
3448 | | - return sleep if is_live() else 0 |
| 3422 | + |
0 commit comments