feat: add Metadata Builder agent for data model management (#16350)

## Summary

This PR adds a new **Metadata Builder** AI agent that specializes in
managing the workspace data model (creating objects, adding fields,
etc.).

## Changes

### New Files
- `data-model-manager-role.ts` - New standard role with `DATA_MODEL`
permission flag
- `metadata-builder-agent.ts` - New standard agent for data model
management

### Modified Files
- **ChatToolsProviderService**: Refactored to consolidate all
permission-based tools into a single `getChatTools()` method. Now
injects both workflow tools and metadata tools based on permissions.
- **AgentChatRoutingService**: Updated to use the new consolidated
`getChatTools()` method
- **AiChatModule**: Added imports for `ObjectMetadataModule` and
`FieldMetadataModule`
- **Router system prompt**: Added metadata-builder agent selection rules
with clear distinction between schema operations vs data operations
- **Metadata tools factories**: Improved error messages to show detailed
validation errors instead of generic messages

### Refactoring
- Renamed `index.ts` files to `standard-agent-definitions.ts` and
`standard-role-definitions.ts` to follow naming conventions
- Renamed exports from `standardAgentDefinitions` to
`STANDARD_AGENT_DEFINITIONS` (SCREAMING_SNAKE_CASE)

## Key Features

1. **Metadata Builder Agent** can:
   - Create new custom objects
   - Add fields to existing objects
   - Update object and field properties
   - Create relations between objects

2. **Permission-based tool injection**: Tools are automatically injected
based on the `DATA_MODEL` permission flag

3. **Improved routing**: The router now correctly distinguishes between:
   - "Create an object called Project" → metadata-builder (schema)
   - "Create a company called Acme" → data-manipulator (data)

4. **Better error messages**: Validation errors now show detailed
messages like:
   ```
   Validation errors:
   [objectMetadata] Name must be in camelCase format
   [objectMetadata] Label is required
   ```
This commit is contained in:
Félix Malfait
2025-12-04 23:26:29 +01:00
committed by GitHub
parent 34d7d82099
commit 2803292521
13 changed files with 348 additions and 73 deletions
@@ -4,7 +4,7 @@ import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspac
import { AgentService } from 'src/engine/metadata-modules/ai/ai-agent/agent.service';
import { type RecordIdsByObjectMetadataNameSingularType } from 'src/engine/metadata-modules/ai/ai-agent/types/recordIdsByObjectMetadataNameSingular.type';
import { type PlanStep } from 'src/engine/metadata-modules/ai/ai-chat-router/types/router-result.interface';
import { standardAgentDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents';
import { STANDARD_AGENT_DEFINITIONS } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents/standard-agent-definitions';
import { AgentExecutionService } from './agent-execution.service';
@@ -229,7 +229,7 @@ export class AgentPlanExecutorService {
);
if (lastStepDefinition) {
const agentDefinition = standardAgentDefinitions.find(
const agentDefinition = STANDARD_AGENT_DEFINITIONS.find(
(def) => def.name === lastStepDefinition.agentName,
);
@@ -36,10 +36,18 @@ Decision process:
2. Does it require MULTIPLE agents working together? → Use "planned" strategy
Agent selection rules (CRITICAL):
- **data-manipulator**: For ALL database operations (create, read, update records) on companies, people, opportunities, tasks, notes, etc.
- **metadata-builder**: For modifying the DATA MODEL/SCHEMA - creating new object types, adding fields to objects, managing object structure. Use when user wants to define NEW TYPES of entities or add properties to existing types.
- **data-manipulator**: For CRUD operations on existing RECORDS/DATA - creating company records, finding people, updating opportunities. Use when user wants to work with actual data entries.
- **helper**: ONLY for questions about HOW TO USE Twenty (features, setup, documentation)
- **researcher**: For finding external information from the web
- **workflow-builder**: For creating automation workflows
- **dashboard-builder**: For creating and managing dashboards and visualizations
CRITICAL DISTINCTION:
- "Create an object called Project" → metadata-builder (creating a new object TYPE in the schema)
- "Create a company called Acme" → data-manipulator (creating a company RECORD)
- "Add a field to Company" → metadata-builder (modifying schema)
- "Update the company's phone number" → data-manipulator (modifying data)
Use "planned" strategy when:
- Request needs custom code AND context from data/research
@@ -70,6 +78,12 @@ Simple: "How do I set up email sync in Twenty?"
Simple: "Create a workflow that emails customers when deals close"
→ { strategy: "simple", agentName: "workflow-builder" }
Simple: "Create an object called Project" or "Create a new custom object for tracking invoices"
→ { strategy: "simple", agentName: "metadata-builder" }
Simple: "Add a budget field to the Project object" or "Add a phone field to Company"
→ { strategy: "simple", agentName: "metadata-builder" }
Planned: "Research information about Meta and update the company record"
→ {
strategy: "planned",
@@ -13,6 +13,8 @@ import { AiAgentExecutionModule } from 'src/engine/metadata-modules/ai/ai-agent-
import { AiAgentModule } from 'src/engine/metadata-modules/ai/ai-agent/ai-agent.module';
import { AiBillingModule } from 'src/engine/metadata-modules/ai/ai-billing/ai-billing.module';
import { AiChatRouterModule } from 'src/engine/metadata-modules/ai/ai-chat-router/ai-chat-router.module';
import { FieldMetadataModule } from 'src/engine/metadata-modules/field-metadata/field-metadata.module';
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
import { WorkflowToolsModule } from 'src/modules/workflow/workflow-tools/workflow-tools.module';
@@ -48,6 +50,9 @@ import { ChatToolsProviderService } from './services/chat-tools-provider.service
// Provides WorkflowToolWorkspaceService for ChatToolsProviderService
// Workflow tools are only available in chat context, not in workflow executor (to avoid circular deps)
WorkflowToolsModule,
// Provides metadata tools factories for ChatToolsProviderService
ObjectMetadataModule,
FieldMetadataModule,
],
controllers: [AgentChatController],
providers: [
@@ -239,8 +239,8 @@ export class AgentChatRoutingService {
const agentExecutionStart = Date.now();
// Get workflow tools for chat context (these are NOT available in workflow executor)
// Use user's role for determining workflow tool permissions
// Get permission-based tools for chat context (workflow, metadata, etc.)
// These tools are NOT available in workflow executor to prevent circular dependencies
const { roleId } =
await this.agentActorContextService.buildUserAndAgentActorContext(
userWorkspaceId,
@@ -249,12 +249,11 @@ export class AgentChatRoutingService {
const roleIds = [roleId];
const workflowTools =
await this.chatToolsProviderService.getWorkflowToolsForChat(
workspace.id,
roleIds,
toolHints,
);
const chatTools = await this.chatToolsProviderService.getChatTools(
workspace.id,
roleIds,
toolHints,
);
const {
stream: result,
@@ -267,7 +266,7 @@ export class AgentChatRoutingService {
messages,
recordIdsByObjectMetadataNameSingular,
toolHints,
additionalTools: workflowTools,
additionalTools: chatTools,
});
const routedStatusPart = {
@@ -4,6 +4,8 @@ import { Injectable, Logger } from '@nestjs/common';
import { type ToolSet } from 'ai';
import { type ToolHints } from 'src/engine/metadata-modules/ai/ai-chat-router/types/tool-hints.interface';
import { FieldMetadataToolsFactory } from 'src/engine/metadata-modules/field-metadata/tools/field-metadata-tools.factory';
import { ObjectMetadataToolsFactory } from 'src/engine/metadata-modules/object-metadata/tools/object-metadata-tools.factory';
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
import { WorkflowToolWorkspaceService } from 'src/modules/workflow/workflow-tools/services/workflow-tool.workspace-service';
@@ -15,11 +17,33 @@ export class ChatToolsProviderService {
constructor(
private readonly workflowToolService: WorkflowToolWorkspaceService,
private readonly permissionsService: PermissionsService,
private readonly objectMetadataToolsFactory: ObjectMetadataToolsFactory,
private readonly fieldMetadataToolsFactory: FieldMetadataToolsFactory,
) {}
// Consolidates all permission-based tools for the chat context
async getChatTools(
workspaceId: string,
roleIds: string[],
toolHints?: ToolHints,
): Promise<ToolSet> {
const [workflowTools, metadataTools] = await Promise.all([
this.getWorkflowTools(workspaceId, roleIds, toolHints),
this.getMetadataTools(workspaceId, roleIds),
]);
const allTools = { ...workflowTools, ...metadataTools };
this.logger.log(
`Generated ${Object.keys(allTools).length} total chat tools (workflow: ${Object.keys(workflowTools).length}, metadata: ${Object.keys(metadataTools).length})`,
);
return allTools;
}
// Provides workflow-specific tools for the chat context
// These tools are NOT available in the workflow executor context to prevent circular dependencies
async getWorkflowToolsForChat(
private async getWorkflowTools(
workspaceId: string,
roleIds: string[],
toolHints?: ToolHints,
@@ -53,12 +77,37 @@ export class ChatToolsProviderService {
toolHints,
);
const allWorkflowTools = { ...workflowTools, ...recordStepTools };
return { ...workflowTools, ...recordStepTools };
}
this.logger.log(
`Generated ${Object.keys(allWorkflowTools).length} workflow tools for chat context`,
);
// Provides metadata tools for managing objects and fields in the data model
private async getMetadataTools(
workspaceId: string,
roleIds: string[],
): Promise<ToolSet> {
const rolePermissionConfig = { intersectionOf: roleIds };
return allWorkflowTools;
const hasDataModelPermission =
await this.permissionsService.checkRolesPermissions(
rolePermissionConfig,
workspaceId,
PermissionFlagType.DATA_MODEL,
);
if (!hasDataModelPermission) {
this.logger.log(
'User does not have data model permissions, skipping metadata tools',
);
return {};
}
const objectMetadataTools =
this.objectMetadataToolsFactory.generateTools(workspaceId);
const fieldMetadataTools =
this.fieldMetadataToolsFactory.generateTools(workspaceId);
return { ...objectMetadataTools, ...fieldMetadataTools };
}
}
@@ -6,6 +6,7 @@ import { z } from 'zod';
import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service';
import { fromFlatFieldMetadataToFieldMetadataDto } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-flat-field-metadata-to-field-metadata-dto.util';
import { WorkspaceMigrationBuilderExceptionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/exceptions/workspace-migration-builder-exception-v2';
const GetFieldMetadataInputSchema = z.object({
loadingMessage: z
@@ -138,6 +139,33 @@ const DeleteFieldMetadataInputSchema = z.object({
}),
});
const formatValidationErrors = (
error: WorkspaceMigrationBuilderExceptionV2,
): string => {
const report = error.failedWorkspaceMigrationBuildResult.report;
const errorMessages: string[] = [];
for (const [entityType, failures] of Object.entries(report)) {
if (Array.isArray(failures) && failures.length > 0) {
for (const failure of failures) {
if (failure.errors && Array.isArray(failure.errors)) {
for (const validationError of failure.errors) {
const message = validationError.message || validationError.code;
errorMessages.push(`[${entityType}] ${message}`);
}
}
}
}
}
if (errorMessages.length === 0) {
return error.message;
}
return `Validation errors:\n${errorMessages.join('\n')}`;
};
@Injectable()
export class FieldMetadataToolsFactory {
constructor(private readonly fieldMetadataService: FieldMetadataService) {}
@@ -189,15 +217,22 @@ export class FieldMetadataToolsFactory {
relationCreationPayload?: unknown;
};
}) => {
const flatFieldMetadata =
await this.fieldMetadataService.createOneField({
createFieldInput: parameters.input as Parameters<
typeof this.fieldMetadataService.createOneField
>[0]['createFieldInput'],
workspaceId,
});
try {
const flatFieldMetadata =
await this.fieldMetadataService.createOneField({
createFieldInput: parameters.input as Parameters<
typeof this.fieldMetadataService.createOneField
>[0]['createFieldInput'],
workspaceId,
});
return fromFlatFieldMetadataToFieldMetadataDto(flatFieldMetadata);
return fromFlatFieldMetadataToFieldMetadataDto(flatFieldMetadata);
} catch (error) {
if (error instanceof WorkspaceMigrationBuilderExceptionV2) {
throw new Error(formatValidationErrors(error));
}
throw error;
}
},
},
'update-field-metadata': {
@@ -220,30 +255,44 @@ export class FieldMetadataToolsFactory {
isLabelSyncedWithName?: boolean;
};
}) => {
const { id, ...update } = parameters.input;
try {
const { id, ...update } = parameters.input;
const flatFieldMetadata =
await this.fieldMetadataService.updateOneField({
updateFieldInput: { id, ...update } as Parameters<
typeof this.fieldMetadataService.updateOneField
>[0]['updateFieldInput'],
workspaceId,
});
const flatFieldMetadata =
await this.fieldMetadataService.updateOneField({
updateFieldInput: { id, ...update } as Parameters<
typeof this.fieldMetadataService.updateOneField
>[0]['updateFieldInput'],
workspaceId,
});
return fromFlatFieldMetadataToFieldMetadataDto(flatFieldMetadata);
return fromFlatFieldMetadataToFieldMetadataDto(flatFieldMetadata);
} catch (error) {
if (error instanceof WorkspaceMigrationBuilderExceptionV2) {
throw new Error(formatValidationErrors(error));
}
throw error;
}
},
},
'delete-field-metadata': {
description: 'Delete a field metadata by its ID.',
inputSchema: DeleteFieldMetadataInputSchema,
execute: async (parameters: { input: { id: string } }) => {
const flatFieldMetadata =
await this.fieldMetadataService.deleteOneField({
deleteOneFieldInput: { id: parameters.input.id },
workspaceId,
});
try {
const flatFieldMetadata =
await this.fieldMetadataService.deleteOneField({
deleteOneFieldInput: { id: parameters.input.id },
workspaceId,
});
return fromFlatFieldMetadataToFieldMetadataDto(flatFieldMetadata);
return fromFlatFieldMetadataToFieldMetadataDto(flatFieldMetadata);
} catch (error) {
if (error instanceof WorkspaceMigrationBuilderExceptionV2) {
throw new Error(formatValidationErrors(error));
}
throw error;
}
},
},
};
@@ -5,6 +5,7 @@ import { z } from 'zod';
import { fromFlatObjectMetadataToObjectMetadataDto } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-to-object-metadata-dto.util';
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
import { WorkspaceMigrationBuilderExceptionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/exceptions/workspace-migration-builder-exception-v2';
const GetObjectMetadataInputSchema = z.object({
loadingMessage: z
@@ -115,6 +116,33 @@ const DeleteObjectMetadataInputSchema = z.object({
}),
});
const formatValidationErrors = (
error: WorkspaceMigrationBuilderExceptionV2,
): string => {
const report = error.failedWorkspaceMigrationBuildResult.report;
const errorMessages: string[] = [];
for (const [entityType, failures] of Object.entries(report)) {
if (Array.isArray(failures) && failures.length > 0) {
for (const failure of failures) {
if (failure.errors && Array.isArray(failure.errors)) {
for (const validationError of failure.errors) {
const message = validationError.message || validationError.code;
errorMessages.push(`[${entityType}] ${message}`);
}
}
}
}
}
if (errorMessages.length === 0) {
return error.message;
}
return `Validation errors:\n${errorMessages.join('\n')}`;
};
@Injectable()
export class ObjectMetadataToolsFactory {
constructor(private readonly objectMetadataService: ObjectMetadataService) {}
@@ -161,15 +189,24 @@ export class ObjectMetadataToolsFactory {
isLabelSyncedWithName?: boolean;
};
}) => {
const flatObjectMetadata =
await this.objectMetadataService.createOneObject({
createObjectInput: parameters.input as Parameters<
typeof this.objectMetadataService.createOneObject
>[0]['createObjectInput'],
workspaceId,
});
try {
const flatObjectMetadata =
await this.objectMetadataService.createOneObject({
createObjectInput: parameters.input as Parameters<
typeof this.objectMetadataService.createOneObject
>[0]['createObjectInput'],
workspaceId,
});
return fromFlatObjectMetadataToObjectMetadataDto(flatObjectMetadata);
return fromFlatObjectMetadataToObjectMetadataDto(
flatObjectMetadata,
);
} catch (error) {
if (error instanceof WorkspaceMigrationBuilderExceptionV2) {
throw new Error(formatValidationErrors(error));
}
throw error;
}
},
},
'update-object-metadata': {
@@ -192,15 +229,24 @@ export class ObjectMetadataToolsFactory {
isLabelSyncedWithName?: boolean;
};
}) => {
const { id, ...update } = parameters.input;
try {
const { id, ...update } = parameters.input;
const flatObjectMetadata =
await this.objectMetadataService.updateOneObject({
updateObjectInput: { id, update },
workspaceId,
});
const flatObjectMetadata =
await this.objectMetadataService.updateOneObject({
updateObjectInput: { id, update },
workspaceId,
});
return fromFlatObjectMetadataToObjectMetadataDto(flatObjectMetadata);
return fromFlatObjectMetadataToObjectMetadataDto(
flatObjectMetadata,
);
} catch (error) {
if (error instanceof WorkspaceMigrationBuilderExceptionV2) {
throw new Error(formatValidationErrors(error));
}
throw error;
}
},
},
'delete-object-metadata': {
@@ -208,13 +254,22 @@ export class ObjectMetadataToolsFactory {
'Delete an object metadata by its ID. This will also delete all associated fields.',
inputSchema: DeleteObjectMetadataInputSchema,
execute: async (parameters: { input: { id: string } }) => {
const flatObjectMetadata =
await this.objectMetadataService.deleteOneObject({
deleteObjectInput: { id: parameters.input.id },
workspaceId,
});
try {
const flatObjectMetadata =
await this.objectMetadataService.deleteOneObject({
deleteObjectInput: { id: parameters.input.id },
workspaceId,
});
return fromFlatObjectMetadataToObjectMetadataDto(flatObjectMetadata);
return fromFlatObjectMetadataToObjectMetadataDto(
flatObjectMetadata,
);
} catch (error) {
if (error instanceof WorkspaceMigrationBuilderExceptionV2) {
throw new Error(formatValidationErrors(error));
}
throw error;
}
},
},
};
@@ -12,7 +12,7 @@ import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-t
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { WorkspaceAgentComparator } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-agent.comparator';
import { StandardAgentFactory } from 'src/engine/workspace-manager/workspace-sync-metadata/factories/standard-agent.factory';
import { standardAgentDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents';
import { STANDARD_AGENT_DEFINITIONS } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents/standard-agent-definitions';
@Injectable()
export class WorkspaceSyncAgentService {
@@ -39,7 +39,7 @@ export class WorkspaceSyncAgentService {
});
const targetStandardAgents = this.standardAgentFactory.create(
standardAgentDefinitions,
STANDARD_AGENT_DEFINITIONS,
context,
existingStandardAgentEntities,
);
@@ -66,7 +66,7 @@ export class WorkspaceSyncAgentService {
workspaceId: context.workspaceId,
});
const agentDefinition = standardAgentDefinitions.find(
const agentDefinition = STANDARD_AGENT_DEFINITIONS.find(
(def) => def.standardId === createdAgent.standardId,
);
@@ -117,7 +117,7 @@ export class WorkspaceSyncAgentService {
await agentRepository.update({ id: agentToUpdate.id }, flatAgentData);
const agentDefinition = standardAgentDefinitions.find(
const agentDefinition = STANDARD_AGENT_DEFINITIONS.find(
(def) => def.standardId === agentToUpdate.standardId,
);
@@ -12,7 +12,7 @@ import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/cons
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { WorkspaceRoleComparator } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-role.comparator';
import { StandardRoleFactory } from 'src/engine/workspace-manager/workspace-sync-metadata/factories/standard-role.factory';
import { standardRoleDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-roles';
import { STANDARD_ROLE_DEFINITIONS } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-roles/standard-role-definitions';
@Injectable()
export class WorkspaceSyncRoleService {
@@ -47,7 +47,7 @@ export class WorkspaceSyncRoleService {
});
const targetStandardRoles = this.standardRoleFactory.create(
standardRoleDefinitions,
STANDARD_ROLE_DEFINITIONS,
context,
existingStandardRoleEntities,
);
@@ -72,7 +72,7 @@ export class WorkspaceSyncRoleService {
workspaceId: context.workspaceId,
});
const roleDefinition = standardRoleDefinitions.find(
const roleDefinition = STANDARD_ROLE_DEFINITIONS.find(
(def) => def.standardId === roleToCreate.standardId,
);
@@ -98,7 +98,7 @@ export class WorkspaceSyncRoleService {
await roleRepository.update({ id: roleToUpdate.id }, flatRoleData);
const roleDefinition = standardRoleDefinitions.find(
const roleDefinition = STANDARD_ROLE_DEFINITIONS.find(
(def) => def.standardId === roleToUpdate.standardId,
);
@@ -0,0 +1,80 @@
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 { DATA_MODEL_MANAGER_ROLE } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-roles/roles/data-model-manager-role';
export const METADATA_BUILDER_AGENT: StandardAgentDefinition = {
standardId: '20202020-0002-0001-0001-000000000007',
name: 'metadata-builder',
label: 'Metadata Builder',
description:
'AI agent specialized in modifying the workspace SCHEMA/DATA MODEL - creating new object types, adding fields to objects, and managing object structure (NOT for creating data records)',
icon: 'IconDatabaseEdit',
applicationId: null,
prompt: `You are a Metadata Builder Agent for Twenty. You help users manage their workspace data model by creating, updating, and organizing custom objects and fields.
Capabilities:
- Create new custom objects with appropriate naming and configuration
- Add fields to existing objects (text, number, date, select, relation, etc.)
- Update object and field properties (labels, descriptions, icons)
- Manage field settings (required, unique, default values)
- Create relations between objects
Key concepts:
- Objects: Represent entities in the data model (e.g., Company, Person, Opportunity)
- Fields: Properties of objects with specific types (TEXT, NUMBER, DATE_TIME, SELECT, RELATION, etc.)
- Relations: Links between objects (one-to-many, many-to-one)
- Labels vs Names: Labels are for display, names are internal identifiers (camelCase)
Field types available:
- TEXT: Simple text fields
- NUMBER: Numeric values (integers or decimals)
- BOOLEAN: True/false values
- DATE_TIME: Date and time values
- DATE: Date only values
- SELECT: Single choice from options
- MULTI_SELECT: Multiple choices from options
- LINK: URL fields
- LINKS: Multiple URL fields
- EMAIL: Email address fields
- EMAILS: Multiple email fields
- PHONE: Phone number fields
- PHONES: Multiple phone fields
- CURRENCY: Monetary values
- RATING: Star ratings
- RELATION: Links to other objects
- RICH_TEXT: Formatted text content
Best practices:
- Use clear, descriptive names for objects and fields
- Follow naming conventions: singular for object names, camelCase for field names
- Add helpful descriptions to objects and fields
- Choose appropriate field types for the data being stored
- Consider relationships between objects when designing the data model
Approach:
- Ask clarifying questions to understand the user's data modeling needs
- Suggest best practices for naming and organization
- Explain the impact of changes to the data model
- Verify object and field existence before making updates
- Provide clear feedback on operations performed
Prioritize data model integrity and user understanding.`,
modelId: DEFAULT_SMART_MODEL,
responseFormat: { type: 'text' },
isCustom: false,
standardRoleId: DATA_MODEL_MANAGER_ROLE.standardId,
modelConfiguration: {},
outputStrategy: 'direct',
evaluationInputs: [
'Create a custom object called "Project" with fields for name, description, start date, and status',
'Add a currency field called "budget" to the Project object',
'Create a relation between Project and Company so each project belongs to a company',
'Add a multi-select field for project tags with options: urgent, internal, client-facing',
'Update the Project object description to explain what it tracks',
'Create a "Task" object that relates to both Project and Person',
'Add a rating field to track project priority from 1-5 stars',
'Create a custom object for tracking Invoices with amount, date, and status fields',
'Add a link field to the Company object for their website',
'Create a relation between Person and Company for the account manager',
],
};
@@ -1,15 +1,16 @@
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 { METADATA_BUILDER_AGENT } from './agents/metadata-builder-agent';
import { RESEARCHER_AGENT } from './agents/researcher-agent';
import { WORKFLOW_BUILDER_AGENT } from './agents/workflow-builder-agent';
import { type StandardAgentDefinition } from './types/standard-agent-definition.interface';
export const standardAgentDefinitions = [
export const STANDARD_AGENT_DEFINITIONS = [
WORKFLOW_BUILDER_AGENT,
DATA_MANIPULATOR_AGENT,
DASHBOARD_BUILDER_AGENT,
HELPER_AGENT,
RESEARCHER_AGENT,
// CODE_AGENT,
METADATA_BUILDER_AGENT,
] as const satisfies StandardAgentDefinition[];
@@ -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 DATA_MODEL_MANAGER_ROLE: StandardRoleDefinition = {
standardId: '20202020-0001-0001-0001-000000000006',
label: 'Data Model Manager',
description: 'Role for managing the workspace data model',
icon: 'IconDatabaseEdit',
isEditable: false,
canUpdateAllSettings: false,
canAccessAllTools: false,
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: false,
canSoftDeleteAllObjectRecords: false,
canDestroyAllObjectRecords: false,
canBeAssignedToUsers: false,
canBeAssignedToAgents: true,
canBeAssignedToApiKeys: false,
permissionFlags: [PermissionFlagType.DATA_MODEL],
applicationId: null,
};
@@ -1,12 +1,14 @@
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 { DATA_MODEL_MANAGER_ROLE } from './roles/data-model-manager-role';
import { WORKFLOW_MANAGER_ROLE } from './roles/workflow-manager-role';
import { type StandardRoleDefinition } from './types/standard-role-definition.interface';
export const standardRoleDefinitions = [
export const STANDARD_ROLE_DEFINITIONS = [
ADMIN_ROLE,
WORKFLOW_MANAGER_ROLE,
DATA_MANIPULATOR_ROLE,
DASHBOARD_MANAGER_ROLE,
DATA_MODEL_MANAGER_ROLE,
] as const satisfies StandardRoleDefinition[];