1+ import logging
12import time
3+ import uuid
24from dataclasses import dataclass
35from enum import Enum
46from typing import Any , Callable , Dict , Iterable , List , Optional
57
68from ldclient import Context , LDClient
79
10+ logger = logging .getLogger (__name__ )
11+
812
913class FeedbackKind (Enum ):
1014 """
@@ -97,6 +101,8 @@ def __init__(
97101 self ._provider_name = provider_name
98102 self ._context = context
99103 self ._summary = LDAIMetricSummary ()
104+ self ._run_id = str (uuid .uuid4 ())
105+ self ._tracked : Dict [str , bool ] = {}
100106
101107 def __get_track_data (self , graph_key : Optional [str ] = None ) -> dict :
102108 """
@@ -106,6 +112,7 @@ def __get_track_data(self, graph_key: Optional[str] = None) -> dict:
106112 :return: Dictionary containing variation and config keys.
107113 """
108114 data = {
115+ "runId" : self ._run_id ,
109116 "variationKey" : self ._variation_key ,
110117 "configKey" : self ._config_key ,
111118 "version" : self ._version ,
@@ -124,6 +131,10 @@ def track_duration(self, duration: int, *, graph_key: Optional[str] = None) -> N
124131 :param graph_key: When set, include ``graphKey`` in the event payload
125132 (e.g. config-level metrics inside a graph).
126133 """
134+ if 'duration' in self ._tracked :
135+ logger .warning ("Duration has already been tracked for this execution." )
136+ return
137+ self ._tracked ['duration' ] = True
127138 self ._summary ._duration = duration
128139 self ._ld_client .track (
129140 "$ld:ai:duration:total" , self ._context , self .__get_track_data (graph_key ), duration
@@ -138,6 +149,10 @@ def track_time_to_first_token(
138149 :param time_to_first_token: Time to first token in milliseconds.
139150 :param graph_key: When set, include ``graphKey`` in the event payload.
140151 """
152+ if 'time_to_first_token' in self ._tracked :
153+ logger .warning ("Time to first token has already been tracked for this execution." )
154+ return
155+ self ._tracked ['time_to_first_token' ] = True
141156 self ._summary ._time_to_first_token = time_to_first_token
142157 self ._ld_client .track (
143158 "$ld:ai:tokens:ttf" ,
@@ -297,6 +312,10 @@ def track_feedback(self, feedback: Dict[str, FeedbackKind], *, graph_key: Option
297312 :param feedback: Dictionary containing feedback kind.
298313 :param graph_key: When set, include ``graphKey`` in the event payload.
299314 """
315+ if 'feedback' in self ._tracked :
316+ logger .warning ("Feedback has already been tracked for this execution." )
317+ return
318+ self ._tracked ['feedback' ] = True
300319 self ._summary ._feedback = feedback
301320 if feedback ["kind" ] == FeedbackKind .Positive :
302321 self ._ld_client .track (
@@ -319,6 +338,10 @@ def track_success(self, *, graph_key: Optional[str] = None) -> None:
319338
320339 :param graph_key: When set, include ``graphKey`` in the event payload.
321340 """
341+ if 'success' in self ._tracked :
342+ logger .warning ("Success has already been tracked for this execution." )
343+ return
344+ self ._tracked ['success' ] = True
322345 self ._summary ._success = True
323346 self ._ld_client .track (
324347 "$ld:ai:generation:success" , self ._context , self .__get_track_data (graph_key = graph_key ), 1
@@ -330,6 +353,10 @@ def track_error(self, *, graph_key: Optional[str] = None) -> None:
330353
331354 :param graph_key: When set, include ``graphKey`` in the event payload.
332355 """
356+ if 'success' in self ._tracked :
357+ logger .warning ("Success has already been tracked for this execution." )
358+ return
359+ self ._tracked ['success' ] = True
333360 self ._summary ._success = False
334361 self ._ld_client .track (
335362 "$ld:ai:generation:error" , self ._context , self .__get_track_data (graph_key = graph_key ), 1
@@ -397,6 +424,10 @@ def track_tokens(self, tokens: TokenUsage, *, graph_key: Optional[str] = None) -
397424 :param tokens: Token usage data from either custom, OpenAI, or Bedrock sources.
398425 :param graph_key: When set, include ``graphKey`` in the event payload.
399426 """
427+ if 'tokens' in self ._tracked :
428+ logger .warning ("Tokens have already been tracked for this execution." )
429+ return
430+ self ._tracked ['tokens' ] = True
400431 self ._summary ._usage = tokens
401432 td = self .__get_track_data (graph_key = graph_key )
402433 if tokens .total > 0 :
0 commit comments