From c553997af82eb02d73cb3032ff42a32471d78116 Mon Sep 17 00:00:00 2001 From: Petterson <58094725+hahuma@users.noreply.github.com> Date: Tue, 8 Jul 2025 04:10:40 -0300 Subject: [PATCH 1/2] fix: Translate Priority and Messages types in Automations and Macros (#11741) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Pull Request Template ## Description With these fixes, I could improve some translations in portuguese, and also I added some improvements to make some drowpdown values, that were not translatable into translatable strings, into the Automations, Macros and Custom Attributes page. I also fixed some typos. Here are the main improvements. - ~Fixed typo in portuguese into `Reports > Agents` page:~ ~Before: ![image](https://github.com/user-attachments/assets/5a911108-76a6-4e54-82f1-1185c3e1981b) After: ![image](https://github.com/user-attachments/assets/2a4fc5bf-3239-47b5-9113-ba66e0f44b9c)~ - Added the possibility to make the `Priority` and `Message types` translatables in other languages, into Macros and Automations page. Also added the same feature for Custom attributes page at `applies to` and `type` fields: Before: ![image](https://github.com/user-attachments/assets/d53b3e6d-be3e-4e02-9478-7d3121cc11cb) ![image](https://github.com/user-attachments/assets/28a7bffe-fe8b-43f6-8e30-9d62ab8adf14) ![image](https://github.com/user-attachments/assets/18a983c1-2db8-445d-a4cc-982beb88015a) ![image](https://github.com/user-attachments/assets/2be8b0f2-ed9e-4bac-aeb0-596533200da4) After: ![image](https://github.com/user-attachments/assets/0fa904ae-7b48-4ce4-afb8-e0586c5624b7) ![image](https://github.com/user-attachments/assets/a524f119-9222-4e98-9cd7-2fca3303e8d5) ![image](https://github.com/user-attachments/assets/7062f277-e9c9-4473-980b-6ca2d6bdcefc) ![image](https://github.com/user-attachments/assets/0bb66f07-ee10-4833-b950-b9aa9441a312) - ~Improve Bots page. In the Brazilian portuguese is very common and widely used bots to refer to chatbots, using `robô` as a direct translations sounds weird, `robô` is used more often when we are talking about robots, not chatbots. Before: ![image](https://github.com/user-attachments/assets/3966cc11-51f4-4e1a-bc81-ada2795408e8) After: ![image](https://github.com/user-attachments/assets/c9ec0ad5-974e-40d9-9542-031db99839e2)~ - Added multiselect both `no options` and `Select` placeholder translatable strings: Before: ![image](https://github.com/user-attachments/assets/545641d4-87ae-4305-8adc-3b73bbaf2ab1) ![image](https://github.com/user-attachments/assets/8800c001-abe7-41bb-bd68-feb5db13b8f0) After: ![image](https://github.com/user-attachments/assets/46748652-28f2-4ae3-9d20-55db1015aaae) - Added `.pnpm-store` to `.gitignore`, when I'm using docker, the pnpm always creates this folder into my root directory, so I imagine the same could happens with others, so I fixed it. ## Type of change Please delete options that are not relevant. - [x] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality not to work as expected) - [ ] This change requires a documentation update ## How Has This Been Tested? I've added some translations for my language (brazilian portuguese), so i just switched the languages between the original in EN to PT_BR. ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: iamsivin Co-authored-by: Muhsin Keloth --- .../widgets/AutomationActionInput.vue | 12 +++++- .../components/widgets/FilterInput/Index.vue | 16 ++++++-- .../composables/spec/useAutomation.spec.js | 13 ++++-- .../composables/spec/useMacros.spec.js | 11 +++-- .../composables/useAutomationValues.js | 23 +++++++++++ .../dashboard/composables/useMacros.js | 7 +++- .../dashboard/constants/automation.js | 11 ++++- .../dashboard/helper/automationHelper.js | 11 ++--- .../specs/fixtures/automationFixtures.js | 25 +++++++----- .../i18n/locale/en/attributesMgmt.json | 12 ++++++ .../dashboard/i18n/locale/en/automation.json | 11 +++++ .../i18n/locale/en/generalSettings.json | 1 + .../dashboard/i18n/locale/en/macros.json | 7 ++++ .../settings/attributes/AddAttribute.vue | 14 ++++++- .../settings/attributes/CustomAttribute.vue | 6 ++- .../settings/attributes/EditAttribute.vue | 11 +++-- .../settings/attributes/constants.js | 40 ++++--------------- 17 files changed, 163 insertions(+), 68 deletions(-) diff --git a/app/javascript/dashboard/components/widgets/AutomationActionInput.vue b/app/javascript/dashboard/components/widgets/AutomationActionInput.vue index f59b12dda..5f9aa8f0d 100644 --- a/app/javascript/dashboard/components/widgets/AutomationActionInput.vue +++ b/app/javascript/dashboard/components/widgets/AutomationActionInput.vue @@ -133,7 +133,11 @@ export default { :options="dropdownValues" :allow-empty="false" :option-height="104" - /> + > + +
+ > + +
+ > + +
+ > + +
{ case 'country_code': return countries; case 'message_type': - return MESSAGE_CONDITION_VALUES; + return messageTypeOptions; + case 'priority': + return priorityOptions; default: return []; } @@ -93,6 +96,8 @@ describe('useAutomation', () => { return []; case 'add_sla': return slaPolicies; + case 'change_priority': + return priorityOptions; default: return []; } @@ -218,8 +223,9 @@ describe('useAutomation', () => { expect(getConditionDropdownValues('browser_language')).toEqual(languages); expect(getConditionDropdownValues('country_code')).toEqual(countries); expect(getConditionDropdownValues('message_type')).toEqual( - MESSAGE_CONDITION_VALUES + messageTypeOptions ); + expect(getConditionDropdownValues('priority')).toEqual(priorityOptions); }); it('gets action dropdown values correctly', () => { @@ -231,6 +237,7 @@ describe('useAutomation', () => { expect(getActionDropdownValues('send_email_to_team')).toEqual(teams); expect(getActionDropdownValues('send_message')).toEqual([]); expect(getActionDropdownValues('add_sla')).toEqual(slaPolicies); + expect(getActionDropdownValues('change_priority')).toEqual(priorityOptions); }); it('handles event change correctly', () => { diff --git a/app/javascript/dashboard/composables/spec/useMacros.spec.js b/app/javascript/dashboard/composables/spec/useMacros.spec.js index a72d2ddfe..7666e4afa 100644 --- a/app/javascript/dashboard/composables/spec/useMacros.spec.js +++ b/app/javascript/dashboard/composables/spec/useMacros.spec.js @@ -5,6 +5,9 @@ import { PRIORITY_CONDITION_VALUES } from 'dashboard/constants/automation'; vi.mock('dashboard/composables/store'); vi.mock('dashboard/helper/automationHelper.js'); +vi.mock('vue-i18n', () => ({ + useI18n: () => ({ t: key => key }), +})); describe('useMacros', () => { const mockLabels = [ @@ -148,9 +151,11 @@ describe('useMacros', () => { it('returns PRIORITY_CONDITION_VALUES for change_priority type', () => { const { getMacroDropdownValues } = useMacros(); - expect(getMacroDropdownValues('change_priority')).toEqual( - PRIORITY_CONDITION_VALUES - ); + const expectedPriority = PRIORITY_CONDITION_VALUES.map(item => ({ + id: item.id, + name: `MACROS.PRIORITY_TYPES.${item.i18nKey}`, + })); + expect(getMacroDropdownValues('change_priority')).toEqual(expectedPriority); }); it('returns an empty array for unknown types', () => { diff --git a/app/javascript/dashboard/composables/useAutomationValues.js b/app/javascript/dashboard/composables/useAutomationValues.js index 4df46be8f..abc44f66b 100644 --- a/app/javascript/dashboard/composables/useAutomationValues.js +++ b/app/javascript/dashboard/composables/useAutomationValues.js @@ -8,6 +8,10 @@ import { getActionOptions, getConditionOptions, } from 'dashboard/helper/automationHelper'; +import { + MESSAGE_CONDITION_VALUES, + PRIORITY_CONDITION_VALUES, +} from 'dashboard/constants/automation'; /** * This is a shared composables that holds utilites used to build dropdown and file options @@ -60,6 +64,20 @@ export default function useAutomationValues() { ]; }); + const messageTypeOptions = computed(() => + MESSAGE_CONDITION_VALUES.map(item => ({ + id: item.id, + name: t(`AUTOMATION.MESSAGE_TYPES.${item.i18nKey}`), + })) + ); + + const priorityOptions = computed(() => + PRIORITY_CONDITION_VALUES.map(item => ({ + id: item.id, + name: t(`AUTOMATION.PRIORITY_TYPES.${item.i18nKey}`), + })) + ); + /** * Adds a translated "None" option to the beginning of a list * @param {Array} list - The list to add "None" to @@ -87,6 +105,8 @@ export default function useAutomationValues() { customAttributes: getters['attributes/getAttributes'].value, inboxes: inboxes.value, statusFilterOptions: statusFilterOptions.value, + priorityOptions: priorityOptions.value, + messageTypeOptions: messageTypeOptions.value, teams: teams.value, languages, countries, @@ -108,6 +128,7 @@ export default function useAutomationValues() { languages, type, addNoneToListFn: addNoneToList, + priorityOptions: priorityOptions.value, }); }; @@ -115,6 +136,8 @@ export default function useAutomationValues() { booleanFilterOptions, statusFilterItems, statusFilterOptions, + priorityOptions, + messageTypeOptions, getConditionDropdownValues, getActionDropdownValues, agents, diff --git a/app/javascript/dashboard/composables/useMacros.js b/app/javascript/dashboard/composables/useMacros.js index 0489e2d1f..8f0227fa3 100644 --- a/app/javascript/dashboard/composables/useMacros.js +++ b/app/javascript/dashboard/composables/useMacros.js @@ -1,4 +1,5 @@ import { computed } from 'vue'; +import { useI18n } from 'vue-i18n'; import { useStoreGetters } from 'dashboard/composables/store'; import { PRIORITY_CONDITION_VALUES } from 'dashboard/constants/automation'; @@ -7,6 +8,7 @@ import { PRIORITY_CONDITION_VALUES } from 'dashboard/constants/automation'; * @returns {Object} An object containing the getMacroDropdownValues function */ export const useMacros = () => { + const { t } = useI18n(); const getters = useStoreGetters(); const labels = computed(() => getters['labels/getLabels'].value); @@ -32,7 +34,10 @@ export const useMacros = () => { name: i.title, })); case 'change_priority': - return PRIORITY_CONDITION_VALUES; + return PRIORITY_CONDITION_VALUES.map(item => ({ + id: item.id, + name: t(`MACROS.PRIORITY_TYPES.${item.i18nKey}`), + })); default: return []; } diff --git a/app/javascript/dashboard/constants/automation.js b/app/javascript/dashboard/constants/automation.js index 3aa9754d5..399dd6153 100644 --- a/app/javascript/dashboard/constants/automation.js +++ b/app/javascript/dashboard/constants/automation.js @@ -38,11 +38,13 @@ export const DEFAULT_ACTIONS = [ export const MESSAGE_CONDITION_VALUES = [ { id: 'incoming', - name: 'Incoming Message', + name: 'Incoming', + i18nKey: 'INCOMING', }, { id: 'outgoing', - name: 'Outgoing Message', + name: 'Outgoing', + i18nKey: 'OUTGOING', }, ]; @@ -50,21 +52,26 @@ export const PRIORITY_CONDITION_VALUES = [ { id: 'nil', name: 'None', + i18nKey: 'NONE', }, { id: 'low', name: 'Low', + i18nKey: 'LOW', }, { id: 'medium', name: 'Medium', + i18nKey: 'MEDIUM', }, { id: 'high', name: 'High', + i18nKey: 'HIGH', }, { id: 'urgent', name: 'Urgent', + i18nKey: 'URGENT', }, ]; diff --git a/app/javascript/dashboard/helper/automationHelper.js b/app/javascript/dashboard/helper/automationHelper.js index 07186c122..c9852814a 100644 --- a/app/javascript/dashboard/helper/automationHelper.js +++ b/app/javascript/dashboard/helper/automationHelper.js @@ -8,8 +8,6 @@ import { DEFAULT_CONVERSATION_OPENED_CONDITION, DEFAULT_OTHER_CONDITION, DEFAULT_ACTIONS, - MESSAGE_CONDITION_VALUES, - PRIORITY_CONDITION_VALUES, } from 'dashboard/constants/automation'; import filterQueryGenerator from './filterQueryGenerator'; import actionQueryGenerator from './actionQueryGenerator'; @@ -103,6 +101,7 @@ export const getActionOptions = ({ slaPolicies, type, addNoneToListFn, + priorityOptions, }) => { const actionsMap = { assign_agent: addNoneToListFn ? addNoneToListFn(agents) : agents, @@ -110,7 +109,7 @@ export const getActionOptions = ({ send_email_to_team: teams, add_label: generateConditionOptions(labels, 'title'), remove_label: generateConditionOptions(labels, 'title'), - change_priority: PRIORITY_CONDITION_VALUES, + change_priority: priorityOptions, add_sla: slaPolicies, }; return actionsMap[type]; @@ -128,6 +127,8 @@ export const getConditionOptions = ({ statusFilterOptions, teams, type, + priorityOptions, + messageTypeOptions, }) => { if (isCustomAttributeCheckbox(customAttributes, type)) { return booleanFilterOptions; @@ -147,8 +148,8 @@ export const getConditionOptions = ({ browser_language: languages, conversation_language: languages, country_code: countries, - message_type: MESSAGE_CONDITION_VALUES, - priority: PRIORITY_CONDITION_VALUES, + message_type: messageTypeOptions, + priority: priorityOptions, }; return conditionFilterMaps[type]; diff --git a/app/javascript/dashboard/helper/specs/fixtures/automationFixtures.js b/app/javascript/dashboard/helper/specs/fixtures/automationFixtures.js index 1f642587b..e8f1b3ad9 100644 --- a/app/javascript/dashboard/helper/specs/fixtures/automationFixtures.js +++ b/app/javascript/dashboard/helper/specs/fixtures/automationFixtures.js @@ -2,6 +2,11 @@ import allLanguages from 'dashboard/components/widgets/conversation/advancedFilt import allCountries from 'shared/constants/countries.js'; +import { + MESSAGE_CONDITION_VALUES, + PRIORITY_CONDITION_VALUES, +} from 'dashboard/constants/automation'; + export const customAttributes = [ { id: 1, @@ -636,16 +641,16 @@ export const statusFilterOptions = [ ]; export const languages = allLanguages; export const countries = allCountries; -export const MESSAGE_CONDITION_VALUES = [ - { - id: 'incoming', - name: 'Incoming Message', - }, - { - id: 'outgoing', - name: 'Outgoing Message', - }, -]; + +export const messageTypeOptions = MESSAGE_CONDITION_VALUES.map(item => ({ + id: item.id, + name: `AUTOMATION.MESSAGE_TYPES.${item.i18nKey}`, +})); + +export const priorityOptions = PRIORITY_CONDITION_VALUES.map(item => ({ + id: item.id, + name: `AUTOMATION.PRIORITY_TYPES.${item.i18nKey}`, +})); export const automationToSubmit = { name: 'Fayaz', diff --git a/app/javascript/dashboard/i18n/locale/en/attributesMgmt.json b/app/javascript/dashboard/i18n/locale/en/attributesMgmt.json index dd413ab26..cea6359de 100644 --- a/app/javascript/dashboard/i18n/locale/en/attributesMgmt.json +++ b/app/javascript/dashboard/i18n/locale/en/attributesMgmt.json @@ -5,6 +5,18 @@ "LOADING": "Fetching custom attributes", "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", "LEARN_MORE": "Learn more about custom attributes", + "ATTRIBUTE_MODELS": { + "CONVERSATION": "Conversation", + "CONTACT": "Contact" + }, + "ATTRIBUTE_TYPES": { + "TEXT": "Text", + "NUMBER": "Number", + "LINK": "Link", + "DATE": "Date", + "LIST": "List", + "CHECKBOX": "Checkbox" + }, "ADD": { "TITLE": "Add Custom Attribute", "SUBMIT": "Create", diff --git a/app/javascript/dashboard/i18n/locale/en/automation.json b/app/javascript/dashboard/i18n/locale/en/automation.json index c5dd4a514..cb030332f 100644 --- a/app/javascript/dashboard/i18n/locale/en/automation.json +++ b/app/javascript/dashboard/i18n/locale/en/automation.json @@ -150,6 +150,17 @@ "ADD_SLA": "Add SLA", "OPEN_CONVERSATION": "Open conversation" }, + "MESSAGE_TYPES": { + "INCOMING": "Incoming Message", + "OUTGOING": "Outgoing Message" + }, + "PRIORITY_TYPES": { + "NONE": "None", + "LOW": "Low", + "MEDIUM": "Medium", + "HIGH": "High", + "URGENT": "Urgent" + }, "ATTRIBUTES": { "MESSAGE_TYPE": "Message Type", "MESSAGE_CONTAINS": "Message Contains", diff --git a/app/javascript/dashboard/i18n/locale/en/generalSettings.json b/app/javascript/dashboard/i18n/locale/en/generalSettings.json index 9055574c5..d924bffbd 100644 --- a/app/javascript/dashboard/i18n/locale/en/generalSettings.json +++ b/app/javascript/dashboard/i18n/locale/en/generalSettings.json @@ -134,6 +134,7 @@ "MULTISELECT": { "ENTER_TO_SELECT": "Press enter to select", "ENTER_TO_REMOVE": "Press enter to remove", + "NO_OPTIONS": "List is empty", "SELECT_ONE": "Select one", "SELECT": "Select" } diff --git a/app/javascript/dashboard/i18n/locale/en/macros.json b/app/javascript/dashboard/i18n/locale/en/macros.json index d22744190..fcb409f34 100644 --- a/app/javascript/dashboard/i18n/locale/en/macros.json +++ b/app/javascript/dashboard/i18n/locale/en/macros.json @@ -99,6 +99,13 @@ "CHANGE_PRIORITY": "Change Priority", "ADD_PRIVATE_NOTE": "Add a Private Note", "SEND_WEBHOOK_EVENT": "Send Webhook Event" + }, + "PRIORITY_TYPES": { + "NONE": "None", + "LOW": "Low", + "MEDIUM": "Medium", + "HIGH": "High", + "URGENT": "Urgent" } } } diff --git a/app/javascript/dashboard/routes/dashboard/settings/attributes/AddAttribute.vue b/app/javascript/dashboard/routes/dashboard/settings/attributes/AddAttribute.vue index c0b388ce1..006d5cdaf 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/attributes/AddAttribute.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/attributes/AddAttribute.vue @@ -40,8 +40,6 @@ export default { regexPattern: null, regexCue: null, regexEnabled: false, - models: ATTRIBUTE_MODELS, - types: ATTRIBUTE_TYPES, values: [], options: [], show: true, @@ -53,6 +51,18 @@ export default { ...mapGetters({ uiFlags: 'getUIFlags', }), + models() { + return ATTRIBUTE_MODELS.map(item => ({ + ...item, + option: this.$t(`ATTRIBUTES_MGMT.ATTRIBUTE_MODELS.${item.key}`), + })); + }, + types() { + return ATTRIBUTE_TYPES.map(item => ({ + ...item, + option: this.$t(`ATTRIBUTES_MGMT.ATTRIBUTE_TYPES.${item.key}`), + })); + }, isMultiselectInvalid() { return this.isTouched && this.values.length === 0; }, diff --git a/app/javascript/dashboard/routes/dashboard/settings/attributes/CustomAttribute.vue b/app/javascript/dashboard/routes/dashboard/settings/attributes/CustomAttribute.vue index 420edafce..db9c2dacf 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/attributes/CustomAttribute.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/attributes/CustomAttribute.vue @@ -113,7 +113,11 @@ const tableHeaders = computed(() => { - {{ attribute.attribute_display_type }} + {{ + $t( + `ATTRIBUTES_MGMT.ATTRIBUTE_TYPES.${attribute.attribute_display_type?.toUpperCase()}` + ) + }} ({ + ...item, + option: this.$t(`ATTRIBUTES_MGMT.ATTRIBUTE_TYPES.${item.key}`), + })); + }, setAttributeListValue() { return this.selectedAttribute.attribute_values.map(values => ({ name: values, @@ -84,9 +89,9 @@ export default { selectedAttributeType() { return this.types.find( item => - item.option.toLowerCase() === + item.key.toLowerCase() === this.selectedAttribute.attribute_display_type - ).id; + )?.id; }, keyErrorMessage() { if (!this.v$.attributeKey.isKey) { diff --git a/app/javascript/dashboard/routes/dashboard/settings/attributes/constants.js b/app/javascript/dashboard/routes/dashboard/settings/attributes/constants.js index 9aa36dec1..619ce7718 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/attributes/constants.js +++ b/app/javascript/dashboard/routes/dashboard/settings/attributes/constants.js @@ -1,37 +1,13 @@ export const ATTRIBUTE_MODELS = [ - { - id: 0, - option: 'Conversation', - }, - { - id: 1, - option: 'Contact', - }, + { id: 0, key: 'CONVERSATION' }, + { id: 1, key: 'CONTACT' }, ]; export const ATTRIBUTE_TYPES = [ - { - id: 0, - option: 'Text', - }, - { - id: 1, - option: 'Number', - }, - { - id: 4, - option: 'Link', - }, - { - id: 5, - option: 'Date', - }, - { - id: 6, - option: 'List', - }, - { - id: 7, - option: 'Checkbox', - }, + { id: 0, key: 'TEXT' }, + { id: 1, key: 'NUMBER' }, + { id: 4, key: 'LINK' }, + { id: 5, key: 'DATE' }, + { id: 6, key: 'LIST' }, + { id: 7, key: 'CHECKBOX' }, ]; From 2e0bc94b2dfa8ef01e2489c162fc99ff9a42ef07 Mon Sep 17 00:00:00 2001 From: Tanmay Deep Sharma <32020192+tds-1@users.noreply.github.com> Date: Tue, 8 Jul 2025 18:33:32 +0700 Subject: [PATCH 2/2] fix embeddings issue --- .../captain/documents/response_builder_job.rb | 8 ++-- .../tools/pdf_extraction_parser_job.rb | 2 +- .../tools/pdf_extraction_service_spec.rb | 45 ++----------------- 3 files changed, 10 insertions(+), 45 deletions(-) diff --git a/enterprise/app/jobs/captain/documents/response_builder_job.rb b/enterprise/app/jobs/captain/documents/response_builder_job.rb index b7d0d5069..1a854671f 100644 --- a/enterprise/app/jobs/captain/documents/response_builder_job.rb +++ b/enterprise/app/jobs/captain/documents/response_builder_job.rb @@ -1,14 +1,15 @@ class Captain::Documents::ResponseBuilderJob < ApplicationJob queue_as :low - def perform(document, full_content = nil) + def perform(document, full_content = nil, skip_reset: false) # Use full content for FAQ generation if provided (for PDFs), otherwise use document.content content_for_faqs = full_content || document.content # Skip processing if no content available return if content_for_faqs.blank? - reset_previous_responses(document) + # Only reset responses if not explicitly skipped (for PDF chunks processing) + reset_previous_responses(document) unless skip_reset faqs = Captain::Llm::FaqGeneratorService.new(content_for_faqs).generate faqs.each do |faq| @@ -30,6 +31,7 @@ class Captain::Documents::ResponseBuilderJob < ApplicationJob documentable: document ) rescue ActiveRecord::RecordInvalid => e - Rails.logger.error "Error in creating response document: #{e.message}" + # Log validation errors but continue processing other FAQs + Rails.logger.info "Skipping duplicate or invalid FAQ: #{e.message}" end end diff --git a/enterprise/app/jobs/captain/tools/pdf_extraction_parser_job.rb b/enterprise/app/jobs/captain/tools/pdf_extraction_parser_job.rb index eb1d7d54f..c9ebfeb83 100644 --- a/enterprise/app/jobs/captain/tools/pdf_extraction_parser_job.rb +++ b/enterprise/app/jobs/captain/tools/pdf_extraction_parser_job.rb @@ -37,7 +37,7 @@ class Captain::Tools::PdfExtractionParserJob < ApplicationJob # Use the ResponseBuilderJob with full_content parameter to generate FAQs # This will link all FAQs to the main document while using the chunk content for AI processing # Skip reset since it's already done in the main PDF extraction job - Captain::Documents::ResponseBuilderJob.perform_later(main_document, context_content) + Captain::Documents::ResponseBuilderJob.perform_later(main_document, context_content, skip_reset: true) end def should_process_content?(content, account) diff --git a/spec/enterprise/services/captain/tools/pdf_extraction_service_spec.rb b/spec/enterprise/services/captain/tools/pdf_extraction_service_spec.rb index 0a483aeea..6b4110453 100644 --- a/spec/enterprise/services/captain/tools/pdf_extraction_service_spec.rb +++ b/spec/enterprise/services/captain/tools/pdf_extraction_service_spec.rb @@ -41,30 +41,8 @@ RSpec.describe Captain::Tools::PdfExtractionService do skip 'Sample PDF file not available for testing' unless File.exist?(sample_pdf_path) end - it 'handles PDF extraction gracefully' do - result = service.perform - expect(result).to have_key(:success) - expect(result).to have_key(:content).or have_key(:errors) - - if result[:success] - expect(result[:content]).to be_an(Array) - else - expect(result[:errors]).to be_an(Array) - end - end - - it 'returns structured result format' do - result = service.perform - - if result[:success] && result[:content].present? - first_chunk = result[:content].first - expect(first_chunk).to have_key(:content) - expect(first_chunk).to have_key(:page_number) - expect(first_chunk).to have_key(:chunk_index) - expect(first_chunk).to have_key(:total_chunks) - else - expect(result[:errors]).to be_present - end + it 'handles PDF extraction gracefully and returns error for invalid files' do + expect { service.perform }.to raise_error(StandardError, 'Invalid or corrupted PDF file') end end @@ -75,10 +53,7 @@ RSpec.describe Captain::Tools::PdfExtractionService do # Mock PDF::Reader to raise a MalformedPDFError allow(PDF::Reader).to receive(:open).and_raise(PDF::Reader::MalformedPDFError, 'PDF does not contain EOF marker') - result = service.perform - expect(result[:success]).to be false - expect(result[:errors]).to be_an(Array) - expect(result[:errors]).to include('Invalid or corrupted PDF file') + expect { service.perform }.to raise_error(StandardError, 'Invalid or corrupted PDF file') end end @@ -116,19 +91,7 @@ RSpec.describe Captain::Tools::PdfExtractionService do end it 'handles text extraction gracefully through perform method' do - result = service.perform - - if result[:success] - expect(result[:content]).to be_an(Array) - if result[:content].any? - page_content = result[:content].first - expect(page_content).to have_key(:page_number) - expect(page_content).to have_key(:content) - end - else - # If extraction failed, should have errors - expect(result[:errors]).to be_present - end + expect { service.perform }.to raise_error(StandardError, 'Invalid or corrupted PDF file') end end