Skip to content

Commit 623c9bd

Browse files
google-genai-botcopybara-github
authored andcommitted
refactor(telemetry): change agent and tool execution duration metrics from milliseconds to seconds
PiperOrigin-RevId: 931094668
1 parent c423fcd commit 623c9bd

4 files changed

Lines changed: 45 additions & 46 deletions

File tree

src/google/adk/telemetry/_instrumentation.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
logger = logging.getLogger("google_adk." + __name__)
4141

4242

43-
def _get_elapsed_ms(
43+
def _get_elapsed_s(
4444
span: trace.Span | tracing.GenerateContentSpan | None,
4545
fallback_start: float,
4646
) -> float:
@@ -54,20 +54,20 @@ def _get_elapsed_ms(
5454
fallback_start (float): Fallback start time in seconds (monotonic).
5555
5656
Returns:
57-
float: Elapsed duration in milliseconds.
57+
float: Elapsed duration in seconds.
5858
"""
5959
if span is None:
60-
return (time.monotonic() - fallback_start) * 1000
60+
return time.monotonic() - fallback_start
6161

6262
span = span.span if hasattr(span, "span") else span
6363
start_ns = getattr(span, "start_time", None)
6464
end_ns = getattr(span, "end_time", None)
6565

6666
if isinstance(start_ns, int) and isinstance(end_ns, int):
67-
return (end_ns - start_ns) / 1e6 # Convert ns to ms
67+
return (end_ns - start_ns) / 1e9 # Convert ns to s
6868

6969
# Fallback if span times are missing
70-
return (time.monotonic() - fallback_start) * 1000
70+
return time.monotonic() - fallback_start
7171

7272

7373
@dataclasses.dataclass
@@ -93,15 +93,15 @@ def record_llm_response(
9393

9494
def _record_agent_metrics(
9595
agent_name: str,
96-
elapsed_ms: float,
96+
elapsed_s: float,
9797
user_content: Any,
9898
events: Any,
9999
caught_error: Exception | None,
100100
) -> None:
101101
try:
102102
_metrics.record_agent_invocation_duration(
103103
agent_name,
104-
elapsed_ms,
104+
elapsed_s,
105105
caught_error,
106106
)
107107
_metrics.record_agent_request_size(agent_name, user_content)
@@ -130,10 +130,9 @@ async def record_agent_invocation(
130130
caught_error = e
131131
raise
132132
finally:
133-
elapsed_ms = _get_elapsed_ms(span, start_time)
134133
_record_agent_metrics(
135134
agent.name,
136-
elapsed_ms,
135+
_get_elapsed_s(span, start_time),
137136
getattr(ctx, "user_content", None),
138137
getattr(getattr(ctx, "session", None), "events", []),
139138
caught_error,
@@ -178,7 +177,7 @@ async def record_tool_execution(
178177
_metrics.record_tool_execution_duration(
179178
tool_name=tool.name,
180179
agent_name=agent.name,
181-
elapsed_ms=_get_elapsed_ms(span, start_time),
180+
elapsed_s=_get_elapsed_s(span, start_time),
182181
error=caught_error,
183182
)
184183
except Exception: # pylint: disable=broad-exception-caught
@@ -206,13 +205,13 @@ async def record_inference_telemetry(
206205
yield tel_ctx
207206
finally:
208207
inference_error = sys.exc_info()[1]
209-
elapsed_ms = _get_elapsed_ms(tel_ctx.span, start_time)
210208
agent = invocation_context.agent
209+
elapsed_s = _get_elapsed_s(tel_ctx.span, start_time)
211210
try:
212211
if agent is not None and tracing._should_emit_native_telemetry(agent):
213212
_metrics.record_client_operation_duration(
214213
agent_name=agent.name,
215-
elapsed_ms=elapsed_ms,
214+
elapsed_s=elapsed_s,
216215
llm_request=llm_request,
217216
responses=tel_ctx.llm_responses,
218217
error=(

src/google/adk/telemetry/_metrics.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@
4343

4444
_agent_invocation_duration = meter.create_histogram(
4545
"gen_ai.agent.invocation.duration",
46-
unit="ms",
46+
unit="s",
4747
description="Duration of agent invocations.",
4848
)
4949
_tool_execution_duration = meter.create_histogram(
5050
"gen_ai.tool.execution.duration",
51-
unit="ms",
51+
unit="s",
5252
description="Duration of tool executions.",
5353
)
5454
_agent_request_size = meter.create_histogram(
@@ -74,14 +74,14 @@
7474

7575
def record_agent_invocation_duration(
7676
agent_name: str,
77-
elapsed_ms: float,
77+
elapsed_s: float,
7878
error: Exception | None = None,
7979
):
8080
"""Records the duration of the agent invocation."""
8181
attrs = {gen_ai_attributes.GEN_AI_AGENT_NAME: agent_name}
8282
if error is not None:
8383
attrs[error_attributes.ERROR_TYPE] = type(error).__name__
84-
_agent_invocation_duration.record(elapsed_ms, attributes=attrs)
84+
_agent_invocation_duration.record(elapsed_s, attributes=attrs)
8585

8686

8787
def record_agent_request_size(
@@ -116,7 +116,7 @@ def record_agent_workflow_steps(agent_name: str, events: list[Event]):
116116
def record_tool_execution_duration(
117117
tool_name: str,
118118
agent_name: str,
119-
elapsed_ms: float,
119+
elapsed_s: float,
120120
error: Exception | None = None,
121121
):
122122
"""Records the duration of the tool execution."""
@@ -126,12 +126,12 @@ def record_tool_execution_duration(
126126
}
127127
if error is not None:
128128
attrs[error_attributes.ERROR_TYPE] = type(error).__name__
129-
_tool_execution_duration.record(elapsed_ms, attributes=attrs)
129+
_tool_execution_duration.record(elapsed_s, attributes=attrs)
130130

131131

132132
def record_client_operation_duration(
133133
agent_name: str,
134-
elapsed_ms: float,
134+
elapsed_s: float,
135135
llm_request: LlmRequest,
136136
responses: list[LlmResponse],
137137
error: Exception | None = None,
@@ -154,7 +154,7 @@ def record_client_operation_duration(
154154
if error is not None:
155155
attrs[error_attributes.ERROR_TYPE] = type(error).__name__
156156

157-
_client_operation_duration.record(elapsed_ms / 1000.0, attributes=attrs)
157+
_client_operation_duration.record(elapsed_s, attributes=attrs)
158158

159159

160160
def record_client_token_usage(

tests/unittests/telemetry/test_instrumentation.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,65 +22,65 @@
2222
import pytest
2323

2424

25-
def test_get_elapsed_ms_span_none():
25+
def test_get_elapsed_s_span_none():
2626
"""Tests fallback when span is None."""
2727
start_time = 10.0
2828
with mock.patch("time.monotonic", return_value=12.0):
29-
elapsed = _instrumentation._get_elapsed_ms(None, start_time)
30-
assert elapsed == 2000.0 # (12 - 10) * 1000
29+
elapsed = _instrumentation._get_elapsed_s(None, start_time)
30+
assert elapsed == 2.0 # 12 - 10
3131

3232

33-
def test_get_elapsed_ms_span_valid():
33+
def test_get_elapsed_s_span_valid():
3434
"""Tests duration calculation with valid span times."""
3535
mock_span = mock.MagicMock(spec=trace.Span)
3636
mock_span.start_time = 1000000000 # 1s in ns
3737
mock_span.end_time = 2000000000 # 2s in ns
38-
elapsed = _instrumentation._get_elapsed_ms(mock_span, time.monotonic())
39-
assert elapsed == 1000.0 # (2 - 1) * 1000 ms
38+
elapsed = _instrumentation._get_elapsed_s(mock_span, time.monotonic())
39+
assert elapsed == 1.0 # (2 - 1) s
4040

4141

42-
def test_get_elapsed_ms_span_missing_start():
42+
def test_get_elapsed_s_span_missing_start():
4343
"""Tests fallback when start_time is missing."""
4444
mock_span = mock.MagicMock(spec=trace.Span)
4545
del mock_span.start_time
4646
mock_span.end_time = 2000000000
4747
start_time = 10.0
4848
with mock.patch("time.monotonic", return_value=12.0):
49-
elapsed = _instrumentation._get_elapsed_ms(mock_span, start_time)
50-
assert elapsed == 2000.0
49+
elapsed = _instrumentation._get_elapsed_s(mock_span, start_time)
50+
assert elapsed == 2.0
5151

5252

53-
def test_get_elapsed_ms_span_missing_end():
53+
def test_get_elapsed_s_span_missing_end():
5454
"""Tests fallback when end_time is missing."""
5555
mock_span = mock.MagicMock(spec=trace.Span)
5656
mock_span.start_time = 1000000000
5757
del mock_span.end_time
5858
start_time = 10.0
5959
with mock.patch("time.monotonic", return_value=12.0):
60-
elapsed = _instrumentation._get_elapsed_ms(mock_span, start_time)
61-
assert elapsed == 2000.0
60+
elapsed = _instrumentation._get_elapsed_s(mock_span, start_time)
61+
assert elapsed == 2.0
6262

6363

64-
def test_get_elapsed_ms_span_non_int_start():
64+
def test_get_elapsed_s_span_non_int_start():
6565
"""Tests fallback when start_time is not an integer."""
6666
mock_span = mock.MagicMock(spec=trace.Span)
6767
mock_span.start_time = 1000000000.0
6868
mock_span.end_time = 2000000000
6969
start_time = 10.0
7070
with mock.patch("time.monotonic", return_value=12.0):
71-
elapsed = _instrumentation._get_elapsed_ms(mock_span, start_time)
72-
assert elapsed == 2000.0
71+
elapsed = _instrumentation._get_elapsed_s(mock_span, start_time)
72+
assert elapsed == 2.0
7373

7474

75-
def test_get_elapsed_ms_span_non_int_end():
75+
def test_get_elapsed_s_span_non_int_end():
7676
"""Tests fallback when end_time is not an integer."""
7777
mock_span = mock.MagicMock(spec=trace.Span)
7878
mock_span.start_time = 1000000000
7979
mock_span.end_time = 2000000000.0
8080
start_time = 10.0
8181
with mock.patch("time.monotonic", return_value=12.0):
82-
elapsed = _instrumentation._get_elapsed_ms(mock_span, start_time)
83-
assert elapsed == 2000.0
82+
elapsed = _instrumentation._get_elapsed_s(mock_span, start_time)
83+
assert elapsed == 2.0
8484

8585

8686
@pytest.mark.asyncio
@@ -111,7 +111,7 @@ async def test_record_agent_invocation_tolerates_minimal_context():
111111

112112
mock_record.assert_called_once()
113113
call_args = mock_record.call_args
114-
# positional: (agent_name, elapsed_ms, user_content, events, caught_error)
114+
# positional: (agent_name, elapsed_s, user_content, events, caught_error)
115115
assert call_args.args[0] == "test_agent"
116116
assert call_args.args[2] is None # user_content default
117117
assert call_args.args[3] == [] # events default

tests/unittests/telemetry/test_metrics.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,12 @@ def test_record_agent_invocation_duration(mock_meter_setup):
107107
"""Tests record_agent_invocation_duration records correctly."""
108108
_metrics.record_agent_invocation_duration(
109109
"test_agent",
110-
1000.0,
110+
1.0,
111111
)
112112
agent_duration_hist = mock_meter_setup["agent_duration"]
113113
agent_duration_hist.record.assert_called_once()
114114
args, kwargs = agent_duration_hist.record.call_args
115-
assert args[0] == 1000.0
115+
assert args[0] == 1.0
116116
want_attributes = {"gen_ai.agent.name": "test_agent"}
117117
assert kwargs["attributes"] == want_attributes
118118

@@ -122,7 +122,7 @@ def test_record_agent_invocation_duration_with_error(mock_meter_setup):
122122
test_error = ValueError("agent failed")
123123
_metrics.record_agent_invocation_duration(
124124
"test_agent",
125-
1000.0,
125+
1.0,
126126
error=test_error,
127127
)
128128
agent_duration_hist = mock_meter_setup["agent_duration"]
@@ -170,12 +170,12 @@ def test_record_tool_execution_duration(mock_meter_setup):
170170
_metrics.record_tool_execution_duration(
171171
"test_tool",
172172
"test_agent",
173-
500.0,
173+
0.5,
174174
)
175175
tool_duration_hist = mock_meter_setup["tool_duration"]
176176
tool_duration_hist.record.assert_called_once()
177177
args, kwargs = tool_duration_hist.record.call_args
178-
assert args[0] == 500.0
178+
assert args[0] == 0.5
179179
want_attributes = {
180180
"gen_ai.agent.name": "test_agent",
181181
"gen_ai.tool.name": "test_tool",
@@ -189,7 +189,7 @@ def test_record_tool_execution_duration_with_error(mock_meter_setup):
189189
_metrics.record_tool_execution_duration(
190190
"test_tool",
191191
"test_agent",
192-
500.0,
192+
0.5,
193193
error=test_error,
194194
)
195195
tool_duration_hist = mock_meter_setup["tool_duration"]
@@ -260,7 +260,7 @@ def test_record_client_operation_duration(mock_meter_setup):
260260
)
261261
_metrics.record_client_operation_duration(
262262
agent_name="test_agent",
263-
elapsed_ms=100.0,
263+
elapsed_s=0.1,
264264
llm_request=llm_request,
265265
responses=[response],
266266
)

0 commit comments

Comments
 (0)