Skip to content

feat: add AI-driven table dashboard generation with customizable options#1631

Merged
Artuomka merged 1 commit intomainfrom
backend_automatic_ai_dashboards
Feb 25, 2026
Merged

feat: add AI-driven table dashboard generation with customizable options#1631
Artuomka merged 1 commit intomainfrom
backend_automatic_ai_dashboards

Conversation

@Artuomka
Copy link
Collaborator

  • Implemented GenerateTableDashboardWithAiDto for input validation.
  • Created GenerateTableDashboardWithAiUseCase to handle AI-driven dashboard generation.
  • Added integration tests for dashboard generation, including success and failure scenarios.
  • Ensured AI-generated panels are validated for safety before persistence.
  • Supported custom dashboard naming and handling of non-existent tables.

- Implemented GenerateTableDashboardWithAiDto for input validation.
- Created GenerateTableDashboardWithAiUseCase to handle AI-driven dashboard generation.
- Added integration tests for dashboard generation, including success and failure scenarios.
- Ensured AI-generated panels are validated for safety before persistence.
- Supported custom dashboard naming and handling of non-existent tables.
Copilot AI review requested due to automatic review settings February 25, 2026 10:58
@Artuomka Artuomka enabled auto-merge February 25, 2026 10:58
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new backend capability to generate and persist an AI-created dashboard for a given table, plus refactors the existing AI “widget” generation to use “panel” terminology in responses and tests.

Changes:

  • Introduces GenerateTableDashboardWithAiUseCase and related DTO/DS wiring to generate a dashboard + panels via AI and persist them.
  • Adds new SAAS and non-SAAS E2E tests covering success and failure scenarios for table-dashboard generation.
  • Renames AI-generated response fields in the existing widget-generation flow from widget_* to panel_* and updates tests accordingly.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
backend/test/ava-tests/saas-tests/dashboard-ai-widget-e2e.test.ts Updates AI widget E2E assertions/mocks to panel_type/panel_options.
backend/test/ava-tests/saas-tests/dashboard-ai-generate-table-dashboard-e2e.test.ts New SAAS E2E coverage for generating/persisting AI dashboards from a table.
backend/test/ava-tests/non-saas-tests/non-saas-dashboard-ai-generate-table-dashboard-e2e.test.ts New non-SAAS E2E coverage for generating/persisting AI dashboards from a table.
backend/src/entities/visualizations/panel-position/use-cases/panel-position-use-cases.interface.ts Adds IGenerateTableDashboardWithAi interface contract.
backend/src/entities/visualizations/panel-position/use-cases/generate-table-dashboard-with-ai.use.case.ts Implements AI-driven dashboard suggestion, panel generation, safety checks, and persistence.
backend/src/entities/visualizations/panel-position/use-cases/generate-panel-position-with-ai.use.case.ts Refactors AI widget generation to use panel_* naming and improved query response cleaning.
backend/src/entities/visualizations/panel-position/panel-position.module.ts Registers new use case provider and route middleware configuration.
backend/src/entities/visualizations/panel-position/panel-position.controller.ts Adds /dashboard/generate-table-dashboard/:connectionId endpoint and updates AI-generate docs/DTO usage.
backend/src/entities/visualizations/panel-position/dto/generated-panel-with-position.dto.ts Changes generated response DTO field names to panel_type/panel_options and marks dashboard_id nullable.
backend/src/entities/visualizations/panel-position/dto/generate-table-dashboard-with-ai.dto.ts New request DTO for max panels and optional dashboard name.
backend/src/entities/visualizations/panel-position/data-structures/generate-table-dashboard-with-ai.ds.ts New DS for table-dashboard AI generation use case input.
backend/src/common/data-injection.tokens.ts Adds DI token for the new generate-table-dashboard use case.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +413 to +421
break;
}

this.logger.log(`Query corrected by AI after EXPLAIN (iteration ${iteration + 1})`);
currentQuery = correctedQuery;

if (explainResult.success) {
break;
}
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the AI changes the query, the code may break out when the previous EXPLAIN succeeded (if (explainResult.success) break;) without re-running EXPLAIN on the modified query. That can allow an invalid query to be accepted after AI modification. Re-run EXPLAIN after any change (or only allow modifications when the previous EXPLAIN failed).

Suggested change
break;
}
this.logger.log(`Query corrected by AI after EXPLAIN (iteration ${iteration + 1})`);
currentQuery = correctedQuery;
if (explainResult.success) {
break;
}
if (explainResult.success) {
break;
}
// EXPLAIN failed and AI did not change the query; allow further iterations
continue;
}
this.logger.log(`Query corrected by AI after EXPLAIN (iteration ${iteration + 1})`);
currentQuery = correctedQuery;
// Do not break here: the next iteration will re-run EXPLAIN on the modified query

Copilot uses AI. Check for mistakes.
Comment on lines +28 to 32
@ApiProperty({ description: 'Panel type', enum: DashboardWidgetTypeEnum })
panel_type: DashboardWidgetTypeEnum;

@ApiPropertyOptional({ description: 'Chart type for chart widgets' })
@ApiPropertyOptional({ description: 'Chart type for chart panels' })
chart_type: string | null;
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GeneratedPanelWithPositionDto now exposes panel_type/panel_options, while other visualization APIs still expose widget_type/widget_options (e.g., saved query DTOs). This makes /dashboard/:dashboardId/widget/generate/:connectionId inconsistent and is a breaking response change for existing clients. Consider keeping the existing property names (or returning both sets temporarily) to avoid breaking consumers and to keep naming consistent across endpoints.

