From d4c422e736d30f720792740f02b1bc21dee1509c Mon Sep 17 00:00:00 2001 From: Max Ind Date: Thu, 2 Jul 2026 07:25:15 -0700 Subject: [PATCH] chore(telemetry): Remove gen_ai.agent.{request,response}.size metrics In https://github.com/open-telemetry/semantic-conventions-genai/pull/202 we've decided to remove those metrics as we don't see a use case for them. They can be reevaluated in the future if new use case appears. Co-authored-by: Max Ind PiperOrigin-RevId: 941689939 --- src/google/adk/telemetry/_instrumentation.py | 4 - src/google/adk/telemetry/_metrics.py | 79 --------------- .../telemetry/functional_node_test_cases.py | 24 ----- .../telemetry/functional_test_cases.py | 24 ----- .../telemetry/functional_test_helpers.py | 10 -- .../telemetry/test_instrumentation.py | 15 ++- tests/unittests/telemetry/test_metrics.py | 96 ------------------- 7 files changed, 7 insertions(+), 245 deletions(-) diff --git a/src/google/adk/telemetry/_instrumentation.py b/src/google/adk/telemetry/_instrumentation.py index 8bda08edd8..caf01cf082 100644 --- a/src/google/adk/telemetry/_instrumentation.py +++ b/src/google/adk/telemetry/_instrumentation.py @@ -106,7 +106,6 @@ def record_llm_response( def _record_agent_metrics( agent_name: str, elapsed_s: float, - user_content: object, events: object, caught_error: Exception | None, ) -> None: @@ -116,8 +115,6 @@ def _record_agent_metrics( elapsed_s, caught_error, ) - _metrics.record_agent_request_size(agent_name, user_content) - _metrics.record_agent_response_size(agent_name, events) _metrics.record_agent_workflow_steps(agent_name, events) except Exception: # pylint: disable=broad-exception-caught logger.exception("Failed to record agent metrics for agent %s", agent_name) @@ -145,7 +142,6 @@ async def record_agent_invocation( _record_agent_metrics( agent.name, _metrics.get_elapsed_s(span, start_time), - getattr(ctx, "user_content", None), getattr(getattr(ctx, "session", None), "events", []), caught_error, ) diff --git a/src/google/adk/telemetry/_metrics.py b/src/google/adk/telemetry/_metrics.py index 04ec0e9c77..08c0a66335 100644 --- a/src/google/adk/telemetry/_metrics.py +++ b/src/google/adk/telemetry/_metrics.py @@ -21,7 +21,6 @@ from google.adk import version from google.adk.telemetry import tracing from google.adk.telemetry._token_usage import TokenUsage -from google.genai import types from opentelemetry import metrics from opentelemetry.semconv._incubating.attributes import gen_ai_attributes from opentelemetry.semconv._incubating.metrics import gen_ai_metrics @@ -92,48 +91,6 @@ 81.92, ], ) -_agent_request_size = meter.create_histogram( - "gen_ai.agent.request.size", - unit="By", - description="Size of agent requests.", - explicit_bucket_boundaries_advisory=[ - 1, - 4, - 16, - 64, - 256, - 1024, - 4096, - 16384, - 65536, - 262144, - 1048576, - 4194304, - 16777216, - 67108864, - ], -) -_agent_response_size = meter.create_histogram( - "gen_ai.agent.response.size", - unit="By", - description="Size of agent responses.", - explicit_bucket_boundaries_advisory=[ - 1, - 4, - 16, - 64, - 256, - 1024, - 4096, - 16384, - 65536, - 262144, - 1048576, - 4194304, - 16777216, - 67108864, - ], -) _agent_workflow_steps = meter.create_histogram( "gen_ai.agent.workflow.steps", unit="1", @@ -191,28 +148,6 @@ def record_workflow_invocation_duration( _workflow_invocation_duration.record(elapsed_s, attributes=attrs) -def record_agent_request_size( - agent_name: str, user_content: types.Content | None -): - """Records the size of the agent request.""" - size = _get_content_size(user_content) - attrs = {gen_ai_attributes.GEN_AI_AGENT_NAME: agent_name} - _agent_request_size.record(size, attributes=attrs) - - -def record_agent_response_size(agent_name: str, events: list[Event]): - """Records the size of the agent response by extracting content from events.""" - response_content: types.Content | None = None - for event in reversed(events): - if event.author == agent_name and event.content: - response_content = event.content - break - - size = _get_content_size(response_content) - attrs = {gen_ai_attributes.GEN_AI_AGENT_NAME: agent_name} - _agent_response_size.record(size, attributes=attrs) - - def record_agent_workflow_steps(agent_name: str, events: list[Event]): """Records the number of steps in the agent workflow by counting the number of events.""" attrs = {gen_ai_attributes.GEN_AI_AGENT_NAME: agent_name} @@ -318,20 +253,6 @@ def record_client_token_usage( _client_token_usage.record(output_token_count, attributes=output_attrs) -def _get_content_size( - content: types.Content | None, -) -> int: - if not content or not content.parts: - return 0 - size = 0 - for part in content.parts: - if part.text is not None: - size += len(part.text.encode("utf-8")) - if part.inline_data and part.inline_data.data: - size += len(part.inline_data.data) - return size - - def _get_provider_name() -> str: return tracing._guess_gemini_system_name() diff --git a/tests/unittests/telemetry/functional_node_test_cases.py b/tests/unittests/telemetry/functional_node_test_cases.py index f562565f2b..5294757f34 100644 --- a/tests/unittests/telemetry/functional_node_test_cases.py +++ b/tests/unittests/telemetry/functional_node_test_cases.py @@ -2597,18 +2597,6 @@ value=NON_DETERMINISTIC, ), }), - "gen_ai.agent.request.size": frozenset({ - MetricPoint( - attributes={"gen_ai.agent.name": AGENT_NAME}, - value=len(USER_PROMPT), - ), - }), - "gen_ai.agent.response.size": frozenset({ - MetricPoint( - attributes={"gen_ai.agent.name": AGENT_NAME}, - value=len(FINAL_TEXT), - ), - }), "gen_ai.agent.workflow.steps": frozenset({ MetricPoint(attributes={"gen_ai.agent.name": AGENT_NAME}, value=3), }), @@ -2653,18 +2641,6 @@ value=NON_DETERMINISTIC, ), }), - "gen_ai.agent.request.size": frozenset({ - MetricPoint( - attributes={"gen_ai.agent.name": AGENT_NAME}, - value=len(USER_PROMPT), - ), - }), - "gen_ai.agent.response.size": frozenset({ - MetricPoint( - attributes={"gen_ai.agent.name": AGENT_NAME}, - value=len(FINAL_TEXT), - ), - }), "gen_ai.agent.workflow.steps": frozenset({ MetricPoint(attributes={"gen_ai.agent.name": AGENT_NAME}, value=3), }), diff --git a/tests/unittests/telemetry/functional_test_cases.py b/tests/unittests/telemetry/functional_test_cases.py index ea6383acb5..3cf39f514c 100644 --- a/tests/unittests/telemetry/functional_test_cases.py +++ b/tests/unittests/telemetry/functional_test_cases.py @@ -2291,18 +2291,6 @@ value=NON_DETERMINISTIC, ), }), - "gen_ai.agent.request.size": frozenset({ - MetricPoint( - attributes={"gen_ai.agent.name": AGENT_NAME}, - value=len(USER_PROMPT), - ), - }), - "gen_ai.agent.response.size": frozenset({ - MetricPoint( - attributes={"gen_ai.agent.name": AGENT_NAME}, - value=len(FINAL_TEXT), - ), - }), "gen_ai.agent.workflow.steps": frozenset({ MetricPoint(attributes={"gen_ai.agent.name": AGENT_NAME}, value=3), }), @@ -2338,18 +2326,6 @@ value=NON_DETERMINISTIC, ), }), - "gen_ai.agent.request.size": frozenset({ - MetricPoint( - attributes={"gen_ai.agent.name": AGENT_NAME}, - value=len(USER_PROMPT), - ), - }), - "gen_ai.agent.response.size": frozenset({ - MetricPoint( - attributes={"gen_ai.agent.name": AGENT_NAME}, - value=len(FINAL_TEXT), - ), - }), "gen_ai.agent.workflow.steps": frozenset({ MetricPoint(attributes={"gen_ai.agent.name": AGENT_NAME}, value=3), }), diff --git a/tests/unittests/telemetry/functional_test_helpers.py b/tests/unittests/telemetry/functional_test_helpers.py index 031e8e0a54..49c072ee32 100644 --- a/tests/unittests/telemetry/functional_test_helpers.py +++ b/tests/unittests/telemetry/functional_test_helpers.py @@ -314,16 +314,6 @@ class HistogramSpec(NamedTuple): attr="_tool_execution_duration", metric_name="gen_ai.execute_tool.duration", ), - HistogramSpec( - module=_metrics, - attr="_agent_request_size", - metric_name="gen_ai.agent.request.size", - ), - HistogramSpec( - module=_metrics, - attr="_agent_response_size", - metric_name="gen_ai.agent.response.size", - ), HistogramSpec( module=_metrics, attr="_agent_workflow_steps", diff --git a/tests/unittests/telemetry/test_instrumentation.py b/tests/unittests/telemetry/test_instrumentation.py index dc41adb51f..41de19b500 100644 --- a/tests/unittests/telemetry/test_instrumentation.py +++ b/tests/unittests/telemetry/test_instrumentation.py @@ -86,16 +86,16 @@ def test_get_elapsed_s_span_non_int_end(): @pytest.mark.asyncio async def test_record_agent_invocation_tolerates_minimal_context(): - """Tolerates context-likes that lack user_content or session. + """Tolerates context-likes that lack session. Test doubles, partial migrations, and external embedders can pass an - InvocationContext-like object without `user_content` or with a `session` - that has no `events` attribute. The telemetry path must not raise - AttributeError on the metrics call in those cases. + InvocationContext-like object with a `session` that has no `events` + attribute. The telemetry path must not raise AttributeError on the + metrics call in those cases. """ agent = mock.MagicMock() agent.name = "test_agent" - # Bare object without `user_content` and without `session`. + # Bare object without `session`. bare_ctx = object() with ( @@ -112,7 +112,6 @@ async def test_record_agent_invocation_tolerates_minimal_context(): mock_record.assert_called_once() call_args = mock_record.call_args - # positional: (agent_name, elapsed_s, user_content, events, caught_error) + # positional: (agent_name, elapsed_s, events, caught_error) assert call_args.args[0] == "test_agent" - assert call_args.args[2] is None # user_content default - assert call_args.args[3] == [] # events default + assert call_args.args[2] == [] # events default diff --git a/tests/unittests/telemetry/test_metrics.py b/tests/unittests/telemetry/test_metrics.py index d18f01e897..62d1d5c662 100644 --- a/tests/unittests/telemetry/test_metrics.py +++ b/tests/unittests/telemetry/test_metrics.py @@ -29,8 +29,6 @@ def _mock_meter_setup(monkeypatch): agent_duration_hist = mock.MagicMock(spec=metrics.Histogram) workflow_duration_hist = mock.MagicMock(spec=metrics.Histogram) tool_duration_hist = mock.MagicMock(spec=metrics.Histogram) - request_size_hist = mock.MagicMock(spec=metrics.Histogram) - response_size_hist = mock.MagicMock(spec=metrics.Histogram) steps_hist = mock.MagicMock(spec=metrics.Histogram) client_duration_hist = mock.MagicMock(spec=metrics.Histogram) client_token_usage_hist = mock.MagicMock(spec=metrics.Histogram) @@ -38,8 +36,6 @@ def _mock_meter_setup(monkeypatch): agent_duration_hist.name = "agent_invocation_duration" workflow_duration_hist.name = "workflow_invocation_duration" tool_duration_hist.name = "tool_execution_duration" - request_size_hist.name = "agent_request_size" - response_size_hist.name = "agent_response_size" steps_hist.name = "agent_workflow_steps" client_duration_hist.name = "client_operation_duration" client_token_usage_hist.name = "client_token_usage" @@ -51,10 +47,6 @@ def create_histogram_side_effect(name, **_kwargs): return workflow_duration_hist elif name == "gen_ai.execute_tool.duration": return tool_duration_hist - elif name == "gen_ai.agent.request.size": - return request_size_hist - elif name == "gen_ai.agent.response.size": - return response_size_hist elif name == "gen_ai.agent.workflow.steps": return steps_hist elif name == "gen_ai.client.operation.duration": @@ -74,8 +66,6 @@ def create_histogram_side_effect(name, **_kwargs): _metrics, "_workflow_invocation_duration", workflow_duration_hist ) monkeypatch.setattr(_metrics, "_tool_execution_duration", tool_duration_hist) - monkeypatch.setattr(_metrics, "_agent_request_size", request_size_hist) - monkeypatch.setattr(_metrics, "_agent_response_size", response_size_hist) monkeypatch.setattr(_metrics, "_agent_workflow_steps", steps_hist) monkeypatch.setattr( _metrics, "_client_operation_duration", client_duration_hist @@ -87,30 +77,12 @@ def create_histogram_side_effect(name, **_kwargs): "agent_duration": agent_duration_hist, "workflow_duration": workflow_duration_hist, "tool_duration": tool_duration_hist, - "request_size": request_size_hist, - "response_size": response_size_hist, "steps": steps_hist, "client_duration": client_duration_hist, "client_token_usage": client_token_usage_hist, } -def test_record_agent_request_size(mock_meter_setup): - """Tests record_agent_request_size records correctly.""" - user_content = "hello" - _metrics.record_agent_request_size( - "test_agent", types.Content(parts=[types.Part(text=user_content)]) - ) - request_size_hist = mock_meter_setup["request_size"] - request_size_hist.record.assert_called_once() - args, kwargs = request_size_hist.record.call_args - assert args[0] == len(user_content) - want_attributes = { - "gen_ai.agent.name": "test_agent", - } - assert kwargs["attributes"] == want_attributes - - def test_record_agent_invocation_duration(mock_meter_setup): """Tests record_agent_invocation_duration records correctly.""" _metrics.record_agent_invocation_duration( @@ -173,22 +145,6 @@ def test_record_workflow_invocation_duration_nested_with_error( assert kwargs["attributes"]["error.type"] == "ValueError" -def test_record_agent_response_size(mock_meter_setup): - """Tests record_agent_response_size records correctly.""" - response_text = "response" - event = mock.MagicMock( - author="test_agent", - content=types.Content(parts=[types.Part(text=response_text)]), - ) - _metrics.record_agent_response_size("test_agent", [event]) - response_size_hist = mock_meter_setup["response_size"] - response_size_hist.record.assert_called_once() - args, kwargs = response_size_hist.record.call_args - assert args[0] == len(response_text) - want_attributes = {"gen_ai.agent.name": "test_agent"} - assert kwargs["attributes"] == want_attributes - - def test_record_agent_workflow_steps(mock_meter_setup): """Tests record_agent_workflow_steps records correctly.""" _metrics.record_agent_workflow_steps( @@ -243,58 +199,6 @@ def test_record_tool_execution_duration_with_error(mock_meter_setup): assert kwargs["attributes"]["error.type"] == "ValueError" -@pytest.mark.parametrize( - "content,expected_size", - [ - (None, 0), - (types.Content(parts=[types.Part(text="hello")]), 5), - ( - types.Content( - parts=[ - types.Part(text="hello"), - types.Part(text=" world"), - ] - ), - 11, - ), - ( - types.Content( - parts=[ - types.Part( - inline_data=types.Blob( - mime_type="image/png", data=b"12345" - ) - ) - ] - ), - 5, - ), - ( - types.Content( - parts=[ - types.Part(text="hello"), - types.Part( - inline_data=types.Blob( - mime_type="image/png", data=b"12345" - ) - ), - ] - ), - 10, - ), - ], - ids=[ - "none_content", - "simple_text", - "multi_text", - "inline_data", - "mixed_content", - ], -) -def test_get_content_size(content, expected_size): - assert _metrics._get_content_size(content) == expected_size - - def test_record_client_operation_duration(mock_meter_setup): """Tests record_client_operation_duration records correctly.""" llm_request = mock.MagicMock(