Skip to content

feat(server-ai): stamp modelKey and modelVersion on AI usage events (AIC-2851)#208

Merged
atornsii merged 5 commits into
mainfrom
atornsii/AIC-2851/add-support-for-model-key-version
Jul 22, 2026
Merged

feat(server-ai): stamp modelKey and modelVersion on AI usage events (AIC-2851)#208
atornsii merged 5 commits into
mainfrom
atornsii/AIC-2851/add-support-for-model-key-version

Conversation

@atornsii

@atornsii atornsii commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

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 LDAIConfigTracker 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-2851 / AIC-2849. Depends on backend payload work (AIC-2876) shipping modelKey/modelVersion on variations.

Test plan

  • uv run pytest in packages/sdk/server-ai (229 passed)
  • uv run mypy src/ldai
  • uv run isort --check --atomic src/ldai
  • uv run pycodestyle src/ldai
  • Verify against a staging environment once AIC-2876 payload is available

Made with Cursor


Open in Devin Review

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>
@jsonbailey

Copy link
Copy Markdown
Contributor

Holding based on our conversation today and we will follow up on slack before reviewing.

@atornsii
atornsii marked this pull request as ready for review July 9, 2026 20:32
@atornsii
atornsii requested a review from a team as a code owner July 9, 2026 20:32
cursor[bot]

This comment was marked as resolved.

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
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
atornsii marked this pull request as ready for review July 16, 2026 16:55
atornsii and others added 2 commits July 17, 2026 13:04
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>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

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>&nbsp;<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>&nbsp;</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>&nbsp;<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>&nbsp;</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
atornsii merged commit 66588f0 into main Jul 22, 2026
46 checks passed
@atornsii
atornsii deleted the atornsii/AIC-2851/add-support-for-model-key-version branch July 22, 2026 22:48
@github-actions github-actions Bot mentioned this pull request Jul 22, 2026
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>&nbsp;<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>&nbsp;</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 -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants