@adminforth/bulk-ai-flow 1.8.1 → 1.9.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 +42 -13
- package/custom/VisionAction.vue +153 -65
- package/custom/VisionTable.vue +3 -1
- package/dist/custom/ImageGenerationCarousel.vue +42 -13
- package/dist/custom/VisionAction.vue +153 -65
- package/dist/custom/VisionTable.vue +3 -1
- package/dist/index.js +310 -195
- package/index.ts +315 -207
- package/package.json +1 -1
- package/types.ts +9 -0
package/build.log
CHANGED
|
@@ -11,5 +11,5 @@ custom/package-lock.json
|
|
|
11
11
|
custom/package.json
|
|
12
12
|
custom/tsconfig.json
|
|
13
13
|
|
|
14
|
-
sent
|
|
15
|
-
total size is
|
|
14
|
+
sent 184,983 bytes received 134 bytes 370,234.00 bytes/sec
|
|
15
|
+
total size is 184,450 speedup is 1.00
|
|
@@ -190,7 +190,7 @@ const { t: $t } = useI18n();
|
|
|
190
190
|
|
|
191
191
|
const prompt = ref('');
|
|
192
192
|
const emit = defineEmits(['close', 'selectImage', 'error', 'updateCarouselIndex']);
|
|
193
|
-
const props = defineProps(['meta', 'record', 'images', 'recordId', 'prompt', 'fieldName', 'isError', 'errorMessage', 'carouselImageIndex']);
|
|
193
|
+
const props = defineProps(['meta', 'record', 'images', 'recordId', 'prompt', 'fieldName', 'isError', 'errorMessage', 'carouselImageIndex', 'regenerateImagesRefreshRate']);
|
|
194
194
|
const images = ref([]);
|
|
195
195
|
const loading = ref(false);
|
|
196
196
|
const attachmentFiles = ref<string[]>([])
|
|
@@ -370,26 +370,24 @@ async function generateImages() {
|
|
|
370
370
|
let error = null;
|
|
371
371
|
try {
|
|
372
372
|
resp = await callAdminForthApi({
|
|
373
|
-
path: `/plugin/${props.meta.pluginInstanceId}/
|
|
373
|
+
path: `/plugin/${props.meta.pluginInstanceId}/create-job`,
|
|
374
374
|
method: 'POST',
|
|
375
375
|
body: {
|
|
376
|
-
|
|
376
|
+
actionType: 'regenerate_images',
|
|
377
377
|
recordId: props.recordId,
|
|
378
|
-
|
|
378
|
+
prompt: prompt.value,
|
|
379
|
+
fieldName: props.fieldName
|
|
379
380
|
},
|
|
380
381
|
});
|
|
381
382
|
} catch (e) {
|
|
382
383
|
console.error(e);
|
|
383
|
-
} finally {
|
|
384
|
-
clearInterval(ticker);
|
|
385
|
-
loadingTimer.value = null;
|
|
386
|
-
loading.value = false;
|
|
387
384
|
}
|
|
385
|
+
|
|
388
386
|
if (resp?.error) {
|
|
389
387
|
error = resp.error;
|
|
390
388
|
}
|
|
391
389
|
if (!resp) {
|
|
392
|
-
error = $t('Error
|
|
390
|
+
error = $t('Error creating image generation job');
|
|
393
391
|
}
|
|
394
392
|
|
|
395
393
|
if (error) {
|
|
@@ -401,19 +399,50 @@ async function generateImages() {
|
|
|
401
399
|
variant: 'danger',
|
|
402
400
|
timeout: 'unlimited',
|
|
403
401
|
});
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
402
|
+
}
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const jobId = resp.jobId;
|
|
407
|
+
let jobStatus = null;
|
|
408
|
+
let jobResponse = null;
|
|
409
|
+
while (jobStatus !== 'completed' && jobStatus !== 'failed') {
|
|
410
|
+
jobResponse = await callAdminForthApi({
|
|
411
|
+
path: `/plugin/${props.meta.pluginInstanceId}/get-job-status`,
|
|
412
|
+
method: 'POST',
|
|
413
|
+
body: { jobId },
|
|
407
414
|
});
|
|
415
|
+
if (jobResponse?.error) {
|
|
416
|
+
error = jobResponse.error;
|
|
417
|
+
break;
|
|
418
|
+
};
|
|
419
|
+
jobStatus = jobResponse?.job?.status;
|
|
420
|
+
if (jobStatus === 'failed') {
|
|
421
|
+
error = jobResponse?.job?.error || $t('Image generation job failed');
|
|
408
422
|
}
|
|
423
|
+
await new Promise((resolve) => setTimeout(resolve, props.regenerateImagesRefreshRate));
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (error) {
|
|
427
|
+
adminforth.alert({
|
|
428
|
+
message: error,
|
|
429
|
+
variant: 'danger',
|
|
430
|
+
timeout: 'unlimited',
|
|
431
|
+
});
|
|
409
432
|
return;
|
|
410
433
|
}
|
|
411
434
|
|
|
435
|
+
const respImages = jobResponse?.job?.result[props.fieldName] || [];
|
|
436
|
+
|
|
412
437
|
images.value = [
|
|
413
438
|
...images.value,
|
|
414
|
-
...
|
|
439
|
+
...respImages,
|
|
415
440
|
];
|
|
416
441
|
|
|
442
|
+
clearInterval(ticker);
|
|
443
|
+
loadingTimer.value = null;
|
|
444
|
+
loading.value = false;
|
|
445
|
+
|
|
417
446
|
await nextTick();
|
|
418
447
|
|
|
419
448
|
|
package/custom/VisionAction.vue
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
header="Bulk AI Flow"
|
|
11
11
|
class="!max-w-full w-full lg:w-[1600px] !lg:max-w-[1600px]"
|
|
12
12
|
:buttons="[
|
|
13
|
-
{ label: checkedCount > 1 ? 'Save fields' : 'Save field', options: { disabled: isLoading || checkedCount < 1 || isCriticalError || isFetchingRecords, loader: isLoading, class: 'w-fit sm:w-40' }, onclick: (dialog) => { saveData(); dialog.hide(); } },
|
|
13
|
+
{ label: checkedCount > 1 ? 'Save fields' : 'Save field', options: { disabled: isLoading || checkedCount < 1 || isCriticalError || isFetchingRecords || isGeneratingImages || isAnalizingFields || isAnalizingImages, loader: isLoading, class: 'w-fit sm:w-40' }, onclick: (dialog) => { saveData(); dialog.hide(); } },
|
|
14
14
|
{ label: 'Cancel', onclick: (dialog) => dialog.hide() },
|
|
15
15
|
]"
|
|
16
16
|
>
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
@error="handleTableError"
|
|
34
34
|
:carouselSaveImages="carouselSaveImages"
|
|
35
35
|
:carouselImageIndex="carouselImageIndex"
|
|
36
|
+
:regenerateImagesRefreshRate="props.meta.refreshRates?.regenerateImages"
|
|
36
37
|
/>
|
|
37
38
|
</div>
|
|
38
39
|
<div class="text-red-600 flex items-center w-full">
|
|
@@ -52,7 +53,6 @@ import { useI18n } from 'vue-i18n';
|
|
|
52
53
|
import { AdminUser, type AdminForthResourceCommon } from '@/types';
|
|
53
54
|
|
|
54
55
|
const { t } = useI18n();
|
|
55
|
-
|
|
56
56
|
const props = defineProps<{
|
|
57
57
|
checkboxes: any,
|
|
58
58
|
meta: any,
|
|
@@ -88,9 +88,14 @@ const isCriticalError = ref(false);
|
|
|
88
88
|
const isImageGenerationError = ref(false);
|
|
89
89
|
const errorMessage = ref('');
|
|
90
90
|
const checkedCount = ref(0);
|
|
91
|
+
const isGeneratingImages = ref(false);
|
|
92
|
+
const isAnalizingFields = ref(false);
|
|
93
|
+
const isAnalizingImages = ref(false);
|
|
94
|
+
|
|
91
95
|
|
|
92
96
|
const openDialog = async () => {
|
|
93
97
|
confirmDialog.value.open();
|
|
98
|
+
isFetchingRecords.value = true;
|
|
94
99
|
await getRecords();
|
|
95
100
|
if (props.meta.isAttachFiles) {
|
|
96
101
|
await getImages();
|
|
@@ -101,42 +106,41 @@ const openDialog = async () => {
|
|
|
101
106
|
tableColumnsIndexes.value = result.indexes;
|
|
102
107
|
customFieldNames.value = tableHeaders.value.slice((props.meta.isAttachFiles) ? 3 : 2).map(h => h.fieldName);
|
|
103
108
|
setSelected();
|
|
109
|
+
if (props.meta.isImageGeneration) {
|
|
110
|
+
fillCarouselSaveImages();
|
|
111
|
+
}
|
|
104
112
|
for (let i = 0; i < selected.value?.length; i++) {
|
|
105
113
|
openGenerationCarousel.value[i] = props.meta.outputImageFields?.reduce((acc,key) =>{
|
|
106
114
|
acc[key] = false;
|
|
107
115
|
return acc;
|
|
108
116
|
},{[primaryKey]: records.value[i][primaryKey]} as Record<string, boolean>);
|
|
109
117
|
}
|
|
110
|
-
isFetchingRecords.value =
|
|
111
|
-
|
|
118
|
+
isFetchingRecords.value = false;
|
|
119
|
+
|
|
120
|
+
if (props.meta.isImageGeneration) {
|
|
121
|
+
isGeneratingImages.value = true;
|
|
122
|
+
runAiAction({
|
|
123
|
+
endpoint: 'initial_image_generate',
|
|
124
|
+
actionType: 'generate_images',
|
|
125
|
+
responseFlag: isAiResponseReceivedImage,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
112
128
|
if (props.meta.isFieldsForAnalizeFromImages) {
|
|
113
|
-
|
|
129
|
+
isAnalizingImages.value = true;
|
|
130
|
+
runAiAction({
|
|
114
131
|
endpoint: 'analyze',
|
|
115
132
|
actionType: 'analyze',
|
|
116
133
|
responseFlag: isAiResponseReceivedAnalize,
|
|
117
|
-
})
|
|
134
|
+
});
|
|
118
135
|
}
|
|
119
136
|
if (props.meta.isFieldsForAnalizePlain) {
|
|
120
|
-
|
|
137
|
+
isAnalizingFields.value = true;
|
|
138
|
+
runAiAction({
|
|
121
139
|
endpoint: 'analyze_no_images',
|
|
122
140
|
actionType: 'analyze_no_images',
|
|
123
141
|
responseFlag: isAiResponseReceivedAnalize,
|
|
124
|
-
})
|
|
125
|
-
}
|
|
126
|
-
if (props.meta.isImageGeneration) {
|
|
127
|
-
tasks.push(runAiAction({
|
|
128
|
-
endpoint: 'initial_image_generate',
|
|
129
|
-
actionType: 'generate_images',
|
|
130
|
-
responseFlag: isAiResponseReceivedImage,
|
|
131
|
-
}));
|
|
132
|
-
}
|
|
133
|
-
await Promise.all(tasks);
|
|
134
|
-
|
|
135
|
-
if (props.meta.isImageGeneration) {
|
|
136
|
-
fillCarouselSaveImages();
|
|
142
|
+
});
|
|
137
143
|
}
|
|
138
|
-
|
|
139
|
-
isFetchingRecords.value = false;
|
|
140
144
|
}
|
|
141
145
|
|
|
142
146
|
watch(selected, (val) => {
|
|
@@ -149,10 +153,10 @@ function fillCarouselSaveImages() {
|
|
|
149
153
|
const tempItem: any = {};
|
|
150
154
|
const tempItemIndex: any = {};
|
|
151
155
|
for (const [key, value] of Object.entries(item)) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
+
if (props.meta.outputImageFields?.includes(key)) {
|
|
157
|
+
tempItem[key] = [];
|
|
158
|
+
tempItemIndex[key] = 0;
|
|
159
|
+
}
|
|
156
160
|
}
|
|
157
161
|
carouselSaveImages.value.push(tempItem);
|
|
158
162
|
carouselImageIndex.value.push(tempItemIndex);
|
|
@@ -398,38 +402,129 @@ async function runAiAction({
|
|
|
398
402
|
responseFlag: Ref<boolean[]>;
|
|
399
403
|
updateOnSuccess?: boolean;
|
|
400
404
|
}) {
|
|
401
|
-
let
|
|
402
|
-
let
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
405
|
+
let hasError = false;
|
|
406
|
+
let errorMessage = '';
|
|
407
|
+
const jobsIds: { jobId: any; recordId: any; }[] = [];
|
|
408
|
+
responseFlag.value = props.checkboxes.map(() => false);
|
|
409
|
+
|
|
410
|
+
//creating jobs
|
|
411
|
+
const tasks = props.checkboxes.map(async (checkbox, i) => {
|
|
412
|
+
try {
|
|
413
|
+
const res = await callAdminForthApi({
|
|
414
|
+
path: `/plugin/${props.meta.pluginInstanceId}/create-job`,
|
|
415
|
+
method: 'POST',
|
|
416
|
+
body: {
|
|
417
|
+
actionType: actionType,
|
|
418
|
+
recordId: checkbox,
|
|
419
|
+
},
|
|
420
|
+
});
|
|
406
421
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
422
|
+
if (res?.error) {
|
|
423
|
+
throw new Error(res.error);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (!res) {
|
|
427
|
+
throw new Error(`${actionType} request returned empty response.`);
|
|
428
|
+
}
|
|
414
429
|
|
|
415
|
-
|
|
416
|
-
|
|
430
|
+
jobsIds.push({ jobId: res.jobId, recordId: checkbox });
|
|
431
|
+
} catch (e) {
|
|
432
|
+
console.error(`Error during ${actionType} for item ${i}:`, e);
|
|
433
|
+
hasError = true;
|
|
434
|
+
errorMessage = `Failed to ${actionType.replace('_', ' ')}. Please, try to re-run the action.`;
|
|
435
|
+
return { success: false, index: i, error: e };
|
|
417
436
|
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
error = `Failed to ${actionType.replace('_', ' ')}. Please, try to re-run the action.`;
|
|
421
|
-
}
|
|
437
|
+
});
|
|
438
|
+
await Promise.all(tasks);
|
|
422
439
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
440
|
+
//polling jobs
|
|
441
|
+
let isInProgress = true;
|
|
442
|
+
//if no jobs were created, skip polling
|
|
443
|
+
while (isInProgress) {
|
|
444
|
+
//check if at least one job is still in progress
|
|
445
|
+
let isAtLeastOneInProgress = false;
|
|
446
|
+
//checking status of each job
|
|
447
|
+
for (const { jobId, recordId } of jobsIds) {
|
|
448
|
+
//check job status
|
|
449
|
+
const jobResponse = await callAdminForthApi({
|
|
450
|
+
path: `/plugin/${props.meta.pluginInstanceId}/get-job-status`,
|
|
451
|
+
method: 'POST',
|
|
452
|
+
body: { jobId },
|
|
453
|
+
});
|
|
454
|
+
//check for errors
|
|
455
|
+
if (jobResponse?.error) {
|
|
456
|
+
console.error(`Error during ${actionType}:`, jobResponse.error);
|
|
457
|
+
break;
|
|
458
|
+
};
|
|
459
|
+
// extract job status
|
|
460
|
+
let jobStatus = jobResponse?.job?.status;
|
|
461
|
+
// check if job is still in progress. If in progress - skip to next job
|
|
462
|
+
if (jobStatus === 'in_progress') {
|
|
463
|
+
isAtLeastOneInProgress = true;
|
|
464
|
+
//if job is completed - update record data
|
|
465
|
+
} else if (jobStatus === 'completed') {
|
|
466
|
+
// finding index of the record in selected array
|
|
467
|
+
const index = selected.value.findIndex(item => String(item[primaryKey]) === String(recordId));
|
|
468
|
+
//if we are generating images - update carouselSaveImages with new image
|
|
469
|
+
if (actionType === 'generate_images') {
|
|
470
|
+
for (const [key, value] of Object.entries(carouselSaveImages.value[index])) {
|
|
471
|
+
if (props.meta.outputImageFields?.includes(key)) {
|
|
472
|
+
carouselSaveImages.value[index][key] = [jobResponse.job.result[key]];
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
//marking that we received response for this record
|
|
477
|
+
if (actionType !== 'analyze_no_images' || !props.meta.isFieldsForAnalizeFromImages) {
|
|
478
|
+
responseFlag.value[index] = true;
|
|
479
|
+
}
|
|
480
|
+
//updating selected with new data from AI
|
|
481
|
+
const pk = selected.value[index]?.[primaryKey];
|
|
482
|
+
if (pk) {
|
|
483
|
+
selected.value[index] = {
|
|
484
|
+
...selected.value[index],
|
|
485
|
+
...jobResponse.job.result,
|
|
486
|
+
isChecked: true,
|
|
487
|
+
[primaryKey]: pk,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
//removing job from jobsIds
|
|
491
|
+
if (index !== -1) {
|
|
492
|
+
jobsIds.splice(jobsIds.findIndex(j => j.jobId === jobId), 1);
|
|
493
|
+
}
|
|
494
|
+
// checking one more time if we have in progress jobs
|
|
495
|
+
isAtLeastOneInProgress = true;
|
|
496
|
+
// if job is failed - set error
|
|
497
|
+
} else if (jobStatus === 'failed') {
|
|
498
|
+
const index = selected.value.findIndex(item => String(item[primaryKey]) === String(recordId));
|
|
499
|
+
if (actionType !== 'analyze_no_images' || !props.meta.isFieldsForAnalizeFromImages) {
|
|
500
|
+
responseFlag.value[index] = true;
|
|
501
|
+
}
|
|
502
|
+
adminforth.alert({
|
|
503
|
+
message: `Generation action "${actionType.replace('_', ' ')}" failed for record: ${recordId}. Error: ${jobResponse.job?.error || 'Unknown error'}`,
|
|
504
|
+
variant: 'danger',
|
|
505
|
+
timeout: 'unlimited',
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
if (!isAtLeastOneInProgress) {
|
|
510
|
+
isInProgress = false;
|
|
511
|
+
}
|
|
512
|
+
if (jobsIds.length > 0) {
|
|
513
|
+
if (actionType === 'generate_images') {
|
|
514
|
+
await new Promise(resolve => setTimeout(resolve, props.meta.refreshRates?.generateImages));
|
|
515
|
+
} else if (actionType === 'analyze') {
|
|
516
|
+
await new Promise(resolve => setTimeout(resolve, props.meta.refreshRates?.fillFieldsFromImages));
|
|
517
|
+
} else if (actionType === 'analyze_no_images') {
|
|
518
|
+
await new Promise(resolve => setTimeout(resolve, props.meta.refreshRates?.fillPlainFields));
|
|
519
|
+
} else {
|
|
520
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
521
|
+
}
|
|
522
|
+
}
|
|
428
523
|
}
|
|
429
524
|
|
|
430
|
-
if (
|
|
525
|
+
if (hasError) {
|
|
431
526
|
adminforth.alert({
|
|
432
|
-
message:
|
|
527
|
+
message: errorMessage,
|
|
433
528
|
variant: 'danger',
|
|
434
529
|
timeout: 'unlimited',
|
|
435
530
|
});
|
|
@@ -437,26 +532,19 @@ async function runAiAction({
|
|
|
437
532
|
if (actionType === 'generate_images') {
|
|
438
533
|
isImageGenerationError.value = true;
|
|
439
534
|
}
|
|
440
|
-
errorMessage.value =
|
|
535
|
+
this.errorMessage.value = errorMessage;
|
|
441
536
|
return;
|
|
442
537
|
}
|
|
443
538
|
|
|
444
|
-
if (
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
...item,
|
|
451
|
-
isChecked: true,
|
|
452
|
-
[primaryKey]: pk,
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
});
|
|
539
|
+
if (actionType === 'generate_images') {
|
|
540
|
+
isGeneratingImages.value = false;
|
|
541
|
+
} else if (actionType === 'analyze') {
|
|
542
|
+
isAnalizingImages.value = false;
|
|
543
|
+
} else if (actionType === 'analyze_no_images') {
|
|
544
|
+
isAnalizingFields.value = false;
|
|
456
545
|
}
|
|
457
546
|
}
|
|
458
547
|
|
|
459
|
-
|
|
460
548
|
async function uploadImage(imgBlob, id, fieldName) {
|
|
461
549
|
const file = new File([imgBlob], `generated_${fieldName}_${id}.${imgBlob.type.split('/').pop()}`, { type: imgBlob.type });
|
|
462
550
|
const { name, size, type } = file;
|
package/custom/VisionTable.vue
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
</template>
|
|
11
11
|
<!-- CHECKBOX CELL TEMPLATE -->
|
|
12
12
|
<template #cell:checkboxes="{ item }">
|
|
13
|
-
<div class="flex items-center justify-center">
|
|
13
|
+
<div class="max-w-[100px] flex items-center justify-center">
|
|
14
14
|
<Checkbox
|
|
15
15
|
v-model="selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])].isChecked"
|
|
16
16
|
/>
|
|
@@ -99,6 +99,7 @@
|
|
|
99
99
|
:meta="props.meta"
|
|
100
100
|
:fieldName="n"
|
|
101
101
|
:carouselImageIndex="carouselImageIndex[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
|
|
102
|
+
:regenerateImagesRefreshRate="regenerateImagesRefreshRate"
|
|
102
103
|
@error="handleError"
|
|
103
104
|
@close="openGenerationCarousel[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n] = false"
|
|
104
105
|
@selectImage="updateSelectedImage"
|
|
@@ -139,6 +140,7 @@ const props = defineProps<{
|
|
|
139
140
|
errorMessage: string
|
|
140
141
|
carouselSaveImages: any[]
|
|
141
142
|
carouselImageIndex: any[]
|
|
143
|
+
regenerateImagesRefreshRate: number
|
|
142
144
|
}>();
|
|
143
145
|
const emit = defineEmits(['error']);
|
|
144
146
|
|
|
@@ -190,7 +190,7 @@ const { t: $t } = useI18n();
|
|
|
190
190
|
|
|
191
191
|
const prompt = ref('');
|
|
192
192
|
const emit = defineEmits(['close', 'selectImage', 'error', 'updateCarouselIndex']);
|
|
193
|
-
const props = defineProps(['meta', 'record', 'images', 'recordId', 'prompt', 'fieldName', 'isError', 'errorMessage', 'carouselImageIndex']);
|
|
193
|
+
const props = defineProps(['meta', 'record', 'images', 'recordId', 'prompt', 'fieldName', 'isError', 'errorMessage', 'carouselImageIndex', 'regenerateImagesRefreshRate']);
|
|
194
194
|
const images = ref([]);
|
|
195
195
|
const loading = ref(false);
|
|
196
196
|
const attachmentFiles = ref<string[]>([])
|
|
@@ -370,26 +370,24 @@ async function generateImages() {
|
|
|
370
370
|
let error = null;
|
|
371
371
|
try {
|
|
372
372
|
resp = await callAdminForthApi({
|
|
373
|
-
path: `/plugin/${props.meta.pluginInstanceId}/
|
|
373
|
+
path: `/plugin/${props.meta.pluginInstanceId}/create-job`,
|
|
374
374
|
method: 'POST',
|
|
375
375
|
body: {
|
|
376
|
-
|
|
376
|
+
actionType: 'regenerate_images',
|
|
377
377
|
recordId: props.recordId,
|
|
378
|
-
|
|
378
|
+
prompt: prompt.value,
|
|
379
|
+
fieldName: props.fieldName
|
|
379
380
|
},
|
|
380
381
|
});
|
|
381
382
|
} catch (e) {
|
|
382
383
|
console.error(e);
|
|
383
|
-
} finally {
|
|
384
|
-
clearInterval(ticker);
|
|
385
|
-
loadingTimer.value = null;
|
|
386
|
-
loading.value = false;
|
|
387
384
|
}
|
|
385
|
+
|
|
388
386
|
if (resp?.error) {
|
|
389
387
|
error = resp.error;
|
|
390
388
|
}
|
|
391
389
|
if (!resp) {
|
|
392
|
-
error = $t('Error
|
|
390
|
+
error = $t('Error creating image generation job');
|
|
393
391
|
}
|
|
394
392
|
|
|
395
393
|
if (error) {
|
|
@@ -401,19 +399,50 @@ async function generateImages() {
|
|
|
401
399
|
variant: 'danger',
|
|
402
400
|
timeout: 'unlimited',
|
|
403
401
|
});
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
402
|
+
}
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const jobId = resp.jobId;
|
|
407
|
+
let jobStatus = null;
|
|
408
|
+
let jobResponse = null;
|
|
409
|
+
while (jobStatus !== 'completed' && jobStatus !== 'failed') {
|
|
410
|
+
jobResponse = await callAdminForthApi({
|
|
411
|
+
path: `/plugin/${props.meta.pluginInstanceId}/get-job-status`,
|
|
412
|
+
method: 'POST',
|
|
413
|
+
body: { jobId },
|
|
407
414
|
});
|
|
415
|
+
if (jobResponse?.error) {
|
|
416
|
+
error = jobResponse.error;
|
|
417
|
+
break;
|
|
418
|
+
};
|
|
419
|
+
jobStatus = jobResponse?.job?.status;
|
|
420
|
+
if (jobStatus === 'failed') {
|
|
421
|
+
error = jobResponse?.job?.error || $t('Image generation job failed');
|
|
408
422
|
}
|
|
423
|
+
await new Promise((resolve) => setTimeout(resolve, props.regenerateImagesRefreshRate));
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (error) {
|
|
427
|
+
adminforth.alert({
|
|
428
|
+
message: error,
|
|
429
|
+
variant: 'danger',
|
|
430
|
+
timeout: 'unlimited',
|
|
431
|
+
});
|
|
409
432
|
return;
|
|
410
433
|
}
|
|
411
434
|
|
|
435
|
+
const respImages = jobResponse?.job?.result[props.fieldName] || [];
|
|
436
|
+
|
|
412
437
|
images.value = [
|
|
413
438
|
...images.value,
|
|
414
|
-
...
|
|
439
|
+
...respImages,
|
|
415
440
|
];
|
|
416
441
|
|
|
442
|
+
clearInterval(ticker);
|
|
443
|
+
loadingTimer.value = null;
|
|
444
|
+
loading.value = false;
|
|
445
|
+
|
|
417
446
|
await nextTick();
|
|
418
447
|
|
|
419
448
|
|