Skip to content

Commit d68bdbf

Browse files
committed
docs: Clarify runId purpose and per-method tracker semantics
Expand the LDAIConfigTracker class docstring to explain that all events emitted by a tracker share a runId and that the resumption token preserves it across processes. Document the AIConfig.create_tracker factory: each call mints a new runId, and metrics from different runIds cannot be combined. Add per-method paragraphs to each track_* method (on both LDAIConfigTracker and AIGraphTracker) describing whether they record at-most-once or may be called multiple times. Note that track_success and track_error share state, as do track_invocation_success and track_invocation_failure on the graph tracker. Document that the track_metrics_of wrappers re-run the inner block but emit no extra metric events on subsequent calls. Sweep "execution" / "invocation" docstring and comment uses that meant "a single AI run" and replace with "AI run" or "graph run". Public method and wire-format event names (track_invocation_success/failure, $ld:ai:graph:invocation_*) are unchanged. Fix the README examples to use ai_config.create_tracker() -- the ai_config.tracker attribute no longer exists. Follow-up to launchdarkly/go-server-sdk #363, applying the same doc clarifications to the python SDK.
1 parent 2efad25 commit d68bdbf

3 files changed

Lines changed: 91 additions & 23 deletions

File tree

packages/sdk/server-ai/README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ ai_config = ai_client.completion_config(
9292
if ai_config.enabled:
9393
messages = ai_config.messages
9494
model = ai_config.model
95-
tracker = ai_config.tracker
95+
tracker = ai_config.create_tracker()
9696
# Use with your AI provider
9797
```
9898

@@ -156,8 +156,9 @@ async def main():
156156
# Create LangChain model from configuration
157157
llm = await LangChainProvider.create_langchain_model(ai_config)
158158

159-
# Use with tracking (sync invoke)
160-
response = ai_config.tracker.track_metrics_of(
159+
# Use with tracking (sync invoke). Mint a tracker once per AI run.
160+
tracker = ai_config.create_tracker()
161+
response = tracker.track_metrics_of(
161162
lambda: llm.invoke(messages),
162163
lambda result: LangChainProvider.get_ai_metrics_from_response(result)
163164
)
@@ -196,7 +197,9 @@ async def main():
196197
temperature=ai_config.model.get_parameter('temperature') if ai_config.model else 0.5,
197198
)
198199

199-
result = await ai_config.tracker.track_metrics_of_async(
200+
# Mint a tracker once per AI run.
201+
tracker = ai_config.create_tracker()
202+
result = await tracker.track_metrics_of_async(
200203
call_custom_provider,
201204
map_custom_provider_metrics
202205
)

packages/sdk/server-ai/src/ldai/models.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,20 @@ class AIConfig:
203203
204204
Instances are always created by the SDK client, which injects a real
205205
``create_tracker`` factory. User code should never need to construct
206-
this directly — use the ``*Default`` variants for default values.
206+
this directly -- use the ``*Default`` variants for default values.
207+
208+
``create_tracker`` is a zero-argument callable: each invocation creates a
209+
new tracker for a fresh AI run. Each call mints a new ``runId`` (a UUIDv4)
210+
that LaunchDarkly uses to correlate the run's events in metrics views.
211+
Call it once per AI run; metrics from different ``runId``s cannot be
212+
combined.
207213
"""
208214
key: str
209215
enabled: bool
216+
#: Factory that creates a new tracker for a fresh AI run. Each call mints a
217+
#: new ``runId`` (a UUIDv4) so LaunchDarkly can correlate the run's events
218+
#: in metrics views. Call this once per AI run; metrics from different
219+
#: ``runId``s cannot be combined.
210220
create_tracker: Callable[[], Any]
211221
model: Optional[ModelConfig] = None
212222
provider: Optional[ProviderConfig] = None

packages/sdk/server-ai/src/ldai/tracker.py

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,20 @@ def resumption_token(self) -> Optional[str]:
8484
"""
8585
URL-safe Base64-encoded resumption token captured at tracker
8686
instantiation. Useful for deferred feedback flows where a downstream
87-
process needs to associate events with the original execution.
87+
process needs to associate events with the original AI run.
8888
"""
8989
return self._resumption_token
9090

9191

9292
class LDAIConfigTracker:
9393
"""
94-
Tracks configuration and usage metrics for LaunchDarkly AI operations.
94+
Records metrics for a single AI run.
95+
96+
All events a tracker emits share a runId (a UUIDv4) so LaunchDarkly can correlate
97+
them in metrics views. See individual track methods for their specific semantics.
98+
Call ``create_tracker`` on the AI Config to start a new run. A resumption token
99+
preserves the runId, so events emitted by a tracker reconstructed in another
100+
process correlate with the original run.
95101
"""
96102

97103
def __init__(
@@ -110,7 +116,7 @@ def __init__(
110116
Initialize an AI Config tracker.
111117
112118
:param ld_client: LaunchDarkly client instance.
113-
:param run_id: Unique identifier for this execution.
119+
:param run_id: Unique identifier for this AI run.
114120
:param config_key: Configuration key for tracking.
115121
:param variation_key: Variation key for tracking.
116122
:param version: Version of the variation.
@@ -162,7 +168,7 @@ def from_resumption_token(cls, token: str, ld_client: LDClient, context: Context
162168
163169
This is used for cross-process scenarios such as deferred feedback,
164170
where a different service needs to associate tracking events with the
165-
original execution's ``runId``.
171+
original AI run's ``runId``.
166172
167173
:param token: A URL-safe Base64-encoded resumption token obtained from
168174
:attr:`resumption_token`.
@@ -219,7 +225,9 @@ def __get_track_data(self) -> dict:
219225

220226
def track_duration(self, duration: int) -> None:
221227
"""
222-
Manually track the duration of an AI operation.
228+
Manually track the duration of an AI run.
229+
230+
Records at most once per Tracker; further calls are ignored.
223231
224232
:param duration: Duration in milliseconds.
225233
"""
@@ -237,7 +245,9 @@ def track_duration(self, duration: int) -> None:
237245

238246
def track_time_to_first_token(self, time_to_first_token: int) -> None:
239247
"""
240-
Manually track the time to first token of an AI operation.
248+
Manually track the time to first token of an AI run.
249+
250+
Records at most once per Tracker; further calls are ignored.
241251
242252
:param time_to_first_token: Time to first token in milliseconds.
243253
"""
@@ -258,10 +268,10 @@ def track_time_to_first_token(self, time_to_first_token: int) -> None:
258268

259269
def track_duration_of(self, func):
260270
"""
261-
Automatically track the duration of an AI operation.
271+
Automatically track the duration of an AI run.
262272
263-
An exception occurring during the execution of the function will still
264-
track the duration. The exception will be re-thrown.
273+
An exception raised while the function runs will still record the
274+
duration. The exception will be re-thrown.
265275
266276
:param func: Function to track (synchronous only).
267277
:return: Result of the tracked function.
@@ -322,6 +332,10 @@ def track_metrics_of(
322332
non-``None`` ``duration_ms`` field, that value is used as the measured duration
323333
instead of the wall-clock elapsed time.
324334
335+
Because each inner metric is at-most-once per Tracker, calling this twice
336+
on the same Tracker will run the inner block again but produce no
337+
additional metric events.
338+
325339
:param metrics_extractor: Function that extracts LDAIMetrics from the operation result
326340
:param func: Synchronous callable that runs the operation
327341
:return: The result of the operation
@@ -353,6 +367,10 @@ async def track_metrics_of_async(
353367
non-``None`` ``duration_ms`` field, that value is used as the measured duration
354368
instead of the wall-clock elapsed time.
355369
370+
Because each inner metric is at-most-once per Tracker, calling this twice
371+
on the same Tracker will run the inner block again but produce no
372+
additional metric events.
373+
356374
:param metrics_extractor: Function that extracts LDAIMetrics from the operation result
357375
:param func: Async callable or zero-arg callable that returns an awaitable when called
358376
:return: The result of the operation
@@ -375,6 +393,9 @@ def track_judge_result(self, judge_result: Any) -> None:
375393
"""
376394
Track a judge result, including the evaluation score with judge config key.
377395
396+
May be called multiple times per Tracker; each call records the
397+
provided judge result.
398+
378399
:param judge_result: JudgeResult object containing score, metric key, and success status
379400
"""
380401
if not judge_result.sampled:
@@ -393,7 +414,9 @@ def track_judge_result(self, judge_result: Any) -> None:
393414

394415
def track_feedback(self, feedback: Dict[str, FeedbackKind]) -> None:
395416
"""
396-
Track user feedback for an AI operation.
417+
Track user feedback for an AI run.
418+
419+
Records at most once per Tracker; further calls are ignored.
397420
398421
:param feedback: Dictionary containing feedback kind.
399422
"""
@@ -422,11 +445,14 @@ def track_feedback(self, feedback: Dict[str, FeedbackKind]) -> None:
422445

423446
def track_tool_calls(self, tool_calls: Iterable[str]) -> None:
424447
"""
425-
Track the tool calls made during an AI operation.
448+
Track the tool calls made during an AI run.
426449
427450
Appends to the summary's tool call list and fires a
428451
``$ld:ai:tool_call`` event for each tool.
429452
453+
May be called multiple times per Tracker; each call records an event
454+
for every tool identifier provided.
455+
430456
:param tool_calls: Tool identifiers (e.g. from a model response).
431457
"""
432458
tool_calls_list = list(tool_calls)
@@ -437,6 +463,10 @@ def track_tool_calls(self, tool_calls: Iterable[str]) -> None:
437463
def track_success(self) -> None:
438464
"""
439465
Track a successful AI generation.
466+
467+
Records at most once per Tracker. track_success and track_error share
468+
state; only one of the two can record per Tracker, and subsequent calls
469+
are ignored.
440470
"""
441471
if self._summary.success is not None:
442472
log.warning(
@@ -453,6 +483,10 @@ def track_success(self) -> None:
453483
def track_error(self) -> None:
454484
"""
455485
Track an unsuccessful AI generation attempt.
486+
487+
Records at most once per Tracker. track_success and track_error share
488+
state; only one of the two can record per Tracker, and subsequent calls
489+
are ignored.
456490
"""
457491
if self._summary.success is not None:
458492
log.warning(
@@ -492,6 +526,8 @@ def track_tokens(self, tokens: TokenUsage) -> None:
492526
"""
493527
Track token usage metrics.
494528
529+
Records at most once per Tracker; further calls are ignored.
530+
495531
:param tokens: Token usage data from either custom, OpenAI, or Bedrock sources.
496532
"""
497533
if self._summary.tokens is not None:
@@ -527,7 +563,10 @@ def track_tokens(self, tokens: TokenUsage) -> None:
527563

528564
def track_tool_call(self, tool_key: str) -> None:
529565
"""
530-
Track a tool invocation for this configuration (standalone or within a graph).
566+
Track a tool call for this configuration (standalone or within a graph).
567+
568+
May be called multiple times per Tracker; each call records a tool
569+
call event for the provided tool key.
531570
532571
:param tool_key: Identifier of the tool that was invoked.
533572
"""
@@ -625,7 +664,11 @@ def __get_track_data(self):
625664

626665
def track_invocation_success(self) -> None:
627666
"""
628-
Track a successful graph invocation.
667+
Track a successful graph run.
668+
669+
Records at most once per graph tracker. track_invocation_success and
670+
track_invocation_failure share state; only one of the two can record
671+
per graph tracker, and subsequent calls are ignored.
629672
"""
630673
if self._summary.success is not None:
631674
log.warning(
@@ -644,7 +687,11 @@ def track_invocation_success(self) -> None:
644687

645688
def track_invocation_failure(self) -> None:
646689
"""
647-
Track an unsuccessful graph invocation.
690+
Track an unsuccessful graph run.
691+
692+
Records at most once per graph tracker. track_invocation_success and
693+
track_invocation_failure share state; only one of the two can record
694+
per graph tracker, and subsequent calls are ignored.
648695
"""
649696
if self._summary.success is not None:
650697
log.warning(
@@ -663,7 +710,9 @@ def track_invocation_failure(self) -> None:
663710

664711
def track_duration(self, duration: int) -> None:
665712
"""
666-
Track the total duration of graph execution.
713+
Track the total duration of a graph run.
714+
715+
Records at most once per graph tracker; further calls are ignored.
667716
668717
:param duration: Duration in milliseconds.
669718
"""
@@ -684,7 +733,9 @@ def track_duration(self, duration: int) -> None:
684733

685734
def track_total_tokens(self, tokens: Optional[TokenUsage] = None) -> None:
686735
"""
687-
Track aggregated token usage across the entire graph invocation.
736+
Track aggregated token usage across the entire graph run.
737+
738+
Records at most once per graph tracker; further calls are ignored.
688739
689740
:param tokens: Token usage data, or ``None`` when usage is unknown.
690741
"""
@@ -707,10 +758,14 @@ def track_total_tokens(self, tokens: Optional[TokenUsage] = None) -> None:
707758

708759
def track_path(self, path: List[str]) -> None:
709760
"""
710-
Track the execution path through the graph.
761+
Track the path traversed through the graph during a graph run.
711762
712763
Appends to the summary's path list and fires a ``$ld:ai:graph:path``
713-
event. Can be called multiple times to build the path incrementally.
764+
event.
765+
766+
May be called multiple times per Tracker; each call records the
767+
provided path segment and appends it to the summary so the full
768+
path can be built incrementally.
714769
715770
:param path: An array of configuration keys representing the sequence of nodes executed during graph traversal.
716771
"""

0 commit comments

Comments
 (0)