Skip to content

Commit 6237d6c

Browse files
jsonbaileyclaude
andcommitted
refactor: Move UUID generation from tracker constructor to factory closure
The run_id parameter on LDAIConfigTracker is now required (no default). UUID generation happens in the tracker_factory closure in client.py, keeping the tracker itself a plain data holder. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 211ead4 commit 6237d6c

4 files changed

Lines changed: 42 additions & 41 deletions

File tree

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import base64
22
import json
3+
import uuid
34
from typing import Any, Callable, Dict, List, Optional, Tuple
45

56
import chevron
@@ -863,6 +864,7 @@ def tracker_factory() -> LDAIConfigTracker:
863864
model_name,
864865
provider_name,
865866
context,
867+
run_id=str(uuid.uuid4()),
866868
graph_key=graph_key,
867869
)
868870

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import json
33
import logging
44
import time
5-
import uuid
65
from dataclasses import dataclass
76
from enum import Enum
87
from typing import Any, Callable, Dict, Iterable, List, Optional
@@ -83,8 +82,8 @@ def __init__(
8382
model_name: str,
8483
provider_name: str,
8584
context: Context,
85+
run_id: str,
8686
graph_key: Optional[str] = None,
87-
run_id: Optional[str] = None,
8887
):
8988
"""
9089
Initialize an AI Config tracker.
@@ -96,9 +95,9 @@ def __init__(
9695
:param model_name: Name of the model used.
9796
:param provider_name: Name of the provider used.
9897
:param context: Context for evaluation.
98+
:param run_id: Unique identifier for this execution.
9999
:param graph_key: When set, include ``graphKey`` in all event payloads
100100
(e.g. config-level metrics inside a graph).
101-
:param run_id: Optional run ID. When not provided, a new UUID is generated.
102101
"""
103102
self._ld_client = ld_client
104103
self._variation_key = variation_key
@@ -109,7 +108,7 @@ def __init__(
109108
self._context = context
110109
self._graph_key = graph_key
111110
self._summary = LDAIMetricSummary()
112-
self._run_id = run_id or str(uuid.uuid4())
111+
self._run_id = run_id
113112
self._tracked: Dict[str, bool] = {}
114113

115114
@property

packages/sdk/server-ai/tests/test_judge.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ def context() -> Context:
5454
@pytest.fixture
5555
def tracker(client: LDClient, context: Context) -> LDAIConfigTracker:
5656
return LDAIConfigTracker(
57-
client, 'judge-v1', 'judge-config', 1, 'gpt-4', 'openai', context
57+
client, 'judge-v1', 'judge-config', 1, 'gpt-4', 'openai', context,
58+
run_id='test-run-id',
5859
)
5960

6061

packages/sdk/server-ai/tests/test_tracker.py

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def client(td: TestData) -> LDClient:
4343

4444
def test_summary_starts_empty(client: LDClient):
4545
context = Context.create("user-key")
46-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 1, "fakeModel", "fakeProvider", context)
46+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 1, "fakeModel", "fakeProvider", context, run_id="test-run-id")
4747

4848
assert tracker.get_summary().duration is None
4949
assert tracker.get_summary().feedback is None
@@ -53,7 +53,7 @@ def test_summary_starts_empty(client: LDClient):
5353

5454
def test_tracks_duration(client: LDClient):
5555
context = Context.create("user-key")
56-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
56+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
5757
tracker.track_duration(100)
5858

