feat: Migrate ruby llm captain (#12981)
Co-authored-by: aakashb95 <aakash@chatwoot.com> Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
This commit is contained in:
26
enterprise/app/services/captain/tools/base_tool.rb
Normal file
26
enterprise/app/services/captain/tools/base_tool.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
class Captain::Tools::BaseTool < RubyLLM::Tool
|
||||
attr_accessor :assistant
|
||||
|
||||
def initialize(assistant, user: nil)
|
||||
@assistant = assistant
|
||||
@user = user
|
||||
super()
|
||||
end
|
||||
|
||||
def active?
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def user_has_permission(permission)
|
||||
return false if @user.blank?
|
||||
|
||||
account_user = AccountUser.find_by(account_id: @assistant.account_id, user_id: @user.id)
|
||||
return false if account_user.blank?
|
||||
|
||||
return account_user.custom_role.permissions.include?(permission) if account_user.custom_role.present?
|
||||
|
||||
account_user.administrator? || account_user.agent?
|
||||
end
|
||||
end
|
||||
@@ -1,32 +1,11 @@
|
||||
class Captain::Tools::Copilot::GetArticleService < Captain::Tools::BaseService
|
||||
def name
|
||||
class Captain::Tools::Copilot::GetArticleService < Captain::Tools::BaseTool
|
||||
def self.name
|
||||
'get_article'
|
||||
end
|
||||
description 'Get details of an article including its content and metadata'
|
||||
param :article_id, type: :number, desc: 'The ID of the article to retrieve', required: true
|
||||
|
||||
def description
|
||||
'Get details of an article including its content and metadata'
|
||||
end
|
||||
|
||||
def parameters
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
article_id: {
|
||||
type: 'number',
|
||||
description: 'The ID of the article to retrieve'
|
||||
}
|
||||
},
|
||||
required: %w[article_id]
|
||||
}
|
||||
end
|
||||
|
||||
def execute(arguments)
|
||||
article_id = arguments['article_id']
|
||||
|
||||
Rails.logger.info { "#{self.class.name}: Article ID: #{article_id}" }
|
||||
|
||||
return 'Missing required parameters' if article_id.blank?
|
||||
|
||||
def execute(article_id:)
|
||||
article = Article.find_by(id: article_id, account_id: @assistant.account_id)
|
||||
return 'Article not found' if article.nil?
|
||||
|
||||
|
||||
@@ -1,32 +1,11 @@
|
||||
class Captain::Tools::Copilot::GetContactService < Captain::Tools::BaseService
|
||||
def name
|
||||
class Captain::Tools::Copilot::GetContactService < Captain::Tools::BaseTool
|
||||
def self.name
|
||||
'get_contact'
|
||||
end
|
||||
description 'Get details of a contact including their profile information'
|
||||
param :contact_id, type: :number, desc: 'The ID of the contact to retrieve', required: true
|
||||
|
||||
def description
|
||||
'Get details of a contact including their profile information'
|
||||
end
|
||||
|
||||
def parameters
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
contact_id: {
|
||||
type: 'number',
|
||||
description: 'The ID of the contact to retrieve'
|
||||
}
|
||||
},
|
||||
required: %w[contact_id]
|
||||
}
|
||||
end
|
||||
|
||||
def execute(arguments)
|
||||
contact_id = arguments['contact_id']
|
||||
|
||||
Rails.logger.info "#{self.class.name}: Contact ID: #{contact_id}"
|
||||
|
||||
return 'Missing required parameters' if contact_id.blank?
|
||||
|
||||
def execute(contact_id:)
|
||||
contact = Contact.find_by(id: contact_id, account_id: @assistant.account_id)
|
||||
return 'Contact not found' if contact.nil?
|
||||
|
||||
|
||||
@@ -1,32 +1,12 @@
|
||||
class Captain::Tools::Copilot::GetConversationService < Captain::Tools::BaseService
|
||||
def name
|
||||
class Captain::Tools::Copilot::GetConversationService < Captain::Tools::BaseTool
|
||||
def self.name
|
||||
'get_conversation'
|
||||
end
|
||||
description 'Get details of a conversation including messages and contact information'
|
||||
|
||||
def description
|
||||
'Get details of a conversation including messages and contact information'
|
||||
end
|
||||
|
||||
def parameters
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
conversation_id: {
|
||||
type: 'number',
|
||||
description: 'The ID of the conversation to retrieve'
|
||||
}
|
||||
},
|
||||
required: %w[conversation_id]
|
||||
}
|
||||
end
|
||||
|
||||
def execute(arguments)
|
||||
conversation_id = arguments['conversation_id']
|
||||
|
||||
Rails.logger.info "#{self.class.name}: Conversation ID: #{conversation_id}"
|
||||
|
||||
return 'Missing required parameters' if conversation_id.blank?
|
||||
param :conversation_id, type: :integer, desc: 'ID of the conversation to retrieve', required: true
|
||||
|
||||
def execute(conversation_id:)
|
||||
conversation = Conversation.find_by(display_id: conversation_id, account_id: @assistant.account_id)
|
||||
return 'Conversation not found' if conversation.blank?
|
||||
|
||||
|
||||
@@ -1,36 +1,22 @@
|
||||
class Captain::Tools::Copilot::SearchArticlesService < Captain::Tools::BaseService
|
||||
def name
|
||||
class Captain::Tools::Copilot::SearchArticlesService < Captain::Tools::BaseTool
|
||||
def self.name
|
||||
'search_articles'
|
||||
end
|
||||
|
||||
def description
|
||||
'Search articles based on parameters'
|
||||
description 'Search articles based on parameters'
|
||||
params do
|
||||
string :query, description: 'Search articles by title or content (partial match)'
|
||||
number :category_id, description: 'Filter articles by category ID'
|
||||
any_of :status, description: 'Filter articles by status' do
|
||||
string enum: %w[draft published archived]
|
||||
end
|
||||
end
|
||||
|
||||
def parameters
|
||||
{
|
||||
type: 'object',
|
||||
properties: properties,
|
||||
required: ['query']
|
||||
}
|
||||
end
|
||||
|
||||
def execute(arguments)
|
||||
query = arguments['query']
|
||||
category_id = arguments['category_id']
|
||||
status = arguments['status']
|
||||
|
||||
Rails.logger.info "#{self.class.name}: Query: #{query}, Category ID: #{category_id}, Status: #{status}"
|
||||
|
||||
return 'Missing required parameters' if query.blank?
|
||||
|
||||
articles = fetch_articles(query, category_id, status)
|
||||
|
||||
def execute(query: nil, category_id: nil, status: nil)
|
||||
articles = fetch_articles(query: query, category_id: category_id, status: status)
|
||||
return 'No articles found' unless articles.exists?
|
||||
|
||||
total_count = articles.count
|
||||
articles = articles.limit(100)
|
||||
|
||||
<<~RESPONSE
|
||||
#{total_count > 100 ? "Found #{total_count} articles (showing first 100)" : "Total number of articles: #{total_count}"}
|
||||
#{articles.map(&:to_llm_text).join("\n---\n")}
|
||||
@@ -43,29 +29,11 @@ class Captain::Tools::Copilot::SearchArticlesService < Captain::Tools::BaseServi
|
||||
|
||||
private
|
||||
|
||||
def fetch_articles(query, category_id, status)
|
||||
def fetch_articles(query:, category_id:, status:)
|
||||
articles = Article.where(account_id: @assistant.account_id)
|
||||
articles = articles.where('title ILIKE :query OR content ILIKE :query', query: "%#{query}%") if query.present?
|
||||
articles = articles.where(category_id: category_id) if category_id.present?
|
||||
articles = articles.where(status: status) if status.present?
|
||||
articles
|
||||
end
|
||||
|
||||
def properties
|
||||
{
|
||||
query: {
|
||||
type: 'string',
|
||||
description: 'Search articles by title or content (partial match)'
|
||||
},
|
||||
category_id: {
|
||||
type: 'number',
|
||||
description: 'Filter articles by category ID'
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
enum: %w[draft published archived],
|
||||
description: 'Filter articles by status'
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,27 +1,14 @@
|
||||
class Captain::Tools::Copilot::SearchContactsService < Captain::Tools::BaseService
|
||||
def name
|
||||
class Captain::Tools::Copilot::SearchContactsService < Captain::Tools::BaseTool
|
||||
def self.name
|
||||
'search_contacts'
|
||||
end
|
||||
|
||||
def description
|
||||
'Search contacts based on query parameters'
|
||||
end
|
||||
|
||||
def parameters
|
||||
{
|
||||
type: 'object',
|
||||
properties: properties,
|
||||
required: []
|
||||
}
|
||||
end
|
||||
|
||||
def execute(arguments)
|
||||
email = arguments['email']
|
||||
phone_number = arguments['phone_number']
|
||||
name = arguments['name']
|
||||
|
||||
Rails.logger.info "#{self.class.name} Email: #{email}, Phone Number: #{phone_number}, Name: #{name}"
|
||||
description 'Search contacts based on query parameters'
|
||||
param :email, type: :string, desc: 'Filter contacts by email'
|
||||
param :phone_number, type: :string, desc: 'Filter contacts by phone number'
|
||||
param :name, type: :string, desc: 'Filter contacts by name (partial match)'
|
||||
|
||||
def execute(email: nil, phone_number: nil, name: nil)
|
||||
contacts = Contact.where(account_id: @assistant.account_id)
|
||||
contacts = contacts.where(email: email) if email.present?
|
||||
contacts = contacts.where(phone_number: phone_number) if phone_number.present?
|
||||
@@ -39,23 +26,4 @@ class Captain::Tools::Copilot::SearchContactsService < Captain::Tools::BaseServi
|
||||
def active?
|
||||
user_has_permission('contact_manage')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def properties
|
||||
{
|
||||
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)'
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,26 +1,15 @@
|
||||
class Captain::Tools::Copilot::SearchConversationsService < Captain::Tools::BaseService
|
||||
def name
|
||||
'search_conversations'
|
||||
class Captain::Tools::Copilot::SearchConversationsService < Captain::Tools::BaseTool
|
||||
def self.name
|
||||
'search_conversation'
|
||||
end
|
||||
description 'Search conversations based on parameters'
|
||||
|
||||
def description
|
||||
'Search conversations based on parameters'
|
||||
end
|
||||
|
||||
def parameters
|
||||
{
|
||||
type: 'object',
|
||||
properties: properties,
|
||||
required: []
|
||||
}
|
||||
end
|
||||
|
||||
def execute(arguments)
|
||||
status = arguments['status']
|
||||
contact_id = arguments['contact_id']
|
||||
priority = arguments['priority']
|
||||
labels = arguments['labels']
|
||||
param :status, type: :string, desc: 'Status of the conversation'
|
||||
param :contact_id, type: :number, desc: 'Contact id'
|
||||
param :priority, type: :string, desc: 'Priority of conversation'
|
||||
param :labels, type: :string, desc: 'Labels available'
|
||||
|
||||
def execute(status: nil, contact_id: nil, priority: nil, labels: nil)
|
||||
conversations = get_conversations(status, contact_id, priority, labels)
|
||||
|
||||
return 'No conversations found' unless conversations.exists?
|
||||
@@ -58,13 +47,4 @@ class Captain::Tools::Copilot::SearchConversationsService < Captain::Tools::Base
|
||||
@assistant.account
|
||||
).perform
|
||||
end
|
||||
|
||||
def properties
|
||||
{
|
||||
contact_id: { type: 'number', 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' },
|
||||
labels: { type: 'array', items: { type: 'string' }, description: 'Filter conversations by labels' }
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,34 +1,14 @@
|
||||
class Captain::Tools::Copilot::SearchLinearIssuesService < Captain::Tools::BaseService
|
||||
def name
|
||||
class Captain::Tools::Copilot::SearchLinearIssuesService < Captain::Tools::BaseTool
|
||||
def self.name
|
||||
'search_linear_issues'
|
||||
end
|
||||
|
||||
def description
|
||||
'Search Linear issues based on a search term'
|
||||
end
|
||||
description 'Search Linear issues based on a search term'
|
||||
param :term, type: :string, desc: 'The search term to find Linear issues', required: true
|
||||
|
||||
def parameters
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
term: {
|
||||
type: 'string',
|
||||
description: 'The search term to find Linear issues'
|
||||
}
|
||||
},
|
||||
required: %w[term]
|
||||
}
|
||||
end
|
||||
|
||||
def execute(arguments)
|
||||
def execute(term:)
|
||||
return 'Linear integration is not enabled' unless active?
|
||||
|
||||
term = arguments['term']
|
||||
|
||||
Rails.logger.info "#{self.class.name}: Service called with the search term #{term}"
|
||||
|
||||
return 'Missing required parameters' if term.blank?
|
||||
|
||||
linear_service = Integrations::Linear::ProcessorService.new(account: @assistant.account)
|
||||
result = linear_service.search_issue(term)
|
||||
|
||||
|
||||
@@ -1,27 +1,12 @@
|
||||
class Captain::Tools::SearchDocumentationService < Captain::Tools::BaseService
|
||||
def name
|
||||
class Captain::Tools::SearchDocumentationService < Captain::Tools::BaseTool
|
||||
def self.name
|
||||
'search_documentation'
|
||||
end
|
||||
description 'Search and retrieve documentation from knowledge base'
|
||||
|
||||
def description
|
||||
'Search and retrieve documentation from knowledge base'
|
||||
end
|
||||
param :query, desc: 'Search Query', required: true
|
||||
|
||||
def parameters
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
search_query: {
|
||||
type: 'string',
|
||||
description: 'The search query to look up in the documentation.'
|
||||
}
|
||||
},
|
||||
required: ['search_query']
|
||||
}
|
||||
end
|
||||
|
||||
def execute(arguments)
|
||||
query = arguments['search_query']
|
||||
def execute(query:)
|
||||
Rails.logger.info { "#{self.class.name}: #{query}" }
|
||||
|
||||
responses = assistant.responses.approved.search(query)
|
||||
|
||||
Reference in New Issue
Block a user