Skip to content

Commit 1f0e019

Browse files
authored
Add tests for Agent Browser Automation tool (#42439)
1 parent cb51696 commit 1f0e019

File tree

7 files changed

+180
-140
lines changed

7 files changed

+180
-140
lines changed

sdk/ai/azure-ai-agents/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "python",
44
"TagPrefix": "python/ai/azure-ai-agents",
5-
"Tag": "python/ai/azure-ai-agents_0a512f2bb3"
5+
"Tag": "python/ai/azure-ai-agents_e418bdd70b"
66
}

sdk/ai/azure-ai-agents/azure_ai_agents_tests.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ AZURE_AI_AGENTS_TESTS_SEARCH_INDEX_NAME=
1616
AZURE_AI_AGENTS_TESTS_SEARCH_CONNECTION_ID=
1717
AZURE_AI_AGENTS_TESTS_IS_TEST_RUN=True
1818
AZURE_AI_AGENTS_TESTS_BING_CONNECTION_ID=
19+
AZURE_AI_AGENTS_TESTS_PLAYWRIGHT_CONNECTION_ID=
1920
AZURE_AI_AGENTS_TESTS_DEEP_RESEARCH_MODEL=

sdk/ai/azure-ai-agents/cspell.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111
"VELRAIL",
1212
"AEROJET",
1313
"OCEACARGO",
14-
"GENAI"
14+
"GENAI",
15+
"cegr",
16+
"oupfoo",
17+
"abcdabcdabcda",
18+
"abcdefghijklm",
1519
],
1620
"ignorePaths": [
1721
"*.csv",

sdk/ai/azure-ai-agents/tests/conftest.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,13 @@ def azure_workspace_triad_sanitizer():
153153
value="/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/00000/providers/Microsoft.CognitiveServices/accounts/00000/projects/00000/connections/00000",
154154
)
155155

156+
# Sanitize Browser Automation tool's Playwright Workspace connection ID
157+
add_body_key_sanitizer(
158+
json_path="tools[*].browser_automation.connection.id",
159+
value="/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/00000/providers/Microsoft.CognitiveServices/accounts/00000/projects/00000/connections/00000",
160+
)
161+
162+
156163
# Sanitize API key from service response (/tests/connections)
157164
add_body_key_sanitizer(json_path="properties.credentials.key", value="Sanitized")
158165

sdk/ai/azure-ai-agents/tests/test_agents_client.py

Lines changed: 42 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,16 @@
77
from typing import Any, Optional
88

99
import os
10-
import datetime
1110
import json
12-
import logging
1311
import tempfile
14-
import sys
1512
import time
1613
import pytest
17-
import functools
1814
import io
1915
import user_functions
2016

2117
from azure.ai.agents import AgentsClient
2218
from azure.core.exceptions import HttpResponseError
2319
from devtools_testutils import (
24-
AzureRecordedTestCase,
25-
EnvironmentVariableLoader,
2620
recorded_by_proxy,
2721
)
2822
from azure.ai.agents.models import (
@@ -32,6 +26,7 @@
3226
AzureAISearchTool,
3327
AzureFunctionStorageQueue,
3428
AzureFunctionTool,
29+
BrowserAutomationTool,
3530
CodeInterpreterTool,
3631
CodeInterpreterToolResource,
3732
ConnectedAgentTool,
@@ -49,6 +44,7 @@
4944
ResponseFormatJsonSchema,
5045
ResponseFormatJsonSchemaType,
5146
RunAdditionalFieldList,
47+
RunStepBrowserAutomationToolCall,
5248
RunStepConnectedAgentToolCall,
5349
RunStepDeepResearchToolCall,
5450
RunStepDeltaChunk,
@@ -70,72 +66,16 @@
7066
VectorStoreDataSource,
7167
VectorStoreDataSourceAssetType,
7268
)
73-
from devtools_testutils.azure_testcase import is_live
7469
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
13171

13272
# Statically defined user functions for fast reference
13373
user_functions_recording = {fetch_current_datetime_recordings}
13474
user_functions_live = {fetch_current_datetime_live}
13575

13676

13777
# The test class name needs to start with "Test" to get collected by pytest
138-
class TestAgentClient(AzureRecordedTestCase):
78+
class TestAgentClient(TestAgentClientBase):
13979

14080
# helper function: create client using environment variables
14181
def create_client(self, by_endpoint=False, **kwargs) -> AgentsClient:
@@ -3152,6 +3092,38 @@ def test_azure_function_call(self, **kwargs):
31523092
specific_message_text="bar",
31533093
)
31543094

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+
31553127
@agentClientPreparer()
31563128
@recorded_by_proxy
31573129
def test_deep_research_tool(self, **kwargs):
@@ -3300,10 +3272,12 @@ def _do_test_tool(
33003272
:param tool_to_test: The pre created tool to be used.
33013273
:param instructions: The instructions, given to an agent.
33023274
: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).
33033277
:param headers: The headers used to call the agents.
33043278
For example: {"x-ms-enable-preview": "true"}
33053279
: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.
33073281
"""
33083282
if headers is None:
33093283
headers = {}
@@ -3361,6 +3335,8 @@ def _do_test_tool(
33613335
found_step = True
33623336
if "connected_agent_name" in kwargs:
33633337
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)
33643340
assert found_step, f"The {expected_class} was not found."
33653341
finally:
33663342
client.delete_agent(agent.id)
@@ -3443,6 +3419,4 @@ def _do_test_tool_streaming(
34433419
client.delete_agent(agent.id)
34443420
client.threads.delete(thread.id)
34453421

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

Comments
 (0)