@adminforth/bulk-ai-flow 1.8.1 → 1.9.0

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/dist/index.js CHANGED
@@ -10,6 +10,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { AdminForthPlugin, Filters } from "adminforth";
11
11
  import Handlebars from 'handlebars';
12
12
  import { RateLimiter } from "adminforth";
13
+ import { randomUUID } from "crypto";
14
+ const STUB_MODE = false;
15
+ const jobs = new Map();
13
16
  export default class BulkAiFlowPlugin extends AdminForthPlugin {
14
17
  constructor(options) {
15
18
  super(options, import.meta.url);
@@ -51,11 +54,257 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
51
54
  }
52
55
  }
53
56
  }
57
+ analyze_image(jobId, recordId, adminUser, headers) {
58
+ return __awaiter(this, void 0, void 0, function* () {
59
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
60
+ const selectedId = recordId;
61
+ let isError = false;
62
+ if (typeof ((_a = this.options.rateLimits) === null || _a === void 0 ? void 0 : _a.fillFieldsFromImages) === 'string') {
63
+ if (this.checkRateLimit("fillFieldsFromImages", this.options.rateLimits.fillFieldsFromImages, headers)) {
64
+ jobs.set(jobId, { status: 'failed', error: "Rate limit exceeded" });
65
+ return { error: "Rate limit exceeded" };
66
+ }
67
+ }
68
+ // Fetch the record using the provided ID
69
+ const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
70
+ const record = yield this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ(primaryKeyColumn.name, selectedId)]);
71
+ //recieve image URLs to analyze
72
+ const attachmentFiles = yield this.options.attachFiles({ record: record });
73
+ if (STUB_MODE) {
74
+ yield new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * 8000) + 1000));
75
+ jobs.set(jobId, { status: 'completed', result: {} });
76
+ return {};
77
+ }
78
+ else if (attachmentFiles.length !== 0) {
79
+ //create prompt for OpenAI
80
+ const compiledOutputFields = this.compileOutputFieldsTemplates(record);
81
+ const prompt = `Analyze the following image(s) and return a single JSON in format like: {'param1': 'value1', 'param2': 'value2'}.
82
+ Do NOT return array of objects. Do NOT include any Markdown, code blocks, explanations, or extra text. Only return valid JSON.
83
+ Each object must contain the following fields: ${JSON.stringify(compiledOutputFields)} Use the exact field names. If it's number field - return only number.
84
+ Image URLs:`;
85
+ //send prompt to OpenAI and get response
86
+ let chatResponse;
87
+ try {
88
+ chatResponse = yield this.options.visionAdapter.generate({ prompt, inputFileUrls: attachmentFiles });
89
+ }
90
+ catch (e) {
91
+ isError = true;
92
+ jobs.set(jobId, { status: 'failed', error: 'AI provider refused to analyze images' });
93
+ return { ok: false, error: 'AI provider refused to analyze images' };
94
+ }
95
+ if (!isError) {
96
+ const resp = chatResponse.response;
97
+ const topLevelError = chatResponse.error;
98
+ if (topLevelError || (resp === null || resp === void 0 ? void 0 : resp.error)) {
99
+ jobs.set(jobId, { status: 'failed', error: `ERROR: ${JSON.stringify(topLevelError || (resp === null || resp === void 0 ? void 0 : resp.error))}` });
100
+ }
101
+ const textOutput = (_g = (_f = (_e = (_d = (_c = (_b = resp === null || resp === void 0 ? void 0 : resp.output) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.content) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.text) !== null && _f !== void 0 ? _f : resp === null || resp === void 0 ? void 0 : resp.output_text) !== null && _g !== void 0 ? _g : (_k = (_j = (_h = resp === null || resp === void 0 ? void 0 : resp.choices) === null || _h === void 0 ? void 0 : _h[0]) === null || _j === void 0 ? void 0 : _j.message) === null || _k === void 0 ? void 0 : _k.content;
102
+ if (!textOutput || typeof textOutput !== 'string') {
103
+ jobs.set(jobId, { status: 'failed', error: 'Unexpected AI response format' });
104
+ }
105
+ //parse response and update record
106
+ const resData = JSON.parse(textOutput);
107
+ const result = resData;
108
+ jobs.set(jobId, { status: 'completed', result });
109
+ return { ok: true };
110
+ }
111
+ }
112
+ ;
113
+ });
114
+ }
115
+ analyzeNoImages(jobId, recordId, adminUser, headers) {
116
+ return __awaiter(this, void 0, void 0, function* () {
117
+ var _a;
118
+ const selectedId = recordId;
119
+ let isError = false;
120
+ if (typeof ((_a = this.options.rateLimits) === null || _a === void 0 ? void 0 : _a.fillPlainFields) === 'string') {
121
+ if (this.checkRateLimit("fillPlainFields", this.options.rateLimits.fillPlainFields, headers)) {
122
+ jobs.set(jobId, { status: 'failed', error: "Rate limit exceeded" });
123
+ return { error: "Rate limit exceeded" };
124
+ }
125
+ }
126
+ if (STUB_MODE) {
127
+ yield new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * 20000) + 1000));
128
+ jobs.set(jobId, { status: 'completed', result: {} });
129
+ return {};
130
+ }
131
+ else {
132
+ const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
133
+ const record = yield this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ(primaryKeyColumn.name, selectedId)]);
134
+ const compiledOutputFields = this.compileOutputFieldsTemplatesNoImage(record);
135
+ const prompt = `Analyze the following fields and return a single JSON in format like: {'param1': 'value1', 'param2': 'value2'}.
136
+ Do NOT return array of objects. Do NOT include any Markdown, code blocks, explanations, or extra text. Only return valid JSON.
137
+ Each object must contain the following fields: ${JSON.stringify(compiledOutputFields)} Use the exact field names.
138
+ If it's number field - return only number.`;
139
+ //send prompt to OpenAI and get response
140
+ const numberOfTokens = this.options.fillPlainFieldsMaxTokens ? this.options.fillPlainFieldsMaxTokens : 1000;
141
+ let resp;
142
+ try {
143
+ const { content: chatResponse } = yield this.options.textCompleteAdapter.complete(prompt, [], numberOfTokens);
144
+ resp = chatResponse.response;
145
+ const topLevelError = chatResponse.error;
146
+ if (topLevelError || (resp === null || resp === void 0 ? void 0 : resp.error)) {
147
+ isError = true;
148
+ jobs.set(jobId, { status: 'failed', error: `ERROR: ${JSON.stringify(topLevelError || (resp === null || resp === void 0 ? void 0 : resp.error))}` });
149
+ }
150
+ resp = chatResponse;
151
+ }
152
+ catch (e) {
153
+ isError = true;
154
+ jobs.set(jobId, { status: 'failed', error: 'AI provider refused to fill fields' });
155
+ return { ok: false, error: 'AI provider refused to fill fields' };
156
+ }
157
+ const resData = JSON.parse(resp);
158
+ const result = resData;
159
+ jobs.set(jobId, { status: 'completed', result });
160
+ return { ok: true };
161
+ }
162
+ });
163
+ }
164
+ initialImageGenerate(jobId, recordId, adminUser, headers) {
165
+ return __awaiter(this, void 0, void 0, function* () {
166
+ var _a, _b, _c;
167
+ const selectedId = recordId;
168
+ let isError = false;
169
+ if (typeof ((_a = this.options.rateLimits) === null || _a === void 0 ? void 0 : _a.generateImages) === 'string') {
170
+ if (this.checkRateLimit("generateImages", this.options.rateLimits.generateImages, headers)) {
171
+ jobs.set(jobId, { status: 'failed', error: "Rate limit exceeded" });
172
+ return { error: "Rate limit exceeded" };
173
+ }
174
+ }
175
+ const start = +new Date();
176
+ 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, selectedId)]);
177
+ let attachmentFiles;
178
+ if (!this.options.attachFiles) {
179
+ attachmentFiles = [];
180
+ }
181
+ else {
182
+ attachmentFiles = yield this.options.attachFiles({ record });
183
+ }
184
+ const fieldTasks = Object.keys(((_c = this.options) === null || _c === void 0 ? void 0 : _c.generateImages) || {}).map((key) => __awaiter(this, void 0, void 0, function* () {
185
+ const prompt = this.compileGenerationFieldTemplates(record)[key];
186
+ let images;
187
+ if (this.options.attachFiles && attachmentFiles.length === 0) {
188
+ jobs.set(jobId, { status: 'failed', error: "No attachment files found" });
189
+ return { key, images: [] };
190
+ }
191
+ else {
192
+ if (STUB_MODE) {
193
+ yield new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * 20000) + 1000));
194
+ images = `https://pic.re/image`;
195
+ }
196
+ else {
197
+ let generationAdapter;
198
+ if (this.options.generateImages[key].adapter) {
199
+ generationAdapter = this.options.generateImages[key].adapter;
200
+ }
201
+ else {
202
+ generationAdapter = this.options.imageGenerationAdapter;
203
+ }
204
+ let resp;
205
+ try {
206
+ resp = yield generationAdapter.generate({
207
+ prompt,
208
+ inputFiles: attachmentFiles,
209
+ n: 1,
210
+ size: this.options.generateImages[key].outputSize,
211
+ });
212
+ }
213
+ catch (e) {
214
+ jobs.set(jobId, { status: 'failed', error: "AI provider refused to generate image" });
215
+ isError = true;
216
+ return { key, images: [] };
217
+ }
218
+ images = resp.imageURLs[0];
219
+ }
220
+ return { key, images };
221
+ }
222
+ }));
223
+ const fieldResults = yield Promise.all(fieldTasks);
224
+ const recordResult = {};
225
+ fieldResults.forEach(({ key, images }) => {
226
+ recordResult[key] = images;
227
+ });
228
+ const result = recordResult;
229
+ this.totalCalls++;
230
+ this.totalDuration += (+new Date() - start) / 1000;
231
+ if (!isError) {
232
+ jobs.set(jobId, { status: 'completed', result });
233
+ return { ok: true };
234
+ }
235
+ else {
236
+ return { ok: false, error: 'Error during image generation' };
237
+ }
238
+ });
239
+ }
240
+ regenerateImage(jobId, recordId, fieldName, prompt, adminUser, headers) {
241
+ return __awaiter(this, void 0, void 0, function* () {
242
+ var _a;
243
+ const Id = recordId;
244
+ let isError = false;
245
+ if (this.checkRateLimit(fieldName, this.options.generateImages[fieldName].rateLimit, headers)) {
246
+ jobs.set(jobId, { status: 'failed', error: "Rate limit exceeded" });
247
+ return { error: "Rate limit exceeded" };
248
+ }
249
+ const start = +new Date();
250
+ 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)]);
251
+ let attachmentFiles;
252
+ if (!this.options.attachFiles) {
253
+ attachmentFiles = [];
254
+ }
255
+ else {
256
+ attachmentFiles = yield this.options.attachFiles({ record });
257
+ }
258
+ const images = yield Promise.all((new Array(this.options.generateImages[fieldName].countToGenerate)).fill(0).map(() => __awaiter(this, void 0, void 0, function* () {
259
+ if (this.options.attachFiles && attachmentFiles.length === 0) {
260
+ jobs.set(jobId, { status: 'failed', error: "No attachment files found" });
261
+ return null;
262
+ }
263
+ if (STUB_MODE) {
264
+ yield new Promise((resolve) => setTimeout(resolve, 2000));
265
+ jobs.set(jobId, { status: 'completed', result: {} });
266
+ return `https://pic.re/image`;
267
+ }
268
+ let generationAdapter;
269
+ if (this.options.generateImages[fieldName].adapter) {
270
+ generationAdapter = this.options.generateImages[fieldName].adapter;
271
+ }
272
+ else {
273
+ generationAdapter = this.options.imageGenerationAdapter;
274
+ }
275
+ let resp;
276
+ try {
277
+ resp = yield generationAdapter.generate({
278
+ prompt,
279
+ inputFiles: attachmentFiles,
280
+ n: 1,
281
+ size: this.options.generateImages[fieldName].outputSize,
282
+ });
283
+ }
284
+ catch (e) {
285
+ jobs.set(jobId, { status: 'failed', error: "AI provider refused to generate image" });
286
+ isError = true;
287
+ return [];
288
+ }
289
+ return resp.imageURLs[0];
290
+ })));
291
+ this.totalCalls++;
292
+ this.totalDuration += (+new Date() - start) / 1000;
293
+ if (!isError) {
294
+ jobs.set(jobId, { status: 'completed', result: { [fieldName]: images } });
295
+ return { ok: true };
296
+ }
297
+ else {
298
+ return { ok: false, error: 'Error during image generation' };
299
+ }
300
+ });
301
+ }
54
302
  modifyResourceConfig(adminforth, resourceConfig) {
55
303
  const _super = Object.create(null, {
56
304
  modifyResourceConfig: { get: () => super.modifyResourceConfig }
57
305
  });
58
306
  return __awaiter(this, void 0, void 0, function* () {
307
+ var _a, _b, _c, _d;
59
308
  _super.modifyResourceConfig.call(this, adminforth, resourceConfig);
60
309
  //check if options names are provided
61
310
  const columns = this.resourceConfig.columns;
@@ -116,6 +365,13 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
116
365
  isFieldsForAnalizePlain: this.options.fillPlainFields ? Object.keys(this.options.fillPlainFields).length > 0 : false,
117
366
  isImageGeneration: this.options.generateImages ? Object.keys(this.options.generateImages).length > 0 : false,
118
367
  isAttachFiles: this.options.attachFiles ? true : false,
368
+ disabledWhenNoCheckboxes: true,
369
+ refreshRates: {
370
+ fillFieldsFromImages: ((_a = this.options.refreshRates) === null || _a === void 0 ? void 0 : _a.fillFieldsFromImages) || 2000,
371
+ fillPlainFields: ((_b = this.options.refreshRates) === null || _b === void 0 ? void 0 : _b.fillPlainFields) || 1000,
372
+ generateImages: ((_c = this.options.refreshRates) === null || _c === void 0 ? void 0 : _c.generateImages) || 5000,
373
+ regenerateImages: ((_d = this.options.refreshRates) === null || _d === void 0 ? void 0 : _d.regenerateImages) || 5000,
374
+ }
119
375
  }
120
376
  };
121
377
  if (!resourceConfig.options.pageInjections) {
@@ -191,86 +447,6 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
191
447
  return `${this.pluginOptions.actionName}`;
192
448
  }
193
449
  setupEndpoints(server) {
194
- server.endpoint({
195
- method: 'POST',
196
- path: `/plugin/${this.pluginInstanceId}/analyze`,
197
- handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers }) {
198
- var _b;
199
- const selectedIds = body.selectedIds || [];
200
- if (typeof ((_b = this.options.rateLimits) === null || _b === void 0 ? void 0 : _b.fillFieldsFromImages) === 'string') {
201
- if (this.checkRateLimit("fillFieldsFromImages", this.options.rateLimits.fillFieldsFromImages, headers)) {
202
- return { error: "Rate limit exceeded" };
203
- }
204
- }
205
- const tasks = selectedIds.map((ID) => __awaiter(this, void 0, void 0, function* () {
206
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
207
- // Fetch the record using the provided ID
208
- const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
209
- const record = yield this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ(primaryKeyColumn.name, ID)]);
210
- //recieve image URLs to analyze
211
- const attachmentFiles = yield this.options.attachFiles({ record: record });
212
- if (attachmentFiles.length !== 0) {
213
- //create prompt for OpenAI
214
- const compiledOutputFields = this.compileOutputFieldsTemplates(record);
215
- const prompt = `Analyze the following image(s) and return a single JSON in format like: {'param1': 'value1', 'param2': 'value2'}.
216
- Do NOT return array of objects. Do NOT include any Markdown, code blocks, explanations, or extra text. Only return valid JSON.
217
- Each object must contain the following fields: ${JSON.stringify(compiledOutputFields)} Use the exact field names. If it's number field - return only number.
218
- Image URLs:`;
219
- //send prompt to OpenAI and get response
220
- const chatResponse = yield this.options.visionAdapter.generate({ prompt, inputFileUrls: attachmentFiles });
221
- const resp = chatResponse.response;
222
- const topLevelError = chatResponse.error;
223
- if (topLevelError || (resp === null || resp === void 0 ? void 0 : resp.error)) {
224
- throw new Error(`ERROR: ${JSON.stringify(topLevelError || (resp === null || resp === void 0 ? void 0 : resp.error))}`);
225
- }
226
- 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;
227
- if (!textOutput || typeof textOutput !== 'string') {
228
- throw new Error('Unexpected AI response format');
229
- }
230
- //parse response and update record
231
- const resData = JSON.parse(textOutput);
232
- return resData;
233
- }
234
- ;
235
- }));
236
- const result = yield Promise.all(tasks);
237
- return { result };
238
- })
239
- });
240
- server.endpoint({
241
- method: 'POST',
242
- path: `/plugin/${this.pluginInstanceId}/analyze_no_images`,
243
- handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers }) {
244
- var _b;
245
- const selectedIds = body.selectedIds || [];
246
- if (typeof ((_b = this.options.rateLimits) === null || _b === void 0 ? void 0 : _b.fillPlainFields) === 'string') {
247
- if (this.checkRateLimit("fillPlainFields", this.options.rateLimits.fillPlainFields, headers)) {
248
- return { error: "Rate limit exceeded" };
249
- }
250
- }
251
- const tasks = selectedIds.map((ID) => __awaiter(this, void 0, void 0, function* () {
252
- const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
253
- const record = yield this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ(primaryKeyColumn.name, ID)]);
254
- const compiledOutputFields = this.compileOutputFieldsTemplatesNoImage(record);
255
- const prompt = `Analyze the following fields and return a single JSON in format like: {'param1': 'value1', 'param2': 'value2'}.
256
- Do NOT return array of objects. Do NOT include any Markdown, code blocks, explanations, or extra text. Only return valid JSON.
257
- Each object must contain the following fields: ${JSON.stringify(compiledOutputFields)} Use the exact field names.
258
- If it's number field - return only number.`;
259
- //send prompt to OpenAI and get response
260
- const numberOfTokens = this.options.fillPlainFieldsMaxTokens ? this.options.fillPlainFieldsMaxTokens : 1000;
261
- const { content: chatResponse } = yield this.options.textCompleteAdapter.complete(prompt, [], numberOfTokens);
262
- const resp = chatResponse.response;
263
- const topLevelError = chatResponse.error;
264
- if (topLevelError || (resp === null || resp === void 0 ? void 0 : resp.error)) {
265
- throw new Error(`ERROR: ${JSON.stringify(topLevelError || (resp === null || resp === void 0 ? void 0 : resp.error))}`);
266
- }
267
- const resData = JSON.parse(chatResponse);
268
- return resData;
269
- }));
270
- const result = yield Promise.all(tasks);
271
- return { result };
272
- })
273
- });
274
450
  server.endpoint({
275
451
  method: 'POST',
276
452
  path: `/plugin/${this.pluginInstanceId}/get_records`,
@@ -385,121 +561,6 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
385
561
  }
386
562
  })
