ruby_conversations 1.0.7 → 1.0.9
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/README.md +5 -41
- data/app/models/concerns/ruby_conversations/conversation_chat.rb +49 -0
- data/app/models/concerns/ruby_conversations/conversation_messages.rb +18 -0
- data/app/models/{ruby_conversations/concerns → concerns/ruby_conversations}/llm_credentials.rb +1 -0
- data/app/models/concerns/ruby_conversations/message_api_attributes.rb +39 -0
- data/app/models/concerns/ruby_conversations/message_attributes.rb +44 -0
- data/app/models/concerns/ruby_conversations/message_processing.rb +48 -0
- data/app/models/{ruby_conversations/concerns → concerns/ruby_conversations}/message_validation.rb +2 -2
- data/app/models/concerns/ruby_conversations/storable.rb +13 -0
- data/app/models/ruby_conversations/conversation.rb +102 -0
- data/app/models/ruby_conversations/message.rb +108 -0
- data/app/models/ruby_conversations/message_builder.rb +45 -20
- data/app/models/ruby_conversations/message_input.rb +69 -0
- data/app/models/ruby_conversations/message_prompt.rb +110 -0
- data/app/models/ruby_conversations/prompt.rb +48 -39
- data/lib/ruby_conversations/aws_credential_provider.rb +11 -5
- data/lib/ruby_conversations/client.rb +90 -0
- data/lib/ruby_conversations/configuration.rb +3 -13
- data/lib/ruby_conversations/engine.rb +2 -0
- data/lib/ruby_conversations/errors.rb +9 -0
- data/lib/ruby_conversations/version.rb +1 -1
- data/lib/ruby_conversations.rb +15 -42
- metadata +43 -81
- data/Rakefile +0 -12
- data/app/models/concerns/ruby_conversations/messageable.rb +0 -13
- data/app/models/concerns/ruby_conversations/versionable.rb +0 -20
- data/app/models/ruby_conversations/ai_conversation.rb +0 -91
- data/app/models/ruby_conversations/ai_message.rb +0 -28
- data/app/models/ruby_conversations/ai_message_input.rb +0 -22
- data/app/models/ruby_conversations/ai_message_prompt.rb +0 -28
- data/app/models/ruby_conversations/prompt_version.rb +0 -16
- data/lib/generators/ruby_conversations/install/install_generator.rb +0 -60
- data/lib/generators/ruby_conversations/install/templates/README.md +0 -42
- data/lib/generators/ruby_conversations/install/templates/initializer.rb.erb +0 -18
- data/lib/generators/ruby_conversations/install/templates/migrations/create_ai_conversations.rb.erb +0 -13
- data/lib/generators/ruby_conversations/install/templates/migrations/create_ai_message_inputs.rb.erb +0 -13
- data/lib/generators/ruby_conversations/install/templates/migrations/create_ai_message_prompts.rb.erb +0 -18
- data/lib/generators/ruby_conversations/install/templates/migrations/create_ai_messages.rb.erb +0 -18
- data/lib/generators/ruby_conversations/install/templates/migrations/create_ai_tool_calls.rb.erb +0 -15
- data/lib/generators/ruby_conversations/install/templates/migrations/create_prompt_versions.rb.erb +0 -14
- data/lib/generators/ruby_conversations/install/templates/migrations/create_prompts.rb.erb +0 -16
- data/lib/ruby_conversations/jwt_client.rb +0 -23
- data/lib/ruby_conversations/storage/base.rb +0 -28
- data/lib/ruby_conversations/storage/local.rb +0 -30
- data/lib/ruby_conversations/storage/remote.rb +0 -93
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model'
|
4
|
+
|
5
|
+
module RubyConversations
|
6
|
+
# Represents an input value for a message prompt
|
7
|
+
class MessageInput
|
8
|
+
include ActiveModel::Model
|
9
|
+
|
10
|
+
# Define attributes
|
11
|
+
attr_accessor :id, :message_prompt_id, :placeholder_name, :value,
|
12
|
+
:created_at, :updated_at
|
13
|
+
|
14
|
+
# Validations
|
15
|
+
validates :placeholder_name, presence: true
|
16
|
+
validate :value_not_nil
|
17
|
+
|
18
|
+
# Association-like methods
|
19
|
+
def message_prompt=(prompt)
|
20
|
+
@message_prompt = prompt
|
21
|
+
@message_prompt_id = prompt&.id
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :message_prompt
|
25
|
+
|
26
|
+
# Scopes
|
27
|
+
def self.ordered
|
28
|
+
all.sort_by(&:placeholder_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.all
|
32
|
+
@all ||= []
|
33
|
+
end
|
34
|
+
|
35
|
+
# Attributes method for serialization/logging
|
36
|
+
def attributes
|
37
|
+
base_attributes
|
38
|
+
end
|
39
|
+
|
40
|
+
# Method for API serialization
|
41
|
+
def attributes_for_api
|
42
|
+
{
|
43
|
+
id: id,
|
44
|
+
placeholder_name: placeholder_name,
|
45
|
+
value: value
|
46
|
+
}.compact
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def value_not_nil
|
52
|
+
errors.add(:value, "can't be blank") if value.nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
def base_attributes
|
56
|
+
{
|
57
|
+
'id' => id,
|
58
|
+
'message_prompt_id' => message_prompt_id,
|
59
|
+
'placeholder_name' => placeholder_name,
|
60
|
+
'value' => value,
|
61
|
+
'created_at' => created_at,
|
62
|
+
'updated_at' => updated_at
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Alias for compatibility with Zeitwerk
|
68
|
+
AIMessageInput = MessageInput
|
69
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model'
|
4
|
+
require_relative 'message_input'
|
5
|
+
|
6
|
+
module RubyConversations
|
7
|
+
# Represents a prompt used to generate a message in a conversation
|
8
|
+
class MessagePrompt
|
9
|
+
include ActiveModel::Model
|
10
|
+
|
11
|
+
# Define attributes
|
12
|
+
attr_accessor :id, :message_id, :prompt_version_id, :name, :role, :content, :metadata,
|
13
|
+
:created_at, :updated_at, :message_inputs, :draft, :message
|
14
|
+
|
15
|
+
# Define nested attributes writer
|
16
|
+
def message_inputs_attributes=(attributes)
|
17
|
+
@message_inputs ||= []
|
18
|
+
attributes.each do |attrs|
|
19
|
+
@message_inputs << RubyConversations::MessageInput.new(attrs)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Initialization
|
24
|
+
def initialize(attributes = {})
|
25
|
+
@message_inputs = [] # Initialize inputs array
|
26
|
+
# Extract nested inputs before super
|
27
|
+
inputs_attributes = extract_nested_attributes!(attributes, :message_inputs)
|
28
|
+
|
29
|
+
super
|
30
|
+
initialize_message_inputs(inputs_attributes)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Handle nested inputs initialization
|
34
|
+
def initialize_message_inputs(attributes_array)
|
35
|
+
(attributes_array || []).each do |attrs|
|
36
|
+
next if attrs.blank?
|
37
|
+
|
38
|
+
@message_inputs << RubyConversations::MessageInput.new(attrs)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Attributes method for serialization/logging
|
43
|
+
def attributes
|
44
|
+
base_attributes.merge(message_inputs_attributes_hash)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Method for API serialization
|
48
|
+
def attributes_for_api
|
49
|
+
{
|
50
|
+
id: id,
|
51
|
+
prompt_version_id: prompt_version_id,
|
52
|
+
name: name,
|
53
|
+
role: role,
|
54
|
+
content: content,
|
55
|
+
metadata: metadata,
|
56
|
+
draft: draft,
|
57
|
+
ai_message_inputs_attributes: message_inputs.map(&:attributes_for_api)
|
58
|
+
}.compact
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def base_attributes
|
64
|
+
identity_attributes.merge(content_attributes).merge(timestamp_attributes)
|
65
|
+
end
|
66
|
+
|
67
|
+
def identity_attributes
|
68
|
+
{
|
69
|
+
'id' => id,
|
70
|
+
'message_id' => message_id,
|
71
|
+
'prompt_version_id' => prompt_version_id,
|
72
|
+
'message' => message
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
def content_attributes
|
77
|
+
{
|
78
|
+
'name' => name,
|
79
|
+
'role' => role,
|
80
|
+
'content' => content,
|
81
|
+
'metadata' => metadata,
|
82
|
+
'draft' => draft
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
def timestamp_attributes
|
87
|
+
{
|
88
|
+
'created_at' => created_at,
|
89
|
+
'updated_at' => updated_at
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
def message_inputs_attributes_hash
|
94
|
+
{
|
95
|
+
'message_inputs' => message_inputs.map(&:attributes)
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
# Helper method to extract nested attributes
|
100
|
+
def extract_nested_attributes!(attributes, key)
|
101
|
+
nested = extract_key_attributes(attributes, key)
|
102
|
+
nested_attributes = extract_key_attributes(attributes, "#{key}_attributes")
|
103
|
+
nested.concat(nested_attributes)
|
104
|
+
end
|
105
|
+
|
106
|
+
def extract_key_attributes(attributes, key)
|
107
|
+
attributes.delete(key) || attributes.delete(key.to_s) || attributes.delete(key.to_sym) || []
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -1,61 +1,70 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
# A prompt is a message template that can be used to generate messages for an AI conversation.
|
5
|
-
class Prompt < ActiveRecord::Base
|
6
|
-
include Versionable
|
3
|
+
require 'active_model'
|
7
4
|
|
8
|
-
|
9
|
-
|
5
|
+
module RubyConversations
|
6
|
+
# Represents a prompt template used to generate AI messages.
|
7
|
+
class Prompt
|
8
|
+
include ActiveModel::Model
|
10
9
|
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
has_many :messages, through: :message_prompts, class_name: RubyConversations.configuration.message_class,
|
15
|
-
source: :ai_message, dependent: :nullify
|
16
|
-
belongs_to :organization, optional: true
|
10
|
+
# Define attributes
|
11
|
+
attr_accessor :id, :name, :role, :message, :valid_placeholders, :temperature, :metadata, :created_at, :updated_at,
|
12
|
+
:latest_version_id
|
17
13
|
|
18
|
-
#
|
19
|
-
|
20
|
-
validates :temperature, numericality: { greater_than_or_equal_to: 0.0 }, allow_nil: true
|
21
|
-
validates :role, presence: true, inclusion: { in: VALID_ROLES }
|
22
|
-
|
23
|
-
# Scopes
|
24
|
-
scope :active, -> { where(active: true) }
|
25
|
-
scope :by_role, ->(role) { where(role: role) }
|
26
|
-
scope :with_versions, -> { includes(:versions) }
|
27
|
-
scope :ordered_by_name, -> { order(:name) }
|
28
|
-
scope :internal, -> { where(internal: true) }
|
29
|
-
scope :external, -> { where(internal: false) }
|
14
|
+
# Constants
|
15
|
+
ROLES = %w[system user assistant].freeze
|
30
16
|
|
31
17
|
# Class methods
|
18
|
+
def self.roles
|
19
|
+
ROLES
|
20
|
+
end
|
21
|
+
|
32
22
|
def self.find_by_name!(name)
|
33
|
-
|
23
|
+
prompt_data = RubyConversations.client.fetch_prompt(name)
|
24
|
+
raise "Prompt not found: #{name}" unless prompt_data
|
25
|
+
|
26
|
+
new(prompt_data)
|
34
27
|
end
|
35
28
|
|
36
|
-
def self.
|
37
|
-
|
29
|
+
def self.find_by!(conditions)
|
30
|
+
name = conditions[:name]
|
31
|
+
find_by_name!(name)
|
38
32
|
end
|
39
33
|
|
40
|
-
#
|
41
|
-
|
42
|
-
|
43
|
-
|
34
|
+
# Validations
|
35
|
+
validates :name, presence: true
|
36
|
+
validates :role, presence: true, inclusion: { in: ROLES }
|
37
|
+
validates :message, presence: true
|
38
|
+
|
39
|
+
# Initialization
|
40
|
+
def initialize(attributes = {})
|
41
|
+
super
|
44
42
|
end
|
45
43
|
|
46
|
-
|
47
|
-
|
44
|
+
# Basic attributes method
|
45
|
+
def attributes
|
46
|
+
instance_values
|
48
47
|
end
|
49
48
|
|
50
|
-
|
51
|
-
|
52
|
-
|
49
|
+
# Method for API serialization
|
50
|
+
def attributes_for_api
|
51
|
+
{
|
52
|
+
id: id,
|
53
|
+
name: name,
|
54
|
+
role: role,
|
55
|
+
message: message,
|
56
|
+
valid_placeholders: valid_placeholders,
|
57
|
+
temperature: temperature,
|
58
|
+
metadata: metadata,
|
59
|
+
latest_version_id: latest_version_id
|
60
|
+
}.compact
|
53
61
|
end
|
54
62
|
|
55
|
-
|
63
|
+
# Interpolate placeholders in the message
|
64
|
+
def interpolate(variables = {})
|
65
|
+
return message if message.nil? || variables.empty?
|
56
66
|
|
57
|
-
|
58
|
-
%i[content role]
|
67
|
+
format(message, **variables)
|
59
68
|
end
|
60
69
|
end
|
61
70
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'aws-sdk-core'
|
4
|
+
require_relative 'errors'
|
4
5
|
|
5
6
|
module RubyConversations
|
6
7
|
# Manages AWS credentials with automatic refresh capability
|
@@ -53,7 +54,7 @@ module RubyConversations
|
|
53
54
|
end
|
54
55
|
|
55
56
|
def use_mock_credentials?
|
56
|
-
|
57
|
+
ENV['RAILS_ENV'] != 'production'
|
57
58
|
end
|
58
59
|
|
59
60
|
def set_mock_credentials
|
@@ -62,7 +63,7 @@ module RubyConversations
|
|
62
63
|
'mock_secret_access_key',
|
63
64
|
'mock_session_token'
|
64
65
|
)
|
65
|
-
@expiration = Time.now + 1
|
66
|
+
@expiration = Time.now + 3600 # 1 hour
|
66
67
|
end
|
67
68
|
|
68
69
|
def fetch_and_set_real_credentials
|
@@ -78,13 +79,18 @@ module RubyConversations
|
|
78
79
|
end
|
79
80
|
|
80
81
|
def assign_credentials(ecs_credentials)
|
81
|
-
|
82
|
-
|
82
|
+
if ecs_credentials.respond_to?(:credentials)
|
83
|
+
@credentials = ecs_credentials.credentials
|
84
|
+
@expiration = ecs_credentials.expiration if ecs_credentials.respond_to?(:expiration)
|
85
|
+
else
|
86
|
+
@credentials = ecs_credentials
|
87
|
+
@expiration = nil
|
88
|
+
end
|
83
89
|
end
|
84
90
|
|
85
91
|
def expired?
|
86
|
-
return true if @credentials.nil?
|
87
92
|
return false if @expiration.nil?
|
93
|
+
return true if @credentials.nil?
|
88
94
|
|
89
95
|
# Refresh if we're within 5 minutes of expiration
|
90
96
|
@expiration < Time.now + 300
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'faraday'
|
4
|
+
require 'jwt'
|
5
|
+
|
6
|
+
module RubyConversations
|
7
|
+
# HTTP client for interacting with the conversations API
|
8
|
+
class Client
|
9
|
+
PROMPT_ATTRIBUTES = %w[
|
10
|
+
id name message role temperature valid_placeholders created_at updated_at latest_version_id
|
11
|
+
].freeze
|
12
|
+
|
13
|
+
def initialize(url:, jwt_secret:)
|
14
|
+
@url = url
|
15
|
+
@jwt_secret = jwt_secret
|
16
|
+
@jwt_token = nil
|
17
|
+
@jwt_expiration = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def store_conversation(conversation)
|
21
|
+
response = client.post('api/ai_conversations', conversation.conversation_attributes_for_storage)
|
22
|
+
handle_response(response)
|
23
|
+
end
|
24
|
+
|
25
|
+
def fetch_prompt(name)
|
26
|
+
response = client.get("api/prompts/#{name}")
|
27
|
+
data = handle_response(response)
|
28
|
+
map_prompt_attributes(data)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def map_prompt_attributes(data)
|
34
|
+
PROMPT_ATTRIBUTES.each_with_object({}) do |attr_name, attrs|
|
35
|
+
attrs[attr_name.to_sym] = data[attr_name]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def client
|
40
|
+
@client ||= build_client
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_client
|
44
|
+
Faraday.new(url: @url) do |faraday|
|
45
|
+
faraday.request :json
|
46
|
+
faraday.response :json
|
47
|
+
faraday.request :authorization, 'Bearer', -> { current_jwt }
|
48
|
+
faraday.adapter Faraday.default_adapter
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def current_jwt
|
53
|
+
refresh_jwt if token_expired?
|
54
|
+
@jwt_token
|
55
|
+
end
|
56
|
+
|
57
|
+
def token_expired?
|
58
|
+
@jwt_token.nil? || @jwt_expiration.nil? || Time.now.to_i >= @jwt_expiration
|
59
|
+
end
|
60
|
+
|
61
|
+
def refresh_jwt
|
62
|
+
if @jwt_secret.respond_to?(:call)
|
63
|
+
refresh_callable_jwt
|
64
|
+
else
|
65
|
+
refresh_static_jwt
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def refresh_callable_jwt
|
70
|
+
@jwt_token = @jwt_secret.call
|
71
|
+
decoded_token = JWT.decode(@jwt_token, nil, false).first
|
72
|
+
@jwt_expiration = decoded_token['exp']
|
73
|
+
end
|
74
|
+
|
75
|
+
def refresh_static_jwt
|
76
|
+
@jwt_expiration = Time.now.to_i + 3600 # 1 hour
|
77
|
+
payload = { exp: @jwt_expiration, iat: Time.now.to_i }
|
78
|
+
@jwt_token = JWT.encode(payload, @jwt_secret, 'HS256')
|
79
|
+
end
|
80
|
+
|
81
|
+
def handle_response(response)
|
82
|
+
case response.status
|
83
|
+
when 200, 201
|
84
|
+
response.body
|
85
|
+
else
|
86
|
+
raise Error, "API request failed: #{response.body}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -3,26 +3,16 @@
|
|
3
3
|
module RubyConversations
|
4
4
|
# Configuration options for RubyConversations
|
5
5
|
class Configuration
|
6
|
-
attr_accessor :
|
7
|
-
:conversation_class, :message_class, :message_input_class, :message_prompt_class,
|
8
|
-
:prompt_class, :prompt_version_class
|
6
|
+
attr_accessor :api_url, :jwt_secret, :default_llm_model, :default_llm_provider
|
9
7
|
|
10
8
|
def initialize
|
11
|
-
@storage_mode = :local
|
12
9
|
@default_llm_model = 'claude-3-7-sonnet'
|
13
10
|
@default_llm_provider = 'bedrock'
|
14
|
-
@conversation_class = 'AiConversation'
|
15
|
-
@message_class = 'AiMessage'
|
16
|
-
@message_input_class = 'AiMessageInput'
|
17
|
-
@message_prompt_class = 'AiMessagePrompt'
|
18
|
-
@prompt_class = 'Prompt'
|
19
|
-
@prompt_version_class = 'PromptVersion'
|
20
11
|
end
|
21
12
|
|
22
13
|
def validate!
|
23
|
-
|
24
|
-
raise ConfigurationError, '
|
25
|
-
raise ConfigurationError, 'jwt_secret is required for remote storage' unless jwt_secret
|
14
|
+
raise ConfigurationError, 'api_url is required' unless api_url
|
15
|
+
raise ConfigurationError, 'jwt_secret is required' unless jwt_secret
|
26
16
|
end
|
27
17
|
end
|
28
18
|
end
|
data/lib/ruby_conversations.rb
CHANGED
@@ -1,22 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'rails'
|
4
|
-
require 'active_record'
|
5
|
-
require 'ruby_llm'
|
6
|
-
require 'faraday'
|
7
|
-
require 'jwt'
|
8
3
|
require 'zeitwerk'
|
4
|
+
require 'ruby_conversations/version'
|
9
5
|
require 'ruby_conversations/configuration'
|
6
|
+
require 'ruby_conversations/errors'
|
7
|
+
require 'ruby_conversations/client'
|
10
8
|
require 'ruby_conversations/aws_credential_provider'
|
9
|
+
require 'ruby_llm'
|
11
10
|
|
12
|
-
|
13
|
-
loader.ignore("#{__dir__}/generators")
|
14
|
-
|
15
|
-
# Main module for the RubyConversations engine.
|
11
|
+
# Main module for RubyConversations
|
16
12
|
module RubyConversations
|
17
|
-
class Error < StandardError; end
|
18
|
-
class ConfigurationError < Error; end
|
19
|
-
|
20
13
|
class << self
|
21
14
|
attr_writer :configuration
|
22
15
|
|
@@ -26,50 +19,30 @@ module RubyConversations
|
|
26
19
|
|
27
20
|
def configure
|
28
21
|
yield(configuration)
|
29
|
-
configuration.validate!
|
30
|
-
configure_ruby_llm if configuration.default_llm_provider == 'bedrock'
|
31
22
|
end
|
32
23
|
|
33
|
-
def
|
34
|
-
@
|
35
|
-
when :local
|
36
|
-
build_local_storage
|
37
|
-
when :remote
|
38
|
-
build_remote_storage
|
39
|
-
else
|
40
|
-
raise ConfigurationError, "Unknown storage mode: #{configuration.storage_mode}"
|
41
|
-
end
|
24
|
+
def client
|
25
|
+
@client ||= build_client
|
42
26
|
end
|
43
27
|
|
44
28
|
def reset!
|
45
|
-
@configuration =
|
46
|
-
@
|
29
|
+
@configuration = nil
|
30
|
+
@client = nil
|
47
31
|
end
|
48
32
|
|
49
33
|
private
|
50
34
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
def build_remote_storage
|
56
|
-
Storage::Remote.new(
|
35
|
+
def build_client
|
36
|
+
configuration.validate!
|
37
|
+
Client.new(
|
57
38
|
url: configuration.api_url,
|
58
39
|
jwt_secret: configuration.jwt_secret
|
59
40
|
)
|
60
41
|
end
|
61
|
-
|
62
|
-
def configure_ruby_llm
|
63
|
-
credential_provider = AwsCredentialProvider.instance
|
64
|
-
|
65
|
-
RubyLLM.configure do |config|
|
66
|
-
config.bedrock_region = ENV.fetch('AWS_REGION', '')
|
67
|
-
config.bedrock_api_key = credential_provider.access_key_id
|
68
|
-
config.bedrock_secret_key = credential_provider.secret_access_key
|
69
|
-
config.bedrock_session_token = credential_provider.session_token
|
70
|
-
end
|
71
|
-
end
|
72
42
|
end
|
73
43
|
end
|
74
44
|
|
45
|
+
# Set up Zeitwerk autoloading
|
46
|
+
loader = Zeitwerk::Loader.new
|
47
|
+
loader.push_dir("#{__dir__}/../app/models")
|
75
48
|
loader.setup
|