[Dashboards] [ai] update dashboard tools schema and skill with correct config field names (#17180)

<img width="2560" height="1353" alt="CleanShot 2026-01-16 at 00 55 56"
src="https://github.com/user-attachments/assets/c242071c-13d3-494d-bcce-9d45f36fcd77"
/>
This commit is contained in:
nitin
2026-01-16 02:25:47 +05:30
committed by GitHub
parent 0fa8098c50
commit 8908e0785f
4 changed files with 133 additions and 51 deletions
@@ -60,24 +60,25 @@ GRID SYSTEM:
WIDGET TYPES:
1. GRAPH with graphType "AGGREGATE" (KPI number):
- Requires: objectMetadataId, configuration.graphType, configuration.aggregateFieldMetadataId, configuration.aggregateOperation
- Example: { type: "GRAPH", objectMetadataId: "<opportunity-object-uuid>", configuration: { graphType: "AGGREGATE", aggregateFieldMetadataId: "<amount-field-uuid>", aggregateOperation: "SUM" } }
1. GRAPH with configurationType "AGGREGATE_CHART" (KPI number):
- Requires: objectMetadataId, configuration.configurationType, configuration.aggregateFieldMetadataId, configuration.aggregateOperation
- Example: { type: "GRAPH", objectMetadataId: "<opportunity-object-uuid>", configuration: { configurationType: "AGGREGATE_CHART", aggregateFieldMetadataId: "<amount-field-uuid>", aggregateOperation: "SUM" } }
2. GRAPH with graphType "VERTICAL_BAR" or "HORIZONTAL_BAR":
2. GRAPH with configurationType "BAR_CHART":
- Additional required: configuration.primaryAxisGroupByFieldMetadataId, configuration.layout ("VERTICAL" or "HORIZONTAL")
- Example: { type: "GRAPH", objectMetadataId: "<opportunity-object-uuid>", configuration: { configurationType: "BAR_CHART", aggregateFieldMetadataId: "<amount-field-uuid>", aggregateOperation: "COUNT", primaryAxisGroupByFieldMetadataId: "<stage-field-uuid>", layout: "VERTICAL" } }
3. GRAPH with configurationType "LINE_CHART":
- Additional required: configuration.primaryAxisGroupByFieldMetadataId
- Example: { graphType: "VERTICAL_BAR", aggregateFieldMetadataId: "<count-field-uuid>", aggregateOperation: "COUNT", primaryAxisGroupByFieldMetadataId: "<stage-field-uuid>" }
- Example: { type: "GRAPH", objectMetadataId: "<opportunity-object-uuid>", configuration: { configurationType: "LINE_CHART", aggregateFieldMetadataId: "<amount-field-uuid>", aggregateOperation: "SUM", primaryAxisGroupByFieldMetadataId: "<created-date-field-uuid>" } }
3. GRAPH with graphType "LINE":
- Same as bar charts, good for time series
4. GRAPH with configurationType "PIE_CHART":
- Additional required: configuration.groupByFieldMetadataId (note: different field name!)
- Example: { type: "GRAPH", objectMetadataId: "<opportunity-object-uuid>", configuration: { configurationType: "PIE_CHART", aggregateFieldMetadataId: "<id-field-uuid>", aggregateOperation: "COUNT", groupByFieldMetadataId: "<stage-field-uuid>" } }
4. GRAPH with graphType "PIE":
- Requires: objectMetadataId, aggregateFieldMetadataId, aggregateOperation, groupByFieldMetadataId
- Example: { graphType: "PIE", aggregateFieldMetadataId: "<id-field-uuid>", aggregateOperation: "COUNT", groupByFieldMetadataId: "<stage-field-uuid>" }
5. IFRAME: { type: "IFRAME", configuration: { configurationType: "IFRAME", url: "https://..." } }
5. IFRAME: { type: "IFRAME", configuration: { url: "https://..." } }
6. STANDALONE_RICH_TEXT: { type: "STANDALONE_RICH_TEXT", configuration: { body: "..." } }
6. STANDALONE_RICH_TEXT: { type: "STANDALONE_RICH_TEXT", configuration: { configurationType: "STANDALONE_RICH_TEXT", body: { ... } } }
AGGREGATION OPERATIONS: COUNT, SUM, AVG, MIN, MAX, COUNT_EMPTY, COUNT_NOT_EMPTY`,
inputSchema: createCompleteDashboardSchema,
@@ -1,7 +1,7 @@
import { z } from 'zod';
import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant';
import { GraphType } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-type.enum';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum';
export const gridPositionSchema = z.object({
@@ -28,7 +28,7 @@ export const widgetTypeSchema = z.enum([
// Graph configuration schema for AGGREGATE type (KPI numbers)
const aggregateChartConfigSchema = z.object({
graphType: z.literal(GraphType.AGGREGATE_CHART),
configurationType: z.literal(WidgetConfigurationType.AGGREGATE_CHART),
aggregateFieldMetadataId: z
.string()
.uuid()
@@ -48,7 +48,7 @@ const aggregateChartConfigSchema = z.object({
// Graph configuration schema for BAR charts
const barChartConfigSchema = z
.object({
graphType: z.literal(GraphType.BAR_CHART),
configurationType: z.literal(WidgetConfigurationType.BAR_CHART),
aggregateFieldMetadataId: z
.string()
.uuid()
@@ -87,8 +87,6 @@ const barChartConfigSchema = z
displayLegend: z.boolean().optional().default(true),
layout: z
.enum(['VERTICAL', 'HORIZONTAL'])
.optional()
.default('VERTICAL')
.describe('Layout orientation for bar charts'),
filter: z.record(z.string(), z.unknown()).optional(),
})
@@ -118,7 +116,7 @@ const barChartConfigSchema = z
// Graph configuration schema for LINE charts
const lineChartConfigSchema = z
.object({
graphType: z.literal(GraphType.LINE_CHART),
configurationType: z.literal(WidgetConfigurationType.LINE_CHART),
aggregateFieldMetadataId: z.string().uuid(),
aggregateOperation: z.nativeEnum(AggregateOperations),
primaryAxisGroupByFieldMetadataId: z.string().uuid(),
@@ -176,7 +174,7 @@ const lineChartConfigSchema = z
// Graph configuration schema for PIE charts
const pieChartConfigSchema = z
.object({
graphType: z.literal(GraphType.PIE_CHART),
configurationType: z.literal(WidgetConfigurationType.PIE_CHART),
aggregateFieldMetadataId: z.string().uuid(),
aggregateOperation: z.nativeEnum(AggregateOperations),
groupByFieldMetadataId: z
@@ -211,25 +209,37 @@ const pieChartConfigSchema = z
// Iframe configuration
const iframeConfigSchema = z.object({
url: z.string().url().describe('URL to embed'),
configurationType: z.literal(WidgetConfigurationType.IFRAME),
url: z.string().url().optional().describe('URL to embed'),
});
// Rich text configuration
const richTextConfigSchema = z.object({
body: z.string().optional().describe('Rich text content'),
configurationType: z.literal(WidgetConfigurationType.STANDALONE_RICH_TEXT),
body: z.unknown().optional().describe('Rich text content (RichTextV2Body)'),
});
export const graphConfigurationSchema = z.discriminatedUnion('graphType', [
aggregateChartConfigSchema,
barChartConfigSchema,
lineChartConfigSchema,
pieChartConfigSchema,
]);
export const graphConfigurationSchema = z.discriminatedUnion(
'configurationType',
[
aggregateChartConfigSchema,
barChartConfigSchema,
lineChartConfigSchema,
pieChartConfigSchema,
],
);
export const widgetConfigurationSchema = z
.union([graphConfigurationSchema, iframeConfigSchema, richTextConfigSchema])
.discriminatedUnion('configurationType', [
aggregateChartConfigSchema,
barChartConfigSchema,
lineChartConfigSchema,
pieChartConfigSchema,
iframeConfigSchema,
richTextConfigSchema,
])
.optional()
.describe('Widget configuration - structure depends on widget type');
// Export enums for documentation
export { AggregateOperations, GraphType as GraphType };
export { AggregateOperations, WidgetConfigurationType };
@@ -1,4 +1,4 @@
import { isDefined } from 'twenty-shared/utils';
import { isDefined, isEmptyObject } from 'twenty-shared/utils';
import { z } from 'zod';
import { type WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum';
@@ -24,7 +24,7 @@ const updateDashboardWidgetSchema = z.object({
.uuid()
.optional()
.describe('New object metadata ID'),
configuration: widgetConfigurationSchema,
configuration: widgetConfigurationSchema.optional(),
});
export const createUpdateDashboardWidgetTool = (
@@ -54,7 +54,16 @@ Only provide fields you want to change - others remain unchanged.`,
try {
const { widgetId, ...updates } = parameters;
const updateData = Object.fromEntries(
Object.entries(updates).filter(([, value]) => isDefined(value)),
Object.entries(updates).filter(([key, value]) => {
if (!isDefined(value)) {
return false;
}
if (key === 'configuration' && isEmptyObject(value)) {
return false;
}
return true;
}),
);
const widget = await deps.pageLayoutWidgetService.update({