77from typing import Any , Dict , Iterable , List , Optional , Set , Tuple
88
99import opentelemetry .metrics as metrics_api
10- from opentelemetry .metrics import Counter , Meter
10+ from opentelemetry .metrics import Counter , Histogram , Meter
1111
1212from ..telemetry import metrics_constants as constants
1313from ..types .content import Message
@@ -121,22 +121,34 @@ class ToolMetrics:
121121 error_count : int = 0
122122 total_time : float = 0.0
123123
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 :
125132 """Record a new tool call with its outcome.
126133
127134 Args:
128135 tool: The tool that was called.
129136 duration: How long the call took in seconds.
130137 success: Whether the call was successful.
138+ metrics_client: The metrics client for recording the metrics.
139+ attributes: attributes of the metrics.
131140 """
132141 self .tool = tool # Update with latest tool state
133142 self .call_count += 1
134143 self .total_time += duration
135-
144+ metrics_client .tool_call_count .add (1 , attributes = attributes )
145+ metrics_client .tool_duration .record (duration , attributes = attributes )
136146 if success :
137147 self .success_count += 1
148+ metrics_client .tool_success_count .add (1 , attributes = attributes )
138149 else :
139150 self .error_count += 1
151+ metrics_client .tool_error_count .add (1 , attributes = attributes )
140152
141153
142154@dataclass
@@ -159,32 +171,42 @@ class EventLoopMetrics:
159171 accumulated_usage : Usage = field (default_factory = lambda : Usage (inputTokens = 0 , outputTokens = 0 , totalTokens = 0 ))
160172 accumulated_metrics : Metrics = field (default_factory = lambda : Metrics (latencyMs = 0 ))
161173
162- def start_cycle (self ) -> Tuple [float , Trace ]:
174+ def start_cycle (self , metrics_client : "MetricsClient" ) -> Tuple [float , Trace ]:
163175 """Start a new event loop cycle and create a trace for it.
164176
165177 Returns:
166178 A tuple containing the start time and the cycle trace object.
167179 """
180+ metrics_client .event_loop_cycle_count .add (1 )
168181 self .cycle_count += 1
169182 start_time = time .time ()
170183 cycle_trace = Trace (f"Cycle { self .cycle_count } " , start_time = start_time )
171184 self .traces .append (cycle_trace )
172185 return start_time , cycle_trace
173186
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 :
175188 """End the current event loop cycle and record its duration.
176189
177190 Args:
178191 start_time: The timestamp when the cycle started.
179192 cycle_trace: The trace object for this cycle.
193+ metrics_client: The metrics client for recording the metrics.
180194 """
195+ metrics_client .event_loop_end_cycle .add (1 )
181196 end_time = time .time ()
182197 duration = end_time - start_time
198+ metrics_client .event_loop_cycle_duration .record (duration )
183199 self .cycle_durations .append (duration )
184200 cycle_trace .end (end_time )
185201
186202 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" ,
188210 ) -> None :
189211 """Record metrics for a tool invocation.
190212
@@ -194,6 +216,7 @@ def add_tool_usage(
194216 tool_trace: The trace object for this tool call.
195217 success: Whether the tool call was successful.
196218 message: The message associated with the tool call.
219+ metrics_client: The metrics client for recording the metrics.
197220 """
198221 tool_name = tool .get ("name" , "unknown_tool" )
199222 tool_use_id = tool .get ("toolUseId" , "unknown" )
@@ -207,8 +230,16 @@ def add_tool_usage(
207230 tool_trace .raw_name = f"{ tool_name } - { tool_use_id } "
208231 tool_trace .add_message (message )
209232
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+ )
212243 tool_trace .end ()
213244
214245 def update_usage (self , usage : Usage ) -> None :
@@ -217,6 +248,7 @@ def update_usage(self, usage: Usage) -> None:
217248 Args:
218249 usage: The usage data to add to the accumulated totals.
219250 """
251+ # metrics_client.token_usage.add(usage["totalTokens"])
220252 self .accumulated_usage ["inputTokens" ] += usage ["inputTokens" ]
221253 self .accumulated_usage ["outputTokens" ] += usage ["outputTokens" ]
222254 self .accumulated_usage ["totalTokens" ] += usage ["totalTokens" ]
@@ -371,6 +403,14 @@ class MetricsClient:
371403 _instance : Optional ["MetricsClient" ] = None
372404 meter : Meter
373405 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
374414
375415 def __new__ (cls ) -> "MetricsClient" :
376416 """Create or return the singleton instance of MetricsClient.
@@ -401,3 +441,21 @@ def create_instruments(self) -> None:
401441 self .strands_agent_invocation_count = self .meter .create_counter (
402442 name = constants .STRANDS_AGENT_INVOCATION_COUNT , unit = "Count"
403443 )
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