feat: Add support for realtime-events in copilot-threads and copilot-messages (#11557)
- Add API support for creating a thread - Add API support for creating a message - Remove uuid from thread (no longer required, we will use existing websocket connection to send messages) - Update message_type to a column (user, assistant, assistant_thinking)
This commit is contained in:
@@ -1,21 +1,28 @@
|
||||
class Api::V1::Accounts::Captain::CopilotMessagesController < Api::V1::Accounts::BaseController
|
||||
before_action :current_account
|
||||
before_action -> { check_authorization(Captain::Assistant) }
|
||||
before_action :set_copilot_thread
|
||||
|
||||
def index
|
||||
@copilot_messages = @copilot_thread
|
||||
.copilot_messages
|
||||
.includes(:copilot_thread)
|
||||
.order(created_at: :asc)
|
||||
.page(permitted_params[:page] || 1)
|
||||
.per(1000)
|
||||
end
|
||||
|
||||
def create
|
||||
@copilot_message = @copilot_thread.copilot_messages.create!(
|
||||
message: params[:message],
|
||||
message_type: :user
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_copilot_thread
|
||||
@copilot_thread = Current.account.copilot_threads.find_by!(
|
||||
uuid: params[:copilot_thread_id], user_id: Current.user.id
|
||||
id: params[:copilot_thread_id],
|
||||
user: Current.user
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -1,18 +1,41 @@
|
||||
class Api::V1::Accounts::Captain::CopilotThreadsController < Api::V1::Accounts::BaseController
|
||||
before_action :current_account
|
||||
before_action -> { check_authorization(Captain::Assistant) }
|
||||
before_action :ensure_message, only: :create
|
||||
|
||||
def index
|
||||
@copilot_threads = Current.account.copilot_threads
|
||||
.where(user_id: Current.user.id)
|
||||
.includes(:user)
|
||||
.includes(:user, :assistant)
|
||||
.order(created_at: :desc)
|
||||
.page(permitted_params[:page] || 1)
|
||||
.per(5)
|
||||
end
|
||||
|
||||
def create
|
||||
ActiveRecord::Base.transaction do
|
||||
@copilot_thread = Current.account.copilot_threads.create!(
|
||||
title: copilot_thread_params[:message],
|
||||
user: Current.user,
|
||||
assistant: assistant
|
||||
)
|
||||
|
||||
@copilot_thread.copilot_messages.create!(message_type: :user, message: copilot_thread_params[:message])
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensure_message
|
||||
return render_could_not_create_error('Message is required') if copilot_thread_params[:message].blank?
|
||||
end
|
||||
|
||||
def assistant
|
||||
Current.account.captain_assistants.find(copilot_thread_params[:assistant_id])
|
||||
end
|
||||
|
||||
def copilot_thread_params
|
||||
params.permit(:message, :assistant_id)
|
||||
end
|
||||
|
||||
def permitted_params
|
||||
params.permit(:page)
|
||||
end
|
||||
|
||||
13
enterprise/app/listeners/captain_listener.rb
Normal file
13
enterprise/app/listeners/captain_listener.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
class CaptainListener < BaseListener
|
||||
include ::Events::Types
|
||||
|
||||
def conversation_resolved(event)
|
||||
conversation = extract_conversation_and_account(event)[0]
|
||||
assistant = conversation.inbox.captain_assistant
|
||||
|
||||
return unless conversation.inbox.captain_active?
|
||||
|
||||
Captain::Llm::ContactNotesService.new(assistant, conversation).generate_and_update_notes if assistant.config['feature_memory'].present?
|
||||
Captain::Llm::ConversationFaqService.new(assistant, conversation).generate_and_deduplicate if assistant.config['feature_faq'].present?
|
||||
end
|
||||
end
|
||||
11
enterprise/app/listeners/enterprise/action_cable_listener.rb
Normal file
11
enterprise/app/listeners/enterprise/action_cable_listener.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
module Enterprise::ActionCableListener
|
||||
include Events::Types
|
||||
def copilot_message_created(event)
|
||||
copilot_message = event.data[:copilot_message]
|
||||
copilot_thread = copilot_message.copilot_thread
|
||||
account = copilot_thread.account
|
||||
user = copilot_thread.user
|
||||
|
||||
broadcast(account, [user.pubsub_token], COPILOT_MESSAGE_CREATED, copilot_message.push_event_data)
|
||||
end
|
||||
end
|
||||
@@ -29,6 +29,7 @@ class Captain::Assistant < ApplicationRecord
|
||||
has_many :inboxes,
|
||||
through: :captain_inboxes
|
||||
has_many :messages, as: :sender, dependent: :nullify
|
||||
has_many :copilot_threads, dependent: :destroy_async
|
||||
|
||||
validates :name, presence: true
|
||||
validates :description, presence: true
|
||||
|
||||
@@ -4,24 +4,47 @@
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# message :jsonb not null
|
||||
# message_type :string not null
|
||||
# message_type :integer default("user")
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# account_id :bigint not null
|
||||
# copilot_thread_id :bigint not null
|
||||
# user_id :bigint not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_copilot_messages_on_account_id (account_id)
|
||||
# index_copilot_messages_on_copilot_thread_id (copilot_thread_id)
|
||||
# index_copilot_messages_on_user_id (user_id)
|
||||
#
|
||||
class CopilotMessage < ApplicationRecord
|
||||
belongs_to :copilot_thread
|
||||
belongs_to :user
|
||||
belongs_to :account
|
||||
|
||||
validates :message_type, presence: true, inclusion: { in: %w[user assistant assistant_thinking] }
|
||||
before_validation :ensure_account
|
||||
|
||||
enum message_type: { user: 0, assistant: 1, assistant_thinking: 2 }
|
||||
|
||||
validates :message_type, presence: true, inclusion: { in: message_types.keys }
|
||||
validates :message, presence: true
|
||||
|
||||
after_create_commit :broadcast_message
|
||||
|
||||
def push_event_data
|
||||
{
|
||||
id: id,
|
||||
message: message,
|
||||
message_type: message_type,
|
||||
created_at: created_at.to_i,
|
||||
copilot_thread: copilot_thread.push_event_data
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensure_account
|
||||
self.account = copilot_thread.account
|
||||
end
|
||||
|
||||
def broadcast_message
|
||||
Rails.configuration.dispatcher.dispatch(COPILOT_MESSAGE_CREATED, Time.zone.now, copilot_message: self)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,25 +2,47 @@
|
||||
#
|
||||
# Table name: copilot_threads
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# title :string not null
|
||||
# uuid :uuid not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# account_id :bigint not null
|
||||
# user_id :bigint not null
|
||||
# id :bigint not null, primary key
|
||||
# title :string not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# account_id :bigint not null
|
||||
# assistant_id :integer
|
||||
# user_id :bigint not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_copilot_threads_on_account_id (account_id)
|
||||
# index_copilot_threads_on_user_id (user_id)
|
||||
# index_copilot_threads_on_uuid (uuid) UNIQUE
|
||||
# index_copilot_threads_on_account_id (account_id)
|
||||
# index_copilot_threads_on_assistant_id (assistant_id)
|
||||
# index_copilot_threads_on_user_id (user_id)
|
||||
#
|
||||
class CopilotThread < ApplicationRecord
|
||||
belongs_to :user
|
||||
belongs_to :account
|
||||
has_many :copilot_messages, dependent: :destroy
|
||||
belongs_to :assistant, class_name: 'Captain::Assistant'
|
||||
has_many :copilot_messages, dependent: :destroy_async
|
||||
|
||||
validates :title, presence: true
|
||||
validates :uuid, presence: true, uniqueness: true
|
||||
|
||||
def push_event_data
|
||||
{
|
||||
id: id,
|
||||
title: title,
|
||||
created_at: created_at.to_i,
|
||||
user: user.push_event_data,
|
||||
account_id: account_id
|
||||
}
|
||||
end
|
||||
|
||||
def previous_history
|
||||
copilot_messages
|
||||
.where(message_type: %w[user assistant])
|
||||
.order(created_at: :asc)
|
||||
.map do |copilot_message|
|
||||
{
|
||||
content: copilot_message.message,
|
||||
role: copilot_message.message_type
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
json.partial! 'api/v1/models/captain/copilot_message', formats: [:json], resource: @copilot_message
|
||||
@@ -1,8 +1,5 @@
|
||||
json.payload do
|
||||
json.array! @copilot_messages do |message|
|
||||
json.id message.id
|
||||
json.message message.message
|
||||
json.message_type message.message_type
|
||||
json.created_at message.created_at.to_i
|
||||
json.partial! 'api/v1/models/captain/copilot_message', formats: [:json], resource: message
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
json.partial! 'api/v1/models/captain/copilot_thread', formats: [:json], resource: @copilot_thread
|
||||
@@ -1,12 +1,5 @@
|
||||
json.payload do
|
||||
json.array! @copilot_threads do |thread|
|
||||
json.id thread.id
|
||||
json.title thread.title
|
||||
json.uuid thread.uuid
|
||||
json.created_at thread.created_at.to_i
|
||||
json.user do
|
||||
json.id thread.user.id
|
||||
json.name thread.user.name
|
||||
end
|
||||
json.partial! 'api/v1/models/captain/copilot_thread', resource: thread
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
json.id resource.id
|
||||
json.message resource.message
|
||||
json.message_type resource.message_type
|
||||
json.created_at resource.created_at.to_i
|
||||
json.copilot_thread resource.copilot_thread.push_event_data
|
||||
json.account_id resource.account_id
|
||||
@@ -0,0 +1,6 @@
|
||||
json.id resource.id
|
||||
json.title resource.title
|
||||
json.created_at resource.created_at.to_i
|
||||
json.user resource.user.push_event_data
|
||||
json.assistant resource.assistant.push_event_data
|
||||
json.account_id resource.account_id
|
||||
Reference in New Issue
Block a user