diff --git a/app/javascript/dashboard/components/widgets/AIAssistanceButton.vue b/app/javascript/dashboard/components/widgets/AIAssistanceButton.vue index 04255b5b0..12c9ef454 100644 --- a/app/javascript/dashboard/components/widgets/AIAssistanceButton.vue +++ b/app/javascript/dashboard/components/widgets/AIAssistanceButton.vue @@ -1,204 +1,80 @@ - - diff --git a/app/javascript/dashboard/components/widgets/AIAssistanceModal.vue b/app/javascript/dashboard/components/widgets/AIAssistanceModal.vue new file mode 100644 index 000000000..46b25f2fb --- /dev/null +++ b/app/javascript/dashboard/components/widgets/AIAssistanceModal.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/app/javascript/dashboard/components/widgets/AILoader.vue b/app/javascript/dashboard/components/widgets/AILoader.vue new file mode 100644 index 000000000..a7c9dad15 --- /dev/null +++ b/app/javascript/dashboard/components/widgets/AILoader.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/app/javascript/dashboard/components/widgets/conversation/conversation/LabelSuggestion.vue b/app/javascript/dashboard/components/widgets/conversation/conversation/LabelSuggestion.vue index e4c2a6b03..6a45a7425 100644 --- a/app/javascript/dashboard/components/widgets/conversation/conversation/LabelSuggestion.vue +++ b/app/javascript/dashboard/components/widgets/conversation/conversation/LabelSuggestion.vue @@ -112,10 +112,6 @@ export default { required: false, default: () => [], }, - conversationId: { - type: Number, - required: true, - }, }, data() { return { diff --git a/app/javascript/dashboard/helper/AnalyticsHelper/events.js b/app/javascript/dashboard/helper/AnalyticsHelper/events.js index bec4df420..47d60ef22 100644 --- a/app/javascript/dashboard/helper/AnalyticsHelper/events.js +++ b/app/javascript/dashboard/helper/AnalyticsHelper/events.js @@ -81,6 +81,12 @@ export const OPEN_AI_EVENTS = Object.freeze({ SUMMARIZE: 'OpenAI: Used summarize', REPLY_SUGGESTION: 'OpenAI: Used reply suggestion', REPHRASE: 'OpenAI: Used rephrase', + FIX_SPELLING_AND_GRAMMAR: 'OpenAI: Used fix spelling and grammar', + SHORTEN: 'OpenAI: Used shorten', + EXPAND: 'OpenAI: Used expand', + MAKE_FRIENDLY: 'OpenAI: Used make friendly', + MAKE_FORMAL: 'OpenAI: Used make formal', + SIMPLIFY: 'OpenAI: Used simplify', APPLY_LABEL_SUGGESTION: 'OpenAI: Apply label from suggestion', DISMISS_LABEL_SUGGESTION: 'OpenAI: Dismiss label suggestions', }); diff --git a/app/javascript/dashboard/i18n/locale/en/generalSettings.json b/app/javascript/dashboard/i18n/locale/en/generalSettings.json index 63b597a53..aeafa9d54 100644 --- a/app/javascript/dashboard/i18n/locale/en/generalSettings.json +++ b/app/javascript/dashboard/i18n/locale/en/generalSettings.json @@ -110,7 +110,8 @@ "SNOOZE_CONVERSATION": "Snooze Conversation", "ADD_LABEL": "Add label to the conversation", "REMOVE_LABEL": "Remove label from the conversation", - "SETTINGS": "Settings" + "SETTINGS": "Settings", + "AI_ASSIST": "AI Assist" }, "COMMANDS": { "GO_TO_CONVERSATION_DASHBOARD": "Go to Conversation Dashboard", @@ -132,6 +133,7 @@ "GO_TO_NOTIFICATIONS": "Go to Notifications", "ADD_LABELS_TO_CONVERSATION": "Add label to the conversation", "ASSIGN_AN_AGENT": "Assign an agent", + "AI_ASSIST": "AI Assist", "ASSIGN_PRIORITY": "Assign priority", "ASSIGN_A_TEAM": "Assign a team", "MUTE_CONVERSATION": "Mute conversation", diff --git a/app/javascript/dashboard/i18n/locale/en/integrations.json b/app/javascript/dashboard/i18n/locale/en/integrations.json index e3104bd4b..c78c86bad 100644 --- a/app/javascript/dashboard/i18n/locale/en/integrations.json +++ b/app/javascript/dashboard/i18n/locale/en/integrations.json @@ -83,6 +83,28 @@ "CREATE_ERROR": "There was an error creating a meeting link, please try again" }, "OPEN_AI": { + "AI_ASSIST": "AI Assist", + "WITH_AI": " %{option} with AI ", + "OPTIONS": { + "REPLY_SUGGESTION": "Reply Suggestion", + "SUMMARIZE": "Summarize", + "REPHRASE": "Improve Writing", + "FIX_SPELLING_GRAMMAR": "Fix Spelling and Grammar", + "SHORTEN": "Shorten", + "EXPAND": "Expand", + "MAKE_FRIENDLY": "Change message tone to friendly", + "MAKE_FORMAL": "Use formal tone", + "SIMPLIFY": "Simplify" + }, + "ASSISTANCE_MODAL": { + "DRAFT_TITLE": "Draft content", + "GENERATED_TITLE": "Generated content", + "AI_WRITING": "AI is writing", + "BUTTONS": { + "APPLY": "Use this suggestion", + "CANCEL": "Cancel" + } + }, "TITLE": "Improve With AI", "SUMMARY_TITLE": "Summary with AI", "REPLY_TITLE": "Reply suggestion with AI", diff --git a/app/javascript/dashboard/mixins/aiMixin.js b/app/javascript/dashboard/mixins/aiMixin.js index d30045705..48b187120 100644 --- a/app/javascript/dashboard/mixins/aiMixin.js +++ b/app/javascript/dashboard/mixins/aiMixin.js @@ -9,7 +9,11 @@ export default { this.fetchIntegrationsIfRequired(); }, computed: { - ...mapGetters({ appIntegrations: 'integrations/getAppIntegrations' }), + ...mapGetters({ + appIntegrations: 'integrations/getAppIntegrations', + currentChat: 'getSelectedChat', + replyMode: 'draftMessages/getReplyEditorMode', + }), isAIIntegrationEnabled() { return this.appIntegrations.find( integration => integration.id === 'openai' && !!integration.hooks.length @@ -20,6 +24,15 @@ export default { integration => integration.id === 'openai' && !!integration.hooks.length ).hooks[0].id; }, + draftMessage() { + return this.$store.getters['draftMessages/get'](this.draftKey); + }, + draftKey() { + return `draft-${this.conversationId}-${this.replyMode}`; + }, + conversationId() { + return this.currentChat?.id; + }, }, methods: { async fetchIntegrationsIfRequired() { @@ -84,5 +97,22 @@ export default { .map(label => label.trim()) // trim the words .filter((label, index, self) => self.indexOf(label) === index); // remove any duplicates }, + async processEvent(type = 'rephrase') { + try { + const result = await OpenAPI.processEvent({ + hookId: this.hookId, + type, + content: this.draftMessage, + conversationId: this.conversationId, + }); + const { + data: { message: generatedMessage }, + } = result; + return generatedMessage; + } catch (error) { + this.showAlert(this.$t('INTEGRATION_SETTINGS.OPEN_AI.GENERATE_ERROR')); + return ''; + } + }, }, }; diff --git a/app/javascript/dashboard/routes/dashboard/commands/CommandBarIcons.js b/app/javascript/dashboard/routes/dashboard/commands/CommandBarIcons.js index 97831ae53..97e6f33bc 100644 --- a/app/javascript/dashboard/routes/dashboard/commands/CommandBarIcons.js +++ b/app/javascript/dashboard/routes/dashboard/commands/CommandBarIcons.js @@ -58,3 +58,11 @@ export const ICON_PRIORITY_NONE = ` `; + +export const ICON_AI_ASSIST = ``; +export const ICON_AI_SUMMARY = ``; +export const ICON_AI_SPELLING = ``; + +export const ICON_AI_EXPAND = ``; +export const ICON_AI_SHORTEN = ``; +export const ICON_AI_GRAMMAR = ``; diff --git a/app/javascript/dashboard/routes/dashboard/commands/commandBarActions.js b/app/javascript/dashboard/routes/dashboard/commands/commandBarActions.js new file mode 100644 index 000000000..1c728b01c --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/commands/commandBarActions.js @@ -0,0 +1,129 @@ +import wootConstants from 'dashboard/constants/globals'; + +import { + CMD_MUTE_CONVERSATION, + CMD_REOPEN_CONVERSATION, + CMD_RESOLVE_CONVERSATION, + CMD_SEND_TRANSCRIPT, + CMD_SNOOZE_CONVERSATION, + CMD_UNMUTE_CONVERSATION, +} from './commandBarBusEvents'; + +import { + ICON_MUTE_CONVERSATION, + ICON_REOPEN_CONVERSATION, + ICON_RESOLVE_CONVERSATION, + ICON_SEND_TRANSCRIPT, + ICON_SNOOZE_CONVERSATION, + ICON_UNMUTE_CONVERSATION, +} from './CommandBarIcons'; + +const SNOOZE_OPTIONS = wootConstants.SNOOZE_OPTIONS; + +export const OPEN_CONVERSATION_ACTIONS = [ + { + id: 'resolve_conversation', + title: 'COMMAND_BAR.COMMANDS.RESOLVE_CONVERSATION', + section: 'COMMAND_BAR.SECTIONS.CONVERSATION', + icon: ICON_RESOLVE_CONVERSATION, + handler: () => bus.$emit(CMD_RESOLVE_CONVERSATION), + }, +]; + +export const SNOOZE_CONVERSATION_ACTIONS = [ + { + id: 'snooze_conversation', + title: 'COMMAND_BAR.COMMANDS.SNOOZE_CONVERSATION', + icon: ICON_SNOOZE_CONVERSATION, + children: Object.values(SNOOZE_OPTIONS), + }, + + { + id: SNOOZE_OPTIONS.UNTIL_NEXT_REPLY, + title: 'COMMAND_BAR.COMMANDS.UNTIL_NEXT_REPLY', + parent: 'snooze_conversation', + section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION', + icon: ICON_SNOOZE_CONVERSATION, + handler: () => + bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_NEXT_REPLY), + }, + { + id: SNOOZE_OPTIONS.AN_HOUR_FROM_NOW, + title: 'COMMAND_BAR.COMMANDS.AN_HOUR_FROM_NOW', + parent: 'snooze_conversation', + section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION', + icon: ICON_SNOOZE_CONVERSATION, + handler: () => + bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.AN_HOUR_FROM_NOW), + }, + { + id: SNOOZE_OPTIONS.UNTIL_TOMORROW, + title: 'COMMAND_BAR.COMMANDS.UNTIL_TOMORROW', + section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION', + parent: 'snooze_conversation', + icon: ICON_SNOOZE_CONVERSATION, + handler: () => + bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_TOMORROW), + }, + { + id: SNOOZE_OPTIONS.UNTIL_NEXT_WEEK, + title: 'COMMAND_BAR.COMMANDS.UNTIL_NEXT_WEEK', + section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION', + parent: 'snooze_conversation', + icon: ICON_SNOOZE_CONVERSATION, + handler: () => + bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_NEXT_WEEK), + }, + { + id: SNOOZE_OPTIONS.UNTIL_NEXT_MONTH, + title: 'COMMAND_BAR.COMMANDS.UNTIL_NEXT_MONTH', + section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION', + parent: 'snooze_conversation', + icon: ICON_SNOOZE_CONVERSATION, + handler: () => + bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_NEXT_MONTH), + }, + { + id: SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME, + title: 'COMMAND_BAR.COMMANDS.CUSTOM', + section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION', + parent: 'snooze_conversation', + icon: ICON_SNOOZE_CONVERSATION, + handler: () => + bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME), + }, +]; + +export const RESOLVED_CONVERSATION_ACTIONS = [ + { + id: 'reopen_conversation', + title: 'COMMAND_BAR.COMMANDS.REOPEN_CONVERSATION', + section: 'COMMAND_BAR.SECTIONS.CONVERSATION', + icon: ICON_REOPEN_CONVERSATION, + handler: () => bus.$emit(CMD_REOPEN_CONVERSATION), + }, +]; + +export const SEND_TRANSCRIPT_ACTION = { + id: 'send_transcript', + title: 'COMMAND_BAR.COMMANDS.SEND_TRANSCRIPT', + section: 'COMMAND_BAR.SECTIONS.CONVERSATION', + icon: ICON_SEND_TRANSCRIPT, + handler: () => bus.$emit(CMD_SEND_TRANSCRIPT), +}; + +export const UNMUTE_ACTION = { + id: 'unmute_conversation', + title: 'COMMAND_BAR.COMMANDS.UNMUTE_CONVERSATION', + section: 'COMMAND_BAR.SECTIONS.CONVERSATION', + icon: ICON_UNMUTE_CONVERSATION, + handler: () => bus.$emit(CMD_UNMUTE_CONVERSATION), +}; + +export const MUTE_ACTION = { + id: 'mute_conversation', + title: 'COMMAND_BAR.COMMANDS.MUTE_CONVERSATION', + section: 'COMMAND_BAR.SECTIONS.CONVERSATION', + icon: ICON_MUTE_CONVERSATION, + handler: () => bus.$emit(CMD_MUTE_CONVERSATION), +}; diff --git a/app/javascript/dashboard/routes/dashboard/commands/commandBarBusEvents.js b/app/javascript/dashboard/routes/dashboard/commands/commandBarBusEvents.js index 1c10fd5bb..e291248e3 100644 --- a/app/javascript/dashboard/routes/dashboard/commands/commandBarBusEvents.js +++ b/app/javascript/dashboard/routes/dashboard/commands/commandBarBusEvents.js @@ -14,3 +14,4 @@ export const CMD_TOGGLE_CONTACT_SIDEBAR = 'CMD_TOGGLE_CONTACT_SIDEBAR'; export const CMD_REOPEN_CONVERSATION = 'CMD_REOPEN_CONVERSATION'; export const CMD_RESOLVE_CONVERSATION = 'CMD_RESOLVE_CONVERSATION'; export const CMD_SNOOZE_CONVERSATION = 'CMD_SNOOZE_CONVERSATION'; +export const CMD_AI_ASSIST = 'CMD_AI_ASSIST'; diff --git a/app/javascript/dashboard/routes/dashboard/commands/conversationHotKeys.js b/app/javascript/dashboard/routes/dashboard/commands/conversationHotKeys.js index 7cb41eed9..3a60094d7 100644 --- a/app/javascript/dashboard/routes/dashboard/commands/conversationHotKeys.js +++ b/app/javascript/dashboard/routes/dashboard/commands/conversationHotKeys.js @@ -1,142 +1,34 @@ import { mapGetters } from 'vuex'; import wootConstants from 'dashboard/constants/globals'; -import { - CMD_MUTE_CONVERSATION, - CMD_REOPEN_CONVERSATION, - CMD_RESOLVE_CONVERSATION, - CMD_SEND_TRANSCRIPT, - CMD_SNOOZE_CONVERSATION, - CMD_UNMUTE_CONVERSATION, -} from './commandBarBusEvents'; +import { CMD_AI_ASSIST } from './commandBarBusEvents'; +import { REPLY_EDITOR_MODES } from 'dashboard/components/widgets/WootWriter/constants'; import { ICON_ADD_LABEL, ICON_ASSIGN_AGENT, ICON_ASSIGN_PRIORITY, ICON_ASSIGN_TEAM, - ICON_MUTE_CONVERSATION, ICON_REMOVE_LABEL, - ICON_REOPEN_CONVERSATION, - ICON_RESOLVE_CONVERSATION, - ICON_SEND_TRANSCRIPT, - ICON_SNOOZE_CONVERSATION, - ICON_UNMUTE_CONVERSATION, ICON_PRIORITY_URGENT, ICON_PRIORITY_HIGH, ICON_PRIORITY_LOW, ICON_PRIORITY_MEDIUM, ICON_PRIORITY_NONE, + ICON_AI_ASSIST, + ICON_AI_SUMMARY, + ICON_AI_SHORTEN, + ICON_AI_EXPAND, + ICON_AI_GRAMMAR, } from './CommandBarIcons'; -const SNOOZE_OPTIONS = wootConstants.SNOOZE_OPTIONS; - -const OPEN_CONVERSATION_ACTIONS = [ - { - id: 'resolve_conversation', - title: 'COMMAND_BAR.COMMANDS.RESOLVE_CONVERSATION', - section: 'COMMAND_BAR.SECTIONS.CONVERSATION', - icon: ICON_RESOLVE_CONVERSATION, - handler: () => bus.$emit(CMD_RESOLVE_CONVERSATION), - }, -]; - -const SNOOZE_CONVERSATION_ACTIONS = [ - { - id: 'snooze_conversation', - title: 'COMMAND_BAR.COMMANDS.SNOOZE_CONVERSATION', - icon: ICON_SNOOZE_CONVERSATION, - children: Object.values(SNOOZE_OPTIONS), - }, - - { - id: SNOOZE_OPTIONS.UNTIL_NEXT_REPLY, - title: 'COMMAND_BAR.COMMANDS.UNTIL_NEXT_REPLY', - parent: 'snooze_conversation', - section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION', - icon: ICON_SNOOZE_CONVERSATION, - handler: () => - bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_NEXT_REPLY), - }, - { - id: SNOOZE_OPTIONS.AN_HOUR_FROM_NOW, - title: 'COMMAND_BAR.COMMANDS.AN_HOUR_FROM_NOW', - parent: 'snooze_conversation', - section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION', - icon: ICON_SNOOZE_CONVERSATION, - handler: () => - bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.AN_HOUR_FROM_NOW), - }, - { - id: SNOOZE_OPTIONS.UNTIL_TOMORROW, - title: 'COMMAND_BAR.COMMANDS.UNTIL_TOMORROW', - section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION', - parent: 'snooze_conversation', - icon: ICON_SNOOZE_CONVERSATION, - handler: () => - bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_TOMORROW), - }, - { - id: SNOOZE_OPTIONS.UNTIL_NEXT_WEEK, - title: 'COMMAND_BAR.COMMANDS.UNTIL_NEXT_WEEK', - section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION', - parent: 'snooze_conversation', - icon: ICON_SNOOZE_CONVERSATION, - handler: () => - bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_NEXT_WEEK), - }, - { - id: SNOOZE_OPTIONS.UNTIL_NEXT_MONTH, - title: 'COMMAND_BAR.COMMANDS.UNTIL_NEXT_MONTH', - section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION', - parent: 'snooze_conversation', - icon: ICON_SNOOZE_CONVERSATION, - handler: () => - bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_NEXT_MONTH), - }, - { - id: SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME, - title: 'COMMAND_BAR.COMMANDS.CUSTOM', - section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION', - parent: 'snooze_conversation', - icon: ICON_SNOOZE_CONVERSATION, - handler: () => - bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME), - }, -]; - -const RESOLVED_CONVERSATION_ACTIONS = [ - { - id: 'reopen_conversation', - title: 'COMMAND_BAR.COMMANDS.REOPEN_CONVERSATION', - section: 'COMMAND_BAR.SECTIONS.CONVERSATION', - icon: ICON_REOPEN_CONVERSATION, - handler: () => bus.$emit(CMD_REOPEN_CONVERSATION), - }, -]; - -const SEND_TRANSCRIPT_ACTION = { - id: 'send_transcript', - title: 'COMMAND_BAR.COMMANDS.SEND_TRANSCRIPT', - section: 'COMMAND_BAR.SECTIONS.CONVERSATION', - icon: ICON_SEND_TRANSCRIPT, - handler: () => bus.$emit(CMD_SEND_TRANSCRIPT), -}; - -const UNMUTE_ACTION = { - id: 'unmute_conversation', - title: 'COMMAND_BAR.COMMANDS.UNMUTE_CONVERSATION', - section: 'COMMAND_BAR.SECTIONS.CONVERSATION', - icon: ICON_UNMUTE_CONVERSATION, - handler: () => bus.$emit(CMD_UNMUTE_CONVERSATION), -}; - -const MUTE_ACTION = { - id: 'mute_conversation', - title: 'COMMAND_BAR.COMMANDS.MUTE_CONVERSATION', - section: 'COMMAND_BAR.SECTIONS.CONVERSATION', - icon: ICON_MUTE_CONVERSATION, - handler: () => bus.$emit(CMD_MUTE_CONVERSATION), -}; +import { + OPEN_CONVERSATION_ACTIONS, + SNOOZE_CONVERSATION_ACTIONS, + RESOLVED_CONVERSATION_ACTIONS, + SEND_TRANSCRIPT_ACTION, + UNMUTE_ACTION, + MUTE_ACTION, +} from './commandBarActions'; export const isAConversationRoute = routeName => [ @@ -162,9 +54,24 @@ export default { activeLabels() { this.setCommandbarData(); }, + draftMessage() { + this.setCommandbarData(); + }, + replyMode() { + this.setCommandbarData(); + }, }, computed: { - ...mapGetters({ currentChat: 'getSelectedChat' }), + ...mapGetters({ + currentChat: 'getSelectedChat', + replyMode: 'draftMessages/getReplyEditorMode', + }), + draftMessage() { + return this.$store.getters['draftMessages/get'](this.draftKey); + }, + draftKey() { + return `draft-${this.conversationId}-${this.replyMode}`; + }, inboxId() { return this.currentChat?.inbox_id; }, @@ -337,6 +244,94 @@ export default { ]); }, + nonDraftMessageAIAssistActions() { + if (this.replyMode === REPLY_EDITOR_MODES.REPLY) { + return [ + { + label: this.$t( + 'INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.REPLY_SUGGESTION' + ), + key: 'reply_suggestion', + icon: ICON_AI_ASSIST, + }, + ]; + } + return [ + { + label: this.$t('INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.SUMMARIZE'), + key: 'summarize', + icon: ICON_AI_SUMMARY, + }, + ]; + }, + + draftMessageAIAssistActions() { + return [ + { + label: this.$t('INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.REPHRASE'), + key: 'rephrase', + icon: ICON_AI_ASSIST, + }, + { + label: this.$t( + 'INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.FIX_SPELLING_GRAMMAR' + ), + key: 'fix_spelling_grammar', + icon: ICON_AI_GRAMMAR, + }, + { + label: this.$t('INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.EXPAND'), + key: 'expand', + icon: ICON_AI_EXPAND, + }, + { + label: this.$t('INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.SHORTEN'), + key: 'shorten', + icon: ICON_AI_SHORTEN, + }, + { + label: this.$t('INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.MAKE_FRIENDLY'), + key: 'make_friendly', + icon: ICON_AI_ASSIST, + }, + { + label: this.$t('INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.MAKE_FORMAL'), + key: 'make_formal', + icon: ICON_AI_ASSIST, + }, + { + label: this.$t('INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.SIMPLIFY'), + key: 'simplify', + icon: ICON_AI_ASSIST, + }, + ]; + }, + + AIAssistActions() { + const aiOptions = this.draftMessage + ? this.draftMessageAIAssistActions + : this.nonDraftMessageAIAssistActions; + const options = aiOptions.map(item => ({ + id: `ai-assist-${item.key}`, + title: item.label, + parent: 'ai_assist', + section: this.$t('COMMAND_BAR.SECTIONS.AI_ASSIST'), + priority: item, + icon: item.icon, + handler: () => bus.$emit(CMD_AI_ASSIST, item.key), + })); + return [ + { + id: 'ai_assist', + title: this.$t('COMMAND_BAR.COMMANDS.AI_ASSIST'), + section: this.$t('COMMAND_BAR.SECTIONS.AI_ASSIST'), + icon: ICON_AI_ASSIST, + children: options.map(option => option.id), + }, + ...options, + ]; + }, + conversationHotKeys() { if (isAConversationRoute(this.$route.name)) { return [ @@ -346,6 +341,7 @@ export default { ...this.assignTeamActions, ...this.labelActions, ...this.assignPriorityActions, + ...this.AIAssistActions, ]; } diff --git a/app/javascript/shared/constants/openai.js b/app/javascript/shared/constants/openai.js new file mode 100644 index 000000000..8c5adb875 --- /dev/null +++ b/app/javascript/shared/constants/openai.js @@ -0,0 +1,11 @@ +export const OPEN_AI_OPTIONS = { + IMPROVE_WRITING: 'improve_writing', + FIX_SPELLING_GRAMMAR: 'fix_spelling_grammar', + SHORTEN: 'shorten', + EXPAND: 'expand', + MAKE_FRIENDLY: 'make_friendly', + MAKE_FORMAL: 'make_formal', + SIMPLIFY: 'simplify', + REPLY_SUGGESTION: 'reply_suggestion', + SUMMARIZE: 'summarize', +}; diff --git a/lib/integrations/openai/processor_service.rb b/lib/integrations/openai/processor_service.rb index 8670d3b7e..a52cd3561 100644 --- a/lib/integrations/openai/processor_service.rb +++ b/lib/integrations/openai/processor_service.rb @@ -1,6 +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 + LANGUAGE_INSTRUCTION = 'Ensure that the reply should be in user language.'.freeze def reply_suggestion_message make_api_call(reply_suggestion_body) end @@ -40,7 +40,7 @@ class Integrations::Openai::ProcessorService < Integrations::OpenaiBaseService private def rephrase_body - build_api_call_body("#{AGENT_INSTRUCTION} Please rephrase the following response to a more #{event['data']['tone']} tone. " \ + build_api_call_body("#{AGENT_INSTRUCTION} Please rephrase the following response. " \ "#{LANGUAGE_INSTRUCTION}") end diff --git a/spec/lib/integrations/openai/processor_service_spec.rb b/spec/lib/integrations/openai/processor_service_spec.rb index cc74bf0a9..1a9a0a6d0 100644 --- a/spec/lib/integrations/openai/processor_service_spec.rb +++ b/spec/lib/integrations/openai/processor_service_spec.rb @@ -40,8 +40,8 @@ RSpec.describe Integrations::Openai::ProcessorService do { '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." + 'Please rephrase the following response. ' \ + 'Ensure that the reply should be in user language.' }, { 'role' => 'user', 'content' => event['data']['content'] } ] @@ -130,7 +130,7 @@ RSpec.describe Integrations::Openai::ProcessorService do '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.' }, + 'Ensure that the reply should be in user language.' }, { 'role' => 'user', 'content' => event['data']['content'] } ] }.to_json @@ -152,7 +152,7 @@ RSpec.describe Integrations::Openai::ProcessorService do '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.' }, + 'Ensure that the reply should be in user language.' }, { 'role' => 'user', 'content' => event['data']['content'] } ] }.to_json @@ -174,7 +174,7 @@ RSpec.describe Integrations::Openai::ProcessorService do '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.' }, + 'Ensure that the reply should be in user language.' }, { 'role' => 'user', 'content' => event['data']['content'] } ] }.to_json @@ -196,7 +196,7 @@ RSpec.describe Integrations::Openai::ProcessorService do '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.' }, + 'Ensure that the reply should be in user language.' }, { 'role' => 'user', 'content' => event['data']['content'] } ] }.to_json @@ -218,7 +218,7 @@ RSpec.describe Integrations::Openai::ProcessorService do '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.' }, + 'Ensure that the reply should be in user language.' }, { 'role' => 'user', 'content' => event['data']['content'] } ] }.to_json @@ -240,7 +240,7 @@ RSpec.describe Integrations::Openai::ProcessorService do '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.' }, + 'Ensure that the reply should be in user language.' }, { 'role' => 'user', 'content' => event['data']['content'] } ] }.to_json