[AI] Add group_by_* database tools and centralize groupBy validation (#19406)
closes https://discord.com/channels/1130383047699738754/1488990242873806868 https://github.com/user-attachments/assets/2b2bbfba-3fa6-4114-9a26-96a61599d748 <img width="729" height="1283" alt="CleanShot 2026-04-07 at 20 43 06" src="https://github.com/user-attachments/assets/815efb97-81a0-44ea-8d79-b3ce7d5b00b6" /> <img width="708" height="1266" alt="CleanShot 2026-04-07 at 20 40 13" src="https://github.com/user-attachments/assets/692366bc-b629-4d9f-b6b8-ab670d5ad046" /> <img width="665" height="3524" alt="CleanShot 2026-04-07 at 20 42 00" src="https://github.com/user-attachments/assets/5e844e0f-7835-47a8-9d20-a5baddc0992d" />
This commit is contained in:
+31
@@ -19,6 +19,10 @@ import { generateUpdateRecordInputSchema } from 'src/engine/core-modules/record-
|
||||
import { DeleteToolInputSchema } from 'src/engine/core-modules/record-crud/zod-schemas/delete-tool.zod-schema';
|
||||
import { FindOneToolInputSchema } from 'src/engine/core-modules/record-crud/zod-schemas/find-one-tool.zod-schema';
|
||||
import { generateFindToolInputSchema } from 'src/engine/core-modules/record-crud/zod-schemas/find-tool.zod-schema';
|
||||
import {
|
||||
generateGroupByToolInputSchema,
|
||||
hasGroupByToolInputSchema,
|
||||
} from 'src/engine/core-modules/record-crud/zod-schemas/group-by-tool.zod-schema';
|
||||
import { ToolCategory } from 'twenty-shared/ai';
|
||||
import { type ToolDescriptor } from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type';
|
||||
import { type ToolIndexEntry } from 'src/engine/core-modules/tool-provider/types/tool-index-entry.type';
|
||||
@@ -138,6 +142,33 @@ export class DatabaseToolProvider implements ToolProvider {
|
||||
icon: flatObject.icon ?? undefined,
|
||||
operation: 'find_one',
|
||||
});
|
||||
|
||||
const groupBySchema = includeSchemas
|
||||
? generateGroupByToolInputSchema(objectMetadata, restrictedFields)
|
||||
: null;
|
||||
const hasGroupBySchema =
|
||||
groupBySchema !== null ||
|
||||
hasGroupByToolInputSchema(objectMetadata, restrictedFields);
|
||||
|
||||
if (hasGroupBySchema) {
|
||||
descriptors.push({
|
||||
name: `group_by_${snakePlural}`,
|
||||
description: `Group ${objectMetadata.labelPlural} records by one or two fields and compute an aggregate (COUNT, SUM, AVG, MIN, MAX, etc.). Use for questions like "how many deals per stage?" or "total revenue by company". Returns groups with dimension values and aggregate results, ordered by the aggregate value.`,
|
||||
category: ToolCategory.DATABASE_CRUD,
|
||||
...(includeSchemas &&
|
||||
groupBySchema && {
|
||||
inputSchema: z.toJSONSchema(groupBySchema),
|
||||
}),
|
||||
executionRef: {
|
||||
kind: 'database_crud',
|
||||
objectNameSingular: objectMetadata.nameSingular,
|
||||
operation: 'group_by',
|
||||
},
|
||||
objectName: objectMetadata.nameSingular,
|
||||
icon: flatObject.icon ?? undefined,
|
||||
operation: 'group_by',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (permission.canUpdateObjectRecords) {
|
||||
|
||||
+30
@@ -1,9 +1,12 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { type AggregateOperations } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { type ObjectRecordGroupBy } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
||||
|
||||
import { type FlatWorkspace } from 'src/engine/core-modules/workspace/types/flat-workspace.type';
|
||||
import { fromUserEntityToFlat } from 'src/engine/core-modules/user/utils/from-user-entity-to-flat.util';
|
||||
import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type';
|
||||
@@ -19,6 +22,7 @@ import { CreateManyRecordsService } from 'src/engine/core-modules/record-crud/se
|
||||
import { CreateRecordService } from 'src/engine/core-modules/record-crud/services/create-record.service';
|
||||
import { DeleteRecordService } from 'src/engine/core-modules/record-crud/services/delete-record.service';
|
||||
import { FindRecordsService } from 'src/engine/core-modules/record-crud/services/find-records.service';
|
||||
import { GroupByRecordsService } from 'src/engine/core-modules/record-crud/services/group-by-records.service';
|
||||
import { type FindRecordsParams } from 'src/engine/core-modules/record-crud/types/find-records-params.type';
|
||||
import { UpdateManyRecordsService } from 'src/engine/core-modules/record-crud/services/update-many-records.service';
|
||||
import { UpdateRecordService } from 'src/engine/core-modules/record-crud/services/update-record.service';
|
||||
@@ -48,6 +52,7 @@ export class ToolExecutorService {
|
||||
|
||||
constructor(
|
||||
private readonly findRecordsService: FindRecordsService,
|
||||
private readonly groupByRecordsService: GroupByRecordsService,
|
||||
private readonly createRecordService: CreateRecordService,
|
||||
private readonly createManyRecordsService: CreateManyRecordsService,
|
||||
private readonly updateRecordService: UpdateRecordService,
|
||||
@@ -181,6 +186,31 @@ export class ToolExecutorService {
|
||||
rolePermissionConfig: context.rolePermissionConfig,
|
||||
soft: true,
|
||||
});
|
||||
|
||||
case 'group_by': {
|
||||
const {
|
||||
groupBy,
|
||||
aggregateOperation,
|
||||
aggregateFieldName,
|
||||
limit: groupByLimit,
|
||||
orderBy: groupByOrderBy,
|
||||
...groupByFilter
|
||||
} = args;
|
||||
|
||||
return this.groupByRecordsService.execute({
|
||||
objectName: ref.objectNameSingular,
|
||||
groupBy: groupBy as ObjectRecordGroupBy,
|
||||
aggregateOperation: aggregateOperation as
|
||||
| keyof typeof AggregateOperations
|
||||
| undefined,
|
||||
aggregateFieldName: aggregateFieldName as string | undefined,
|
||||
limit: groupByLimit as number | undefined,
|
||||
orderBy: groupByOrderBy as 'ASC' | 'DESC' | undefined,
|
||||
filter: groupByFilter,
|
||||
authContext,
|
||||
rolePermissionConfig: context.rolePermissionConfig,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -5,4 +5,5 @@ export type DatabaseCrudOperation =
|
||||
| 'create_many'
|
||||
| 'update'
|
||||
| 'update_many'
|
||||
| 'delete';
|
||||
| 'delete'
|
||||
| 'group_by';
|
||||
|
||||
Reference in New Issue
Block a user