# Pull Request Template ## Description Captain v1 does not have access to contact attributes. Added a toggle to let user choose if they want contact information available to Captain. ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. Specs and locally <img width="1924" height="740" alt="CleanShot 2026-03-19 at 18 48 19@2x" src="https://github.com/user-attachments/assets/353cfeaa-cd58-40eb-89e7-d660a1dc1185" /> ![Uploading CleanShot 2026-03-19 at 18.53.26@2x.png…]() ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules
129 lines
4.1 KiB
Ruby
129 lines
4.1 KiB
Ruby
class Captain::Copilot::ChatService < Llm::BaseAiService
|
|
include Captain::ChatHelper
|
|
|
|
attr_reader :assistant, :account, :user, :copilot_thread, :previous_history, :messages
|
|
|
|
def initialize(assistant, config)
|
|
super()
|
|
|
|
@assistant = assistant
|
|
@account = assistant.account
|
|
@user = nil
|
|
@copilot_thread = nil
|
|
@previous_history = []
|
|
@conversation = @account.conversations.find_by(display_id: config[:conversation_id])
|
|
@conversation_id = @conversation&.display_id
|
|
|
|
setup_user(config)
|
|
setup_message_history(config)
|
|
@tools = build_tools
|
|
@messages = build_messages(config)
|
|
end
|
|
|
|
def generate_response(input)
|
|
@messages << { role: 'user', content: input } if input.present?
|
|
response = request_chat_completion
|
|
|
|
Rails.logger.debug { "#{self.class.name} Assistant: #{@assistant.id}, Received response #{response}" }
|
|
Rails.logger.info(
|
|
"#{self.class.name} Assistant: #{@assistant.id}, Incrementing response usage for account #{@account.id}"
|
|
)
|
|
@account.increment_response_usage
|
|
|
|
response
|
|
end
|
|
|
|
private
|
|
|
|
def setup_user(config)
|
|
@user = @account.users.find_by(id: config[:user_id]) if config[:user_id].present?
|
|
end
|
|
|
|
def build_messages(config)
|
|
messages= [system_message]
|
|
messages << account_id_context
|
|
messages += @previous_history if @previous_history.present?
|
|
messages += current_viewing_history(config[:conversation_id]) if config[:conversation_id].present?
|
|
messages
|
|
end
|
|
|
|
def setup_message_history(config)
|
|
Rails.logger.info(
|
|
"#{self.class.name} Assistant: #{@assistant.id}, Previous History: #{config[:previous_history]&.length || 0}, Language: #{config[:language]}"
|
|
)
|
|
|
|
@copilot_thread = @account.copilot_threads.find_by(id: config[:copilot_thread_id]) if config[:copilot_thread_id].present?
|
|
@previous_history = if @copilot_thread.present?
|
|
@copilot_thread.previous_history
|
|
else
|
|
config[:previous_history].presence || []
|
|
end
|
|
end
|
|
|
|
def build_tools
|
|
tools = []
|
|
|
|
tools << Captain::Tools::SearchDocumentationService.new(@assistant, user: @user)
|
|
tools << Captain::Tools::Copilot::GetConversationService.new(@assistant, user: @user)
|
|
tools << Captain::Tools::Copilot::SearchConversationsService.new(@assistant, user: @user)
|
|
tools << Captain::Tools::Copilot::GetContactService.new(@assistant, user: @user)
|
|
tools << Captain::Tools::Copilot::GetArticleService.new(@assistant, user: @user)
|
|
tools << Captain::Tools::Copilot::SearchArticlesService.new(@assistant, user: @user)
|
|
tools << Captain::Tools::Copilot::SearchContactsService.new(@assistant, user: @user)
|
|
tools << Captain::Tools::Copilot::SearchLinearIssuesService.new(@assistant, user: @user)
|
|
|
|
tools.select(&:active?)
|
|
end
|
|
|
|
def system_message
|
|
{
|
|
role: 'system',
|
|
content: Captain::Llm::SystemPromptsService.copilot_response_generator(
|
|
@assistant.config['product_name'],
|
|
tools_summary,
|
|
@assistant.config
|
|
)
|
|
}
|
|
end
|
|
|
|
def tools_summary
|
|
@tools.map { |tool| "- #{tool.class.name}: #{tool.class.description}" }.join("\n")
|
|
end
|
|
|
|
def account_id_context
|
|
{
|
|
role: 'system',
|
|
content: "The current account id is #{@account.id}. The account is using #{@account.locale_english_name} as the language."
|
|
}
|
|
end
|
|
|
|
def current_viewing_history(conversation_id)
|
|
conversation = @account.conversations.find_by(display_id: conversation_id)
|
|
return [] unless conversation
|
|
|
|
Rails.logger.info("#{self.class.name} Assistant: #{@assistant.id}, Setting viewing history for conversation_id=#{conversation_id}")
|
|
contact_id = conversation.contact_id
|
|
[{
|
|
role: 'system',
|
|
content: <<~HISTORY.strip
|
|
You are currently viewing the conversation with the following details:
|
|
Conversation ID: #{conversation_id}
|
|
Contact ID: #{contact_id}
|
|
HISTORY
|
|
}]
|
|
end
|
|
|
|
def persist_message(message, message_type = 'assistant')
|
|
return if @copilot_thread.blank?
|
|
|
|
@copilot_thread.copilot_messages.create!(
|
|
message: message,
|
|
message_type: message_type
|
|
)
|
|
end
|
|
|
|
def feature_name
|
|
'copilot'
|
|
end
|
|
end
|