feat(client): stamp modelKey and modelVersion from _ldMeta on tracking events - #97
Conversation
…g events Gonfalon (launchdarkly/gonfalon#67230) delivers the pinned model config's identity in _ldMeta.modelKey / _ldMeta.modelVersion. extract_variation rebuilt meta from a fixed key list and dropped them; it now preserves both. Copy them onto every TrackData (execute_and_track, execute_and_stream, resolve_graph, make_track_data) so they reach every $ld:ai:* event payload and the __ld variable, matching the legacy server-ai SDK. Keys are omitted when absent; empty modelKey is treated as absent; modelVersion is int. Adds the shared model_stamps_from_meta helper (exported from the package root). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ng parent model identity Code-review follow-up to the modelKey/modelVersion passthrough: - model_stamps_from_meta no longer raises on a malformed _ldMeta.modelVersion. It accepts int (not bool), integral float and integer strings; anything else is omitted so telemetry metadata can never abort an invocation. - run_judge strips modelKey/modelVersion from the parent track_data before overlaying the judge's own, so a judge without a pinned model config no longer reports the parent's model identity. Judges with their own stamps are unchanged. Adds omit_model_stamps to utils. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
There was a problem hiding this comment.
Devin Review found 2 potential issues.
2 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)
| "version": meta.get("version", 1) if isinstance(meta, dict) else 1, | ||
| "modelName": "", | ||
| "providerName": "", | ||
| **model_stamps_from_meta(meta), |
There was a problem hiding this comment.
🟡 Graph model stamps never refresh
After a graph variation changes, graph_track_data keeps cached model stamps for the same context. Later graph events remain attributed to the old pinned model version.
Learn more
GraphInstance.invoke caches both the resolved GraphDefinition and graph_track_data by context in the context cache. There is no expiry or LaunchDarkly-driven invalidation. The cached definition also contains every node's metadata, so both graph-level and node-level model stamps remain fixed for the lifetime of that cache entry. Model reassignment or version changes therefore never reach later tracking events from the same GraphInstance and context.
Example: A graph first resolves with modelVersion=5. LaunchDarkly updates the pin to version 6, but the next invocation with the same context reuses version 5 in all affected events.
Recommended fix: Do not indefinitely cache variation-derived metadata. Reevaluate graph and node variations for each invocation, or add a bounded freshness/invalidation mechanism that refreshes the cached definition and tracking data together.
Was this helpful? React with 👍 or 👎 to provide feedback.
Parity with the Devin review on launchdarkly/js-ai-sdk#66: a truthy non-string _ldMeta.modelKey (number, object, list, bool) was passed through to tracking payloads. Only a non-empty str is accepted now. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Chatted offline about addressing bugbot feedback, but looking good 👍 |
Human
Adding modelKey and modelVersion fields to metadata
Summary
Gonfalon moved the pinned model config's identity into
_ldMeta.modelKey/_ldMeta.modelVersion(launchdarkly/gonfalon#67230). The legacypython-server-sdk-aialready stamps both fields on every$ld:ai:*event; this SDK had no reference to either, so events from it could not be cost-attributed by pinned model version (AIC-2881).extract_variationrebuiltmetafrom a fixed key list and silently dropped the two fields. It now merges them in.model_stamps_from_metahelper is the single source for all fourTrackDatabuilders:execute_and_track,execute_and_stream,_build_graph, and the sharedmake_track_dataused by the native graph adapters. Keys are omitted when absent; an emptymodelKeyis treated as absent;modelVersionacceptsint(notbool), integralfloatand integer strings, and anything else is omitted so telemetry metadata can never raise and abort an invocation.run_judgestrips the two stamps fromparent_track_databefore overlaying the judge's own, so a judge variation without a linked model config no longer reports the parent's model identity in the returnedtrack_data. Judges with their own stamps are unchanged, and the judge's emitted events were never affected.Spec:
TESTING.md§1.5, §3.10 "Model stamps on trackData", §3.13, §3.16, Appendix A.12 (launchdarkly/ai-sdks-monorepo PR opened alongside this one). Companion TypeScript PR in launchdarkly/js-ai-sdk.Test plan
config().invoke()/.stream()payloads, graph-level payloads,inspect_config/extract_variationmeta preservation,model_stamps_from_meta(including malformed inputs),omit_model_stamps,make_track_data, andrun_judgeresulttrack_data. All were confirmed to fail before the implementation.uv run pytest(1276 passing),mypy,ruff check,ruff format --checkclean.agentscenario (uv run python main.py agent sdk-test ...) against a production AI Config pinned toOpenAI.gpt-4.1-mini. Exit 0, output JSON written, and the returnedtrack_datacarriesmodelKey: "OpenAI.gpt-4.1-mini",modelVersion: 1alongsidemodelName: "gpt-4.1-mini". Failure case (doesnt-exist) exits 1 with a clean error and writes nothing.🤖 Generated with Claude Code
Note
Overview
Adds pinned model-config identity (
modelKey,modelVersion) from flag_ldMetatoVariationMeta,TrackData, and every$ld:ai:*payload so events align with Gonfalon cost attribution (matching the legacy Python AI SDK).A shared
model_stamps_from_metahelper copies the two fields when present, omits them (neverNone), treats emptymodelKeyas absent, and coercesmodelVersionsafely so bad flag payloads cannot abort an invocation. It is wired intoextract_variation,execute_and_track/execute_and_stream, graph-level track data, andmake_track_datafor native graph adapters.omit_model_stampsis used inrun_judgeso judges without their own pinned model config do not inherit the parent’s stamps in returnedtrack_data(other parent keys likegraphKeystill flow through).Docs and public exports are updated; package versions bump to 0.2.2 / 0.1.6 with broad unit tests for invoke, stream, graph events, lifecycle meta, helpers, and judge merging.
Reviewed by Cursor Bugbot for commit 4e92178. Bugbot is set up for automated code reviews on this repo. Configure here.