@adminforth/bulk-ai-flow 1.14.6 → 1.15.1
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/ImageGenerationCarousel.vue +13 -5
- package/custom/VisionAction.vue +173 -16
- package/custom/VisionTable.vue +2 -0
- package/custom/tsconfig.json +14 -3
- package/dist/custom/ImageGenerationCarousel.vue +13 -5
- package/dist/custom/VisionAction.vue +173 -16
- package/dist/custom/VisionTable.vue +2 -0
- package/dist/custom/tsconfig.json +14 -3
- package/dist/index.js +61 -32
- package/index.ts +44 -26
- package/package.json +2 -2
- package/types.ts +12 -2
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 80,944 bytes received 172 bytes 162,232.00 bytes/sec
|
|
17
|
+
total size is 80,312 speedup is 0.99
|
|
@@ -143,7 +143,7 @@ const sliderRef = ref(null)
|
|
|
143
143
|
|
|
144
144
|
const prompt = ref('');
|
|
145
145
|
const emit = defineEmits(['close', 'selectImage', 'error', 'updateCarouselIndex']);
|
|
146
|
-
const props = defineProps(['meta', 'record', 'images', 'recordId', 'prompt', 'fieldName', 'isError', 'errorMessage', 'carouselImageIndex', 'regenerateImagesRefreshRate','sourceImage']);
|
|
146
|
+
const props = defineProps(['meta', 'record', 'images', 'recordId', 'prompt', 'fieldName', 'isError', 'errorMessage', 'carouselImageIndex', 'regenerateImagesRefreshRate','sourceImage', 'imageGenerationPrompts']);
|
|
147
147
|
const images = ref([]);
|
|
148
148
|
const loading = ref(false);
|
|
149
149
|
const attachmentFiles = ref<string[]>([])
|
|
@@ -154,7 +154,7 @@ onMounted(async () => {
|
|
|
154
154
|
}
|
|
155
155
|
const temp = await getGenerationPrompt() || '';
|
|
156
156
|
attachmentFiles.value = props.sourceImage || [];
|
|
157
|
-
prompt.value = temp[
|
|
157
|
+
prompt.value = Object.keys(JSON.parse(temp))[0];
|
|
158
158
|
await nextTick();
|
|
159
159
|
|
|
160
160
|
const currentIndex = props.carouselImageIndex || 0;
|
|
@@ -212,12 +212,20 @@ async function getHistoricalAverage() {
|
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
async function getGenerationPrompt() {
|
|
215
|
-
|
|
215
|
+
const [key, ...rest] = props.imageGenerationPrompts.split(":");
|
|
216
|
+
const value = rest.join(":").trim();
|
|
217
|
+
|
|
218
|
+
const json = {
|
|
219
|
+
[key.trim()]: value
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
try {
|
|
216
223
|
const resp = await callAdminForthApi({
|
|
217
|
-
path: `/plugin/${props.meta.pluginInstanceId}/
|
|
224
|
+
path: `/plugin/${props.meta.pluginInstanceId}/get_image_generation_prompts`,
|
|
218
225
|
method: 'POST',
|
|
219
226
|
body: {
|
|
220
227
|
recordId: props.recordId,
|
|
228
|
+
customPrompt: JSON.stringify(json) || {},
|
|
221
229
|
},
|
|
222
230
|
});
|
|
223
231
|
if(!resp) {
|
|
@@ -226,7 +234,7 @@ async function getGenerationPrompt() {
|
|
|
226
234
|
errorMessage: "Error getting generation prompts."
|
|
227
235
|
});
|
|
228
236
|
}
|
|
229
|
-
return resp?.
|
|
237
|
+
return resp?.prompt || null;
|
|
230
238
|
} catch (e) {
|
|
231
239
|
emit('error', {
|
|
232
240
|
isError: true,
|
package/custom/VisionAction.vue
CHANGED
|
@@ -8,16 +8,49 @@
|
|
|
8
8
|
<Dialog
|
|
9
9
|
ref="confirmDialog"
|
|
10
10
|
header="Bulk AI Flow"
|
|
11
|
-
class="[scrollbar-gutter:stable] !max-w-full w-
|
|
11
|
+
class="[scrollbar-gutter:stable] !max-w-full w-fit h-fit"
|
|
12
|
+
:class="popupMode === 'generation' ? 'lg:w-[1600px] !lg:max-w-[1600px]'
|
|
13
|
+
: popupMode === 'settings' ? 'lg:w-[1000px] !lg:max-w-[1000px]'
|
|
14
|
+
: 'lg:w-[500px] !lg:max-w-[500px]'"
|
|
12
15
|
:beforeCloseFunction="closeDialog"
|
|
13
|
-
:buttons="[
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
:buttons="popupMode === 'generation' ? [
|
|
17
|
+
{
|
|
18
|
+
label: checkedCount > 1 ? 'Save fields' : 'Save field',
|
|
19
|
+
options: {
|
|
20
|
+
disabled: isLoading || checkedCount < 1 || isCriticalError || isFetchingRecords || isGeneratingImages || isAnalizingFields || isAnalizingImages,
|
|
21
|
+
loader: isLoading, class: 'w-fit'
|
|
22
|
+
},
|
|
23
|
+
onclick: async (dialog) => { await saveData(); dialog.hide(); }
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
label: 'Cancel',
|
|
27
|
+
options: {
|
|
28
|
+
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'
|
|
29
|
+
},
|
|
30
|
+
onclick: (dialog) => dialog.hide()
|
|
31
|
+
},
|
|
32
|
+
] : popupMode === 'settings' ? [
|
|
33
|
+
{
|
|
34
|
+
label: 'Save settings',
|
|
35
|
+
options: {
|
|
36
|
+
class: 'w-fit'
|
|
37
|
+
},
|
|
38
|
+
onclick: (dialog) => { saveSettings(); }
|
|
39
|
+
},
|
|
40
|
+
] :
|
|
41
|
+
[
|
|
42
|
+
{
|
|
43
|
+
label: 'Edit prompts',
|
|
44
|
+
options: {
|
|
45
|
+
class: 'w-fit ml-auto'
|
|
46
|
+
},
|
|
47
|
+
onclick: (dialog) => { clickSettingsButton(); }
|
|
48
|
+
},
|
|
49
|
+
]"
|
|
17
50
|
:click-to-close-outside="false"
|
|
18
51
|
>
|
|
19
|
-
<div class="[scrollbar-gutter:stable] bulk-vision-table flex flex-col items-center max-w-[1560px] md:max-h-[
|
|
20
|
-
<div v-if="records && props.checkboxes.length" class="w-full overflow-x-auto">
|
|
52
|
+
<div class="[scrollbar-gutter:stable] 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">
|
|
53
|
+
<div v-if="records && props.checkboxes.length && popupMode === 'generation'" class="w-full overflow-x-auto">
|
|
21
54
|
<VisionTable
|
|
22
55
|
:checkbox="props.checkboxes"
|
|
23
56
|
:records="records"
|
|
@@ -44,10 +77,40 @@
|
|
|
44
77
|
:imageGenerationErrorMessage="imageGenerationErrorMessage"
|
|
45
78
|
@regenerate-images="regenerateImages"
|
|
46
79
|
:isImageHasPreviewUrl="isImageHasPreviewUrl"
|
|
80
|
+
:imageGenerationPrompts="generationPrompts.generateImages"
|
|
47
81
|
/>
|
|
82
|
+
<div class="text-red-600 flex items-center w-full">
|
|
83
|
+
<p v-if="isError === true">{{ errorMessage }}</p>
|
|
84
|
+
</div>
|
|
48
85
|
</div>
|
|
49
|
-
<div
|
|
50
|
-
|
|
86
|
+
<div
|
|
87
|
+
v-else-if="popupMode === 'settings'"
|
|
88
|
+
v-for="(promptsCategory, key) in generationPrompts"
|
|
89
|
+
:key="key"
|
|
90
|
+
class="w-full"
|
|
91
|
+
>
|
|
92
|
+
<div v-if="Object.keys(promptsCategory).length > 0" class="gap-4 mb-6 ml-1">
|
|
93
|
+
<p class="text-start w-full text-xl font-bold mb-2">{{
|
|
94
|
+
key === "plainFieldsPrompts" ? "Prompts for non-image fields"
|
|
95
|
+
: key === "generateImages" ? "Prompts for image fields"
|
|
96
|
+
: "Prompts for image analysis"
|
|
97
|
+
}}</p>
|
|
98
|
+
<div class="grid grid-cols-2 gap-4">
|
|
99
|
+
<div v-for="(prompt, promptKey) in promptsCategory" :key="promptKey">
|
|
100
|
+
{{ formatLabel(promptKey) }} prompt:
|
|
101
|
+
<Textarea
|
|
102
|
+
v-model="generationPrompts[key][promptKey]"
|
|
103
|
+
class="w-full h-32 p-2 border border-gray-300 rounded-md resize-none focus:outline-none focus:ring-2 focus:ring-purple-500"
|
|
104
|
+
></Textarea>
|
|
105
|
+
<p class="text-red-500 hover:underline hover:cursor-pointer mt-2" @click="resetPromptToDefault(key, promptKey)">reset to default</p>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
<div v-else class="flex flex-col gap-2">
|
|
111
|
+
<Button @click="runAiActions" class="px-5 py-2.5 my-20 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">
|
|
112
|
+
Start generation
|
|
113
|
+
</Button>
|
|
51
114
|
</div>
|
|
52
115
|
</div>
|
|
53
116
|
</Dialog>
|
|
@@ -56,11 +119,11 @@
|
|
|
56
119
|
<script lang="ts" setup>
|
|
57
120
|
import { callAdminForthApi } from '@/utils';
|
|
58
121
|
import { Ref, ref, watch } from 'vue'
|
|
59
|
-
import { Dialog, Button } from '@/afcl';
|
|
122
|
+
import { Dialog, Button, Textarea } from '@/afcl';
|
|
60
123
|
import VisionTable from './VisionTable.vue'
|
|
61
124
|
import adminforth from '@/adminforth';
|
|
62
125
|
import { useI18n } from 'vue-i18n';
|
|
63
|
-
import { AdminUser, type AdminForthResourceCommon } from '@/types';
|
|
126
|
+
import { AdminUser, type AdminForthResourceCommon } from '@/types/Common';
|
|
64
127
|
import { run } from 'node:test';
|
|
65
128
|
|
|
66
129
|
const { t } = useI18n();
|
|
@@ -114,6 +177,8 @@ const aiGenerationErrorMessage = ref<string[]>([]);
|
|
|
114
177
|
const isAiImageGenerationError = ref<boolean[]>([false]);
|
|
115
178
|
const imageGenerationErrorMessage = ref<string[]>([]);
|
|
116
179
|
const isImageHasPreviewUrl = ref<Record<string, boolean>>({});
|
|
180
|
+
const popupMode = ref<'generation' | 'confirmation' | 'settings'>('confirmation');
|
|
181
|
+
const generationPrompts = ref<any>({});
|
|
117
182
|
|
|
118
183
|
const openDialog = async () => {
|
|
119
184
|
isDialogOpen.value = true;
|
|
@@ -144,8 +209,19 @@ const openDialog = async () => {
|
|
|
144
209
|
},{[primaryKey]: records.value[i][primaryKey]} as Record<string, boolean>);
|
|
145
210
|
}
|
|
146
211
|
isFetchingRecords.value = false;
|
|
147
|
-
|
|
148
|
-
if (
|
|
212
|
+
// Ensure prompts are loaded before any automatic AI action run
|
|
213
|
+
if (!generationPrompts.value || Object.keys(generationPrompts.value).length === 0) {
|
|
214
|
+
await getGenerationPrompts();
|
|
215
|
+
}
|
|
216
|
+
if (!props.meta.askConfirmationBeforeGenerating) {
|
|
217
|
+
runAiActions();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
function runAiActions() {
|
|
223
|
+
popupMode.value = 'generation';
|
|
224
|
+
if (props.meta.isImageGeneration) {
|
|
149
225
|
isGeneratingImages.value = true;
|
|
150
226
|
runAiAction({
|
|
151
227
|
endpoint: 'initial_image_generate',
|
|
@@ -170,9 +246,8 @@ const openDialog = async () => {
|
|
|
170
246
|
});
|
|
171
247
|
}
|
|
172
248
|
}
|
|
173
|
-
|
|
249
|
+
|
|
174
250
|
const closeDialog = () => {
|
|
175
|
-
confirmDialog.value.close();
|
|
176
251
|
isAiResponseReceivedAnalize.value = [];
|
|
177
252
|
isAiResponseReceivedImage.value = [];
|
|
178
253
|
|
|
@@ -186,10 +261,10 @@ const closeDialog = () => {
|
|
|
186
261
|
isImageGenerationError.value = false;
|
|
187
262
|
errorMessage.value = '';
|
|
188
263
|
isDialogOpen.value = false;
|
|
264
|
+
popupMode.value = 'confirmation';
|
|
189
265
|
}
|
|
190
266
|
|
|
191
267
|
watch(selected, (val) => {
|
|
192
|
-
//console.log('Selected changed:', val);
|
|
193
268
|
checkedCount.value = val.filter(item => item.isChecked === true).length;
|
|
194
269
|
}, { deep: true });
|
|
195
270
|
|
|
@@ -492,6 +567,15 @@ async function runAiAction({
|
|
|
492
567
|
return;
|
|
493
568
|
};
|
|
494
569
|
}
|
|
570
|
+
|
|
571
|
+
let customPrompt;
|
|
572
|
+
if (actionType === 'generate_images') {
|
|
573
|
+
customPrompt = generationPrompts.value.imageGenerationPrompts || generationPrompts.value.generateImages;
|
|
574
|
+
} else if (actionType === 'analyze') {
|
|
575
|
+
customPrompt = generationPrompts.value.imageFieldsPrompts;
|
|
576
|
+
} else if (actionType === 'analyze_no_images') {
|
|
577
|
+
customPrompt = generationPrompts.value.plainFieldsPrompts;
|
|
578
|
+
}
|
|
495
579
|
//creating jobs
|
|
496
580
|
const tasks = recordsIds.map(async (checkbox, i) => {
|
|
497
581
|
try {
|
|
@@ -501,6 +585,7 @@ async function runAiAction({
|
|
|
501
585
|
body: {
|
|
502
586
|
actionType: actionType,
|
|
503
587
|
recordId: checkbox,
|
|
588
|
+
...(customPrompt !== undefined ? { customPrompt: JSON.stringify(customPrompt) } : {}),
|
|
504
589
|
},
|
|
505
590
|
});
|
|
506
591
|
|
|
@@ -753,4 +838,76 @@ function click() {
|
|
|
753
838
|
openDialog();
|
|
754
839
|
}
|
|
755
840
|
|
|
841
|
+
function saveSettings() {
|
|
842
|
+
popupMode.value = 'confirmation';
|
|
843
|
+
localStorage.setItem(`bulkAiFlowGenerationPrompts_${props.meta.pluginInstanceId}`, JSON.stringify(generationPrompts.value));
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
async function getGenerationPrompts() {
|
|
847
|
+
const calculatedGenerationPrompts: any = {};
|
|
848
|
+
const savedPrompts = localStorage.getItem(`bulkAiFlowGenerationPrompts_${props.meta.pluginInstanceId}`);
|
|
849
|
+
if (props.meta.generationPrompts.plainFieldsPrompts) {
|
|
850
|
+
calculatedGenerationPrompts.plainFieldsPrompts = props.meta.generationPrompts.plainFieldsPrompts;
|
|
851
|
+
}
|
|
852
|
+
if (props.meta.generationPrompts.imageFieldsPrompts) {
|
|
853
|
+
calculatedGenerationPrompts.imageFieldsPrompts = props.meta.generationPrompts.imageFieldsPrompts;
|
|
854
|
+
}
|
|
855
|
+
if (props.meta.generationPrompts.imageGenerationPrompts) {
|
|
856
|
+
let imageFields = {};
|
|
857
|
+
for (const [key, value] of Object.entries(props.meta.generationPrompts.imageGenerationPrompts)) {
|
|
858
|
+
// value might be typed as unknown; cast to any to access prompt safely
|
|
859
|
+
imageFields[key] = (value as any).prompt;
|
|
860
|
+
}
|
|
861
|
+
calculatedGenerationPrompts.generateImages = imageFields;
|
|
862
|
+
}
|
|
863
|
+
if (savedPrompts && props.meta.askConfirmationBeforeGenerating) {
|
|
864
|
+
|
|
865
|
+
generationPrompts.value = checkAndAddNewFieldsToPrompts(JSON.parse(savedPrompts), calculatedGenerationPrompts);
|
|
866
|
+
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
generationPrompts.value = calculatedGenerationPrompts;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
function resetPromptToDefault(categoryKey, promptKey) {
|
|
873
|
+
if (categoryKey === 'generateImages') {
|
|
874
|
+
generationPrompts.value[categoryKey][promptKey] = props.meta.generationPrompts.imageGenerationPrompts[promptKey].prompt;
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
generationPrompts.value[categoryKey][promptKey] = props.meta.generationPrompts[categoryKey][promptKey];
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
function clickSettingsButton() {
|
|
881
|
+
getGenerationPrompts();
|
|
882
|
+
popupMode.value = 'settings';
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
function checkAndAddNewFieldsToPrompts(savedPrompts, defaultPrompts) {
|
|
887
|
+
for (const categoryKey in defaultPrompts) {
|
|
888
|
+
if (!savedPrompts.hasOwnProperty(categoryKey)) {
|
|
889
|
+
savedPrompts[categoryKey] = defaultPrompts[categoryKey];
|
|
890
|
+
} else {
|
|
891
|
+
for (const promptKey in defaultPrompts[categoryKey]) {
|
|
892
|
+
if (!savedPrompts[categoryKey].hasOwnProperty(promptKey)) {
|
|
893
|
+
savedPrompts[categoryKey][promptKey] = defaultPrompts[categoryKey][promptKey];
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
//remove deprecated fields
|
|
899
|
+
for (const categoryKey in savedPrompts) {
|
|
900
|
+
if (!defaultPrompts.hasOwnProperty(categoryKey)) {
|
|
901
|
+
delete savedPrompts[categoryKey];
|
|
902
|
+
} else {
|
|
903
|
+
for (const promptKey in savedPrompts[categoryKey]) {
|
|
904
|
+
if (!defaultPrompts[categoryKey].hasOwnProperty(promptKey)) {
|
|
905
|
+
delete savedPrompts[categoryKey][promptKey];
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return savedPrompts;
|
|
911
|
+
}
|
|
912
|
+
|
|
756
913
|
</script>
|
package/custom/VisionTable.vue
CHANGED
|
@@ -173,6 +173,7 @@
|
|
|
173
173
|
:carouselImageIndex="carouselImageIndex[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
|
|
174
174
|
:regenerateImagesRefreshRate="regenerateImagesRefreshRate"
|
|
175
175
|
:sourceImage="item.images && item.images.length ? item.images : null"
|
|
176
|
+
:imageGenerationPrompts="imageGenerationPrompts[n]"
|
|
176
177
|
@error="handleError"
|
|
177
178
|
@close="openGenerationCarousel[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n] = false"
|
|
178
179
|
@selectImage="updateSelectedImage"
|
|
@@ -231,6 +232,7 @@ const props = defineProps<{
|
|
|
231
232
|
imageGenerationErrorMessage: string[],
|
|
232
233
|
oldData: any[],
|
|
233
234
|
isImageHasPreviewUrl: Record<string, boolean>
|
|
235
|
+
imageGenerationPrompts: Record<string, any>
|
|
234
236
|
}>();
|
|
235
237
|
const emit = defineEmits(['error', 'regenerateImages']);
|
|
236
238
|
|
package/custom/tsconfig.json
CHANGED
|
@@ -4,16 +4,27 @@
|
|
|
4
4
|
"paths": {
|
|
5
5
|
"@/*": [
|
|
6
6
|
// "node_modules/adminforth/dist/spa/src/*"
|
|
7
|
-
"../../../spa/src/*"
|
|
7
|
+
"../../../adminforth/spa/src/*"
|
|
8
8
|
],
|
|
9
9
|
"*": [
|
|
10
10
|
// "node_modules/adminforth/dist/spa/node_modules/*"
|
|
11
|
-
"../../../spa/node_modules/*"
|
|
11
|
+
"../../../adminforth/spa/node_modules/*"
|
|
12
12
|
],
|
|
13
13
|
"@@/*": [
|
|
14
14
|
// "node_modules/adminforth/dist/spa/src/*"
|
|
15
15
|
"."
|
|
16
16
|
]
|
|
17
17
|
}
|
|
18
|
-
}
|
|
18
|
+
},
|
|
19
|
+
"include": [
|
|
20
|
+
"./**/*.ts",
|
|
21
|
+
"./**/*.tsx",
|
|
22
|
+
"./**/*.vue",
|
|
23
|
+
"../**/*.ts",
|
|
24
|
+
"../**/*.tsx",
|
|
25
|
+
"../**/*.vue",
|
|
26
|
+
"../*.vue",
|
|
27
|
+
"../*.ts",
|
|
28
|
+
"../*.tsx"
|
|
29
|
+
]
|
|
19
30
|
}
|
|
@@ -143,7 +143,7 @@ const sliderRef = ref(null)
|
|
|
143
143
|
|
|
144
144
|
const prompt = ref('');
|
|
145
145
|
const emit = defineEmits(['close', 'selectImage', 'error', 'updateCarouselIndex']);
|
|
146
|
-
const props = defineProps(['meta', 'record', 'images', 'recordId', 'prompt', 'fieldName', 'isError', 'errorMessage', 'carouselImageIndex', 'regenerateImagesRefreshRate','sourceImage']);
|
|
146
|
+
const props = defineProps(['meta', 'record', 'images', 'recordId', 'prompt', 'fieldName', 'isError', 'errorMessage', 'carouselImageIndex', 'regenerateImagesRefreshRate','sourceImage', 'imageGenerationPrompts']);
|
|
147
147
|
const images = ref([]);
|
|
148
148
|
const loading = ref(false);
|
|
149
149
|
const attachmentFiles = ref<string[]>([])
|
|
@@ -154,7 +154,7 @@ onMounted(async () => {
|
|
|
154
154
|
}
|
|
155
155
|
const temp = await getGenerationPrompt() || '';
|
|
156
156
|
attachmentFiles.value = props.sourceImage || [];
|
|
157
|
-
prompt.value = temp[
|
|
157
|
+
prompt.value = Object.keys(JSON.parse(temp))[0];
|
|
158
158
|
await nextTick();
|
|
159
159
|
|
|
160
160
|
const currentIndex = props.carouselImageIndex || 0;
|
|
@@ -212,12 +212,20 @@ async function getHistoricalAverage() {
|
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
async function getGenerationPrompt() {
|
|
215
|
-
|
|
215
|
+
const [key, ...rest] = props.imageGenerationPrompts.split(":");
|
|
216
|
+
const value = rest.join(":").trim();
|
|
217
|
+
|
|
218
|
+
const json = {
|
|
219
|
+
[key.trim()]: value
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
try {
|
|
216
223
|
const resp = await callAdminForthApi({
|
|
217
|
-
path: `/plugin/${props.meta.pluginInstanceId}/
|
|
224
|
+
path: `/plugin/${props.meta.pluginInstanceId}/get_image_generation_prompts`,
|
|
218
225
|
method: 'POST',
|
|
219
226
|
body: {
|
|
220
227
|
recordId: props.recordId,
|
|
228
|
+
customPrompt: JSON.stringify(json) || {},
|
|
221
229
|
},
|
|
222
230
|
});
|
|
223
231
|
if(!resp) {
|
|
@@ -226,7 +234,7 @@ async function getGenerationPrompt() {
|
|
|
226
234
|
errorMessage: "Error getting generation prompts."
|
|
227
235
|
});
|
|
228
236
|
}
|
|
229
|
-
return resp?.
|
|
237
|
+
return resp?.prompt || null;
|
|
230
238
|
} catch (e) {
|
|
231
239
|
emit('error', {
|
|
232
240
|
isError: true,
|
|
@@ -8,16 +8,49 @@
|
|
|
8
8
|
<Dialog
|
|
9
9
|
ref="confirmDialog"
|
|
10
10
|
header="Bulk AI Flow"
|
|
11
|
-
class="[scrollbar-gutter:stable] !max-w-full w-
|
|
11
|
+
class="[scrollbar-gutter:stable] !max-w-full w-fit h-fit"
|
|
12
|
+
:class="popupMode === 'generation' ? 'lg:w-[1600px] !lg:max-w-[1600px]'
|
|
13
|
+
: popupMode === 'settings' ? 'lg:w-[1000px] !lg:max-w-[1000px]'
|
|
14
|
+
: 'lg:w-[500px] !lg:max-w-[500px]'"
|
|
12
15
|
:beforeCloseFunction="closeDialog"
|
|
13
|
-
:buttons="[
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
:buttons="popupMode === 'generation' ? [
|
|
17
|
+
{
|
|
18
|
+
label: checkedCount > 1 ? 'Save fields' : 'Save field',
|
|
19
|
+
options: {
|
|
20
|
+
disabled: isLoading || checkedCount < 1 || isCriticalError || isFetchingRecords || isGeneratingImages || isAnalizingFields || isAnalizingImages,
|
|
21
|
+
loader: isLoading, class: 'w-fit'
|
|
22
|
+
},
|
|
23
|
+
onclick: async (dialog) => { await saveData(); dialog.hide(); }
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
label: 'Cancel',
|
|
27
|
+
options: {
|
|
28
|
+
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'
|
|
29
|
+
},
|
|
30
|
+
onclick: (dialog) => dialog.hide()
|
|
31
|
+
},
|
|
32
|
+
] : popupMode === 'settings' ? [
|
|
33
|
+
{
|
|
34
|
+
label: 'Save settings',
|
|
35
|
+
options: {
|
|
36
|
+
class: 'w-fit'
|
|
37
|
+
},
|
|
38
|
+
onclick: (dialog) => { saveSettings(); }
|
|
39
|
+
},
|
|
40
|
+
] :
|
|
41
|
+
[
|
|
42
|
+
{
|
|
43
|
+
label: 'Edit prompts',
|
|
44
|
+
options: {
|
|
45
|
+
class: 'w-fit ml-auto'
|
|
46
|
+
},
|
|
47
|
+
onclick: (dialog) => { clickSettingsButton(); }
|
|
48
|
+
},
|
|
49
|
+
]"
|
|
17
50
|
:click-to-close-outside="false"
|
|
18
51
|
>
|
|
19
|
-
<div class="[scrollbar-gutter:stable] bulk-vision-table flex flex-col items-center max-w-[1560px] md:max-h-[
|
|
20
|
-
<div v-if="records && props.checkboxes.length" class="w-full overflow-x-auto">
|
|
52
|
+
<div class="[scrollbar-gutter:stable] 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">
|
|
53
|
+
<div v-if="records && props.checkboxes.length && popupMode === 'generation'" class="w-full overflow-x-auto">
|
|
21
54
|
<VisionTable
|
|
22
55
|
:checkbox="props.checkboxes"
|
|
23
56
|
:records="records"
|
|
@@ -44,10 +77,40 @@
|
|
|
44
77
|
:imageGenerationErrorMessage="imageGenerationErrorMessage"
|
|
45
78
|
@regenerate-images="regenerateImages"
|
|
46
79
|
:isImageHasPreviewUrl="isImageHasPreviewUrl"
|
|
80
|
+
:imageGenerationPrompts="generationPrompts.generateImages"
|
|
47
81
|
/>
|
|
82
|
+
<div class="text-red-600 flex items-center w-full">
|
|
83
|
+
<p v-if="isError === true">{{ errorMessage }}</p>
|
|
84
|
+
</div>
|
|
48
85
|
</div>
|
|
49
|
-
<div
|
|
50
|
-
|
|
86
|
+
<div
|
|
87
|
+
v-else-if="popupMode === 'settings'"
|
|
88
|
+
v-for="(promptsCategory, key) in generationPrompts"
|
|
89
|
+
:key="key"
|
|
90
|
+
class="w-full"
|
|
91
|
+
>
|
|
92
|
+
<div v-if="Object.keys(promptsCategory).length > 0" class="gap-4 mb-6 ml-1">
|
|
93
|
+
<p class="text-start w-full text-xl font-bold mb-2">{{
|
|
94
|
+
key === "plainFieldsPrompts" ? "Prompts for non-image fields"
|
|
95
|
+
: key === "generateImages" ? "Prompts for image fields"
|
|
96
|
+
: "Prompts for image analysis"
|
|
97
|
+
}}</p>
|
|
98
|
+
<div class="grid grid-cols-2 gap-4">
|
|
99
|
+
<div v-for="(prompt, promptKey) in promptsCategory" :key="promptKey">
|
|
100
|
+
{{ formatLabel(promptKey) }} prompt:
|
|
101
|
+
<Textarea
|
|
102
|
+
v-model="generationPrompts[key][promptKey]"
|
|
103
|
+
class="w-full h-32 p-2 border border-gray-300 rounded-md resize-none focus:outline-none focus:ring-2 focus:ring-purple-500"
|
|
104
|
+
></Textarea>
|
|
105
|
+
<p class="text-red-500 hover:underline hover:cursor-pointer mt-2" @click="resetPromptToDefault(key, promptKey)">reset to default</p>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
<div v-else class="flex flex-col gap-2">
|
|
111
|
+
<Button @click="runAiActions" class="px-5 py-2.5 my-20 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">
|
|
112
|
+
Start generation
|
|
113
|
+
</Button>
|
|
51
114
|
</div>
|
|
52
115
|
</div>
|
|
53
116
|
</Dialog>
|
|
@@ -56,11 +119,11 @@
|
|
|
56
119
|
<script lang="ts" setup>
|
|
57
120
|
import { callAdminForthApi } from '@/utils';
|
|
58
121
|
import { Ref, ref, watch } from 'vue'
|
|
59
|
-
import { Dialog, Button } from '@/afcl';
|
|
122
|
+
import { Dialog, Button, Textarea } from '@/afcl';
|
|
60
123
|
import VisionTable from './VisionTable.vue'
|
|
61
124
|
import adminforth from '@/adminforth';
|
|
62
125
|
import { useI18n } from 'vue-i18n';
|
|
63
|
-
import { AdminUser, type AdminForthResourceCommon } from '@/types';
|
|
126
|
+
import { AdminUser, type AdminForthResourceCommon } from '@/types/Common';
|
|
64
127
|
import { run } from 'node:test';
|
|
65
128
|
|
|
66
129
|
const { t } = useI18n();
|
|
@@ -114,6 +177,8 @@ const aiGenerationErrorMessage = ref<string[]>([]);
|
|
|
114
177
|
const isAiImageGenerationError = ref<boolean[]>([false]);
|
|
115
178
|
const imageGenerationErrorMessage = ref<string[]>([]);
|
|
116
179
|
const isImageHasPreviewUrl = ref<Record<string, boolean>>({});
|
|
180
|
+
const popupMode = ref<'generation' | 'confirmation' | 'settings'>('confirmation');
|
|
181
|
+
const generationPrompts = ref<any>({});
|
|
117
182
|
|
|
118
183
|
const openDialog = async () => {
|
|
119
184
|
isDialogOpen.value = true;
|
|
@@ -144,8 +209,19 @@ const openDialog = async () => {
|
|
|
144
209
|
},{[primaryKey]: records.value[i][primaryKey]} as Record<string, boolean>);
|
|
145
210
|
}
|
|
146
211
|
isFetchingRecords.value = false;
|
|
147
|
-
|
|
148
|
-
if (
|
|
212
|
+
// Ensure prompts are loaded before any automatic AI action run
|
|
213
|
+
if (!generationPrompts.value || Object.keys(generationPrompts.value).length === 0) {
|
|
214
|
+
await getGenerationPrompts();
|
|
215
|
+
}
|
|
216
|
+
if (!props.meta.askConfirmationBeforeGenerating) {
|
|
217
|
+
runAiActions();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
function runAiActions() {
|
|
223
|
+
popupMode.value = 'generation';
|
|
224
|
+
if (props.meta.isImageGeneration) {
|
|
149
225
|
isGeneratingImages.value = true;
|
|
150
226
|
runAiAction({
|
|
151
227
|
endpoint: 'initial_image_generate',
|
|
@@ -170,9 +246,8 @@ const openDialog = async () => {
|
|
|
170
246
|
});
|
|
171
247
|
}
|
|
172
248
|
}
|
|
173
|
-
|
|
249
|
+
|
|
174
250
|
const closeDialog = () => {
|
|
175
|
-
confirmDialog.value.close();
|
|
176
251
|
isAiResponseReceivedAnalize.value = [];
|
|
177
252
|
isAiResponseReceivedImage.value = [];
|
|
178
253
|
|
|
@@ -186,10 +261,10 @@ const closeDialog = () => {
|
|
|
186
261
|
isImageGenerationError.value = false;
|
|
187
262
|
errorMessage.value = '';
|
|
188
263
|
isDialogOpen.value = false;
|
|
264
|
+
popupMode.value = 'confirmation';
|
|
189
265
|
}
|
|
190
266
|
|
|
191
267
|
watch(selected, (val) => {
|
|
192
|
-
//console.log('Selected changed:', val);
|
|
193
268
|
checkedCount.value = val.filter(item => item.isChecked === true).length;
|
|
194
269
|
}, { deep: true });
|
|
195
270
|
|
|
@@ -492,6 +567,15 @@ async function runAiAction({
|
|
|
492
567
|
return;
|
|
493
568
|
};
|
|
494
569
|
}
|
|
570
|
+
|
|
571
|
+
let customPrompt;
|
|
572
|
+
if (actionType === 'generate_images') {
|
|
573
|
+
customPrompt = generationPrompts.value.imageGenerationPrompts || generationPrompts.value.generateImages;
|
|
574
|
+
} else if (actionType === 'analyze') {
|
|
575
|
+
customPrompt = generationPrompts.value.imageFieldsPrompts;
|
|
576
|
+
} else if (actionType === 'analyze_no_images') {
|
|
577
|
+
customPrompt = generationPrompts.value.plainFieldsPrompts;
|
|
578
|
+
}
|
|
495
579
|
//creating jobs
|
|
496
580
|
const tasks = recordsIds.map(async (checkbox, i) => {
|
|
497
581
|
try {
|
|
@@ -501,6 +585,7 @@ async function runAiAction({
|
|
|
501
585
|
body: {
|
|
502
586
|
actionType: actionType,
|
|
503
587
|
recordId: checkbox,
|
|
588
|
+
...(customPrompt !== undefined ? { customPrompt: JSON.stringify(customPrompt) } : {}),
|
|
504
589
|
},
|
|
505
590
|
});
|
|
506
591
|
|
|
@@ -753,4 +838,76 @@ function click() {
|
|
|
753
838
|
openDialog();
|
|
754
839
|
}
|
|
755
840
|
|
|
841
|
+
function saveSettings() {
|
|
842
|
+
popupMode.value = 'confirmation';
|
|
843
|
+
localStorage.setItem(`bulkAiFlowGenerationPrompts_${props.meta.pluginInstanceId}`, JSON.stringify(generationPrompts.value));
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
async function getGenerationPrompts() {
|
|
847
|
+
const calculatedGenerationPrompts: any = {};
|
|
848
|
+
const savedPrompts = localStorage.getItem(`bulkAiFlowGenerationPrompts_${props.meta.pluginInstanceId}`);
|
|
849
|
+
if (props.meta.generationPrompts.plainFieldsPrompts) {
|
|
850
|
+
calculatedGenerationPrompts.plainFieldsPrompts = props.meta.generationPrompts.plainFieldsPrompts;
|
|
851
|
+
}
|
|
852
|
+
if (props.meta.generationPrompts.imageFieldsPrompts) {
|
|
853
|
+
calculatedGenerationPrompts.imageFieldsPrompts = props.meta.generationPrompts.imageFieldsPrompts;
|
|
854
|
+
}
|
|
855
|
+
if (props.meta.generationPrompts.imageGenerationPrompts) {
|
|
856
|
+
let imageFields = {};
|
|
857
|
+
for (const [key, value] of Object.entries(props.meta.generationPrompts.imageGenerationPrompts)) {
|
|
858
|
+
// value might be typed as unknown; cast to any to access prompt safely
|
|
859
|
+
imageFields[key] = (value as any).prompt;
|
|
860
|
+
}
|
|
861
|
+
calculatedGenerationPrompts.generateImages = imageFields;
|
|
862
|
+
}
|
|
863
|
+
if (savedPrompts && props.meta.askConfirmationBeforeGenerating) {
|
|
864
|
+
|
|
865
|
+
generationPrompts.value = checkAndAddNewFieldsToPrompts(JSON.parse(savedPrompts), calculatedGenerationPrompts);
|
|
866
|
+
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
generationPrompts.value = calculatedGenerationPrompts;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
function resetPromptToDefault(categoryKey, promptKey) {
|
|
873
|
+
if (categoryKey === 'generateImages') {
|
|
874
|
+
generationPrompts.value[categoryKey][promptKey] = props.meta.generationPrompts.imageGenerationPrompts[promptKey].prompt;
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
generationPrompts.value[categoryKey][promptKey] = props.meta.generationPrompts[categoryKey][promptKey];
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
function clickSettingsButton() {
|
|
881
|
+
getGenerationPrompts();
|
|
882
|
+
popupMode.value = 'settings';
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
function checkAndAddNewFieldsToPrompts(savedPrompts, defaultPrompts) {
|
|
887
|
+
for (const categoryKey in defaultPrompts) {
|
|
888
|
+
if (!savedPrompts.hasOwnProperty(categoryKey)) {
|
|
889
|
+
savedPrompts[categoryKey] = defaultPrompts[categoryKey];
|
|
890
|
+
} else {
|
|
891
|
+
for (const promptKey in defaultPrompts[categoryKey]) {
|
|
892
|
+
if (!savedPrompts[categoryKey].hasOwnProperty(promptKey)) {
|
|
893
|
+
savedPrompts[categoryKey][promptKey] = defaultPrompts[categoryKey][promptKey];
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
//remove deprecated fields
|
|
899
|
+
for (const categoryKey in savedPrompts) {
|
|
900
|
+
if (!defaultPrompts.hasOwnProperty(categoryKey)) {
|
|
901
|
+
delete savedPrompts[categoryKey];
|
|
902
|
+
} else {
|
|
903
|
+
for (const promptKey in savedPrompts[categoryKey]) {
|
|
904
|
+
if (!defaultPrompts[categoryKey].hasOwnProperty(promptKey)) {
|
|
905
|
+
delete savedPrompts[categoryKey][promptKey];
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return savedPrompts;
|
|
911
|
+
}
|
|
912
|
+
|
|
756
913
|
</script>
|
|
@@ -173,6 +173,7 @@
|
|
|
173
173
|
:carouselImageIndex="carouselImageIndex[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
|
|
174
174
|
:regenerateImagesRefreshRate="regenerateImagesRefreshRate"
|
|
175
175
|
:sourceImage="item.images && item.images.length ? item.images : null"
|
|
176
|
+
:imageGenerationPrompts="imageGenerationPrompts[n]"
|
|
176
177
|
@error="handleError"
|
|
177
178
|
@close="openGenerationCarousel[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n] = false"
|
|
178
179
|
@selectImage="updateSelectedImage"
|
|
@@ -231,6 +232,7 @@ const props = defineProps<{
|
|
|
231
232
|
imageGenerationErrorMessage: string[],
|
|
232
233
|
oldData: any[],
|
|
233
234
|
isImageHasPreviewUrl: Record<string, boolean>
|
|
235
|
+
imageGenerationPrompts: Record<string, any>
|
|
234
236
|
}>();
|
|
235
237
|
const emit = defineEmits(['error', 'regenerateImages']);
|
|
236
238
|
|
|
@@ -4,16 +4,27 @@
|
|
|
4
4
|
"paths": {
|
|
5
5
|
"@/*": [
|
|
6
6
|
// "node_modules/adminforth/dist/spa/src/*"
|
|
7
|
-
"../../../spa/src/*"
|
|
7
|
+
"../../../adminforth/spa/src/*"
|
|
8
8
|
],
|
|
9
9
|
"*": [
|
|
10
10
|
// "node_modules/adminforth/dist/spa/node_modules/*"
|
|
11
|
-
"../../../spa/node_modules/*"
|
|
11
|
+
"../../../adminforth/spa/node_modules/*"
|
|
12
12
|
],
|
|
13
13
|
"@@/*": [
|
|
14
14
|
// "node_modules/adminforth/dist/spa/src/*"
|
|
15
15
|
"."
|
|
16
16
|
]
|
|
17
17
|
}
|
|
18
|
-
}
|
|
18
|
+
},
|
|
19
|
+
"include": [
|
|
20
|
+
"./**/*.ts",
|
|
21
|
+
"./**/*.tsx",
|
|
22
|
+
"./**/*.vue",
|
|
23
|
+
"../**/*.ts",
|
|
24
|
+
"../**/*.tsx",
|
|
25
|
+
"../**/*.vue",
|
|
26
|
+
"../*.vue",
|
|
27
|
+
"../*.ts",
|
|
28
|
+
"../*.tsx"
|
|
29
|
+
]
|
|
19
30
|
}
|
package/dist/index.js
CHANGED
|
@@ -25,27 +25,39 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
25
25
|
}
|
|
26
26
|
// Compile Handlebars templates in outputFields using record fields as context
|
|
27
27
|
compileTemplates(source, record, valueSelector) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const tpl = Handlebars.compile(templateStr);
|
|
33
|
-
compiled[key] = tpl(record);
|
|
28
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
29
|
+
if (this.options.provideAdditionalContextForRecord) {
|
|
30
|
+
const additionalFields = yield this.options.provideAdditionalContextForRecord({ record, adminUser: null, resource: this.resourceConfig });
|
|
31
|
+
record = Object.assign(Object.assign({}, record), additionalFields);
|
|
34
32
|
}
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
const compiled = {};
|
|
34
|
+
for (const [key, value] of Object.entries(source)) {
|
|
35
|
+
const templateStr = valueSelector(value);
|
|
36
|
+
try {
|
|
37
|
+
const tpl = Handlebars.compile(templateStr);
|
|
38
|
+
compiled[key] = tpl(record);
|
|
39
|
+
}
|
|
40
|
+
catch (_a) {
|
|
41
|
+
compiled[key] = templateStr;
|
|
42
|
+
}
|
|
37
43
|
}
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
return compiled;
|
|
45
|
+
});
|
|
40
46
|
}
|
|
41
|
-
compileOutputFieldsTemplates(record) {
|
|
42
|
-
return
|
|
47
|
+
compileOutputFieldsTemplates(record, customPrompt) {
|
|
48
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
49
|
+
return yield this.compileTemplates(customPrompt ? JSON.parse(customPrompt) : this.options.fillFieldsFromImages, record, v => String(v));
|
|
50
|
+
});
|
|
43
51
|
}
|
|
44
|
-
compileOutputFieldsTemplatesNoImage(record) {
|
|
45
|
-
return
|
|
52
|
+
compileOutputFieldsTemplatesNoImage(record, customPrompt) {
|
|
53
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
54
|
+
return yield this.compileTemplates(customPrompt ? JSON.parse(customPrompt) : this.options.fillPlainFields, record, v => String(v));
|
|
55
|
+
});
|
|
46
56
|
}
|
|
47
|
-
compileGenerationFieldTemplates(record) {
|
|
48
|
-
return
|
|
57
|
+
compileGenerationFieldTemplates(record, customPrompt) {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
return yield this.compileTemplates(customPrompt ? JSON.parse(customPrompt) : this.options.generateImages, record, v => String(customPrompt ? v : v.prompt));
|
|
60
|
+
});
|
|
49
61
|
}
|
|
50
62
|
checkRateLimit(field, fieldNameRateLimit, headers) {
|
|
51
63
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -65,7 +77,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
65
77
|
}
|
|
66
78
|
});
|
|
67
79
|
}
|
|
68
|
-
analyze_image(jobId, recordId, adminUser, headers) {
|
|
80
|
+
analyze_image(jobId, recordId, adminUser, headers, customPrompt) {
|
|
69
81
|
return __awaiter(this, void 0, void 0, function* () {
|
|
70
82
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
71
83
|
const selectedId = recordId;
|
|
@@ -100,7 +112,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
100
112
|
return { ok: false, error: 'One of the image URLs is not valid' };
|
|
101
113
|
}
|
|
102
114
|
//create prompt for OpenAI
|
|
103
|
-
const compiledOutputFields = this.compileOutputFieldsTemplates(record);
|
|
115
|
+
const compiledOutputFields = yield this.compileOutputFieldsTemplates(record, customPrompt);
|
|
104
116
|
const prompt = `Analyze the following image(s) and return a single JSON in format like: {'param1': 'value1', 'param2': 'value2'}.
|
|
105
117
|
Do NOT return array of objects. Do NOT include any Markdown, code blocks, explanations, or extra text. Only return valid JSON.
|
|
106
118
|
Each object must contain the following fields: ${JSON.stringify(compiledOutputFields)} Use the exact field names. If it's number field - return only number.
|
|
@@ -145,7 +157,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
145
157
|
}
|
|
146
158
|
});
|
|
147
159
|
}
|
|
148
|
-
analyzeNoImages(jobId, recordId, adminUser, headers) {
|
|
160
|
+
analyzeNoImages(jobId, recordId, adminUser, headers, customPrompt) {
|
|
149
161
|
return __awaiter(this, void 0, void 0, function* () {
|
|
150
162
|
const selectedId = recordId;
|
|
151
163
|
let isError = false;
|
|
@@ -157,7 +169,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
157
169
|
else {
|
|
158
170
|
const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
|
|
159
171
|
const record = yield this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ(primaryKeyColumn.name, selectedId)]);
|
|
160
|
-
const compiledOutputFields = this.compileOutputFieldsTemplatesNoImage(record);
|
|
172
|
+
const compiledOutputFields = yield this.compileOutputFieldsTemplatesNoImage(record, customPrompt);
|
|
161
173
|
const prompt = `Analyze the following fields and return a single JSON in format like: {'param1': 'value1', 'param2': 'value2'}.
|
|
162
174
|
Do NOT return array of objects. Do NOT include any Markdown, code blocks, explanations, or extra text. Only return valid JSON.
|
|
163
175
|
Each object must contain the following fields: ${JSON.stringify(compiledOutputFields)} Use the exact field names.
|
|
@@ -187,7 +199,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
187
199
|
}
|
|
188
200
|
});
|
|
189
201
|
}
|
|
190
|
-
initialImageGenerate(jobId, recordId, adminUser, headers) {
|
|
202
|
+
initialImageGenerate(jobId, recordId, adminUser, headers, customPrompt) {
|
|
191
203
|
return __awaiter(this, void 0, void 0, function* () {
|
|
192
204
|
var _a, _b;
|
|
193
205
|
const selectedId = recordId;
|
|
@@ -211,7 +223,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
211
223
|
}
|
|
212
224
|
}
|
|
213
225
|
const fieldTasks = Object.keys(((_b = this.options) === null || _b === void 0 ? void 0 : _b.generateImages) || {}).map((key) => __awaiter(this, void 0, void 0, function* () {
|
|
214
|
-
const prompt = this.compileGenerationFieldTemplates(record)[key];
|
|
226
|
+
const prompt = (yield this.compileGenerationFieldTemplates(record, customPrompt))[key];
|
|
215
227
|
let images;
|
|
216
228
|
if (this.options.attachFiles && attachmentFiles.length === 0) {
|
|
217
229
|
isError = true;
|
|
@@ -409,6 +421,12 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
409
421
|
fillPlainFields: ((_b = this.options.refreshRates) === null || _b === void 0 ? void 0 : _b.fillPlainFields) || 1000,
|
|
410
422
|
generateImages: ((_c = this.options.refreshRates) === null || _c === void 0 ? void 0 : _c.generateImages) || 5000,
|
|
411
423
|
regenerateImages: ((_d = this.options.refreshRates) === null || _d === void 0 ? void 0 : _d.regenerateImages) || 5000,
|
|
424
|
+
},
|
|
425
|
+
askConfirmationBeforeGenerating: this.options.askConfirmationBeforeGenerating || false,
|
|
426
|
+
generationPrompts: {
|
|
427
|
+
plainFieldsPrompts: this.options.fillPlainFields || {},
|
|
428
|
+
imageFieldsPrompts: this.options.fillFieldsFromImages || {},
|
|
429
|
+
imageGenerationPrompts: this.options.generateImages || {},
|
|
412
430
|
}
|
|
413
431
|
}
|
|
414
432
|
};
|
|
@@ -480,7 +498,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
480
498
|
}
|
|
481
499
|
}
|
|
482
500
|
}
|
|
483
|
-
if (this.options.fillFieldsFromImages || this.options.fillPlainFields || this.options.generateImages) {
|
|
501
|
+
if ((this.options.fillFieldsFromImages || this.options.fillPlainFields || this.options.generateImages) && !this.options.provideAdditionalContextForRecord) {
|
|
484
502
|
let matches = [];
|
|
485
503
|
const regex = /{{(.*?)}}/g;
|
|
486
504
|
if (this.options.fillFieldsFromImages) {
|
|
@@ -594,7 +612,12 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
594
612
|
if (oldRecord[value]) {
|
|
595
613
|
// put tag to delete old file
|
|
596
614
|
try {
|
|
597
|
-
|
|
615
|
+
if (columnPlugin.pluginOptions.storageAdapter.markKeyForDeletion !== undefined) {
|
|
616
|
+
yield columnPlugin.pluginOptions.storageAdapter.markKeyForDeletion(oldRecord[value]);
|
|
617
|
+
}
|
|
618
|
+
else {
|
|
619
|
+
yield columnPlugin.pluginOptions.storageAdapter.markKeyForDeletation(oldRecord[value]);
|
|
620
|
+
}
|
|
598
621
|
}
|
|
599
622
|
catch (e) {
|
|
600
623
|
// file might be e.g. already deleted, so we catch error
|
|
@@ -604,7 +627,12 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
604
627
|
if (fieldsToUpdate[idx][value] && fieldsToUpdate[idx][value] !== null) {
|
|
605
628
|
// remove tag from new file
|
|
606
629
|
// in this case we let it crash if it fails: this is a new file which just was uploaded.
|
|
607
|
-
|
|
630
|
+
if (columnPlugin.pluginOptions.storageAdapter.markKeyForNotDeletion !== undefined) {
|
|
631
|
+
yield columnPlugin.pluginOptions.storageAdapter.markKeyForNotDeletion(fieldsToUpdate[idx][value]);
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
yield columnPlugin.pluginOptions.storageAdapter.markKeyForNotDeletation(fieldsToUpdate[idx][value]);
|
|
635
|
+
}
|
|
608
636
|
}
|
|
609
637
|
}
|
|
610
638
|
}
|
|
@@ -628,13 +656,14 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
628
656
|
});
|
|
629
657
|
server.endpoint({
|
|
630
658
|
method: 'POST',
|
|
631
|
-
path: `/plugin/${this.pluginInstanceId}/
|
|
659
|
+
path: `/plugin/${this.pluginInstanceId}/get_image_generation_prompts`,
|
|
632
660
|
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, headers }) {
|
|
633
661
|
var _b;
|
|
634
662
|
const Id = body.recordId || [];
|
|
663
|
+
const customPrompt = body.customPrompt || null;
|
|
635
664
|
const record = yield this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ((_b = this.resourceConfig.columns.find(c => c.primaryKey)) === null || _b === void 0 ? void 0 : _b.name, Id)]);
|
|
636
|
-
const compiledGenerationOptions = this.compileGenerationFieldTemplates(record);
|
|
637
|
-
return
|
|
665
|
+
const compiledGenerationOptions = yield this.compileGenerationFieldTemplates(record, JSON.stringify({ "prompt": customPrompt }));
|
|
666
|
+
return compiledGenerationOptions;
|
|
638
667
|
})
|
|
639
668
|
});
|
|
640
669
|
server.endpoint({
|
|
@@ -652,7 +681,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
652
681
|
method: 'POST',
|
|
653
682
|
path: `/plugin/${this.pluginInstanceId}/create-job`,
|
|
654
683
|
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers }) {
|
|
655
|
-
const { actionType, recordId } = body;
|
|
684
|
+
const { actionType, recordId, customPrompt } = body;
|
|
656
685
|
const jobId = randomUUID();
|
|
657
686
|
jobs.set(jobId, { status: "in_progress" });
|
|
658
687
|
if (!actionType) {
|
|
@@ -666,13 +695,13 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
666
695
|
else {
|
|
667
696
|
switch (actionType) {
|
|
668
697
|
case 'generate_images':
|
|
669
|
-
this.initialImageGenerate(jobId, recordId, adminUser, headers);
|
|
698
|
+
this.initialImageGenerate(jobId, recordId, adminUser, headers, customPrompt);
|
|
670
699
|
break;
|
|
671
700
|
case 'analyze_no_images':
|
|
672
|
-
this.analyzeNoImages(jobId, recordId, adminUser, headers);
|
|
701
|
+
this.analyzeNoImages(jobId, recordId, adminUser, headers, customPrompt);
|
|
673
702
|
break;
|
|
674
703
|
case 'analyze':
|
|
675
|
-
this.analyze_image(jobId, recordId, adminUser, headers);
|
|
704
|
+
this.analyze_image(jobId, recordId, adminUser, headers, customPrompt);
|
|
676
705
|
break;
|
|
677
706
|
case 'regenerate_images':
|
|
678
707
|
if (!body.prompt || !body.fieldName) {
|
package/index.ts
CHANGED
|
@@ -25,11 +25,15 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
// Compile Handlebars templates in outputFields using record fields as context
|
|
28
|
-
private compileTemplates<T extends Record<string, any>>(
|
|
28
|
+
private async compileTemplates<T extends Record<string, any>>(
|
|
29
29
|
source: T,
|
|
30
30
|
record: any,
|
|
31
31
|
valueSelector: (value: T[keyof T]) => string
|
|
32
|
-
): Record<string, string
|
|
32
|
+
): Promise<Record<string, string>> {
|
|
33
|
+
if (this.options.provideAdditionalContextForRecord) {
|
|
34
|
+
const additionalFields = await this.options.provideAdditionalContextForRecord({ record, adminUser: null, resource: this.resourceConfig });
|
|
35
|
+
record = { ...record, ...additionalFields };
|
|
36
|
+
}
|
|
33
37
|
const compiled: Record<string, string> = {};
|
|
34
38
|
for (const [key, value] of Object.entries(source)) {
|
|
35
39
|
const templateStr = valueSelector(value);
|
|
@@ -43,16 +47,16 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
43
47
|
return compiled;
|
|
44
48
|
}
|
|
45
49
|
|
|
46
|
-
private compileOutputFieldsTemplates(record: any) {
|
|
47
|
-
return this.compileTemplates(this.options.fillFieldsFromImages, record, v => String(v));
|
|
50
|
+
private async compileOutputFieldsTemplates(record: any, customPrompt? : string) {
|
|
51
|
+
return await this.compileTemplates(customPrompt ? JSON.parse(customPrompt) :this.options.fillFieldsFromImages, record, v => String(v));
|
|
48
52
|
}
|
|
49
53
|
|
|
50
|
-
private compileOutputFieldsTemplatesNoImage(record: any) {
|
|
51
|
-
return this.compileTemplates(this.options.fillPlainFields, record, v => String(v));
|
|
54
|
+
private async compileOutputFieldsTemplatesNoImage(record: any, customPrompt? : string) {
|
|
55
|
+
return await this.compileTemplates(customPrompt ? JSON.parse(customPrompt) : this.options.fillPlainFields, record, v => String(v));
|
|
52
56
|
}
|
|
53
57
|
|
|
54
|
-
private compileGenerationFieldTemplates(record: any) {
|
|
55
|
-
return this.compileTemplates(this.options.generateImages, record, v => String(v.prompt));
|
|
58
|
+
private async compileGenerationFieldTemplates(record: any, customPrompt? : string) {
|
|
59
|
+
return await this.compileTemplates(customPrompt ? JSON.parse(customPrompt) : this.options.generateImages, record, v => String(customPrompt ? v : v.prompt));
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
private async checkRateLimit(field: string, fieldNameRateLimit: string | undefined, headers: Record<string, string | string[] | undefined>): Promise<void | { error?: string; }> {
|
|
@@ -72,7 +76,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
72
76
|
}
|
|
73
77
|
}
|
|
74
78
|
|
|
75
|
-
private async analyze_image(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined
|
|
79
|
+
private async analyze_image(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined>, customPrompt? : string) {
|
|
76
80
|
const selectedId = recordId;
|
|
77
81
|
let isError = false;
|
|
78
82
|
// Fetch the record using the provided ID
|
|
@@ -102,7 +106,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
102
106
|
return { ok: false, error: 'One of the image URLs is not valid' };
|
|
103
107
|
}
|
|
104
108
|
//create prompt for OpenAI
|
|
105
|
-
const compiledOutputFields = this.compileOutputFieldsTemplates(record);
|
|
109
|
+
const compiledOutputFields = await this.compileOutputFieldsTemplates(record, customPrompt);
|
|
106
110
|
const prompt = `Analyze the following image(s) and return a single JSON in format like: {'param1': 'value1', 'param2': 'value2'}.
|
|
107
111
|
Do NOT return array of objects. Do NOT include any Markdown, code blocks, explanations, or extra text. Only return valid JSON.
|
|
108
112
|
Each object must contain the following fields: ${JSON.stringify(compiledOutputFields)} Use the exact field names. If it's number field - return only number.
|
|
@@ -148,7 +152,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
148
152
|
|
|
149
153
|
}
|
|
150
154
|
|
|
151
|
-
private async analyzeNoImages(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined
|
|
155
|
+
private async analyzeNoImages(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined>, customPrompt? : string) {
|
|
152
156
|
const selectedId = recordId;
|
|
153
157
|
let isError = false;
|
|
154
158
|
if (STUB_MODE) {
|
|
@@ -159,7 +163,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
159
163
|
const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
|
|
160
164
|
const record = await this.adminforth.resource(this.resourceConfig.resourceId).get( [Filters.EQ(primaryKeyColumn.name, selectedId)] );
|
|
161
165
|
|
|
162
|
-
const compiledOutputFields = this.compileOutputFieldsTemplatesNoImage(record);
|
|
166
|
+
const compiledOutputFields = await this.compileOutputFieldsTemplatesNoImage(record, customPrompt);
|
|
163
167
|
const prompt = `Analyze the following fields and return a single JSON in format like: {'param1': 'value1', 'param2': 'value2'}.
|
|
164
168
|
Do NOT return array of objects. Do NOT include any Markdown, code blocks, explanations, or extra text. Only return valid JSON.
|
|
165
169
|
Each object must contain the following fields: ${JSON.stringify(compiledOutputFields)} Use the exact field names.
|
|
@@ -188,7 +192,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
188
192
|
}
|
|
189
193
|
}
|
|
190
194
|
|
|
191
|
-
private async initialImageGenerate(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined
|
|
195
|
+
private async initialImageGenerate(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined>, customPrompt? : string) {
|
|
192
196
|
const selectedId = recordId;
|
|
193
197
|
let isError = false;
|
|
194
198
|
const start = +new Date();
|
|
@@ -208,7 +212,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
208
212
|
}
|
|
209
213
|
}
|
|
210
214
|
const fieldTasks = Object.keys(this.options?.generateImages || {}).map(async (key) => {
|
|
211
|
-
const prompt = this.compileGenerationFieldTemplates(record)[key];
|
|
215
|
+
const prompt = (await this.compileGenerationFieldTemplates(record, customPrompt))[key];
|
|
212
216
|
let images;
|
|
213
217
|
if (this.options.attachFiles && attachmentFiles.length === 0) {
|
|
214
218
|
isError = true;
|
|
@@ -391,7 +395,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
391
395
|
};
|
|
392
396
|
|
|
393
397
|
const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
|
|
394
|
-
|
|
398
|
+
|
|
395
399
|
const pageInjection = {
|
|
396
400
|
file: this.componentPath('VisionAction.vue'),
|
|
397
401
|
meta: {
|
|
@@ -413,6 +417,12 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
413
417
|
fillPlainFields: this.options.refreshRates?.fillPlainFields || 1_000,
|
|
414
418
|
generateImages: this.options.refreshRates?.generateImages || 5_000,
|
|
415
419
|
regenerateImages: this.options.refreshRates?.regenerateImages || 5_000,
|
|
420
|
+
},
|
|
421
|
+
askConfirmationBeforeGenerating: this.options.askConfirmationBeforeGenerating || false,
|
|
422
|
+
generationPrompts: {
|
|
423
|
+
plainFieldsPrompts: this.options.fillPlainFields || {},
|
|
424
|
+
imageFieldsPrompts: this.options.fillFieldsFromImages || {},
|
|
425
|
+
imageGenerationPrompts: this.options.generateImages || {},
|
|
416
426
|
}
|
|
417
427
|
}
|
|
418
428
|
}
|
|
@@ -489,7 +499,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
489
499
|
}
|
|
490
500
|
}
|
|
491
501
|
}
|
|
492
|
-
if (this.options.fillFieldsFromImages || this.options.fillPlainFields || this.options.generateImages) {
|
|
502
|
+
if ((this.options.fillFieldsFromImages || this.options.fillPlainFields || this.options.generateImages) && !this.options.provideAdditionalContextForRecord) {
|
|
493
503
|
let matches: string[] = [];
|
|
494
504
|
const regex = /{{(.*?)}}/g;
|
|
495
505
|
|
|
@@ -615,7 +625,11 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
615
625
|
if (oldRecord[value]) {
|
|
616
626
|
// put tag to delete old file
|
|
617
627
|
try {
|
|
618
|
-
|
|
628
|
+
if (columnPlugin.pluginOptions.storageAdapter.markKeyForDeletion !== undefined) {
|
|
629
|
+
await columnPlugin.pluginOptions.storageAdapter.markKeyForDeletion(oldRecord[value]);
|
|
630
|
+
} else {
|
|
631
|
+
await columnPlugin.pluginOptions.storageAdapter.markKeyForDeletation(oldRecord[value]);
|
|
632
|
+
}
|
|
619
633
|
} catch (e) {
|
|
620
634
|
// file might be e.g. already deleted, so we catch error
|
|
621
635
|
console.error(`Error setting tag to true for object ${oldRecord[value]}. File will not be auto-cleaned up`);
|
|
@@ -624,7 +638,11 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
624
638
|
if (fieldsToUpdate[idx][value] && fieldsToUpdate[idx][value] !== null) {
|
|
625
639
|
// remove tag from new file
|
|
626
640
|
// in this case we let it crash if it fails: this is a new file which just was uploaded.
|
|
627
|
-
|
|
641
|
+
if (columnPlugin.pluginOptions.storageAdapter.markKeyForNotDeletion !== undefined) {
|
|
642
|
+
await columnPlugin.pluginOptions.storageAdapter.markKeyForNotDeletion(fieldsToUpdate[idx][value]);
|
|
643
|
+
} else {
|
|
644
|
+
await columnPlugin.pluginOptions.storageAdapter.markKeyForNotDeletation(fieldsToUpdate[idx][value]);
|
|
645
|
+
}
|
|
628
646
|
}
|
|
629
647
|
}
|
|
630
648
|
}
|
|
@@ -652,12 +670,13 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
652
670
|
|
|
653
671
|
server.endpoint({
|
|
654
672
|
method: 'POST',
|
|
655
|
-
path: `/plugin/${this.pluginInstanceId}/
|
|
673
|
+
path: `/plugin/${this.pluginInstanceId}/get_image_generation_prompts`,
|
|
656
674
|
handler: async ({ body, headers }) => {
|
|
657
675
|
const Id = body.recordId || [];
|
|
676
|
+
const customPrompt = body.customPrompt || null;
|
|
658
677
|
const record = await this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ(this.resourceConfig.columns.find(c => c.primaryKey)?.name, Id)]);
|
|
659
|
-
const compiledGenerationOptions = this.compileGenerationFieldTemplates(record);
|
|
660
|
-
return
|
|
678
|
+
const compiledGenerationOptions = await this.compileGenerationFieldTemplates(record, JSON.stringify({"prompt": customPrompt}));
|
|
679
|
+
return compiledGenerationOptions;
|
|
661
680
|
}
|
|
662
681
|
});
|
|
663
682
|
|
|
@@ -679,10 +698,9 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
679
698
|
method: 'POST',
|
|
680
699
|
path: `/plugin/${this.pluginInstanceId}/create-job`,
|
|
681
700
|
handler: async ({ body, adminUser, headers }) => {
|
|
682
|
-
const { actionType, recordId } = body;
|
|
701
|
+
const { actionType, recordId, customPrompt } = body;
|
|
683
702
|
const jobId = randomUUID();
|
|
684
703
|
jobs.set(jobId, { status: "in_progress" });
|
|
685
|
-
|
|
686
704
|
if (!actionType) {
|
|
687
705
|
jobs.set(jobId, { status: "failed", error: "Missing action type" });
|
|
688
706
|
//return { error: "Missing action type" };
|
|
@@ -693,13 +711,13 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
693
711
|
} else {
|
|
694
712
|
switch(actionType) {
|
|
695
713
|
case 'generate_images':
|
|
696
|
-
this.initialImageGenerate(jobId, recordId, adminUser, headers);
|
|
714
|
+
this.initialImageGenerate(jobId, recordId, adminUser, headers, customPrompt);
|
|
697
715
|
break;
|
|
698
716
|
case 'analyze_no_images':
|
|
699
|
-
this.analyzeNoImages(jobId, recordId, adminUser, headers);
|
|
717
|
+
this.analyzeNoImages(jobId, recordId, adminUser, headers, customPrompt);
|
|
700
718
|
break;
|
|
701
719
|
case 'analyze':
|
|
702
|
-
this.analyze_image(jobId, recordId, adminUser, headers);
|
|
720
|
+
this.analyze_image(jobId, recordId, adminUser, headers, customPrompt);
|
|
703
721
|
break;
|
|
704
722
|
case 'regenerate_images':
|
|
705
723
|
if (!body.prompt || !body.fieldName) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adminforth/bulk-ai-flow",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@types/handlebars": "^4.0.40",
|
|
30
|
-
"adminforth": "^2.4.0-next.
|
|
30
|
+
"adminforth": "^2.4.0-next.315",
|
|
31
31
|
"handlebars": "^4.7.8"
|
|
32
32
|
},
|
|
33
33
|
"release": {
|
package/types.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { ImageVisionAdapter, ImageGenerationAdapter, CompletionAdapter } from "adminforth";
|
|
2
|
-
|
|
1
|
+
import AdminForth, { ImageVisionAdapter, ImageGenerationAdapter, CompletionAdapter } from "adminforth";
|
|
3
2
|
|
|
4
3
|
export interface PluginOptions {
|
|
5
4
|
/**
|
|
@@ -109,4 +108,15 @@ export interface PluginOptions {
|
|
|
109
108
|
ok: boolean;
|
|
110
109
|
error?: undefined;
|
|
111
110
|
}>
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Custom message for the context shown to the user when performing the action
|
|
114
|
+
*/
|
|
115
|
+
provideAdditionalContextForRecord?: ({record, adminUser, resource}: {
|
|
116
|
+
record: any;
|
|
117
|
+
adminUser: any;
|
|
118
|
+
resource: any;
|
|
119
|
+
}) => Record<string, any> | Promise<Record<string, any>>;
|
|
120
|
+
|
|
121
|
+
askConfirmationBeforeGenerating?: boolean;
|
|
112
122
|
}
|