From 19ff7382119f85c374ec82f421ad502125a1ddf8 Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Thu, 13 Jul 2023 10:26:25 +0530 Subject: [PATCH] feat: Add more options for AI reply suggestions (#7493) --- .../integrations/openai_processor_service.rb | 3 +- lib/integrations/openai/processor_service.rb | 62 +++++++- lib/integrations/openai_base_service.rb | 2 +- .../openai/processor_service_spec.rb | 149 ++++++++++++++++++ 4 files changed, 210 insertions(+), 6 deletions(-) diff --git a/enterprise/lib/enterprise/integrations/openai_processor_service.rb b/enterprise/lib/enterprise/integrations/openai_processor_service.rb index a9d746852..85cc3ad75 100644 --- a/enterprise/lib/enterprise/integrations/openai_processor_service.rb +++ b/enterprise/lib/enterprise/integrations/openai_processor_service.rb @@ -1,5 +1,6 @@ module Enterprise::Integrations::OpenaiProcessorService - ALLOWED_EVENT_NAMES = %w[rephrase summarize reply_suggestion label_suggestion].freeze + ALLOWED_EVENT_NAMES = %w[rephrase summarize reply_suggestion label_suggestion fix_spelling_grammar shorten expand + make_friendly make_formal simplify].freeze CACHEABLE_EVENTS = %w[label_suggestion].freeze def label_suggestion_message diff --git a/lib/integrations/openai/processor_service.rb b/lib/integrations/openai/processor_service.rb index 61825a072..8670d3b7e 100644 --- a/lib/integrations/openai/processor_service.rb +++ b/lib/integrations/openai/processor_service.rb @@ -1,4 +1,6 @@ class Integrations::Openai::ProcessorService < Integrations::OpenaiBaseService + AGENT_INSTRUCTION = 'You are a helpful support agent.'.freeze + LANGUAGE_INSTRUCTION = 'Reply in the user\'s language.'.freeze def reply_suggestion_message make_api_call(reply_suggestion_body) end @@ -11,16 +13,68 @@ class Integrations::Openai::ProcessorService < Integrations::OpenaiBaseService make_api_call(rephrase_body) end + def fix_spelling_grammar_message + make_api_call(fix_spelling_grammar_body) + end + + def shorten_message + make_api_call(shorten_body) + end + + def expand_message + make_api_call(expand_body) + end + + def make_friendly_message + make_api_call(make_friendly_body) + end + + def make_formal_message + make_api_call(make_formal_body) + end + + def simplify_message + make_api_call(simplify_body) + end + private def rephrase_body + build_api_call_body("#{AGENT_INSTRUCTION} Please rephrase the following response to a more #{event['data']['tone']} tone. " \ + "#{LANGUAGE_INSTRUCTION}") + end + + def fix_spelling_grammar_body + build_api_call_body("#{AGENT_INSTRUCTION} Please fix the spelling and grammar of the following response. " \ + "#{LANGUAGE_INSTRUCTION}") + end + + def shorten_body + build_api_call_body("#{AGENT_INSTRUCTION} Please shorten the following response. #{LANGUAGE_INSTRUCTION}") + end + + def expand_body + build_api_call_body("#{AGENT_INSTRUCTION} Please expand the following response. #{LANGUAGE_INSTRUCTION}") + end + + def make_friendly_body + build_api_call_body("#{AGENT_INSTRUCTION} Please make the following response more friendly. #{LANGUAGE_INSTRUCTION}") + end + + def make_formal_body + build_api_call_body("#{AGENT_INSTRUCTION} Please make the following response more formal. #{LANGUAGE_INSTRUCTION}") + end + + def simplify_body + build_api_call_body("#{AGENT_INSTRUCTION} Please simplify the following response. #{LANGUAGE_INSTRUCTION}") + end + + def build_api_call_body(system_content, user_content = event['data']['content']) { model: GPT_MODEL, messages: [ - { role: 'system', - content: "You are a helpful support agent. Please rephrase the following response to a more #{event['data']['tone']} tone. " \ - "Reply in the user's language." }, - { role: 'user', content: event['data']['content'] } + { role: 'system', content: system_content }, + { role: 'user', content: user_content } ] }.to_json end diff --git a/lib/integrations/openai_base_service.rb b/lib/integrations/openai_base_service.rb index d3f18763c..ce1fce245 100644 --- a/lib/integrations/openai_base_service.rb +++ b/lib/integrations/openai_base_service.rb @@ -6,7 +6,7 @@ class Integrations::OpenaiBaseService API_URL = 'https://api.openai.com/v1/chat/completions'.freeze GPT_MODEL = 'gpt-3.5-turbo'.freeze - ALLOWED_EVENT_NAMES = %w[rephrase summarize reply_suggestion].freeze + ALLOWED_EVENT_NAMES = %w[rephrase summarize reply_suggestion fix_spelling_grammar shorten expand make_friendly make_formal simplify].freeze CACHEABLE_EVENTS = %w[].freeze pattr_initialize [:hook!, :event!] diff --git a/spec/lib/integrations/openai/processor_service_spec.rb b/spec/lib/integrations/openai/processor_service_spec.rb index 3f487ce8d..cc74bf0a9 100644 --- a/spec/lib/integrations/openai/processor_service_spec.rb +++ b/spec/lib/integrations/openai/processor_service_spec.rb @@ -104,5 +104,154 @@ RSpec.describe Integrations::Openai::ProcessorService do expect(result).to eq('This is a reply from openai.') end end + + context 'when event name is label_suggestion with no labels' do + let(:event) { { 'name' => 'label_suggestion', 'data' => { 'conversation_display_id' => conversation.display_id } } } + + it 'returns nil' do + result = subject.perform + expect(result).to be_nil + end + end + + context 'when event name is not one that can be processed' do + let(:event) { { 'name' => 'unknown', 'data' => {} } } + + it 'returns nil' do + expect(subject.perform).to be_nil + end + end + + context 'when event name is fix_spelling_grammar' do + let(:event) { { 'name' => 'fix_spelling_grammar', 'data' => { 'content' => 'This is a test' } } } + + it 'returns the corrected text' do + request_body = { + 'model' => 'gpt-3.5-turbo', + 'messages' => [ + { 'role' => 'system', 'content' => 'You are a helpful support agent. Please fix the spelling and grammar of the following response. ' \ + 'Reply in the user\'s language.' }, + { 'role' => 'user', 'content' => event['data']['content'] } + ] + }.to_json + + stub_request(:post, 'https://api.openai.com/v1/chat/completions') + .with(body: request_body, headers: expected_headers) + .to_return(status: 200, body: openai_response, headers: {}) + + result = subject.perform + expect(result).to eq('This is a reply from openai.') + end + end + + context 'when event name is shorten' do + let(:event) { { 'name' => 'shorten', 'data' => { 'content' => 'This is a test' } } } + + it 'returns the shortened text' do + request_body = { + 'model' => 'gpt-3.5-turbo', + 'messages' => [ + { 'role' => 'system', 'content' => 'You are a helpful support agent. Please shorten the following response. ' \ + 'Reply in the user\'s language.' }, + { 'role' => 'user', 'content' => event['data']['content'] } + ] + }.to_json + + stub_request(:post, 'https://api.openai.com/v1/chat/completions') + .with(body: request_body, headers: expected_headers) + .to_return(status: 200, body: openai_response, headers: {}) + + result = subject.perform + expect(result).to eq('This is a reply from openai.') + end + end + + context 'when event name is expand' do + let(:event) { { 'name' => 'expand', 'data' => { 'content' => 'help you' } } } + + it 'returns the expanded text' do + request_body = { + 'model' => 'gpt-3.5-turbo', + 'messages' => [ + { 'role' => 'system', 'content' => 'You are a helpful support agent. Please expand the following response. ' \ + 'Reply in the user\'s language.' }, + { 'role' => 'user', 'content' => event['data']['content'] } + ] + }.to_json + + stub_request(:post, 'https://api.openai.com/v1/chat/completions') + .with(body: request_body, headers: expected_headers) + .to_return(status: 200, body: openai_response, headers: {}) + + result = subject.perform + expect(result).to eq('This is a reply from openai.') + end + end + + context 'when event name is make_friendly' do + let(:event) { { 'name' => 'make_friendly', 'data' => { 'content' => 'This is a test' } } } + + it 'returns the friendly text' do + request_body = { + 'model' => 'gpt-3.5-turbo', + 'messages' => [ + { 'role' => 'system', 'content' => 'You are a helpful support agent. Please make the following response more friendly. ' \ + 'Reply in the user\'s language.' }, + { 'role' => 'user', 'content' => event['data']['content'] } + ] + }.to_json + + stub_request(:post, 'https://api.openai.com/v1/chat/completions') + .with(body: request_body, headers: expected_headers) + .to_return(status: 200, body: openai_response, headers: {}) + + result = subject.perform + expect(result).to eq('This is a reply from openai.') + end + end + + context 'when event name is make_formal' do + let(:event) { { 'name' => 'make_formal', 'data' => { 'content' => 'This is a test' } } } + + it 'returns the formal text' do + request_body = { + 'model' => 'gpt-3.5-turbo', + 'messages' => [ + { 'role' => 'system', 'content' => 'You are a helpful support agent. Please make the following response more formal. ' \ + 'Reply in the user\'s language.' }, + { 'role' => 'user', 'content' => event['data']['content'] } + ] + }.to_json + + stub_request(:post, 'https://api.openai.com/v1/chat/completions') + .with(body: request_body, headers: expected_headers) + .to_return(status: 200, body: openai_response, headers: {}) + + result = subject.perform + expect(result).to eq('This is a reply from openai.') + end + end + + context 'when event name is simplify' do + let(:event) { { 'name' => 'simplify', 'data' => { 'content' => 'This is a test' } } } + + it 'returns the simplified text' do + request_body = { + 'model' => 'gpt-3.5-turbo', + 'messages' => [ + { 'role' => 'system', 'content' => 'You are a helpful support agent. Please simplify the following response. ' \ + 'Reply in the user\'s language.' }, + { 'role' => 'user', 'content' => event['data']['content'] } + ] + }.to_json + + stub_request(:post, 'https://api.openai.com/v1/chat/completions') + .with(body: request_body, headers: expected_headers) + .to_return(status: 200, body: openai_response, headers: {}) + + result = subject.perform + expect(result).to eq('This is a reply from openai.') + end + end end end