From 07d1d86b068bbfc3813cfd48e15092d0b602854a Mon Sep 17 00:00:00 2001 From: Bruno Bornsztein Date: Wed, 7 May 2025 08:45:29 -0500 Subject: [PATCH] use foreign_key instead of to_s --- lib/ruby_llm/active_record/acts_as.rb | 12 ++++++------ spec/ruby_llm/active_record/acts_as_spec.rb | 19 +++++++++++-------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/lib/ruby_llm/active_record/acts_as.rb b/lib/ruby_llm/active_record/acts_as.rb index 408fb2640..2d8424e21 100644 --- a/lib/ruby_llm/active_record/acts_as.rb +++ b/lib/ruby_llm/active_record/acts_as.rb @@ -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 include MessageMethods @chat_class = chat_class.to_s - @chat_foreign_key = "#{@chat_class.underscore}_id" + @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, @@ -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, diff --git a/spec/ruby_llm/active_record/acts_as_spec.rb b/spec/ruby_llm/active_record/acts_as_spec.rb index 9715d5352..1fdb664d4 100644 --- a/spec/ruby_llm/active_record/acts_as_spec.rb +++ b/spec/ruby_llm/active_record/acts_as_spec.rb @@ -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 @@ -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 @@ -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) @@ -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 @@ -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) @@ -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)