Skip to content

Commit e4a6ac4

Browse files
authored
chore: Add chatbot examples for OpenAI and AWS Bedrock (#2)
1 parent 3679c43 commit e4a6ac4

File tree

5 files changed

+314
-1
lines changed

5 files changed

+314
-1
lines changed

examples/chatbot/README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# LaunchDarkly sample Ruby AI application
2+
3+
We've built a simple console application that demonstrates how LaunchDarkly's Ruby AI SDK works.
4+
5+
Below, you'll find the build procedure. For more comprehensive instructions, you can visit your [Quickstart page](https://docs.launchdarkly.com/home/ai-configs/quickstart) or the [Ruby reference guide](https://docs.launchdarkly.com/sdk/ai/ruby).
6+
7+
This demo requires Ruby 3.0 or higher.
8+
9+
## Build Instructions
10+
11+
This repository includes examples for `OpenAI` and `Bedrock`. Depending on your preferred provider, you may have to take some additional steps.
12+
13+
### General setup
14+
15+
1. Install the required dependencies with `bundle install` in the appropriate example directory.
16+
1. Set the environment variable `LAUNCHDARKLY_SDK_KEY` to your LaunchDarkly SDK key. If there is an existing an AI Config in your LaunchDarkly project that you want to evaluate, set `LAUNCHDARKLY_AI_CONFIG_KEY` to the flag key; otherwise, an AI Config of `sample-ai-config` will be assumed.
17+
18+
```bash
19+
export LAUNCHDARKLY_SDK_KEY="1234567890abcdef"
20+
export LAUNCHDARKLY_AI_CONFIG_KEY="sample-ai-config"
21+
```
22+
23+
1. Replace `my-default-model` with your preferred model if the application cannot connect to LaunchDarkly Services.
24+
25+
### OpenAI setup
26+
27+
1. Set the environment variable `OPENAI_API_KEY` to your OpenAI key.
28+
29+
```bash
30+
export OPENAI_API_KEY="0987654321fedcba"
31+
```
32+
33+
1. Run the program `bundle exec ruby hello_openai.rb`
34+
35+
### Bedrock setup
36+
37+
1. Ensure the required AWS credentials can be [auto-detected by the AWS client][aws-configuration]. In the provided example we use the following environment variables.
38+
39+
```bash
40+
export AWS_ACCESS_KEY_ID="0987654321fedcba"
41+
export AWS_SECRET_ACCESS_KEY="0987654321fedcba"
42+
```
43+
44+
1. Run the program `bundle exec ruby hello_bedrock.rb`
45+
46+
[aws-configuration]: https://docs.aws.amazon.com/sdk-for-ruby/v3/developer-guide/configuring.html#precedence-settings

examples/chatbot/aws-bedrock/Gemfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# frozen_string_literal: true
2+
3+
source 'https://rubygems.org'
4+
5+
ruby '>= 3.0.0'
6+
7+
gem 'aws-sdk-bedrockruntime', '~> 1.0'
8+
gem 'launchdarkly-server-sdk-ai', path: '../../..'
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# frozen_string_literal: true
2+
3+
require 'aws-sdk-bedrockruntime'
4+
require 'launchdarkly-server-sdk'
5+
require 'launchdarkly-server-sdk-ai'
6+
7+
# Set sdk_key to your LaunchDarkly SDK key.
8+
sdk_key = ENV['LAUNCHDARKLY_SDK_KEY']
9+
10+
# Set config_key to the AI Config key you want to evaluate.
11+
ai_config_key = ENV['LAUNCHDARKLY_AI_CONFIG_KEY'] || 'sample-ai-config'
12+
13+
# Set aws_access_key_id and aws_secret_access_key for AWS credentials.
14+
aws_access_key_id = ENV['AWS_ACCESS_KEY_ID']
15+
aws_secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
16+
region = ENV['AWS_REGION'] || 'us-east-1'
17+
18+
if sdk_key.nil? || sdk_key.empty?
19+
puts '*** Please set the LAUNCHDARKLY_SDK_KEY env first'
20+
exit 1
21+
end
22+
23+
if aws_access_key_id.nil? || aws_access_key_id.empty?
24+
puts '*** Please set the AWS_ACCESS_KEY_ID env variable first'
25+
exit 1
26+
end
27+
28+
if aws_secret_access_key.nil? || aws_secret_access_key.empty?
29+
puts '*** Please set the AWS_SECRET_ACCESS_KEY env variable first'
30+
exit 1
31+
end
32+
33+
#
34+
# Chatbot class that interacts with LaunchDarkly AI and AWS Bedrock
35+
#
36+
class BedrockChatbot
37+
attr_reader :aiclient, :ai_config_key, :bedrock_client
38+
39+
DEFAULT_VALUE = LaunchDarkly::Server::AI::AIConfig.new(
40+
enabled: true,
41+
model: LaunchDarkly::Server::AI::ModelConfig.new(name: 'my-default-model'),
42+
messages: [
43+
LaunchDarkly::Server::AI::Message.new('system',
44+
'You are a default unhelpful assistant with the persona of HAL 9000 talking with {{ldctx.name}}'),
45+
LaunchDarkly::Server::AI::Message.new('user', '{{user_question}}'),
46+
]
47+
)
48+
49+
def initialize(aiclient, ai_config_key, bedrock_client, context)
50+
@aiclient = aiclient
51+
@ai_config_key = ai_config_key
52+
@bedrock_client = bedrock_client
53+
@context = context
54+
end
55+
56+
def ask_agent(question)
57+
ai_config = aiclient.config(
58+
@ai_config_key,
59+
@context,
60+
DEFAULT_VALUE,
61+
{ user_question: question }
62+
)
63+
64+
begin
65+
response = ai_config.tracker.track_bedrock_converse_metrics do
66+
@bedrock_client.converse(
67+
map_converse_arguments(
68+
ai_config.model.name,
69+
ai_config.messages
70+
)
71+
)
72+
end
73+
[response.output.message.content[0].text, ai_config.tracker]
74+
rescue StandardError => e
75+
["An error occured: #{e.message}", nil]
76+
end
77+
end
78+
79+
def agent_was_helpful(tracker, helpful)
80+
kind = helpful ? :positive : :negative
81+
tracker.track_feedback(kind: kind)
82+
end
83+
84+
def map_converse_arguments(model_id, messages)
85+
args = {
86+
model_id: model_id,
87+
}
88+
89+
mapped_messages = []
90+
user_messages = messages.select { |msg| msg.role == 'user' }
91+
mapped_messages << { role: 'user', content: user_messages.map { |msg| { text: msg.content } } } unless user_messages.empty?
92+
93+
assistant_messages = messages.select { |msg| msg.role == 'assistant' }
94+
mapped_messages << { role: 'assistant', content: assistant_messages.map { |msg| { text: msg.content } } } unless assistant_messages.empty?
95+
args[:messages] = mapped_messages unless mapped_messages.empty?
96+
97+
system_messages = messages.select { |msg| msg.role == 'system' }
98+
args[:system] = system_messages.map { |msg| { text: msg.content } } unless system_messages.empty?
99+
100+
args
101+
end
102+
end
103+
104+
# Initialize the LaunchDarkly client
105+
ld_client = LaunchDarkly::LDClient.new(sdk_key)
106+
ai_client = LaunchDarkly::Server::AI::Client.new(ld_client)
107+
108+
unless ld_client.initialized?
109+
puts '*** SDK failed to initialize!'
110+
exit 1
111+
end
112+
113+
# Create the LDContext
114+
context = LaunchDarkly::LDContext.create({
115+
key: 'user-key',
116+
kind: 'user',
117+
name: 'Lucy',
118+
})
119+
120+
bedrock_client = Aws::BedrockRuntime::Client.new(
121+
aws_access_key_id: aws_access_key_id,
122+
aws_secret_access_key: aws_secret_access_key,
123+
region: region
124+
)
125+
chatbot = BedrockChatbot.new(ai_client, ai_config_key, bedrock_client, context)
126+
127+
loop do
128+
print "Ask a question: (or type 'exit'): "
129+
question = gets&.chomp
130+
break if question.nil? || question.strip.downcase == 'exit'
131+
132+
response, tracker = chatbot.ask_agent(question)
133+
puts "AI Response: #{response}"
134+
135+
next if tracker.nil? # If tracker is nil, skip feedback collection
136+
137+
print "Was the response helpful? [yes/no] (or type 'exit'): "
138+
feedback = gets&.chomp
139+
break if feedback.nil? || feedback.strip.downcase == 'exit'
140+
141+
chatbot.agent_was_helpful(tracker, feedback == 'yes')
142+
end
143+
144+
ld_client.close

examples/hello-bedrock/Gemfile renamed to examples/chatbot/openai/Gemfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@
22

33
source 'https://rubygems.org'
44

5-
gem 'aws-sdk-bedrockruntime'
5+
ruby '>= 3.0.0'
6+
67
gem 'launchdarkly-server-sdk-ai', path: '../../..'
8+
gem 'openai', '~> 0.7.0'
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# frozen_string_literal: true
2+
3+
require 'launchdarkly-server-sdk'
4+
require 'launchdarkly-server-sdk-ai'
5+
require 'openai'
6+
7+
# Set sdk_key to your LaunchDarkly SDK key.
8+
sdk_key = ENV['LAUNCHDARKLY_SDK_KEY']
9+
10+
# Set config_key to the AI Config key you want to evaluate.
11+
ai_config_key = ENV['LAUNCHDARKLY_AI_CONFIG_KEY'] || 'sample-ai-config'
12+
13+
# Set openai_api_key to your OpenAI API key.
14+
openai_api_key = ENV['OPENAI_API_KEY']
15+
16+
if sdk_key.nil? || sdk_key.empty?
17+
puts '*** Please set the LAUNCHDARKLY_SDK_KEY env first'
18+
exit 1
19+
end
20+
21+
if openai_api_key.nil? || openai_api_key.empty?
22+
puts '*** Please set the OPENAI_API_KEY env first'
23+
exit 1
24+
end
25+
26+
#
27+
# Chatbot class that interacts with LaunchDarkly AI and OpenAI
28+
#
29+
class Chatbot
30+
attr_reader :aiclient, :ai_config_key, :openai_client, :context
31+
32+
DEFAULT_VALUE = LaunchDarkly::Server::AI::AIConfig.new(
33+
enabled: true,
34+
model: LaunchDarkly::Server::AI::ModelConfig.new(name: 'my-default-model'),
35+
messages: [
36+
LaunchDarkly::Server::AI::Message.new('system',
37+
'You are a default unhelpful assistant with the persona of HAL 9000 talking with {{ldctx.name}}'),
38+
LaunchDarkly::Server::AI::Message.new('user', '{{user_question}}'),
39+
]
40+
)
41+
42+
def initialize(aiclient, ai_config_key, openai_client, context)
43+
@aiclient = aiclient
44+
@ai_config_key = ai_config_key
45+
@openai_client = openai_client
46+
@context = context
47+
end
48+
49+
def ask_agent(question)
50+
ai_config = aiclient.config(
51+
@ai_config_key,
52+
@context,
53+
DEFAULT_VALUE,
54+
{ user_question: question }
55+
)
56+
57+
begin
58+
completion = ai_config.tracker.track_openai_metrics do
59+
@openai_client.chat.completions.create(
60+
model: ai_config.model.name,
61+
messages: ai_config.messages.map(&:to_h)
62+
)
63+
end
64+
[completion[:choices][0][:message][:content], ai_config.tracker]
65+
rescue StandardError => e
66+
["An error occurred: #{e.message}", nil]
67+
end
68+
end
69+
70+
def agent_was_helpful(tracker, helpful)
71+
kind = helpful ? :positive : :negative
72+
tracker.track_feedback(kind: kind)
73+
end
74+
end
75+
76+
ld_client = LaunchDarkly::LDClient.new(sdk_key)
77+
ai_client = LaunchDarkly::Server::AI::Client.new(ld_client)
78+
79+
unless ld_client.initialized?
80+
puts '*** SDK failed to initialize!'
81+
exit 1
82+
end
83+
84+
puts '*** SDK successfully initialized'
85+
86+
# Create the LDContext
87+
context = LaunchDarkly::LDContext.create({
88+
key: 'user-key',
89+
kind: 'user',
90+
name: 'Lucy',
91+
})
92+
93+
chatbot = Chatbot.new(ai_client, ai_config_key, OpenAI::Client.new(api_key: openai_api_key), context)
94+
95+
loop do
96+
print "Ask a question (or type 'exit'): "
97+
input = gets&.chomp
98+
break if input.nil? || input.strip.downcase == 'exit'
99+
100+
response, tracker = chatbot.ask_agent(input)
101+
puts "AI Response: #{response}"
102+
103+
next if tracker.nil? # If tracker is nil, skip feedback collection
104+
105+
print "Was the response helpful? [yes/no] (or type 'exit'): "
106+
feedback = gets&.chomp
107+
break if feedback.nil? || feedback.strip.downcase == 'exit'
108+
109+
helpful = feedback.strip.downcase == 'yes'
110+
chatbot.agent_was_helpful(tracker, helpful)
111+
end
112+
113+
ld_client.close

0 commit comments

Comments
 (0)