@adminforth/bulk-ai-flow 1.5.3 → 1.5.5
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 +44 -40
- package/custom/visionTable.vue +3 -3
- package/dist/custom/visionAction.vue +44 -40
- package/dist/custom/visionTable.vue +3 -3
- package/dist/index.js +46 -37
- package/index.ts +49 -42
- package/package.json +1 -1
package/build.log
CHANGED
|
@@ -11,5 +11,5 @@ custom/tsconfig.json
|
|
|
11
11
|
custom/visionAction.vue
|
|
12
12
|
custom/visionTable.vue
|
|
13
13
|
|
|
14
|
-
sent 182,
|
|
15
|
-
total size is
|
|
14
|
+
sent 182,651 bytes received 134 bytes 365,570.00 bytes/sec
|
|
15
|
+
total size is 182,116 speedup is 1.00
|
package/custom/visionAction.vue
CHANGED
|
@@ -106,6 +106,7 @@ const openGenerationCarousel = ref([]);
|
|
|
106
106
|
const isLoading = ref(false);
|
|
107
107
|
const isError = ref(false);
|
|
108
108
|
const isCriticalError = ref(false);
|
|
109
|
+
const isImageGenerationError = ref(false);
|
|
109
110
|
const errorMessage = ref('');
|
|
110
111
|
const checkedCount = ref(0);
|
|
111
112
|
|
|
@@ -289,26 +290,27 @@ async function prepareDataForSave() {
|
|
|
289
290
|
.filter(item => item.isChecked === true)
|
|
290
291
|
.map(item => item[primaryKey]);
|
|
291
292
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
for (const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
293
|
+
if (isImageGenerationError.value !== true) {
|
|
294
|
+
const promises = [];
|
|
295
|
+
for (const item of checkedItems) {
|
|
296
|
+
for (const [key, value] of Object.entries(item)) {
|
|
297
|
+
if(props.meta.outputImageFields?.includes(key)) {
|
|
298
|
+
const p = convertImages(key, value).then(result => {
|
|
299
|
+
item[key] = result;
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
promises.push(p);
|
|
303
|
+
}
|
|
301
304
|
}
|
|
302
305
|
}
|
|
306
|
+
await Promise.all(promises);
|
|
303
307
|
}
|
|
304
|
-
await Promise.all(promises);
|
|
305
|
-
|
|
306
308
|
return [checkedItemsIDs, checkedItems];
|
|
307
309
|
}
|
|
308
310
|
|
|
309
311
|
async function convertImages(fieldName, img) {
|
|
310
312
|
let imgBlob;
|
|
311
|
-
if (img.startsWith('data:')) {
|
|
313
|
+
if (typeof img === 'string' && img.startsWith('data:')) {
|
|
312
314
|
const base64 = img.split(',')[1];
|
|
313
315
|
const mimeType = img.split(';')[0].split(':')[1];
|
|
314
316
|
const byteCharacters = atob(base64);
|
|
@@ -318,7 +320,7 @@ async function convertImages(fieldName, img) {
|
|
|
318
320
|
}
|
|
319
321
|
const byteArray = new Uint8Array(byteNumbers);
|
|
320
322
|
imgBlob = new Blob([byteArray], { type: mimeType });
|
|
321
|
-
} else {
|
|
323
|
+
} else if (typeof img === 'string') {
|
|
322
324
|
imgBlob = await fetch(
|
|
323
325
|
`/adminapi/v1/plugin/${props.meta.outputImagesPluginInstanceIds[fieldName]}/cors-proxy?url=${encodeURIComponent(img)}`
|
|
324
326
|
).then(res => { return res.blob() });
|
|
@@ -328,9 +330,8 @@ async function convertImages(fieldName, img) {
|
|
|
328
330
|
|
|
329
331
|
|
|
330
332
|
async function analyzeFields() {
|
|
333
|
+
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => false);
|
|
331
334
|
try {
|
|
332
|
-
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => false);
|
|
333
|
-
|
|
334
335
|
const res = await callAdminForthApi({
|
|
335
336
|
path: `/plugin/${props.meta.pluginInstanceId}/analyze`,
|
|
336
337
|
method: 'POST',
|
|
@@ -339,8 +340,6 @@ async function analyzeFields() {
|
|
|
339
340
|
},
|
|
340
341
|
});
|
|
341
342
|
|
|
342
|
-
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => true);
|
|
343
|
-
|
|
344
343
|
if (res?.error) {
|
|
345
344
|
adminforth.alert({
|
|
346
345
|
message: res.error,
|
|
@@ -350,7 +349,7 @@ async function analyzeFields() {
|
|
|
350
349
|
|
|
351
350
|
console.error('Failed to analyze image(s):', res.error);
|
|
352
351
|
isError.value = true;
|
|
353
|
-
isCriticalError.value = true;
|
|
352
|
+
//isCriticalError.value = true;
|
|
354
353
|
errorMessage.value = `Failed to fetch analyze image(s). Please, try to re-run the action.`;
|
|
355
354
|
} else {
|
|
356
355
|
res.result.forEach((item, idx) => {
|
|
@@ -375,16 +374,16 @@ async function analyzeFields() {
|
|
|
375
374
|
|
|
376
375
|
console.error('Failed to analyze image(s):', error);
|
|
377
376
|
isError.value = true;
|
|
378
|
-
isCriticalError.value = true;
|
|
377
|
+
//isCriticalError.value = true;
|
|
379
378
|
errorMessage.value = res.error;
|
|
380
379
|
}
|
|
380
|
+
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => true);
|
|
381
381
|
}
|
|
382
382
|
|
|
383
383
|
|
|
384
384
|
async function analyzeFieldsNoImages() {
|
|
385
|
+
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => false);
|
|
385
386
|
try {
|
|
386
|
-
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => false);
|
|
387
|
-
|
|
388
387
|
const res = await callAdminForthApi({
|
|
389
388
|
path: `/plugin/${props.meta.pluginInstanceId}/analyze_no_images`,
|
|
390
389
|
method: 'POST',
|
|
@@ -392,9 +391,6 @@ async function analyzeFieldsNoImages() {
|
|
|
392
391
|
selectedIds: props.checkboxes,
|
|
393
392
|
},
|
|
394
393
|
});
|
|
395
|
-
if(!props.meta.isFieldsForAnalizeFromImages) {
|
|
396
|
-
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => true);
|
|
397
|
-
}
|
|
398
394
|
if(res?.error) {
|
|
399
395
|
adminforth.alert({
|
|
400
396
|
message: res.error,
|
|
@@ -403,7 +399,7 @@ async function analyzeFieldsNoImages() {
|
|
|
403
399
|
});
|
|
404
400
|
console.error('Failed to analyze fields:', res.error);
|
|
405
401
|
isError.value = true;
|
|
406
|
-
isCriticalError.value = true;
|
|
402
|
+
//isCriticalError.value = true;
|
|
407
403
|
errorMessage.value = res.error;
|
|
408
404
|
} else {
|
|
409
405
|
res.result.forEach((item, idx) => {
|
|
@@ -427,9 +423,12 @@ async function analyzeFieldsNoImages() {
|
|
|
427
423
|
});
|
|
428
424
|
console.error('Failed to analyze fields:', error);
|
|
429
425
|
isError.value = true;
|
|
430
|
-
isCriticalError.value = true;
|
|
426
|
+
//isCriticalError.value = true;
|
|
431
427
|
errorMessage.value = `Failed to analyze fields. Please, try to re-run the action.`;
|
|
432
428
|
}
|
|
429
|
+
if(!props.meta.isFieldsForAnalizeFromImages) {
|
|
430
|
+
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => true);
|
|
431
|
+
}
|
|
433
432
|
}
|
|
434
433
|
|
|
435
434
|
|
|
@@ -443,19 +442,23 @@ async function saveData() {
|
|
|
443
442
|
try {
|
|
444
443
|
isLoading.value = true;
|
|
445
444
|
const [checkedItemsIDs, reqData] = await prepareDataForSave();
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
445
|
+
if (isImageGenerationError.value === false) {
|
|
446
|
+
const imagesToUpload = [];
|
|
447
|
+
for (const item of reqData) {
|
|
448
|
+
for (const [key, value] of Object.entries(item)) {
|
|
449
|
+
if(props.meta.outputImageFields?.includes(key)) {
|
|
450
|
+
if (!value) {
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
const p = uploadImage(value, item[primaryKey], key).then(result => {
|
|
454
|
+
item[key] = result;
|
|
455
|
+
});
|
|
456
|
+
imagesToUpload.push(p);
|
|
457
|
+
}
|
|
455
458
|
}
|
|
456
459
|
}
|
|
460
|
+
await Promise.all(imagesToUpload);
|
|
457
461
|
}
|
|
458
|
-
await Promise.all(imagesToUpload);
|
|
459
462
|
|
|
460
463
|
const res = await callAdminForthApi({
|
|
461
464
|
path: `/plugin/${props.meta.pluginInstanceId}/update_fields`,
|
|
@@ -463,6 +466,7 @@ async function saveData() {
|
|
|
463
466
|
body: {
|
|
464
467
|
selectedIds: checkedItemsIDs,
|
|
465
468
|
fields: reqData,
|
|
469
|
+
saveImages: !isImageGenerationError.value
|
|
466
470
|
},
|
|
467
471
|
});
|
|
468
472
|
|
|
@@ -508,7 +512,7 @@ async function generateImages() {
|
|
|
508
512
|
} catch (e) {
|
|
509
513
|
console.error('Error generating images:', e);
|
|
510
514
|
isError.value = true;
|
|
511
|
-
|
|
515
|
+
isImageGenerationError.value = true;
|
|
512
516
|
errorMessage.value = `Failed to generate images. Please, try to re-run the action.`;
|
|
513
517
|
}
|
|
514
518
|
isAiResponseReceivedImage.value = props.checkboxes.map(() => true);
|
|
@@ -519,7 +523,7 @@ async function generateImages() {
|
|
|
519
523
|
if (!res) {
|
|
520
524
|
error = 'Error generating images, something went wrong';
|
|
521
525
|
isError.value = true;
|
|
522
|
-
|
|
526
|
+
isImageGenerationError.value = true;
|
|
523
527
|
errorMessage.value = `Failed to generate images. Please, try to re-run the action.`;
|
|
524
528
|
}
|
|
525
529
|
|
|
@@ -528,9 +532,9 @@ async function generateImages() {
|
|
|
528
532
|
message: error,
|
|
529
533
|
variant: 'danger',
|
|
530
534
|
timeout: 'unlimited',
|
|
531
|
-
})
|
|
535
|
+
});
|
|
532
536
|
isError.value = true;
|
|
533
|
-
|
|
537
|
+
isImageGenerationError.value = true;
|
|
534
538
|
errorMessage.value = error;
|
|
535
539
|
} else {
|
|
536
540
|
res.result.forEach((item, idx) => {
|
package/custom/visionTable.vue
CHANGED
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
</div>
|
|
79
79
|
</div>
|
|
80
80
|
|
|
81
|
-
<div v-
|
|
81
|
+
<div v-if="isAiResponseReceivedImage[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])]">
|
|
82
82
|
<div v-if="isInColumnImage(n)">
|
|
83
83
|
<div class="mt-2 flex items-center justify-center gap-2">
|
|
84
84
|
<img
|
|
@@ -102,11 +102,11 @@
|
|
|
102
102
|
</div>
|
|
103
103
|
</div>
|
|
104
104
|
|
|
105
|
-
<div v-
|
|
105
|
+
<div v-if="!isAiResponseReceivedImage[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])] && isInColumnImage(n)">
|
|
106
106
|
<Skeleton type="image" class="w-20 h-20" />
|
|
107
107
|
</div>
|
|
108
108
|
|
|
109
|
-
<div v-
|
|
109
|
+
<div v-if="!isAiResponseReceivedAnalize[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])] && !isInColumnImage(n)">
|
|
110
110
|
<Skeleton class="w-full h-6" />
|
|
111
111
|
</div>
|
|
112
112
|
</template>
|
|
@@ -106,6 +106,7 @@ const openGenerationCarousel = ref([]);
|
|
|
106
106
|
const isLoading = ref(false);
|
|
107
107
|
const isError = ref(false);
|
|
108
108
|
const isCriticalError = ref(false);
|
|
109
|
+
const isImageGenerationError = ref(false);
|
|
109
110
|
const errorMessage = ref('');
|
|
110
111
|
const checkedCount = ref(0);
|
|
111
112
|
|
|
@@ -289,26 +290,27 @@ async function prepareDataForSave() {
|
|
|
289
290
|
.filter(item => item.isChecked === true)
|
|
290
291
|
.map(item => item[primaryKey]);
|
|
291
292
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
for (const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
293
|
+
if (isImageGenerationError.value !== true) {
|
|
294
|
+
const promises = [];
|
|
295
|
+
for (const item of checkedItems) {
|
|
296
|
+
for (const [key, value] of Object.entries(item)) {
|
|
297
|
+
if(props.meta.outputImageFields?.includes(key)) {
|
|
298
|
+
const p = convertImages(key, value).then(result => {
|
|
299
|
+
item[key] = result;
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
promises.push(p);
|
|
303
|
+
}
|
|
301
304
|
}
|
|
302
305
|
}
|
|
306
|
+
await Promise.all(promises);
|
|
303
307
|
}
|
|
304
|
-
await Promise.all(promises);
|
|
305
|
-
|
|
306
308
|
return [checkedItemsIDs, checkedItems];
|
|
307
309
|
}
|
|
308
310
|
|
|
309
311
|
async function convertImages(fieldName, img) {
|
|
310
312
|
let imgBlob;
|
|
311
|
-
if (img.startsWith('data:')) {
|
|
313
|
+
if (typeof img === 'string' && img.startsWith('data:')) {
|
|
312
314
|
const base64 = img.split(',')[1];
|
|
313
315
|
const mimeType = img.split(';')[0].split(':')[1];
|
|
314
316
|
const byteCharacters = atob(base64);
|
|
@@ -318,7 +320,7 @@ async function convertImages(fieldName, img) {
|
|
|
318
320
|
}
|
|
319
321
|
const byteArray = new Uint8Array(byteNumbers);
|
|
320
322
|
imgBlob = new Blob([byteArray], { type: mimeType });
|
|
321
|
-
} else {
|
|
323
|
+
} else if (typeof img === 'string') {
|
|
322
324
|
imgBlob = await fetch(
|
|
323
325
|
`/adminapi/v1/plugin/${props.meta.outputImagesPluginInstanceIds[fieldName]}/cors-proxy?url=${encodeURIComponent(img)}`
|
|
324
326
|
).then(res => { return res.blob() });
|
|
@@ -328,9 +330,8 @@ async function convertImages(fieldName, img) {
|
|
|
328
330
|
|
|
329
331
|
|
|
330
332
|
async function analyzeFields() {
|
|
333
|
+
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => false);
|
|
331
334
|
try {
|
|
332
|
-
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => false);
|
|
333
|
-
|
|
334
335
|
const res = await callAdminForthApi({
|
|
335
336
|
path: `/plugin/${props.meta.pluginInstanceId}/analyze`,
|
|
336
337
|
method: 'POST',
|
|
@@ -339,8 +340,6 @@ async function analyzeFields() {
|
|
|
339
340
|
},
|
|
340
341
|
});
|
|
341
342
|
|
|
342
|
-
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => true);
|
|
343
|
-
|
|
344
343
|
if (res?.error) {
|
|
345
344
|
adminforth.alert({
|
|
346
345
|
message: res.error,
|
|
@@ -350,7 +349,7 @@ async function analyzeFields() {
|
|
|
350
349
|
|
|
351
350
|
console.error('Failed to analyze image(s):', res.error);
|
|
352
351
|
isError.value = true;
|
|
353
|
-
isCriticalError.value = true;
|
|
352
|
+
//isCriticalError.value = true;
|
|
354
353
|
errorMessage.value = `Failed to fetch analyze image(s). Please, try to re-run the action.`;
|
|
355
354
|
} else {
|
|
356
355
|
res.result.forEach((item, idx) => {
|
|
@@ -375,16 +374,16 @@ async function analyzeFields() {
|
|
|
375
374
|
|
|
376
375
|
console.error('Failed to analyze image(s):', error);
|
|
377
376
|
isError.value = true;
|
|
378
|
-
isCriticalError.value = true;
|
|
377
|
+
//isCriticalError.value = true;
|
|
379
378
|
errorMessage.value = res.error;
|
|
380
379
|
}
|
|
380
|
+
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => true);
|
|
381
381
|
}
|
|
382
382
|
|
|
383
383
|
|
|
384
384
|
async function analyzeFieldsNoImages() {
|
|
385
|
+
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => false);
|
|
385
386
|
try {
|
|
386
|
-
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => false);
|
|
387
|
-
|
|
388
387
|
const res = await callAdminForthApi({
|
|
389
388
|
path: `/plugin/${props.meta.pluginInstanceId}/analyze_no_images`,
|
|
390
389
|
method: 'POST',
|
|
@@ -392,9 +391,6 @@ async function analyzeFieldsNoImages() {
|
|
|
392
391
|
selectedIds: props.checkboxes,
|
|
393
392
|
},
|
|
394
393
|
});
|
|
395
|
-
if(!props.meta.isFieldsForAnalizeFromImages) {
|
|
396
|
-
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => true);
|
|
397
|
-
}
|
|
398
394
|
if(res?.error) {
|
|
399
395
|
adminforth.alert({
|
|
400
396
|
message: res.error,
|
|
@@ -403,7 +399,7 @@ async function analyzeFieldsNoImages() {
|
|
|
403
399
|
});
|
|
404
400
|
console.error('Failed to analyze fields:', res.error);
|
|
405
401
|
isError.value = true;
|
|
406
|
-
isCriticalError.value = true;
|
|
402
|
+
//isCriticalError.value = true;
|
|
407
403
|
errorMessage.value = res.error;
|
|
408
404
|
} else {
|
|
409
405
|
res.result.forEach((item, idx) => {
|
|
@@ -427,9 +423,12 @@ async function analyzeFieldsNoImages() {
|
|
|
427
423
|
});
|
|
428
424
|
console.error('Failed to analyze fields:', error);
|
|
429
425
|
isError.value = true;
|
|
430
|
-
isCriticalError.value = true;
|
|
426
|
+
//isCriticalError.value = true;
|
|
431
427
|
errorMessage.value = `Failed to analyze fields. Please, try to re-run the action.`;
|
|
432
428
|
}
|
|
429
|
+
if(!props.meta.isFieldsForAnalizeFromImages) {
|
|
430
|
+
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => true);
|
|
431
|
+
}
|
|
433
432
|
}
|
|
434
433
|
|
|
435
434
|
|
|
@@ -443,19 +442,23 @@ async function saveData() {
|
|
|
443
442
|
try {
|
|
444
443
|
isLoading.value = true;
|
|
445
444
|
const [checkedItemsIDs, reqData] = await prepareDataForSave();
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
445
|
+
if (isImageGenerationError.value === false) {
|
|
446
|
+
const imagesToUpload = [];
|
|
447
|
+
for (const item of reqData) {
|
|
448
|
+
for (const [key, value] of Object.entries(item)) {
|
|
449
|
+
if(props.meta.outputImageFields?.includes(key)) {
|
|
450
|
+
if (!value) {
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
const p = uploadImage(value, item[primaryKey], key).then(result => {
|
|
454
|
+
item[key] = result;
|
|
455
|
+
});
|
|
456
|
+
imagesToUpload.push(p);
|
|
457
|
+
}
|
|
455
458
|
}
|
|
456
459
|
}
|
|
460
|
+
await Promise.all(imagesToUpload);
|
|
457
461
|
}
|
|
458
|
-
await Promise.all(imagesToUpload);
|
|
459
462
|
|
|
460
463
|
const res = await callAdminForthApi({
|
|
461
464
|
path: `/plugin/${props.meta.pluginInstanceId}/update_fields`,
|
|
@@ -463,6 +466,7 @@ async function saveData() {
|
|
|
463
466
|
body: {
|
|
464
467
|
selectedIds: checkedItemsIDs,
|
|
465
468
|
fields: reqData,
|
|
469
|
+
saveImages: !isImageGenerationError.value
|
|
466
470
|
},
|
|
467
471
|
});
|
|
468
472
|
|
|
@@ -508,7 +512,7 @@ async function generateImages() {
|
|
|
508
512
|
} catch (e) {
|
|
509
513
|
console.error('Error generating images:', e);
|
|
510
514
|
isError.value = true;
|
|
511
|
-
|
|
515
|
+
isImageGenerationError.value = true;
|
|
512
516
|
errorMessage.value = `Failed to generate images. Please, try to re-run the action.`;
|
|
513
517
|
}
|
|
514
518
|
isAiResponseReceivedImage.value = props.checkboxes.map(() => true);
|
|
@@ -519,7 +523,7 @@ async function generateImages() {
|
|
|
519
523
|
if (!res) {
|
|
520
524
|
error = 'Error generating images, something went wrong';
|
|
521
525
|
isError.value = true;
|
|
522
|
-
|
|
526
|
+
isImageGenerationError.value = true;
|
|
523
527
|
errorMessage.value = `Failed to generate images. Please, try to re-run the action.`;
|
|
524
528
|
}
|
|
525
529
|
|
|
@@ -528,9 +532,9 @@ async function generateImages() {
|
|
|
528
532
|
message: error,
|
|
529
533
|
variant: 'danger',
|
|
530
534
|
timeout: 'unlimited',
|
|
531
|
-
})
|
|
535
|
+
});
|
|
532
536
|
isError.value = true;
|
|
533
|
-
|
|
537
|
+
isImageGenerationError.value = true;
|
|
534
538
|
errorMessage.value = error;
|
|
535
539
|
} else {
|
|
536
540
|
res.result.forEach((item, idx) => {
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
</div>
|
|
79
79
|
</div>
|
|
80
80
|
|
|
81
|
-
<div v-
|
|
81
|
+
<div v-if="isAiResponseReceivedImage[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])]">
|
|
82
82
|
<div v-if="isInColumnImage(n)">
|
|
83
83
|
<div class="mt-2 flex items-center justify-center gap-2">
|
|
84
84
|
<img
|
|
@@ -102,11 +102,11 @@
|
|
|
102
102
|
</div>
|
|
103
103
|
</div>
|
|
104
104
|
|
|
105
|
-
<div v-
|
|
105
|
+
<div v-if="!isAiResponseReceivedImage[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])] && isInColumnImage(n)">
|
|
106
106
|
<Skeleton type="image" class="w-20 h-20" />
|
|
107
107
|
</div>
|
|
108
108
|
|
|
109
|
-
<div v-
|
|
109
|
+
<div v-if="!isAiResponseReceivedAnalize[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])] && !isInColumnImage(n)">
|
|
110
110
|
<Skeleton class="w-full h-6" />
|
|
111
111
|
</div>
|
|
112
112
|
</template>
|
package/dist/index.js
CHANGED
|
@@ -211,26 +211,29 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
211
211
|
const record = yield this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ(primaryKeyColumn.name, ID)]);
|
|
212
212
|
//recieve image URLs to analyze
|
|
213
213
|
const attachmentFiles = yield this.options.attachFiles({ record: record });
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
214
|
+
if (attachmentFiles.length !== 0) {
|
|
215
|
+
//create prompt for OpenAI
|
|
216
|
+
const compiledOutputFields = this.compileOutputFieldsTemplates(record);
|
|
217
|
+
const prompt = `Analyze the following image(s) and return a single JSON in format like: {'param1': 'value1', 'param2': 'value2'}.
|
|
218
|
+
Do NOT return array of objects. Do NOT include any Markdown, code blocks, explanations, or extra text. Only return valid JSON.
|
|
219
|
+
Each object must contain the following fields: ${JSON.stringify(compiledOutputFields)} Use the exact field names. If it's number field - return only number.
|
|
220
|
+
Image URLs:`;
|
|
221
|
+
//send prompt to OpenAI and get response
|
|
222
|
+
const chatResponse = yield this.options.visionAdapter.generate({ prompt, inputFileUrls: attachmentFiles });
|
|
223
|
+
const resp = chatResponse.response;
|
|
224
|
+
const topLevelError = chatResponse.error;
|
|
225
|
+
if (topLevelError || (resp === null || resp === void 0 ? void 0 : resp.error)) {
|
|
226
|
+
throw new Error(`ERROR: ${JSON.stringify(topLevelError || (resp === null || resp === void 0 ? void 0 : resp.error))}`);
|
|
227
|
+
}
|
|
228
|
+
const textOutput = (_f = (_e = (_d = (_c = (_b = (_a = resp === null || resp === void 0 ? void 0 : resp.output) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.content) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.text) !== null && _e !== void 0 ? _e : resp === null || resp === void 0 ? void 0 : resp.output_text) !== null && _f !== void 0 ? _f : (_j = (_h = (_g = resp === null || resp === void 0 ? void 0 : resp.choices) === null || _g === void 0 ? void 0 : _g[0]) === null || _h === void 0 ? void 0 : _h.message) === null || _j === void 0 ? void 0 : _j.content;
|
|
229
|
+
if (!textOutput || typeof textOutput !== 'string') {
|
|
230
|
+
throw new Error('Unexpected AI response format');
|
|
231
|
+
}
|
|
232
|
+
//parse response and update record
|
|
233
|
+
const resData = JSON.parse(textOutput);
|
|
234
|
+
return resData;
|
|
230
235
|
}
|
|
231
|
-
|
|
232
|
-
const resData = JSON.parse(textOutput);
|
|
233
|
-
return resData;
|
|
236
|
+
;
|
|
234
237
|
}));
|
|
235
238
|
const result = yield Promise.all(tasks);
|
|
236
239
|
return { result };
|
|
@@ -315,6 +318,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
315
318
|
if (isAllowedToSave.ok !== false) {
|
|
316
319
|
const selectedIds = body.selectedIds || [];
|
|
317
320
|
const fieldsToUpdate = body.fields || {};
|
|
321
|
+
const saveImages = body.saveImages;
|
|
318
322
|
const outputImageFields = [];
|
|
319
323
|
if (this.options.generateImages) {
|
|
320
324
|
for (const [key, value] of Object.entries(this.options.generateImages)) {
|
|
@@ -327,7 +331,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
327
331
|
for (const [key, value] of Object.entries(outputImageFields)) {
|
|
328
332
|
const columnPlugin = this.adminforth.activatedPlugins.find(p => p.resourceConfig.resourceId === this.resourceConfig.resourceId &&
|
|
329
333
|
p.pluginOptions.pathColumnName === value);
|
|
330
|
-
if (columnPlugin) {
|
|
334
|
+
if (columnPlugin && saveImages) {
|
|
331
335
|
if (columnPlugin.pluginOptions.storageAdapter.objectCanBeAccesedPublicly()) {
|
|
332
336
|
if (oldRecord[value]) {
|
|
333
337
|
// put tag to delete old file
|
|
@@ -339,7 +343,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
339
343
|
console.error(`Error setting tag to true for object ${oldRecord[value]}. File will not be auto-cleaned up`, e);
|
|
340
344
|
}
|
|
341
345
|
}
|
|
342
|
-
if (fieldsToUpdate[idx][key] !== null) {
|
|
346
|
+
if (fieldsToUpdate[idx][key] && fieldsToUpdate[idx][key] !== null) {
|
|
343
347
|
// remove tag from new file
|
|
344
348
|
// in this case we let it crash if it fails: this is a new file which just was uploaded.
|
|
345
349
|
yield columnPlugin.pluginOptions.storageAdapter.markKeyForNotDeletation(fieldsToUpdate[idx][value]);
|
|
@@ -429,28 +433,33 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
429
433
|
const fieldTasks = Object.keys(((_b = this.options) === null || _b === void 0 ? void 0 : _b.generateImages) || {}).map((key) => __awaiter(this, void 0, void 0, function* () {
|
|
430
434
|
const prompt = this.compileGenerationFieldTemplates(record)[key];
|
|
431
435
|
let images;
|
|
432
|
-
if (
|
|
433
|
-
|
|
434
|
-
images = `https://picsum.photos/200/300?random=${Math.floor(Math.random() * 1000)}`;
|
|
436
|
+
if (this.options.attachFiles && attachmentFiles.length === 0) {
|
|
437
|
+
return { key, images: [] };
|
|
435
438
|
}
|
|
436
439
|
else {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
+
if (STUB_MODE) {
|
|
441
|
+
yield new Promise((resolve) => setTimeout(resolve, 2000));
|
|
442
|
+
images = `https://picsum.photos/200/300?random=${Math.floor(Math.random() * 1000)}`;
|
|
440
443
|
}
|
|
441
444
|
else {
|
|
442
|
-
generationAdapter
|
|
443
|
-
|
|
445
|
+
let generationAdapter;
|
|
446
|
+
if (this.options.generateImages[key].adapter) {
|
|
447
|
+
generationAdapter = this.options.generateImages[key].adapter;
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
generationAdapter = this.options.imageGenerationAdapter;
|
|
451
|
+
``;
|
|
452
|
+
}
|
|
453
|
+
const resp = yield generationAdapter.generate({
|
|
454
|
+
prompt,
|
|
455
|
+
inputFiles: attachmentFiles,
|
|
456
|
+
n: 1,
|
|
457
|
+
size: this.options.generateImages[key].outputSize,
|
|
458
|
+
});
|
|
459
|
+
images = resp.imageURLs[0];
|
|
444
460
|
}
|
|
445
|
-
|
|
446
|
-
prompt,
|
|
447
|
-
inputFiles: attachmentFiles,
|
|
448
|
-
n: 1,
|
|
449
|
-
size: this.options.generateImages[key].outputSize,
|
|
450
|
-
});
|
|
451
|
-
images = resp.imageURLs[0];
|
|
461
|
+
return { key, images };
|
|
452
462
|
}
|
|
453
|
-
return { key, images };
|
|
454
463
|
}));
|
|
455
464
|
const fieldResults = yield Promise.all(fieldTasks);
|
|
456
465
|
const recordResult = {};
|
package/index.ts
CHANGED
|
@@ -237,31 +237,33 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
237
237
|
|
|
238
238
|
//recieve image URLs to analyze
|
|
239
239
|
const attachmentFiles = await this.options.attachFiles({ record: record });
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
240
|
+
if (attachmentFiles.length !== 0) {
|
|
241
|
+
//create prompt for OpenAI
|
|
242
|
+
const compiledOutputFields = this.compileOutputFieldsTemplates(record);
|
|
243
|
+
const prompt = `Analyze the following image(s) and return a single JSON in format like: {'param1': 'value1', 'param2': 'value2'}.
|
|
244
|
+
Do NOT return array of objects. Do NOT include any Markdown, code blocks, explanations, or extra text. Only return valid JSON.
|
|
245
|
+
Each object must contain the following fields: ${JSON.stringify(compiledOutputFields)} Use the exact field names. If it's number field - return only number.
|
|
246
|
+
Image URLs:`;
|
|
247
|
+
|
|
248
|
+
//send prompt to OpenAI and get response
|
|
249
|
+
const chatResponse = await this.options.visionAdapter.generate({ prompt, inputFileUrls: attachmentFiles });
|
|
250
|
+
|
|
251
|
+
const resp: any = (chatResponse as any).response;
|
|
252
|
+
const topLevelError = (chatResponse as any).error;
|
|
253
|
+
if (topLevelError || resp?.error) {
|
|
254
|
+
throw new Error(`ERROR: ${JSON.stringify(topLevelError || resp?.error)}`);
|
|
255
|
+
}
|
|
255
256
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
257
|
+
const textOutput = resp?.output?.[0]?.content?.[0]?.text ?? resp?.output_text ?? resp?.choices?.[0]?.message?.content;
|
|
258
|
+
if (!textOutput || typeof textOutput !== 'string') {
|
|
259
|
+
throw new Error('Unexpected AI response format');
|
|
260
|
+
}
|
|
260
261
|
|
|
261
|
-
|
|
262
|
-
|
|
262
|
+
//parse response and update record
|
|
263
|
+
const resData = JSON.parse(textOutput);
|
|
263
264
|
|
|
264
|
-
|
|
265
|
+
return resData;
|
|
266
|
+
};
|
|
265
267
|
});
|
|
266
268
|
|
|
267
269
|
const result = await Promise.all(tasks);
|
|
@@ -360,6 +362,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
360
362
|
if (isAllowedToSave.ok !== false) {
|
|
361
363
|
const selectedIds = body.selectedIds || [];
|
|
362
364
|
const fieldsToUpdate = body.fields || {};
|
|
365
|
+
const saveImages = body.saveImages;
|
|
363
366
|
const outputImageFields = [];
|
|
364
367
|
if (this.options.generateImages) {
|
|
365
368
|
for (const [key, value] of Object.entries(this.options.generateImages)) {
|
|
@@ -374,7 +377,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
374
377
|
p.resourceConfig!.resourceId === this.resourceConfig.resourceId &&
|
|
375
378
|
p.pluginOptions.pathColumnName === value
|
|
376
379
|
);
|
|
377
|
-
if (columnPlugin) {
|
|
380
|
+
if (columnPlugin && saveImages) {
|
|
378
381
|
if(columnPlugin.pluginOptions.storageAdapter.objectCanBeAccesedPublicly()) {
|
|
379
382
|
if (oldRecord[value]) {
|
|
380
383
|
// put tag to delete old file
|
|
@@ -385,7 +388,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
385
388
|
console.error(`Error setting tag to true for object ${oldRecord[value]}. File will not be auto-cleaned up`, e);
|
|
386
389
|
}
|
|
387
390
|
}
|
|
388
|
-
if (fieldsToUpdate[idx][key] !== null) {
|
|
391
|
+
if (fieldsToUpdate[idx][key] && fieldsToUpdate[idx][key] !== null) {
|
|
389
392
|
// remove tag from new file
|
|
390
393
|
// in this case we let it crash if it fails: this is a new file which just was uploaded.
|
|
391
394
|
await columnPlugin.pluginOptions.storageAdapter.markKeyForNotDeletation(fieldsToUpdate[idx][value]);
|
|
@@ -478,27 +481,31 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
478
481
|
const fieldTasks = Object.keys(this.options?.generateImages || {}).map(async (key) => {
|
|
479
482
|
const prompt = this.compileGenerationFieldTemplates(record)[key];
|
|
480
483
|
let images;
|
|
481
|
-
if (
|
|
482
|
-
|
|
483
|
-
images = `https://picsum.photos/200/300?random=${Math.floor(Math.random() * 1000)}`;
|
|
484
|
+
if (this.options.attachFiles && attachmentFiles.length === 0) {
|
|
485
|
+
return { key, images: [] };
|
|
484
486
|
} else {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
487
|
+
if (STUB_MODE) {
|
|
488
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
489
|
+
images = `https://picsum.photos/200/300?random=${Math.floor(Math.random() * 1000)}`;
|
|
488
490
|
} else {
|
|
489
|
-
generationAdapter
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
{
|
|
493
|
-
|
|
494
|
-
inputFiles: attachmentFiles,
|
|
495
|
-
n: 1,
|
|
496
|
-
size: this.options.generateImages[key].outputSize,
|
|
491
|
+
let generationAdapter;
|
|
492
|
+
if (this.options.generateImages[key].adapter) {
|
|
493
|
+
generationAdapter = this.options.generateImages[key].adapter;
|
|
494
|
+
} else {
|
|
495
|
+
generationAdapter = this.options.imageGenerationAdapter;``
|
|
497
496
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
497
|
+
const resp = await generationAdapter.generate(
|
|
498
|
+
{
|
|
499
|
+
prompt,
|
|
500
|
+
inputFiles: attachmentFiles,
|
|
501
|
+
n: 1,
|
|
502
|
+
size: this.options.generateImages[key].outputSize,
|
|
503
|
+
}
|
|
504
|
+
)
|
|
505
|
+
images = resp.imageURLs[0];
|
|
506
|
+
}
|
|
507
|
+
return { key, images };
|
|
508
|
+
}
|
|
502
509
|
});
|
|
503
510
|
|
|
504
511
|
const fieldResults = await Promise.all(fieldTasks);
|