Class: AssistantBrainEntry
Overview
== Schema Information
Table name: assistant_brain_entries
Database name: primary
id :bigint not null, primary key
applies_to_services :string default([]), not null, is an Array
category :string not null
priority :integer default("normal"), not null
rule :text not null
scope :string default("global"), not null
source :string default("manual"), not null
status :string default("active"), not null
title :string not null
created_at :datetime not null
updated_at :datetime not null
approved_by_id :bigint
assistant_conversation_id :bigint
suggested_by_id :bigint
user_id :bigint
Indexes
index_assistant_brain_entries_on_category (category)
index_assistant_brain_entries_on_scope (scope)
index_assistant_brain_entries_on_status_and_category (status,category)
index_assistant_brain_entries_on_status_and_priority (status,priority)
index_assistant_brain_entries_on_user_id (user_id)
Constant Summary
collapse
- CATEGORIES =
%w[url_rules product_data content_rules seo_rules general].freeze
- STATUSES =
%w[active pending inactive].freeze
- SOURCES =
%w[manual auto_learned].freeze
- SCOPES =
%w[global user].freeze
- CATEGORY_LABELS =
{
'url_rules' => 'URL Rules',
'product_data' => 'Product Data',
'content_rules' => 'Content Rules',
'seo_rules' => 'SEO Rules',
'general' => 'General'
}.freeze
Models::Embeddable::DEFAULT_MODEL, Models::Embeddable::MAX_CONTENT_LENGTH
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
#content_embeddings, embeddable_content_types, #embeddable_locales, #embedding_content_hash, embedding_partition_class, #embedding_stale?, #embedding_type_name, #embedding_vector, #find_content_embedding, #find_similar, #generate_all_embeddings!, #generate_chunked_embeddings!, #generate_embedding!, #has_embedding?, #locale_for_embedding, #needs_chunking?, regenerate_all_embeddings, semantic_search
ransackable_associations, ransackable_scopes, ransortable_attributes, #to_relation
#publish_event
Instance Attribute Details
#category ⇒ Object
53
|
# File 'app/models/assistant_brain_entry.rb', line 53
validates :category, presence: true, inclusion: { in: CATEGORIES }
|
#rule ⇒ Object
55
|
# File 'app/models/assistant_brain_entry.rb', line 55
validates :rule, presence: true
|
#scope ⇒ Object
58
|
# File 'app/models/assistant_brain_entry.rb', line 58
validates :scope, presence: true, inclusion: { in: SCOPES }
|
#source ⇒ Object
57
|
# File 'app/models/assistant_brain_entry.rb', line 57
validates :source, presence: true, inclusion: { in: SOURCES }
|
#status ⇒ Object
56
|
# File 'app/models/assistant_brain_entry.rb', line 56
validates :status, presence: true, inclusion: { in: STATUSES }
|
#title ⇒ Object
54
|
# File 'app/models/assistant_brain_entry.rb', line 54
validates :title, presence: true
|
#user_id ⇒ Object
59
|
# File 'app/models/assistant_brain_entry.rb', line 59
validates :user_id, presence: true, if: -> { scope == 'user' }
|
Class Method Details
A relation of AssistantBrainEntries that are active. Active Record Scope
61
|
# File 'app/models/assistant_brain_entry.rb', line 61
scope :active, -> { where(status: 'active') }
|
.auto_learned ⇒ ActiveRecord::Relation<AssistantBrainEntry>
A relation of AssistantBrainEntries that are auto learned. Active Record Scope
69
|
# File 'app/models/assistant_brain_entry.rb', line 69
scope :auto_learned, -> { where(source: 'auto_learned') }
|
.by_category ⇒ ActiveRecord::Relation<AssistantBrainEntry>
A relation of AssistantBrainEntries that are by category. Active Record Scope
64
|
# File 'app/models/assistant_brain_entry.rb', line 64
scope :by_category, -> { order(:category, :title) }
|
.for_context(user_id:, active_services: []) ⇒ ActiveRecord::Relation
Fetch all entries applicable to a given user and set of active tool services.
Returns global entries + the user's personal entries, filtered by service applicability.
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
# File 'app/models/assistant_brain_entry.rb', line 77
def self.for_context(user_id:, active_services: [])
base = active.where('scope = ? OR (scope = ? AND user_id = ?)', 'global', 'user', user_id)
if active_services.present?
base.where(
'cardinality(applies_to_services) = 0 OR applies_to_services && ARRAY[?]::varchar[]',
active_services
)
else
base.where('cardinality(applies_to_services) = 0')
end
end
|
A relation of AssistantBrainEntries that are for user. Active Record Scope
67
|
# File 'app/models/assistant_brain_entry.rb', line 67
scope :for_user, ->(user_id) { where(scope: 'user', user_id: user_id) }
|
.global_scope ⇒ ActiveRecord::Relation<AssistantBrainEntry>
A relation of AssistantBrainEntries that are global scope. Active Record Scope
65
|
# File 'app/models/assistant_brain_entry.rb', line 65
scope :global_scope, -> { where(scope: 'global') }
|
A relation of AssistantBrainEntries that are inactive. Active Record Scope
63
|
# File 'app/models/assistant_brain_entry.rb', line 63
scope :inactive, -> { where(status: 'inactive') }
|
A relation of AssistantBrainEntries that are manual. Active Record Scope
68
|
# File 'app/models/assistant_brain_entry.rb', line 68
scope :manual, -> { where(source: 'manual') }
|
A relation of AssistantBrainEntries that are pending. Active Record Scope
62
|
# File 'app/models/assistant_brain_entry.rb', line 62
scope :pending, -> { where(status: 'pending') }
|
.ransackable_attributes(_auth_object = nil) ⇒ Object
147
148
149
|
# File 'app/models/assistant_brain_entry.rb', line 147
def self.ransackable_attributes(_auth_object = nil)
%w[title rule category status source scope priority created_at updated_at]
end
|
.semantic_search_for(query, within_ids:, limit: 20) ⇒ Array<AssistantBrainEntry>
Find the most semantically relevant active brain entries for a user query.
Used by ChatService when the total entry count exceeds the injection threshold
and full verbatim injection would bloat the system prompt.
117
118
119
120
121
122
123
124
125
126
127
128
129
|
# File 'app/models/assistant_brain_entry.rb', line 117
def self.semantic_search_for(query, within_ids:, limit: 20)
return [] if query.blank? || within_ids.empty?
query_vector = ContentEmbedding.generate_query_embedding(query)
return [] unless query_vector
ContentEmbedding
.where(embeddable_type: 'AssistantBrainEntry', embeddable_id: within_ids, content_type: 'primary')
.nearest_neighbors(:embedding, query_vector, distance: :cosine)
.limit(limit)
.includes(:embeddable)
.filter_map(&:embeddable)
end
|
A relation of AssistantBrainEntries that are user scope. Active Record Scope
66
|
# File 'app/models/assistant_brain_entry.rb', line 66
scope :user_scope, -> { where(scope: 'user') }
|
Instance Method Details
#active? ⇒ Boolean
97
|
# File 'app/models/assistant_brain_entry.rb', line 97
def active? = status == 'active'
|
#applies_everywhere? ⇒ Boolean
104
105
106
|
# File 'app/models/assistant_brain_entry.rb', line 104
def applies_everywhere?
applies_to_services.blank?
end
|
#approve!(approver) ⇒ Object
139
140
141
|
# File 'app/models/assistant_brain_entry.rb', line 139
def approve!(approver)
update!(status: 'active', approved_by: approver)
end
|
#approved_by ⇒ Party
50
|
# File 'app/models/assistant_brain_entry.rb', line 50
belongs_to :approved_by, class_name: 'Party', optional: true
|
51
|
# File 'app/models/assistant_brain_entry.rb', line 51
belongs_to :assistant_conversation, class_name: 'AssistantConversation', optional: true
|
#auto_learned? ⇒ Boolean
100
|
# File 'app/models/assistant_brain_entry.rb', line 100
def auto_learned? = source == 'auto_learned'
|
#category_label ⇒ Object
93
94
95
|
# File 'app/models/assistant_brain_entry.rb', line 93
def category_label
CATEGORY_LABELS.fetch(category, category.humanize)
end
|
#content_for_embedding(_content_type = :primary) ⇒ Object
Embed the full title + rule text so retrieval matches on both what the rule
is about (title) and what it says (rule body).
135
136
137
|
# File 'app/models/assistant_brain_entry.rb', line 135
def content_for_embedding(_content_type = :primary)
"#{title}\n\n#{rule}"
end
|
#global? ⇒ Boolean
101
|
# File 'app/models/assistant_brain_entry.rb', line 101
def global? = scope == 'global'
|
#inactive? ⇒ Boolean
99
|
# File 'app/models/assistant_brain_entry.rb', line 99
def inactive? = status == 'inactive'
|
#pending? ⇒ Boolean
98
|
# File 'app/models/assistant_brain_entry.rb', line 98
def pending? = status == 'pending'
|
#personal? ⇒ Boolean
102
|
# File 'app/models/assistant_brain_entry.rb', line 102
def personal? = scope == 'user'
|
#reject! ⇒ Object
143
144
145
|
# File 'app/models/assistant_brain_entry.rb', line 143
def reject!
update!(status: 'inactive')
end
|
#suggested_by ⇒ Party
49
|
# File 'app/models/assistant_brain_entry.rb', line 49
belongs_to :suggested_by, class_name: 'Party', optional: true
|