Skip to content

Commit 973bcd6

Browse files
committed
Implement batch processing for AI-generated table settings and enhance error handling
1 parent b38813a commit 973bcd6

2 files changed

Lines changed: 63 additions & 14 deletions

File tree

backend/src/entities/ai/ai.service.ts

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,44 @@ interface AIResponse {
2727
tables: AIGeneratedTableSettings[];
2828
}
2929

30+
const AI_BATCH_SIZE = 10;
31+
3032
@Injectable()
3133
export class AiService {
3234
constructor(protected readonly aiCoreService: AICoreService) {}
3335

3436
public async generateNewTableSettingsWithAI(
3537
tablesInformation: Array<TableInformation>,
3638
): Promise<Array<TableSettingsEntity>> {
39+
const allSettings: Array<TableSettingsEntity> = [];
40+
41+
for (let i = 0; i < tablesInformation.length; i += AI_BATCH_SIZE) {
42+
const batch = tablesInformation.slice(i, i + AI_BATCH_SIZE);
43+
try {
44+
const batchSettings = await this.processTablesBatch(batch);
45+
allSettings.push(...batchSettings);
46+
} catch (error) {
47+
console.warn(`Batch processing failed, falling back to individual table processing: ${error.message}`);
48+
for (const tableInfo of batch) {
49+
try {
50+
const singleTableSettings = await this.processTablesBatch([tableInfo]);
51+
allSettings.push(...singleTableSettings);
52+
} catch (singleError) {
53+
console.error(`Error processing AI for table "${tableInfo.table_name}": ${singleError.message}`);
54+
}
55+
}
56+
}
57+
}
58+
59+
return allSettings;
60+
}
61+
62+
private async processTablesBatch(tablesInformation: Array<TableInformation>): Promise<Array<TableSettingsEntity>> {
3763
const prompt = this.buildPrompt(tablesInformation);
3864
const aiResponse = await this.aiCoreService.completeWithProvider(AIProviderType.BEDROCK, prompt, {
3965
temperature: 0.3,
4066
});
41-
const parsedResponse = this.parseAIResponse(aiResponse);
67+
const parsedResponse = this.parseAIResponse(aiResponse, tablesInformation);
4268
return this.buildTableSettingsEntities(parsedResponse, tablesInformation);
4369
}
4470

@@ -131,13 +157,14 @@ Respond ONLY with valid JSON in this exact format (no markdown, no explanations)
131157
}`;
132158
}
133159

134-
private parseAIResponse(aiResponse: string): AIResponse {
160+
private parseAIResponse(aiResponse: string, tablesInformation: Array<TableInformation>): AIResponse {
135161
const cleanedResponse = cleanAIJsonResponse(aiResponse);
162+
const tableNames = tablesInformation.map((t) => t.table_name);
136163

137164
try {
138165
return JSON.parse(cleanedResponse) as AIResponse;
139166
} catch (error) {
140-
throw new Error(`Failed to parse AI response: ${error.message}`);
167+
throw new Error(`Failed to parse AI response for tables [${tableNames.join(', ')}]: ${error.message}`);
141168
}
142169
}
143170

@@ -156,7 +183,9 @@ Respond ONLY with valid JSON in this exact format (no markdown, no explanations)
156183
settings.readonly_fields = this.filterValidColumns(tableSettings.readonly_fields, validColumnNames);
157184
settings.columns_view = this.filterValidColumns(tableSettings.columns_view, validColumnNames);
158185
settings.ordering = this.mapOrdering(tableSettings.ordering);
159-
settings.ordering_field = validColumnNames.includes(tableSettings.ordering_field) ? tableSettings.ordering_field : null;
186+
settings.ordering_field = validColumnNames.includes(tableSettings.ordering_field)
187+
? tableSettings.ordering_field
188+
: null;
160189
settings.table_widgets = tableSettings.widgets
161190
.filter((w) => validColumnNames.includes(w.field_name))
162191
.map((widgetData) => {

backend/src/entities/shared-jobs/shared-jobs.service.ts

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,24 +51,44 @@ export class SharedJobsService {
5151
}
5252

5353
const queue = new PQueue({ concurrency: 4 });
54-
const tablesInformation = await Promise.all(
54+
const tablesInformationResults = await Promise.all(
5555
tablesToScan.map((table) =>
5656
queue.add(async () => {
57-
const structure = await dao.getTableStructure(table.tableName, null);
58-
const primaryColumns = await dao.getTablePrimaryColumns(table.tableName, null);
59-
const foreignKeys = await dao.getTableForeignKeys(table.tableName, null);
60-
return {
61-
table_name: table.tableName,
62-
structure,
63-
primaryColumns,
64-
foreignKeys,
65-
};
57+
try {
58+
const structure = await dao.getTableStructure(table.tableName, null);
59+
const primaryColumns = await dao.getTablePrimaryColumns(table.tableName, null);
60+
const foreignKeys = await dao.getTableForeignKeys(table.tableName, null);
61+
return {
62+
table_name: table.tableName,
63+
structure,
64+
primaryColumns,
65+
foreignKeys,
66+
};
67+
} catch (error) {
68+
console.error(`Error getting table information for "${table.tableName}": ${error.message}`);
69+
return null;
70+
}
6671
}),
6772
),
6873
);
6974

75+
const tablesInformation = tablesInformationResults.filter((info) => info !== null);
76+
77+
if (tablesInformation.length === 0) {
78+
console.info(`No valid tables to process for connection with id "${connection.id}"`);
79+
return;
80+
}
81+
82+
console.info(`Processing ${tablesInformation.length} tables with AI for connection "${connection.id}"`);
7083
const generatedTableSettings = await this.aiService.generateNewTableSettingsWithAI(tablesInformation);
7184

85+
if (generatedTableSettings.length === 0) {
86+
console.info(`No table settings generated by AI for connection with id "${connection.id}"`);
87+
return;
88+
}
89+
90+
console.info(`AI generated settings for ${generatedTableSettings.length} tables`);
91+
7292
const widgetsByTable = new Map<string, Array<TableWidgetEntity>>();
7393
for (const setting of generatedTableSettings) {
7494
if (setting.table_widgets && setting.table_widgets.length > 0) {

0 commit comments

Comments
 (0)