Class: AssistantMessage

Inherits:
ApplicationRecord show all
Includes:
PgSearch::Model
Defined in:
app/models/assistant_message.rb

Overview

== Schema Information

Table name: assistant_messages
Database name: primary

id :bigint not null, primary key
cache_until_here :boolean default(FALSE), not null
citations :jsonb
compacted_content :text
content :text
content_raw :json
finish_reason :string
grounding_assessment :jsonb
model_id :string
provider :string
role :string not null
thinking_signature :text
thinking_text :text
created_at :datetime not null
updated_at :datetime not null
assistant_conversation_id :bigint not null
sender_id :bigint

Indexes

index_assistant_messages_on_assistant_conversation_id (assistant_conversation_id)
index_assistant_messages_on_provider_and_model_id (provider,model_id)
index_assistant_messages_on_role (role)
index_assistant_messages_on_sender_id (sender_id)

Foreign Keys

fk_rails_... (assistant_conversation_id => assistant_conversations.id)

Token and cost figures are NOT columns here any more — RubyLLM 2.0 keeps a
per-attempt ledger in ruby_llm_usage_entries, reachable as message.tokens
/ message.cost (nil when the message recorded no usage).

Constant Summary collapse

LEGACY_OPPORTUNITY_BRIEFING_PREFIX =

Prefix used to identify legacy opportunity briefing prompts.

'Prepare an on-demand sales briefing'

Constants included from Models::Schedulable

Models::Schedulable::SIMPLE_FORM_OPTIONS

Instance Attribute Summary collapse

Has many collapse

Has one collapse

Belongs to collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ApplicationRecord

ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation

Methods included from Models::Schedulable

config

Methods included from Models::AfterCommittable

#after_commit

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#assistant_conversationAssistantConversation (readonly)



81
# File 'app/models/assistant_message.rb', line 81

validates :assistant_conversation, presence: true

#roleString (readonly)

Returns:

  • (String)


79
# File 'app/models/assistant_message.rb', line 79

validates :role, presence: true

Class Method Details

.assistant_messagesActiveRecord::Relation<AssistantMessage>

A relation of AssistantMessages that are assistant messages. Active Record Scope

Returns:

See Also:



88
# File 'app/models/assistant_message.rb', line 88

scope :assistant_messages, -> { where(role: 'assistant') }

.by_senderActiveRecord::Relation<AssistantMessage>

A relation of AssistantMessages that are by sender. Active Record Scope

Returns:

See Also:



91
# File 'app/models/assistant_message.rb', line 91

scope :by_sender, ->(party_id) { where(sender_id: party_id) }

.system_messagesActiveRecord::Relation<AssistantMessage>

A relation of AssistantMessages that are system messages. Active Record Scope

Returns:

See Also:



89
# File 'app/models/assistant_message.rb', line 89

scope :system_messages, -> { where(role: 'system') }

.user_messagesActiveRecord::Relation<AssistantMessage>

A relation of AssistantMessages that are user messages. Active Record Scope

Returns:

See Also:



87
# File 'app/models/assistant_message.rb', line 87

scope :user_messages, -> { where(role: 'user') }

.with_thinkingActiveRecord::Relation<AssistantMessage>

A relation of AssistantMessages that are with thinking. Active Record Scope

Returns:

See Also:



90
# File 'app/models/assistant_message.rb', line 90

scope :with_thinking, -> { where.not(thinking_text: nil) }

Instance Method Details

#assistant_parent_tool_callAssistantToolCall?

Returns:



69
70
# File 'app/models/assistant_message.rb', line 69

has_one :assistant_parent_tool_call, -> { where(result_type: AssistantToolCall::MESSAGE_TYPE) },
class_name: 'AssistantToolCall', foreign_key: :result_id, inverse_of: :result

#assistant_tool_callsActiveRecord::Relation<AssistantToolCall>

Tool calls moved into RubyLLM-owned storage in 2.0. AssistantToolCall is now
the application's read model over that table (see the class for why) — these
two associations keep the app's own names so the seam holds. dependent: is
deliberately absent: acts_as_message's own ruby_llm_tool_calls association
already destroys/nullifies the same rows.

Do not call the result-side association parent_tool_call: RubyLLM's
MessageMethods defines that name and returns a RubyLLM::ToolCall value object,
shadowing ActiveRecord's generated association method. Keeping the record and
value-object names distinct prevents integer row ids from being confused with
provider ids such as toolu_... during replay (AppSignal #3808 / #3830).

Returns:

See Also:



66
67
# File 'app/models/assistant_message.rb', line 66

has_many :assistant_tool_calls, -> { where(message_type: AssistantToolCall::MESSAGE_TYPE).order(:id) },
class_name: 'AssistantToolCall', foreign_key: :message_id, inverse_of: :assistant_message

#parent_tool_callAssistantToolCall?

Returns:



72
73
# File 'app/models/assistant_message.rb', line 72

has_one :parent_tool_call, -> { where(result_type: AssistantToolCall::MESSAGE_TYPE) },
class_name: 'AssistantToolCall', foreign_key: :result_id, deprecated: true

#senderParty?

Returns:



76
# File 'app/models/assistant_message.rb', line 76

belongs_to :sender, class_name: 'Party', optional: true

#template_initiated?Boolean

Application-generated prompts are persisted as user turns for LLM history,
but should not be presented as if a rep typed their full internal payload.

Returns:

  • (Boolean)


101
102
103
104
105
106
# File 'app/models/assistant_message.rb', line 101

def template_initiated?
  return false unless role == 'user' && content.present?
  return false unless assistant_conversation

  assistant_conversation.template_prompt?(content) || legacy_opportunity_briefing_prompt?
end