@adminforth/bulk-ai-flow 1.7.4 → 1.8.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/README.md +7 -1
- package/build.log +5 -5
- package/custom/{imageGenerationCarousel.vue → ImageGenerationCarousel.vue} +18 -14
- package/custom/{visionAction.vue → VisionAction.vue} +57 -75
- package/{dist/custom/visionTable.vue → custom/VisionTable.vue} +41 -27
- package/dist/custom/{imageGenerationCarousel.vue → ImageGenerationCarousel.vue} +18 -14
- package/dist/custom/{visionAction.vue → VisionAction.vue} +57 -75
- package/{custom/visionTable.vue → dist/custom/VisionTable.vue} +41 -27
- package/dist/index.js +99 -60
- package/index.ts +111 -86
- package/package.json +1 -1
- package/types.ts +37 -1
package/index.ts
CHANGED
|
@@ -73,58 +73,30 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
73
73
|
const columns = this.resourceConfig.columns;
|
|
74
74
|
let columnEnums = [];
|
|
75
75
|
if (this.options.fillFieldsFromImages) {
|
|
76
|
-
if (!this.options.attachFiles) {
|
|
77
|
-
throw new Error('⚠️ attachFiles function must be provided in options when fillFieldsFromImages is used');
|
|
78
|
-
}
|
|
79
|
-
if (!this.options.visionAdapter) {
|
|
80
|
-
throw new Error('⚠️ visionAdapter must be provided in options when fillFieldsFromImages is used');
|
|
81
|
-
}
|
|
82
|
-
|
|
83
76
|
for (const [key, value] of Object.entries((this.options.fillFieldsFromImages ))) {
|
|
84
77
|
const column = columns.find(c => c.name.toLowerCase() === key.toLowerCase());
|
|
85
|
-
if (column) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
} else {
|
|
94
|
-
throw new Error(`⚠️ No column found for key "${key}"`);
|
|
78
|
+
if (column && column.enum) {
|
|
79
|
+
(this.options.fillFieldsFromImages as any)[key] = `${value} Select ${key} from the list (USE ONLY VALUE FIELD. USE ONLY VALUES FROM THIS LIST): ${JSON.stringify(column.enum)}`;
|
|
80
|
+
columnEnums.push({
|
|
81
|
+
name: key,
|
|
82
|
+
enum: column.enum,
|
|
83
|
+
});
|
|
95
84
|
}
|
|
96
85
|
}
|
|
97
86
|
}
|
|
98
87
|
|
|
99
88
|
if (this.options.fillPlainFields) {
|
|
100
|
-
if (!this.options.textCompleteAdapter) {
|
|
101
|
-
throw new Error('⚠️ textCompleteAdapter must be provided in options when fillPlainFields is used');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
89
|
for (const [key, value] of Object.entries((this.options.fillPlainFields))) {
|
|
105
90
|
const column = columns.find(c => c.name.toLowerCase() === key.toLowerCase());
|
|
106
|
-
if (column) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
} else {
|
|
115
|
-
throw new Error(`⚠️ No column found for key "${key}"`);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (this.options.generateImages && !this.options.imageGenerationAdapter) {
|
|
121
|
-
for (const [key, value] of Object.entries(this.options.generateImages)) {
|
|
122
|
-
if (!this.options.generateImages[key].adapter) {
|
|
123
|
-
throw new Error(`⚠️ No image generation adapter found for key "${key}"`);
|
|
91
|
+
if (column && column.enum) {
|
|
92
|
+
(this.options.fillPlainFields as any)[key] = `${value} Select ${key} from the list (USE ONLY VALUE FIELD. USE ONLY VALUES FROM THIS LIST): ${JSON.stringify(column.enum)}`;
|
|
93
|
+
columnEnums.push({
|
|
94
|
+
name: key,
|
|
95
|
+
enum: column.enum,
|
|
96
|
+
});
|
|
124
97
|
}
|
|
125
98
|
}
|
|
126
|
-
}
|
|
127
|
-
|
|
99
|
+
}
|
|
128
100
|
|
|
129
101
|
const outputImageFields = [];
|
|
130
102
|
if (this.options.generateImages) {
|
|
@@ -136,25 +108,11 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
136
108
|
//check if Upload plugin is installed on all attachment fields
|
|
137
109
|
if (this.options.generateImages) {
|
|
138
110
|
for (const [key, value] of Object.entries(this.options.generateImages)) {
|
|
139
|
-
const column = columns.find(c => c.name.toLowerCase() === key.toLowerCase());
|
|
140
|
-
if (!column) {
|
|
141
|
-
throw new Error(`⚠️ No column found for key "${key}"`);
|
|
142
|
-
}
|
|
143
111
|
const plugin = adminforth.activatedPlugins.find(p =>
|
|
144
112
|
p.resourceConfig!.resourceId === this.resourceConfig.resourceId &&
|
|
145
113
|
p.pluginOptions.pathColumnName === key
|
|
146
114
|
);
|
|
147
|
-
|
|
148
|
-
throw new Error(`Plugin for attachment field '${key}' not found in resource '${this.resourceConfig.resourceId}', please check if Upload Plugin is installed on the field ${key}`);
|
|
149
|
-
}
|
|
150
|
-
if (!plugin.pluginOptions.storageAdapter.objectCanBeAccesedPublicly()) {
|
|
151
|
-
throw new Error(`Upload Plugin for attachment field '${key}' in resource '${this.resourceConfig.resourceId}'
|
|
152
|
-
uses adapter which is not configured to store objects in public way, so it will produce only signed private URLs which can not be used in HTML text of blog posts.
|
|
153
|
-
Please configure adapter in such way that it will store objects publicly (e.g. for S3 use 'public-read' ACL).
|
|
154
|
-
`);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
outputImagesPluginInstanceIds[key] = plugin.pluginInstanceId;
|
|
115
|
+
outputImagesPluginInstanceIds[key] = plugin.pluginInstanceId;
|
|
158
116
|
}
|
|
159
117
|
}
|
|
160
118
|
|
|
@@ -168,7 +126,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
168
126
|
const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
|
|
169
127
|
|
|
170
128
|
const pageInjection = {
|
|
171
|
-
file: this.componentPath('
|
|
129
|
+
file: this.componentPath('VisionAction.vue'),
|
|
172
130
|
meta: {
|
|
173
131
|
pluginInstanceId: this.pluginInstanceId,
|
|
174
132
|
outputFields: outputFields,
|
|
@@ -199,18 +157,71 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
199
157
|
}
|
|
200
158
|
|
|
201
159
|
validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource) {
|
|
202
|
-
|
|
160
|
+
const columns = this.resourceConfig.columns;
|
|
161
|
+
if (this.options.fillFieldsFromImages) {
|
|
162
|
+
if (!this.options.attachFiles) {
|
|
163
|
+
throw new Error('⚠️ attachFiles function must be provided when fillFieldsFromImages is used');
|
|
164
|
+
}
|
|
165
|
+
if (!this.options.visionAdapter) {
|
|
166
|
+
throw new Error('⚠️ visionAdapter must be provided when fillFieldsFromImages is used');
|
|
167
|
+
}
|
|
168
|
+
for (const key of Object.keys(this.options.fillFieldsFromImages)) {
|
|
169
|
+
const column = columns.find(c => c.name.toLowerCase() === key.toLowerCase());
|
|
170
|
+
if (!column) {
|
|
171
|
+
throw new Error(`⚠️ No column found for key "${key}"`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (this.options.fillPlainFields) {
|
|
176
|
+
if (!this.options.textCompleteAdapter) {
|
|
177
|
+
throw new Error('⚠️ textCompleteAdapter must be provided when fillPlainFields is used');
|
|
178
|
+
}
|
|
179
|
+
for (const key of Object.keys(this.options.fillPlainFields)) {
|
|
180
|
+
const column = columns.find(c => c.name.toLowerCase() === key.toLowerCase());
|
|
181
|
+
if (!column) {
|
|
182
|
+
throw new Error(`⚠️ No column found for key "${key}"`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (this.options.generateImages) {
|
|
187
|
+
for (const key of Object.keys(this.options.generateImages)) {
|
|
188
|
+
const column = columns.find(c => c.name.toLowerCase() === key.toLowerCase());
|
|
189
|
+
if (!column) {
|
|
190
|
+
throw new Error(`⚠️ No column found for key "${key}"`);
|
|
191
|
+
}
|
|
192
|
+
const perKeyAdapter = this.options.generateImages[key].adapter;
|
|
193
|
+
if (!perKeyAdapter && !this.options.imageGenerationAdapter) {
|
|
194
|
+
throw new Error(`⚠️ No image generation adapter provided for key "${key}"`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const plugin = adminforth.activatedPlugins.find(p =>
|
|
198
|
+
p.resourceConfig!.resourceId === this.resourceConfig.resourceId &&
|
|
199
|
+
p.pluginOptions.pathColumnName === key
|
|
200
|
+
);
|
|
201
|
+
if (!plugin) {
|
|
202
|
+
throw new Error(`Plugin for attachment field '${key}' not found in resource '${this.resourceConfig.resourceId}', please check if Upload Plugin is installed on the field ${key}`);
|
|
203
|
+
}
|
|
204
|
+
if (!plugin.pluginOptions || !plugin.pluginOptions.storageAdapter) {
|
|
205
|
+
throw new Error(`Upload Plugin for attachment field '${key}' in resource '${this.resourceConfig.resourceId}' is missing a storageAdapter configuration.`);
|
|
206
|
+
}
|
|
207
|
+
if (typeof plugin.pluginOptions.storageAdapter.objectCanBeAccesedPublicly !== 'function') {
|
|
208
|
+
throw new Error(`Upload Plugin for attachment field '${key}' in resource '${this.resourceConfig.resourceId}' uses a storage adapter without 'objectCanBeAccesedPublicly' method.`);
|
|
209
|
+
}
|
|
210
|
+
if (!plugin.pluginOptions.storageAdapter.objectCanBeAccesedPublicly()) {
|
|
211
|
+
throw new Error(`Upload Plugin for attachment field '${key}' in resource '${this.resourceConfig.resourceId}'
|
|
212
|
+
uses adapter which is not configured to store objects in public way, so it will produce only signed private URLs which can not be used in HTML text of blog posts.
|
|
213
|
+
Please configure adapter in such way that it will store objects publicly (e.g. for S3 use 'public-read' ACL).
|
|
214
|
+
`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
203
218
|
}
|
|
204
219
|
|
|
205
220
|
instanceUniqueRepresentation(pluginOptions: any) : string {
|
|
206
|
-
// optional method to return unique string representation of plugin instance.
|
|
207
|
-
// Needed if plugin can have multiple instances on one resource
|
|
208
221
|
return `${this.pluginOptions.actionName}`;
|
|
209
222
|
}
|
|
210
223
|
|
|
211
224
|
setupEndpoints(server: IHttpServer) {
|
|
212
|
-
|
|
213
|
-
|
|
214
225
|
server.endpoint({
|
|
215
226
|
method: 'POST',
|
|
216
227
|
path: `/plugin/${this.pluginInstanceId}/analyze`,
|
|
@@ -224,7 +235,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
224
235
|
const tasks = selectedIds.map(async (ID) => {
|
|
225
236
|
// Fetch the record using the provided ID
|
|
226
237
|
const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
|
|
227
|
-
const record = await this.adminforth.resource(this.resourceConfig.resourceId).get(
|
|
238
|
+
const record = await this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ(primaryKeyColumn.name, ID)] );
|
|
228
239
|
|
|
229
240
|
//recieve image URLs to analyze
|
|
230
241
|
const attachmentFiles = await this.options.attachFiles({ record: record });
|
|
@@ -274,26 +285,23 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
274
285
|
}
|
|
275
286
|
}
|
|
276
287
|
const tasks = selectedIds.map(async (ID) => {
|
|
277
|
-
// Fetch the record using the provided ID
|
|
278
288
|
const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
|
|
279
289
|
const record = await this.adminforth.resource(this.resourceConfig.resourceId).get( [Filters.EQ(primaryKeyColumn.name, ID)] );
|
|
280
290
|
|
|
281
|
-
//create prompt for OpenAI
|
|
282
291
|
const compiledOutputFields = this.compileOutputFieldsTemplatesNoImage(record);
|
|
283
292
|
const prompt = `Analyze the following fields and return a single JSON in format like: {'param1': 'value1', 'param2': 'value2'}.
|
|
284
293
|
Do NOT return array of objects. Do NOT include any Markdown, code blocks, explanations, or extra text. Only return valid JSON.
|
|
285
294
|
Each object must contain the following fields: ${JSON.stringify(compiledOutputFields)} Use the exact field names.
|
|
286
295
|
If it's number field - return only number.`;
|
|
287
296
|
//send prompt to OpenAI and get response
|
|
288
|
-
const
|
|
297
|
+
const numberOfTokens = this.options.fillPlainFieldsMaxTokens ? this.options.fillPlainFieldsMaxTokens : 1000;
|
|
298
|
+
const { content: chatResponse } = await this.options.textCompleteAdapter.complete(prompt, [], numberOfTokens);
|
|
289
299
|
|
|
290
300
|
const resp: any = (chatResponse as any).response;
|
|
291
301
|
const topLevelError = (chatResponse as any).error;
|
|
292
302
|
if (topLevelError || resp?.error) {
|
|
293
303
|
throw new Error(`ERROR: ${JSON.stringify(topLevelError || resp?.error)}`);
|
|
294
304
|
}
|
|
295
|
-
|
|
296
|
-
//parse response and update record
|
|
297
305
|
const resData = JSON.parse(chatResponse);
|
|
298
306
|
|
|
299
307
|
return resData;
|
|
@@ -304,8 +312,6 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
304
312
|
return { result };
|
|
305
313
|
}
|
|
306
314
|
});
|
|
307
|
-
|
|
308
|
-
|
|
309
315
|
server.endpoint({
|
|
310
316
|
method: 'POST',
|
|
311
317
|
path: `/plugin/${this.pluginInstanceId}/get_records`,
|
|
@@ -316,13 +322,16 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
316
322
|
for( const [index, record] of records.entries() ) {
|
|
317
323
|
records[index]._label = this.resourceConfig.recordLabel(records[index]);
|
|
318
324
|
}
|
|
325
|
+
const order = Object.fromEntries(body.body.record.map((id, i) => [id, i]));
|
|
326
|
+
|
|
327
|
+
const sortedRecords = records.sort(
|
|
328
|
+
(a, b) => order[a.id] - order[b.id]
|
|
329
|
+
);
|
|
319
330
|
return {
|
|
320
|
-
records,
|
|
331
|
+
records: sortedRecords,
|
|
321
332
|
};
|
|
322
333
|
}
|
|
323
334
|
});
|
|
324
|
-
|
|
325
|
-
|
|
326
335
|
server.endpoint({
|
|
327
336
|
method: 'POST',
|
|
328
337
|
path: `/plugin/${this.pluginInstanceId}/get_images`,
|
|
@@ -340,8 +349,6 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
340
349
|
};
|
|
341
350
|
}
|
|
342
351
|
});
|
|
343
|
-
|
|
344
|
-
|
|
345
352
|
server.endpoint({
|
|
346
353
|
method: 'POST',
|
|
347
354
|
path: `/plugin/${this.pluginInstanceId}/update_fields`,
|
|
@@ -387,6 +394,35 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
387
394
|
}
|
|
388
395
|
}
|
|
389
396
|
}
|
|
397
|
+
try {
|
|
398
|
+
const AuditLogPlugin:any = this.adminforth.getPluginByClassName('AuditLogPlugin');
|
|
399
|
+
if (AuditLogPlugin) {
|
|
400
|
+
|
|
401
|
+
for (const [key, value] of Object.entries(oldRecord)) {
|
|
402
|
+
if (!(key in fieldsToUpdate[idx])) {
|
|
403
|
+
delete oldRecord[key];
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const reorderedOldRecord = Object.keys(fieldsToUpdate[idx]).reduce((acc, key) => {
|
|
408
|
+
if (key in oldRecord) {
|
|
409
|
+
acc[key] = oldRecord[key];
|
|
410
|
+
}
|
|
411
|
+
return acc;
|
|
412
|
+
}, {} as Record<string, unknown>);
|
|
413
|
+
|
|
414
|
+
AuditLogPlugin.logCustomAction({
|
|
415
|
+
resourceId: this.resourceConfig.resourceId,
|
|
416
|
+
recordId: ID,
|
|
417
|
+
actionId: 'Bulk-ai-flow',
|
|
418
|
+
oldData: reorderedOldRecord,
|
|
419
|
+
data: fieldsToUpdate[idx],
|
|
420
|
+
user: adminUser,
|
|
421
|
+
headers: headers
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
} catch (error) { }
|
|
425
|
+
|
|
390
426
|
return this.adminforth.resource(this.resourceConfig.resourceId).update(ID, fieldsToUpdate[idx])
|
|
391
427
|
});
|
|
392
428
|
await Promise.all(updates);
|
|
@@ -396,8 +432,6 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
396
432
|
}
|
|
397
433
|
}
|
|
398
434
|
});
|
|
399
|
-
|
|
400
|
-
|
|
401
435
|
server.endpoint({
|
|
402
436
|
method: 'POST',
|
|
403
437
|
path: `/plugin/${this.pluginInstanceId}/regenerate_images`,
|
|
@@ -449,8 +483,6 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
449
483
|
return { images };
|
|
450
484
|
}
|
|
451
485
|
});
|
|
452
|
-
|
|
453
|
-
|
|
454
486
|
server.endpoint({
|
|
455
487
|
method: 'POST',
|
|
456
488
|
path: `/plugin/${this.pluginInstanceId}/initial_image_generate`,
|
|
@@ -518,8 +550,6 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
518
550
|
return { result };
|
|
519
551
|
}
|
|
520
552
|
});
|
|
521
|
-
|
|
522
|
-
|
|
523
553
|
server.endpoint({
|
|
524
554
|
method: 'POST',
|
|
525
555
|
path: `/plugin/${this.pluginInstanceId}/get_generation_prompts`,
|
|
@@ -530,8 +560,6 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
530
560
|
return { generationOptions: compiledGenerationOptions };
|
|
531
561
|
}
|
|
532
562
|
});
|
|
533
|
-
|
|
534
|
-
|
|
535
563
|
server.endpoint({
|
|
536
564
|
method: 'GET',
|
|
537
565
|
path: `/plugin/${this.pluginInstanceId}/averageDuration`,
|
|
@@ -543,8 +571,5 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
543
571
|
};
|
|
544
572
|
}
|
|
545
573
|
});
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
574
|
}
|
|
550
575
|
}
|
package/package.json
CHANGED
package/types.ts
CHANGED
|
@@ -1,20 +1,52 @@
|
|
|
1
|
-
import { ImageVisionAdapter,
|
|
1
|
+
import { ImageVisionAdapter, ImageGenerationAdapter, CompletionAdapter } from "adminforth";
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
export interface PluginOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Name of the action in three dots menu.
|
|
7
|
+
*/
|
|
5
8
|
actionName: string,
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The adapter to use for scaning images and filling fields basing on the image content.
|
|
12
|
+
*/
|
|
6
13
|
visionAdapter?: ImageVisionAdapter,
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The adapter to use for text->text generation.
|
|
17
|
+
*/
|
|
7
18
|
textCompleteAdapter?: CompletionAdapter,
|
|
19
|
+
|
|
8
20
|
/**
|
|
9
21
|
* The adapter to use for image generation.
|
|
10
22
|
*/
|
|
11
23
|
imageGenerationAdapter?: ImageGenerationAdapter,
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* List of fields that should be filled based on the image content analysis.
|
|
27
|
+
*/
|
|
12
28
|
fillFieldsFromImages?: Record<string, string>, // can analyze what is on image and fill fields, typical tasks "find dominant color", "describe what is on image", "clasify to one enum item, e.g. what is on image dog/cat/plant"
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* List of fields that should be filled based on the text.
|
|
32
|
+
*/
|
|
13
33
|
fillPlainFields?: Record<string, string>,
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Number of tokens to generate (Only for text completion adapter). Default is 1000. 1 token ~= ¾ words
|
|
37
|
+
*/
|
|
38
|
+
fillPlainFieldsMaxTokens?: number,
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* If you want to generate fields or images based on the image content attached images
|
|
42
|
+
*/
|
|
14
43
|
attachFiles?: ({ record }: {
|
|
15
44
|
record: any,
|
|
16
45
|
}) => string[] | Promise<string[]>,
|
|
17
46
|
|
|
47
|
+
/**
|
|
48
|
+
* List of image fields, that should be filled.
|
|
49
|
+
*/
|
|
18
50
|
generateImages?: Record<
|
|
19
51
|
string, {
|
|
20
52
|
// can generate from images or just from another fields, e.g. "remove text from images", "improve image quality", "turn image into ghibli style"
|
|
@@ -43,6 +75,10 @@ export interface PluginOptions {
|
|
|
43
75
|
*/
|
|
44
76
|
countToGenerate: number,
|
|
45
77
|
}>,
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Rate limits for each action.
|
|
81
|
+
*/
|
|
46
82
|
rateLimits?: {
|
|
47
83
|
fillFieldsFromImages?: string, // e.g. 5/1d - 5 requests per day
|
|
48
84
|
fillPlainFields?: string,
|