Skip to content

Commit 6df5c84

Browse files
authored
Add test for DeepResearch (#42750)
1 parent 7018e6b commit 6df5c84

File tree

6 files changed

+132
-8
lines changed

6 files changed

+132
-8
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_28d79451bb"
5+
"Tag": "python/ai/azure-ai-agents_65510bf995"
66
}

sdk/ai/azure-ai-agents/azure/ai/agents/telemetry/_ai_agents_instrumentor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ def _process_tool_calls(self, step: RunStep) -> List[Dict[str, Any]]:
459459
"server_label": t.server_label or ""
460460
}
461461
else:
462+
# Works for Deep research
462463
tool_details = t.as_dict()[t.type]
463464

464465
tool_call = {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def check_event_attributes(self, expected_dict, actual_dict):
127127
actual_val = json.dumps(actual_dict)
128128
else:
129129
actual_val = actual_dict
130-
raise AssertionError("check_event_attributes: keys do not match: " + expected_val + "!=" + actual_val)
130+
raise AssertionError(f"check_event_attributes: keys do not match: {set(expected_dict.keys())} != {set(actual_dict.keys())}. The actual dictionaries: {expected_val} != {actual_val}")
131131
for key, expected_val in expected_dict.items():
132132
if key not in actual_dict:
133133
raise AssertionError(f"check_event_attributes: key {key} not found in actuals")

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,7 @@ def _do_test_run_steps_with_toolset_with_tracing_content_recording(
764764
tool: Optional[Tool] = None,
765765
have_submit_tools: bool = False,
766766
run_step_events: List[List[Dict[str, Any]]] = None,
767+
has_annotations: bool = False,
767768
**kwargs
768769
) -> None:
769770
"""The helper method to check the recordings."""
@@ -820,6 +821,7 @@ def _do_test_run_steps_with_toolset_with_tracing_content_recording(
820821
tool_message_attribute_content=tool_message_attribute_content,
821822
event_contents=event_contents,
822823
run_step_events=run_step_events,
824+
has_annotations=has_annotations,
823825
)
824826

825827
@pytest.mark.usefixtures("instrument_with_content")
@@ -959,6 +961,27 @@ def test_telemetry_steps_with_mcp_tool(self, **kwargs):
959961
run_step_events=self.get_expected_mcp_spans(),
960962
)
961963

964+
@pytest.mark.usefixtures("instrument_with_content")
965+
@agentClientPreparer()
966+
@recorded_by_proxy
967+
def test_telemetry_steps_with_deep_research_tool(self, **kwargs):
968+
"""Test running functions with streaming and tracing content recording."""
969+
970+
self._do_test_run_steps_with_toolset_with_tracing_content_recording(
971+
tool=self._get_deep_research_tool(**kwargs),
972+
model="gpt-4o",
973+
use_stream=False,
974+
instructions="You are a helpful agent that assists in researching scientific topics.",
975+
message="Research the benefits of renewable energy sources. Keep the response brief.",
976+
recording_enabled=True,
977+
tool_message_attribute_content='',
978+
event_contents=[],
979+
have_submit_tools=False,
980+
run_step_events=self.get_expected_deep_research_spans(),
981+
has_annotations=True,
982+
**kwargs
983+
)
984+
962985
class MyEventHandler(AgentEventHandler):
963986

964987
def on_message_delta(self, delta: "MessageDeltaChunk") -> None:

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ async def _do_test_run_steps_with_toolset_with_tracing_content_recording(
466466
tool: Optional[Tool] = None,
467467
have_submit_tools=False,
468468
run_step_events: List[List[Dict[str, Any]]] = None,
469+
has_annotations: bool = False,
469470
**kwargs
470471
):
471472
"""The helper method to check the recordings."""
@@ -519,6 +520,7 @@ async def _do_test_run_steps_with_toolset_with_tracing_content_recording(
519520
tool_message_attribute_content=tool_message_attribute_content,
520521
event_contents=event_contents,
521522
run_step_events=run_step_events,
523+
has_annotations=has_annotations,
522524
)
523525

524526
@pytest.mark.usefixtures("instrument_without_content")
@@ -678,6 +680,25 @@ async def test_telemetry_steps_with_mcp_tool(self, **kwargs):
678680
run_step_events=self.get_expected_mcp_spans(),
679681
)
680682

683+
@pytest.mark.usefixtures("instrument_with_content")
684+
@agentClientPreparer()
685+
@recorded_by_proxy_async
686+
async def test_telemetry_steps_with_deep_research_tool(self, **kwargs):
687+
"""Test running functions with streaming and tracing content recording."""
688+
await self._do_test_run_steps_with_toolset_with_tracing_content_recording(
689+
tool=self._get_deep_research_tool(**kwargs),
690+
model="gpt-4o",
691+
use_stream=False,
692+
instructions="You are a helpful agent that assists in researching scientific topics.",
693+
message="Research the benefits of renewable energy sources. Keep the response brief.",
694+
recording_enabled=True,
695+
tool_message_attribute_content='',
696+
event_contents=[],
697+
have_submit_tools=False,
698+
run_step_events=self.get_expected_deep_research_spans(),
699+
has_annotations=True,
700+
**kwargs
701+
)
681702

682703
class MyEventHandler(AsyncAgentEventHandler):
683704

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

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
from azure.ai.agents.telemetry import AIAgentsInstrumentor
1515

16+
from azure.ai.agents.models import DeepResearchTool
17+
1618
from gen_ai_trace_verifier import GenAiTraceVerifier
1719
from memory_trace_exporter import MemoryTraceExporter
1820
from test_agents_client_base import TestAgentClientBase
@@ -49,6 +51,17 @@ def cleanup(self):
4951
trace._TRACER_PROVIDER = None
5052
os.environ.pop(CONTENT_TRACING_ENV_VARIABLE, None)
5153

