From 647161121e85098357922cb58c68f50ebd7e281d Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Tue, 26 Sep 2023 21:05:21 -0700 Subject: [PATCH] chore: pass to agent if there is error parsing json (#7990) - GPT bot should pass the conversation to the agent if AI returns invalid json or any other error --- .github/workflows/run_response_bot_spec.yml | 7 ++- .../message_templates/response_bot_service.rb | 17 +++--- .../response_bot_service_spec.rb | 59 +++++++++++++++++++ 3 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 spec/enterprise/services/enterprise/message_templates/response_bot_service_spec.rb diff --git a/.github/workflows/run_response_bot_spec.yml b/.github/workflows/run_response_bot_spec.yml index ded5c903e..6fd6a7b22 100644 --- a/.github/workflows/run_response_bot_spec.yml +++ b/.github/workflows/run_response_bot_spec.yml @@ -69,7 +69,12 @@ jobs: # Run Response Bot specs - name: Run backend tests run: | - bundle exec rspec spec/enterprise/controllers/api/v1/accounts/response_sources_controller_spec.rb spec/enterprise/controllers/enterprise/api/v1/accounts/inboxes_controller_spec.rb:47 --profile=10 --format documentation + bundle exec rspec \ + spec/enterprise/controllers/api/v1/accounts/response_sources_controller_spec.rb \ + spec/enterprise/services/enterprise/message_templates/response_bot_service_spec.rb \ + spec/enterprise/controllers/enterprise/api/v1/accounts/inboxes_controller_spec.rb:47 \ + --profile=10 \ + --format documentation - name: Upload rails log folder uses: actions/upload-artifact@v3 diff --git a/enterprise/app/services/enterprise/message_templates/response_bot_service.rb b/enterprise/app/services/enterprise/message_templates/response_bot_service.rb index 03fe34a44..ddb986975 100644 --- a/enterprise/app/services/enterprise/message_templates/response_bot_service.rb +++ b/enterprise/app/services/enterprise/message_templates/response_bot_service.rb @@ -4,9 +4,10 @@ class Enterprise::MessageTemplates::ResponseBotService def perform ActiveRecord::Base.transaction do response = get_response(conversation.messages.last.content) - process_response(conversation.messages.last, response['response']) + process_response(response['response']) end rescue StandardError => e + process_action('handoff') # something went wrong, pass to agent ChatwootExceptionTracker.new(e, account: conversation.account).capture_exception true end @@ -43,15 +44,15 @@ class Enterprise::MessageTemplates::ResponseBotService message.message_type == 'incoming' ? 'user' : 'system' end - def process_response(message, response) + def process_response(response) if response == 'conversation_handoff' - process_action(message, 'handoff') + process_action('handoff') else - create_messages(response, conversation) + create_messages(response) end end - def process_action(_message, action) + def process_action(action) case action when 'handoff' conversation.messages.create!('message_type': :outgoing, 'account_id': conversation.account_id, 'inbox_id': conversation.inbox_id, @@ -60,9 +61,9 @@ class Enterprise::MessageTemplates::ResponseBotService end end - def create_messages(response, conversation) + def create_messages(response) response = process_response_content(response).first - create_outgoing_message(response, conversation) + create_outgoing_message(response) end def process_response_content(response) @@ -79,7 +80,7 @@ class Enterprise::MessageTemplates::ResponseBotService [response, article_ids] end - def create_outgoing_message(response, conversation) + def create_outgoing_message(response) conversation.messages.create!( { message_type: :outgoing, diff --git a/spec/enterprise/services/enterprise/message_templates/response_bot_service_spec.rb b/spec/enterprise/services/enterprise/message_templates/response_bot_service_spec.rb new file mode 100644 index 000000000..5b3d89f17 --- /dev/null +++ b/spec/enterprise/services/enterprise/message_templates/response_bot_service_spec.rb @@ -0,0 +1,59 @@ +require 'rails_helper' + +RSpec.describe Enterprise::MessageTemplates::ResponseBotService, type: :service do + let!(:conversation) { create(:conversation, status: :pending) } + let(:service) { described_class.new(conversation: conversation) } + let(:chat_gpt_double) { instance_double(ChatGpt) } + 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? + + create(:message, message_type: :incoming, conversation: conversation, content: 'Hi') + allow(ChatGpt).to receive(:new).and_return(chat_gpt_double) + allow(chat_gpt_double).to receive(:generate_response).and_return({ 'response' => 'some_response', :context_ids => [1, 2] }) + allow(conversation.inbox).to receive(:get_responses).and_return([response_object]) + end + + describe '#perform' do + context 'when successful' do + it 'creates an outgoing message' do + expect do + service.perform + end.to change { conversation.messages.where(message_type: :outgoing).count }.by(1) + + expect(conversation.messages.last.content).to eq('some_response') + end + end + + 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 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.status).to eq('open') + end + end + + context 'when StandardError is raised' do + it 'captures the exception' do + allow(chat_gpt_double).to receive(:generate_response).and_raise(StandardError) + + expect(ChatwootExceptionTracker).to receive(:new).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.status).to eq('open') + end + end + end +end