Skip to content

Commit 6bf91fa

Browse files
jsonbaileyclaude
andcommitted
feat: Add from_resumption_token classmethod to LDAIConfigTracker
Move the resumption token decoding logic from LDAIClient.create_tracker into a classmethod on LDAIConfigTracker per spec 1.1.20.2. The client method now delegates to LDAIConfigTracker.from_resumption_token. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 59c574e commit 6bf91fa

2 files changed

Lines changed: 44 additions & 28 deletions

File tree

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

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import base64
2-
import json
31
import uuid
42
from typing import Any, Callable, Dict, List, Optional, Tuple
53

@@ -68,9 +66,7 @@ def create_tracker(self, token: str, context: Context) -> LDAIConfigTracker:
6866
"""
6967
Reconstruct a tracker from a resumption token.
7068
71-
This is used for cross-process scenarios such as deferred feedback,
72-
where a different service needs to associate tracking events with the
73-
original execution's ``runId``.
69+
Delegates to :meth:`LDAIConfigTracker.from_resumption_token`.
7470
7571
:param token: A URL-safe Base64-encoded resumption token obtained from
7672
:attr:`LDAIConfigTracker.resumption_token`.
@@ -79,29 +75,7 @@ def create_tracker(self, token: str, context: Context) -> LDAIConfigTracker:
7975
``runId`` from the token.
8076
:raises ValueError: If the token is invalid or missing required fields.
8177
"""
82-
try:
83-
# Add padding back before decoding
84-
padded = token + "=" * (-len(token) % 4)
85-
payload = json.loads(
86-
base64.urlsafe_b64decode(padded.encode("utf-8")).decode("utf-8")
87-
)
88-
except (json.JSONDecodeError, Exception) as e:
89-
raise ValueError(f"Invalid resumption token: {e}") from e
90-
91-
for field in ("runId", "configKey", "version"):
92-
if field not in payload:
93-
raise ValueError(f"Invalid resumption token: missing required field '{field}'")
94-
95-
return LDAIConfigTracker(
96-
ld_client=self._client,
97-
run_id=payload["runId"],
98-
config_key=payload["configKey"],
99-
variation_key=payload.get("variationKey") or "",
100-
version=payload["version"],
101-
context=context,
102-
model_name="",
103-
provider_name="",
104-
)
78+
return LDAIConfigTracker.from_resumption_token(token, self._client, context)
10579

10680
def _completion_config(
10781
self,

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,48 @@ def resumption_token(self) -> str:
130130
payload = json.dumps(data)
131131
return base64.urlsafe_b64encode(payload.encode("utf-8")).rstrip(b"=").decode("utf-8")
132132

133+
@classmethod
134+
def from_resumption_token(cls, token: str, ld_client: LDClient, context: Context) -> 'LDAIConfigTracker':
135+
"""
136+
Reconstruct a tracker from a resumption token.
137+
138+
This is used for cross-process scenarios such as deferred feedback,
139+
where a different service needs to associate tracking events with the
140+
original execution's ``runId``.
141+
142+
:param token: A URL-safe Base64-encoded resumption token obtained from
143+
:attr:`resumption_token`.
144+
:param ld_client: LaunchDarkly client instance.
145+
:param context: The context to use for track events.
146+
:return: A new :class:`LDAIConfigTracker` bound to the original
147+
``runId`` from the token.
148+
:raises ValueError: If the token is invalid or missing required fields.
149+
"""
150+
try:
151+
padded = token + "=" * (-len(token) % 4)
152+
payload = json.loads(
153+
base64.urlsafe_b64decode(padded.encode("utf-8")).decode("utf-8")
154+
)
155+
except (json.JSONDecodeError, Exception) as e:
156+
raise ValueError(f"Invalid resumption token: {e}") from e
157+
158+
for field in ("runId", "configKey", "version"):
159+
if field not in payload:
160+
raise ValueError(
161+
f"Invalid resumption token: missing required field '{field}'"
162+
)
163+
164+
return cls(
165+
ld_client=ld_client,
166+
run_id=payload["runId"],
167+
config_key=payload["configKey"],
168+
variation_key=payload.get("variationKey") or "",
169+
version=payload["version"],
170+
context=context,
171+
model_name="",
172+
provider_name="",
173+
)
174+
133175
def __get_track_data(self) -> dict:
134176
"""
135177
Get tracking data for events.

0 commit comments

Comments
 (0)