launchdarkly-server-sdk-ai 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/PROVENANCE.md +28 -22
- data/lib/server/ai/ai_config_tracker.rb +112 -10
- data/lib/server/ai/client.rb +96 -24
- data/lib/server/ai/version.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e1bdaa1e6cd790510ce57f6e0d63b3719f571ec7b5299cecc430995158240897
|
|
4
|
+
data.tar.gz: bca165be2cc34d56f742d1d36ed89f199ba9a48c6d9e9f9fea88d1397d5af89f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3586d99728f38b474dce7a35d9721a1871610f65eb1336070cce5bad4dbff557181c4956fb25e4b6d5230e18fe0da5c43281164bdb387de4a98d3d4b3d42f09c
|
|
7
|
+
data.tar.gz: 1f80b447eb3f615380b353ec87b88fb2c997e332a6515adf60bb9322d3d76a625e4a4f061ca536c1a19ecca052d67e90a433df67cd717ad3836d14c5a7dfd8fe
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.0](https://github.com/launchdarkly/ruby-server-sdk-ai/compare/0.3.0...0.4.0) (2026-05-15)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* Replace tracker tuple from completion_config with AIConfig#create_tracker factory
|
|
9
|
+
* Track each AIConfigTracker metric at most once per tracker
|
|
10
|
+
* Add per-execution runId, at-most-once tracking, and cross-process tracker resumption
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
* Add Client#create_tracker(token:, context:) to resume a tracker across processes ([20f06f1](https://github.com/launchdarkly/ruby-server-sdk-ai/commit/20f06f1f24a42692953ae44cebea70aaa416b29d))
|
|
15
|
+
* Add per-execution runId to correlate AIConfigTracker events ([20f06f1](https://github.com/launchdarkly/ruby-server-sdk-ai/commit/20f06f1f24a42692953ae44cebea70aaa416b29d))
|
|
16
|
+
* Add per-execution runId, at-most-once tracking, and cross-process tracker resumption ([20f06f1](https://github.com/launchdarkly/ruby-server-sdk-ai/commit/20f06f1f24a42692953ae44cebea70aaa416b29d))
|
|
17
|
+
* Replace tracker tuple from completion_config with AIConfig#create_tracker factory ([20f06f1](https://github.com/launchdarkly/ruby-server-sdk-ai/commit/20f06f1f24a42692953ae44cebea70aaa416b29d))
|
|
18
|
+
* Track each AIConfigTracker metric at most once per tracker ([20f06f1](https://github.com/launchdarkly/ruby-server-sdk-ai/commit/20f06f1f24a42692953ae44cebea70aaa416b29d))
|
|
19
|
+
|
|
3
20
|
## [0.3.0](https://github.com/launchdarkly/ruby-server-sdk-ai/compare/0.2.2...0.3.0) (2026-03-05)
|
|
4
21
|
|
|
5
22
|
|
data/PROVENANCE.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
## Verifying SDK build provenance with
|
|
1
|
+
## Verifying SDK build provenance with GitHub artifact attestations
|
|
2
2
|
|
|
3
|
-
LaunchDarkly uses
|
|
3
|
+
LaunchDarkly uses [GitHub artifact attestations](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds) to help developers make their supply chain more secure by ensuring the authenticity and build integrity of our published SDK packages.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
LaunchDarkly publishes provenance about our SDK package builds using [GitHub's `actions/attest` action](https://github.com/actions/attest). These attestations are stored in GitHub's attestation API and can be verified using the [GitHub CLI](https://cli.github.com/).
|
|
6
6
|
|
|
7
|
-
To verify
|
|
7
|
+
To verify build provenance attestations, we recommend using the [GitHub CLI `attestation verify` command](https://cli.github.com/manual/gh_attestation_verify). Example usage for verifying SDK packages is included below:
|
|
8
8
|
|
|
9
9
|
<!-- x-release-please-start-version -->
|
|
10
10
|
```
|
|
11
|
-
# Set the version of the
|
|
12
|
-
VERSION=0.
|
|
11
|
+
# Set the version of the library to verify
|
|
12
|
+
VERSION=0.4.0
|
|
13
13
|
```
|
|
14
14
|
<!-- x-release-please-end -->
|
|
15
15
|
|
|
@@ -17,27 +17,33 @@ VERSION=0.3.0
|
|
|
17
17
|
# Download gem
|
|
18
18
|
$ gem fetch launchdarkly-server-sdk-ai -v $VERSION
|
|
19
19
|
|
|
20
|
-
#
|
|
21
|
-
$
|
|
22
|
-
https://github.com/launchdarkly/ruby-server-sdk-ai/releases/download/${VERSION}/launchdarkly-server-sdk-ai-${VERSION}.gem.intoto.jsonl
|
|
23
|
-
|
|
24
|
-
# Run slsa-verifier to verify provenance against package artifacts
|
|
25
|
-
$ slsa-verifier verify-artifact \
|
|
26
|
-
--provenance-path launchdarkly-server-sdk-ai-${VERSION}.gem.intoto.jsonl \
|
|
27
|
-
--source-uri github.com/launchdarkly/ruby-server-sdk-ai \
|
|
28
|
-
launchdarkly-server-sdk-ai-${VERSION}.gem
|
|
20
|
+
# Verify provenance using the GitHub CLI
|
|
21
|
+
$ gh attestation verify launchdarkly-server-sdk-ai-${VERSION}.gem --owner launchdarkly
|
|
29
22
|
```
|
|
30
23
|
|
|
31
24
|
Below is a sample of expected output.
|
|
32
|
-
|
|
25
|
+
|
|
33
26
|
```
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
27
|
+
Loaded digest sha256:... for file://launchdarkly-server-sdk-ai-0.3.0.gem
|
|
28
|
+
Loaded 1 attestation from GitHub API
|
|
29
|
+
|
|
30
|
+
The following policy criteria will be enforced:
|
|
31
|
+
- Predicate type must match:................ https://slsa.dev/provenance/v1
|
|
32
|
+
- Source Repository Owner URI must match:... https://github.com/launchdarkly
|
|
33
|
+
- Subject Alternative Name must match regex: (?i)^https://github.com/launchdarkly/
|
|
34
|
+
- OIDC Issuer must match:................... https://token.actions.githubusercontent.com
|
|
35
|
+
|
|
36
|
+
✓ Verification succeeded!
|
|
37
|
+
|
|
38
|
+
The following 1 attestation matched the policy criteria
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
- Attestation #1
|
|
41
|
+
- Build repo:..... launchdarkly/ruby-server-sdk-ai
|
|
42
|
+
- Build workflow:. .github/workflows/release-please.yml
|
|
43
|
+
- Signer repo:.... launchdarkly/ruby-server-sdk-ai
|
|
44
|
+
- Signer workflow: .github/workflows/release-please.yml
|
|
39
45
|
```
|
|
40
46
|
|
|
41
|
-
|
|
47
|
+
For more information, see [GitHub's documentation on verifying artifact attestations](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds#verifying-artifact-attestations-with-the-github-cli).
|
|
42
48
|
|
|
43
|
-
**Note:** These instructions do not apply when building our libraries from source.
|
|
49
|
+
**Note:** These instructions do not apply when building our libraries from source.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'base64'
|
|
4
|
+
require 'json'
|
|
3
5
|
require 'ldclient-rb'
|
|
4
6
|
|
|
5
7
|
module LaunchDarkly
|
|
@@ -39,7 +41,14 @@ module LaunchDarkly
|
|
|
39
41
|
end
|
|
40
42
|
|
|
41
43
|
#
|
|
42
|
-
# The AIConfigTracker
|
|
44
|
+
# The AIConfigTracker records metrics for a single AI run. Unless
|
|
45
|
+
# otherwise noted, the tracker's methods are not safe for concurrent use.
|
|
46
|
+
#
|
|
47
|
+
# All events a tracker emits share a runId (a UUIDv4) so LaunchDarkly can
|
|
48
|
+
# correlate them in metrics views. See individual track methods for their
|
|
49
|
+
# specific semantics. Call create_tracker on the AI Config to start a new
|
|
50
|
+
# run. A resumption token preserves the runId, so events emitted by a
|
|
51
|
+
# tracker reconstructed in another process share the original runId.
|
|
43
52
|
#
|
|
44
53
|
class AIConfigTracker
|
|
45
54
|
attr_reader :ld_client, :config_key, :context, :variation_key, :version, :summary, :model_name, :provider_name
|
|
@@ -55,7 +64,7 @@ module LaunchDarkly
|
|
|
55
64
|
# @param provider_name [String] The name of the AI provider
|
|
56
65
|
# @param context [LDContext] The context used for the flag evaluation
|
|
57
66
|
#
|
|
58
|
-
def initialize(ld_client:,
|
|
67
|
+
def initialize(ld_client:, run_id:, config_key:, variation_key:, version:, context:, model_name:, provider_name:)
|
|
59
68
|
@ld_client = ld_client
|
|
60
69
|
@variation_key = variation_key
|
|
61
70
|
@config_key = config_key
|
|
@@ -64,14 +73,62 @@ module LaunchDarkly
|
|
|
64
73
|
@provider_name = provider_name
|
|
65
74
|
@context = context
|
|
66
75
|
@summary = MetricSummary.new
|
|
76
|
+
@run_id = run_id
|
|
77
|
+
@logger = LaunchDarkly::Server::AI.default_logger
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
#
|
|
81
|
+
# Returns a URL-safe Base64-encoded JSON token that can be used to reconstruct
|
|
82
|
+
# a tracker in a different process (e.g. for deferred feedback).
|
|
83
|
+
#
|
|
84
|
+
# The token contains: runId, configKey, variationKey, version.
|
|
85
|
+
# modelName and providerName are NOT included.
|
|
86
|
+
#
|
|
87
|
+
# @return [String] the resumption token
|
|
88
|
+
#
|
|
89
|
+
def resumption_token
|
|
90
|
+
payload = { runId: @run_id, configKey: @config_key }
|
|
91
|
+
payload[:variationKey] = @variation_key if @variation_key && !@variation_key.empty?
|
|
92
|
+
payload[:version] = @version
|
|
93
|
+
Base64.urlsafe_encode64(JSON.generate(payload), padding: false)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
#
|
|
97
|
+
# Reconstructs a tracker from a resumption token.
|
|
98
|
+
#
|
|
99
|
+
# @param token [String] A URL-safe Base64-encoded JSON resumption token
|
|
100
|
+
# @param ld_client [LDClient] The LaunchDarkly client instance
|
|
101
|
+
# @param context [LDContext] The context for track events
|
|
102
|
+
# @return [AIConfigTracker] A new tracker instance
|
|
103
|
+
#
|
|
104
|
+
def self.from_resumption_token(token:, ld_client:, context:)
|
|
105
|
+
json = Base64.urlsafe_decode64(token)
|
|
106
|
+
payload = JSON.parse(json)
|
|
107
|
+
|
|
108
|
+
new(
|
|
109
|
+
ld_client: ld_client,
|
|
110
|
+
run_id: payload['runId'],
|
|
111
|
+
config_key: payload['configKey'],
|
|
112
|
+
variation_key: payload.fetch('variationKey', ''),
|
|
113
|
+
version: payload['version'],
|
|
114
|
+
context: context,
|
|
115
|
+
model_name: '',
|
|
116
|
+
provider_name: ''
|
|
117
|
+
)
|
|
67
118
|
end
|
|
68
119
|
|
|
69
120
|
#
|
|
70
|
-
# Track the duration of an AI
|
|
121
|
+
# Track the duration of an AI run.
|
|
122
|
+
#
|
|
123
|
+
# Records at most once per Tracker; further calls are ignored.
|
|
71
124
|
#
|
|
72
125
|
# @param duration [Integer] The duration in milliseconds
|
|
73
126
|
#
|
|
74
127
|
def track_duration(duration)
|
|
128
|
+
unless @summary.duration.nil?
|
|
129
|
+
@logger&.warn("Skipping track_duration: duration already recorded on this tracker. Call create_tracker on the AI Config for a new run. #{flag_data}")
|
|
130
|
+
return
|
|
131
|
+
end
|
|
75
132
|
@summary.duration = duration
|
|
76
133
|
@ld_client.track(
|
|
77
134
|
'$ld:ai:duration:total',
|
|
@@ -96,11 +153,18 @@ module LaunchDarkly
|
|
|
96
153
|
end
|
|
97
154
|
|
|
98
155
|
#
|
|
99
|
-
# Track time to first token
|
|
156
|
+
# Track time to first token.
|
|
157
|
+
#
|
|
158
|
+
# Records at most once per Tracker; further calls are ignored.
|
|
100
159
|
#
|
|
101
160
|
# @param duration [Integer] The duration in milliseconds
|
|
102
161
|
#
|
|
103
162
|
def track_time_to_first_token(time_to_first_token)
|
|
163
|
+
unless @summary.time_to_first_token.nil?
|
|
164
|
+
@logger&.warn("Skipping track_time_to_first_token: time-to-first-token already recorded on this tracker. " \
|
|
165
|
+
"Call create_tracker on the AI Config for a new run. #{flag_data}")
|
|
166
|
+
return
|
|
167
|
+
end
|
|
104
168
|
@summary.time_to_first_token = time_to_first_token
|
|
105
169
|
@ld_client.track(
|
|
106
170
|
'$ld:ai:tokens:ttf',
|
|
@@ -111,11 +175,17 @@ module LaunchDarkly
|
|
|
111
175
|
end
|
|
112
176
|
|
|
113
177
|
#
|
|
114
|
-
# Track user feedback
|
|
178
|
+
# Track user feedback.
|
|
179
|
+
#
|
|
180
|
+
# Records at most once per Tracker; further calls are ignored.
|
|
115
181
|
#
|
|
116
182
|
# @param kind [Symbol] The kind of feedback (:positive or :negative)
|
|
117
183
|
#
|
|
118
184
|
def track_feedback(kind:)
|
|
185
|
+
unless @summary.feedback.nil?
|
|
186
|
+
@logger&.warn("Skipping track_feedback: feedback already recorded on this tracker. Call create_tracker on the AI Config for a new run. #{flag_data}")
|
|
187
|
+
return
|
|
188
|
+
end
|
|
119
189
|
@summary.feedback = kind
|
|
120
190
|
event_name = kind == :positive ? '$ld:ai:feedback:user:positive' : '$ld:ai:feedback:user:negative'
|
|
121
191
|
@ld_client.track(
|
|
@@ -127,9 +197,17 @@ module LaunchDarkly
|
|
|
127
197
|
end
|
|
128
198
|
|
|
129
199
|
#
|
|
130
|
-
# Track a successful AI generation
|
|
200
|
+
# Track a successful AI generation.
|
|
201
|
+
#
|
|
202
|
+
# Records at most once per Tracker. track_success and track_error share
|
|
203
|
+
# state; only one of the two can record per Tracker, and subsequent
|
|
204
|
+
# calls are ignored.
|
|
131
205
|
#
|
|
132
206
|
def track_success
|
|
207
|
+
unless @summary.success.nil?
|
|
208
|
+
@logger&.warn("Skipping track_success: success/error already recorded on this tracker. Call create_tracker on the AI Config for a new run. #{flag_data}")
|
|
209
|
+
return
|
|
210
|
+
end
|
|
133
211
|
@summary.success = true
|
|
134
212
|
@ld_client.track(
|
|
135
213
|
'$ld:ai:generation:success',
|
|
@@ -140,9 +218,17 @@ module LaunchDarkly
|
|
|
140
218
|
end
|
|
141
219
|
|
|
142
220
|
#
|
|
143
|
-
# Track an error in AI generation
|
|
221
|
+
# Track an error in AI generation.
|
|
222
|
+
#
|
|
223
|
+
# Records at most once per Tracker. track_success and track_error share
|
|
224
|
+
# state; only one of the two can record per Tracker, and subsequent
|
|
225
|
+
# calls are ignored.
|
|
144
226
|
#
|
|
145
227
|
def track_error
|
|
228
|
+
unless @summary.success.nil?
|
|
229
|
+
@logger&.warn("Skipping track_error: success/error already recorded on this tracker. Call create_tracker on the AI Config for a new run. #{flag_data}")
|
|
230
|
+
return
|
|
231
|
+
end
|
|
146
232
|
@summary.success = false
|
|
147
233
|
@ld_client.track(
|
|
148
234
|
'$ld:ai:generation:error',
|
|
@@ -153,11 +239,17 @@ module LaunchDarkly
|
|
|
153
239
|
end
|
|
154
240
|
|
|
155
241
|
#
|
|
156
|
-
# Track token usage
|
|
242
|
+
# Track token usage.
|
|
243
|
+
#
|
|
244
|
+
# Records at most once per Tracker; further calls are ignored.
|
|
157
245
|
#
|
|
158
246
|
# @param token_usage [TokenUsage] An object containing token usage details
|
|
159
247
|
#
|
|
160
248
|
def track_tokens(token_usage)
|
|
249
|
+
unless @summary.usage.nil?
|
|
250
|
+
@logger&.warn("Skipping track_tokens: token usage already recorded on this tracker. Call create_tracker on the AI Config for a new run. #{flag_data}")
|
|
251
|
+
return
|
|
252
|
+
end
|
|
161
253
|
@summary.usage = token_usage
|
|
162
254
|
if token_usage.total.positive?
|
|
163
255
|
@ld_client.track(
|
|
@@ -191,6 +283,10 @@ module LaunchDarkly
|
|
|
191
283
|
# If the provided block raises, this method will also raise.
|
|
192
284
|
# A failed operation will not have any token usage data.
|
|
193
285
|
#
|
|
286
|
+
# Subsequent calls re-run the inner block but emit only metrics not
|
|
287
|
+
# already recorded on this Tracker. Call create_tracker on the AI
|
|
288
|
+
# Config to start a new run.
|
|
289
|
+
#
|
|
194
290
|
# @yield The block to track.
|
|
195
291
|
# @return The result of the tracked block.
|
|
196
292
|
#
|
|
@@ -208,6 +304,10 @@ module LaunchDarkly
|
|
|
208
304
|
# Track AWS Bedrock conversation operations.
|
|
209
305
|
# This method tracks the duration, token usage, and success/error status.
|
|
210
306
|
#
|
|
307
|
+
# Subsequent calls re-run the inner block but emit only metrics not
|
|
308
|
+
# already recorded on this Tracker. Call create_tracker on the AI
|
|
309
|
+
# Config to start a new run.
|
|
310
|
+
#
|
|
211
311
|
# @yield The block to track.
|
|
212
312
|
# @return [Hash] The original response hash.
|
|
213
313
|
#
|
|
@@ -222,13 +322,15 @@ module LaunchDarkly
|
|
|
222
322
|
end
|
|
223
323
|
|
|
224
324
|
private def flag_data
|
|
225
|
-
{
|
|
226
|
-
|
|
325
|
+
data = {
|
|
326
|
+
runId: @run_id,
|
|
227
327
|
configKey: @config_key,
|
|
228
328
|
version: @version,
|
|
229
329
|
modelName: @model_name,
|
|
230
330
|
providerName: @provider_name,
|
|
231
331
|
}
|
|
332
|
+
data[:variationKey] = @variation_key if @variation_key && !@variation_key.empty?
|
|
333
|
+
data
|
|
232
334
|
end
|
|
233
335
|
|
|
234
336
|
private def openai_to_token_usage(usage)
|
data/lib/server/ai/client.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'ldclient-rb'
|
|
4
4
|
require 'mustache'
|
|
5
|
+
require 'securerandom'
|
|
5
6
|
require_relative 'ai_config_tracker'
|
|
6
7
|
require_relative 'sdk_info'
|
|
7
8
|
|
|
@@ -100,26 +101,65 @@ module LaunchDarkly
|
|
|
100
101
|
end
|
|
101
102
|
|
|
102
103
|
#
|
|
103
|
-
# The
|
|
104
|
+
# The AIConfigDefault class represents a user-provided fallback AI
|
|
105
|
+
# configuration.
|
|
104
106
|
#
|
|
105
|
-
class
|
|
106
|
-
|
|
107
|
+
# Pass an instance of this class as the +default:+ parameter to
|
|
108
|
+
# {Client#completion_config} to control the fallback values when a flag
|
|
109
|
+
# is not found or cannot be evaluated.
|
|
110
|
+
#
|
|
111
|
+
# This is an input-only type: it is what an application supplies to the
|
|
112
|
+
# SDK, never what the SDK returns. The SDK always returns an
|
|
113
|
+
# {AIConfig}, which carries a tracker factory; AIConfigDefault does not.
|
|
114
|
+
#
|
|
115
|
+
class AIConfigDefault
|
|
116
|
+
attr_reader :enabled, :messages, :model, :provider
|
|
107
117
|
|
|
108
|
-
|
|
118
|
+
#
|
|
119
|
+
# Returns a new AIConfigDefault with enabled: false and no model,
|
|
120
|
+
# messages, or provider.
|
|
121
|
+
#
|
|
122
|
+
# @return [AIConfigDefault] a new disabled fallback config
|
|
123
|
+
#
|
|
124
|
+
def self.disabled
|
|
125
|
+
new(enabled: false)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def initialize(enabled: false, model: nil, messages: nil, provider: nil)
|
|
109
129
|
@enabled = enabled
|
|
110
130
|
@messages = messages
|
|
111
|
-
@tracker = tracker
|
|
112
131
|
@model = model
|
|
113
132
|
@provider = provider
|
|
114
133
|
end
|
|
115
134
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
135
|
+
def to_h
|
|
136
|
+
{
|
|
137
|
+
_ldMeta: {
|
|
138
|
+
enabled: @enabled || false,
|
|
139
|
+
},
|
|
140
|
+
messages: @messages.is_a?(Array) ? @messages.map { |msg| msg&.to_h } : nil,
|
|
141
|
+
model: @model&.to_h,
|
|
142
|
+
provider: @provider&.to_h,
|
|
143
|
+
}
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
#
|
|
148
|
+
# The AIConfig class represents an AI configuration returned by the SDK.
|
|
149
|
+
#
|
|
150
|
+
# Instances are created by {Client#completion_config} and always carry a
|
|
151
|
+
# tracker factory; see {#create_tracker}. Do not instantiate directly.
|
|
152
|
+
# For application-supplied fallback values, use {AIConfigDefault}.
|
|
153
|
+
#
|
|
154
|
+
class AIConfig
|
|
155
|
+
attr_reader :enabled, :messages, :model, :provider
|
|
156
|
+
|
|
157
|
+
def initialize(tracker_factory:, enabled: nil, model: nil, messages: nil, provider: nil)
|
|
158
|
+
@enabled = enabled
|
|
159
|
+
@messages = messages
|
|
160
|
+
@model = model
|
|
161
|
+
@provider = provider
|
|
162
|
+
@tracker_factory = tracker_factory
|
|
123
163
|
end
|
|
124
164
|
|
|
125
165
|
def to_h
|
|
@@ -132,6 +172,18 @@ module LaunchDarkly
|
|
|
132
172
|
provider: @provider&.to_h,
|
|
133
173
|
}
|
|
134
174
|
end
|
|
175
|
+
|
|
176
|
+
#
|
|
177
|
+
# Creates a new tracker for a fresh AI run. Each call mints a new
|
|
178
|
+
# runId (a UUIDv4) that LaunchDarkly uses to correlate the tracker's
|
|
179
|
+
# events in metrics views. Call this once per AI run; metrics from
|
|
180
|
+
# different runIds cannot be combined.
|
|
181
|
+
#
|
|
182
|
+
# @return [AIConfigTracker] a new tracker instance
|
|
183
|
+
#
|
|
184
|
+
def create_tracker
|
|
185
|
+
@tracker_factory.call
|
|
186
|
+
end
|
|
135
187
|
end
|
|
136
188
|
|
|
137
189
|
#
|
|
@@ -172,14 +224,26 @@ module LaunchDarkly
|
|
|
172
224
|
#
|
|
173
225
|
# @param key [String] The key of the configuration flag
|
|
174
226
|
# @param context [LDContext] The context used when evaluating the flag
|
|
175
|
-
# @param default [
|
|
227
|
+
# @param default [AIConfigDefault] The default value to use if the flag is not found
|
|
176
228
|
# @param variables [Hash] Optional variables for rendering messages
|
|
177
229
|
# @return [AIConfig] An AIConfig instance containing the configuration data
|
|
178
230
|
#
|
|
179
231
|
def completion_config(key:, context:, default: nil, variables: nil)
|
|
180
232
|
@ld_client.track(TRACK_USAGE_COMPLETION_CONFIG, context, key, 1)
|
|
181
233
|
|
|
182
|
-
_completion_config(key:, context:, default: default ||
|
|
234
|
+
_completion_config(key:, context:, default: default || AIConfigDefault.disabled, variables:)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
#
|
|
238
|
+
# Reconstructs a tracker from a resumption token, allowing deferred tracking
|
|
239
|
+
# (e.g. feedback from a different process).
|
|
240
|
+
#
|
|
241
|
+
# @param token [String] A resumption token obtained from AIConfigTracker#resumption_token
|
|
242
|
+
# @param context [LDContext] The context for track events
|
|
243
|
+
# @return [AIConfigTracker] A new tracker instance
|
|
244
|
+
#
|
|
245
|
+
def create_tracker(token:, context:)
|
|
246
|
+
AIConfigTracker.from_resumption_token(token: token, ld_client: @ld_client, context: context)
|
|
183
247
|
end
|
|
184
248
|
|
|
185
249
|
# @deprecated Use {#completion_config} instead.
|
|
@@ -226,20 +290,28 @@ module LaunchDarkly
|
|
|
226
290
|
)
|
|
227
291
|
end
|
|
228
292
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
293
|
+
variation_key = variation.dig(:_ldMeta, :variationKey) || ''
|
|
294
|
+
version = variation.dig(:_ldMeta, :version) || 1
|
|
295
|
+
model_name = model&.name || ''
|
|
296
|
+
provider_name = provider_config&.name || ''
|
|
297
|
+
|
|
298
|
+
tracker_factory = lambda {
|
|
299
|
+
LaunchDarkly::Server::AI::AIConfigTracker.new(
|
|
300
|
+
ld_client: @ld_client,
|
|
301
|
+
run_id: SecureRandom.uuid,
|
|
302
|
+
variation_key: variation_key,
|
|
303
|
+
config_key: key,
|
|
304
|
+
version: version,
|
|
305
|
+
model_name: model_name,
|
|
306
|
+
provider_name: provider_name,
|
|
307
|
+
context: context
|
|
308
|
+
)
|
|
309
|
+
}
|
|
238
310
|
|
|
239
311
|
AIConfig.new(
|
|
240
312
|
enabled: variation.dig(:_ldMeta, :enabled) || false,
|
|
241
313
|
messages:,
|
|
242
|
-
|
|
314
|
+
tracker_factory:,
|
|
243
315
|
model:,
|
|
244
316
|
provider: provider_config
|
|
245
317
|
)
|
data/lib/server/ai/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: launchdarkly-server-sdk-ai
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- LaunchDarkly
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-05-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: base64
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
13
27
|
- !ruby/object:Gem::Dependency
|
|
14
28
|
name: launchdarkly-server-sdk
|
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|