Add support for multiple tools in Chatwoot
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
module LlmFormattable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def to_llm_text
|
||||
LlmFormatter::LlmTextFormatterService.new(self).format
|
||||
def to_llm_text(include_contact_details: false)
|
||||
LlmFormatter::LlmTextFormatterService.new(self).format(
|
||||
include_contact_details: include_contact_details
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class LlmFormatter::ContactLlmFormatter < LlmFormatter::DefaultLlmFormatter
|
||||
def format
|
||||
def format(_config)
|
||||
sections = []
|
||||
sections << "Contact ID: ##{@record.id}"
|
||||
sections << 'Contact Attributes:'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class LlmFormatter::ConversationLlmFormatter < LlmFormatter::DefaultLlmFormatter
|
||||
def format
|
||||
def format(include_contact_details: false)
|
||||
sections = []
|
||||
sections << "Conversation ID: ##{@record.display_id}"
|
||||
sections << "Channel: #{@record.inbox.channel.name}"
|
||||
@@ -10,6 +10,7 @@ class LlmFormatter::ConversationLlmFormatter < LlmFormatter::DefaultLlmFormatter
|
||||
'No messages in this conversation'
|
||||
end
|
||||
|
||||
sections << "Contact Details: #{@record.contact.to_llm_text}" if include_contact_details
|
||||
sections.join("\n")
|
||||
end
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ class LlmFormatter::DefaultLlmFormatter
|
||||
@record = record
|
||||
end
|
||||
|
||||
def format
|
||||
def format(config)
|
||||
# override this
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,9 +3,9 @@ class LlmFormatter::LlmTextFormatterService
|
||||
@record = record
|
||||
end
|
||||
|
||||
def format
|
||||
def format(include_contact_details: false)
|
||||
formatter_class = find_formatter
|
||||
formatter_class.new(@record).format
|
||||
formatter_class.new(@record).format(include_contact_details: include_contact_details)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -22,6 +22,9 @@ class Captain::Copilot::ChatService < Llm::BaseOpenAiService
|
||||
|
||||
def register_tools
|
||||
@tool_registry.register_tool(Captain::Tools::Copilot::GetConversationService)
|
||||
@tool_registry.register_tool(Captain::Tools::Copilot::SearchConversationService)
|
||||
@tool_registry.register_tool(Captain::Tools::Copilot::GetContactService)
|
||||
@tool_registry.register_tool(Captain::Tools::Copilot::SearchContactService)
|
||||
end
|
||||
|
||||
def build_initial_messages(config)
|
||||
@@ -52,7 +55,7 @@ class Captain::Copilot::ChatService < Llm::BaseOpenAiService
|
||||
def system_message
|
||||
{
|
||||
role: 'system',
|
||||
content: Captain::Llm::SystemPromptsService.copilot_response_generator(@assistant.config['product_name'], @language)
|
||||
content: Captain::Llm::SystemPromptsService.copilot_response_generator(@assistant.config['product_name'], @language, @assistant.account_id)
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -56,11 +56,12 @@ class Captain::Llm::SystemPromptsService
|
||||
SYSTEM_PROMPT_MESSAGE
|
||||
end
|
||||
|
||||
def copilot_response_generator(product_name, language)
|
||||
def copilot_response_generator(product_name, language, account_id)
|
||||
<<~SYSTEM_PROMPT_MESSAGE
|
||||
[Identity]
|
||||
You are Captain, a helpful and friendly copilot assistant for support agents using the product #{product_name}. Your primary role is to assist support agents by retrieving information, compiling accurate responses, and guiding them through customer interactions.
|
||||
You should only provide information related to #{product_name} and must not address queries about other products or external events.
|
||||
You are an assistant for the account with ID: #{account_id}
|
||||
|
||||
[Context]
|
||||
Identify unresolved queries, and ensure responses are relevant and consistent with previous interactions. Always maintain a coherent and professional tone throughout the conversation.
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
class Captain::Tools::Copilot::GetContactService < Captain::Tools::BaseService
|
||||
def name
|
||||
'get_contact'
|
||||
end
|
||||
|
||||
def description
|
||||
'Get details of a contact including their profile information'
|
||||
end
|
||||
|
||||
def parameters
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
contact_id: {
|
||||
type: 'string',
|
||||
description: 'The ID of the contact to retrieve'
|
||||
},
|
||||
account_id: {
|
||||
type: 'string',
|
||||
description: 'The ID of the account the contact belongs to'
|
||||
}
|
||||
},
|
||||
required: %w[contact_id account_id]
|
||||
}
|
||||
end
|
||||
|
||||
def execute(arguments)
|
||||
contact_id = arguments['contact_id']
|
||||
account_id = arguments['account_id']
|
||||
|
||||
Rails.logger.info { "[CAPTAIN][GetContact] #{contact_id}, #{account_id}" }
|
||||
|
||||
return 'Missing required parameters' if contact_id.blank? || account_id.blank?
|
||||
|
||||
contact = Contact.find_by(id: contact_id, account_id: account_id)
|
||||
return 'Contact not found' if contact.nil?
|
||||
|
||||
contact.to_llm_text
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,63 @@
|
||||
class Captain::Tools::Copilot::SearchContactService < Captain::Tools::BaseService
|
||||
def name
|
||||
'search_contact'
|
||||
end
|
||||
|
||||
def description
|
||||
'Search for contacts based on query parameters'
|
||||
end
|
||||
|
||||
def parameters
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
account_id: {
|
||||
type: 'string',
|
||||
description: 'The ID of the account to search contacts in'
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
description: 'Filter contacts by email'
|
||||
},
|
||||
phone_number: {
|
||||
type: 'string',
|
||||
description: 'Filter contacts by phone number'
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Filter contacts by name (partial match)'
|
||||
}
|
||||
},
|
||||
required: %w[account_id]
|
||||
}
|
||||
end
|
||||
|
||||
def execute(arguments)
|
||||
account_id = arguments['account_id']
|
||||
email = arguments['email']
|
||||
phone_number = arguments['phone_number']
|
||||
name = arguments['name']
|
||||
|
||||
Rails.logger.info do
|
||||
"[CAPTAIN][SearchContact] account_id: #{account_id}, email: #{email}, phone_number: #{phone_number}, name: #{name}"
|
||||
end
|
||||
|
||||
return 'Missing required parameters' if account_id.blank?
|
||||
|
||||
contacts = Contact.where(account_id: account_id)
|
||||
contacts = contacts.where(email: email) if email.present?
|
||||
contacts = contacts.where(phone_number: phone_number) if phone_number.present?
|
||||
contacts = contacts.where('name ILIKE ?', "%#{name}%") if name.present?
|
||||
|
||||
return 'No contacts found' unless contacts.exists?
|
||||
|
||||
<<~RESPONSE
|
||||
Total number of contacts: #{contacts.count}
|
||||
#{
|
||||
contacts.map do |contact|
|
||||
contact.to_llm_text
|
||||
end.join("\n---\n")
|
||||
}
|
||||
RESPONSE
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,65 @@
|
||||
class Captain::Tools::Copilot::SearchConversationService < Captain::Tools::BaseService
|
||||
def name
|
||||
'search_conversation'
|
||||
end
|
||||
|
||||
def description
|
||||
'Search for conversations based on query parameters'
|
||||
end
|
||||
|
||||
def parameters
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
account_id: {
|
||||
type: 'string',
|
||||
description: 'The ID of the account to search conversations in'
|
||||
},
|
||||
contact_id: {
|
||||
type: 'string',
|
||||
description: 'Filter conversations by contact ID'
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
enum: %w[open resolved pending snoozed],
|
||||
description: 'Filter conversations by status'
|
||||
},
|
||||
priority: {
|
||||
type: 'string',
|
||||
enum: %w[low medium high urgent],
|
||||
description: 'Filter conversations by priority'
|
||||
}
|
||||
},
|
||||
required: %w[account_id]
|
||||
}
|
||||
end
|
||||
|
||||
def execute(arguments)
|
||||
account_id = arguments['account_id']
|
||||
status = arguments['status']
|
||||
contact_id = arguments['contact_id']
|
||||
priority = arguments['priority']
|
||||
|
||||
Rails.logger.info do
|
||||
"[CAPTAIN][SearchConversation] account_id: #{account_id}, status: #{status}, contact_id: #{contact_id}, priority: #{priority}"
|
||||
end
|
||||
|
||||
return 'Missing required parameters' if account_id.blank?
|
||||
|
||||
conversations = Conversation.where(account_id: account_id)
|
||||
conversations = conversations.where(contact_id: contact_id) if contact_id.present?
|
||||
conversations = conversations.where(status: status) if status.present?
|
||||
conversations = conversations.where(priority: priority) if priority.present?
|
||||
|
||||
return 'No conversations found' unless conversations.exists?
|
||||
|
||||
<<~RESPONSE
|
||||
Total number of conversations: #{conversations.count}
|
||||
#{
|
||||
conversations.map do |conversation|
|
||||
conversation.to_llm_text(include_contact_details: true)
|
||||
end.join("\n---\n")
|
||||
}
|
||||
RESPONSE
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user