Skip to main content

Bulk AI Flow

This plugin allows filling fields in multiple selected records based on data from other fields using LLM. This also supports vision tasks so you can ask it to e.g. detect dominant color on image or describe what is on the image. Plugin supports classification to enum options automatically.

Installation

To install the plugin:

npm install @adminforth/bulk-ai-flow --save

You'll also need an image vision adapter:

npm install @adminforth/image-vision-adapter-openai --save

Vision mode

This mode covers next generations:

- Image(one or many fields) -> to -> Text/Number/Enum/Boolean(one or many fields)
- Image(one or many fields) + Text/Number/Enum/Boolean(one or many fields) -> to -> Text/Number/Enum/Boolean(one or many fields)

Lets try both. Add a column for storing the URL or path to the image in the database, add this statement to the ./schema.prisma:

./schema.prisma
model apartments {
id String @id
created_at DateTime?
title String
square_meter Float?
price Decimal
number_of_rooms Int?
description String?
country String?
listed Boolean
realtor_id String?
apartment_image String?
}

Migrate prisma schema:

npm run makemigration -- --name add-apartment-image-url ; npm run migrate:local

We will also attach upload plugin to this field.

Add credentials in your .env file:

.env
...

OPENAI_API_KEY=your_secret_openai_key

...

Add column to aparts resource configuration:

./resources/apartments.ts
import BulkAiFlowPlugin  from '@adminforth/bulk-ai-flow';
import AdminForthImageVisionAdapterOpenAi from '@adminforth/image-vision-adapter-openai';
import UploadPlugin from '@adminforth/upload';
import { randomUUID } from 'crypto';
import AdminForthAdapterS3Storage from '@adminforth/storage-adapter-amazon-s3'

export const admin = new AdminForth({
...
resourceId: 'aparts',
columns: [
...
{
name: 'apartment_image',
label: 'Image',
showIn: { list: false, create: true, edit: true},
}
...
],
plugins: [
...
new UploadPlugin({
storageAdapter: new AdminForthAdapterS3Storage({
bucket: process.env.AWS_BUCKET_NAME,
region: process.env.AWS_REGION,
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
s3ACL: 'public-read',
}),
pathColumnName: 'apartment_image',
allowedFileExtensions: ['jpg', 'jpeg', 'png', 'gif', 'webm', 'webp'],
filePath: ({originalFilename, originalExtension, contentType}) =>
`aparts/${new Date().getFullYear()}/${uuid()}-${originalFilename}.${originalExtension}`,
}),

new BulkAiFlowPlugin({
actionName: 'Analyze',
attachFiles: async ({ record }: { record: any }) => {
if (!record.apartment_image) {
return [];
}
return [`https://tmpbucket-adminforth.s3.eu-central-1.amazonaws.com/${record.apartment_image}`];
},
visionAdapter: new AdminForthImageVisionAdapterOpenAi(
{
openAiApiKey: process.env.OPENAI_API_KEY as string,
model: 'gpt-4.1-mini',
}
),
fillFieldsFromImages: {
'description': 'describe what is in the image, also take into account that price is {{price}}',
'country': 'In which country it can be located?',
'number_of_rooms': 'How many rooms are in the apartment? Just try to guess what is a typical one. If you do not know, just guess',
'square_meter': 'Try to guess what is the typical square of the apartment in square meters? If you do not know, just guess',
'listed': 'Is the apartment should be listed for sale? If you do not know, just guess, return boolean value',
},
}),
],


...

});

⚠️ Make sure your attachFiles function returns a valid array of image URLs or an empty array. Returning anything else may cause an error.

Usage

  1. Select fields you want to fill
  2. Click on the three dots menu
  3. Click analyze alt text
  4. Wait for finish analyze
  5. Check and edit result alt text
  6. Save changhes alt text

Text-to-Text Processing

This is the most basic plugin usage. You can connect any text completion adapter to fill one or several string/number/boolean fields from other fields.

Example: Translate Names to English

Normalize user names by translating them from any language to English for internal processing.

import CompletionAdapterOpenAIChatGPT from '@adminforth/completion-adapter-open-ai-chat-gpt/index.js';

// Add to your resource plugins array
new BulkAiFlowPlugin({
actionName: 'Translate surnames',
textCompleteAdapter: new CompletionAdapterOpenAIChatGPT({
openAiApiKey: process.env.OPENAI_API_KEY as string,
model: 'gpt-4o',
expert: {
temperature: 0.7
}
}),
fillPlainFields: {
'full_name_en': 'Translate this name to English: {{users_full_name}}',
},
}),

Image-to-Text Analysis (Vision)

Analyze images and extract information to fill text, number, enum, or boolean fields.

Example: Age Detection from Photos

import AdminForthImageVisionAdapterOpenAi from '@adminforth/image-vision-adapter-openai/index.js';

// Add to your resource plugins array
new BulkAiFlowPlugin({
actionName: 'Guess age',
visionAdapter: new AdminForthImageVisionAdapterOpenAi({
openAiApiKey: process.env.OPENAI_API_KEY as string,
model: 'gpt-4.1-mini',
}),
fillFieldsFromImages: {
'age': 'Analyze the image and estimate the age of the person. Return only a number.',
},
attachFiles: async ({ record }) => {
if (!record.image_url) {
return [];
}
return [`https://users-images.s3.eu-north-1.amazonaws.com/${record.image_url}`];
},
}),

Text-to-Image generation or image editing

Generate new images based on existing data and/or images using AI image generation adapters.

Example: Creating Cartoon Avatars

import ImageGenerationAdapterOpenAI from '@adminforth/image-generation-adapter-openai/index.js';

// Add to your resource plugins array
new BulkAiFlowPlugin({
actionName: 'Generate cartoon avatars',
imageGenerationAdapter: new ImageGenerationAdapterOpenAI({
openAiApiKey: process.env.OPENAI_API_KEY as string,
model: 'gpt-image-1',
}),
attachFiles: async ({ record }) => {
if (!record.user_photo) {
return [];
}
return [`https://bulk-ai-flow-playground.s3.eu-north-1.amazonaws.com/${record.users_photo}`];
},
generateImages: {
users_avatar: {
prompt: 'Transform this photo into a cartoon-style avatar. Maintain the person\'s features but apply cartoon styling. Do not add text or logos.',
outputSize: '1024x1024',
countToGenerate: 2,
rateLimit: '3/1h'
},
},
bulkGenerationRateLimit: "1/1h"
}),

Rate Limiting and Best Practices

  • Use rateLimit for individual image generation operations and for the bulk image generation
  new BulkAiFlowPlugin({
...

generateImages: {
users_avatar: {
... //image re-generation limits
rateLimit: '1/5m' // one request per 5 minutes
}
},

...

rateLimits: { // bulk generation limits
fillFieldsFromImages: "5/1d", // 5 requests per day
fillPlainFields: "3/1h", // 3 requests per one hour
generateImages: "1/2m", // 2 request per one minute
}

...

})
  • Consider using lower resolution (512x512) for faster generation and lower costs
  • Test prompts thoroughly before applying to large datasets