app/models/ruby_conversations/message.rb
# frozen_string_literal: true require 'active_model' module RubyConversations # Represents a message in a conversation, either from the user or AI class Message include ActiveModel::Model include Concerns::MessageAttributes include Concerns::MessageApiAttributes # Define attributes attr_accessor :id, :conversation_id, :llm, :temperature, :tool, :metadata, :created_at, :updated_at, :message_prompts # Constants ROLES = { user: 'user', assistant: 'assistant' }.freeze def initialize(attributes = {}) @message_prompts = [] prompts_attributes = extract_nested_attributes!(attributes, :message_prompts) # Also check for Rails-style nested attributes prompts_attributes ||= extract_nested_attributes!(attributes, :ai_message_prompts_attributes) super initialize_message_prompts(prompts_attributes) end def initialize_message_prompts(attributes_array) (attributes_array || []).each do |attrs| next if attrs.blank? @message_prompts << RubyConversations::MessagePrompt.new(attrs) end end def message_prompts_attributes=(attributes_array) @message_prompts = [] initialize_message_prompts(attributes_array) end # Alias for Rails nested attributes convention alias ai_message_prompts_attributes= message_prompts_attributes= # Attributes method for serialization/logging def attributes base_attributes.merge( 'llm' => llm, 'temperature' => temperature, 'tool' => tool, 'ai_message_prompts_attributes' => message_prompts.map(&:attributes) ) end # Method for API serialization def attributes_for_api { id: id, conversation_id: conversation_id, metadata: metadata, llm: llm, tool: tool, ai_message_prompts_attributes: message_prompts.map(&:attributes_for_api) }.merge(message_attributes_for_api).compact end # Helper methods for role checking def user? request.present? && response.blank? end def assistant? response.present? end def prompt_inputs message_prompts.flat_map(&:message_inputs) end private def base_attributes { 'id' => id, 'conversation_id' => conversation_id, 'metadata' => metadata, 'created_at' => created_at, 'updated_at' => updated_at }.merge(message_base_attributes) end def extract_nested_attributes!(attributes, key) nested = attributes.delete(key) nested ||= attributes.delete(key.to_s) nested end end # Alias for compatibility with Zeitwerk AIMessage = Message end