Skip to content

Commit cef2c7b

Browse files
committed
fix: fix image generation and save for field without image
1 parent 95a81ee commit cef2c7b

File tree

2 files changed

+52
-43
lines changed

2 files changed

+52
-43
lines changed

custom/visionAction.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ async function prepareDataForSave() {
310310
311311
async function convertImages(fieldName, img) {
312312
let imgBlob;
313-
if (img.startsWith('data:')) {
313+
if (typeof img === 'string' && img.startsWith('data:')) {
314314
const base64 = img.split(',')[1];
315315
const mimeType = img.split(';')[0].split(':')[1];
316316
const byteCharacters = atob(base64);
@@ -320,7 +320,7 @@ async function convertImages(fieldName, img) {
320320
}
321321
const byteArray = new Uint8Array(byteNumbers);
322322
imgBlob = new Blob([byteArray], { type: mimeType });
323-
} else {
323+
} else if (typeof img === 'string') {
324324
imgBlob = await fetch(
325325
`/adminapi/v1/plugin/${props.meta.outputImagesPluginInstanceIds[fieldName]}/cors-proxy?url=${encodeURIComponent(img)}`
326326
).then(res => { return res.blob() });
@@ -447,6 +447,9 @@ async function saveData() {
447447
for (const item of reqData) {
448448
for (const [key, value] of Object.entries(item)) {
449449
if(props.meta.outputImageFields?.includes(key)) {
450+
if (!value) {
451+
continue;
452+
}
450453
const p = uploadImage(value, item[primaryKey], key).then(result => {
451454
item[key] = result;
452455
});

index.ts

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -237,31 +237,33 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
237237

238238
//recieve image URLs to analyze
239239
const attachmentFiles = await this.options.attachFiles({ record: record });
240-
//create prompt for OpenAI
241-
const compiledOutputFields = this.compileOutputFieldsTemplates(record);
242-
const prompt = `Analyze the following image(s) and return a single JSON in format like: {'param1': 'value1', 'param2': 'value2'}.
243-
Do NOT return array of objects. Do NOT include any Markdown, code blocks, explanations, or extra text. Only return valid JSON.
244-
Each object must contain the following fields: ${JSON.stringify(compiledOutputFields)} Use the exact field names. If it's number field - return only number.
245-
Image URLs:`;
246-
247-
//send prompt to OpenAI and get response
248-
const chatResponse = await this.options.visionAdapter.generate({ prompt, inputFileUrls: attachmentFiles });
249-
250-
const resp: any = (chatResponse as any).response;
251-
const topLevelError = (chatResponse as any).error;
252-
if (topLevelError || resp?.error) {
253-
throw new Error(`ERROR: ${JSON.stringify(topLevelError || resp?.error)}`);
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+
}
255256

256-
const textOutput = resp?.output?.[0]?.content?.[0]?.text ?? resp?.output_text ?? resp?.choices?.[0]?.message?.content;
257-
if (!textOutput || typeof textOutput !== 'string') {
258-
throw new Error('Unexpected AI response format');
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+
}
260261

261-
//parse response and update record
262-
const resData = JSON.parse(textOutput);
262+
//parse response and update record
263+
const resData = JSON.parse(textOutput);
263264

264-
return resData;
265+
return resData;
266+
};
265267
});
266268

267269
const result = await Promise.all(tasks);
@@ -386,7 +388,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
386388
console.error(`Error setting tag to true for object ${oldRecord[value]}. File will not be auto-cleaned up`, e);
387389
}
388390
}
389-
if (fieldsToUpdate[idx][key] !== null) {
391+
if (fieldsToUpdate[idx][key] && fieldsToUpdate[idx][key] !== null) {
390392
// remove tag from new file
391393
// in this case we let it crash if it fails: this is a new file which just was uploaded.
392394
await columnPlugin.pluginOptions.storageAdapter.markKeyForNotDeletation(fieldsToUpdate[idx][value]);
@@ -479,27 +481,31 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
479481
const fieldTasks = Object.keys(this.options?.generateImages || {}).map(async (key) => {
480482
const prompt = this.compileGenerationFieldTemplates(record)[key];
481483
let images;
482-
if (STUB_MODE) {
483-
await new Promise((resolve) => setTimeout(resolve, 2000));
484-
images = `https://picsum.photos/200/300?random=${Math.floor(Math.random() * 1000)}`;
484+
if (this.options.attachFiles && attachmentFiles.length === 0) {
485+
return { key, images: [] };
485486
} else {
486-
let generationAdapter;
487-
if (this.options.generateImages[key].adapter) {
488-
generationAdapter = this.options.generateImages[key].adapter;
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)}`;
489490
} else {
490-
generationAdapter = this.options.imageGenerationAdapter;``
491-
}
492-
const resp = await generationAdapter.generate(
493-
{
494-
prompt,
495-
inputFiles: attachmentFiles,
496-
n: 1,
497-
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;``
498496
}
499-
)
500-
images = resp.imageURLs[0];
501-
}
502-
return { key, images };
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+
}
503509
});
504510

505511
const fieldResults = await Promise.all(fieldTasks);

0 commit comments

Comments
 (0)