7
7
from typing import Any , Dict , Iterable , List , Optional , Set , Tuple
8
8
9
9
import opentelemetry .metrics as metrics_api
10
- from opentelemetry .metrics import Counter , Meter
10
+ from opentelemetry .metrics import Counter , Histogram , Meter
11
11
12
12
from ..telemetry import metrics_constants as constants
13
13
from ..types .content import Message
@@ -121,22 +121,34 @@ class ToolMetrics:
121
121
error_count : int = 0
122
122
total_time : float = 0.0
123
123
124
- def add_call (self , tool : ToolUse , duration : float , success : bool ) -> None :
124
+ def add_call (
125
+ self ,
126
+ tool : ToolUse ,
127
+ duration : float ,
128
+ success : bool ,
129
+ metrics_client : "MetricsClient" ,
130
+ attributes : Optional [Dict [str , Any ]] = None ,
131
+ ) -> None :
125
132
"""Record a new tool call with its outcome.
126
133
127
134
Args:
128
135
tool: The tool that was called.
129
136
duration: How long the call took in seconds.
130
137
success: Whether the call was successful.
138
+ metrics_client: The metrics client for recording the metrics.
139
+ attributes: attributes of the metrics.
131
140
"""
132
141
self .tool = tool # Update with latest tool state
133
142
self .call_count += 1
134
143
self .total_time += duration
135
-
144
+ metrics_client .tool_call_count .add (1 , attributes = attributes )
145
+ metrics_client .tool_duration .record (duration , attributes = attributes )
136
146
if success :
137
147
self .success_count += 1
148
+ metrics_client .tool_success_count .add (1 , attributes = attributes )
138
149
else :
139
150
self .error_count += 1
151
+ metrics_client .tool_error_count .add (1 , attributes = attributes )
140
152
141
153
142
154
@dataclass
@@ -159,32 +171,42 @@ class EventLoopMetrics:
159
171
accumulated_usage : Usage = field (default_factory = lambda : Usage (inputTokens = 0 , outputTokens = 0 , totalTokens = 0 ))
160
172
accumulated_metrics : Metrics = field (default_factory = lambda : Metrics (latencyMs = 0 ))
161
173
162
- def start_cycle (self ) -> Tuple [float , Trace ]:
174
+ def start_cycle (self , metrics_client : "MetricsClient" ) -> Tuple [float , Trace ]:
163
175
"""Start a new event loop cycle and create a trace for it.
164
176
165
177
Returns:
166
178
A tuple containing the start time and the cycle trace object.
167
179
"""
180
+ metrics_client .event_loop_cycle_count .add (1 )
168
181
self .cycle_count += 1
169
182
start_time = time .time ()
170
183
cycle_trace = Trace (f"Cycle { self .cycle_count } " , start_time = start_time )
171
184
self .traces .append (cycle_trace )
172
185
return start_time , cycle_trace
173
186
174
- def end_cycle (self , start_time : float , cycle_trace : Trace ) -> None :
187
+ def end_cycle (self , start_time : float , cycle_trace : Trace , metrics_client : "MetricsClient" ) -> None :
175
188
"""End the current event loop cycle and record its duration.
176
189
177
190
Args:
178
191
start_time: The timestamp when the cycle started.
179
192
cycle_trace: The trace object for this cycle.
193
+ metrics_client: The metrics client for recording the metrics.
180
194
"""
195
+ metrics_client .event_loop_end_cycle .add (1 )
181
196
end_time = time .time ()
182
197
duration = end_time - start_time
198
+ metrics_client .event_loop_cycle_duration .record (duration )
183
199
self .cycle_durations .append (duration )
184
200
cycle_trace .end (end_time )
185
201
186
202
def add_tool_usage (
187
- self , tool : ToolUse , duration : float , tool_trace : Trace , success : bool , message : Message
203
+ self ,
204
+ tool : ToolUse ,
205
+ duration : float ,
206
+ tool_trace : Trace ,
207
+ success : bool ,
208
+ message : Message ,
209
+ metrics_client : "MetricsClient" ,
188
210
) -> None :
189
211
"""Record metrics for a tool invocation.
190
212
@@ -194,6 +216,7 @@ def add_tool_usage(
194
216
tool_trace: The trace object for this tool call.
195
217
success: Whether the tool call was successful.
196
218
message: The message associated with the tool call.
219
+ metrics_client: The metrics client for recording the metrics.
197
220
"""
198
221
tool_name = tool .get ("name" , "unknown_tool" )
199
222
tool_use_id = tool .get ("toolUseId" , "unknown" )
@@ -207,8 +230,16 @@ def add_tool_usage(
207
230
tool_trace .raw_name = f"{ tool_name } - { tool_use_id } "
208
231
tool_trace .add_message (message )
209
232
210
- self .tool_metrics .setdefault (tool_name , ToolMetrics (tool )).add_call (tool , duration , success )
211
-
233
+ self .tool_metrics .setdefault (tool_name , ToolMetrics (tool )).add_call (
234
+ tool ,
235
+ duration ,
236
+ success ,
237
+ metrics_client ,
238
+ attributes = {
239
+ "tool_name" : tool_name ,
240
+ "tool_use_id" : tool_use_id ,
241
+ },
242
+ )
212
243
tool_trace .end ()
213
244
214
245
def update_usage (self , usage : Usage ) -> None :
@@ -217,6 +248,7 @@ def update_usage(self, usage: Usage) -> None:
217
248
Args:
218
249
usage: The usage data to add to the accumulated totals.
219
250
"""
251
+ # metrics_client.token_usage.add(usage["totalTokens"])
220
252
self .accumulated_usage ["inputTokens" ] += usage ["inputTokens" ]
221
253
self .accumulated_usage ["outputTokens" ] += usage ["outputTokens" ]
222
254
self .accumulated_usage ["totalTokens" ] += usage ["totalTokens" ]
@@ -371,6 +403,14 @@ class MetricsClient:
371
403
_instance : Optional ["MetricsClient" ] = None
372
404
meter : Meter
373
405
strands_agent_invocation_count : Counter
406
+ event_loop_cycle_count : Counter
407
+ event_loop_start_cycle : Counter
408
+ event_loop_end_cycle : Counter
409
+ event_loop_cycle_duration : Histogram
410
+ tool_call_count : Counter
411
+ tool_success_count : Counter
412
+ tool_error_count : Counter
413
+ tool_duration : Histogram
374
414
375
415
def __new__ (cls ) -> "MetricsClient" :
376
416
"""Create or return the singleton instance of MetricsClient.
@@ -401,3 +441,21 @@ def create_instruments(self) -> None:
401
441
self .strands_agent_invocation_count = self .meter .create_counter (
402
442
name = constants .STRANDS_AGENT_INVOCATION_COUNT , unit = "Count"
403
443
)
444
+ self .event_loop_cycle_count = self .meter .create_counter (
445
+ name = constants .STRANDS_AGENT_EVENT_LOOP_CYCLE_COUNT , unit = "Count"
446
+ )
447
+ self .event_loop_start_cycle = self .meter .create_counter (
448
+ name = constants .STRANDS_AGENT_EVENT_LOOP_START_CYCLE , unit = "Count"
449
+ )
450
+ self .event_loop_end_cycle = self .meter .create_counter (
451
+ name = constants .STRANDS_AGENT_EVENT_LOOP_END_CYCLE , unit = "Count"
452
+ )
453
+ self .event_loop_cycle_duration = self .meter .create_histogram (
454
+ name = constants .STRANDS_AGENT_EVENT_LOOP_CYCLE_DURATION , unit = "s"
455
+ )
456
+ self .tool_call_count = self .meter .create_counter (name = constants .STRANDS_AGENT_TOOL_CALL_COUNT , unit = "Count" )
457
+ self .tool_success_count = self .meter .create_counter (
458
+ name = constants .STRANDS_AGENT_TOOL_SUCCESS_COUNT , unit = "Count"
459
+ )
460
+ self .tool_error_count = self .meter .create_counter (name = constants .STRANDS_AGENT_TOOL_ERROR_COUNT , unit = "Count" )
461
+ self .tool_duration = self .meter .create_histogram (name = constants .STRANDS_AGENT_TOOL_DURATION , unit = "s" )
0 commit comments