387
563
  });
388
- server.endpoint({
389
- method: 'POST',
390
- path: `/plugin/${this.pluginInstanceId}/regenerate_images`,
391
- handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, headers }) {
392
- var _b;
393
- const Id = body.recordId || [];
394
- const prompt = body.prompt || '';
395
- const fieldName = body.fieldName || '';
396
- if (this.checkRateLimit(fieldName, this.options.generateImages[fieldName].rateLimit, headers)) {
397
- return { error: "Rate limit exceeded" };
398
- }
399
- const start = +new Date();
400
- const STUB_MODE = false;
401
- 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)]);
402
- let attachmentFiles;
403
- if (!this.options.attachFiles) {
404
- attachmentFiles = [];
405
- }
406
- else {
407
- attachmentFiles = yield this.options.attachFiles({ record });
408
- }
409
- const images = yield Promise.all((new Array(this.options.generateImages[fieldName].countToGenerate)).fill(0).map(() => __awaiter(this, void 0, void 0, function* () {
410
- if (this.options.attachFiles && attachmentFiles.length === 0) {
411
- return null;
412
- }
413
- if (STUB_MODE) {
414
- yield new Promise((resolve) => setTimeout(resolve, 2000));
415
- return `https://picsum.photos/200/300?random=${Math.floor(Math.random() * 1000)}`;
416
- }
417
- let generationAdapter;
418
- if (this.options.generateImages[fieldName].adapter) {
419
- generationAdapter = this.options.generateImages[fieldName].adapter;
420
- }
421
- else {
422
- generationAdapter = this.options.imageGenerationAdapter;
423
- }
424
- const resp = yield generationAdapter.generate({
425
- prompt,
426
- inputFiles: attachmentFiles,
427
- n: 1,
428
- size: this.options.generateImages[fieldName].outputSize,
429
- });
430
- return resp.imageURLs[0];
431
- })));
432
- this.totalCalls++;
433
- this.totalDuration += (+new Date() - start) / 1000;
434
- return { images };
435
- })
436
- });
437
- server.endpoint({
438
- method: 'POST',
439
- path: `/plugin/${this.pluginInstanceId}/initial_image_generate`,
440
- handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, headers }) {
441
- var _b;
442
- const selectedIds = body.selectedIds || [];
443
- const STUB_MODE = false;
444
- if (typeof ((_b = this.options.rateLimits) === null || _b === void 0 ? void 0 : _b.generateImages) === 'string') {
445
- if (this.checkRateLimit("generateImages", this.options.rateLimits.generateImages, headers)) {
446
- return { error: "Rate limit exceeded" };
447
- }
448
- }
449
- const start = +new Date();
450
- const tasks = selectedIds.map((ID) => __awaiter(this, void 0, void 0, function* () {
451
- var _a, _b;
452
- 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)]);
453
- let attachmentFiles;
454
- if (!this.options.attachFiles) {
455
- attachmentFiles = [];
456
- }
457
- else {
458
- attachmentFiles = yield this.options.attachFiles({ record });
459
- }
460
- const fieldTasks = Object.keys(((_b = this.options) === null || _b === void 0 ? void 0 : _b.generateImages) || {}).map((key) => __awaiter(this, void 0, void 0, function* () {
461
- const prompt = this.compileGenerationFieldTemplates(record)[key];
462
- let images;
463
- if (this.options.attachFiles && attachmentFiles.length === 0) {
464
- return { key, images: [] };
465
- }
466
- else {
467
- if (STUB_MODE) {
468
- yield new Promise((resolve) => setTimeout(resolve, 2000));
469
- images = `https://picsum.photos/200/300?random=${Math.floor(Math.random() * 1000)}`;
470
- }
471
- else {
472
- let generationAdapter;
473
- if (this.options.generateImages[key].adapter) {
474
- generationAdapter = this.options.generateImages[key].adapter;
475
- }
476
- else {
477
- generationAdapter = this.options.imageGenerationAdapter;
478
- }
479
- const resp = yield generationAdapter.generate({
480
- prompt,
481
- inputFiles: attachmentFiles,
482
- n: 1,
483
- size: this.options.generateImages[key].outputSize,
484
- });
485
- images = resp.imageURLs[0];
486
- }
487
- return { key, images };
488
- }
489
- }));
490
- const fieldResults = yield Promise.all(fieldTasks);
491
- const recordResult = {};
492
- fieldResults.forEach(({ key, images }) => {
493
- recordResult[key] = images;
494
- });
495
- return recordResult;
496
- }));
497
- const result = yield Promise.all(tasks);
498
- this.totalCalls++;
499
- this.totalDuration += (+new Date() - start) / 1000;
500
- return { result };
501
- })
502
- });
503
564
  server.endpoint({
504
565
  method: 'POST',
505
566
  path: `/plugin/${this.pluginInstanceId}/get_generation_prompts`,
@@ -522,5 +583,59 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
522
583
  };
523
584
  })