54+
def _get_deep_research_tool(self, **kwargs):
55+
"""Get deep research tool."""
56+
bing_conn_id = kwargs.pop("azure_ai_agents_tests_bing_connection_id")
57+
deep_research_model = kwargs.pop("azure_ai_agents_tests_deep_research_model")
58+
59+
# Create DeepResearchTool
60+
return DeepResearchTool(
61+
bing_grounding_connection_id=bing_conn_id,
62+
deep_research_model=deep_research_model,
63+
)
64+
5265
def _check_spans(
5366
self,
5467
model: str,
@@ -60,6 +73,7 @@ def _check_spans(
6073
tool_message_attribute_content: str,
6174
event_contents: List[str],
6275
run_step_events: Optional[List[List[Dict[str, Any]]]] = None,
76+
has_annotations: bool = False,
6377
):
6478
"""Check the spans for correctness."""
6579
spans = self.exporter.get_spans_by_name("create_agent my-agent")
@@ -210,7 +224,7 @@ def _check_spans(
210224
assert events_match == True
211225

212226
spans = self.exporter.get_spans_by_name("list_messages")
213-
assert len(spans) == 2
227+
assert len(spans) >= 2
214228
span = spans[0]
215229
expected_attributes = [
216230
("gen_ai.system", "az.ai.agents"),
@@ -221,7 +235,13 @@ def _check_spans(
221235
attributes_match = GenAiTraceVerifier().check_span_attributes(span, expected_attributes)
222236
assert attributes_match == True
223237

224-
content = '{"content": {"text": {"value": "*"}}, "role": "assistant"}' if recording_enabled else '{"role": "assistant"}'
238+
if recording_enabled:
239+
if has_annotations:
240+
content = '{"content": {"text": {"value": "*", "annotations": "*"}}, "role": "assistant"}'
241+
else:
242+
content = '{"content": {"text": {"value": "*"}}, "role": "assistant"}'
243+
else:
244+
content = '{"role": "assistant"}'
225245
expected_events = [
226246
{
227247
"name": "gen_ai.assistant.message",
@@ -239,7 +259,7 @@ def _check_spans(
239259
events_match = GenAiTraceVerifier().check_span_events(span, expected_events)
240260
assert events_match == True
241261

242-
span = spans[1]
262+
span = spans[-1]
243263
attributes_match = GenAiTraceVerifier().check_span_attributes(span, expected_attributes)
244264
assert attributes_match == True
245265

@@ -260,15 +280,21 @@ def _check_spans(
260280

261281
spans = self.exporter.get_spans_by_name("list_run_steps")
262282
if run_step_events:
263-
assert len(spans) == len(run_step_events)
264283
expected_attributes = [
265284
("gen_ai.system", "az.ai.agents"),
266285
("gen_ai.operation.name", "list_run_steps"),
267286
("server.address", ""),
268287
("gen_ai.thread.id", ""),
269288
("gen_ai.thread.run.id", ""),
270289
]
271-
for span, expected_span_events in zip(spans, run_step_events):
290+
if len(spans) < 5:
291+
assert len(spans) == len(run_step_events)
292+
zip_obj = zip(spans, run_step_events)
293+
else:
294+
assert len(run_step_events) == 5
295+
# If it is deep research there may be multiple run steps.
296+
zip_obj = zip(spans[:3] + spans[-2:], run_step_events)
297+
for span, expected_span_events in zip_obj:
272298
attributes_match = GenAiTraceVerifier().check_span_attributes(span, expected_attributes)
273299
assert attributes_match == True
274300
events_match = GenAiTraceVerifier().check_span_events(span, expected_span_events)
@@ -411,4 +437,57 @@ def get_expected_mcp_spans(self):
411437
])
412438
expected_spans.append([])
413439
return expected_spans
414-
440+
441+
def get_expected_deep_research_spans(self):
442+
expected_event_content = json.dumps(
443+
{'tool_calls':
444+
[
445+
{
446+
"id": "*",
447+
"type": "deep_research",
448+
"deep_research": {
449+
"input": "*",
450+
"output": "*"
451+
},
452+
}
453+
]
454+
}
455+
)
456+
457+
expected_spans = [
458+
[
459+
{
460+
"name": "gen_ai.run_step.message_creation",
461+
"attributes": {
462+
"gen_ai.system": "az.ai.agents",
463+
"gen_ai.thread.id": "*",
464+
"gen_ai.agent.id": "*",
465+
"gen_ai.thread.run.id": "*",
466+
"gen_ai.message.id": "*",
467+
"gen_ai.run_step.status": "completed",
468+
"gen_ai.run_step.start.timestamp": "*",
469+
"gen_ai.run_step.end.timestamp": "*",
470+
"gen_ai.usage.input_tokens": 0,
471+
"gen_ai.usage.output_tokens": 0,
472+
},
473+
},
474+
]
475+
] * 4
476+
expected_spans.append([
477+
{
478+
"name": "gen_ai.run_step.tool_calls",
479+
"attributes": {
480+
"gen_ai.system": "az.ai.agents",
481+
"gen_ai.thread.id": "*",
482+
"gen_ai.agent.id": "*",
483+
"gen_ai.thread.run.id": "*",
484+
"gen_ai.run_step.status": "completed",
485+
"gen_ai.run_step.start.timestamp": "*",
486+
"gen_ai.run_step.end.timestamp": "*",
487+
"gen_ai.usage.input_tokens": "+",
488+
"gen_ai.usage.output_tokens": "+",
489+
"gen_ai.event.content": expected_event_content
490+
},
491+
},
492+
])
493+
return expected_spans

0 commit comments

Comments
 (0)