feat(ai): add dashboard tools for AI chat (#16517)
## Summary - Implements real tools for the dashboard-building skill to create and manage dashboards through the AI chat interface - Adds 6 new dashboard tools: `create_complete_dashboard`, `list_dashboards`, `get_dashboard`, `add_dashboard_widget`, `update_dashboard_widget`, `delete_dashboard_widget` - Improves widget configuration robustness with typed Zod schemas and discriminated unions for graph types ## Key Changes **New Dashboard Tools:** - `create_complete_dashboard` - Creates a dashboard with layout, tab, and widgets in a single call - `list_dashboards` - Lists all dashboards in the workspace - `get_dashboard` - Gets full dashboard details including tabs and widget configurations - `add_dashboard_widget` - Adds a widget to an existing dashboard tab - `update_dashboard_widget` - Updates widget properties or configuration - `delete_dashboard_widget` - Removes a widget from a dashboard **Widget Configuration Improvements:** - Typed Zod schemas for each chart type (AGGREGATE, BAR, LINE, PIE) - Discriminated union validation based on `graphType` - Widget-level error handling for partial success when creating dashboards - Clear documentation about required `objectMetadataId` and field UUIDs **Skill Documentation Updates:** - Updated `dashboard-building.skill.ts` with critical guidance about looking up field metadata first - Added workflow instructions: use `list_object_metadata_items` before creating GRAPH widgets - Practical grid layout recommendations ## Test plan - [ ] Create a new dashboard via AI chat - [ ] Verify widgets display data correctly when proper field IDs are provided - [ ] Test adding/updating/deleting widgets on existing dashboards - [ ] Verify error messages are helpful when configuration is incorrect
This commit is contained in:
+1
@@ -0,0 +1 @@
|
||||
export const DASHBOARD_TOOL_SERVICE_TOKEN = Symbol('DASHBOARD_TOOL_SERVICE');
|
||||
+1
@@ -5,4 +5,5 @@ export enum ToolCategory {
|
||||
METADATA = 'METADATA',
|
||||
NATIVE_MODEL = 'NATIVE_MODEL',
|
||||
VIEW = 'VIEW',
|
||||
DASHBOARD = 'DASHBOARD',
|
||||
}
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
import { Inject, Injectable, Optional } from '@nestjs/common';
|
||||
|
||||
import { type ToolSet } from 'ai';
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
|
||||
import {
|
||||
type ToolProvider,
|
||||
type ToolProviderContext,
|
||||
} from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface';
|
||||
|
||||
import { DASHBOARD_TOOL_SERVICE_TOKEN } from 'src/engine/core-modules/tool-provider/constants/dashboard-tool-service.token';
|
||||
import { ToolCategory } from 'src/engine/core-modules/tool-provider/enums/tool-category.enum';
|
||||
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
|
||||
import type { DashboardToolWorkspaceService } from 'src/modules/dashboard/tools/services/dashboard-tool.workspace-service';
|
||||
|
||||
@Injectable()
|
||||
export class DashboardToolProvider implements ToolProvider {
|
||||
readonly category = ToolCategory.DASHBOARD;
|
||||
|
||||
constructor(
|
||||
@Optional()
|
||||
@Inject(DASHBOARD_TOOL_SERVICE_TOKEN)
|
||||
private readonly dashboardToolService: DashboardToolWorkspaceService | null,
|
||||
private readonly permissionsService: PermissionsService,
|
||||
) {}
|
||||
|
||||
async isAvailable(context: ToolProviderContext): Promise<boolean> {
|
||||
if (!this.dashboardToolService) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.permissionsService.checkRolesPermissions(
|
||||
context.rolePermissionConfig,
|
||||
context.workspaceId,
|
||||
PermissionFlagType.LAYOUTS,
|
||||
);
|
||||
}
|
||||
|
||||
async generateTools(context: ToolProviderContext): Promise<ToolSet> {
|
||||
if (!this.dashboardToolService) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return this.dashboardToolService.generateDashboardTools(
|
||||
context.workspaceId,
|
||||
context.rolePermissionConfig,
|
||||
);
|
||||
}
|
||||
}
|
||||
+15
-2
@@ -15,14 +15,26 @@ import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-perm
|
||||
export type ToolIndexEntry = {
|
||||
name: string;
|
||||
description: string;
|
||||
category: 'database' | 'action' | 'workflow' | 'metadata' | 'view';
|
||||
category:
|
||||
| 'database'
|
||||
| 'action'
|
||||
| 'workflow'
|
||||
| 'metadata'
|
||||
| 'view'
|
||||
| 'dashboard';
|
||||
objectName?: string;
|
||||
operation?: string;
|
||||
};
|
||||
|
||||
export type ToolSearchOptions = {
|
||||
limit?: number;
|
||||
category?: 'database' | 'action' | 'workflow' | 'metadata' | 'view';
|
||||
category?:
|
||||
| 'database'
|
||||
| 'action'
|
||||
| 'workflow'
|
||||
| 'metadata'
|
||||
| 'view'
|
||||
| 'dashboard';
|
||||
};
|
||||
|
||||
export type ToolContext = {
|
||||
@@ -174,6 +186,7 @@ export class ToolRegistryService {
|
||||
METADATA: 'metadata',
|
||||
NATIVE_MODEL: 'action',
|
||||
VIEW: 'view',
|
||||
DASHBOARD: 'dashboard',
|
||||
};
|
||||
|
||||
return Object.entries(tools).map(([name, tool]) => ({
|
||||
|
||||
+9
-8
@@ -4,6 +4,7 @@ import { RecordCrudModule } from 'src/engine/core-modules/record-crud/record-cru
|
||||
import { ToolGeneratorModule } from 'src/engine/core-modules/tool-generator/tool-generator.module';
|
||||
import { TOOL_PROVIDERS } from 'src/engine/core-modules/tool-provider/constants/tool-providers.token';
|
||||
import { ActionToolProvider } from 'src/engine/core-modules/tool-provider/providers/action-tool.provider';
|
||||
import { DashboardToolProvider } from 'src/engine/core-modules/tool-provider/providers/dashboard-tool.provider';
|
||||
import { DatabaseToolProvider } from 'src/engine/core-modules/tool-provider/providers/database-tool.provider';
|
||||
import { MetadataToolProvider } from 'src/engine/core-modules/tool-provider/providers/metadata-tool.provider';
|
||||
import { ViewToolProvider } from 'src/engine/core-modules/tool-provider/providers/view-tool.provider';
|
||||
@@ -21,14 +22,10 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache
|
||||
import { ToolProviderService } from './services/tool-provider.service';
|
||||
import { ToolRegistryService } from './services/tool-registry.service';
|
||||
|
||||
// NOTE: This module does NOT import WorkflowToolsModule to avoid circular dependency:
|
||||
// ToolProviderModule -> WorkflowToolsModule -> WorkflowTriggerModule
|
||||
// -> WorkflowRunnerModule -> WorkflowExecutorModule -> AiAgentActionModule
|
||||
// -> AiAgentExecutionModule -> ToolProviderModule
|
||||
//
|
||||
// Instead, WorkflowToolsModule is a @Global() module that provides WORKFLOW_TOOL_SERVICE_TOKEN.
|
||||
// When WorkflowToolsModule is imported anywhere in the app (e.g., AiChatModule),
|
||||
// the token becomes available globally to WorkflowToolProvider via @Optional() injection.
|
||||
// NOTE: This module does NOT import WorkflowToolsModule or DashboardToolsModule to avoid
|
||||
// circular dependencies. Instead, they are @Global() modules that provide their tokens.
|
||||
// When imported anywhere in the app (e.g., AiChatModule), the tokens become available
|
||||
// globally to their respective providers via @Optional() injection.
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -46,6 +43,7 @@ import { ToolRegistryService } from './services/tool-registry.service';
|
||||
],
|
||||
providers: [
|
||||
ActionToolProvider,
|
||||
DashboardToolProvider,
|
||||
DatabaseToolProvider,
|
||||
MetadataToolProvider,
|
||||
ViewToolProvider,
|
||||
@@ -54,12 +52,14 @@ import { ToolRegistryService } from './services/tool-registry.service';
|
||||
provide: TOOL_PROVIDERS,
|
||||
useFactory: (
|
||||
actionProvider: ActionToolProvider,
|
||||
dashboardProvider: DashboardToolProvider,
|
||||
databaseProvider: DatabaseToolProvider,
|
||||
metadataProvider: MetadataToolProvider,
|
||||
viewProvider: ViewToolProvider,
|
||||
workflowProvider: WorkflowToolProvider,
|
||||
) => [
|
||||
actionProvider,
|
||||
dashboardProvider,
|
||||
databaseProvider,
|
||||
metadataProvider,
|
||||
viewProvider,
|
||||
@@ -67,6 +67,7 @@ import { ToolRegistryService } from './services/tool-registry.service';
|
||||
],
|
||||
inject: [
|
||||
ActionToolProvider,
|
||||
DashboardToolProvider,
|
||||
DatabaseToolProvider,
|
||||
MetadataToolProvider,
|
||||
ViewToolProvider,
|
||||
|
||||
Reference in New Issue
Block a user