524
585
  });
586
+ server.endpoint({
587
+ method: 'POST',
588
+ path: `/plugin/${this.pluginInstanceId}/create-job`,
589
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers }) {
590
+ const { actionType, recordId } = body;
591
+ const jobId = randomUUID();
592
+ jobs.set(jobId, { status: "in_progress" });
593
+ if (!actionType) {
594
+ jobs.set(jobId, { status: "failed", error: "Missing action type" });
595
+ return { error: "Missing action type" };
596
+ }
597
+ if (!recordId) {
598
+ jobs.set(jobId, { status: "failed", error: "Missing record id" });
599
+ return { error: "Missing record id" };
600
+ }
601
+ switch (actionType) {
602
+ case 'generate_images':
603
+ setTimeout(() => __awaiter(this, void 0, void 0, function* () { return yield this.initialImageGenerate(jobId, recordId, adminUser, headers); }), 100);
604
+ break;
605
+ case 'analyze_no_images':
606
+ setTimeout(() => __awaiter(this, void 0, void 0, function* () { return yield this.analyzeNoImages(jobId, recordId, adminUser, headers); }), 100);
607
+ break;
608
+ case 'analyze':
609
+ setTimeout(() => __awaiter(this, void 0, void 0, function* () { return yield this.analyze_image(jobId, recordId, adminUser, headers); }), 100);
610
+ break;
611
+ case 'regenerate_images':
612
+ if (!body.prompt || !body.fieldName) {
613
+ jobs.set(jobId, { status: "failed", error: "Missing prompt or field name" });
614
+ break;
615
+ }
616
+ setTimeout(() => __awaiter(this, void 0, void 0, function* () { return yield this.regenerateImage(jobId, recordId, body.fieldName, body.prompt, adminUser, headers); }), 100);
617
+ break;
618
+ default:
619
+ jobs.set(jobId, { status: "failed", error: "Unknown action type" });
620
+ }
621
+ setTimeout(() => jobs.delete(jobId), 300000);
622
+ return { ok: true, jobId };
623
+ })
624
+ });
625
+ server.endpoint({
626
+ method: 'POST',
627
+ path: `/plugin/${this.pluginInstanceId}/get-job-status`,
628
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers }) {
629
+ const jobId = body.jobId;
630
+ if (!jobId) {
631
+ return { error: "Can't find job id" };
632
+ }
633
+ const job = jobs.get(jobId);
634
+ if (!job) {
635
+ return { error: "Job not found" };
636
+ }
637
+ return { ok: true, job };
638
+ })
639
+ });
525
640
  }
526
641
  }