Copilot uses AI. Check for mistakes.
Comment on lines +209 to +213
@SlugUuid('connectionId') connectionId: string,
@Query('tableName') tableName: string,
@MasterPassword() masterPwd: string,
@UserId() userId: string,
@Body() generateDto: GenerateTableDashboardWithAiDto,
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The required tableName query parameter is not validated before being used to build the DS. This currently relies on downstream DAO failures to produce a 400, which can yield unclear messages. Add an explicit check (or a validation pipe) to return a BadRequestException when tableName is missing/empty.

Copilot uses AI. Check for mistakes.
max_panels: generateDto.max_panels,
dashboard_name: generateDto.dashboard_name,
};
return await this.generateTableDashboardWithAiUseCase.execute(inputData, InTransactionEnum.ON);
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This endpoint executes the use case with InTransactionEnum.ON, which starts a global DB transaction before the use case runs. Since the use case performs multiple AI calls (and potentially multiple EXPLAIN calls) before persisting anything, this can keep a transaction open for a long time and increase lock/connection contention. Prefer running the AI generation outside a transaction and only wrapping the persistence section in a transaction (e.g., execute with OFF and manage a shorter transaction around the saves).

Suggested change
return await this.generateTableDashboardWithAiUseCase.execute(inputData, InTransactionEnum.ON);
return await this.generateTableDashboardWithAiUseCase.execute(inputData, InTransactionEnum.OFF);

Copilot uses AI. Check for mistakes.
public async implementation(inputData: GenerateTableDashboardWithAiDs): Promise<{ success: boolean }> {
const { connectionId, masterPassword, table_name, max_panels, dashboard_name } = inputData;

const maxPanels = max_panels ?? DEFAULT_MAX_PANELS;
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

max_panels is taken directly from input without any defensive clamping here. Even though the DTO validates this for HTTP requests, this use case can still be called programmatically with out-of-range values; consider clamping/enforcing the supported range (and handling non-positive values) before using it in prompts/slicing.

Suggested change
const maxPanels = max_panels ?? DEFAULT_MAX_PANELS;
const rawMaxPanels = max_panels ?? DEFAULT_MAX_PANELS;
const normalizedMaxPanels = Number.isFinite(rawMaxPanels as number) ? Math.floor(rawMaxPanels as number) : DEFAULT_MAX_PANELS;
const maxPanels = Math.max(1, Math.min(DEFAULT_MAX_PANELS, normalizedMaxPanels));

Copilot uses AI. Check for mistakes.
Comment on lines +135 to +136
const effectiveDashboardName =
dashboard_name || dashboardSuggestion.dashboard_name || `${table_name} Dashboard`;
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

effectiveDashboardName can come from the AI response without enforcing the DB/DTO length limits. If the underlying column is length-limited, an overlong name can cause persistence errors. Consider validating/truncating to the maximum supported length before saving.

Copilot uses AI. Check for mistakes.
Comment on lines +212 to +217
"suggested_panel_type": "chart" | "counter" | "table",
"suggested_chart_type": "bar" | "line" | "pie" | "doughnut" | "polarArea"
}
]
}

Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This prompt’s JSON example isn’t valid JSON because it includes TypeScript-style union syntax (e.g., "chart" | "counter" | "table"). That can cause the model to emit invalid JSON that fails parsing. Use a valid JSON example and list allowed values separately (in prose) to improve reliability.

Suggested change
"suggested_panel_type": "chart" | "counter" | "table",
"suggested_chart_type": "bar" | "line" | "pie" | "doughnut" | "polarArea"
}
]
}
"suggested_panel_type": "chart",
"suggested_chart_type": "bar"
}
]
}
Allowed values for "suggested_panel_type" are: "chart", "counter", "table".
Allowed values for "suggested_chart_type" are: "bar", "line", "pie", "doughnut", "polarArea".

Copilot uses AI. Check for mistakes.
Comment on lines +403 to +405
const aiResponse = await this.aiCoreService.completeWithProvider(AIProviderType.BEDROCK, correctionPrompt, {
temperature: 0.2,
});
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AI correction call is executed on every loop iteration regardless of whether the EXPLAIN succeeded. This adds cost/latency even for already-valid queries. Consider only invoking the AI correction step when EXPLAIN fails or when you detect a specific optimization opportunity.

Copilot uses AI. Check for mistakes.
@Artuomka Artuomka requested a review from Copilot February 25, 2026 11:09
@Artuomka Artuomka merged commit 58f8938 into main Feb 25, 2026
25 of 28 checks passed
@Artuomka Artuomka deleted the backend_automatic_ai_dashboards branch February 25, 2026 11:12
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

})),
};
} catch (error) {
throw new BadRequestException(`Failed to get table structure for "${table_name}": ${error.message}`);
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error message includes the original exception message from the underlying DAO/driver. That can leak internal DB details (table/schema names, driver errors) to clients. Prefer logging the full error server-side and returning a generic BadRequest message (or a sanitized one) to the caller.

Suggested change
throw new BadRequestException(`Failed to get table structure for "${table_name}": ${error.message}`);
this.logger.error(
`Failed to get table structure for "${table_name}".`,
error instanceof Error ? error.stack : String(error),
);
throw new BadRequestException(
`Failed to get table structure for "${table_name}". Please verify the table name and try again.`,
);

Copilot uses AI. Check for mistakes.
export class GenerateTableDashboardWithAiDs {
connectionId: string;
masterPassword: string;
userId: string;
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

userId is part of the data structure, but the corresponding use case implementation doesn’t read it. Consider removing it from the DS/controller payload if it’s not needed, or using it for audit/ownership checks to avoid misleading/unused fields in the API contract.

Suggested change
userId: string;

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants