From 6096932f7669a1997395b7bba974532cfae32f0b Mon Sep 17 00:00:00 2001 From: Pranav Date: Wed, 15 Jan 2025 20:24:34 -0800 Subject: [PATCH] feat: Add a review step for FAQs generated from conversations before using it (#10693) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR introduces a review step for generated FAQs, allowing a human to validate and approve them before use in customer interactions. While hallucinations are minimal, this step ensures accurate and reliable FAQs for Captain to use during LLM calls when responding to customers. - Added a status field for the FAQ - Allow the filter on the UI. Screenshot 2025-01-15 at 6 39 26 PM --- .../dashboard/api/captain/response.js | 3 +- .../captain/assistant/ResponseCard.story.vue | 2 + .../captain/assistant/ResponseCard.vue | 29 ++++ .../document/RelatedResponses.vue | 1 + .../i18n/locale/en/integrations.json | 22 ++- .../dashboard/captain/responses/Index.vue | 136 +++++++++++++++++- ...d_status_to_captain_assistant_responses.rb | 6 + db/schema.rb | 4 +- .../captain/assistant_responses_controller.rb | 6 +- .../app/models/captain/assistant_response.rb | 9 ++ .../services/captain/copilot/chat_service.rb | 1 + .../captain/llm/assistant_chat_service.rb | 1 + .../captain/llm/conversation_faq_service.rb | 2 +- .../captain/_assistant_response.json.jbuilder | 1 + .../llm/conversation_faq_service_spec.rb | 9 +- 15 files changed, 216 insertions(+), 16 deletions(-) create mode 100644 db/migrate/20250116000103_add_status_to_captain_assistant_responses.rb diff --git a/app/javascript/dashboard/api/captain/response.js b/app/javascript/dashboard/api/captain/response.js index 0cd31fa3c..e3c42757a 100644 --- a/app/javascript/dashboard/api/captain/response.js +++ b/app/javascript/dashboard/api/captain/response.js @@ -6,13 +6,14 @@ class CaptainResponses extends ApiClient { super('captain/assistant_responses', { accountScoped: true }); } - get({ page = 1, searchKey, assistantId, documentId } = {}) { + get({ page = 1, searchKey, assistantId, documentId, status } = {}) { return axios.get(this.url, { params: { page, searchKey, assistant_id: assistantId, document_id: documentId, + status, }, }); } diff --git a/app/javascript/dashboard/components-next/captain/assistant/ResponseCard.story.vue b/app/javascript/dashboard/components-next/captain/assistant/ResponseCard.story.vue index 2522c113a..27fba8b6b 100644 --- a/app/javascript/dashboard/components-next/captain/assistant/ResponseCard.story.vue +++ b/app/javascript/dashboard/components-next/captain/assistant/ResponseCard.story.vue @@ -9,6 +9,7 @@ const responses = [ created_at: 1736283330, id: 87, question: 'Why is my Messenger in Chatwoot deactivated?', + status: 'pending', assistant: { account_id: 1, config: { @@ -148,6 +149,7 @@ const responses = [ :id="response.id" :question="response.question" :answer="response.answer" + :status="response.status" :assistant="response.assistant" :created-at="response.created_at" /> diff --git a/app/javascript/dashboard/components-next/captain/assistant/ResponseCard.vue b/app/javascript/dashboard/components-next/captain/assistant/ResponseCard.vue index 23ef50a07..b23753441 100644 --- a/app/javascript/dashboard/components-next/captain/assistant/ResponseCard.vue +++ b/app/javascript/dashboard/components-next/captain/assistant/ResponseCard.vue @@ -25,6 +25,10 @@ const props = defineProps({ type: Boolean, default: false, }, + status: { + type: String, + default: 'approved', + }, assistant: { type: Object, default: () => ({}), @@ -45,7 +49,22 @@ const { t } = useI18n(); const [showActionsDropdown, toggleDropdown] = useToggle(); +const statusAction = computed(() => { + if (props.status === 'pending') { + return [ + { + label: t('CAPTAIN.RESPONSES.OPTIONS.APPROVE'), + value: 'approve', + action: 'approve', + icon: 'i-lucide-circle-check-big', + }, + ]; + } + return []; +}); + const menuItems = computed(() => [ + ...statusAction.value, { label: t('CAPTAIN.RESPONSES.OPTIONS.EDIT_RESPONSE'), value: 'edit', @@ -107,6 +126,16 @@ const handleAssistantAction = ({ action, value }) => { {{ assistant?.name || '' }} +
+ + {{ t(`CAPTAIN.RESPONSES.STATUS.${status.toUpperCase()}`) }} +
diff --git a/app/javascript/dashboard/components-next/captain/pageComponents/document/RelatedResponses.vue b/app/javascript/dashboard/components-next/captain/pageComponents/document/RelatedResponses.vue index 34c138eb4..6e00eda9b 100644 --- a/app/javascript/dashboard/components-next/captain/pageComponents/document/RelatedResponses.vue +++ b/app/javascript/dashboard/components-next/captain/pageComponents/document/RelatedResponses.vue @@ -57,6 +57,7 @@ defineExpose({ dialogRef }); :id="response.id" :key="response.id" :question="response.question" + :status="response.status" :answer="response.answer" :assistant="response.assistant" :created-at="response.created_at" diff --git a/app/javascript/dashboard/i18n/locale/en/integrations.json b/app/javascript/dashboard/i18n/locale/en/integrations.json index ba1b73658..0157d5571 100644 --- a/app/javascript/dashboard/i18n/locale/en/integrations.json +++ b/app/javascript/dashboard/i18n/locale/en/integrations.json @@ -347,7 +347,7 @@ }, "FEATURES": { "TITLE": "Features", - "ALLOW_CONVERSATION_FAQS": "Generate responses from resolved conversations", + "ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations", "ALLOW_MEMORIES": "Capture key details as memories from customer interactions." } }, @@ -366,8 +366,8 @@ "HEADER": "Documents", "ADD_NEW": "Create a new document", "RELATED_RESPONSES": { - "TITLE": "Related Responses", - "DESCRIPTION": "These responses are generated directly from the document." + "TITLE": "Related FAQs", + "DESCRIPTION": "These FAQs are generated directly from the document." }, "FORM_DESCRIPTION": "Enter the URL of the document to add it as a knowledge source and choose the assistant to associate it with.", "CREATE": { @@ -410,6 +410,17 @@ "SUCCESS_MESSAGE": "FAQ deleted successfully", "ERROR_MESSAGE": "There was an error deleting the FAQ, please try again." }, + "FILTER" :{ + "ASSISTANT": "Assistant: {selected}", + "STATUS": "Status: {selected}", + "ALL_ASSISTANTS": "All" + }, + "STATUS": { + "TITLE": "Status", + "PENDING": "Pending", + "APPROVED": "Approved", + "ALL": "All" + }, "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "CREATE": { "TITLE": "Add an FAQ", @@ -437,10 +448,11 @@ "EDIT": { "TITLE": "Update the FAQ", "SUCCESS_MESSAGE": "The FAQ has been successfully updated", - "ERROR_MESSAGE": "There was an error updating the FAQ, please try again." + "ERROR_MESSAGE": "There was an error updating the FAQ, please try again", + "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" }, - "OPTIONS": { + "APPROVE": "Mark as approved", "EDIT_RESPONSE": "Edit FAQ", "DELETE_RESPONSE": "Delete FAQ" } diff --git a/app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue b/app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue index 9ce72d367..df43f7395 100644 --- a/app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue +++ b/app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue @@ -1,6 +1,11 @@