feat(server-ai): stamp modelKey and modelVersion on AI usage events (AIC-2851)#208
Merged
atornsii merged 5 commits intoJul 22, 2026
Merged
Conversation
Read pinned model key/version from the AI Config variation payload and include them on tracker metric events to support version-aware cost attribution. Co-authored-by: Cursor <cursoragent@cursor.com>
This was referenced Jul 8, 2026
Contributor
|
Holding based on our conversation today and we will follow up on slack before reviewing. |
atornsii
marked this pull request as ready for review
July 9, 2026 20:32
Older variations are not backfilled with modelVersion, so defaulting to 1 mis-stamped events. Only include the field when the payload provides it and let the backend apply its version-1 fallback on read. Co-authored-by: Cursor <cursoragent@cursor.com>
atornsii
marked this pull request as draft
July 15, 2026 21:37
Gonfalon moved these two fields from the payload's model object to _ldMeta (launchdarkly/gonfalon#67230) to avoid modelVersion reading as the underlying LLM's own version. Field names are unchanged; only the JSON location moves. ModelConfig's public attributes and the tracker's stamped event fields are untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
atornsii
marked this pull request as ready for review
July 16, 2026 16:55
model_key/model_version should only be visible via the tracker's stamped event data, mirroring how variation_key/version are treated. They now flow from the already-parsed _ldMeta fields straight into the tracker factory instead of being attached to ModelConfig. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Consolidates the scattered variation_key/version/model_key/model_version locals in __evaluate into one parsed object, mirroring the internal-only meta holders in the Go, .NET, and Java SDKs. Purely internal -- the tracker factory now reads from the parsed struct instead of re-parsing the raw dict, with no change to __evaluate's return shape or any caller. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
jsonbailey
approved these changes
Jul 20, 2026
atornsii
added a commit
to launchdarkly/go-server-sdk
that referenced
this pull request
Jul 22, 2026
…850) (#414) <!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## Summary - Read `modelKey` and `modelVersion` from the AI Config variation payload (`model.modelKey` / `model.modelVersion`) and expose them on `Config` via `ModelKey()` / `ModelVersion()` accessors and builder methods - Stamp `modelKey` (when present) and `modelVersion` on all `Tracker` metric event payloads, alongside existing `modelName`/`providerName` fields - Default `modelVersion` to `1` when absent, matching variation version handling; exclude both fields from the resumption token - Additive/backward compatible — older payloads without the new fields continue to work Part of [AIC-2850](https://launchdarkly.atlassian.net/browse/AIC-2850) / [AIC-2849](https://launchdarkly.atlassian.net/browse/AIC-2849). Depends on backend payload work ([AIC-2876](https://launchdarkly.atlassian.net/browse/AIC-2876)) shipping `modelKey`/`modelVersion` on variations. ## Test plan - [x] `go build ./...` in `ldai/` - [x] `go vet ./...` in `ldai/` - [x] `go test ./...` in `ldai/` (all passed) - [ ] Verify against a staging environment once AIC-2876 payload is available **Requirements** - [x] I have added test coverage for new or changed functionality - [x] I have followed the repository's pull request submission guidelines - [x] I have validated my changes against all supported platform versions **Related issues** - [AIC-2850](https://launchdarkly.atlassian.net/browse/AIC-2850) - Parent: [AIC-2849](https://launchdarkly.atlassian.net/browse/AIC-2849) - Depends on: [AIC-2876](https://launchdarkly.atlassian.net/browse/AIC-2876) **Describe the solution you've provided** The Go AI SDK now parses `modelKey` and `modelVersion` from the variation's `model` object, exposes them through `Config` accessors, and includes them in the `trackData` stamped on every metric event. `modelVersion` is always emitted (defaulting to 1); `modelKey` is omitted when empty. Neither field is included in the resumption token. **Describe alternatives you've considered** Threading `modelKey`/`modelVersion` as explicit constructor parameters to `newTracker` (as in the Python SDK PR) was considered but rejected in favor of sourcing from `Config`, matching the existing `modelName`/`providerName` pattern. **Additional context** Mirrors [python-server-sdk-ai#208](launchdarkly/python-server-sdk-ai#208). <!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-35afdb3c-d035-4129-99d6-64ebb700f3a2"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-35afdb3c-d035-4129-99d6-64ebb700f3a2"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div> [AIC-2850]: https://launchdarkly.atlassian.net/browse/AIC-2850?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ [AIC-2849]: https://launchdarkly.atlassian.net/browse/AIC-2849?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ [AIC-2876]: https://launchdarkly.atlassian.net/browse/AIC-2876?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ [AIC-2850]: https://launchdarkly.atlassian.net/browse/AIC-2850?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Anthony Torns II <atornsii@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
atornsii
added a commit
to launchdarkly/js-core
that referenced
this pull request
Jul 22, 2026
…AIC-2858) (#1794) <!-- CURSOR_AGENT_PR_BODY_BEGIN --> **Requirements** - [x] I have added test coverage for new or changed functionality - [x] I have followed the repository's pull request submission guidelines - [x] I have validated my changes against all supported platform versions **Related issues** - [AIC-2858](https://launchdarkly.atlassian.net/browse/AIC-2858) — Make Changes to Node.js - Parent: [AIC-2849](https://launchdarkly.atlassian.net/browse/AIC-2849) — Add modelKey and modelVersion as fields to be read by SDK - Depends on backend payload work: [AIC-2876](https://launchdarkly.atlassian.net/browse/AIC-2876) - Mirrors Go: [AIC-2850](https://launchdarkly.atlassian.net/browse/AIC-2850) / Python: [PR #208](launchdarkly/python-server-sdk-ai#208) **Describe the solution you've provided** Read `modelKey` and `modelVersion` from the AI Config variation payload (`variation.model`) and stamp them on all `LDAIConfigTracker` metric event payloads, alongside existing `modelName`/`providerName` fields. - Add optional `modelKey`/`modelVersion` to `LDModelConfig` - Pass the new fields into `LDAIConfigTrackerImpl` from `LDAIClientImpl._evaluate` - Include `modelVersion` (always) and `modelKey` (when present) in `getTrackData()` - Default `modelVersion` to `1` when absent; exclude both fields from the resumption token - Additive/backward compatible — older payloads without the new fields continue to work **Describe alternatives you've considered** None — this mirrors the established pattern from the Go and Python SDK implementations. **Additional context** Tech spec: [Models Primitive Improvements](https://launchdarkly.atlassian.net/wiki/spaces/PD/pages/4977984384/Models+Primitive+Improvements+-+Tech+Spec) ## Test plan - [x] `yarn workspace @launchdarkly/server-sdk-ai test` (241 passed) - [x] `yarn workspace @launchdarkly/server-sdk-ai lint` - [x] `yarn workspaces foreach -pR --topological-dev --from '@launchdarkly/server-sdk-ai' run build` - [ ] Verify against a staging environment once AIC-2876 payload is available <!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-cf6a4de0-cf19-4491-b781-9b617da9a68d"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-cf6a4de0-cf19-4491-b781-9b617da9a68d"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div> [AIC-2858]: https://launchdarkly.atlassian.net/browse/AIC-2858?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ [AIC-2849]: https://launchdarkly.atlassian.net/browse/AIC-2849?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ [AIC-2876]: https://launchdarkly.atlassian.net/browse/AIC-2876?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ [AIC-2850]: https://launchdarkly.atlassian.net/browse/AIC-2850?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Anthony Torns II <atornsii@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
atornsii
added a commit
to launchdarkly/ruby-server-sdk-ai
that referenced
this pull request
Jul 22, 2026
…AIC-2857) (#36) <!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## Summary - Read `modelKey` and `modelVersion` from the AI Config variation payload (`variation['model']`) and expose them on `ModelConfig` - Stamp `modelKey` (when present) and `modelVersion` on all `AIConfigTracker` metric event payloads, alongside existing `modelName`/`providerName` fields - Default `modelVersion` to `1` when absent, matching variation version handling; exclude both fields from the resumption token - Additive/backward compatible — older payloads without the new fields continue to work Part of [AIC-2857](https://launchdarkly.atlassian.net/browse/AIC-2857) / [AIC-2849](https://launchdarkly.atlassian.net/browse/AIC-2849). Mirrors the Python SDK implementation in [python-server-sdk-ai#208](launchdarkly/python-server-sdk-ai#208). Depends on backend payload work ([AIC-2876](https://launchdarkly.atlassian.net/browse/AIC-2876)) shipping `modelKey`/`modelVersion` on variations. ## Test plan - [x] `bundle exec rspec` (72 passed) - [x] `bundle exec rubocop` on changed files (no offenses) - [ ] Verify against a staging environment once AIC-2876 payload is available <!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-94c75e39-b994-47a2-a2f8-59af0649aaa1"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-94c75e39-b994-47a2-a2f8-59af0649aaa1"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div> [AIC-2857]: https://launchdarkly.atlassian.net/browse/AIC-2857?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ [AIC-2849]: https://launchdarkly.atlassian.net/browse/AIC-2849?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ [AIC-2876]: https://launchdarkly.atlassian.net/browse/AIC-2876?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/launchdarkly/ruby-server-sdk-ai/pull/36" target="_blank"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open in Devin Review"> </picture> </a> <!-- devin-review-badge-end -->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
modelKeyandmodelVersionfrom the AI Config variation payload (variation['model']) and expose them onModelConfigmodelKey(when present) andmodelVersionon allLDAIConfigTrackermetric event payloads, alongside existingmodelName/providerNamefieldsmodelVersionto1when absent, matching variation version handling; exclude both fields from the resumption tokenPart of AIC-2851 / AIC-2849. Depends on backend payload work (AIC-2876) shipping
modelKey/modelVersionon variations.Test plan
uv run pytestinpackages/sdk/server-ai(229 passed)uv run mypy src/ldaiuv run isort --check --atomic src/ldaiuv run pycodestyle src/ldaiMade with Cursor