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:
+1
@@ -168,6 +168,7 @@ export class FlatAgentValidatorService {
|
||||
validationResult.errors.push(
|
||||
...validateAgentRequiredProperties({
|
||||
flatAgent: optimisticFlatAgent,
|
||||
updatedProperties: partialFlatAgent,
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
+17
-7
@@ -1,18 +1,28 @@
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { msg, t } from '@lingui/core/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
|
||||
import { AgentExceptionCode } from 'src/engine/metadata-modules/ai/ai-agent/agent.exception';
|
||||
import { type FlatAgent } from 'src/engine/metadata-modules/flat-agent/types/flat-agent.type';
|
||||
import { type FlatEntityValidationError } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
|
||||
type ValidateAgentRequiredPropertiesArgs = {
|
||||
flatAgent: FlatAgent;
|
||||
updatedProperties?: Partial<FlatAgent>;
|
||||
};
|
||||
|
||||
export const validateAgentRequiredProperties = ({
|
||||
flatAgent,
|
||||
}: {
|
||||
flatAgent: FlatAgent;
|
||||
}): FlatEntityValidationError<AgentExceptionCode>[] => {
|
||||
updatedProperties,
|
||||
}: ValidateAgentRequiredPropertiesArgs): FlatEntityValidationError<AgentExceptionCode>[] => {
|
||||
const errors: FlatEntityValidationError<AgentExceptionCode>[] = [];
|
||||
|
||||
if (!isNonEmptyString(flatAgent.label)) {
|
||||
// For updates, only validate properties that are being changed
|
||||
const isUpdate = updatedProperties !== undefined;
|
||||
const shouldValidateLabel = !isUpdate || 'label' in updatedProperties;
|
||||
const shouldValidatePrompt = !isUpdate || 'prompt' in updatedProperties;
|
||||
const shouldValidateModelId = !isUpdate || 'modelId' in updatedProperties;
|
||||
|
||||
if (shouldValidateLabel && !isNonEmptyString(flatAgent.label)) {
|
||||
errors.push({
|
||||
code: AgentExceptionCode.INVALID_AGENT_INPUT,
|
||||
message: t`Label cannot be empty`,
|
||||
@@ -20,7 +30,7 @@ export const validateAgentRequiredProperties = ({
|
||||
});
|
||||
}
|
||||
|
||||
if (!isNonEmptyString(flatAgent.prompt)) {
|
||||
if (shouldValidatePrompt && !isNonEmptyString(flatAgent.prompt)) {
|
||||
errors.push({
|
||||
code: AgentExceptionCode.INVALID_AGENT_INPUT,
|
||||
message: t`Prompt cannot be empty`,
|
||||
@@ -28,7 +38,7 @@ export const validateAgentRequiredProperties = ({
|
||||
});
|
||||
}
|
||||
|
||||
if (!isNonEmptyString(flatAgent.modelId)) {
|
||||
if (shouldValidateModelId && !isNonEmptyString(flatAgent.modelId)) {
|
||||
errors.push({
|
||||
code: AgentExceptionCode.INVALID_AGENT_INPUT,
|
||||
message: t`Model ID cannot be empty`,
|
||||
|
||||
Reference in New Issue
Block a user