feat: update tools

This commit is contained in:
Shivam Mishra
2025-07-15 21:55:09 +05:30
parent e52becbc40
commit 89a748eef6
6 changed files with 110 additions and 113 deletions
@@ -1,36 +1,26 @@
class Captain::Tools::AddContactNoteTool < Captain::Tools::BaseAgentTool
class Captain::Tools::AddContactNoteTool < Captain::Tools::BasePublicTool
description 'Add a note to a contact profile'
param :contact_id, type: 'string', desc: 'The ID of the contact'
param :note, type: 'string', desc: 'The note content to add to the contact'
def perform(_tool_context, note:, contact_id:)
log_tool_usage('add_contact_note', { contact_id: contact_id, note_length: note.length })
return 'Missing required parameters: contact_id, note' if note.blank? || contact_id.blank?
contact = find_contact(contact_id)
def perform(tool_context, note:)
contact = find_contact(tool_context.state)
return 'Contact not found' unless contact
return 'Note content is required' if note.blank?
log_tool_usage('add_contact_note', { contact_id: contact.id, note_length: note.length })
create_contact_note(contact, note)
"Note added successfully to contact #{contact.name} (ID: #{contact.id})"
end
private
def find_contact(contact_id)
account_scoped(::Contact).find_by(id: contact_id)
end
def create_contact_note(contact, note)
contact.notes.create!(
account: @assistant.account,
contact: contact,
content: note,
user: @user
)
contact.notes.create!(content: note)
end
def active?
user_has_permission('contact_manage')
def permissions
%w[contact_manage]
end
end
@@ -0,0 +1,34 @@
class Captain::Tools::AddLabelToConversationTool < Captain::Tools::BasePublicTool
description 'Add a label to a conversation'
param :label_name, type: 'string', desc: 'The name of the label to add'
def perform(tool_context, label_name:)
conversation = find_conversation(tool_context.state)
return 'Conversation not found' unless conversation
label_name = label_name&.strip&.downcase
return 'Label name is required' if label_name.blank?
label = find_label(label_name)
return 'Label not found' unless label
add_label_to_conversation(conversation, label_name)
log_tool_usage('added_label', conversation_id: conversation.id, label: label_name)
"Label '#{label_name}' added to conversation ##{conversation.display_id}"
end
private
def find_label(label_name)
account_scoped(Label).find_by(title: label_name)
end
def add_label_to_conversation(conversation, label_name)
conversation.add_labels(label_name)
rescue StandardError => e
Rails.logger.error "Failed to add label to conversation: #{e.message}"
raise
end
end
@@ -1,40 +1,31 @@
class Captain::Tools::AddPrivateNoteTool < Captain::Tools::BaseAgentTool
class Captain::Tools::AddPrivateNoteTool < Captain::Tools::BasePublicTool
description 'Add a private note to a conversation'
param :conversation_id, type: 'string', desc: 'The display ID of the conversation'
param :note, type: 'string', desc: 'The private note content'
def perform(_tool_context, conversation_id:, note:)
log_tool_usage('add_private_note', { conversation_id: conversation_id, note_length: note.length })
return 'Missing required parameters: conversation_id, note' if conversation_id.blank? || note.blank?
conversation = find_conversation(conversation_id)
def perform(tool_context, note:)
conversation = find_conversation(tool_context.state)
return 'Conversation not found' unless conversation
log_tool_usage('add_private_note', { conversation_id: conversation.id, note_length: note.length })
create_private_note(conversation, note)
"Private note added successfully to conversation #{conversation_id}"
'Private note added successfully'
end
private
def find_conversation(conversation_id)
account_scoped(::Conversation).find_by(display_id: conversation_id)
end
def create_private_note(conversation, note)
conversation.messages.create!(
account: @assistant.account,
inbox: conversation.inbox,
sender: @user,
sender: @assistant,
message_type: :outgoing,
content: note,
message_type: 'activity',
private: true
)
end
def active?
user_has_permission('conversation_manage') ||
user_has_permission('conversation_unassigned_manage') ||
user_has_permission('conversation_participating_manage')
def permissions
%w[conversation_manage conversation_unassigned_manage conversation_participating_manage]
end
end
@@ -1,46 +0,0 @@
require 'agents'
class Captain::Tools::BaseAgentTool < Agents::Tool
def initialize(assistant, user: nil)
@assistant = assistant
@user = user
@account_user = find_account_user if @user.present?
super()
end
def active?
user_has_permission(required_permission)
end
protected
def required_permission
# Override in subclasses to specify the required permission
'agent'
end
private
def user_has_permission(permission)
return false if @account_user.blank?
return @account_user.custom_role.permissions.include?(permission) if @account_user.custom_role.present?
# Default permission for agents without custom roles
@account_user.administrator? || @account_user.agent?
end
def find_account_user
AccountUser.find_by(account_id: @assistant.account_id, user_id: @user.id)
end
def account_scoped(model_class)
model_class.where(account_id: @assistant.account_id)
end
def log_tool_usage(action, details = {})
Rails.logger.info do
"#{self.class.name}: #{action} by user #{@user&.id} for assistant #{@assistant&.id} - #{details.inspect}"
end
end
end
@@ -0,0 +1,45 @@
require 'agents'
class Captain::Tools::BasePublicTool < Agents::Tool
def initialize(assistant)
@assistant = assistant
super()
end
def active?
# Public tools are always active
true
end
def permissions
# Override in subclasses to specify required permissions
# Returns empty array for public tools (no permissions required)
[]
end
private
def account_scoped(model_class)
model_class.where(account_id: @assistant.account_id)
end
def find_conversation(state)
conversation_id = state[:conversation][:id]
return nil unless conversation_id
account_scoped(::Conversation).find_by(id: conversation_id)
end
def find_contact(state)
contact_id = state[:contact][:id]
return nil unless contact_id
account_scoped(::Contact).find_by(id: contact_id)
end
def log_tool_usage(action, details = {})
Rails.logger.info do
"#{self.class.name}: #{action} for assistant #{@assistant&.id} - #{details.inspect}"
end
end
end
@@ -1,40 +1,25 @@
class Captain::Tools::UpdatePriorityTool < Captain::Tools::BaseAgentTool
class Captain::Tools::UpdatePriorityTool < Captain::Tools::BasePublicTool
description 'Update the priority of a conversation'
param :conversation_id, type: 'string', desc: 'The display ID of the conversation'
param :priority, type: 'string', desc: 'The priority level: low, medium, high, urgent, or nil to remove priority'
def perform(_tool_context, conversation_id:, priority:)
log_tool_usage('update_priority', { conversation_id: conversation_id, priority: priority })
def perform(tool_context, priority:)
@conversation = find_conversation(tool_context.state)
return 'Conversation not found' unless @conversation
error = validate_and_prepare(conversation_id, priority)
return error if error
@normalized_priority = normalize_priority(priority)
return "Invalid priority. Valid options: #{valid_priority_options}" unless valid_priority?(@normalized_priority)
log_tool_usage('update_priority', { conversation_id: @conversation.id, priority: priority })
execute_priority_update
end
private
def validate_and_prepare(conversation_id, priority)
return 'Missing required parameter: conversation_id' if conversation_id.blank?
@conversation = find_conversation(conversation_id)
return 'Conversation not found' unless @conversation
@normalized_priority = normalize_priority(priority)
return "Invalid priority. Valid options: #{valid_priority_options}" unless valid_priority?(@normalized_priority)
@conversation_id = conversation_id
nil
end
def execute_priority_update
update_conversation_priority(@conversation, @normalized_priority)
priority_text = @normalized_priority || 'none'
"Priority updated to '#{priority_text}' for conversation #{@conversation_id}"
end
def find_conversation(conversation_id)
account_scoped(::Conversation).find_by(display_id: conversation_id)
"Priority updated to '#{priority_text}' for conversation ##{@conversation.display_id}"
end
def normalize_priority(priority)
@@ -57,9 +42,7 @@ class Captain::Tools::UpdatePriorityTool < Captain::Tools::BaseAgentTool
conversation.update!(priority: priority)
end
def active?
user_has_permission('conversation_manage') ||
user_has_permission('conversation_unassigned_manage') ||
user_has_permission('conversation_participating_manage')
def permissions
%w[conversation_manage conversation_unassigned_manage conversation_participating_manage]
end
end