feat: Add Agent Evaluation System and Refactor AI Modules (#16111)

## Summary

This PR introduces a comprehensive agent evaluation system and refactors
the AI module structure for better organization.

## Key Changes

### 🎯 Agent Evaluation System
- Added **Agent Turn Evaluation** entities, DTOs, and database schema
- New GraphQL mutations: `evaluateAgentTurn` and `runEvaluationInput`
- Added `evaluationInputs` field to Agent entity for storing test inputs
- New `AgentTurnGraderService` for automatic turn evaluation
- Added evaluation UI with new **Evals** and **Logs** tabs in agent
detail pages

### 🏗️ Entity & Module Refactoring
- Renamed `AgentChatMessage` → `AgentMessage` for clarity
- Consolidated chat entities: `AgentMessage`, `AgentTurn`, and
`AgentChatThread`
- Reorganized AI modules under `ai/` subdirectory structure
- Updated imports across codebase to reflect new module paths

### 🤖 New Agents & Roles
- Added **Dashboard Builder Agent** for dashboard creation and
management
- Added **Dashboard Manager Role** with appropriate permissions
- Updated role permissions to be more granular (users vs agents vs API
keys)

### 🔐 Permission System Updates
- Added `HTTP_REQUEST_TOOL` permission flag
- Updated Workflow Manager role permissions (restricted tool access)
- Enhanced permission flag types to differentiate between user/agent/API
key contexts
- Added `isRelevantForAgents`, `isRelevantForApiKeys`,
`isRelevantForUsers` to permission flags

### 📨 Message Role Enhancement
- Added `system` role to `AgentMessageRole` enum (alongside
user/assistant)
- Updated message handling to support system prompts

### 🎨 UI/UX Improvements
- New tabs in agent detail: **Evals** and **Logs**
- Added turn detail page: `/ai/agents/:agentId/turns/:turnId`
- Fixed text overflow in `SettingsListItemCardContent`
- Updated role applicability labels ("Assignable to Workspace Members")

### 🛠️ Technical Improvements
- Fixed Zod schema validation for UUID and Date fields (use string
validators)
- Updated `ToolRegistryService` to properly register HTTP tool with
permission flag
- Enhanced error handling in agent execution services
- Updated database migrations for new entity schema

## Database Migrations
- `1764210000000-add-system-role-to-agent-message.ts`
- `1764220000000-add-evaluation-inputs-to-agent.ts`
- `1764200000000-add-agent-turn-evaluation.ts`
- `1764100000000-refactor-agent-chat-entities.ts`

## Testing
- [ ] Agent evaluation flow tested
- [ ] Dashboard Builder agent tested
- [ ] Permission system validated
- [ ] UI tabs and navigation tested
- [ ] Database migrations run successfully

## Breaking Changes
⚠️ **Entity Rename**: `AgentChatMessage` renamed to `AgentMessage` -
GraphQL queries need updating

## Related Issues
<!-- Link any related issues here -->

## Screenshots
<!-- Add screenshots if applicable -->
This commit is contained in:
Félix Malfait
2025-11-27 08:25:40 +01:00
committed by GitHub
parent 35f81805b8
commit 4f20fd35c5
158 changed files with 2954 additions and 556 deletions
@@ -7,7 +7,7 @@ import { ApplicationService } from 'src/engine/core-modules/application/applicat
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { AgentService } from 'src/engine/metadata-modules/ai-agent/agent.service';
import { AgentService } from 'src/engine/metadata-modules/ai/ai-agent/agent.service';
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
@@ -1,6 +1,6 @@
import { type QueryRunner } from 'typeorm';
import { AgentChatMessageRole } from 'src/engine/metadata-modules/ai-chat/entities/agent-chat-message.entity';
import { AgentMessageRole } from 'src/engine/metadata-modules/ai/ai-agent-execution/entities/agent-message.entity';
import {
SEED_APPLE_WORKSPACE_ID,
SEED_YCOMBINATOR_WORKSPACE_ID,
@@ -8,8 +8,9 @@ import {
import { USER_WORKSPACE_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-user-workspaces.util';
const agentChatThreadTableName = 'agentChatThread';
const agentChatMessageTableName = 'agentChatMessage';
const agentChatMessagePartTableName = 'agentChatMessagePart';
const agentTurnTableName = 'agentTurn';
const agentMessageTableName = 'agentMessage';
const agentMessagePartTableName = 'agentMessagePart';
export const AGENT_DATA_SEED_IDS = {
APPLE_DEFAULT_AGENT: '20202020-0000-4000-8000-000000000001',
@@ -109,10 +110,12 @@ const seedChatMessages = async ({
}: SeedChatMessagesArgs) => {
let messageIds: string[];
let partIds: string[];
let turnIds: string[];
let messages: Array<{
id: string;
threadId: string;
role: AgentChatMessageRole;
turnId: string;
role: AgentMessageRole;
createdAt: Date;
}>;
let messageParts: Array<{
@@ -140,29 +143,37 @@ const seedChatMessages = async ({
AGENT_CHAT_MESSAGE_PART_DATA_SEED_IDS.APPLE_MESSAGE_3_PART_1,
AGENT_CHAT_MESSAGE_PART_DATA_SEED_IDS.APPLE_MESSAGE_4_PART_1,
];
turnIds = [
'20202020-0000-4000-8000-000000000061',
'20202020-0000-4000-8000-000000000062',
];
messages = [
{
id: messageIds[0],
threadId,
role: AgentChatMessageRole.USER,
turnId: turnIds[0],
role: AgentMessageRole.USER,
createdAt: new Date(baseTime.getTime()),
},
{
id: messageIds[1],
threadId,
role: AgentChatMessageRole.ASSISTANT,
turnId: turnIds[0],
role: AgentMessageRole.ASSISTANT,
createdAt: new Date(baseTime.getTime() + 5 * 60 * 1000),
},
{
id: messageIds[2],
threadId,
role: AgentChatMessageRole.USER,
turnId: turnIds[1],
role: AgentMessageRole.USER,
createdAt: new Date(baseTime.getTime() + 10 * 60 * 1000),
},
{
id: messageIds[3],
threadId,
role: AgentChatMessageRole.ASSISTANT,
turnId: turnIds[1],
role: AgentMessageRole.ASSISTANT,
createdAt: new Date(baseTime.getTime() + 15 * 60 * 1000),
},
];
@@ -217,29 +228,37 @@ const seedChatMessages = async ({
AGENT_CHAT_MESSAGE_PART_DATA_SEED_IDS.YCOMBINATOR_MESSAGE_3_PART_1,
AGENT_CHAT_MESSAGE_PART_DATA_SEED_IDS.YCOMBINATOR_MESSAGE_4_PART_1,
];
turnIds = [
'20202020-0000-4000-8000-000000000071',
'20202020-0000-4000-8000-000000000072',
];
messages = [
{
id: messageIds[0],
threadId,
role: AgentChatMessageRole.USER,
turnId: turnIds[0],
role: AgentMessageRole.USER,
createdAt: new Date(baseTime.getTime()),
},
{
id: messageIds[1],
threadId,
role: AgentChatMessageRole.ASSISTANT,
turnId: turnIds[0],
role: AgentMessageRole.ASSISTANT,
createdAt: new Date(baseTime.getTime() + 3 * 60 * 1000),
},
{
id: messageIds[2],
threadId,
role: AgentChatMessageRole.USER,
turnId: turnIds[1],
role: AgentMessageRole.USER,
createdAt: new Date(baseTime.getTime() + 8 * 60 * 1000),
},
{
id: messageIds[3],
threadId,
role: AgentChatMessageRole.ASSISTANT,
turnId: turnIds[1],
role: AgentMessageRole.ASSISTANT,
createdAt: new Date(baseTime.getTime() + 12 * 60 * 1000),
},
];
@@ -287,12 +306,32 @@ const seedChatMessages = async ({
);
}
// Create turns first
const turns = turnIds.map((id, index) => ({
id,
threadId,
createdAt: messages[index * 2].createdAt,
}));
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(`${schemaName}.${agentChatMessageTableName}`, [
.into(`${schemaName}.${agentTurnTableName}`, [
'id',
'threadId',
'createdAt',
])
.orIgnore()
.values(turns)
.execute();
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(`${schemaName}.${agentMessageTableName}`, [
'id',
'threadId',
'turnId',
'role',
'createdAt',
])
@@ -303,7 +342,7 @@ const seedChatMessages = async ({
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(`${schemaName}.${agentChatMessagePartTableName}`, [
.into(`${schemaName}.${agentMessagePartTableName}`, [
'id',
'messageId',
'orderIndex',
@@ -5,7 +5,7 @@ import { ApplicationModule } from 'src/engine/core-modules/application/applicati
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { AiAgentModule } from 'src/engine/metadata-modules/ai-agent/ai-agent.module';
import { AiAgentModule } from 'src/engine/metadata-modules/ai/ai-agent/ai-agent.module';
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
@@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
import { type WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface';
import { type AgentEntity } from 'src/engine/metadata-modules/ai-agent/entities/agent.entity';
import { type AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
import { type FlatAgent } from 'src/engine/metadata-modules/flat-agent/types/flat-agent.type';
import { transformStandardAgentDefinitionToFlatAgent } from 'src/engine/metadata-modules/flat-agent/utils/transform-standard-agent-definition-to-flat-agent.util';
import { type StandardAgentDefinition } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents/types/standard-agent-definition.interface';
@@ -6,7 +6,7 @@ import { IsNull, Not, type EntityManager } from 'typeorm';
import { ComparatorAction } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface';
import { type WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface';
import { AgentEntity } from 'src/engine/metadata-modules/ai-agent/entities/agent.entity';
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
import { transformAgentEntityToFlatAgent } from 'src/engine/metadata-modules/flat-agent/utils/transform-agent-entity-to-flat-agent.util';
import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
@@ -0,0 +1,70 @@
import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
import { type StandardAgentDefinition } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents/types/standard-agent-definition.interface';
import { DASHBOARD_MANAGER_ROLE } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-roles/roles/dashboard-manager-role';
export const DASHBOARD_BUILDER_AGENT: StandardAgentDefinition = {
standardId: '20202020-0002-0001-0001-000000000006',
name: 'dashboard-builder',
label: 'Dashboard Builder',
description: 'AI agent specialized in creating and managing dashboards',
icon: 'IconLayoutDashboard',
applicationId: null,
prompt: `You are a Dashboard Builder Agent for Twenty. You help users create and manage dashboards with widgets.
Capabilities:
- Create new dashboards from scratch
- Add, modify, and remove widgets from dashboards
- Configure widget types (VIEW, GRAPH, FIELDS, TIMELINE, TASKS, NOTES, FILES, EMAILS, CALENDAR, RICH_TEXT, IFRAME, WORKFLOW)
- Manage dashboard tabs and layouts
- Position widgets in a grid system (12-column layout)
Dashboard structure:
- Dashboard: Container with a title and pageLayout
- PageLayout: Contains tabs (type: DASHBOARD)
- PageLayoutTab: Contains widgets with a title, position, and layoutMode (grid/vertical-list/canvas)
- PageLayoutWidget: Individual widget with type, title, gridPosition, and optional configuration
Grid system:
- 12 columns total
- Grid positions: { row, column, rowSpan, columnSpan }
- Common sizes: Full width (columnSpan: 12), Half width (columnSpan: 6), Quarter width (columnSpan: 3)
- Typical heights: Small (rowSpan: 4), Medium (rowSpan: 6), Large (rowSpan: 8)
Widget types explained:
- VIEW: Display a filtered view of records (companies, people, opportunities, etc.)
- GRAPH: Show charts and visualizations of data
- FIELDS: Display specific fields from a record
- TIMELINE: Show activity timeline
- TASKS: Display tasks list
- NOTES: Show notes
- FILES: Display file attachments
- EMAILS: Show email communications
- CALENDAR: Display calendar events
- RICH_TEXT: Custom text content
- IFRAME: Embed external content
- WORKFLOW: Display workflow information
Approach:
- Ask clarifying questions about dashboard purpose and desired widgets
- Suggest appropriate widget types and layouts for the use case
- Create well-organized, visually balanced dashboards
- For modifications, first understand current structure
- Explain widget placement and purpose
- Consider responsive design (widgets stack on smaller screens)
Layout best practices:
- Place most important information at the top
- Group related widgets together
- Use consistent widget sizes when possible
- Leave some whitespace for visual clarity
- Consider logical reading order (left to right, top to bottom)
Prioritize user needs and dashboard usability.`,
modelId: DEFAULT_SMART_MODEL,
responseFormat: { type: 'text' },
isCustom: false,
standardRoleId: DASHBOARD_MANAGER_ROLE.standardId,
modelConfiguration: {},
outputStrategy: 'direct',
evaluationInputs: [],
};
@@ -1,5 +1,5 @@
import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
import { type StandardAgentDefinition } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents/types/standard-agent-definition.interface';
import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
import { DATA_MANIPULATOR_ROLE } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-roles/roles/data-manipulator-role';
export const DATA_MANIPULATOR_AGENT: StandardAgentDefinition = {
@@ -44,4 +44,5 @@ Prioritize data integrity and provide clear feedback on operations performed.`,
isCustom: false,
standardRoleId: DATA_MANIPULATOR_ROLE.standardId,
modelConfiguration: {},
evaluationInputs: [],
};
@@ -1,4 +1,4 @@
import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
import { type StandardAgentDefinition } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents/types/standard-agent-definition.interface';
export const HELPER_AGENT: StandardAgentDefinition = {
@@ -36,4 +36,5 @@ Always base answers on official Twenty documentation. Be patient and helpful.`,
responseFormat: { type: 'text' },
isCustom: false,
modelConfiguration: {},
evaluationInputs: [],
};
@@ -1,5 +1,5 @@
import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
import { type StandardAgentDefinition } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents/types/standard-agent-definition.interface';
import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
export const RESEARCHER_AGENT: StandardAgentDefinition = {
standardId: '20202020-0002-0001-0001-000000000005',
@@ -39,4 +39,5 @@ Be persistent in finding accurate information.`,
enabled: true,
},
},
evaluationInputs: [],
};
@@ -1,5 +1,5 @@
import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
import { type StandardAgentDefinition } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents/types/standard-agent-definition.interface';
import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
import { WORKFLOW_MANAGER_ROLE } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-roles/roles/workflow-manager-role';
export const WORKFLOW_BUILDER_AGENT: StandardAgentDefinition = {
@@ -22,6 +22,26 @@ Key concepts:
- Data flow: Use {{stepId.fieldName}} to reference previous step outputs
- Relationships: Use nested objects like {"company": {"id": "{{reference}}"}}
CRON Trigger Settings Schema:
For CRON triggers, settings.type must be one of these exact values:
1. "DAYS" - Daily schedule
- Requires: schedule: { day: number (1+), hour: number (0-23), minute: number (0-59) }
- Example: { type: "DAYS", schedule: { day: 1, hour: 9, minute: 0 }, outputSchema: {} }
2. "HOURS" - Hourly schedule (USE THIS FOR "EVERY HOUR")
- Requires: schedule: { hour: number (1+), minute: number (0-59) }
- Example: { type: "HOURS", schedule: { hour: 1, minute: 0 }, outputSchema: {} }
- This runs every X hours at Y minutes past the hour
3. "MINUTES" - Minute-based schedule
- Requires: schedule: { minute: number (1+) }
- Example: { type: "MINUTES", schedule: { minute: 15 }, outputSchema: {} }
4. "CUSTOM" - Custom cron pattern
- Requires: pattern: string (cron expression)
- Example: { type: "CUSTOM", pattern: "0 * * * *", outputSchema: {} }
Critical: Always rely on tool schema definitions
- The workflow creation tool provides comprehensive schemas with examples
- Follow schema definitions exactly for field names, types, and structures
@@ -41,4 +61,30 @@ Prioritize user understanding and workflow effectiveness.`,
standardRoleId: WORKFLOW_MANAGER_ROLE.standardId,
modelConfiguration: {},
outputStrategy: 'direct',
evaluationInputs: [
'Build a workflow that runs everyday at 9:00 AM, finds the companies added in the last 24 hours, and create task title Welcome {companyName} for each',
'Create a workflow that listens to company creation events and makes an http call to companies.twenty.com/{domain} to enrich them',
'Update the quick lead workflow to add an http request to Google.com as the last step',
'when a new lead is created, automatically send an email to the sales team with the lead details',
'I need a workflow that runs every monday morning and creates a weekly summary report of all closed deals',
'can you make a workflow to automatically assign new oppurtunities to sales reps based on territory?',
'create workflow that updates contact status to inactive if theres no activity for 90 days',
'Build automation to send followup email 3 days after first contact with prospect',
'i want to automatically create a task for account manager when deal reaches negotiation stage',
'setup a workflow that enriches company data from clearbit when new account is created',
'make a workflow to notify slack channel when deal amount is over $50k',
'need workflow that runs daily and finds all overdue tasks then sends reminder emails',
'Create automation to update lead score when contact opens email or clicks link',
'workflow to automatically create renewal opportunity 60 days before contract end date',
'can you build a flow that copies contact info to company record when deal is won?',
'I need to send a survey email 7 days after deal closes',
'make workflow that assigns leads round-robin style to available sales reps',
'create automation to tag contacts as "hot lead" when they visit pricing page 3 times',
'workflow that escalates support tickets to manager if not resolved in 48 hours',
'need a workflow to sync new contacts to mailchimp mailing list',
'build flow that updates deal stage to lost if no activity for 30 days',
'create workflow that sends birthday email to contacts on thier birthday',
'can i get a workflow that creates calendar event when meeting is scheduled with prospect',
'workflow to automatically generate quote pdf when opportunity moves to proposal stage',
],
};
@@ -1,3 +1,4 @@
import { DASHBOARD_BUILDER_AGENT } from './agents/dashboard-builder-agent';
import { DATA_MANIPULATOR_AGENT } from './agents/data-manipulator-agent';
import { HELPER_AGENT } from './agents/helper-agent';
import { RESEARCHER_AGENT } from './agents/researcher-agent';
@@ -7,6 +8,7 @@ import { type StandardAgentDefinition } from './types/standard-agent-definition.
export const standardAgentDefinitions = [
WORKFLOW_BUILDER_AGENT,
DATA_MANIPULATOR_AGENT,
DASHBOARD_BUILDER_AGENT,
HELPER_AGENT,
RESEARCHER_AGENT,
// CODE_AGENT,
@@ -1,4 +1,5 @@
import { ADMIN_ROLE } from './roles/admin-role';
import { DASHBOARD_MANAGER_ROLE } from './roles/dashboard-manager-role';
import { DATA_MANIPULATOR_ROLE } from './roles/data-manipulator-role';
import { WORKFLOW_MANAGER_ROLE } from './roles/workflow-manager-role';
import { type StandardRoleDefinition } from './types/standard-role-definition.interface';
@@ -7,4 +8,5 @@ export const standardRoleDefinitions = [
ADMIN_ROLE,
WORKFLOW_MANAGER_ROLE,
DATA_MANIPULATOR_ROLE,
DASHBOARD_MANAGER_ROLE,
] as const satisfies StandardRoleDefinition[];
@@ -0,0 +1,21 @@
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
import { type StandardRoleDefinition } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-roles/types/standard-role-definition.interface';
export const DASHBOARD_MANAGER_ROLE: StandardRoleDefinition = {
standardId: '20202020-0001-0001-0001-000000000005',
label: 'Dashboard Manager',
description: 'Role for creating and managing dashboards',
icon: 'IconLayoutDashboard',
isEditable: false,
canUpdateAllSettings: false,
canAccessAllTools: false,
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: true,
canSoftDeleteAllObjectRecords: false,
canDestroyAllObjectRecords: false,
canBeAssignedToUsers: false,
canBeAssignedToAgents: true,
canBeAssignedToApiKeys: false,
permissionFlags: [PermissionFlagType.LAYOUTS],
applicationId: null, // TODO: Replace with Twenty application ID
};
@@ -8,8 +8,8 @@ export const WORKFLOW_MANAGER_ROLE: StandardRoleDefinition = {
icon: 'IconSettingsAutomation',
isEditable: false,
canUpdateAllSettings: false,
canAccessAllTools: true,
canReadAllObjectRecords: true,
canAccessAllTools: false,
canReadAllObjectRecords: false,
canUpdateAllObjectRecords: true,
canSoftDeleteAllObjectRecords: false,
canDestroyAllObjectRecords: false,
@@ -5,7 +5,7 @@ import { ApplicationModule } from 'src/engine/core-modules/application/applicati
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { AiAgentRoleModule } from 'src/engine/metadata-modules/ai-agent-role/ai-agent-role.module';
import { AiAgentRoleModule } from 'src/engine/metadata-modules/ai/ai-agent-role/ai-agent-role.module';
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';