@@ -1360,6 +1360,7 @@ def test_agent_call_creates_and_ends_span_on_success(mock_get_tracer, mock_model
13601360 tools = agent .tool_names ,
13611361 system_prompt = agent .system_prompt ,
13621362 custom_trace_attributes = agent .trace_attributes ,
1363+ tools_config = unittest .mock .ANY ,
13631364 )
13641365
13651366 # Verify span was ended with the result
@@ -1394,6 +1395,7 @@ async def test_event_loop(*args, **kwargs):
13941395 tools = agent .tool_names ,
13951396 system_prompt = agent .system_prompt ,
13961397 custom_trace_attributes = agent .trace_attributes ,
1398+ tools_config = unittest .mock .ANY ,
13971399 )
13981400
13991401 expected_response = AgentResult (
@@ -1432,6 +1434,7 @@ def test_agent_call_creates_and_ends_span_on_exception(mock_get_tracer, mock_mod
14321434 tools = agent .tool_names ,
14331435 system_prompt = agent .system_prompt ,
14341436 custom_trace_attributes = agent .trace_attributes ,
1437+ tools_config = unittest .mock .ANY ,
14351438 )
14361439
14371440 # Verify span was ended with the exception
@@ -1468,6 +1471,7 @@ async def test_agent_stream_async_creates_and_ends_span_on_exception(mock_get_tr
14681471 tools = agent .tool_names ,
14691472 system_prompt = agent .system_prompt ,
14701473 custom_trace_attributes = agent .trace_attributes ,
1474+ tools_config = unittest .mock .ANY ,
14711475 )
14721476
14731477 # Verify span was ended with the exception
@@ -2240,8 +2244,82 @@ def test_agent_backwards_compatibility_single_text_block():
22402244
22412245 # Should extract text for backwards compatibility
22422246 assert agent .system_prompt == text
2243-
2244-
2247+
2248+
2249+ def test_agent_does_not_include_tools_in_trace_by_default (tool_decorated , monkeypatch ):
2250+ """Verify that by default, the agent does not add tool specs to the trace."""
2251+ monkeypatch .setenv ("OTEL_SEMCONV_STABILITY_OPT_IN" , "" )
2252+ with unittest .mock .patch ("strands.agent.agent.get_tracer" ) as mock_get_tracer :
2253+ # We need to re-import the tracer to pick up the new env var
2254+ import importlib
2255+
2256+ from strands .telemetry import tracer
2257+
2258+ importlib .reload (tracer )
2259+
2260+ mock_tracer_instance = tracer .Tracer ()
2261+ mock_span = unittest .mock .MagicMock ()
2262+ mock_tracer_instance .start_agent_span = unittest .mock .MagicMock (return_value = mock_span )
2263+ mock_get_tracer .return_value = mock_tracer_instance
2264+
2265+ mock_model = MockedModelProvider ([{"role" : "assistant" , "content" : [{"text" : "hello!" }]}])
2266+
2267+ agent = Agent (tools = [tool_decorated ], model = mock_model )
2268+ agent ("test prompt" )
2269+
2270+ # Check that set_attribute was not called for our specific key
2271+ called_attributes = [call .args [0 ] for call in mock_span .set_attribute .call_args_list ]
2272+ assert "gen_ai.tool.definitions" not in called_attributes
2273+
2274+
2275+ def test_agent_includes_tools_in_trace_when_enabled (tool_decorated , monkeypatch ):
2276+ """Verify that the agent adds tool specs to the trace when the flag is enabled."""
2277+ monkeypatch .setenv ("OTEL_SEMCONV_STABILITY_OPT_IN" , "gen_ai_tool_definitions" )
2278+ with unittest .mock .patch ("strands.agent.agent.get_tracer" ) as mock_get_tracer :
2279+ # We need to re-import the tracer to pick up the new env var
2280+ import importlib
2281+
2282+ from strands .telemetry import tracer
2283+
2284+ importlib .reload (tracer )
2285+
2286+ # Create a REAL tracer instance so its logic runs
2287+ mock_tracer_instance = tracer .Tracer ()
2288+ mock_span = unittest .mock .MagicMock ()
2289+
2290+ # Mock the lower-level _start_span method, NOT start_agent_span
2291+ mock_tracer_instance ._start_span = unittest .mock .MagicMock (return_value = mock_span )
2292+ mock_get_tracer .return_value = mock_tracer_instance
2293+
2294+ mock_model = MockedModelProvider ([{"role" : "assistant" , "content" : [{"text" : "hello!" }]}])
2295+
2296+ agent = Agent (tools = [tool_decorated ], model = mock_model )
2297+ agent ("test prompt" )
2298+
2299+ # Now, we assert that _start_span was called, and we inspect the
2300+ # attributes that were passed to it.
2301+ mock_tracer_instance ._start_span .assert_called_once ()
2302+ call_args , call_kwargs = mock_tracer_instance ._start_span .call_args
2303+ passed_attributes = call_kwargs .get ("attributes" , {})
2304+
2305+ # Construct the data we expect to find in the attributes
2306+ tool_spec = tool_decorated .tool_spec
2307+ expected_tool_details = [
2308+ {
2309+ "name" : tool_spec .get ("name" ),
2310+ "description" : tool_spec .get ("description" ),
2311+ "inputSchema" : tool_spec .get ("inputSchema" ),
2312+ "outputSchema" : tool_spec .get ("outputSchema" ),
2313+ }
2314+ ]
2315+ # The final code serializes the data to pass mypy checks, so the test must expect a string.
2316+ expected_json = serialize (expected_tool_details )
2317+
2318+ # Verify that our tool definitions are present in the attributes passed to the span
2319+ assert "gen_ai.tool.definitions" in passed_attributes
2320+ assert passed_attributes ["gen_ai.tool.definitions" ] == expected_json
2321+
2322+
22452323@pytest .mark .parametrize (
22462324 "content, expected" ,
22472325 [
0 commit comments