From 5764d199df94f85f46cd9d194aca132bfb1a04ee Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 22:13:14 +0000 Subject: [PATCH 1/3] [AIC-1636] fix: Improve usage reporting Co-Authored-By: jbailey@launchdarkly.com --- lib/server/ai/client.rb | 30 ++++++++++++++++++++-- spec/server/ai/client_spec.rb | 36 +++++++++++++++++++-------- spec/server/ai/config_tracker_spec.rb | 9 ++++--- 3 files changed, 58 insertions(+), 17 deletions(-) diff --git a/lib/server/ai/client.rb b/lib/server/ai/client.rb index 63b063a..a103b9b 100644 --- a/lib/server/ai/client.rb +++ b/lib/server/ai/client.rb @@ -3,6 +3,7 @@ require 'ldclient-rb' require 'mustache' require_relative 'ai_config_tracker' +require_relative 'version' module LaunchDarkly # @@ -127,6 +128,15 @@ def to_h # # The Client class is the main entry point for the LaunchDarkly AI SDK. # + TRACK_SDK_INFO = '$ld:ai:sdk-info' + TRACK_USAGE_COMPLETION_CONFIG = '$ld:ai:usage:completion-config' + + INIT_TRACK_CONTEXT = LaunchDarkly::LDContext.create({ + kind: 'user', + key: 'ld-internal-tracking', + anonymous: true, + }) + class Client attr_reader :logger, :ld_client @@ -135,6 +145,17 @@ def initialize(ld_client) @ld_client = ld_client @logger = LaunchDarkly::Server::AI.default_logger + + @ld_client.track( + TRACK_SDK_INFO, + INIT_TRACK_CONTEXT, + { + aiSdkName: 'launchdarkly-server-sdk-ai', + aiSdkVersion: LaunchDarkly::Server::AI::VERSION, + aiSdkLanguage: 'ruby', + }, + 1 + ) end # @@ -146,8 +167,8 @@ def initialize(ld_client) # @param variables [Hash] Optional variables for rendering messages # @return [AIConfig] An AIConfig instance containing the configuration data # - def config(config_key, context, default_value = nil, variables = nil) - @ld_client.track('$ld:ai:config:function:single', context, config_key, 1) + def completion_config(config_key, context, default_value = nil, variables = nil) + @ld_client.track(TRACK_USAGE_COMPLETION_CONFIG, context, config_key, 1) variation = @ld_client.variation( config_key, @@ -203,6 +224,11 @@ def config(config_key, context, default_value = nil, variables = nil) provider: provider_config ) end + # @deprecated Use {#completion_config} instead. + def config(config_key, context, default_value = nil, variables = nil) + warn '[DEPRECATION] `config` is deprecated. Use `completion_config` instead.' + completion_config(config_key, context, default_value, variables) + end end end end diff --git a/spec/server/ai/client_spec.rb b/spec/server/ai/client_spec.rb index 0d6515d..86d281b 100644 --- a/spec/server/ai/client_spec.rb +++ b/spec/server/ai/client_spec.rb @@ -129,9 +129,23 @@ it 'raises an error if LDClient is not an instance of LaunchDarkly::LDClient' do expect { described_class.new('not a client') }.to raise_error(ArgumentError, 'LDClient instance is required') end + + it 'tracks sdk-info on construction' do + expect(ld_client).to receive(:track).with( + '$ld:ai:sdk-info', + an_object_satisfying { |ctx| ctx.key == 'ld-internal-tracking' && ctx.get_value(:anonymous) == true }, + { + aiSdkName: 'launchdarkly-server-sdk-ai', + aiSdkVersion: LaunchDarkly::Server::AI::VERSION, + aiSdkLanguage: 'ruby', + }, + 1 + ) + described_class.new(ld_client) + end end - describe '#config' do + describe '#completion_config' do it 'uses default config on invalid flag' do context = LaunchDarkly::LDContext.create({ key: 'user-key', kind: 'user' }) model = LaunchDarkly::Server::AI::ModelConfig.new(name: 'fakeModel', @@ -146,7 +160,7 @@ ) variables = { 'name' => 'World' } - config = ai_client.config('missing-flag', context, default_config, variables) + config = ai_client.completion_config('missing-flag', context, default_config, variables) expect(config.messages).not_to be_nil expect(config.messages.length).to be > 0 expect(config.messages[0].content).to eq('Hello, World!') @@ -167,7 +181,7 @@ ) variables = { 'name' => 'World' } - config = ai_client.config('model-config', context, default_value, variables) + config = ai_client.completion_config('model-config', context, default_value, variables) expect(config.messages).not_to be_nil expect(config.messages.length).to be > 0 expect(config.messages[0].content).to eq('Hello, World!') @@ -187,7 +201,7 @@ messages: [] ) - config = ai_client.config('model-config', context, default_value, {}) + config = ai_client.completion_config('model-config', context, default_value, {}) expect(config.messages).not_to be_nil expect(config.messages.length).to be > 0 @@ -209,7 +223,7 @@ ) variables = { 'name' => 'World' } - config = ai_client.config('model-config', context, default_value, variables) + config = ai_client.completion_config('model-config', context, default_value, variables) expect(config.provider).not_to be_nil expect(config.provider.name).to eq('fakeProvider') @@ -229,7 +243,7 @@ ) variables = { 'name' => 'World' } - config = ai_client.config('ctx-interpolation', context, default_value, variables) + config = ai_client.completion_config('ctx-interpolation', context, default_value, variables) expect(config.messages).not_to be_nil expect(config.messages.length).to be > 0 @@ -255,7 +269,7 @@ ) variables = { 'name' => 'World' } - config = ai_client.config('multi-ctx-interpolation', context, default_value, variables) + config = ai_client.completion_config('multi-ctx-interpolation', context, default_value, variables) expect(config.messages).not_to be_nil expect(config.messages.length).to be > 0 @@ -278,7 +292,7 @@ ) variables = { 'name' => 'World', 'day' => 'Monday' } - config = ai_client.config('multiple-messages', context, default_value, variables) + config = ai_client.completion_config('multiple-messages', context, default_value, variables) expect(config.messages).not_to be_nil expect(config.messages.length).to be > 0 @@ -300,7 +314,7 @@ messages: [] ) - config = ai_client.config('off-config', context, default_value, {}) + config = ai_client.completion_config('off-config', context, default_value, {}) expect(config.model).not_to be_nil expect(config.enabled).to be false @@ -317,7 +331,7 @@ messages: [] ) - config = ai_client.config('initial-config-disabled', context, default_value, {}) + config = ai_client.completion_config('initial-config-disabled', context, default_value, {}) expect(config.enabled).to be false expect(config.model).to be_nil @@ -333,7 +347,7 @@ messages: [] ) - config = ai_client.config('initial-config-enabled', context, default_value, {}) + config = ai_client.completion_config('initial-config-enabled', context, default_value, {}) expect(config.enabled).to be true expect(config.model).to be_nil diff --git a/spec/server/ai/config_tracker_spec.rb b/spec/server/ai/config_tracker_spec.rb index 4a8373a..eb1f4ba 100644 --- a/spec/server/ai/config_tracker_spec.rb +++ b/spec/server/ai/config_tracker_spec.rb @@ -378,10 +378,11 @@ end end - describe 'config method tracking' do - it 'calls track with correct parameters when config is called' do + describe 'completion_config method tracking' do + it 'calls track with correct parameters when completion_config is called' do + allow(ld_client).to receive(:track) expect(ld_client).to receive(:track).with( - '$ld:ai:config:function:single', + '$ld:ai:usage:completion-config', context, 'test-config-key', 1 @@ -396,7 +397,7 @@ client = LaunchDarkly::Server::AI::Client.new(ld_client) default_value = LaunchDarkly::Server::AI::AIConfig.new(enabled: false) - client.config('test-config-key', context, default_value) + client.completion_config('test-config-key', context, default_value) end end end From 9aaa9c0f9449da368148df2f36ad1e4468209a97 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 23:14:40 +0000 Subject: [PATCH 2/3] [AIC-1636] refactor: extract SDK info into dedicated sdk_info module Co-Authored-By: jbailey@launchdarkly.com --- lib/launchdarkly-server-sdk-ai.rb | 1 + lib/server/ai/client.rb | 7 ++++--- lib/server/ai/sdk_info.rb | 12 ++++++++++++ spec/server/ai/client_spec.rb | 4 ++-- 4 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 lib/server/ai/sdk_info.rb diff --git a/lib/launchdarkly-server-sdk-ai.rb b/lib/launchdarkly-server-sdk-ai.rb index 1abaeec..3577b19 100644 --- a/lib/launchdarkly-server-sdk-ai.rb +++ b/lib/launchdarkly-server-sdk-ai.rb @@ -4,6 +4,7 @@ require 'mustache' require 'server/ai/version' +require 'server/ai/sdk_info' require 'server/ai/client' require 'server/ai/ai_config_tracker' diff --git a/lib/server/ai/client.rb b/lib/server/ai/client.rb index a103b9b..0ef0313 100644 --- a/lib/server/ai/client.rb +++ b/lib/server/ai/client.rb @@ -3,7 +3,7 @@ require 'ldclient-rb' require 'mustache' require_relative 'ai_config_tracker' -require_relative 'version' +require_relative 'sdk_info' module LaunchDarkly # @@ -150,9 +150,9 @@ def initialize(ld_client) TRACK_SDK_INFO, INIT_TRACK_CONTEXT, { - aiSdkName: 'launchdarkly-server-sdk-ai', + aiSdkName: LaunchDarkly::Server::AI::SDK_NAME, aiSdkVersion: LaunchDarkly::Server::AI::VERSION, - aiSdkLanguage: 'ruby', + aiSdkLanguage: LaunchDarkly::Server::AI::SDK_LANGUAGE, }, 1 ) @@ -224,6 +224,7 @@ def completion_config(config_key, context, default_value = nil, variables = nil) provider: provider_config ) end + # @deprecated Use {#completion_config} instead. def config(config_key, context, default_value = nil, variables = nil) warn '[DEPRECATION] `config` is deprecated. Use `completion_config` instead.' diff --git a/lib/server/ai/sdk_info.rb b/lib/server/ai/sdk_info.rb new file mode 100644 index 0000000..c27b97f --- /dev/null +++ b/lib/server/ai/sdk_info.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require_relative 'version' + +module LaunchDarkly + module Server + module AI + SDK_NAME = 'launchdarkly-server-sdk-ai' + SDK_LANGUAGE = 'ruby' + end + end +end diff --git a/spec/server/ai/client_spec.rb b/spec/server/ai/client_spec.rb index 86d281b..84d438c 100644 --- a/spec/server/ai/client_spec.rb +++ b/spec/server/ai/client_spec.rb @@ -135,9 +135,9 @@ '$ld:ai:sdk-info', an_object_satisfying { |ctx| ctx.key == 'ld-internal-tracking' && ctx.get_value(:anonymous) == true }, { - aiSdkName: 'launchdarkly-server-sdk-ai', + aiSdkName: LaunchDarkly::Server::AI::SDK_NAME, aiSdkVersion: LaunchDarkly::Server::AI::VERSION, - aiSdkLanguage: 'ruby', + aiSdkLanguage: LaunchDarkly::Server::AI::SDK_LANGUAGE, }, 1 ) From abddf389e96ac310fbfd84dcb9569f92a7fb807a Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 20 Feb 2026 18:17:46 +0000 Subject: [PATCH 3/3] [AIC-1636] fix: update context kind to 'ld-ai' per sdk-specs #134 Co-Authored-By: jbailey@launchdarkly.com --- lib/server/ai/client.rb | 2 +- spec/server/ai/client_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server/ai/client.rb b/lib/server/ai/client.rb index 0ef0313..49788ad 100644 --- a/lib/server/ai/client.rb +++ b/lib/server/ai/client.rb @@ -132,7 +132,7 @@ def to_h TRACK_USAGE_COMPLETION_CONFIG = '$ld:ai:usage:completion-config' INIT_TRACK_CONTEXT = LaunchDarkly::LDContext.create({ - kind: 'user', + kind: 'ld-ai', key: 'ld-internal-tracking', anonymous: true, }) diff --git a/spec/server/ai/client_spec.rb b/spec/server/ai/client_spec.rb index 84d438c..7db0e97 100644 --- a/spec/server/ai/client_spec.rb +++ b/spec/server/ai/client_spec.rb @@ -133,7 +133,7 @@ it 'tracks sdk-info on construction' do expect(ld_client).to receive(:track).with( '$ld:ai:sdk-info', - an_object_satisfying { |ctx| ctx.key == 'ld-internal-tracking' && ctx.get_value(:anonymous) == true }, + an_object_satisfying { |ctx| ctx.kind == 'ld-ai' && ctx.key == 'ld-internal-tracking' && ctx.get_value(:anonymous) == true }, { aiSdkName: LaunchDarkly::Server::AI::SDK_NAME, aiSdkVersion: LaunchDarkly::Server::AI::VERSION,