@@ -963,6 +963,8 @@ def record_events_on_stop_iteration(self, transaction, request_timestamp=None):
963963
964964 try :
965965 bedrock_attrs ["duration" ] = self ._nr_ft .duration * 1000
966+ if hasattr (self , "_nr_time_to_first_token" ):
967+ bedrock_attrs ["time_to_first_token" ] = self ._nr_time_to_first_token
966968 handle_chat_completion_event (transaction , bedrock_attrs , request_timestamp )
967969 except Exception :
968970 _logger .warning (RESPONSE_PROCESSING_FAILURE_LOG_MESSAGE , exc_info = True )
@@ -1012,8 +1014,23 @@ def record_stream_chunk(self, event, transaction, request_timestamp=None):
10121014
10131015 def invoke_record_stream_chunk (self , event , transaction , request_timestamp = None ):
10141016 bedrock_attrs = getattr (self , "_nr_bedrock_attrs" , {})
1017+
1018+ # Store time to first token now, but only attach it if we successfully parse the chunk.
1019+ # Record the time here since parsing may be a bit slow, and can inflate the metric.
1020+ time_to_first_token = (
1021+ int (1000.0 * time .time ()) - self ._nr_request_timestamp
1022+ if not hasattr (self , "_nr_time_to_first_token" )
1023+ else None
1024+ )
1025+
1026+ # Load and parse the chunk to extract model specific data
10151027 chunk = json .loads (event ["chunk" ]["bytes" ].decode ("utf-8" ))
10161028 self ._nr_model_extractor (chunk , bedrock_attrs )
1029+
1030+ # Attach time to first token after parsing to ensure there are no errors
1031+ if time_to_first_token is not None :
1032+ self ._nr_time_to_first_token = time_to_first_token
1033+
10171034 # In Langchain, the bedrock iterator exits early if type is "content_block_stop".
10181035 # So we need to call the record events here since stop iteration will not be raised.
10191036 _type = chunk .get ("type" )
@@ -1027,10 +1044,14 @@ def converse_record_stream_chunk(self, event, transaction):
10271044 return
10281045
10291046 content = ((event .get ("contentBlockDelta" ) or {}).get ("delta" ) or {}).get ("text" , "" )
1047+
10301048 if "output_message_list" not in bedrock_attrs :
10311049 bedrock_attrs ["output_message_list" ] = [{"role" : "assistant" , "content" : "" }]
10321050 bedrock_attrs ["output_message_list" ][0 ]["content" ] += content
10331051
1052+ if content and not hasattr (self , "_nr_time_to_first_token" ):
1053+ self ._nr_time_to_first_token = int (1000.0 * time .time ()) - self ._nr_request_timestamp
1054+
10341055 if "messageStop" in event :
10351056 bedrock_attrs ["response.choices.finish_reason" ] = (event .get ("messageStop" ) or {}).get ("stopReason" , "" )
10361057
@@ -1198,6 +1219,7 @@ def handle_chat_completion_event(transaction, bedrock_attrs, request_timestamp=N
11981219 "response.choices.finish_reason" : bedrock_attrs .get ("response.choices.finish_reason" , None ),
11991220 "error" : bedrock_attrs .get ("error" , None ),
12001221 "timestamp" : request_timestamp or None ,
1222+ "time_to_first_token" : bedrock_attrs .get ("time_to_first_token" , None ),
12011223 }
12021224 chat_completion_summary_dict .update (llm_metadata_dict )
12031225 chat_completion_summary_dict = {k : v for k , v in chat_completion_summary_dict .items () if v is not None }
0 commit comments