fix: Set empty JSON string as placeholder for redacted content in traces

When content capture is disabled, trace attributes for tool arguments, tool responses, LLM requests, LLM responses, and agent data are now set to the string '{}' instead of an empty dictionary

Close #4094

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 855304806
This commit is contained in:
George Weale
2026-01-12 11:14:45 -08:00
committed by Copybara-Service
parent 458d24e24e
commit 5880109ab1
2 changed files with 71 additions and 54 deletions
+6 -6
View File
@@ -149,7 +149,7 @@ def trace_tool_call(
_safe_json_serialize(args),
)
else:
span.set_attribute('gcp.vertex.agent.tool_call_args', {})
span.set_attribute('gcp.vertex.agent.tool_call_args', '{}')
# Tracing tool response
tool_call_id = '<not specified>'
@@ -179,7 +179,7 @@ def trace_tool_call(
_safe_json_serialize(tool_response),
)
else:
span.set_attribute('gcp.vertex.agent.tool_response', {})
span.set_attribute('gcp.vertex.agent.tool_response', '{}')
def trace_merged_tool_calls(
@@ -219,7 +219,7 @@ def trace_merged_tool_calls(
function_response_event_json,
)
else:
span.set_attribute('gcp.vertex.agent.tool_response', {})
span.set_attribute('gcp.vertex.agent.tool_response', '{}')
# Setting empty llm request and response (as UI expect these) while not
# applicable for tool_response.
span.set_attribute('gcp.vertex.agent.llm_request', '{}')
@@ -265,7 +265,7 @@ def trace_call_llm(
_safe_json_serialize(_build_llm_request_for_trace(llm_request)),
)
else:
span.set_attribute('gcp.vertex.agent.llm_request', {})
span.set_attribute('gcp.vertex.agent.llm_request', '{}')
# Consider removing once GenAI SDK provides a way to record this info.
if llm_request.config:
if llm_request.config.top_p:
@@ -290,7 +290,7 @@ def trace_call_llm(
llm_response_json,
)
else:
span.set_attribute('gcp.vertex.agent.llm_response', {})
span.set_attribute('gcp.vertex.agent.llm_response', '{}')
if llm_response.usage_metadata is not None:
span.set_attribute(
@@ -346,7 +346,7 @@ def trace_send_data(
]),
)
else:
span.set_attribute('gcp.vertex.agent.data', {})
span.set_attribute('gcp.vertex.agent.data', '{}')
def _build_llm_request_for_trace(llm_request: LlmRequest) -> dict[str, Any]:
+65 -48
View File
@@ -27,6 +27,7 @@ from google.adk.telemetry.tracing import ADK_CAPTURE_MESSAGE_CONTENT_IN_SPANS
from google.adk.telemetry.tracing import trace_agent_invocation
from google.adk.telemetry.tracing import trace_call_llm
from google.adk.telemetry.tracing import trace_merged_tool_calls
from google.adk.telemetry.tracing import trace_send_data
from google.adk.telemetry.tracing import trace_tool_call
from google.adk.tools.base_tool import BaseTool
from google.genai import types
@@ -447,7 +448,7 @@ def test_trace_merged_tool_calls_sets_correct_attributes(
async def test_call_llm_disabling_request_response_content(
monkeypatch, mock_span_fixture
):
"""Test trace_call_llm doesn't set request and response attributes if env is set to false"""
"""Test trace_call_llm sets placeholders when capture is disabled."""
# Arrange
monkeypatch.setenv(ADK_CAPTURE_MESSAGE_CONTENT_IN_SPANS, 'false')
monkeypatch.setattr(
@@ -474,23 +475,19 @@ async def test_call_llm_disabling_request_response_content(
trace_call_llm(invocation_context, 'test_event_id', llm_request, llm_response)
# Assert
assert not any(
arg_name == 'gcp.vertex.agent.llm_request' and arg_value != {}
for arg_name, arg_value in (
call_obj.args
for call_obj in mock_span_fixture.set_attribute.call_args_list
)
), "Attribute 'gcp.vertex.agent.llm_request' was incorrectly set on the span."
assert not any(
arg_name == 'gcp.vertex.agent.llm_response' and arg_value != {}
for arg_name, arg_value in (
call_obj.args
for call_obj in mock_span_fixture.set_attribute.call_args_list
)
), (
"Attribute 'gcp.vertex.agent.llm_response' was incorrectly set on the"
' span.'
assert (
'gcp.vertex.agent.llm_request',
'{}',
) in (
call_obj.args
for call_obj in mock_span_fixture.set_attribute.call_args_list
)
assert (
'gcp.vertex.agent.llm_response',
'{}',
) in (
call_obj.args
for call_obj in mock_span_fixture.set_attribute.call_args_list
)
@@ -500,7 +497,7 @@ def test_trace_tool_call_disabling_request_response_content(
mock_tool_fixture,
mock_event_fixture,
):
"""Test trace_tool_call doesn't set request and response attributes if env is set to false"""
"""Test trace_tool_call sets placeholders when capture is disabled."""
# Arrange
monkeypatch.setenv(ADK_CAPTURE_MESSAGE_CONTENT_IN_SPANS, 'false')
monkeypatch.setattr(
@@ -537,26 +534,19 @@ def test_trace_tool_call_disabling_request_response_content(
)
# Assert
assert not any(
arg_name == 'gcp.vertex.agent.tool_call_args' and arg_value != {}
for arg_name, arg_value in (
call_obj.args
for call_obj in mock_span_fixture.set_attribute.call_args_list
)
), (
"Attribute 'gcp.vertex.agent.tool_call_args' was incorrectly set on the"
' span.'
assert (
'gcp.vertex.agent.tool_call_args',
'{}',
) in (
call_obj.args
for call_obj in mock_span_fixture.set_attribute.call_args_list
)
assert not any(
arg_name == 'gcp.vertex.agent.tool_response' and arg_value != {}
for arg_name, arg_value in (
call_obj.args
for call_obj in mock_span_fixture.set_attribute.call_args_list
)
), (
"Attribute 'gcp.vertex.agent.tool_response' was incorrectly set on the"
' span.'
assert (
'gcp.vertex.agent.tool_response',
'{}',
) in (
call_obj.args
for call_obj in mock_span_fixture.set_attribute.call_args_list
)
@@ -565,7 +555,7 @@ def test_trace_merged_tool_disabling_request_response_content(
mock_span_fixture,
mock_event_fixture,
):
"""Test trace_merged_tool doesn't set request and response attributes if env is set to false"""
"""Test trace_merged_tool_calls sets placeholders when capture is disabled."""
# Arrange
monkeypatch.setenv(ADK_CAPTURE_MESSAGE_CONTENT_IN_SPANS, 'false')
monkeypatch.setattr(
@@ -585,13 +575,40 @@ def test_trace_merged_tool_disabling_request_response_content(
)
# Assert
assert not any(
arg_name == 'gcp.vertex.agent.tool_response' and arg_value != {}
for arg_name, arg_value in (
call_obj.args
for call_obj in mock_span_fixture.set_attribute.call_args_list
)
), (
"Attribute 'gcp.vertex.agent.tool_response' was incorrectly set on the"
' span.'
assert (
'gcp.vertex.agent.tool_response',
'{}',
) in (
call_obj.args
for call_obj in mock_span_fixture.set_attribute.call_args_list
)
@pytest.mark.asyncio
async def test_trace_send_data_disabling_request_response_content(
monkeypatch, mock_span_fixture
):
"""Test trace_send_data sets placeholders when capture is disabled."""
monkeypatch.setenv(ADK_CAPTURE_MESSAGE_CONTENT_IN_SPANS, 'false')
monkeypatch.setattr(
'opentelemetry.trace.get_current_span', lambda: mock_span_fixture
)
agent = LlmAgent(name='test_agent')
invocation_context = await _create_invocation_context(agent)
trace_send_data(
invocation_context=invocation_context,
event_id='test_event_id',
data=[
types.Content(
role='user',
parts=[types.Part(text='hi')],
)
],
)
assert ('gcp.vertex.agent.data', '{}') in (
call_obj.args
for call_obj in mock_span_fixture.set_attribute.call_args_list
)