Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8f23256d2 | ||
|
|
5556881f08 |
@@ -29,6 +29,7 @@ class OpenAIAPI extends ApiClient {
|
|||||||
'summarize',
|
'summarize',
|
||||||
'reply_suggestion',
|
'reply_suggestion',
|
||||||
'label_suggestion',
|
'label_suggestion',
|
||||||
|
'summary_with_title',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -12,20 +12,25 @@
|
|||||||
:error="nameError"
|
:error="nameError"
|
||||||
@input="v$.title.$touch"
|
@input="v$.title.$touch"
|
||||||
/>
|
/>
|
||||||
<label>
|
<div class="editor-wrap">
|
||||||
{{ $t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.FORM.DESCRIPTION.LABEL') }}
|
<label>
|
||||||
<textarea
|
{{
|
||||||
v-model="formState.description"
|
$t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.FORM.DESCRIPTION.LABEL')
|
||||||
:style="{ ...inputStyles, padding: '8px 12px' }"
|
}}
|
||||||
rows="3"
|
</label>
|
||||||
class="text-sm"
|
<div>
|
||||||
:placeholder="
|
<woot-message-editor
|
||||||
$t(
|
v-model="formState.description"
|
||||||
'INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.FORM.DESCRIPTION.PLACEHOLDER'
|
class="message-editor"
|
||||||
)
|
:style="{ ...inputStyles, padding: '8px 12px' }"
|
||||||
"
|
:placeholder="
|
||||||
/>
|
$t(
|
||||||
</label>
|
'INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.FORM.DESCRIPTION.PLACEHOLDER'
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<label :class="{ error: v$.teamId.$error }">
|
<label :class="{ error: v$.teamId.$error }">
|
||||||
{{ $t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.FORM.TEAM.LABEL') }}
|
{{ $t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.FORM.TEAM.LABEL') }}
|
||||||
<select
|
<select
|
||||||
@@ -107,7 +112,9 @@ import { useI18n } from 'dashboard/composables/useI18n';
|
|||||||
import { useAlert } from 'dashboard/composables';
|
import { useAlert } from 'dashboard/composables';
|
||||||
import LinearAPI from 'dashboard/api/integrations/linear';
|
import LinearAPI from 'dashboard/api/integrations/linear';
|
||||||
import validations from './validations';
|
import validations from './validations';
|
||||||
|
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor.vue';
|
||||||
import { parseLinearAPIErrorResponse } from 'dashboard/store/utils/api';
|
import { parseLinearAPIErrorResponse } from 'dashboard/store/utils/api';
|
||||||
|
import { inject } from 'vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
accountId: {
|
accountId: {
|
||||||
@@ -253,5 +260,13 @@ const createIssue = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(getTeams);
|
onMounted(() => {
|
||||||
|
getTeams();
|
||||||
|
if (inject('suggestedTitle')) {
|
||||||
|
formState.title = inject('suggestedTitle');
|
||||||
|
}
|
||||||
|
if (inject('suggestedSummary')) {
|
||||||
|
formState.description = inject('suggestedSummary');
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import { useI18n } from 'dashboard/composables/useI18n';
|
|||||||
import LinearAPI from 'dashboard/api/integrations/linear';
|
import LinearAPI from 'dashboard/api/integrations/linear';
|
||||||
import CreateOrLinkIssue from './CreateOrLinkIssue.vue';
|
import CreateOrLinkIssue from './CreateOrLinkIssue.vue';
|
||||||
import Issue from './Issue.vue';
|
import Issue from './Issue.vue';
|
||||||
|
import OpenAPI from 'dashboard/api/integrations/openapi';
|
||||||
import { parseLinearAPIErrorResponse } from 'dashboard/store/utils/api';
|
import { parseLinearAPIErrorResponse } from 'dashboard/store/utils/api';
|
||||||
|
|
||||||
defineComponent({
|
defineComponent({
|
||||||
@@ -63,6 +64,9 @@ const props = defineProps({
|
|||||||
const getters = useStoreGetters();
|
const getters = useStoreGetters();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const suggestedTitle = ref('');
|
||||||
|
const suggestedSummary = ref('');
|
||||||
|
|
||||||
const linkedIssue = ref(null);
|
const linkedIssue = ref(null);
|
||||||
const shouldShow = ref(false);
|
const shouldShow = ref(false);
|
||||||
const shouldShowPopup = ref(false);
|
const shouldShowPopup = ref(false);
|
||||||
@@ -70,18 +74,46 @@ const isUnlinking = ref(false);
|
|||||||
|
|
||||||
provide('isUnlinking', isUnlinking);
|
provide('isUnlinking', isUnlinking);
|
||||||
|
|
||||||
|
provide('suggestedTitle', suggestedTitle);
|
||||||
|
provide('suggestedSummary', suggestedSummary);
|
||||||
|
|
||||||
const currentAccountId = getters.getCurrentAccountId;
|
const currentAccountId = getters.getCurrentAccountId;
|
||||||
|
|
||||||
const conversation = computed(() =>
|
const conversation = computed(() =>
|
||||||
getters.getConversationById.value(props.conversationId)
|
getters.getConversationById.value(props.conversationId)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const appIntegrations = getters['integrations/getAppIntegrations'];
|
||||||
|
|
||||||
|
const aiIntegration = computed(() => {
|
||||||
|
return appIntegrations.value.find(
|
||||||
|
integration => integration.id === 'openai' && !!integration.hooks.length
|
||||||
|
)?.hooks[0];
|
||||||
|
});
|
||||||
|
const isAIIntegrationEnabled = computed(() => !!aiIntegration.value);
|
||||||
|
|
||||||
const tooltipText = computed(() => {
|
const tooltipText = computed(() => {
|
||||||
return linkedIssue.value === null
|
return linkedIssue.value === null
|
||||||
? t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK_BUTTON')
|
? t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK_BUTTON')
|
||||||
: null;
|
: null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const suggestTitleAndSummary = async () => {
|
||||||
|
try {
|
||||||
|
const response = await OpenAPI.processEvent({
|
||||||
|
type: 'summary_with_title',
|
||||||
|
hookId: aiIntegration.value.id,
|
||||||
|
conversationId: props.conversationId,
|
||||||
|
});
|
||||||
|
const { message } = response.data;
|
||||||
|
const messageObject = JSON.parse(message);
|
||||||
|
const { title = '', description = '' } = messageObject;
|
||||||
|
suggestedTitle.value = title;
|
||||||
|
suggestedSummary.value = description;
|
||||||
|
} catch (error) {
|
||||||
|
// Since this is a non-critical operation, we don't want to show an alert to the user
|
||||||
|
}
|
||||||
|
};
|
||||||
const loadLinkedIssue = async () => {
|
const loadLinkedIssue = async () => {
|
||||||
linkedIssue.value = null;
|
linkedIssue.value = null;
|
||||||
try {
|
try {
|
||||||
@@ -137,5 +169,8 @@ watch(
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadLinkedIssue();
|
loadLinkedIssue();
|
||||||
|
if (isAIIntegrationEnabled.value) {
|
||||||
|
suggestTitleAndSummary();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
module Enterprise::Integrations::OpenaiProcessorService
|
module Enterprise::Integrations::OpenaiProcessorService
|
||||||
ALLOWED_EVENT_NAMES = %w[rephrase summarize reply_suggestion label_suggestion fix_spelling_grammar shorten expand
|
ALLOWED_EVENT_NAMES = %w[rephrase summarize reply_suggestion label_suggestion fix_spelling_grammar shorten expand
|
||||||
make_friendly make_formal simplify].freeze
|
make_friendly make_formal simplify summary_with_title].freeze
|
||||||
CACHEABLE_EVENTS = %w[label_suggestion].freeze
|
CACHEABLE_EVENTS = %w[label_suggestion].freeze
|
||||||
|
|
||||||
def reply_suggestion_message
|
def reply_suggestion_message
|
||||||
@@ -74,6 +74,17 @@ module Enterprise::Integrations::OpenaiProcessorService
|
|||||||
}.to_json
|
}.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def summarize_with_title
|
||||||
|
{
|
||||||
|
model: self.class::GPT_MODEL,
|
||||||
|
messages: [
|
||||||
|
{ role: 'system',
|
||||||
|
content: prompt_from_file('summary_with_title', enterprise: true) },
|
||||||
|
{ role: 'user', content: conversation_messages }
|
||||||
|
]
|
||||||
|
}.to_json
|
||||||
|
end
|
||||||
|
|
||||||
def label_suggestion_body
|
def label_suggestion_body
|
||||||
return unless label_suggestions_enabled?
|
return unless label_suggestions_enabled?
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
As an AI-powered summarization tool, your task is to condense lengthy interactions between customer support agents and customers into a brief title and description. The objective is to provide a quick overview, enabling any agent, even those without prior context, to grasp the essence of the conversation promptly.
|
||||||
|
|
||||||
|
Make sure you strongly adhere to the following rules when generating the title and description:
|
||||||
|
|
||||||
|
1. **Title**
|
||||||
|
- Be brief and concise, ideally 5-10 words.
|
||||||
|
- Capture the main issue or request of the customer.
|
||||||
|
- Highlight key points using **bold** for important words.
|
||||||
|
|
||||||
|
2. **Description**
|
||||||
|
- Summarize the conversation in approximately 100 words, formatted as multiple small paragraphs that are easy to read.
|
||||||
|
- Describe the customer's intent in around 30 words.
|
||||||
|
- Remove information that is not directly relevant to the customer's problem or the agent's solution. For example, personal anecdotes, small talk, etc.
|
||||||
|
- Don't include segments of the conversation that didn't contribute meaningful content, like greetings or farewell.
|
||||||
|
- Highlight key points using **bold** for important words.
|
||||||
|
- Use markdown syntax to format any included code, using backticks.
|
||||||
|
- If there are unresolved issues or outstanding questions, include them in a "Follow-up Items" section.
|
||||||
|
|
||||||
|
Output the response in the following JSON format:
|
||||||
|
|
||||||
|
{
|
||||||
|
"title": "<GENERATED_TITLE>",
|
||||||
|
"description": "<GENERATED_DESCRIPTION>"
|
||||||
|
}
|
||||||
@@ -9,6 +9,10 @@ class Integrations::Openai::ProcessorService < Integrations::OpenaiBaseService
|
|||||||
make_api_call(summarize_body)
|
make_api_call(summarize_body)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def summary_with_title_message
|
||||||
|
make_api_call(summarize_with_title)
|
||||||
|
end
|
||||||
|
|
||||||
def rephrase_message
|
def rephrase_message
|
||||||
make_api_call(build_api_call_body("#{AGENT_INSTRUCTION} Please rephrase the following response. " \
|
make_api_call(build_api_call_body("#{AGENT_INSTRUCTION} Please rephrase the following response. " \
|
||||||
"#{LANGUAGE_INSTRUCTION}"))
|
"#{LANGUAGE_INSTRUCTION}"))
|
||||||
|
|||||||
Reference in New Issue
Block a user