ruby_conversations 1.0.9 → 1.0.11
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/app/models/ruby_conversations/concerns/conversation_chat.rb +63 -0
- data/app/models/ruby_conversations/concerns/conversation_messages.rb +31 -0
- data/app/models/ruby_conversations/concerns/llm_credentials.rb +42 -0
- data/app/models/ruby_conversations/concerns/message_api_attributes.rb +41 -0
- data/app/models/ruby_conversations/concerns/message_attributes.rb +46 -0
- data/app/models/ruby_conversations/concerns/message_processing.rb +54 -0
- data/app/models/ruby_conversations/concerns/message_validation.rb +46 -0
- data/app/models/ruby_conversations/conversation.rb +6 -12
- data/app/models/ruby_conversations/message.rb +2 -5
- data/lib/ruby_conversations/client.rb +46 -11
- data/lib/ruby_conversations/version.rb +1 -1
- metadata +23 -10
- data/app/models/concerns/ruby_conversations/conversation_chat.rb +0 -49
- data/app/models/concerns/ruby_conversations/conversation_messages.rb +0 -18
- data/app/models/concerns/ruby_conversations/llm_credentials.rb +0 -40
- data/app/models/concerns/ruby_conversations/message_api_attributes.rb +0 -39
- data/app/models/concerns/ruby_conversations/message_attributes.rb +0 -44
- data/app/models/concerns/ruby_conversations/message_processing.rb +0 -48
- data/app/models/concerns/ruby_conversations/message_validation.rb +0 -48
- data/app/models/concerns/ruby_conversations/storable.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 247b000cfed0f52453c93f9ba188a79a3b09e0e7d96dbb6866faee48e2f33386
|
4
|
+
data.tar.gz: d65f04b9333a3afecf5b05e7ddd51b030a7a0f9974d4cee84fa212216b68d140
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ec989c1dd0510d247dbdc329b64af6c729ea60347cf1011edc73fbeed465c00f53f09819ae6dc7af767f47e0f99520c535af267225826261241198317764c76
|
7
|
+
data.tar.gz: e02836afeeb708033c85f83001ce40d79798cf70fe1cdb6e1b8d586f65106b520f73f23f67055e91f23adc20e7f1b61c5b2171caf1be5c3eba13d4f6e1d858db
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyConversations
|
4
|
+
module Concerns
|
5
|
+
# Handles chat-related functionality for Conversation
|
6
|
+
module ConversationChat
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
def execute(system_message: nil)
|
10
|
+
validate_conversation_state
|
11
|
+
response = generate_chat_response(system_message)
|
12
|
+
store_and_update_conversation(response)
|
13
|
+
end
|
14
|
+
|
15
|
+
def llm
|
16
|
+
case model_identifier
|
17
|
+
when 'claude-3-7-sonnet'
|
18
|
+
'us.anthropic.claude-3-sonnet-20250219-v1:0'
|
19
|
+
else
|
20
|
+
model_identifier
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def validate_conversation_state
|
27
|
+
message = messages.last
|
28
|
+
raise ArgumentError, 'Conversation must have at least one message to execute' unless message
|
29
|
+
end
|
30
|
+
|
31
|
+
def generate_chat_response(system_message)
|
32
|
+
chat = setup_llm_chat(system_message: system_message)
|
33
|
+
chat.ask(messages.last.request)
|
34
|
+
end
|
35
|
+
|
36
|
+
def store_and_update_conversation(response)
|
37
|
+
update_last_message_response(messages.last, response)
|
38
|
+
RubyConversations.client.store_conversation_or_message(self, messages.last)
|
39
|
+
|
40
|
+
{
|
41
|
+
response: response,
|
42
|
+
conversation: self
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def setup_llm_chat(system_message: nil)
|
47
|
+
configure_llm_credentials
|
48
|
+
build_chat(system_message)
|
49
|
+
end
|
50
|
+
|
51
|
+
def update_last_message_response(message, response)
|
52
|
+
message.response = response.to_h.to_json
|
53
|
+
end
|
54
|
+
|
55
|
+
def build_chat(system_message)
|
56
|
+
chat = RubyLLM.chat(model: llm, provider: provider).with_temperature(0.0)
|
57
|
+
chat.with_tool(tool) if tool.present?
|
58
|
+
chat.add_message(role: :system, content: system_message) if system_message.present?
|
59
|
+
chat
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyConversations
|
4
|
+
module Concerns
|
5
|
+
# Handles message-related functionality for Conversation
|
6
|
+
module ConversationMessages
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
def initialize_messages(message_attributes)
|
10
|
+
(message_attributes || []).each do |msg_data|
|
11
|
+
processed_prompts = process_message_prompts(msg_data)
|
12
|
+
|
13
|
+
message_instance = RubyConversations::Message.new(msg_data.except('id', 'conversation_id', 'created_at',
|
14
|
+
'updated_at'))
|
15
|
+
message_instance.message_prompts = processed_prompts
|
16
|
+
@messages << message_instance # Add to the messages array
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def ask_multiple(prompt_inputs, description: nil)
|
21
|
+
MessageBuilder.new(self).build_from_multiple_prompts(prompt_inputs, description: description)
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def ask(name, description: nil, inputs: {})
|
26
|
+
MessageBuilder.new(self).build_from_single_prompt(name, description: description, inputs: inputs)
|
27
|
+
self
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyConversations
|
4
|
+
module Concerns
|
5
|
+
# Handles the management of LLM credentials for AI conversations.
|
6
|
+
# This concern provides methods for configuring LLM credentials from either
|
7
|
+
# environment variables or an AWS credential provider.
|
8
|
+
module LlmCredentials
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def configure_llm_credentials
|
14
|
+
RubyLLM.configure do |config|
|
15
|
+
credentials = ENV['AWS_ACCESS_KEY_ID'].present? ? env_credentials : provider_credentials
|
16
|
+
config.bedrock_region = ENV.fetch('AWS_REGION', 'us-west-2')
|
17
|
+
config.bedrock_api_key = credentials[:api_key]
|
18
|
+
config.bedrock_secret_key = credentials[:secret_key]
|
19
|
+
config.bedrock_session_token = credentials[:session_token]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def env_credentials
|
24
|
+
{
|
25
|
+
api_key: ENV.fetch('AWS_ACCESS_KEY_ID', nil),
|
26
|
+
secret_key: ENV.fetch('AWS_SECRET_ACCESS_KEY', nil),
|
27
|
+
session_token: ENV.fetch('AWS_SESSION_TOKEN', nil)
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def provider_credentials
|
32
|
+
credential_provider = AwsCredentialProvider.instance
|
33
|
+
credential_provider.refresh_if_expired!
|
34
|
+
{
|
35
|
+
api_key: credential_provider.access_key_id,
|
36
|
+
secret_key: credential_provider.secret_access_key,
|
37
|
+
session_token: credential_provider.session_token
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyConversations
|
4
|
+
module Concerns
|
5
|
+
# Handles API-related attribute functionality for Message
|
6
|
+
module MessageApiAttributes
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
def message_attributes_for_api
|
10
|
+
{
|
11
|
+
request: request,
|
12
|
+
response: response,
|
13
|
+
model_identifier: model_identifier,
|
14
|
+
change_description: change_description,
|
15
|
+
llm: llm
|
16
|
+
}.compact
|
17
|
+
end
|
18
|
+
|
19
|
+
def message_base_attributes
|
20
|
+
{
|
21
|
+
'request' => request,
|
22
|
+
'response' => response,
|
23
|
+
'model_identifier' => model_identifier,
|
24
|
+
'change_description' => change_description
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def remote_attributes
|
29
|
+
base_attributes.compact
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def base_attributes
|
35
|
+
RubyConversations::Concerns::MessageAttributes::ATTRIBUTE_NAMES.each_with_object({}) do |attr_name, attrs|
|
36
|
+
attrs[attr_name] = public_send(attr_name)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyConversations
|
4
|
+
module Concerns
|
5
|
+
# Handles attribute-related functionality for Message
|
6
|
+
module MessageAttributes
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
ATTRIBUTE_NAMES = %w[
|
10
|
+
id ai_conversation_id request response model_identifier
|
11
|
+
tool change_description llm created_at updated_at
|
12
|
+
].freeze
|
13
|
+
|
14
|
+
included do
|
15
|
+
# Define core message attributes
|
16
|
+
attr_accessor :request, :response, :model_identifier, :change_description
|
17
|
+
|
18
|
+
# Define relationship attributes
|
19
|
+
attr_accessor :ai_conversation, :message_prompts, :prompts
|
20
|
+
|
21
|
+
# Common validations
|
22
|
+
validates :llm, presence: true
|
23
|
+
validates :request, presence: true
|
24
|
+
|
25
|
+
# Override initialize to ensure arrays are initialized
|
26
|
+
def initialize(attributes = {})
|
27
|
+
@message_prompts = []
|
28
|
+
@prompts = []
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def remote_attributes
|
34
|
+
base_attributes.compact
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def base_attributes
|
40
|
+
ATTRIBUTE_NAMES.each_with_object({}) do |attr_name, attrs|
|
41
|
+
attrs[attr_name] = public_send(attr_name)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyConversations
|
4
|
+
module Concerns
|
5
|
+
# Handles message processing functionality for Conversation
|
6
|
+
module MessageProcessing
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
def process_message_prompts(message_data)
|
10
|
+
prompts_data = extract_prompts_data(message_data)
|
11
|
+
return [] unless prompts_data
|
12
|
+
|
13
|
+
prompts_data.map do |prompt_data|
|
14
|
+
process_single_prompt(prompt_data)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def extract_prompts_data(message_data)
|
21
|
+
message_data['message_prompts'] ||
|
22
|
+
message_data[:message_prompts] ||
|
23
|
+
message_data['ai_message_prompts_attributes'] ||
|
24
|
+
message_data[:ai_message_prompts_attributes]
|
25
|
+
end
|
26
|
+
|
27
|
+
def process_single_prompt(prompt_data)
|
28
|
+
inputs_data = extract_inputs_data(prompt_data)
|
29
|
+
prompt_instance = RubyConversations::MessagePrompt.new(
|
30
|
+
prompt_data.except('id', 'message_id', 'created_at', 'updated_at')
|
31
|
+
)
|
32
|
+
|
33
|
+
process_prompt_inputs(prompt_instance, inputs_data)
|
34
|
+
prompt_instance
|
35
|
+
end
|
36
|
+
|
37
|
+
def extract_inputs_data(prompt_data)
|
38
|
+
prompt_data['message_inputs'] ||
|
39
|
+
prompt_data[:message_inputs] ||
|
40
|
+
prompt_data['ai_message_inputs_attributes'] ||
|
41
|
+
prompt_data[:ai_message_inputs_attributes] || []
|
42
|
+
end
|
43
|
+
|
44
|
+
def process_prompt_inputs(prompt_instance, inputs_data)
|
45
|
+
inputs_data.each do |input_data|
|
46
|
+
input_instance = RubyConversations::MessageInput.new(
|
47
|
+
input_data.except('id', 'message_prompt_id', 'created_at', 'updated_at')
|
48
|
+
)
|
49
|
+
prompt_instance.message_inputs << input_instance
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyConversations
|
4
|
+
module Concerns
|
5
|
+
# Handles validation-related functionality for Message
|
6
|
+
module MessageValidation
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def validate_messages
|
12
|
+
messages.each do |message|
|
13
|
+
next if message.valid?
|
14
|
+
|
15
|
+
message.errors.full_messages.each do |msg|
|
16
|
+
errors.add(:messages, "invalid: #{msg}")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def validate_message_level(message)
|
22
|
+
return if message.valid?
|
23
|
+
|
24
|
+
message.errors.full_messages.each do |msg|
|
25
|
+
errors.add(:base, "Message error: #{msg}")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def validate_prompt_level(prompt)
|
30
|
+
return if prompt.valid?
|
31
|
+
|
32
|
+
prompt.errors.full_messages.each do |msg|
|
33
|
+
errors.add(:base, "Message prompt error: #{msg}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def validate_input_level(input)
|
38
|
+
return if input.valid?
|
39
|
+
|
40
|
+
input.errors.full_messages.each do |msg|
|
41
|
+
errors.add(:base, "Message prompt input error: #{msg}")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -1,22 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'active_model'
|
4
|
-
|
5
|
-
require_relative '../concerns/ruby_conversations/conversation_messages'
|
6
|
-
require_relative '../concerns/ruby_conversations/llm_credentials'
|
7
|
-
require_relative '../concerns/ruby_conversations/message_validation'
|
8
|
-
require_relative '../concerns/ruby_conversations/message_processing'
|
9
|
-
require_relative '../concerns/ruby_conversations/storable'
|
4
|
+
|
10
5
|
module RubyConversations
|
11
6
|
# Represents a conversation thread, managing messages and interactions with the LLM.
|
12
7
|
class Conversation
|
13
8
|
include ActiveModel::Model
|
14
|
-
include ConversationChat
|
15
|
-
include ConversationMessages
|
16
|
-
include LlmCredentials
|
17
|
-
include MessageValidation
|
18
|
-
include MessageProcessing
|
19
|
-
include Storable
|
9
|
+
include RubyConversations::Concerns::ConversationChat
|
10
|
+
include RubyConversations::Concerns::ConversationMessages
|
11
|
+
include RubyConversations::Concerns::LlmCredentials
|
12
|
+
include RubyConversations::Concerns::MessageValidation
|
13
|
+
include RubyConversations::Concerns::MessageProcessing
|
20
14
|
|
21
15
|
# Define attributes needed for API interaction & local state
|
22
16
|
attr_accessor :id, :conversationable_type, :conversationable_id, :conversationable,
|
@@ -1,16 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'active_model'
|
4
|
-
require_relative 'message_prompt'
|
5
|
-
require_relative '../concerns/ruby_conversations/message_attributes'
|
6
|
-
require_relative '../concerns/ruby_conversations/message_api_attributes'
|
7
4
|
|
8
5
|
module RubyConversations
|
9
6
|
# Represents a message in a conversation, either from the user or AI
|
10
7
|
class Message
|
11
8
|
include ActiveModel::Model
|
12
|
-
include MessageAttributes
|
13
|
-
include MessageApiAttributes
|
9
|
+
include Concerns::MessageAttributes
|
10
|
+
include Concerns::MessageApiAttributes
|
14
11
|
|
15
12
|
# Define attributes
|
16
13
|
attr_accessor :id, :conversation_id, :llm,
|
@@ -2,26 +2,67 @@
|
|
2
2
|
|
3
3
|
require 'faraday'
|
4
4
|
require 'jwt'
|
5
|
+
require 'logger'
|
5
6
|
|
6
7
|
module RubyConversations
|
7
8
|
# HTTP client for interacting with the conversations API
|
8
9
|
class Client
|
10
|
+
attr_reader :client
|
11
|
+
|
9
12
|
PROMPT_ATTRIBUTES = %w[
|
10
13
|
id name message role temperature valid_placeholders created_at updated_at latest_version_id
|
11
14
|
].freeze
|
12
15
|
|
16
|
+
# Initialize a new API client
|
17
|
+
# @param url [String] The base URL for the API
|
18
|
+
# @param jwt_secret [String, #call] The JWT secret or a callable that returns a JWT token
|
13
19
|
def initialize(url:, jwt_secret:)
|
14
20
|
@url = url
|
15
21
|
@jwt_secret = jwt_secret
|
16
22
|
@jwt_token = nil
|
17
23
|
@jwt_expiration = nil
|
24
|
+
@logger = Logger.new($stdout)
|
25
|
+
@client = build_client
|
18
26
|
end
|
19
27
|
|
28
|
+
# Store a conversation or add a message to an existing conversation
|
29
|
+
# @param conversation [Conversation] The conversation to store
|
30
|
+
# @param message [Message] The message to store
|
31
|
+
# @return [Hash] The API response data
|
32
|
+
def store_conversation_or_message(conversation, message)
|
33
|
+
if conversation.id.present?
|
34
|
+
store_message(conversation, message)
|
35
|
+
else
|
36
|
+
store_conversation(conversation)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Store a new conversation
|
41
|
+
# @param conversation [Conversation] The conversation to store
|
42
|
+
# @return [Hash] The API response data
|
43
|
+
# @raise [Error] If the API response is missing a conversation ID
|
20
44
|
def store_conversation(conversation)
|
21
45
|
response = client.post('api/ai_conversations', conversation.conversation_attributes_for_storage)
|
46
|
+
data = handle_response(response)
|
47
|
+
raise RubyConversations::Error, 'API response missing conversation ID' unless data['id'].present?
|
48
|
+
|
49
|
+
conversation.id = data['id']
|
50
|
+
data
|
51
|
+
end
|
52
|
+
|
53
|
+
# Store a message in an existing conversation
|
54
|
+
# @param conversation [Conversation] The conversation to add the message to
|
55
|
+
# @param message [Message] The message to store
|
56
|
+
# @return [Hash] The API response data
|
57
|
+
def store_message(conversation, message)
|
58
|
+
response = client.post("api/ai_conversations/#{conversation.id}/ai_messages",
|
59
|
+
{ ai_message: message.attributes_for_api })
|
22
60
|
handle_response(response)
|
23
61
|
end
|
24
62
|
|
63
|
+
# Fetch a prompt by name
|
64
|
+
# @param name [String] The name of the prompt to fetch
|
65
|
+
# @return [Hash] The prompt attributes
|
25
66
|
def fetch_prompt(name)
|
26
67
|
response = client.get("api/prompts/#{name}")
|
27
68
|
data = handle_response(response)
|
@@ -36,10 +77,6 @@ module RubyConversations
|
|
36
77
|
end
|
37
78
|
end
|
38
79
|
|
39
|
-
def client
|
40
|
-
@client ||= build_client
|
41
|
-
end
|
42
|
-
|
43
80
|
def build_client
|
44
81
|
Faraday.new(url: @url) do |faraday|
|
45
82
|
faraday.request :json
|
@@ -55,7 +92,7 @@ module RubyConversations
|
|
55
92
|
end
|
56
93
|
|
57
94
|
def token_expired?
|
58
|
-
@jwt_token
|
95
|
+
[@jwt_token, @jwt_expiration].any?(&:nil?) || Time.now.to_i >= @jwt_expiration
|
59
96
|
end
|
60
97
|
|
61
98
|
def refresh_jwt
|
@@ -79,12 +116,10 @@ module RubyConversations
|
|
79
116
|
end
|
80
117
|
|
81
118
|
def handle_response(response)
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
raise Error, "API request failed: #{response.body}"
|
87
|
-
end
|
119
|
+
return response.body if response.success?
|
120
|
+
|
121
|
+
@logger.error("API request failed: #{response.body.inspect}")
|
122
|
+
raise RubyConversations::Error, "API request failed: #{response.body}"
|
88
123
|
end
|
89
124
|
end
|
90
125
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_conversations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Shippy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-04-
|
11
|
+
date: 2025-04-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -192,6 +192,20 @@ dependencies:
|
|
192
192
|
- - ">="
|
193
193
|
- !ruby/object:Gem::Version
|
194
194
|
version: '0'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: rubocop-factory_bot
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - ">="
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
202
|
+
type: :development
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - ">="
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: '0'
|
195
209
|
- !ruby/object:Gem::Dependency
|
196
210
|
name: rubocop-rails
|
197
211
|
requirement: !ruby/object:Gem::Requirement
|
@@ -242,14 +256,13 @@ extensions: []
|
|
242
256
|
extra_rdoc_files: []
|
243
257
|
files:
|
244
258
|
- README.md
|
245
|
-
- app/models/concerns/
|
246
|
-
- app/models/concerns/
|
247
|
-
- app/models/concerns/
|
248
|
-
- app/models/concerns/
|
249
|
-
- app/models/concerns/
|
250
|
-
- app/models/concerns/
|
251
|
-
- app/models/concerns/
|
252
|
-
- app/models/concerns/ruby_conversations/storable.rb
|
259
|
+
- app/models/ruby_conversations/concerns/conversation_chat.rb
|
260
|
+
- app/models/ruby_conversations/concerns/conversation_messages.rb
|
261
|
+
- app/models/ruby_conversations/concerns/llm_credentials.rb
|
262
|
+
- app/models/ruby_conversations/concerns/message_api_attributes.rb
|
263
|
+
- app/models/ruby_conversations/concerns/message_attributes.rb
|
264
|
+
- app/models/ruby_conversations/concerns/message_processing.rb
|
265
|
+
- app/models/ruby_conversations/concerns/message_validation.rb
|
253
266
|
- app/models/ruby_conversations/conversation.rb
|
254
267
|
- app/models/ruby_conversations/message.rb
|
255
268
|
- app/models/ruby_conversations/message_builder.rb
|
@@ -1,49 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RubyConversations
|
4
|
-
# Handles chat-related functionality for Conversation
|
5
|
-
module ConversationChat
|
6
|
-
extend ActiveSupport::Concern
|
7
|
-
|
8
|
-
def execute(system_message: nil)
|
9
|
-
message = messages.last
|
10
|
-
raise ArgumentError, 'Conversation must have at least one message to execute' unless message
|
11
|
-
|
12
|
-
chat = setup_llm_chat(system_message: system_message)
|
13
|
-
response = chat.ask(message.request)
|
14
|
-
update_last_message_response(message, response)
|
15
|
-
json_response = save_conversation
|
16
|
-
{
|
17
|
-
response: response,
|
18
|
-
conversation: json_response
|
19
|
-
}
|
20
|
-
end
|
21
|
-
|
22
|
-
def llm
|
23
|
-
case model_identifier
|
24
|
-
when 'claude-3-7-sonnet'
|
25
|
-
'us.anthropic.claude-3-7-sonnet-20250219-v1:0'
|
26
|
-
else
|
27
|
-
model_identifier
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def setup_llm_chat(system_message: nil)
|
34
|
-
configure_llm_credentials
|
35
|
-
build_chat(system_message)
|
36
|
-
end
|
37
|
-
|
38
|
-
def update_last_message_response(message, response)
|
39
|
-
message.response = response.to_h.to_json
|
40
|
-
end
|
41
|
-
|
42
|
-
def build_chat(system_message)
|
43
|
-
chat = RubyLLM.chat(model: llm, provider: provider).with_temperature(0.0)
|
44
|
-
chat.with_tool(tool) if tool.present?
|
45
|
-
chat.add_message(role: :system, content: system_message) if system_message.present?
|
46
|
-
chat
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RubyConversations
|
4
|
-
# Handles message-related functionality for Conversation
|
5
|
-
module ConversationMessages
|
6
|
-
extend ActiveSupport::Concern
|
7
|
-
|
8
|
-
def ask_multiple(prompt_inputs, description: nil)
|
9
|
-
MessageBuilder.new(self).build_from_multiple_prompts(prompt_inputs, description: description)
|
10
|
-
self
|
11
|
-
end
|
12
|
-
|
13
|
-
def ask(name, description: nil, inputs: {})
|
14
|
-
MessageBuilder.new(self).build_from_single_prompt(name, description: description, inputs: inputs)
|
15
|
-
self
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RubyConversations
|
4
|
-
# Handles the management of LLM credentials for AI conversations.
|
5
|
-
# This concern provides methods for configuring LLM credentials from either
|
6
|
-
# environment variables or an AWS credential provider.
|
7
|
-
module LlmCredentials
|
8
|
-
extend ActiveSupport::Concern
|
9
|
-
|
10
|
-
private
|
11
|
-
|
12
|
-
def configure_llm_credentials
|
13
|
-
RubyLLM.configure do |config|
|
14
|
-
credentials = ENV['AWS_ACCESS_KEY_ID'].present? ? env_credentials : provider_credentials
|
15
|
-
config.bedrock_region = ENV.fetch('AWS_REGION', 'us-west-2')
|
16
|
-
config.bedrock_api_key = credentials[:api_key]
|
17
|
-
config.bedrock_secret_key = credentials[:secret_key]
|
18
|
-
config.bedrock_session_token = credentials[:session_token]
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def env_credentials
|
23
|
-
{
|
24
|
-
api_key: ENV.fetch('AWS_ACCESS_KEY_ID', nil),
|
25
|
-
secret_key: ENV.fetch('AWS_SECRET_ACCESS_KEY', nil),
|
26
|
-
session_token: ENV.fetch('AWS_SESSION_TOKEN', nil)
|
27
|
-
}
|
28
|
-
end
|
29
|
-
|
30
|
-
def provider_credentials
|
31
|
-
credential_provider = AwsCredentialProvider.instance
|
32
|
-
credential_provider.refresh_if_expired!
|
33
|
-
{
|
34
|
-
api_key: credential_provider.access_key_id,
|
35
|
-
secret_key: credential_provider.secret_access_key,
|
36
|
-
session_token: credential_provider.session_token
|
37
|
-
}
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RubyConversations
|
4
|
-
# Handles API-related attribute functionality for Message
|
5
|
-
module MessageApiAttributes
|
6
|
-
extend ActiveSupport::Concern
|
7
|
-
|
8
|
-
def message_attributes_for_api
|
9
|
-
{
|
10
|
-
request: request,
|
11
|
-
response: response,
|
12
|
-
model_identifier: model_identifier,
|
13
|
-
change_description: change_description,
|
14
|
-
llm: llm
|
15
|
-
}.compact
|
16
|
-
end
|
17
|
-
|
18
|
-
def message_base_attributes
|
19
|
-
{
|
20
|
-
'request' => request,
|
21
|
-
'response' => response,
|
22
|
-
'model_identifier' => model_identifier,
|
23
|
-
'change_description' => change_description
|
24
|
-
}
|
25
|
-
end
|
26
|
-
|
27
|
-
def remote_attributes
|
28
|
-
base_attributes.compact
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def base_attributes
|
34
|
-
MessageAttributes::ATTRIBUTE_NAMES.each_with_object({}) do |attr_name, attrs|
|
35
|
-
attrs[attr_name] = public_send(attr_name)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RubyConversations
|
4
|
-
# Handles attribute-related functionality for Message
|
5
|
-
module MessageAttributes
|
6
|
-
extend ActiveSupport::Concern
|
7
|
-
|
8
|
-
ATTRIBUTE_NAMES = %w[
|
9
|
-
id ai_conversation_id request response model_identifier
|
10
|
-
tool change_description llm created_at updated_at
|
11
|
-
].freeze
|
12
|
-
|
13
|
-
included do
|
14
|
-
# Define core message attributes
|
15
|
-
attr_accessor :request, :response, :model_identifier, :change_description
|
16
|
-
|
17
|
-
# Define relationship attributes
|
18
|
-
attr_accessor :ai_conversation, :message_prompts, :prompts
|
19
|
-
|
20
|
-
# Common validations
|
21
|
-
validates :llm, presence: true
|
22
|
-
validates :request, presence: true
|
23
|
-
|
24
|
-
# Override initialize to ensure arrays are initialized
|
25
|
-
def initialize(attributes = {})
|
26
|
-
@message_prompts = []
|
27
|
-
@prompts = []
|
28
|
-
super
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def remote_attributes
|
33
|
-
base_attributes.compact
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def base_attributes
|
39
|
-
ATTRIBUTE_NAMES.each_with_object({}) do |attr_name, attrs|
|
40
|
-
attrs[attr_name] = public_send(attr_name)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RubyConversations
|
4
|
-
# Handles message processing and initialization logic
|
5
|
-
module MessageProcessing
|
6
|
-
extend ActiveSupport::Concern
|
7
|
-
|
8
|
-
private
|
9
|
-
|
10
|
-
def process_message_prompts(msg_data)
|
11
|
-
prompts_data = extract_prompts_data(msg_data)
|
12
|
-
prompts_data.map { |prompt_data| build_message_prompt(prompt_data) }
|
13
|
-
end
|
14
|
-
|
15
|
-
def extract_prompts_data(msg_data)
|
16
|
-
prompts_data = msg_data.delete(:prompts) ||
|
17
|
-
msg_data.delete(:message_prompts) ||
|
18
|
-
msg_data.delete('message_prompts') ||
|
19
|
-
msg_data.delete(:ai_message_prompts_attributes) ||
|
20
|
-
msg_data.delete('ai_message_prompts_attributes')
|
21
|
-
prompts_data || []
|
22
|
-
end
|
23
|
-
|
24
|
-
def build_message_prompt(prompt_data)
|
25
|
-
inputs_data = extract_inputs_data(prompt_data)
|
26
|
-
processed_inputs = inputs_data.map { |input_data| build_message_input(input_data) }
|
27
|
-
|
28
|
-
prompt_instance = RubyConversations::MessagePrompt.new(
|
29
|
-
prompt_data.except('id', 'message_id', 'created_at', 'updated_at')
|
30
|
-
)
|
31
|
-
prompt_instance.message_inputs = processed_inputs
|
32
|
-
prompt_instance
|
33
|
-
end
|
34
|
-
|
35
|
-
def extract_inputs_data(prompt_data)
|
36
|
-
inputs_data = prompt_data.delete(:inputs) ||
|
37
|
-
prompt_data.delete(:message_inputs) ||
|
38
|
-
prompt_data.delete('message_inputs')
|
39
|
-
inputs_data || []
|
40
|
-
end
|
41
|
-
|
42
|
-
def build_message_input(input_data)
|
43
|
-
RubyConversations::MessageInput.new(
|
44
|
-
input_data.except('id', 'message_prompt_id', 'created_at', 'updated_at')
|
45
|
-
)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RubyConversations
|
4
|
-
# Provides validation methods for AI conversation messages, prompts, and inputs.
|
5
|
-
# This concern handles the validation of the message hierarchy in AI conversations,
|
6
|
-
# ensuring that all messages, their prompts, and inputs are valid.
|
7
|
-
module MessageValidation
|
8
|
-
extend ActiveSupport::Concern
|
9
|
-
|
10
|
-
private
|
11
|
-
|
12
|
-
def validate_messages
|
13
|
-
messages.each do |message|
|
14
|
-
validate_message_level(message)
|
15
|
-
message.ai_message_prompts.each do |prompt|
|
16
|
-
validate_prompt_level(prompt)
|
17
|
-
prompt.message_inputs.each do |input|
|
18
|
-
validate_input_level(input)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def validate_message_level(message)
|
25
|
-
return if message.valid?
|
26
|
-
|
27
|
-
message.errors.full_messages.each do |msg|
|
28
|
-
errors.add(:base, "Message error: #{msg}")
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def validate_prompt_level(prompt)
|
33
|
-
return if prompt.valid?
|
34
|
-
|
35
|
-
prompt.errors.full_messages.each do |msg|
|
36
|
-
errors.add(:base, "Message prompt error: #{msg}")
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def validate_input_level(input)
|
41
|
-
return if input.valid?
|
42
|
-
|
43
|
-
input.errors.full_messages.each do |msg|
|
44
|
-
errors.add(:base, "Message prompt input error: #{msg}")
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RubyConversations
|
4
|
-
# Module for handling storage functionality in Ruby Conversations
|
5
|
-
# Provides methods for storing and retrieving conversation data
|
6
|
-
module Storable
|
7
|
-
extend ActiveSupport::Concern
|
8
|
-
|
9
|
-
def save_conversation
|
10
|
-
RubyConversations.client.store_conversation(self)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|