@adminforth/bulk-ai-flow 1.14.5 → 1.15.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/index.ts CHANGED
@@ -25,11 +25,15 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
25
25
  }
26
26
 
27
27
  // Compile Handlebars templates in outputFields using record fields as context
28
- private compileTemplates<T extends Record<string, any>>(
28
+ private async compileTemplates<T extends Record<string, any>>(
29
29
  source: T,
30
30
  record: any,
31
31
  valueSelector: (value: T[keyof T]) => string
32
- ): Record<string, string> {
32
+ ): Promise<Record<string, string>> {
33
+ if (this.options.provideAdditionalContextForRecord) {
34
+ const additionalFields = await this.options.provideAdditionalContextForRecord({ record, adminUser: null, resource: this.resourceConfig });
35
+ record = { ...record, ...additionalFields };
36
+ }
33
37
  const compiled: Record<string, string> = {};
34
38
  for (const [key, value] of Object.entries(source)) {
35
39
  const templateStr = valueSelector(value);
@@ -43,16 +47,16 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
43
47
  return compiled;
44
48
  }
45
49
 
46
- private compileOutputFieldsTemplates(record: any) {
47
- return this.compileTemplates(this.options.fillFieldsFromImages, record, v => String(v));
50
+ private async compileOutputFieldsTemplates(record: any, customPrompt? : string) {
51
+ return await this.compileTemplates(customPrompt ? JSON.parse(customPrompt) :this.options.fillFieldsFromImages, record, v => String(v));
48
52
  }
49
53
 
50
- private compileOutputFieldsTemplatesNoImage(record: any) {
51
- return this.compileTemplates(this.options.fillPlainFields, record, v => String(v));
54
+ private async compileOutputFieldsTemplatesNoImage(record: any, customPrompt? : string) {
55
+ return await this.compileTemplates(customPrompt ? JSON.parse(customPrompt) : this.options.fillPlainFields, record, v => String(v));
52
56
  }
53
57
 
54
- private compileGenerationFieldTemplates(record: any) {
55
- return this.compileTemplates(this.options.generateImages, record, v => String(v.prompt));
58
+ private async compileGenerationFieldTemplates(record: any, customPrompt? : string) {
59
+ return await this.compileTemplates(customPrompt ? JSON.parse(customPrompt) : this.options.generateImages, record, v => String(customPrompt ? v : v.prompt));
56
60
  }
57
61
 
58
62
  private async checkRateLimit(field: string, fieldNameRateLimit: string | undefined, headers: Record<string, string | string[] | undefined>): Promise<void | { error?: string; }> {
@@ -72,7 +76,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
72
76
  }
73
77
  }
74
78
 
75
- private async analyze_image(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined>) {
79
+ private async analyze_image(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined>, customPrompt? : string) {
76
80
  const selectedId = recordId;
77
81
  let isError = false;
78
82
  // Fetch the record using the provided ID
@@ -102,7 +106,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
102
106
  return { ok: false, error: 'One of the image URLs is not valid' };
103
107
  }
104
108
  //create prompt for OpenAI
105
- const compiledOutputFields = this.compileOutputFieldsTemplates(record);
109
+ const compiledOutputFields = await this.compileOutputFieldsTemplates(record, customPrompt);
106
110
  const prompt = `Analyze the following image(s) and return a single JSON in format like: {'param1': 'value1', 'param2': 'value2'}.
107
111
  Do NOT return array of objects. Do NOT include any Markdown, code blocks, explanations, or extra text. Only return valid JSON.
108
112
  Each object must contain the following fields: ${JSON.stringify(compiledOutputFields)} Use the exact field names. If it's number field - return only number.
@@ -148,7 +152,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
148
152
 
149
153
  }
150
154
 
151
- private async analyzeNoImages(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined>) {
155
+ private async analyzeNoImages(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined>, customPrompt? : string) {
152
156
  const selectedId = recordId;
153
157
  let isError = false;
154
158
  if (STUB_MODE) {
@@ -159,7 +163,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
159
163
  const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
160
164
  const record = await this.adminforth.resource(this.resourceConfig.resourceId).get( [Filters.EQ(primaryKeyColumn.name, selectedId)] );
161
165
 
162
- const compiledOutputFields = this.compileOutputFieldsTemplatesNoImage(record);
166
+ const compiledOutputFields = await this.compileOutputFieldsTemplatesNoImage(record, customPrompt);
163
167
  const prompt = `Analyze the following fields and return a single JSON in format like: {'param1': 'value1', 'param2': 'value2'}.
164
168
  Do NOT return array of objects. Do NOT include any Markdown, code blocks, explanations, or extra text. Only return valid JSON.
165
169
  Each object must contain the following fields: ${JSON.stringify(compiledOutputFields)} Use the exact field names.
@@ -188,7 +192,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
188
192
  }
189
193
  }
190
194
 
191
- private async initialImageGenerate(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined>) {
195
+ private async initialImageGenerate(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined>, customPrompt? : string) {
192
196
  const selectedId = recordId;
193
197
  let isError = false;
194
198
  const start = +new Date();
@@ -208,7 +212,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
208
212
  }
209
213
  }
210
214
  const fieldTasks = Object.keys(this.options?.generateImages || {}).map(async (key) => {
211
- const prompt = this.compileGenerationFieldTemplates(record)[key];
215
+ const prompt = (await this.compileGenerationFieldTemplates(record, customPrompt))[key];
212
216
  let images;
213
217
  if (this.options.attachFiles && attachmentFiles.length === 0) {
214
218
  isError = true;
@@ -391,7 +395,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
391
395
  };
392
396
 
393
397
  const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
394
-
398
+
395
399
  const pageInjection = {
396
400
  file: this.componentPath('VisionAction.vue'),
397
401
  meta: {
@@ -413,6 +417,12 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
413
417
  fillPlainFields: this.options.refreshRates?.fillPlainFields || 1_000,
414
418
  generateImages: this.options.refreshRates?.generateImages || 5_000,
415
419
  regenerateImages: this.options.refreshRates?.regenerateImages || 5_000,
420
+ },
421
+ askConfirmationBeforeGenerating: this.options.askConfirmationBeforeGenerating || false,
422
+ generationPrompts: {
423
+ plainFieldsPrompts: this.options.fillPlainFields || {},
424
+ imageFieldsPrompts: this.options.fillFieldsFromImages || {},
425
+ imageGenerationPrompts: this.options.generateImages || {},
416
426
  }
417
427
  }
418
428
  }
@@ -489,7 +499,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
489
499
  }
490
500
  }
491
501
  }
492
- if (this.options.fillFieldsFromImages || this.options.fillPlainFields || this.options.generateImages) {
502
+ if ((this.options.fillFieldsFromImages || this.options.fillPlainFields || this.options.generateImages) && !this.options.provideAdditionalContextForRecord) {
493
503
  let matches: string[] = [];
494
504
  const regex = /{{(.*?)}}/g;
495
505
 
@@ -652,12 +662,13 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
652
662
 
653
663
  server.endpoint({
654
664
  method: 'POST',
655
- path: `/plugin/${this.pluginInstanceId}/get_generation_prompts`,
665
+ path: `/plugin/${this.pluginInstanceId}/get_image_generation_prompts`,
656
666
  handler: async ({ body, headers }) => {
657
667
  const Id = body.recordId || [];
668
+ const customPrompt = body.customPrompt || null;
658
669
  const record = await this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ(this.resourceConfig.columns.find(c => c.primaryKey)?.name, Id)]);
659
- const compiledGenerationOptions = this.compileGenerationFieldTemplates(record);
660
- return { generationOptions: compiledGenerationOptions };
670
+ const compiledGenerationOptions = await this.compileGenerationFieldTemplates(record, JSON.stringify({"prompt": customPrompt}));
671
+ return compiledGenerationOptions;
661
672
  }
662
673
  });
663
674
 
@@ -679,10 +690,9 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
679
690
  method: 'POST',
680
691
  path: `/plugin/${this.pluginInstanceId}/create-job`,
681
692
  handler: async ({ body, adminUser, headers }) => {
682
- const { actionType, recordId } = body;
693
+ const { actionType, recordId, customPrompt } = body;
683
694
  const jobId = randomUUID();
684
695
  jobs.set(jobId, { status: "in_progress" });
685
-
686
696
  if (!actionType) {
687
697
  jobs.set(jobId, { status: "failed", error: "Missing action type" });
688
698
  //return { error: "Missing action type" };
@@ -693,13 +703,13 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
693
703
  } else {
694
704
  switch(actionType) {
695
705
  case 'generate_images':
696
- this.initialImageGenerate(jobId, recordId, adminUser, headers);
706
+ this.initialImageGenerate(jobId, recordId, adminUser, headers, customPrompt);
697
707
  break;
698
708
  case 'analyze_no_images':
699
- this.analyzeNoImages(jobId, recordId, adminUser, headers);
709
+ this.analyzeNoImages(jobId, recordId, adminUser, headers, customPrompt);
700
710
  break;
701
711
  case 'analyze':
702
- this.analyze_image(jobId, recordId, adminUser, headers);
712
+ this.analyze_image(jobId, recordId, adminUser, headers, customPrompt);
703
713
  break;
704
714
  case 'regenerate_images':
705
715
  if (!body.prompt || !body.fieldName) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/bulk-ai-flow",
3
- "version": "1.14.5",
3
+ "version": "1.15.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
package/types.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { ImageVisionAdapter, ImageGenerationAdapter, CompletionAdapter } from "adminforth";
2
-
1
+ import AdminForth, { ImageVisionAdapter, ImageGenerationAdapter, CompletionAdapter } from "adminforth";
3
2
 
4
3
  export interface PluginOptions {
5
4
  /**
@@ -109,4 +108,15 @@ export interface PluginOptions {
109
108
  ok: boolean;
110
109
  error?: undefined;
111
110
  }>
111
+
112
+ /**
113
+ * Custom message for the context shown to the user when performing the action
114
+ */
115
+ provideAdditionalContextForRecord?: ({record, adminUser, resource}: {
116
+ record: any;
117
+ adminUser: any;
118
+ resource: any;
119
+ }) => Record<string, any> | Promise<Record<string, any>>;
120
+
121
+ askConfirmationBeforeGenerating?: boolean;
112
122
  }