Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/launchdarkly-server-sdk-ai.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down
36 changes: 34 additions & 2 deletions lib/server/ai/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'ldclient-rb'
require 'mustache'
require_relative 'ai_config_tracker'
require_relative 'sdk_info'

module LaunchDarkly
#
Expand Down Expand Up @@ -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: 'ld_ai',
key: 'ld-internal-tracking',
anonymous: true,
})

class Client
attr_reader :logger, :ld_client

Expand All @@ -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::AI::SDK_NAME,
aiSdkVersion: LaunchDarkly::Server::AI::VERSION,
aiSdkLanguage: LaunchDarkly::Server::AI::SDK_LANGUAGE,
},
1
)
end

#
Expand All @@ -146,9 +167,21 @@ def initialize(ld_client)
# @param variables [Hash] Optional variables for rendering messages
# @return [AIConfig] An AIConfig instance containing the configuration data
#
def completion_config(config_key, context, default_value = nil, variables = nil)
@ld_client.track(TRACK_USAGE_COMPLETION_CONFIG, context, config_key, 1)

_completion_config(config_key, context, default_value, variables)
end

# @deprecated Use {#completion_config} instead.
def config(config_key, context, default_value = nil, variables = nil)
@ld_client.track('$ld:ai:config:function:single', context, config_key, 1)
warn '[DEPRECATION] `config` is deprecated. Use `completion_config` instead.'
completion_config(config_key, context, default_value, variables)
end

private

def _completion_config(config_key, context, default_value = nil, variables = nil)
variation = @ld_client.variation(
config_key,
context,
Expand All @@ -158,7 +191,6 @@ def config(config_key, context, default_value = nil, variables = nil)
all_variables = variables ? variables.dup : {}
all_variables[:ldctx] = context.to_h

# Process messages and provider configuration
messages = nil
if variation[:messages].is_a?(Array) && variation[:messages].all? { |msg| msg.is_a?(Hash) }
messages = variation[:messages].map do |message|
Expand Down
10 changes: 10 additions & 0 deletions lib/server/ai/sdk_info.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

module LaunchDarkly
module Server
module AI
SDK_NAME = 'launchdarkly-server-sdk-ai'
SDK_LANGUAGE = 'ruby'
end
end
end
36 changes: 25 additions & 11 deletions spec/server/ai/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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.kind == 'ld_ai' && ctx.key == 'ld-internal-tracking' && ctx.get_value(:anonymous) == true },
{
aiSdkName: LaunchDarkly::Server::AI::SDK_NAME,
aiSdkVersion: LaunchDarkly::Server::AI::VERSION,
aiSdkLanguage: LaunchDarkly::Server::AI::SDK_LANGUAGE,
},
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',
Expand All @@ -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!')
Expand All @@ -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!')
Expand All @@ -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
Expand All @@ -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')
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
9 changes: 5 additions & 4 deletions spec/server/ai/config_tracker_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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