Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
12 changes: 6 additions & 6 deletions lib/ruby_llm/active_record/acts_as.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,19 @@ def acts_as_chat(message_class: 'Message', tool_call_class: 'ToolCall')
to: :to_llm
end

def acts_as_message(chat_class: 'Chat', tool_call_class: 'ToolCall', touch_chat: false) # rubocop:disable Metrics/MethodLength
def acts_as_message(chat_class: 'Chat', tool_call_class: 'ToolCall', **options) # rubocop:disable Metrics/MethodLength
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for whatever it's worth, I didn't do this just to avoid changing the call signature, but +1.

include MessageMethods

@chat_class = chat_class.to_s
@chat_foreign_key = "#{@chat_class.underscore}_id"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also for what it's worth, I think changing the existing code to ActiveSupport::Inflector.foreign_key(@chat_class) would do what you want.

@chat_foreign_key = options[:chat_foreign_key] || @chat_class.foreign_key
@tool_call_class = tool_call_class.to_s
@tool_call_foreign_key = "#{@tool_call_class.underscore}_id"
@tool_call_foreign_key = options[:tool_call_foreign_key] || @tool_call_class.foreign_key

belongs_to :chat,
class_name: @chat_class,
foreign_key: @chat_foreign_key,
inverse_of: :messages,
touch: touch_chat
touch: options[:touch_chat]

has_many :tool_calls,
class_name: @tool_call_class,
Expand All @@ -51,9 +51,9 @@ def acts_as_message(chat_class: 'Chat', tool_call_class: 'ToolCall', touch_chat:
delegate :tool_call?, :tool_result?, :tool_results, to: :to_llm
end

def acts_as_tool_call(message_class: 'Message') # rubocop:disable Metrics/MethodLength
def acts_as_tool_call(message_class: 'Message', **options) # rubocop:disable Metrics/MethodLength
@message_class = message_class.to_s
@message_foreign_key = "#{@message_class.underscore}_id"
@message_foreign_key = options[:message_foreign_key] || "#{@message_class.underscore}_id"

belongs_to :message,
class_name: @message_class,
Expand Down
19 changes: 11 additions & 8 deletions spec/ruby_llm/active_record/acts_as_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,12 @@ class Chat < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock
acts_as_chat
end

class BotChat < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock,RSpec/LeakyConstantDeclaration
include RubyLLM::ActiveRecord::ActsAs
acts_as_chat message_class: 'BotMessage', tool_call_class: 'BotToolCall'
module Assistants # rubocop:disable Lint/ConstantDefinitionInBlock,RSpec/LeakyConstantDeclaration
class BotChat < ActiveRecord::Base # rubocop:disable RSpec/LeakyConstantDeclaration
self.table_name = 'bot_chats'
include RubyLLM::ActiveRecord::ActsAs
acts_as_chat message_class: 'BotMessage', tool_call_class: 'BotToolCall'
end
end

class Message < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock,RSpec/LeakyConstantDeclaration
Expand All @@ -86,7 +89,7 @@ class Message < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBl

class BotMessage < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock,RSpec/LeakyConstantDeclaration
include RubyLLM::ActiveRecord::ActsAs
acts_as_message chat_class: 'BotChat', tool_call_class: 'BotToolCall'
acts_as_message chat_class: 'Assistants::BotChat', tool_call_class: 'BotToolCall'
end

class ToolCall < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock,RSpec/LeakyConstantDeclaration
Expand Down Expand Up @@ -115,7 +118,7 @@ def execute(expression:)

shared_examples 'a chainable chat method' do |method_name, *args|
it "returns a Chat instance for ##{method_name}" do
[Chat, BotChat].each do |chat_class|
[Chat, Assistants::BotChat].each do |chat_class|
chat = chat_class.create!(model_id: 'gpt-4.1-nano')
result = chat.public_send(method_name, *args)
expect(result).to be_a(chat_class)
Expand All @@ -125,7 +128,7 @@ def execute(expression:)

shared_examples 'a chainable callback method' do |callback_name|
it "supports #{callback_name} callback" do # rubocop:disable RSpec/ExampleLength
[Chat, BotChat].each do |chat_class|
[Chat, Assistants::BotChat].each do |chat_class|
chat = chat_class.create!(model_id: 'gpt-4.1-nano')
result = chat.public_send(callback_name) do
# no-op for testing
Expand Down Expand Up @@ -172,7 +175,7 @@ def execute(expression:)

describe 'with_tools functionality' do
it 'returns a Chat instance when using with_tool' do
[Chat, BotChat].each do |chat_class|
[Chat, Assistants::BotChat].each do |chat_class|
chat = chat_class.create!(model_id: 'gpt-4.1-nano')
with_tool_result = chat.with_tool(Calculator)
expect(with_tool_result).to be_a(chat_class)
Expand All @@ -196,7 +199,7 @@ def execute(expression:)
it_behaves_like 'a chainable callback method', :on_end_message

it 'supports method chaining with tools' do # rubocop:disable RSpec/ExampleLength
[Chat, BotChat].each do |chat_class|
[Chat, Assistants::BotChat].each do |chat_class|
chat = chat_class.create!(model_id: 'gpt-4.1-nano')
chat.with_tool(Calculator)
.with_temperature(0.5)
Expand Down