Resolved conversations need a separate suggestion layer so repeated FAQ signals can be grouped without creating untrusted knowledge entries. This PR adds the persistence foundation only; it introduces no user-facing behavior by itself. ## Closes - [CW-7495](https://linear.app/chatwoot/issue/CW-7495/backend-llm-changes-to-make-conversation-faqs-as-signalssuggestions) (stacked PR 1/3; the issue is complete after the full stack lands) ## What changed - Added `captain_faq_suggestions` with question, answer, embedding, source count, and review status. - Added `captain_faq_observations` to retain conversation-level signals. - Added Captain assistant, account, and conversation associations. - Added vector and lookup indexes for semantic grouping. ## How to test This layer has no standalone UI behavior. Apply the migration and confirm Captain assistants can persist open FAQ suggestions with attached conversation observations.
43 lines
1.4 KiB
Ruby
43 lines
1.4 KiB
Ruby
# == Schema Information
|
|
#
|
|
# Table name: captain_faq_observations
|
|
#
|
|
# id :bigint not null, primary key
|
|
# generated_answer :text not null
|
|
# generated_question :string not null
|
|
# language :string default("en"), not null
|
|
# status :integer default("attached"), not null
|
|
# created_at :datetime not null
|
|
# updated_at :datetime not null
|
|
# account_id :bigint not null
|
|
# conversation_id :bigint not null
|
|
# faq_suggestion_id :bigint
|
|
#
|
|
class Captain::FaqObservation < ApplicationRecord
|
|
self.table_name = 'captain_faq_observations'
|
|
|
|
belongs_to :account
|
|
belongs_to :conversation, class_name: '::Conversation'
|
|
belongs_to :faq_suggestion, class_name: 'Captain::FaqSuggestion', optional: true, inverse_of: :observations
|
|
|
|
enum status: { attached: 0, discarded: 1 }
|
|
|
|
validates :generated_question, :generated_answer, :language, presence: true
|
|
validates :faq_suggestion, presence: true, if: :attached?
|
|
validate :faq_suggestion_belongs_to_account
|
|
|
|
before_validation :ensure_account
|
|
|
|
private
|
|
|
|
def ensure_account
|
|
self.account = conversation&.account
|
|
end
|
|
|
|
def faq_suggestion_belongs_to_account
|
|
return if faq_suggestion.blank? || faq_suggestion.account_id == account_id
|
|
|
|
errors.add(:faq_suggestion, :invalid)
|
|
end
|
|
end
|