5959
client.track.assert_called_with( # type: ignore
@@ -68,7 +68,7 @@ def test_tracks_duration(client: LDClient):
6868

6969
def test_tracks_duration_of(client: LDClient):
7070
context = Context.create("user-key")
71-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
71+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
7272
tracker.track_duration_of(lambda: sleep(0.01))
7373

7474
calls = client.track.mock_calls # type: ignore
@@ -87,7 +87,7 @@ def test_tracks_duration_of(client: LDClient):
8787

8888
def test_tracks_time_to_first_token(client: LDClient):
8989
context = Context.create("user-key")
90-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
90+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
9191
tracker.track_time_to_first_token(100)
9292

9393
client.track.assert_called_with( # type: ignore
@@ -102,7 +102,7 @@ def test_tracks_time_to_first_token(client: LDClient):
102102

103103
def test_tracks_duration_of_with_exception(client: LDClient):
104104
context = Context.create("user-key")
105-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
105+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
106106

107107
def sleep_and_throw():
108108
sleep(0.01)
@@ -130,7 +130,7 @@ def sleep_and_throw():
130130

131131
def test_tracks_token_usage(client: LDClient):
132132
context = Context.create("user-key")
133-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
133+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
134134

135135
tokens = TokenUsage(300, 200, 100)
136136
tracker.track_tokens(tokens)
@@ -163,7 +163,7 @@ def test_tracks_token_usage(client: LDClient):
163163

164164
def test_tracks_bedrock_metrics(client: LDClient):
165165
context = Context.create("user-key")
166-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
166+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
167167

168168
bedrock_result = {
169169
"ResponseMetadata": {"HTTPStatusCode": 200},
@@ -220,7 +220,7 @@ def test_tracks_bedrock_metrics(client: LDClient):
220220

221221
def test_tracks_bedrock_metrics_with_error(client: LDClient):
222222
context = Context.create("user-key")
223-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
223+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
224224

225225
bedrock_result = {
226226
"ResponseMetadata": {"HTTPStatusCode": 500},
@@ -277,7 +277,7 @@ def test_tracks_bedrock_metrics_with_error(client: LDClient):
277277

278278
def test_tracks_openai_metrics(client: LDClient):
279279
context = Context.create("user-key")
280-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
280+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
281281

282282
class Result:
283283
def __init__(self):
@@ -330,7 +330,7 @@ def get_result():
330330

331331
def test_tracks_openai_metrics_with_exception(client: LDClient):
332332
context = Context.create("user-key")
333-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
333+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
334334

335335
def raise_exception():
336336
raise ValueError("Something went wrong")
@@ -364,7 +364,7 @@ def raise_exception():
364364
)
365365
def test_tracks_feedback(client: LDClient, kind: FeedbackKind, label: str):
366366
context = Context.create("user-key")
367-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
367+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
368368

369369
tracker.track_feedback({"kind": kind})
370370

@@ -379,7 +379,7 @@ def test_tracks_feedback(client: LDClient, kind: FeedbackKind, label: str):
379379

380380
def test_tracks_success(client: LDClient):
381381
context = Context.create("user-key")
382-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
382+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
383383
tracker.track_success()
384384

385385
calls = [
@@ -398,7 +398,7 @@ def test_tracks_success(client: LDClient):
398398

399399
def test_tracks_error(client: LDClient):
400400
context = Context.create("user-key")
401-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
401+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
402402
tracker.track_error()
403403

404404
calls = [
@@ -417,7 +417,7 @@ def test_tracks_error(client: LDClient):
417417

418418
def test_error_after_success_is_blocked(client: LDClient):
419419
context = Context.create("user-key")
420-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
420+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
421421
tracker.track_success()
422422
tracker.track_error()
423423

@@ -446,7 +446,7 @@ def _base_td() -> dict:
446446
def test_config_tracker_includes_graph_key_when_provided(client: LDClient):
447447
context = Context.create("user-key")
448448
tracker = LDAIConfigTracker(
449-
client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, graph_key="my-graph"
449+
client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id", graph_key="my-graph"
450450
)
451451
expected = {**_base_td(), "graphKey": "my-graph"}
452452
tracker.track_success()
@@ -456,7 +456,7 @@ def test_config_tracker_includes_graph_key_when_provided(client: LDClient):
456456
def test_config_tracker_track_tokens_with_graph_key(client: LDClient):
457457
context = Context.create("user-key")
458458
tracker = LDAIConfigTracker(
459-
client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, graph_key="g1"
459+
client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id", graph_key="g1"
460460
)
461461
tokens = TokenUsage(10, 4, 6)
462462
expected = {**_base_td(), "graphKey": "g1"}
@@ -467,7 +467,7 @@ def test_config_tracker_track_tokens_with_graph_key(client: LDClient):
467467
def test_config_tracker_track_feedback_with_graph_key(client: LDClient):
468468
context = Context.create("user-key")
469469
tracker = LDAIConfigTracker(
470-
client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, graph_key="gx"
470+
client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id", graph_key="gx"
471471
)
472472
expected = {**_base_td(), "graphKey": "gx"}
473473
tracker.track_feedback({"kind": FeedbackKind.Positive})
@@ -479,7 +479,7 @@ def test_config_tracker_track_feedback_with_graph_key(client: LDClient):
479479
def test_config_tracker_track_tool_call(client: LDClient):
480480
context = Context.create("user-key")
481481
tracker = LDAIConfigTracker(
482-
client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context
482+
client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id"
483483
)
484484
expected = {**_base_td(), "toolKey": "search"}
485485
tracker.track_tool_call("search")
@@ -489,7 +489,7 @@ def test_config_tracker_track_tool_call(client: LDClient):
489489
def test_config_tracker_track_tool_call_with_graph_key(client: LDClient):
490490
context = Context.create("user-key")
491491
tracker = LDAIConfigTracker(
492-
client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, graph_key="my-graph"
492+
client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id", graph_key="my-graph"
493493
)
494494
expected = {**_base_td(), "graphKey": "my-graph", "toolKey": "calc"}
495495
tracker.track_tool_call("calc")
@@ -499,7 +499,7 @@ def test_config_tracker_track_tool_call_with_graph_key(client: LDClient):
499499
def test_config_tracker_track_tool_calls(client: LDClient):
500500
context = Context.create("user-key")
501501
tracker = LDAIConfigTracker(
502-
client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, graph_key="g"
502+
client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id", graph_key="g"
503503
)
504504
tracker.track_tool_calls(["a", "b"])
505505
assert client.track.call_count == 2 # type: ignore
@@ -520,7 +520,7 @@ def test_config_tracker_track_tool_calls(client: LDClient):
520520
def test_config_tracker_track_metrics_of(client: LDClient):
521521
context = Context.create("user-key")
522522
tracker = LDAIConfigTracker(
523-
client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context
523+
client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id"
524524
)
525525

526526
def fn():
@@ -540,7 +540,7 @@ def extract(r):
540540
async def test_config_tracker_track_metrics_of_async_passes_graph_key(client: LDClient):
541541
context = Context.create("user-key")
542542
tracker = LDAIConfigTracker(
543-
client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, graph_key="gg"
543+
client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id", graph_key="gg"
544544
)
545545

546546
async def fn():
@@ -588,7 +588,7 @@ def test_ai_graph_tracker_track_total_tokens_tracks_when_positive(client: LDClie
588588

589589
def test_duplicate_track_duration_is_ignored(client: LDClient):
590590
context = Context.create("user-key")
591-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
591+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
592592
tracker.track_duration(100)
593593
tracker.track_duration(200)
594594

@@ -598,7 +598,7 @@ def test_duplicate_track_duration_is_ignored(client: LDClient):
598598

599599
def test_duplicate_track_time_to_first_token_is_ignored(client: LDClient):
600600
context = Context.create("user-key")
601-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
601+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
602602
tracker.track_time_to_first_token(50)
603603
tracker.track_time_to_first_token(75)
604604

@@ -608,7 +608,7 @@ def test_duplicate_track_time_to_first_token_is_ignored(client: LDClient):
608608

609609
def test_duplicate_track_tokens_is_ignored(client: LDClient):
610610
context = Context.create("user-key")
611-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
611+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
612612
tokens1 = TokenUsage(300, 200, 100)
613613
tokens2 = TokenUsage(600, 400, 200)
614614
tracker.track_tokens(tokens1)
@@ -621,7 +621,7 @@ def test_duplicate_track_tokens_is_ignored(client: LDClient):
621621

622622
def test_duplicate_track_success_is_ignored(client: LDClient):
623623
context = Context.create("user-key")
624-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
624+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
625625
tracker.track_success()
626626
tracker.track_success()
627627

@@ -631,7 +631,7 @@ def test_duplicate_track_success_is_ignored(client: LDClient):
631631

632632
def test_duplicate_track_error_is_ignored(client: LDClient):
633633
context = Context.create("user-key")
634-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
634+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
635635
tracker.track_error()
636636
tracker.track_error()
637637

@@ -641,7 +641,7 @@ def test_duplicate_track_error_is_ignored(client: LDClient):
641641

642642
def test_duplicate_track_feedback_is_ignored(client: LDClient):
643643
context = Context.create("user-key")
644-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
644+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
645645
tracker.track_feedback({"kind": FeedbackKind.Positive})
646646
tracker.track_feedback({"kind": FeedbackKind.Negative})
647647

@@ -651,18 +651,16 @@ def test_duplicate_track_feedback_is_ignored(client: LDClient):
651651

652652
def test_track_data_includes_run_id(client: LDClient):
653653
context = Context.create("user-key")
654-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
654+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="my-run-id")
655655
tracker.track_success()
656656

657657
track_data = client.track.call_args[0][2] # type: ignore
658-
assert "runId" in track_data
659-
assert isinstance(track_data["runId"], str)
660-
assert len(track_data["runId"]) == 36 # UUID format
658+
assert track_data["runId"] == "my-run-id"
661659

662660

663661
def test_run_id_is_consistent_across_track_calls(client: LDClient):
664662
context = Context.create("user-key")
665-
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context)
663+
tracker = LDAIConfigTracker(client, "variation-key", "config-key", 3, "fakeModel", "fakeProvider", context, run_id="test-run-id")
666664
tracker.track_success()
667665
tracker.track_duration(100)
668666

@@ -680,7 +678,7 @@ def test_resumption_token_round_trip(client: LDClient):
680678
import json
681679

682680
context = Context.create("user-key")
683-
tracker = LDAIConfigTracker(client, "var-key", "cfg-key", 5, "gpt-4", "openai", context)
681+
tracker = LDAIConfigTracker(client, "var-key", "cfg-key", 5, "gpt-4", "openai", context, run_id="test-run-id")
684682

685683
token = tracker.resumption_token
686684
# Token has no padding — add it back before decoding
@@ -698,7 +696,7 @@ def test_resumption_token_round_trip(client: LDClient):
698696

699697
def test_resumption_token_has_no_padding(client: LDClient):
700698
context = Context.create("user-key")
701-
tracker = LDAIConfigTracker(client, "var-key", "cfg-key", 1, "model", "provider", context)
699+
tracker = LDAIConfigTracker(client, "var-key", "cfg-key", 1, "model", "provider", context, run_id="test-run-id")
702700

703701
token = tracker.resumption_token
704702
assert "=" not in token
@@ -708,7 +706,7 @@ def test_resumption_token_is_url_safe_base64(client: LDClient):
708706
import base64
709707

710708
context = Context.create("user-key")
711-
tracker = LDAIConfigTracker(client, "var-key", "cfg-key", 1, "model", "provider", context)
709+
tracker = LDAIConfigTracker(client, "var-key", "cfg-key", 1, "model", "provider", context, run_id="test-run-id")
712710

713711
token = tracker.resumption_token
714712
# Should decode without error using urlsafe variant (with padding restored)
@@ -740,6 +738,7 @@ def test_client_create_tracker_from_resumption_token():
740738
# Create an original tracker and get its token
741739
original = LDAIConfigTracker(
742740
mock_client, "var-abc", "my-config", 7, "gpt-4", "openai", Context.create("original-user"),
741+
run_id="original-run-id-123",
743742
)
744743
token = original.resumption_token
745744

0 commit comments

Comments
 (0)