Merge remote-tracking branch 'origin/feat/support-pdf-upload-faq-gen' into test-pdf-support-captain
This commit is contained in:
@@ -133,7 +133,11 @@ export default {
|
||||
:options="dropdownValues"
|
||||
:allow-empty="false"
|
||||
:option-height="104"
|
||||
/>
|
||||
>
|
||||
<template #noOptions>
|
||||
{{ $t('FORMS.MULTISELECT.NO_OPTIONS') }}
|
||||
</template>
|
||||
</multiselect>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="inputType === 'multi_select'"
|
||||
@@ -152,7 +156,11 @@ export default {
|
||||
:options="dropdownValues"
|
||||
:allow-empty="false"
|
||||
:option-height="104"
|
||||
/>
|
||||
>
|
||||
<template #noOptions>
|
||||
{{ $t('FORMS.MULTISELECT.NO_OPTIONS') }}
|
||||
</template>
|
||||
</multiselect>
|
||||
</div>
|
||||
<input
|
||||
v-else-if="inputType === 'email'"
|
||||
|
||||
@@ -203,7 +203,7 @@ export default {
|
||||
v-model="values"
|
||||
track-by="id"
|
||||
label="name"
|
||||
placeholder="Select"
|
||||
:placeholder="$t('FORMS.MULTISELECT.SELECT')"
|
||||
multiple
|
||||
selected-label
|
||||
:select-label="$t('FORMS.MULTISELECT.ENTER_TO_SELECT')"
|
||||
@@ -211,7 +211,11 @@ export default {
|
||||
:max-height="160"
|
||||
:options="dropdownValues"
|
||||
:allow-empty="false"
|
||||
/>
|
||||
>
|
||||
<template #noOptions>
|
||||
{{ $t('FORMS.MULTISELECT.NO_OPTIONS') }}
|
||||
</template>
|
||||
</multiselect>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="inputType === 'search_select'"
|
||||
@@ -221,7 +225,7 @@ export default {
|
||||
v-model="values"
|
||||
track-by="id"
|
||||
label="name"
|
||||
placeholder="Select"
|
||||
:placeholder="$t('FORMS.MULTISELECT.SELECT')"
|
||||
selected-label
|
||||
:select-label="$t('FORMS.MULTISELECT.ENTER_TO_SELECT')"
|
||||
deselect-label=""
|
||||
@@ -229,7 +233,11 @@ export default {
|
||||
:options="dropdownValues"
|
||||
:allow-empty="false"
|
||||
:option-height="104"
|
||||
/>
|
||||
>
|
||||
<template #noOptions>
|
||||
{{ $t('FORMS.MULTISELECT.NO_OPTIONS') }}
|
||||
</template>
|
||||
</multiselect>
|
||||
</div>
|
||||
<div v-else-if="inputType === 'date'" class="multiselect-wrap--small">
|
||||
<input
|
||||
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
teams,
|
||||
labels,
|
||||
statusFilterOptions,
|
||||
messageTypeOptions,
|
||||
priorityOptions,
|
||||
campaigns,
|
||||
contacts,
|
||||
inboxes,
|
||||
@@ -16,7 +18,6 @@ import {
|
||||
countries,
|
||||
slaPolicies,
|
||||
} from 'dashboard/helper/specs/fixtures/automationFixtures.js';
|
||||
import { MESSAGE_CONDITION_VALUES } from 'dashboard/constants/automation';
|
||||
|
||||
vi.mock('dashboard/composables/store');
|
||||
vi.mock('dashboard/composables');
|
||||
@@ -71,7 +72,9 @@ describe('useAutomation', () => {
|
||||
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', () => {
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 [];
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
|
||||
@@ -113,7 +113,11 @@ const tableHeaders = computed(() => {
|
||||
<td
|
||||
class="py-4 ltr:pr-4 rtl:pl-4 overflow-hidden whitespace-nowrap text-ellipsis"
|
||||
>
|
||||
{{ attribute.attribute_display_type }}
|
||||
{{
|
||||
$t(
|
||||
`ATTRIBUTES_MGMT.ATTRIBUTE_TYPES.${attribute.attribute_display_type?.toUpperCase()}`
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
<td
|
||||
class="py-4 ltr:pr-4 rtl:pl-4 attribute-key overflow-hidden whitespace-nowrap text-ellipsis"
|
||||
|
||||
@@ -32,7 +32,6 @@ export default {
|
||||
regexPattern: null,
|
||||
regexCue: null,
|
||||
regexEnabled: false,
|
||||
types: ATTRIBUTE_TYPES,
|
||||
show: true,
|
||||
attributeKey: '',
|
||||
values: [],
|
||||
@@ -59,6 +58,12 @@ export default {
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
types() {
|
||||
return ATTRIBUTE_TYPES.map(item => ({
|
||||
...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) {
|
||||
|
||||
@@ -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' },
|
||||
];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user