@adminforth/bulk-ai-flow 1.19.0 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build.log +2 -2
- package/custom/VisionAction.vue +53 -17
- package/dist/custom/VisionAction.vue +53 -17
- package/dist/index.js +37 -12
- package/index.ts +39 -9
- package/package.json +1 -1
package/build.log
CHANGED
|
@@ -13,5 +13,5 @@ custom/package-lock.json
|
|
|
13
13
|
custom/package.json
|
|
14
14
|
custom/tsconfig.json
|
|
15
15
|
|
|
16
|
-
sent
|
|
17
|
-
total size is
|
|
16
|
+
sent 95,557 bytes received 172 bytes 191,458.00 bytes/sec
|
|
17
|
+
total size is 94,921 speedup is 0.99
|
package/custom/VisionAction.vue
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="flex items-end justify-start gap-2 cursor-pointer">
|
|
3
3
|
<div class="flex items-center justify-center text-white bg-gradient-to-r h-[18px] from-purple-500 via-purple-600 to-purple-700 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-purple-300 dark:focus:ring-purple-800 font-medium rounded-md text-sm px-1 text-center">
|
|
4
|
-
AI
|
|
4
|
+
{{t('AI')}}
|
|
5
5
|
</div>
|
|
6
6
|
<p class="text-justify max-h-[18px] truncate max-w-[60vw] md:max-w-none">{{ props.meta.actionName }}</p>
|
|
7
7
|
</div>
|
|
8
8
|
<Dialog
|
|
9
9
|
ref="confirmDialog"
|
|
10
|
-
header="Bulk AI
|
|
10
|
+
header="Bulk AI Generation"
|
|
11
11
|
class="[scrollbar-gutter:stable] !max-w-full w-fit h-fit"
|
|
12
12
|
:class="popupMode === 'generation' ? 'lg:w-[1600px] !lg:max-w-[1600px]'
|
|
13
13
|
: popupMode === 'settings' ? 'lg:w-[1000px] !lg:max-w-[1000px]'
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
:beforeCloseFunction="closeDialog"
|
|
16
16
|
:closable="false"
|
|
17
17
|
:askForCloseConfirmation="popupMode === 'generation' ? true : false"
|
|
18
|
-
closeConfirmationText="Are you sure you want to close without saving?"
|
|
18
|
+
:closeConfirmationText="t('Are you sure you want to close without saving?')"
|
|
19
19
|
:buttons="popupMode === 'generation' ? [
|
|
20
20
|
{
|
|
21
|
-
label: checkedCount > 1 ? 'Save fields' : 'Save field',
|
|
21
|
+
label: checkedCount > 1 ? t('Save fields') : t('Save field'),
|
|
22
22
|
options: {
|
|
23
23
|
disabled: isLoading || checkedCount < 1 || isCriticalError || isFetchingRecords || isGeneratingImages || isAnalizingFields || isAnalizingImages,
|
|
24
24
|
loader: isLoading, class: 'w-fit'
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
onclick: async (dialog) => { await saveData(); dialog.hide(); }
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
|
-
label: 'Cancel',
|
|
29
|
+
label: t('Cancel'),
|
|
30
30
|
options: {
|
|
31
31
|
class: 'bg-white hover:!bg-gray-100 !text-gray-900 hover:!text-gray-800 dark:!bg-gray-800 dark:!text-gray-100 dark:hover:!bg-gray-700 !border-gray-200'
|
|
32
32
|
},
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
] : popupMode === 'settings' ? [
|
|
36
36
|
{
|
|
37
|
-
label: 'Save settings',
|
|
37
|
+
label: t('Save settings'),
|
|
38
38
|
options: {
|
|
39
39
|
class: 'w-fit'
|
|
40
40
|
},
|
|
@@ -42,18 +42,32 @@
|
|
|
42
42
|
},
|
|
43
43
|
] :
|
|
44
44
|
[
|
|
45
|
+
// {
|
|
46
|
+
// label: t('Edit prompts'),
|
|
47
|
+
// options: {
|
|
48
|
+
// class: 'w-fit ml-auto'
|
|
49
|
+
// },
|
|
50
|
+
// onclick: (dialog) => { clickSettingsButton(); }
|
|
51
|
+
// },
|
|
45
52
|
{
|
|
46
|
-
label: '
|
|
53
|
+
label: t('Cancel'),
|
|
47
54
|
options: {
|
|
48
|
-
class: 'w-
|
|
55
|
+
class: 'w-2/5 bg-white hover:!bg-gray-100 !text-gray-900 hover:!text-gray-800 dark:!bg-gray-800 dark:!text-gray-100 dark:hover:!bg-gray-700 !border-gray-200'
|
|
49
56
|
},
|
|
50
|
-
onclick: (dialog) =>
|
|
57
|
+
onclick: (dialog) => confirmDialog.tryToHideModal()
|
|
51
58
|
},
|
|
59
|
+
{
|
|
60
|
+
label: t('Start generation'),
|
|
61
|
+
options: {
|
|
62
|
+
class: 'w-3/5 px-5 py-2.5 bg-gradient-to-r from-purple-500 via-purple-600 to-purple-700 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-purple-300 dark:focus:ring-purple-800 rounded-md text-white border-none'
|
|
63
|
+
},
|
|
64
|
+
onclick: (dialog) => { runAiActions(); }
|
|
65
|
+
}
|
|
52
66
|
]"
|
|
53
67
|
:click-to-close-outside="false"
|
|
54
68
|
>
|
|
55
|
-
<div class="
|
|
56
|
-
<div v-if="records && props.checkboxes.length && popupMode === 'generation'" class="w-full overflow-x-auto">
|
|
69
|
+
<div class="bulk-vision-table flex flex-col items-center max-w-[1560px] md:max-h-[75vh] gap-3 md:gap-4 w-full h-full overflow-y-auto">
|
|
70
|
+
<div v-if="records && props.checkboxes.length && popupMode === 'generation'" class="[scrollbar-gutter:stable] w-full overflow-x-auto">
|
|
57
71
|
<VisionTable
|
|
58
72
|
:checkbox="props.checkboxes"
|
|
59
73
|
:records="records"
|
|
@@ -112,7 +126,7 @@
|
|
|
112
126
|
}}</p>
|
|
113
127
|
<div class="grid grid-cols-2 gap-4">
|
|
114
128
|
<div v-for="(prompt, promptKey) in promptsCategory" :key="promptKey">
|
|
115
|
-
{{ formatLabel(promptKey) }} prompt:
|
|
129
|
+
{{ formatLabel(promptKey) }} {{ t('prompt') }}:
|
|
116
130
|
<Textarea
|
|
117
131
|
v-model="generationPrompts[key][promptKey]"
|
|
118
132
|
class="w-full h-64 p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500"
|
|
@@ -122,10 +136,27 @@
|
|
|
122
136
|
</div>
|
|
123
137
|
</div>
|
|
124
138
|
</div>
|
|
125
|
-
<div v-else class="flex flex-col gap-2">
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
|
|
139
|
+
<div v-else class="flex flex-col gap-2 mt-2 w-full h-full">
|
|
140
|
+
<div class="flex items-center justify-between mb-2 w-full">
|
|
141
|
+
<div class="flex items-center justify-center gap-2">
|
|
142
|
+
<IconShieldSolid class="w-6 h-6 text-lightPrimary dark:text-darkPrimary" />
|
|
143
|
+
<p class="sm:text-base text-sm">{{ t('Do not overwrite existing values') }}</p>
|
|
144
|
+
<Tooltip>
|
|
145
|
+
<IconInfoCircleSolid class="w-5 h-5 me-2 text-lightPrimary dark:text-darkPrimary"/>
|
|
146
|
+
<template #tooltip>
|
|
147
|
+
<p class="max-w-64">{{ t('When enabled, the AI will skip generating content for fields that already have data. This helps to preserve existing information and avoid overwriting valuable content.') }}</p>
|
|
148
|
+
</template>
|
|
149
|
+
</Tooltip>
|
|
150
|
+
</div>
|
|
151
|
+
<Toggle
|
|
152
|
+
v-model="skipFilledFieldsForGeneration"
|
|
153
|
+
/>
|
|
154
|
+
</div>
|
|
155
|
+
<div :class="skipFilledFieldsForGeneration === false ? 'opacity-100' : 'opacity-0'" class="flex items-center text-yellow-800 bg-yellow-100 p-2 rounded-md border border-yellow-300">
|
|
156
|
+
<IconExclamationTriangle class="w-6 h-6 me-2"/>
|
|
157
|
+
<p class="sm:text-base text-sm">{{ t('Warning: Existing values will be overwritten.') }}</p>
|
|
158
|
+
</div>
|
|
159
|
+
<p class="w-fit flex justify-start text-lightPrimary dark:text-lightPrimary hover:underline cursor-pointer" @click="clickSettingsButton()">{{ t('Configure prompts') }}</p>
|
|
129
160
|
</div>
|
|
130
161
|
</div>
|
|
131
162
|
</Dialog>
|
|
@@ -134,12 +165,15 @@
|
|
|
134
165
|
<script lang="ts" setup>
|
|
135
166
|
import { callAdminForthApi } from '@/utils';
|
|
136
167
|
import { Ref, ref, watch } from 'vue'
|
|
137
|
-
import { Dialog, Button, Textarea } from '@/afcl';
|
|
168
|
+
import { Dialog, Button, Textarea, Toggle, Tooltip } from '@/afcl';
|
|
138
169
|
import VisionTable from './VisionTable.vue'
|
|
139
170
|
import adminforth from '@/adminforth';
|
|
140
171
|
import { useI18n } from 'vue-i18n';
|
|
141
172
|
import { AdminUser, type AdminForthResourceCommon } from '@/types/Common';
|
|
142
173
|
import { useCoreStore } from '@/stores/core';
|
|
174
|
+
import { IconShieldSolid, IconInfoCircleSolid } from '@iconify-prerendered/vue-flowbite';
|
|
175
|
+
import { IconExclamationTriangle } from '@iconify-prerendered/vue-humbleicons';
|
|
176
|
+
|
|
143
177
|
|
|
144
178
|
const coreStore = useCoreStore();
|
|
145
179
|
|
|
@@ -202,6 +236,7 @@ const generationPrompts = ref<any>({});
|
|
|
202
236
|
const isDataSaved = ref(false);
|
|
203
237
|
|
|
204
238
|
const regeneratingFieldsStatus = ref<Record<string, Record<string, boolean>>>({});
|
|
239
|
+
const skipFilledFieldsForGeneration = ref<boolean>(true);
|
|
205
240
|
|
|
206
241
|
const openDialog = async () => {
|
|
207
242
|
window.addEventListener('beforeunload', beforeUnloadHandler);
|
|
@@ -633,6 +668,7 @@ async function runAiAction({
|
|
|
633
668
|
actionType: actionType,
|
|
634
669
|
recordId: checkbox,
|
|
635
670
|
...(customPrompt !== undefined ? { customPrompt: JSON.stringify(customPrompt) } : {}),
|
|
671
|
+
filterFilledFields: skipFilledFieldsForGeneration.value,
|
|
636
672
|
},
|
|
637
673
|
silentError: true,
|
|
638
674
|
});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="flex items-end justify-start gap-2 cursor-pointer">
|
|
3
3
|
<div class="flex items-center justify-center text-white bg-gradient-to-r h-[18px] from-purple-500 via-purple-600 to-purple-700 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-purple-300 dark:focus:ring-purple-800 font-medium rounded-md text-sm px-1 text-center">
|
|
4
|
-
AI
|
|
4
|
+
{{t('AI')}}
|
|
5
5
|
</div>
|
|
6
6
|
<p class="text-justify max-h-[18px] truncate max-w-[60vw] md:max-w-none">{{ props.meta.actionName }}</p>
|
|
7
7
|
</div>
|
|
8
8
|
<Dialog
|
|
9
9
|
ref="confirmDialog"
|
|
10
|
-
header="Bulk AI
|
|
10
|
+
header="Bulk AI Generation"
|
|
11
11
|
class="[scrollbar-gutter:stable] !max-w-full w-fit h-fit"
|
|
12
12
|
:class="popupMode === 'generation' ? 'lg:w-[1600px] !lg:max-w-[1600px]'
|
|
13
13
|
: popupMode === 'settings' ? 'lg:w-[1000px] !lg:max-w-[1000px]'
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
:beforeCloseFunction="closeDialog"
|
|
16
16
|
:closable="false"
|
|
17
17
|
:askForCloseConfirmation="popupMode === 'generation' ? true : false"
|
|
18
|
-
closeConfirmationText="Are you sure you want to close without saving?"
|
|
18
|
+
:closeConfirmationText="t('Are you sure you want to close without saving?')"
|
|
19
19
|
:buttons="popupMode === 'generation' ? [
|
|
20
20
|
{
|
|
21
|
-
label: checkedCount > 1 ? 'Save fields' : 'Save field',
|
|
21
|
+
label: checkedCount > 1 ? t('Save fields') : t('Save field'),
|
|
22
22
|
options: {
|
|
23
23
|
disabled: isLoading || checkedCount < 1 || isCriticalError || isFetchingRecords || isGeneratingImages || isAnalizingFields || isAnalizingImages,
|
|
24
24
|
loader: isLoading, class: 'w-fit'
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
onclick: async (dialog) => { await saveData(); dialog.hide(); }
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
|
-
label: 'Cancel',
|
|
29
|
+
label: t('Cancel'),
|
|
30
30
|
options: {
|
|
31
31
|
class: 'bg-white hover:!bg-gray-100 !text-gray-900 hover:!text-gray-800 dark:!bg-gray-800 dark:!text-gray-100 dark:hover:!bg-gray-700 !border-gray-200'
|
|
32
32
|
},
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
] : popupMode === 'settings' ? [
|
|
36
36
|
{
|
|
37
|
-
label: 'Save settings',
|
|
37
|
+
label: t('Save settings'),
|
|
38
38
|
options: {
|
|
39
39
|
class: 'w-fit'
|
|
40
40
|
},
|
|
@@ -42,18 +42,32 @@
|
|
|
42
42
|
},
|
|
43
43
|
] :
|
|
44
44
|
[
|
|
45
|
+
// {
|
|
46
|
+
// label: t('Edit prompts'),
|
|
47
|
+
// options: {
|
|
48
|
+
// class: 'w-fit ml-auto'
|
|
49
|
+
// },
|
|
50
|
+
// onclick: (dialog) => { clickSettingsButton(); }
|
|
51
|
+
// },
|
|
45
52
|
{
|
|
46
|
-
label: '
|
|
53
|
+
label: t('Cancel'),
|
|
47
54
|
options: {
|
|
48
|
-
class: 'w-
|
|
55
|
+
class: 'w-2/5 bg-white hover:!bg-gray-100 !text-gray-900 hover:!text-gray-800 dark:!bg-gray-800 dark:!text-gray-100 dark:hover:!bg-gray-700 !border-gray-200'
|
|
49
56
|
},
|
|
50
|
-
onclick: (dialog) =>
|
|
57
|
+
onclick: (dialog) => confirmDialog.tryToHideModal()
|
|
51
58
|
},
|
|
59
|
+
{
|
|
60
|
+
label: t('Start generation'),
|
|
61
|
+
options: {
|
|
62
|
+
class: 'w-3/5 px-5 py-2.5 bg-gradient-to-r from-purple-500 via-purple-600 to-purple-700 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-purple-300 dark:focus:ring-purple-800 rounded-md text-white border-none'
|
|
63
|
+
},
|
|
64
|
+
onclick: (dialog) => { runAiActions(); }
|
|
65
|
+
}
|
|
52
66
|
]"
|
|
53
67
|
:click-to-close-outside="false"
|
|
54
68
|
>
|
|
55
|
-
<div class="
|
|
56
|
-
<div v-if="records && props.checkboxes.length && popupMode === 'generation'" class="w-full overflow-x-auto">
|
|
69
|
+
<div class="bulk-vision-table flex flex-col items-center max-w-[1560px] md:max-h-[75vh] gap-3 md:gap-4 w-full h-full overflow-y-auto">
|
|
70
|
+
<div v-if="records && props.checkboxes.length && popupMode === 'generation'" class="[scrollbar-gutter:stable] w-full overflow-x-auto">
|
|
57
71
|
<VisionTable
|
|
58
72
|
:checkbox="props.checkboxes"
|
|
59
73
|
:records="records"
|
|
@@ -112,7 +126,7 @@
|
|
|
112
126
|
}}</p>
|
|
113
127
|
<div class="grid grid-cols-2 gap-4">
|
|
114
128
|
<div v-for="(prompt, promptKey) in promptsCategory" :key="promptKey">
|
|
115
|
-
{{ formatLabel(promptKey) }} prompt:
|
|
129
|
+
{{ formatLabel(promptKey) }} {{ t('prompt') }}:
|
|
116
130
|
<Textarea
|
|
117
131
|
v-model="generationPrompts[key][promptKey]"
|
|
118
132
|
class="w-full h-64 p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500"
|
|
@@ -122,10 +136,27 @@
|
|
|
122
136
|
</div>
|
|
123
137
|
</div>
|
|
124
138
|
</div>
|
|
125
|
-
<div v-else class="flex flex-col gap-2">
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
|
|
139
|
+
<div v-else class="flex flex-col gap-2 mt-2 w-full h-full">
|
|
140
|
+
<div class="flex items-center justify-between mb-2 w-full">
|
|
141
|
+
<div class="flex items-center justify-center gap-2">
|
|
142
|
+
<IconShieldSolid class="w-6 h-6 text-lightPrimary dark:text-darkPrimary" />
|
|
143
|
+
<p class="sm:text-base text-sm">{{ t('Do not overwrite existing values') }}</p>
|
|
144
|
+
<Tooltip>
|
|
145
|
+
<IconInfoCircleSolid class="w-5 h-5 me-2 text-lightPrimary dark:text-darkPrimary"/>
|
|
146
|
+
<template #tooltip>
|
|
147
|
+
<p class="max-w-64">{{ t('When enabled, the AI will skip generating content for fields that already have data. This helps to preserve existing information and avoid overwriting valuable content.') }}</p>
|
|
148
|
+
</template>
|
|
149
|
+
</Tooltip>
|
|
150
|
+
</div>
|
|
151
|
+
<Toggle
|
|
152
|
+
v-model="skipFilledFieldsForGeneration"
|
|
153
|
+
/>
|
|
154
|
+
</div>
|
|
155
|
+
<div :class="skipFilledFieldsForGeneration === false ? 'opacity-100' : 'opacity-0'" class="flex items-center text-yellow-800 bg-yellow-100 p-2 rounded-md border border-yellow-300">
|
|
156
|
+
<IconExclamationTriangle class="w-6 h-6 me-2"/>
|
|
157
|
+
<p class="sm:text-base text-sm">{{ t('Warning: Existing values will be overwritten.') }}</p>
|
|
158
|
+
</div>
|
|
159
|
+
<p class="w-fit flex justify-start text-lightPrimary dark:text-lightPrimary hover:underline cursor-pointer" @click="clickSettingsButton()">{{ t('Configure prompts') }}</p>
|
|
129
160
|
</div>
|
|
130
161
|
</div>
|
|
131
162
|
</Dialog>
|
|
@@ -134,12 +165,15 @@
|
|
|
134
165
|
<script lang="ts" setup>
|
|
135
166
|
import { callAdminForthApi } from '@/utils';
|
|
136
167
|
import { Ref, ref, watch } from 'vue'
|
|
137
|
-
import { Dialog, Button, Textarea } from '@/afcl';
|
|
168
|
+
import { Dialog, Button, Textarea, Toggle, Tooltip } from '@/afcl';
|
|
138
169
|
import VisionTable from './VisionTable.vue'
|
|
139
170
|
import adminforth from '@/adminforth';
|
|
140
171
|
import { useI18n } from 'vue-i18n';
|
|
141
172
|
import { AdminUser, type AdminForthResourceCommon } from '@/types/Common';
|
|
142
173
|
import { useCoreStore } from '@/stores/core';
|
|
174
|
+
import { IconShieldSolid, IconInfoCircleSolid } from '@iconify-prerendered/vue-flowbite';
|
|
175
|
+
import { IconExclamationTriangle } from '@iconify-prerendered/vue-humbleicons';
|
|
176
|
+
|
|
143
177
|
|
|
144
178
|
const coreStore = useCoreStore();
|
|
145
179
|
|
|
@@ -202,6 +236,7 @@ const generationPrompts = ref<any>({});
|
|
|
202
236
|
const isDataSaved = ref(false);
|
|
203
237
|
|
|
204
238
|
const regeneratingFieldsStatus = ref<Record<string, Record<string, boolean>>>({});
|
|
239
|
+
const skipFilledFieldsForGeneration = ref<boolean>(true);
|
|
205
240
|
|
|
206
241
|
const openDialog = async () => {
|
|
207
242
|
window.addEventListener('beforeunload', beforeUnloadHandler);
|
|
@@ -633,6 +668,7 @@ async function runAiAction({
|
|
|
633
668
|
actionType: actionType,
|
|
634
669
|
recordId: checkbox,
|
|
635
670
|
...(customPrompt !== undefined ? { customPrompt: JSON.stringify(customPrompt) } : {}),
|
|
671
|
+
filterFilledFields: skipFilledFieldsForGeneration.value,
|
|
636
672
|
},
|
|
637
673
|
silentError: true,
|
|
638
674
|
});
|
package/dist/index.js
CHANGED
|
@@ -59,6 +59,18 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
59
59
|
return yield this.compileTemplates(customPrompt ? JSON.parse(customPrompt) : this.options.generateImages, record, v => String(customPrompt ? v : v.prompt));
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
|
+
removeFromPromptFilledFields(compiledOutputFields, record) {
|
|
63
|
+
const newCompiledOutputFields = {};
|
|
64
|
+
for (const [key, value] of Object.entries(record)) {
|
|
65
|
+
if (compiledOutputFields[key]) {
|
|
66
|
+
if (value !== null && value !== undefined && value !== '') {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
newCompiledOutputFields[key] = compiledOutputFields[key];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return newCompiledOutputFields;
|
|
73
|
+
}
|
|
62
74
|
checkRateLimit(field, fieldNameRateLimit, headers) {
|
|
63
75
|
return __awaiter(this, void 0, void 0, function* () {
|
|
64
76
|
if (fieldNameRateLimit) {
|
|
@@ -93,8 +105,8 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
93
105
|
Do NOT wrap in \`\`\` or \`\`\`json. Do not add any extra text. Do not return prompt in response`;
|
|
94
106
|
return prompt;
|
|
95
107
|
}
|
|
96
|
-
analyze_image(
|
|
97
|
-
return __awaiter(this,
|
|
108
|
+
analyze_image(jobId_1, recordId_1, adminUser_1, headers_1, customPrompt_1) {
|
|
109
|
+
return __awaiter(this, arguments, void 0, function* (jobId, recordId, adminUser, headers, customPrompt, filterFilledFields = true) {
|
|
98
110
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
99
111
|
const selectedId = recordId;
|
|
100
112
|
let isError = false;
|
|
@@ -129,7 +141,12 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
129
141
|
}
|
|
130
142
|
//create prompt for OpenAI
|
|
131
143
|
const compiledOutputFields = yield this.compileOutputFieldsTemplates(record, customPrompt);
|
|
132
|
-
const
|
|
144
|
+
const filteredCompiledOutputFields = filterFilledFields ? this.removeFromPromptFilledFields(compiledOutputFields, record) : compiledOutputFields;
|
|
145
|
+
if (Object.keys(filteredCompiledOutputFields).length === 0) {
|
|
146
|
+
jobs.set(jobId, { status: 'completed', result: {} });
|
|
147
|
+
return { ok: true };
|
|
148
|
+
}
|
|
149
|
+
const prompt = this.getPromptForImageAnalysis(filteredCompiledOutputFields);
|
|
133
150
|
//send prompt to OpenAI and get response
|
|
134
151
|
let chatResponse;
|
|
135
152
|
try {
|
|
@@ -171,8 +188,8 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
171
188
|
}
|
|
172
189
|
});
|
|
173
190
|
}
|
|
174
|
-
analyzeNoImages(
|
|
175
|
-
return __awaiter(this,
|
|
191
|
+
analyzeNoImages(jobId_1, recordId_1, adminUser_1, headers_1, customPrompt_1) {
|
|
192
|
+
return __awaiter(this, arguments, void 0, function* (jobId, recordId, adminUser, headers, customPrompt, filterFilledFields = true) {
|
|
176
193
|
const selectedId = recordId;
|
|
177
194
|
let isError = false;
|
|
178
195
|
if (STUB_MODE) {
|
|
@@ -185,7 +202,12 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
185
202
|
const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
|
|
186
203
|
const record = yield this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ(primaryKeyColumn.name, selectedId)]);
|
|
187
204
|
const compiledOutputFields = yield this.compileOutputFieldsTemplatesNoImage(record, customPrompt);
|
|
188
|
-
const
|
|
205
|
+
const filteredCompiledOutputFields = filterFilledFields ? this.removeFromPromptFilledFields(compiledOutputFields, record) : compiledOutputFields;
|
|
206
|
+
if (Object.keys(filteredCompiledOutputFields).length === 0) {
|
|
207
|
+
jobs.set(jobId, { status: 'completed', result: {} });
|
|
208
|
+
return { ok: true };
|
|
209
|
+
}
|
|
210
|
+
const prompt = this.getPromptForPlainFields(filteredCompiledOutputFields);
|
|
189
211
|
//send prompt to OpenAI and get response
|
|
190
212
|
const numberOfTokens = this.options.fillPlainFieldsMaxTokens ? this.options.fillPlainFieldsMaxTokens : 1000;
|
|
191
213
|
let resp;
|
|
@@ -218,8 +240,8 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
218
240
|
}
|
|
219
241
|
});
|
|
220
242
|
}
|
|
221
|
-
initialImageGenerate(
|
|
222
|
-
return __awaiter(this,
|
|
243
|
+
initialImageGenerate(jobId_1, recordId_1, adminUser_1, headers_1, customPrompt_1) {
|
|
244
|
+
return __awaiter(this, arguments, void 0, function* (jobId, recordId, adminUser, headers, customPrompt, filterFilledFields = true) {
|
|
223
245
|
var _a, _b;
|
|
224
246
|
const selectedId = recordId;
|
|
225
247
|
let isError = false;
|
|
@@ -242,6 +264,9 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
242
264
|
}
|
|
243
265
|
}
|
|
244
266
|
const fieldTasks = Object.keys(((_b = this.options) === null || _b === void 0 ? void 0 : _b.generateImages) || {}).map((key) => __awaiter(this, void 0, void 0, function* () {
|
|
267
|
+
if (record[key] && filterFilledFields) {
|
|
268
|
+
return { key, images: [] };
|
|
269
|
+
}
|
|
245
270
|
const prompt = (yield this.compileGenerationFieldTemplates(record, customPrompt))[key];
|
|
246
271
|
let images;
|
|
247
272
|
if (this.options.attachFiles && attachmentFiles.length === 0) {
|
|
@@ -826,7 +851,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
826
851
|
method: 'POST',
|
|
827
852
|
path: `/plugin/${this.pluginInstanceId}/create-job`,
|
|
828
853
|
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers }) {
|
|
829
|
-
const { actionType, recordId, customPrompt } = body;
|
|
854
|
+
const { actionType, recordId, customPrompt, filterFilledFields } = body;
|
|
830
855
|
const jobId = randomUUID();
|
|
831
856
|
jobs.set(jobId, { status: "in_progress" });
|
|
832
857
|
if (!actionType) {
|
|
@@ -840,13 +865,13 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
840
865
|
else {
|
|
841
866
|
switch (actionType) {
|
|
842
867
|
case 'generate_images':
|
|
843
|
-
this.initialImageGenerate(jobId, recordId, adminUser, headers, customPrompt);
|
|
868
|
+
this.initialImageGenerate(jobId, recordId, adminUser, headers, customPrompt, filterFilledFields);
|
|
844
869
|
break;
|
|
845
870
|
case 'analyze_no_images':
|
|
846
|
-
this.analyzeNoImages(jobId, recordId, adminUser, headers, customPrompt);
|
|
871
|
+
this.analyzeNoImages(jobId, recordId, adminUser, headers, customPrompt, filterFilledFields);
|
|
847
872
|
break;
|
|
848
873
|
case 'analyze':
|
|
849
|
-
this.analyze_image(jobId, recordId, adminUser, headers, customPrompt);
|
|
874
|
+
this.analyze_image(jobId, recordId, adminUser, headers, customPrompt, filterFilledFields);
|
|
850
875
|
break;
|
|
851
876
|
case 'regenerate_images':
|
|
852
877
|
if (!body.prompt || !body.fieldName) {
|
package/index.ts
CHANGED
|
@@ -59,6 +59,19 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
59
59
|
return await this.compileTemplates(customPrompt ? JSON.parse(customPrompt) : this.options.generateImages, record, v => String(customPrompt ? v : v.prompt));
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
private removeFromPromptFilledFields(compiledOutputFields: Record<string, string>, record: Record<string, any>): Record<string, string> {
|
|
63
|
+
const newCompiledOutputFields: Record<string, string> = {};
|
|
64
|
+
for (const [key, value] of Object.entries(record)) {
|
|
65
|
+
if (compiledOutputFields[key]) {
|
|
66
|
+
if (value !== null && value !== undefined && value !== '') {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
newCompiledOutputFields[key] = compiledOutputFields[key];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return newCompiledOutputFields;
|
|
73
|
+
}
|
|
74
|
+
|
|
62
75
|
private async checkRateLimit(field: string, fieldNameRateLimit: string | undefined, headers: Record<string, string | string[] | undefined>): Promise<void | { error?: string; }> {
|
|
63
76
|
if (fieldNameRateLimit) {
|
|
64
77
|
// rate limit
|
|
@@ -94,7 +107,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
94
107
|
return prompt;
|
|
95
108
|
}
|
|
96
109
|
|
|
97
|
-
private async analyze_image(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined>, customPrompt? : string) {
|
|
110
|
+
private async analyze_image(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined>, customPrompt? : string, filterFilledFields: boolean = true) {
|
|
98
111
|
const selectedId = recordId;
|
|
99
112
|
let isError = false;
|
|
100
113
|
// Fetch the record using the provided ID
|
|
@@ -125,8 +138,15 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
125
138
|
}
|
|
126
139
|
//create prompt for OpenAI
|
|
127
140
|
const compiledOutputFields = await this.compileOutputFieldsTemplates(record, customPrompt);
|
|
128
|
-
const
|
|
141
|
+
const filteredCompiledOutputFields = filterFilledFields ? this.removeFromPromptFilledFields(compiledOutputFields, record) : compiledOutputFields;
|
|
142
|
+
|
|
143
|
+
if (Object.keys(filteredCompiledOutputFields).length === 0) {
|
|
144
|
+
jobs.set(jobId, { status: 'completed', result: {} });
|
|
145
|
+
return { ok: true };
|
|
146
|
+
}
|
|
129
147
|
|
|
148
|
+
const prompt = this.getPromptForImageAnalysis(filteredCompiledOutputFields);
|
|
149
|
+
|
|
130
150
|
//send prompt to OpenAI and get response
|
|
131
151
|
let chatResponse;
|
|
132
152
|
try {
|
|
@@ -168,7 +188,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
168
188
|
|
|
169
189
|
}
|
|
170
190
|
|
|
171
|
-
private async analyzeNoImages(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined>, customPrompt? : string) {
|
|
191
|
+
private async analyzeNoImages(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined>, customPrompt? : string, filterFilledFields: boolean = true) {
|
|
172
192
|
const selectedId = recordId;
|
|
173
193
|
let isError = false;
|
|
174
194
|
if (STUB_MODE) {
|
|
@@ -181,7 +201,14 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
181
201
|
const record = await this.adminforth.resource(this.resourceConfig.resourceId).get( [Filters.EQ(primaryKeyColumn.name, selectedId)] );
|
|
182
202
|
|
|
183
203
|
const compiledOutputFields = await this.compileOutputFieldsTemplatesNoImage(record, customPrompt);
|
|
184
|
-
|
|
204
|
+
|
|
205
|
+
const filteredCompiledOutputFields = filterFilledFields ? this.removeFromPromptFilledFields(compiledOutputFields, record) : compiledOutputFields;
|
|
206
|
+
|
|
207
|
+
if (Object.keys(filteredCompiledOutputFields).length === 0) {
|
|
208
|
+
jobs.set(jobId, { status: 'completed', result: {} });
|
|
209
|
+
return { ok: true };
|
|
210
|
+
}
|
|
211
|
+
const prompt = this.getPromptForPlainFields(filteredCompiledOutputFields);
|
|
185
212
|
//send prompt to OpenAI and get response
|
|
186
213
|
const numberOfTokens = this.options.fillPlainFieldsMaxTokens ? this.options.fillPlainFieldsMaxTokens : 1000;
|
|
187
214
|
let resp: any;
|
|
@@ -212,7 +239,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
212
239
|
}
|
|
213
240
|
}
|
|
214
241
|
|
|
215
|
-
private async initialImageGenerate(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined>, customPrompt? : string) {
|
|
242
|
+
private async initialImageGenerate(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined>, customPrompt? : string, filterFilledFields: boolean = true) {
|
|
216
243
|
const selectedId = recordId;
|
|
217
244
|
let isError = false;
|
|
218
245
|
const start = +new Date();
|
|
@@ -232,6 +259,9 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
232
259
|
}
|
|
233
260
|
}
|
|
234
261
|
const fieldTasks = Object.keys(this.options?.generateImages || {}).map(async (key) => {
|
|
262
|
+
if ( record[key] && filterFilledFields ) {
|
|
263
|
+
return { key, images: [] };
|
|
264
|
+
}
|
|
235
265
|
const prompt = (await this.compileGenerationFieldTemplates(record, customPrompt))[key];
|
|
236
266
|
let images;
|
|
237
267
|
if (this.options.attachFiles && attachmentFiles.length === 0) {
|
|
@@ -842,7 +872,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
842
872
|
method: 'POST',
|
|
843
873
|
path: `/plugin/${this.pluginInstanceId}/create-job`,
|
|
844
874
|
handler: async ({ body, adminUser, headers }) => {
|
|
845
|
-
const { actionType, recordId, customPrompt } = body;
|
|
875
|
+
const { actionType, recordId, customPrompt, filterFilledFields } = body;
|
|
846
876
|
const jobId = randomUUID();
|
|
847
877
|
jobs.set(jobId, { status: "in_progress" });
|
|
848
878
|
if (!actionType) {
|
|
@@ -855,13 +885,13 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
855
885
|
} else {
|
|
856
886
|
switch(actionType) {
|
|
857
887
|
case 'generate_images':
|
|
858
|
-
this.initialImageGenerate(jobId, recordId, adminUser, headers, customPrompt);
|
|
888
|
+
this.initialImageGenerate(jobId, recordId, adminUser, headers, customPrompt, filterFilledFields);
|
|
859
889
|
break;
|
|
860
890
|
case 'analyze_no_images':
|
|
861
|
-
this.analyzeNoImages(jobId, recordId, adminUser, headers, customPrompt);
|
|
891
|
+
this.analyzeNoImages(jobId, recordId, adminUser, headers, customPrompt, filterFilledFields);
|
|
862
892
|
break;
|
|
863
893
|
case 'analyze':
|
|
864
|
-
this.analyze_image(jobId, recordId, adminUser, headers, customPrompt);
|
|
894
|
+
this.analyze_image(jobId, recordId, adminUser, headers, customPrompt, filterFilledFields);
|
|
865
895
|
break;
|
|
866
896
|
case 'regenerate_images':
|
|
867
897
|
if (!body.prompt || !body.fieldName) {
|