feat: simplify AI chat architecture and add record links (#16463)
## Summary This PR significantly simplifies the AI chat architecture by removing complex routing/planning mechanisms and introduces clickable record links in AI responses. ## Changes ### AI Chat Architecture Simplification - **Removed** the entire `ai-chat-router` module (~850 lines) including: - Strategy decider service - Plan generator service - Complex routing logic - **Removed** agent execution planning services (~700 lines): - `agent-execution.service.ts` - `agent-plan-executor.service.ts` - `agent-tool-generator.service.ts` - **Added** centralized `ToolRegistryService` for tool management: - Builds searchable tool index (database, action, workflow tools) - Provides tool lookup by name - Supports agent search for loading expertise - **Added** `ChatExecutionService` as simple replacement: - Includes full tool catalog in system prompt - Pre-loads common tools (find/create/update for company, person, opportunity, task, note) - Uses `load_tools` mechanism for dynamic tool activation - Enables native web search by default ### Record References in AI Responses - Added `recordReferences` field to tool outputs for create, find, and update operations - Implemented `[[record:objectName:recordId:displayName]]` syntax for AI to reference records - Created `RecordLink` component that renders clickable chips with object icons - Integrated record link parsing into the markdown renderer - Users can now click directly on created/found records in AI responses ### Workflow Agent Fixes - Fixed cache invalidation issue when creating agents in workflows - Added default prompt for workflow-created agents to prevent validation errors - Relaxed agent validation to only check properties being updated (not all required properties) ### Code Quality Improvements - Extracted `getRecordDisplayName` utility that mirrors frontend's `getLabelIdentifierFieldValue` logic - Uses object metadata to determine the correct label identifier field - Handles `FULL_NAME` composite type for person/workspaceMember objects - Shared across create, find, and update record services ## Net Impact - **~1,200 lines deleted** (complex routing/planning code) - **~500 lines added** (simpler tool registry + record links) - Significantly reduced code complexity - Better tool discovery through full catalog in system prompt - Improved UX with clickable record references ## Testing - Typecheck passes - Lint passes - Manual testing of AI chat with record creation and linking
This commit is contained in:
+10
@@ -9,6 +9,7 @@ import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { type ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
|
||||
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
|
||||
import { WorkflowVersionStepOperationsWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-operations.workspace-service';
|
||||
import {
|
||||
@@ -28,6 +29,7 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
|
||||
let objectMetadataRepository: jest.Mocked<any>;
|
||||
let workflowCommonWorkspaceService: jest.Mocked<WorkflowCommonWorkspaceService>;
|
||||
let aiAgentRoleService: jest.Mocked<AiAgentRoleService>;
|
||||
let workspaceCacheService: jest.Mocked<WorkspaceCacheService>;
|
||||
|
||||
beforeEach(async () => {
|
||||
serverlessFunctionService = {
|
||||
@@ -65,6 +67,10 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
|
||||
deleteAgentOnlyRoleIfUnused: jest.fn(),
|
||||
} as unknown as jest.Mocked<AiAgentRoleService>;
|
||||
|
||||
workspaceCacheService = {
|
||||
flush: jest.fn(),
|
||||
} as unknown as jest.Mocked<WorkspaceCacheService>;
|
||||
|
||||
twentyORMGlobalManager = {
|
||||
getRepositoryForWorkspace: jest.fn(),
|
||||
} as unknown as jest.Mocked<TwentyORMGlobalManager>;
|
||||
@@ -104,6 +110,10 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
|
||||
provide: AiAgentRoleService,
|
||||
useValue: aiAgentRoleService,
|
||||
},
|
||||
{
|
||||
provide: WorkspaceCacheService,
|
||||
useValue: workspaceCacheService,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
|
||||
+6
-1
@@ -16,6 +16,7 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat
|
||||
import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
|
||||
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
import {
|
||||
WorkflowVersionStepException,
|
||||
WorkflowVersionStepExceptionCode,
|
||||
@@ -61,6 +62,7 @@ export class WorkflowVersionStepOperationsWorkspaceService {
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService,
|
||||
private readonly aiAgentRoleService: AiAgentRoleService,
|
||||
private readonly workspaceCacheService: WorkspaceCacheService,
|
||||
) {}
|
||||
|
||||
async runWorkflowVersionStepDeletionSideEffects({
|
||||
@@ -369,7 +371,8 @@ export class WorkflowVersionStepOperationsWorkspaceService {
|
||||
label: 'Workflow Agent' + workflowVersion.workflowId.substring(0, 4),
|
||||
icon: 'IconRobot',
|
||||
description: '',
|
||||
prompt: '',
|
||||
prompt:
|
||||
'You are a helpful AI assistant. Complete the task based on the workflow context.',
|
||||
modelId: DEFAULT_SMART_MODEL,
|
||||
responseFormat: { type: 'text' },
|
||||
workspaceId,
|
||||
@@ -383,6 +386,8 @@ export class WorkflowVersionStepOperationsWorkspaceService {
|
||||
);
|
||||
}
|
||||
|
||||
await this.workspaceCacheService.flush(workspaceId, ['flatAgentMaps']);
|
||||
|
||||
return {
|
||||
builtStep: {
|
||||
...baseStep,
|
||||
|
||||
+2
@@ -8,6 +8,7 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat
|
||||
import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { ServerlessFunctionModule } from 'src/engine/metadata-modules/serverless-function/serverless-function.module';
|
||||
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
|
||||
import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module';
|
||||
import { WorkflowSchemaModule } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.module';
|
||||
import { WorkflowVersionStepCreationWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-creation.workspace-service';
|
||||
@@ -23,6 +24,7 @@ import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workfl
|
||||
ServerlessFunctionModule,
|
||||
WorkflowCommonModule,
|
||||
AiAgentRoleModule,
|
||||
WorkspaceCacheModule,
|
||||
NestjsQueryTypeOrmModule.forFeature([
|
||||
ObjectMetadataEntity,
|
||||
AgentEntity,
|
||||
|
||||
Reference in New Issue
Block a user