Skip to content

Commit 03d6208

Browse files
google-genai-botcopybara-github
authored andcommitted
refactor: adjust computation of workflow.steps metric and add new unit tests
PiperOrigin-RevId: 910601469
1 parent 9559968 commit 03d6208

5 files changed

Lines changed: 105 additions & 16 deletions

File tree

src/google/adk/telemetry/_instrumentation.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
if TYPE_CHECKING:
3535
from ..agents.base_agent import BaseAgent
3636
from ..agents.invocation_context import InvocationContext
37+
from ..tools.base_tool import BaseTool
3738

3839

3940
def _get_elapsed_ms(span: trace.Span | None, fallback_start: float) -> float:
@@ -97,7 +98,7 @@ async def record_agent_invocation(
9798
)
9899
_metrics.record_agent_request_size(agent.name, ctx.user_content)
99100
_metrics.record_agent_response_size(agent.name, ctx.session.events)
100-
_metrics.record_agent_workflow_steps(agent.name, len(ctx.session.events))
101+
_metrics.record_agent_workflow_steps(agent.name, ctx.session.events)
101102
except Exception: # pylint: disable=broad-exception-caught
102103
logger.exception(
103104
"Failed to record agent metrics for agent %s", agent.name

src/google/adk/telemetry/_metrics.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,11 @@ def record_agent_response_size(agent_name: str, events: list[Event]):
9898
_agent_response_size.record(size, attributes=attrs)
9999

100100

101-
def record_agent_workflow_steps(agent_name: str, steps_count: int):
102-
"""Records the number of steps in the agent workflow."""
103-
attrs = {
104-
gen_ai_attributes.GEN_AI_AGENT_NAME: agent_name,
105-
}
106-
_agent_workflow_steps.record(steps_count, attributes=attrs)
101+
def record_agent_workflow_steps(agent_name: str, events: list[Event]):
102+
"""Records the number of steps in the agent workflow by counting the number of events."""
103+
attrs = {gen_ai_attributes.GEN_AI_AGENT_NAME: agent_name}
104+
count = sum(1 for event in events if event.author == agent_name)
105+
_agent_workflow_steps.record(count, attributes=attrs)
107106

108107

109108
def record_tool_execution_duration(

tests/unittests/telemetry/test_functional.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,8 @@ async def generate_random_number():
349349
got_steps = _extract_metrics(metrics_list, "gen_ai.agent.workflow.steps")
350350
assert len(got_steps) == 1
351351
want_steps = [
352-
MetricPoint(attributes={"gen_ai.agent.name": "complex_agent"}, value=6)
352+
# (tool call + result) x 2 + text response = 5 steps
353+
MetricPoint(attributes={"gen_ai.agent.name": "complex_agent"}, value=5)
353354
]
354355
assert got_steps == want_steps
355356

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# pylint: disable=protected-access
16+
17+
import time
18+
from unittest import mock
19+
20+
from google.adk.telemetry import _instrumentation
21+
from opentelemetry import trace
22+
23+
24+
def test_get_elapsed_ms_span_none():
25+
"""Tests fallback when span is None."""
26+
start_time = 10.0
27+
with mock.patch("time.monotonic", return_value=12.0):
28+
elapsed = _instrumentation._get_elapsed_ms(None, start_time)
29+
assert elapsed == 2000.0 # (12 - 10) * 1000
30+
31+
32+
def test_get_elapsed_ms_span_valid():
33+
"""Tests duration calculation with valid span times."""
34+
mock_span = mock.MagicMock(spec=trace.Span)
35+
mock_span.start_time = 1000000000 # 1s in ns
36+
mock_span.end_time = 2000000000 # 2s in ns
37+
elapsed = _instrumentation._get_elapsed_ms(mock_span, time.monotonic())
38+
assert elapsed == 1000.0 # (2 - 1) * 1000 ms
39+
40+
41+
def test_get_elapsed_ms_span_missing_start():
42+
"""Tests fallback when start_time is missing."""
43+
mock_span = mock.MagicMock(spec=trace.Span)
44+
del mock_span.start_time
45+
mock_span.end_time = 2000000000
46+
start_time = 10.0
47+
with mock.patch("time.monotonic", return_value=12.0):
48+
elapsed = _instrumentation._get_elapsed_ms(mock_span, start_time)
49+
assert elapsed == 2000.0
50+
51+
52+
def test_get_elapsed_ms_span_missing_end():
53+
"""Tests fallback when end_time is missing."""
54+
mock_span = mock.MagicMock(spec=trace.Span)
55+
mock_span.start_time = 1000000000
56+
del mock_span.end_time
57+
start_time = 10.0
58+
with mock.patch("time.monotonic", return_value=12.0):
59+
elapsed = _instrumentation._get_elapsed_ms(mock_span, start_time)
60+
assert elapsed == 2000.0
61+
62+
63+
def test_get_elapsed_ms_span_non_int_start():
64+
"""Tests fallback when start_time is not an integer."""
65+
mock_span = mock.MagicMock(spec=trace.Span)
66+
mock_span.start_time = 1000000000.0
67+
mock_span.end_time = 2000000000
68+
start_time = 10.0
69+
with mock.patch("time.monotonic", return_value=12.0):
70+
elapsed = _instrumentation._get_elapsed_ms(mock_span, start_time)
71+
assert elapsed == 2000.0
72+
73+
74+
def test_get_elapsed_ms_span_non_int_end():
75+
"""Tests fallback when end_time is not an integer."""
76+
mock_span = mock.MagicMock(spec=trace.Span)
77+
mock_span.start_time = 1000000000
78+
mock_span.end_time = 2000000000.0
79+
start_time = 10.0
80+
with mock.patch("time.monotonic", return_value=12.0):
81+
elapsed = _instrumentation._get_elapsed_ms(mock_span, start_time)
82+
assert elapsed == 2000.0

tests/unittests/telemetry/test_metrics.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,14 @@ def create_histogram_side_effect(name, **_kwargs):
7575

7676
def test_record_agent_request_size(mock_meter_setup):
7777
"""Tests record_agent_request_size records correctly."""
78+
user_content = "hello"
7879
_metrics.record_agent_request_size(
79-
"test_agent", types.Content(parts=[types.Part(text="hello")])
80+
"test_agent", types.Content(parts=[types.Part(text=user_content)])
8081
)
8182
request_size_hist = mock_meter_setup["request_size"]
8283
request_size_hist.record.assert_called_once()
8384
args, kwargs = request_size_hist.record.call_args
84-
assert args[0] == 5 # len('hello')
85+
assert args[0] == len(user_content)
8586
want_attributes = {
8687
"gen_ai.agent.name": "test_agent",
8788
}
@@ -121,7 +122,7 @@ def test_record_agent_response_size(mock_meter_setup):
121122
response_text = "response"
122123
event = mock.MagicMock(
123124
author="test_agent",
124-
content=types.Content(parts=[types.Part(text="response")]),
125+
content=types.Content(parts=[types.Part(text=response_text)]),
125126
)
126127
_metrics.record_agent_response_size("test_agent", [event])
127128
response_size_hist = mock_meter_setup["response_size"]
@@ -134,14 +135,19 @@ def test_record_agent_response_size(mock_meter_setup):
134135

135136
def test_record_agent_workflow_steps(mock_meter_setup):
136137
"""Tests record_agent_workflow_steps records correctly."""
137-
_metrics.record_agent_workflow_steps("test_agent", 5)
138+
_metrics.record_agent_workflow_steps(
139+
"test_agent",
140+
[
141+
mock.MagicMock(author="test_agent"),
142+
mock.MagicMock(author="test_agent"),
143+
mock.MagicMock(author="other_agent"),
144+
],
145+
)
138146
steps_hist = mock_meter_setup["steps"]
139147
steps_hist.record.assert_called_once()
140148
args, kwargs = steps_hist.record.call_args
141-
assert args[0] == 5
142-
want_attributes = {
143-
"gen_ai.agent.name": "test_agent",
144-
}
149+
assert args[0] == 2
150+
want_attributes = {"gen_ai.agent.name": "test_agent"}
145151
assert kwargs["attributes"] == want_attributes
146152

147153

0 commit comments

Comments
 (0)