1818 McpTool ,
1919 MessageDeltaChunk ,
2020 MessageDeltaTextContent ,
21+ MessageInputTextBlock ,
2122 OpenApiAnonymousAuthDetails ,
2223 OpenApiTool ,
2324 RequiredMcpToolCall ,
2829 RunStepToolCallDetails ,
2930 SubmitToolApprovalAction ,
3031 ThreadMessage ,
32+ ThreadMessageOptions ,
3133 ThreadRun ,
3234 Tool ,
3335 ToolApproval ,
3638from azure .ai .agents .telemetry ._ai_agents_instrumentor import _AIAgentsInstrumentorPreview
3739from azure .ai .agents .telemetry import AIAgentsInstrumentor , _utils
3840from azure .core .settings import settings
39- from memory_trace_exporter import MemoryTraceExporter
4041from gen_ai_trace_verifier import GenAiTraceVerifier
41- from opentelemetry import trace
42- from opentelemetry .sdk .trace import TracerProvider
43- from opentelemetry .sdk .trace .export import SimpleSpanProcessor
4442from azure .ai .agents import AgentsClient
4543
4644from devtools_testutils import (
4745 recorded_by_proxy ,
4846)
4947
5048from test_agents_client_base import agentClientPreparer
51- from test_ai_instrumentor_base import TestAiAgentsInstrumentorBase
49+ from test_ai_instrumentor_base import TestAiAgentsInstrumentorBase , MessageCreationMode , CONTENT_TRACING_ENV_VARIABLE
5250
53- CONTENT_TRACING_ENV_VARIABLE = "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"
5451settings .tracing_implementation = "OpenTelemetry"
5552_utils ._span_impl_type = settings .tracing_implementation ()
5653
5754
5855class TestAiAgentsInstrumentor (TestAiAgentsInstrumentorBase ):
56+
5957 """Tests for AI agents instrumentor."""
6058
6159 @pytest .fixture (scope = "function" )
@@ -72,19 +70,6 @@ def instrument_without_content(self):
7270 yield
7371 self .cleanup ()
7472
75- def setup_telemetry (self ):
76- trace ._TRACER_PROVIDER = TracerProvider ()
77- self .exporter = MemoryTraceExporter ()
78- span_processor = SimpleSpanProcessor (self .exporter )
79- trace .get_tracer_provider ().add_span_processor (span_processor )
80- AIAgentsInstrumentor ().instrument ()
81-
82- def cleanup (self ):
83- self .exporter .shutdown ()
84- AIAgentsInstrumentor ().uninstrument ()
85- trace ._TRACER_PROVIDER = None
86- os .environ .pop (CONTENT_TRACING_ENV_VARIABLE , None )
87-
8873 # helper function: create client and using environment variables
8974 def create_client (self , ** kwargs ):
9075 # fetch environment variables
@@ -227,13 +212,38 @@ def set_env_var(var_name, value):
227212 @agentClientPreparer ()
228213 @recorded_by_proxy
229214 def test_agent_chat_with_tracing_content_recording_enabled (self , ** kwargs ):
215+ # Note: The proper way to invoke the same test over and over again with different parameter values is to use @pytest.mark.parametrize. However,
216+ # this does not work together with @recorded_by_proxy. So we call the helper function 4 times instead in a single recorded test.
217+ self ._agent_chat_with_tracing_content_recording_enabled (message_creation_mode = MessageCreationMode .MESSAGE_CREATE_STR , ** kwargs )
218+ self ._agent_chat_with_tracing_content_recording_enabled (message_creation_mode = MessageCreationMode .MESSAGE_CREATE_INPUT_TEXT_BLOCK , ** kwargs )
219+ self ._agent_chat_with_tracing_content_recording_enabled (message_creation_mode = MessageCreationMode .THREAD_CREATE_STR , ** kwargs )
220+ self ._agent_chat_with_tracing_content_recording_enabled (message_creation_mode = MessageCreationMode .THREAD_CREATE_INPUT_TEXT_BLOCK , ** kwargs )
221+
222+ def _agent_chat_with_tracing_content_recording_enabled (self , message_creation_mode : MessageCreationMode , ** kwargs ):
223+ self .cleanup ()
224+ os .environ .update ({CONTENT_TRACING_ENV_VARIABLE : "True" })
225+ self .setup_telemetry ()
230226 assert True == AIAgentsInstrumentor ().is_content_recording_enabled ()
231227 assert True == AIAgentsInstrumentor ().is_instrumented ()
232228
233229 client = self .create_client (** kwargs )
234230 agent = client .create_agent (model = "gpt-4o-mini" , name = "my-agent" , instructions = "You are helpful agent" )
235- thread = client .threads .create ()
236- client .messages .create (thread_id = thread .id , role = "user" , content = "Hello, tell me a joke" )
231+ user_content = "Hello, tell me a joke"
232+
233+ # Test 4 different patterns of thread & message creation
234+ if message_creation_mode == MessageCreationMode .MESSAGE_CREATE_STR :
235+ thread = client .threads .create ()
236+ client .messages .create (thread_id = thread .id , role = "user" , content = user_content )
237+ elif message_creation_mode == MessageCreationMode .MESSAGE_CREATE_INPUT_TEXT_BLOCK :
238+ thread = client .threads .create ()
239+ client .messages .create (thread_id = thread .id , role = "user" , content = [MessageInputTextBlock (text = user_content )])
240+ elif message_creation_mode == MessageCreationMode .THREAD_CREATE_STR :
241+ thread = client .threads .create (messages = [ThreadMessageOptions (role = "user" , content = user_content )])
242+ elif message_creation_mode == MessageCreationMode .THREAD_CREATE_INPUT_TEXT_BLOCK :
243+ thread = client .threads .create (messages = [ThreadMessageOptions (role = "user" , content = [MessageInputTextBlock (text = user_content )])])
244+ else :
245+ assert False , f"Unknown message creation mode: { message_creation_mode } "
246+
237247 run = client .runs .create (thread_id = thread .id , agent_id = agent .id )
238248
239249 while run .status in ["queued" , "in_progress" , "requires_action" ]:
@@ -250,6 +260,7 @@ def test_agent_chat_with_tracing_content_recording_enabled(self, **kwargs):
250260 assert len (messages ) > 1
251261 client .close ()
252262
263+ # ------------------------- Validate "create_agent" span ---------------------------------
253264 self .exporter .force_flush ()
254265 spans = self .exporter .get_spans_by_name ("create_agent my-agent" )
255266 assert len (spans ) == 1
@@ -277,6 +288,7 @@ def test_agent_chat_with_tracing_content_recording_enabled(self, **kwargs):
277288 events_match = GenAiTraceVerifier ().check_span_events (span , expected_events )
278289 assert events_match == True
279290
291+ # ------------------------- Validate "create_thread" span ---------------------------------
280292 spans = self .exporter .get_spans_by_name ("create_thread" )
281293 assert len (spans ) == 1
282294 span = spans [0 ]
@@ -289,32 +301,48 @@ def test_agent_chat_with_tracing_content_recording_enabled(self, **kwargs):
289301 attributes_match = GenAiTraceVerifier ().check_span_attributes (span , expected_attributes )
290302 assert attributes_match == True
291303
292- spans = self .exporter .get_spans_by_name ("create_message" )
293- assert len (spans ) == 1
294- span = spans [0 ]
295- expected_attributes = [
296- ("gen_ai.system" , "az.ai.agents" ),
297- ("gen_ai.operation.name" , "create_message" ),
298- ("server.address" , "" ),
299- ("gen_ai.thread.id" , "" ),
300- ("gen_ai.message.id" , "" ),
301- ]
302- attributes_match = GenAiTraceVerifier ().check_span_attributes (span , expected_attributes )
303- assert attributes_match == True
304-
305- expected_events = [
306- {
307- "name" : "gen_ai.user.message" ,
308- "attributes" : {
309- "gen_ai.system" : "az.ai.agents" ,
310- "gen_ai.thread.id" : "*" ,
311- "gen_ai.event.content" : '{"content": "Hello, tell me a joke", "role": "user"}' ,
312- },
313- }
314- ]
315- events_match = GenAiTraceVerifier ().check_span_events (span , expected_events )
316- assert events_match == True
317-
304+ if message_creation_mode in (MessageCreationMode .THREAD_CREATE_STR , MessageCreationMode .THREAD_CREATE_INPUT_TEXT_BLOCK ):
305+ expected_events = [
306+ {
307+ "name" : "gen_ai.user.message" ,
308+ "attributes" : {
309+ "gen_ai.system" : "az.ai.agents" ,
310+ "gen_ai.event.content" : '{"content": "Hello, tell me a joke", "role": "user"}' ,
311+ },
312+ }
313+ ]
314+ events_match = GenAiTraceVerifier ().check_span_events (span , expected_events )
315+ assert events_match == True
316+
317+ # ------------------------- Validate "create_message" span ---------------------------------
318+ if message_creation_mode in (MessageCreationMode .MESSAGE_CREATE_STR , MessageCreationMode .MESSAGE_CREATE_INPUT_TEXT_BLOCK ):
319+ spans = self .exporter .get_spans_by_name ("create_message" )
320+ assert len (spans ) == 1
321+ span = spans [0 ]
322+ expected_attributes = [
323+ ("gen_ai.system" , "az.ai.agents" ),
324+ ("gen_ai.operation.name" , "create_message" ),
325+ ("server.address" , "" ),
326+ ("gen_ai.thread.id" , "" ),
327+ ("gen_ai.message.id" , "" ),
328+ ]
329+ attributes_match = GenAiTraceVerifier ().check_span_attributes (span , expected_attributes )
330+ assert attributes_match == True
331+
332+ expected_events = [
333+ {
334+ "name" : "gen_ai.user.message" ,
335+ "attributes" : {
336+ "gen_ai.system" : "az.ai.agents" ,
337+ "gen_ai.thread.id" : "*" ,
338+ "gen_ai.event.content" : '{"content": "Hello, tell me a joke", "role": "user"}' ,
339+ },
340+ }
341+ ]
342+ events_match = GenAiTraceVerifier ().check_span_events (span , expected_events )
343+ assert events_match == True
344+
345+ # ------------------------- Validate "start_thread_run" span ---------------------------------
318346 spans = self .exporter .get_spans_by_name ("start_thread_run" )
319347 assert len (spans ) == 1
320348 span = spans [0 ]
@@ -332,6 +360,7 @@ def test_agent_chat_with_tracing_content_recording_enabled(self, **kwargs):
332360 attributes_match = GenAiTraceVerifier ().check_span_attributes (span , expected_attributes )
333361 assert attributes_match == True
334362
363+ # ------------------------- Validate "get_thread_run" span ---------------------------------
335364 spans = self .exporter .get_spans_by_name ("get_thread_run" )
336365 assert len (spans ) >= 1
337366 span = spans [- 1 ]
@@ -350,6 +379,7 @@ def test_agent_chat_with_tracing_content_recording_enabled(self, **kwargs):
350379 attributes_match = GenAiTraceVerifier ().check_span_attributes (span , expected_attributes )
351380 assert attributes_match == True
352381
382+ # ------------------------- Validate "list_messages" span ---------------------------------
353383 spans = self .exporter .get_spans_by_name ("list_messages" )
354384 assert len (spans ) == 2
355385 span = spans [0 ]
0 commit comments