module RubyLLM::ActiveRecord::ChatMethods
ChatMethods provides the RubyLLM::Chat API on ActiveRecord models declared with acts_as_chat, persisting every message to the database. Configuration methods return self so calls can be chained.
class Chat < ApplicationRecord acts_as_chat end chat = Chat.create!(model: 'gpt-5.6-luna') chat.ask "What is the capital of France?" chat.messages.count # => 2
Attributes
When true, skips the model registry lookup so unregistered model ids are accepted. Not persisted; set it again after reloading the record.
An optional RubyLLM::Context supplying per-chat configuration, used when building the underlying chat. Not persisted; set it again after reloading the record.
Overrides the wire protocol the provider would pick for the model, such as :responses or :chat_completions for OpenAI, or nil for the provider default. Not persisted; set it again after reloading the record.
Public Instance Methods
# File lib/ruby_llm/active_record/chat_methods.rb, line 509 def add_message(message_or_attributes) llm_message = message_or_attributes llm_message = llm_message.to_llm if llm_message.respond_to?(:to_llm) llm_message = RubyLLM::Message.new(llm_message) unless llm_message.is_a?(RubyLLM::Message) message_record = messages_association.create!(message_attributes(llm_message)) if llm_message.tool_call_id && (tool_call = find_tool_call(llm_message.tool_call_id)) tool_call.update!(result: message_record) end persist_content(message_record, llm_message.attachments) if llm_message.attachments.any? persist_tool_calls(llm_message.tool_calls, message_record:) if llm_message.tool_calls.present? @chat&.add_message(llm_message) message_record end
Persists message_or_attributes as a message record, including any attachments and tool calls. Accepts a RubyLLM::Message, an attributes Hash, or a record responding to to_llm. Returns the message record.
chat.add_message(role: :user, content: long_context)
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 344
Applies Chat#after_fallback and returns this record.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 320
Applies Chat#after_message and returns this record.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 332
Applies Chat#after_tool_result and returns this record.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 75 def approve(tool_call) record_tool_call_decision(tool_call, 'approved') end
# File lib/ruby_llm/active_record/chat_methods.rb, line 573 def ask(message = nil, with: nil, &) ask_later(message, with: with) complete(&) end
Persists message as a user message, then runs the conversation loop and returns the latest assistant RubyLLM::Message. The loop pauses when awaiting_approval? is true. Yields streaming chunks to a block.
chat.ask "What is the capital of France?" chat.ask "What's in this file?", with: "diagram.png"
# File lib/ruby_llm/active_record/chat_methods.rb, line 586 def ask_later(message = nil, with: nil) to_llm.raise_if_pending_tool_calls! add_message(role: :user, content: message, attachments: with) self end
Persists message as a user message without calling the model, so complete can run later. Returns self.
chat.ask_later "Summarize this document." chat.complete
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 90 def awaiting_approval? to_llm.awaiting_approval? end
Returns whether the conversation is waiting on tool calls that require approval and have no recorded decision. See RubyLLM::Chat#awaiting_approval?.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 338
Applies Chat#before_fallback and returns this record.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 314
Applies Chat#before_message and returns this record.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 308
Applies Chat#before_request and returns this record.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 326
Applies Chat#before_tool_call and returns this record.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 534 def cache_until_here message_record = messages_association.order(:id).last if message_record message_record.cache_until_here elsif @chat&.messages&.any? @chat.cache_until_here else raise ArgumentError, 'No messages to cache' end self end
Marks the latest persisted message as a prompt cache boundary, or the latest in-memory message when none is persisted yet. Returns self.
chat.with_instructions('Reusable analysis prompt').cache_until_here
Raises ArgumentError if the chat has no messages.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 350
Delegates to Chat#caching. See that method for arguments and return values.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 51 def cancel if persisted? update_column(:cancelled, true) else self[:cancelled] = true end @chat&.cancel self end
Requests cancellation of the current in-flight chat operation. The request is persisted so a background job can observe it from another process.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 64 def cancelled? @chat&.cancelled? || self[:cancelled] end
Returns whether this record or its memoized in-memory chat has a pending cancellation request.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 356
Delegates to Chat#citations. See that method for arguments and return values.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 604 def compact to_llm.compact rescue *COMPLETION_ERRORS => e cleanup_after_failure(e) raise end
Compacts the model context and persists its assistant Message without deleting earlier messages. See RubyLLM::Chat#compact.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 362
Delegates to Chat#compaction. See that method for arguments and return values.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 642 def complete(...) to_llm.complete(...) rescue *COMPLETION_ERRORS => e cleanup_after_failure(e) raise end
Runs the completion loop on the underlying chat, persisting each message, and returns the latest RubyLLM::Message. Pauses when a tool requires approval. When the API call fails, destroys the empty assistant message and any orphaned tool results, then re-raises the error.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 634 def complete? to_llm.complete? end
Returns whether the conversation has no pending work, neither a response to generate nor tool calls to run. See RubyLLM::Chat#complete?.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 368
Delegates to Chat#concurrency. See that method for arguments and return values.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 561 def cost records = ruby_llm_usages.to_a RubyLLM::Cost.aggregate(records.map(&:cost), complete: records.all?(&:cost_available?)) end
Returns a RubyLLM::Cost aggregating every persisted usage entry, including retries and attempts that did not produce a message.
chat.cost.total
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 457
Returns the number of input tokens the next request would carry, counted by the provider over the persisted conversation.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 83 def deny(tool_call) record_tool_call_decision(tool_call, 'denied') end
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 464
Yields each message in the conversation. Returns an Enumerator without a block.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 374
Delegates to Chat#end_user. See that method for arguments and return values.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 380
Delegates to Chat#fallbacks. See that method for arguments and return values.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 595 def generate(...) to_llm.generate(...) rescue *COMPLETION_ERRORS => e cleanup_after_failure(e) raise end
Makes a single generation attempt, persists the response, and returns it as a RubyLLM::Message. Tool calls in the response are not executed. See RubyLLM::Chat#generate.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 386
Delegates to Chat#headers. See that method for arguments and return values.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 392
Delegates to Chat#max_output_tokens. See that method for arguments and return values.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 119 def model=(value) if value.is_a?(RubyLLM::ActiveRecord::Model) @pending_model_id = nil @pending_provider = nil super else @pending_model_id = value.respond_to?(:id) ? value.id : value @pending_provider = value.provider if value.respond_to?(:provider) end end
Sets the chat’s model from an id, a RubyLLM::Model value, or the associated internal model record.
chat.model = 'gpt-5.6-luna'
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 139 def model_id model&.model_id || @pending_model_id end
Returns the model id of the associated model record, or nil.
chat.model_id # => "gpt-5.6-luna"
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 131 def model_id=(value) @pending_model_id = value end
Stores value as the model id, resolved to a model record before save.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 101 def pending_approvals ids = to_llm.pending_approvals.map(&:id) RubyLLM::ActiveRecord::ToolCall.where( tool_call_id: ids, message_type: self.class.message_class.constantize.polymorphic_name, message_id: messages_association.select(:id) ) end
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 150 def provider model&.provider || @pending_provider end
Returns the provider of the associated model record, or nil.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 145 def provider=(value) @pending_provider = value end
Stores value as the provider used when resolving the model id before save.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 398
Delegates to Chat#provider_options. See that method for arguments and return values.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 167 def reload(...) super sync_messages if @chat self end
Reloads the record from the database, Rails-style, and refreshes the underlying chat’s persisted message history to match. Runtime-only configuration such as tools, temperature, and callbacks is preserved. Returns self.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 475 PASSTHROUGH_CHAT_DELEGATES.each do |name| define_method(name) do |*args, **kwargs, &block| to_llm.public_send(name, *args, **kwargs, &block) end end
Returns the next request payload with before_request hooks applied.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 613 def run_tools to_llm.run_tools self end
Executes the pending tool calls and persists their results without calling the model. See RubyLLM::Chat#run_tools. Returns self.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 404
Delegates to Chat#schema. See that method for arguments and return values.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 410
Delegates to Chat#server_tools. See that method for arguments and return values.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 624 def step(...) to_llm.step(...) rescue *COMPLETION_ERRORS => e cleanup_after_failure(e) raise end
Advances the conversation by one move: runs the pending tool calls if there are any, otherwise generates a response. Returns nil once the chat is complete or waiting for approval. See RubyLLM::Chat#step.
chat.step until chat.complete? || chat.awaiting_approval?
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 416
Delegates to Chat#temperature. See that method for arguments and return values.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 422
Delegates to Chat#thinking. See that method for arguments and return values.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 159 def to_llm @chat ||= build_llm_chat # rubocop:disable Naming/MemoizedInstanceVariableName end
Returns the underlying RubyLLM::Chat for this record, building it on first call and memoizing it. The chat is loaded with the persisted messages and wired to persist new ones. Subsequent calls return the same chat without touching the database; use reload to refresh its message history from the record.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 552 def tokens RubyLLM::Tokens.aggregate(ruby_llm_usages.map(&:tokens)) end
Returns token usage aggregated across every persisted usage entry, including retries and attempts that did not produce a message.
chat.tokens.input
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 428
Delegates to Chat#tool_options. See that method for arguments and return values.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 439 CHAINABLE_CHAT_DELEGATES.each do |name| define_method(name) do |*args, **kwargs, &block| to_llm.public_send(name, *args, **kwargs, &block) self end end
Delegates to Chat#tools. See that method for arguments and return values.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 272
Applies Chat#with_caching and returns this record.
# File lib/ruby_llm/active_record/chat_methods.rb, line 266
Applies Chat#with_citations and returns this record.
# File lib/ruby_llm/active_record/chat_methods.rb, line 284
Applies Chat#with_compaction and returns this record.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 177 def with_context(value) self.context = value @chat&.with_context(value) self end
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 278
Applies Chat#with_end_user and returns this record.
# File lib/ruby_llm/active_record/chat_methods.rb, line 242
Applies Chat#with_fallbacks and returns this record.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 296
Applies Chat#with_headers and returns this record.
# File lib/ruby_llm/active_record/chat_methods.rb, line 194 def with_instructions(instructions, append: false, persist: true, cache_until_here: false) to_llm if persist if instructions.nil? clear_persisted_system_instructions else persist_system_instruction(instructions, append:, cache_until_here:) end else store_unpersisted_instruction(instructions, append:, cache_until_here:) end sync_messages self end
Sets the system instructions, persisting them as a message with the :system role. Replaces any persisted system messages unless append: is true. Pass persist: false to apply the instructions only to the in-memory chat for this record instance. With cache_until_here: true the instruction becomes an explicit prompt cache boundary. Returns self.
chat.with_instructions "You are a Ruby expert." chat.with_instructions "Use short bullet points.", append: true chat.with_instructions current_context, persist: false
# File lib/ruby_llm/active_record/chat_methods.rb, line 254
Applies Chat#with_max_output_tokens and returns this record.
# File lib/ruby_llm/active_record/chat_methods.rb, line 490 def with_model(model_name, provider: nil, protocol: nil, assume_model_exists: false) model_name ||= (context&.config || RubyLLM.config).default_model self.model = model_name self.provider = provider if provider self.protocol = protocol self.assume_model_exists = assume_model_exists resolve_model save! to_llm.with_model(model_id, provider: provider&.to_sym, protocol:, assume_model_exists:) self end
Switches the chat to model_name, resolving and saving the model record and updating the underlying chat. Falls back to the configured default model when model_name is nil. Pass protocol: to override the wire protocol the provider would pick for the model. Returns self.
chat.with_model 'claude-sonnet-5'
# File lib/ruby_llm/active_record/chat_methods.rb, line 290
Applies Chat#with_provider_options and returns this record.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 302
Applies Chat#with_schema and returns this record.
# File lib/ruby_llm/active_record/chat_methods.rb, line 236
Applies Chat#with_server_tools and returns this record.
# File lib/ruby_llm/active_record/chat_methods.rb, line 248
Applies Chat#with_temperature and returns this record.
# File lib/ruby_llm/active_record/chat_methods.rb, line 260
Applies Chat#with_thinking and returns this record.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 230
Applies Chat#with_tool_options and returns this record.
Source
# File lib/ruby_llm/active_record/chat_methods.rb, line 224
Applies Chat#with_tools and returns this record.