@adminforth/bulk-ai-flow 1.1.5 → 1.2.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 +3 -2
- package/custom/imageGenerationCarousel.vue +462 -0
- package/custom/visionAction.vue +403 -80
- package/custom/visionTable.vue +56 -4
- package/dist/custom/imageGenerationCarousel.vue +462 -0
- package/dist/custom/visionAction.vue +403 -80
- package/dist/custom/visionTable.vue +56 -4
- package/dist/index.js +295 -17
- package/index.ts +346 -26
- package/package.json +1 -1
- package/types.ts +41 -3
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<template #cell:checkboxes="{ item }">
|
|
14
14
|
<div class="flex items-center justify-center">
|
|
15
15
|
<Checkbox
|
|
16
|
-
v-model="selected[tableColumnsIndexes.findIndex(el => el
|
|
16
|
+
v-model="selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])].isChecked"
|
|
17
17
|
/>
|
|
18
18
|
</div>
|
|
19
19
|
</template>
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
</template>
|
|
45
45
|
<!-- CUSTOM FIELD TEMPLATES -->
|
|
46
46
|
<template v-for="n in customFieldNames" :key="n" #[`cell:${n}`]="{ item, column }">
|
|
47
|
-
<div v-if="
|
|
47
|
+
<div v-if="isAiResponseReceivedAnalize[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])] && !isInColumnImage(n)">
|
|
48
48
|
<div v-if="isInColumnEnum(n)">
|
|
49
49
|
<Select
|
|
50
50
|
:options="convertColumnEnumToSelectOptions(props.meta.columnEnums, n)"
|
|
@@ -77,6 +77,35 @@
|
|
|
77
77
|
/>
|
|
78
78
|
</div>
|
|
79
79
|
</div>
|
|
80
|
+
|
|
81
|
+
<div v-else-if="isAiResponseReceivedImage[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])]">
|
|
82
|
+
<div v-if="isInColumnImage(n)">
|
|
83
|
+
<div class="mt-2 flex items-center justify-center gap-2">
|
|
84
|
+
<img
|
|
85
|
+
:src="selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
|
|
86
|
+
class="w-20 h-20 object-cover rounded cursor-pointer border hover:border-blue-500 transition"
|
|
87
|
+
@click="() => {openGenerationCarousel[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n] = true}"
|
|
88
|
+
/>
|
|
89
|
+
</div>
|
|
90
|
+
<div>
|
|
91
|
+
<GenerationCarousel
|
|
92
|
+
v-if="openGenerationCarousel[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
|
|
93
|
+
:images="selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
|
|
94
|
+
:recordId="item[primaryKey]"
|
|
95
|
+
:meta="props.meta"
|
|
96
|
+
:fieldName="n"
|
|
97
|
+
@error="handleError"
|
|
98
|
+
@close="openGenerationCarousel[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n] = false"
|
|
99
|
+
@selectImage="updateSelectedImage"
|
|
100
|
+
/>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<div v-else-if="isInColumnImage(n)">
|
|
106
|
+
<Skeleton type="image" class="w-20 h-20" />
|
|
107
|
+
</div>
|
|
108
|
+
|
|
80
109
|
<div v-else>
|
|
81
110
|
<Skeleton class="w-full h-6" />
|
|
82
111
|
</div>
|
|
@@ -89,6 +118,7 @@
|
|
|
89
118
|
import { ref, nextTick, watch } from 'vue'
|
|
90
119
|
import mediumZoom from 'medium-zoom'
|
|
91
120
|
import { Select, Input, Textarea, Table, Checkbox, Skeleton, Toggle } from '@/afcl'
|
|
121
|
+
import GenerationCarousel from './imageGenerationCarousel.vue'
|
|
92
122
|
|
|
93
123
|
const props = defineProps<{
|
|
94
124
|
meta: any,
|
|
@@ -97,13 +127,20 @@ const props = defineProps<{
|
|
|
97
127
|
customFieldNames: any,
|
|
98
128
|
tableColumnsIndexes: any,
|
|
99
129
|
selected: any,
|
|
100
|
-
|
|
101
|
-
|
|
130
|
+
isAiResponseReceivedAnalize: boolean[],
|
|
131
|
+
isAiResponseReceivedImage: boolean[],
|
|
132
|
+
primaryKey: any,
|
|
133
|
+
openGenerationCarousel: any
|
|
134
|
+
isError: boolean,
|
|
135
|
+
errorMessage: string
|
|
102
136
|
}>();
|
|
137
|
+
const emit = defineEmits(['error']);
|
|
138
|
+
|
|
103
139
|
|
|
104
140
|
const zoomedImage = ref(null)
|
|
105
141
|
const zoomedImg = ref(null)
|
|
106
142
|
|
|
143
|
+
|
|
107
144
|
function zoomImage(img) {
|
|
108
145
|
zoomedImage.value = img
|
|
109
146
|
}
|
|
@@ -131,6 +168,10 @@ function isInColumnEnum(key: string): boolean {
|
|
|
131
168
|
return true;
|
|
132
169
|
}
|
|
133
170
|
|
|
171
|
+
function isInColumnImage(key: string): boolean {
|
|
172
|
+
return props.meta.outputImageFields?.includes(key) || false;
|
|
173
|
+
}
|
|
174
|
+
|
|
134
175
|
function convertColumnEnumToSelectOptions(columnEnumArray: any[], key: string) {
|
|
135
176
|
const col = columnEnumArray.find(c => c.name === key);
|
|
136
177
|
if (!col) return [];
|
|
@@ -140,4 +181,15 @@ function convertColumnEnumToSelectOptions(columnEnumArray: any[], key: string) {
|
|
|
140
181
|
}));
|
|
141
182
|
}
|
|
142
183
|
|
|
184
|
+
function updateSelectedImage(image: string, id: any, fieldName: string) {
|
|
185
|
+
props.selected[props.tableColumnsIndexes.findIndex(el => el[props.primaryKey] === id)][fieldName] = image;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function handleError({ isError, errorMessage }) {
|
|
189
|
+
emit('error', {
|
|
190
|
+
isError,
|
|
191
|
+
errorMessage
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
143
195
|
</script>
|
package/dist/index.js
CHANGED
|
@@ -9,10 +9,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { AdminForthPlugin, Filters } from "adminforth";
|
|
11
11
|
import Handlebars from 'handlebars';
|
|
12
|
+
import { RateLimiter } from "adminforth";
|
|
12
13
|
export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
13
14
|
constructor(options) {
|
|
14
15
|
super(options, import.meta.url);
|
|
15
16
|
this.options = options;
|
|
17
|
+
// for calculating average time
|
|
18
|
+
this.totalCalls = 0;
|
|
19
|
+
this.totalDuration = 0;
|
|
16
20
|
}
|
|
17
21
|
// Compile Handlebars templates in outputFields using record fields as context
|
|
18
22
|
compileOutputFieldsTemplates(record) {
|
|
@@ -28,6 +32,41 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
28
32
|
}
|
|
29
33
|
return compiled;
|
|
30
34
|
}
|
|
35
|
+
compileOutputFieldsTemplatesNoImage(record) {
|
|
36
|
+
const compiled = {};
|
|
37
|
+
for (const [key, templateStr] of Object.entries(this.options.fillPlainFields)) {
|
|
38
|
+
try {
|
|
39
|
+
const tpl = Handlebars.compile(String(templateStr));
|
|
40
|
+
compiled[key] = tpl(record);
|
|
41
|
+
}
|
|
42
|
+
catch (_a) {
|
|
43
|
+
compiled[key] = String(templateStr);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return compiled;
|
|
47
|
+
}
|
|
48
|
+
compileGenerationFieldTemplates(record) {
|
|
49
|
+
const compiled = {};
|
|
50
|
+
for (const key in this.options.generateImages) {
|
|
51
|
+
try {
|
|
52
|
+
const tpl = Handlebars.compile(String(this.options.generateImages[key].prompt));
|
|
53
|
+
compiled[key] = tpl(record);
|
|
54
|
+
}
|
|
55
|
+
catch (_a) {
|
|
56
|
+
compiled[key] = String(this.options.generateImages[key].prompt);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return compiled;
|
|
60
|
+
}
|
|
61
|
+
checkRateLimit(fieldNameRateLimit, headers) {
|
|
62
|
+
if (fieldNameRateLimit) {
|
|
63
|
+
// rate limit
|
|
64
|
+
const { error } = RateLimiter.checkRateLimit(this.pluginInstanceId, fieldNameRateLimit, this.adminforth.auth.getClientIp(headers));
|
|
65
|
+
if (error) {
|
|
66
|
+
return { error: "Rate limit exceeded" };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
31
70
|
modifyResourceConfig(adminforth, resourceConfig) {
|
|
32
71
|
const _super = Object.create(null, {
|
|
33
72
|
modifyResourceConfig: { get: () => super.modifyResourceConfig }
|
|
@@ -37,21 +76,63 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
37
76
|
//check if options names are provided
|
|
38
77
|
const columns = this.resourceConfig.columns;
|
|
39
78
|
let columnEnums = [];
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
79
|
+
if (this.options.fillFieldsFromImages) {
|
|
80
|
+
if (!this.options.attachFiles) {
|
|
81
|
+
throw new Error('⚠️ attachFiles function must be provided in options when fillFieldsFromImages is used');
|
|
82
|
+
}
|
|
83
|
+
if (!this.options.visionAdapter) {
|
|
84
|
+
throw new Error('⚠️ visionAdapter must be provided in options when fillFieldsFromImages is used');
|
|
85
|
+
}
|
|
86
|
+
for (const [key, value] of Object.entries((this.options.fillFieldsFromImages))) {
|
|
87
|
+
const column = columns.find(c => c.name.toLowerCase() === key.toLowerCase());
|
|
88
|
+
if (column) {
|
|
89
|
+
if (column.enum) {
|
|
90
|
+
this.options.fillFieldsFromImages[key] = `${value} Select ${key} from the list (USE ONLY VALUE FIELD. USE ONLY VALUES FROM THIS LIST): ${JSON.stringify(column.enum)}`;
|
|
91
|
+
columnEnums.push({
|
|
92
|
+
name: key,
|
|
93
|
+
enum: column.enum,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
throw new Error(`⚠️ No column found for key "${key}"`);
|
|
49
99
|
}
|
|
50
100
|
}
|
|
51
|
-
|
|
52
|
-
|
|
101
|
+
}
|
|
102
|
+
if (this.options.fillPlainFields) {
|
|
103
|
+
if (!this.options.textCompleteAdapter) {
|
|
104
|
+
throw new Error('⚠️ textCompleteAdapter must be provided in options when fillPlainFields is used');
|
|
105
|
+
}
|
|
106
|
+
for (const [key, value] of Object.entries((this.options.fillPlainFields))) {
|
|
107
|
+
const column = columns.find(c => c.name.toLowerCase() === key.toLowerCase());
|
|
108
|
+
if (column) {
|
|
109
|
+
if (column.enum) {
|
|
110
|
+
this.options.fillPlainFields[key] = `${value} Select ${key} from the list (USE ONLY VALUE FIELD. USE ONLY VALUES FROM THIS LIST): ${JSON.stringify(column.enum)}`;
|
|
111
|
+
columnEnums.push({
|
|
112
|
+
name: key,
|
|
113
|
+
enum: column.enum,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
throw new Error(`⚠️ No column found for key "${key}"`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (this.options.generateImages && !this.options.imageGenerationAdapter) {
|
|
123
|
+
for (const [key, value] of Object.entries(this.options.generateImages)) {
|
|
124
|
+
if (!this.options.generateImages[key].adapter) {
|
|
125
|
+
throw new Error(`⚠️ No image generation adapter found for key "${key}"`);
|
|
126
|
+
}
|
|
53
127
|
}
|
|
54
128
|
}
|
|
129
|
+
const outputImageFields = [];
|
|
130
|
+
if (this.options.generateImages) {
|
|
131
|
+
for (const [key, value] of Object.entries(this.options.generateImages)) {
|
|
132
|
+
outputImageFields.push(key);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const outputImagesPluginInstanceIds = {};
|
|
55
136
|
//check if Upload plugin is installed on all attachment fields
|
|
56
137
|
if (this.options.generateImages) {
|
|
57
138
|
for (const [key, value] of Object.entries(this.options.generateImages)) {
|
|
@@ -70,18 +151,25 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
70
151
|
Please configure adapter in such way that it will store objects publicly (e.g. for S3 use 'public-read' ACL).
|
|
71
152
|
`);
|
|
72
153
|
}
|
|
73
|
-
|
|
154
|
+
outputImagesPluginInstanceIds[key] = plugin.pluginInstanceId;
|
|
74
155
|
}
|
|
75
156
|
}
|
|
157
|
+
const outputFields = Object.assign(Object.assign(Object.assign({}, this.options.fillFieldsFromImages), this.options.fillPlainFields), (this.options.generateImages || {}));
|
|
76
158
|
const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
|
|
77
159
|
const pageInjection = {
|
|
78
160
|
file: this.componentPath('visionAction.vue'),
|
|
79
161
|
meta: {
|
|
80
162
|
pluginInstanceId: this.pluginInstanceId,
|
|
81
|
-
outputFields:
|
|
163
|
+
outputFields: outputFields,
|
|
82
164
|
actionName: this.options.actionName,
|
|
83
165
|
columnEnums: columnEnums,
|
|
166
|
+
outputImageFields: outputImageFields,
|
|
167
|
+
outputPlainFields: this.options.fillPlainFields,
|
|
84
168
|
primaryKey: primaryKeyColumn.name,
|
|
169
|
+
outputImagesPluginInstanceIds: outputImagesPluginInstanceIds,
|
|
170
|
+
isFieldsForAnalizeFromImages: this.options.fillFieldsFromImages ? Object.keys(this.options.fillFieldsFromImages).length > 0 : false,
|
|
171
|
+
isFieldsForAnalizePlain: this.options.fillPlainFields ? Object.keys(this.options.fillPlainFields).length > 0 : false,
|
|
172
|
+
isImageGeneration: this.options.generateImages ? Object.keys(this.options.generateImages).length > 0 : false
|
|
85
173
|
}
|
|
86
174
|
};
|
|
87
175
|
if (!this.resourceConfig.options.pageInjections) {
|
|
@@ -139,6 +227,36 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
139
227
|
return { result };
|
|
140
228
|
})
|
|
141
229
|
});
|
|
230
|
+
server.endpoint({
|
|
231
|
+
method: 'POST',
|
|
232
|
+
path: `/plugin/${this.pluginInstanceId}/analyze_no_images`,
|
|
233
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers }) {
|
|
234
|
+
const selectedIds = body.selectedIds || [];
|
|
235
|
+
const tasks = selectedIds.map((ID) => __awaiter(this, void 0, void 0, function* () {
|
|
236
|
+
// Fetch the record using the provided ID
|
|
237
|
+
const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
|
|
238
|
+
const record = yield this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ(primaryKeyColumn.name, ID)]);
|
|
239
|
+
//create prompt for OpenAI
|
|
240
|
+
const compiledOutputFields = this.compileOutputFieldsTemplatesNoImage(record);
|
|
241
|
+
const prompt = `Analyze the following fields and return a single JSON in format like: {'param1': 'value1', 'param2': 'value2'}.
|
|
242
|
+
Do NOT return array of objects. Do NOT include any Markdown, code blocks, explanations, or extra text. Only return valid JSON.
|
|
243
|
+
Each object must contain the following fields: ${JSON.stringify(compiledOutputFields)} Use the exact field names.
|
|
244
|
+
If it's number field - return only number.`;
|
|
245
|
+
//send prompt to OpenAI and get response
|
|
246
|
+
const { content: chatResponse, finishReason } = yield this.options.textCompleteAdapter.complete(prompt, [], 500);
|
|
247
|
+
const resp = chatResponse.response;
|
|
248
|
+
const topLevelError = chatResponse.error;
|
|
249
|
+
if (topLevelError || (resp === null || resp === void 0 ? void 0 : resp.error)) {
|
|
250
|
+
throw new Error(`ERROR: ${JSON.stringify(topLevelError || (resp === null || resp === void 0 ? void 0 : resp.error))}`);
|
|
251
|
+
}
|
|
252
|
+
//parse response and update record
|
|
253
|
+
const resData = JSON.parse(chatResponse);
|
|
254
|
+
return resData;
|
|
255
|
+
}));
|
|
256
|
+
const result = yield Promise.all(tasks);
|
|
257
|
+
return { result };
|
|
258
|
+
})
|
|
259
|
+
});
|
|
142
260
|
server.endpoint({
|
|
143
261
|
method: 'POST',
|
|
144
262
|
path: `/plugin/${this.pluginInstanceId}/get_records`,
|
|
@@ -161,7 +279,9 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
161
279
|
let images = [];
|
|
162
280
|
if (body.body.record) {
|
|
163
281
|
for (const record of body.body.record) {
|
|
164
|
-
|
|
282
|
+
if (this.options.attachFiles) {
|
|
283
|
+
images.push(yield this.options.attachFiles({ record: record }));
|
|
284
|
+
}
|
|
165
285
|
}
|
|
166
286
|
}
|
|
167
287
|
return {
|
|
@@ -175,12 +295,170 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
175
295
|
handler: (body) => __awaiter(this, void 0, void 0, function* () {
|
|
176
296
|
const selectedIds = body.body.selectedIds || [];
|
|
177
297
|
const fieldsToUpdate = body.body.fields || {};
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
298
|
+
const outputImageFields = [];
|
|
299
|
+
if (this.options.generateImages) {
|
|
300
|
+
for (const [key, value] of Object.entries(this.options.generateImages)) {
|
|
301
|
+
outputImageFields.push(key);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
|
|
305
|
+
const updates = selectedIds.map((ID, idx) => __awaiter(this, void 0, void 0, function* () {
|
|
306
|
+
const oldRecord = yield this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ(primaryKeyColumn.name, ID)]);
|
|
307
|
+
for (const [key, value] of Object.entries(outputImageFields)) {
|
|
308
|
+
const columnPlugin = this.adminforth.activatedPlugins.find(p => p.resourceConfig.resourceId === this.resourceConfig.resourceId &&
|
|
309
|
+
p.pluginOptions.pathColumnName === value);
|
|
310
|
+
if (columnPlugin) {
|
|
311
|
+
if (columnPlugin.pluginOptions.storageAdapter.objectCanBeAccesedPublicly()) {
|
|
312
|
+
if (oldRecord[value]) {
|
|
313
|
+
// put tag to delete old file
|
|
314
|
+
try {
|
|
315
|
+
yield columnPlugin.pluginOptions.storageAdapter.markKeyForDeletation(oldRecord[value]);
|
|
316
|
+
}
|
|
317
|
+
catch (e) {
|
|
318
|
+
// file might be e.g. already deleted, so we catch error
|
|
319
|
+
console.error(`Error setting tag to true for object ${oldRecord[value]}. File will not be auto-cleaned up`, e);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (fieldsToUpdate[idx][key] !== null) {
|
|
323
|
+
// remove tag from new file
|
|
324
|
+
// in this case we let it crash if it fails: this is a new file which just was uploaded.
|
|
325
|
+
yield columnPlugin.pluginOptions.storageAdapter.markKeyForNotDeletation(fieldsToUpdate[idx][value]);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return this.adminforth.resource(this.resourceConfig.resourceId).update(ID, fieldsToUpdate[idx]);
|
|
331
|
+
}));
|
|
181
332
|
yield Promise.all(updates);
|
|
182
333
|
return { ok: true };
|
|
183
334
|
})
|
|
184
335
|
});
|
|
336
|
+
server.endpoint({
|
|
337
|
+
method: 'POST',
|
|
338
|
+
path: `/plugin/${this.pluginInstanceId}/regenerate_images`,
|
|
339
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, headers }) {
|
|
340
|
+
var _b;
|
|
341
|
+
const Id = body.recordId || [];
|
|
342
|
+
const prompt = body.prompt || '';
|
|
343
|
+
const fieldName = body.fieldName || '';
|
|
344
|
+
if (this.checkRateLimit(this.options.generateImages[fieldName].rateLimit, headers)) {
|
|
345
|
+
return { error: "Rate limit exceeded" };
|
|
346
|
+
}
|
|
347
|
+
const start = +new Date();
|
|
348
|
+
const STUB_MODE = false;
|
|
349
|
+
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)]);
|
|
350
|
+
let attachmentFiles;
|
|
351
|
+
if (!this.options.attachFiles) {
|
|
352
|
+
attachmentFiles = [];
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
attachmentFiles = yield this.options.attachFiles({ record });
|
|
356
|
+
}
|
|
357
|
+
const images = yield Promise.all((new Array(this.options.generateImages[fieldName].countToGenerate)).fill(0).map(() => __awaiter(this, void 0, void 0, function* () {
|
|
358
|
+
if (STUB_MODE) {
|
|
359
|
+
yield new Promise((resolve) => setTimeout(resolve, 2000));
|
|
360
|
+
return `https://picsum.photos/200/300?random=${Math.floor(Math.random() * 1000)}`;
|
|
361
|
+
}
|
|
362
|
+
let generationAdapter;
|
|
363
|
+
if (this.options.generateImages[fieldName].adapter) {
|
|
364
|
+
generationAdapter = this.options.generateImages[fieldName].adapter;
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
generationAdapter = this.options.imageGenerationAdapter;
|
|
368
|
+
}
|
|
369
|
+
const resp = yield generationAdapter.generate({
|
|
370
|
+
prompt,
|
|
371
|
+
inputFiles: attachmentFiles,
|
|
372
|
+
n: 1,
|
|
373
|
+
size: this.options.generateImages[fieldName].outputSize,
|
|
374
|
+
});
|
|
375
|
+
return resp.imageURLs[0];
|
|
376
|
+
})));
|
|
377
|
+
this.totalCalls++;
|
|
378
|
+
this.totalDuration += (+new Date() - start) / 1000;
|
|
379
|
+
return { images };
|
|
380
|
+
})
|
|
381
|
+
});
|
|
382
|
+
server.endpoint({
|
|
383
|
+
method: 'POST',
|
|
384
|
+
path: `/plugin/${this.pluginInstanceId}/initial_image_generate`,
|
|
385
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, headers }) {
|
|
386
|
+
const selectedIds = body.selectedIds || [];
|
|
387
|
+
const STUB_MODE = false;
|
|
388
|
+
if (this.checkRateLimit(this.options.bulkGenerationRateLimit, headers)) {
|
|
389
|
+
return { error: "Rate limit exceeded" };
|
|
390
|
+
}
|
|
391
|
+
const start = +new Date();
|
|
392
|
+
const tasks = selectedIds.map((ID) => __awaiter(this, void 0, void 0, function* () {
|
|
393
|
+
var _a, _b;
|
|
394
|
+
const record = yield this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ((_a = this.resourceConfig.columns.find(c => c.primaryKey)) === null || _a === void 0 ? void 0 : _a.name, ID)]);
|
|
395
|
+
let attachmentFiles;
|
|
396
|
+
if (!this.options.attachFiles) {
|
|
397
|
+
attachmentFiles = [];
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
attachmentFiles = yield this.options.attachFiles({ record });
|
|
401
|
+
}
|
|
402
|
+
const fieldTasks = Object.keys(((_b = this.options) === null || _b === void 0 ? void 0 : _b.generateImages) || {}).map((key) => __awaiter(this, void 0, void 0, function* () {
|
|
403
|
+
const prompt = this.compileGenerationFieldTemplates(record)[key];
|
|
404
|
+
let images;
|
|
405
|
+
if (STUB_MODE) {
|
|
406
|
+
yield new Promise((resolve) => setTimeout(resolve, 2000));
|
|
407
|
+
images = `https://picsum.photos/200/300?random=${Math.floor(Math.random() * 1000)}`;
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
let generationAdapter;
|
|
411
|
+
if (this.options.generateImages[key].adapter) {
|
|
412
|
+
generationAdapter = this.options.generateImages[key].adapter;
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
generationAdapter = this.options.imageGenerationAdapter;
|
|
416
|
+
``;
|
|
417
|
+
}
|
|
418
|
+
const resp = yield generationAdapter.generate({
|
|
419
|
+
prompt,
|
|
420
|
+
inputFiles: attachmentFiles,
|
|
421
|
+
n: 1,
|
|
422
|
+
size: this.options.generateImages[key].outputSize,
|
|
423
|
+
});
|
|
424
|
+
images = resp.imageURLs[0];
|
|
425
|
+
}
|
|
426
|
+
return { key, images };
|
|
427
|
+
}));
|
|
428
|
+
const fieldResults = yield Promise.all(fieldTasks);
|
|
429
|
+
const recordResult = {};
|
|
430
|
+
fieldResults.forEach(({ key, images }) => {
|
|
431
|
+
recordResult[key] = images;
|
|
432
|
+
});
|
|
433
|
+
return recordResult;
|
|
434
|
+
}));
|
|
435
|
+
const result = yield Promise.all(tasks);
|
|
436
|
+
this.totalCalls++;
|
|
437
|
+
this.totalDuration += (+new Date() - start) / 1000;
|
|
438
|
+
return { result };
|
|
439
|
+
})
|
|
440
|
+
});
|
|
441
|
+
server.endpoint({
|
|
442
|
+
method: 'POST',
|
|
443
|
+
path: `/plugin/${this.pluginInstanceId}/get_generation_prompts`,
|
|
444
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, headers }) {
|
|
445
|
+
var _b;
|
|
446
|
+
const Id = body.recordId || [];
|
|
447
|
+
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)]);
|
|
448
|
+
const compiledGenerationOptions = this.compileGenerationFieldTemplates(record);
|
|
449
|
+
return { generationOptions: compiledGenerationOptions };
|
|
450
|
+
})
|
|
451
|
+
});
|
|
452
|
+
server.endpoint({
|
|
453
|
+
method: 'GET',
|
|
454
|
+
path: `/plugin/${this.pluginInstanceId}/averageDuration`,
|
|
455
|
+
handler: () => __awaiter(this, void 0, void 0, function* () {
|
|
456
|
+
return {
|
|
457
|
+
totalCalls: this.totalCalls,
|
|
458
|
+
totalDuration: this.totalDuration,
|
|
459
|
+
averageDuration: this.totalCalls ? this.totalDuration / this.totalCalls : null,
|
|
460
|
+
};
|
|
461
|
+
})
|
|
462
|
+
});
|
|
185
463
|
}
|
|
186
464
|
}
|