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 """
@@ -101,6 +105,8 @@ def __init__(
101105 self ._context = context
102106 self ._graph_key = graph_key
103107 self ._summary = LDAIMetricSummary ()
108+ self ._run_id = str (uuid .uuid4 ())
109+ self ._tracked : Dict [str , bool ] = {}
104110
105111 def __get_track_data (self ) -> dict :
106112 """
@@ -109,6 +115,7 @@ def __get_track_data(self) -> dict:
109115 :return: Dictionary containing variation and config keys.
110116 """
111117 data = {
118+ "runId" : self ._run_id ,
112119 "variationKey" : self ._variation_key ,
113120 "configKey" : self ._config_key ,
114121 "version" : self ._version ,
@@ -125,6 +132,10 @@ def track_duration(self, duration: int) -> None:
125132
126133 :param duration: Duration in milliseconds.
127134 """
135+ if 'duration' in self ._tracked :
136+ logger .warning ("Duration has already been tracked for this execution." )
137+ return
138+ self ._tracked ['duration' ] = True
128139 self ._summary ._duration = duration
129140 self ._ld_client .track (
130141 "$ld:ai:duration:total" , self ._context , self .__get_track_data (), duration
@@ -136,6 +147,10 @@ def track_time_to_first_token(self, time_to_first_token: int) -> None:
136147
137148 :param time_to_first_token: Time to first token in milliseconds.
138149 """
150+ if 'time_to_first_token' in self ._tracked :
151+ logger .warning ("Time to first token has already been tracked for this execution." )
152+ return
153+ self ._tracked ['time_to_first_token' ] = True
139154 self ._summary ._time_to_first_token = time_to_first_token
140155 self ._ld_client .track (
141156 "$ld:ai:tokens:ttf" ,
@@ -261,6 +276,10 @@ def track_feedback(self, feedback: Dict[str, FeedbackKind]) -> None:
261276
262277 :param feedback: Dictionary containing feedback kind.
263278 """
279+ if 'feedback' in self ._tracked :
280+ logger .warning ("Feedback has already been tracked for this execution." )
281+ return
282+ self ._tracked ['feedback' ] = True
264283 self ._summary ._feedback = feedback
265284 if feedback ["kind" ] == FeedbackKind .Positive :
266285 self ._ld_client .track (
@@ -281,6 +300,10 @@ def track_success(self) -> None:
281300 """
282301 Track a successful AI generation.
283302 """
303+ if 'success' in self ._tracked :
304+ logger .warning ("Success has already been tracked for this execution." )
305+ return
306+ self ._tracked ['success' ] = True
284307 self ._summary ._success = True
285308 self ._ld_client .track (
286309 "$ld:ai:generation:success" , self ._context , self .__get_track_data (), 1
@@ -290,6 +313,10 @@ def track_error(self) -> None:
290313 """
291314 Track an unsuccessful AI generation attempt.
292315 """
316+ if 'success' in self ._tracked :
317+ logger .warning ("Success has already been tracked for this execution." )
318+ return
319+ self ._tracked ['success' ] = True
293320 self ._summary ._success = False
294321 self ._ld_client .track (
295322 "$ld:ai:generation:error" , self ._context , self .__get_track_data (), 1
@@ -356,6 +383,10 @@ def track_tokens(self, tokens: TokenUsage) -> None:
356383
357384 :param tokens: Token usage data from either custom, OpenAI, or Bedrock sources.
358385 """
386+ if 'tokens' in self ._tracked :
387+ logger .warning ("Tokens have already been tracked for this execution." )
388+ return
389+ self ._tracked ['tokens' ] = True
359390 self ._summary ._usage = tokens
360391 td = self .__get_track_data ()
361392 if tokens .total > 0 :
0 commit comments