feat(ee): Add reporting events for bots (#9027)

Added a new event conversation_bot_resolved and added a job to auto resolve the bot conversations if there was no activity for the last 1 hour.
This commit is contained in:
Sojan Jose
2024-02-28 04:23:28 +05:30
committed by GitHub
parent b7a83dcbcd
commit 41e269e873
20 changed files with 194 additions and 15 deletions

View File

@@ -5,7 +5,7 @@ RSpec.describe 'Response Sources API', type: :request do
let!(:admin) { create(:user, account: account, role: :administrator) }
before do
skip('Skipping since vector is not enabled in this environment') unless Features::ResponseBotService.new.vector_extension_enabled?
skip_unless_response_bot_enabled_test_environment
end
describe 'POST /api/v1/accounts/{account.id}/response_sources/parse' do

View File

@@ -50,7 +50,7 @@ RSpec.describe 'Enterprise Inboxes API', type: :request do
let(:administrator) { create(:user, account: account, role: :administrator) }
before do
skip('Skipping since vector is not enabled in this environment') unless Features::ResponseBotService.new.vector_extension_enabled?
skip_unless_response_bot_enabled_test_environment
end
context 'when it is an unauthenticated user' do

View File

@@ -0,0 +1,29 @@
require 'rails_helper'
RSpec.describe Account::ConversationsResolutionSchedulerJob, type: :job do
let!(:account_with_bot) { create(:account) }
let!(:account_without_bot) { create(:account) }
let!(:inbox_with_bot) { create(:inbox, account: account_with_bot) }
let!(:inbox_without_bot) { create(:inbox, account: account_without_bot) }
let(:response_source) { create(:response_source, account: account_with_bot) }
before do
skip_unless_response_bot_enabled_test_environment
account_with_bot.enable_features!(:response_bot)
create(:inbox_response_source, inbox: inbox_with_bot, response_source: response_source)
end
describe '#perform' do
it 'enqueues resolution jobs only for inboxes with response bot enabled' do
expect do
described_class.perform_now
end.to have_enqueued_job(ResponseBot::InboxPendingConversationsResolutionJob).with(inbox_with_bot).and have_enqueued_job.exactly(:once)
end
it 'does not enqueue resolution jobs for inboxes without response bot enabled' do
expect do
described_class.perform_now
end.not_to have_enqueued_job(ResponseBot::InboxPendingConversationsResolutionJob).with(inbox_without_bot)
end
end
end

View File

@@ -0,0 +1,38 @@
require 'rails_helper'
RSpec.describe ResponseBot::InboxPendingConversationsResolutionJob, type: :job do
include ActiveJob::TestHelper
let!(:inbox) { create(:inbox) }
let!(:resolvable_pending_conversation) { create(:conversation, inbox: inbox, last_activity_at: 2.hours.ago, status: :pending) }
let!(:recent_pending_conversation) { create(:conversation, inbox: inbox, last_activity_at: 10.minutes.ago, status: :pending) }
let!(:open_conversation) { create(:conversation, inbox: inbox, last_activity_at: 1.hour.ago, status: :open) }
before do
stub_const('Limits::BULK_ACTIONS_LIMIT', 2)
end
it 'queues the job' do
expect { described_class.perform_later(inbox) }
.to have_enqueued_job.on_queue('low')
end
it 'resolves only the eligible pending conversations' do
perform_enqueued_jobs { described_class.perform_later(inbox) }
expect(resolvable_pending_conversation.reload.status).to eq('resolved')
expect(recent_pending_conversation.reload.status).to eq('pending')
expect(open_conversation.reload.status).to eq('open')
end
it 'creates an outgoing message for each resolved conversation' do
# resolution message + system message
expect { perform_enqueued_jobs { described_class.perform_later(inbox) } }
.to change { resolvable_pending_conversation.messages.reload.count }.by(2)
resolved_conversation_messages = resolvable_pending_conversation.messages.map(&:content)
expect(resolved_conversation_messages).to include(
'Resolving the conversation as it has been inactive for a while. Please start a new conversation if you need further assistance.'
)
end
end

View File

@@ -8,9 +8,7 @@ RSpec.describe Enterprise::MessageTemplates::ResponseBotService, type: :service
let(:response_object) { instance_double(Response, id: 1, question: 'Q1', answer: 'A1') }
before do
# Uncomment if you want to run the spec in your local machine
# Features::ResponseBotService.new.enable_in_installation
skip('Skipping since vector is not enabled in this environment') unless Features::ResponseBotService.new.vector_extension_enabled?
skip_unless_response_bot_enabled_test_environment
stub_request(:post, 'https://api.openai.com/v1/embeddings').to_return(status: 200, body: {}.to_json,
headers: { Content_Type: 'application/json' })
create(:message, message_type: :incoming, conversation: conversation, content: 'Hi')
@@ -33,6 +31,19 @@ RSpec.describe Enterprise::MessageTemplates::ResponseBotService, type: :service
expect(last_message.content).to include(Response.first.question)
expect(last_message.content).to include('**Sources**')
end
it 'hands off the conversation if the response is handoff' do
allow(chat_gpt_double).to receive(:generate_response).and_return({ 'response' => 'conversation_handoff' })
expect(conversation).to receive(:bot_handoff!).and_call_original
expect do
service.perform
end.to change { conversation.messages.where(message_type: :outgoing).count }.by(1)
last_message = conversation.messages.last
expect(last_message.content).to eq('Transferring to another agent for further assistance.')
expect(conversation.status).to eq('open')
end
end
context 'when context_ids are not present' do
@@ -67,12 +78,13 @@ RSpec.describe Enterprise::MessageTemplates::ResponseBotService, type: :service
context 'when JSON::ParserError is raised' do
it 'creates a handoff message' do
allow(chat_gpt_double).to receive(:generate_response).and_raise(JSON::ParserError)
expect(conversation).to receive(:bot_handoff!).and_call_original
expect do
service.perform
end.to change { conversation.messages.where(message_type: :outgoing).count }.by(1)
expect(conversation.messages.last.content).to eq('passing to an agent')
expect(conversation.messages.last.content).to eq('Transferring to another agent for further assistance.')
expect(conversation.status).to eq('open')
end
end
@@ -80,6 +92,7 @@ RSpec.describe Enterprise::MessageTemplates::ResponseBotService, type: :service
context 'when StandardError is raised' do
it 'captures the exception' do
allow(chat_gpt_double).to receive(:generate_response).and_raise(StandardError)
expect(conversation).to receive(:bot_handoff!).and_call_original
expect(ChatwootExceptionTracker).to receive(:new).and_call_original
@@ -87,7 +100,7 @@ RSpec.describe Enterprise::MessageTemplates::ResponseBotService, type: :service
service.perform
end.to change { conversation.messages.where(message_type: :outgoing).count }.by(1)
expect(conversation.messages.last.content).to eq('passing to an agent')
expect(conversation.messages.last.content).to eq('Transferring to another agent for further assistance.')
expect(conversation.status).to eq('open')
end
end