|
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