[AI] agent node prompt tab new design + refactor (#19012)
This commit is contained in:
+231
@@ -0,0 +1,231 @@
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Command } from 'nest-commander';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
|
||||
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import { type FlatAgent } from 'src/engine/metadata-modules/flat-agent/types/flat-agent.type';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service';
|
||||
import { type WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
|
||||
import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
|
||||
const TEXT_AGENT_DEFAULT_JSON_RESPONSE_FORMAT = {
|
||||
type: 'json' as const,
|
||||
schema: {
|
||||
type: 'object' as const,
|
||||
properties: {
|
||||
response: {
|
||||
type: 'string' as const,
|
||||
description: 'Response of the agent',
|
||||
},
|
||||
},
|
||||
required: ['response'],
|
||||
additionalProperties: false as const,
|
||||
},
|
||||
};
|
||||
|
||||
const TEXT_AGENT_DEFAULT_OUTPUT_SCHEMA = {
|
||||
response: {
|
||||
isLeaf: true,
|
||||
type: 'string',
|
||||
label: 'Response',
|
||||
value: null,
|
||||
},
|
||||
};
|
||||
|
||||
@Command({
|
||||
name: 'upgrade:1-21:migrate-ai-agent-text-to-json-response-format',
|
||||
description:
|
||||
'Migrate AI agents with text response format to JSON with a default response field',
|
||||
})
|
||||
export class MigrateAiAgentTextToJsonResponseFormatCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
|
||||
constructor(
|
||||
@InjectRepository(WorkspaceEntity)
|
||||
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
|
||||
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
|
||||
protected readonly dataSourceService: DataSourceService,
|
||||
private readonly workspaceCacheService: WorkspaceCacheService,
|
||||
private readonly applicationService: ApplicationService,
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
) {
|
||||
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
|
||||
}
|
||||
|
||||
override async runOnWorkspace({
|
||||
workspaceId,
|
||||
options,
|
||||
}: RunOnWorkspaceArgs): Promise<void> {
|
||||
const isDryRun = options.dryRun ?? false;
|
||||
|
||||
const textAgents = await this.findTextFormatCustomAgents(workspaceId);
|
||||
|
||||
if (textAgents.length === 0) {
|
||||
this.logger.log(
|
||||
`No text-format agents found for workspace ${workspaceId}, skipping`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
`${isDryRun ? '[DRY RUN] ' : ''}Found ${textAgents.length} text-format agent(s) for workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
if (isDryRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.migrateAgentsToJson(workspaceId, textAgents);
|
||||
|
||||
const textAgentIds = textAgents.map((agent) => agent.id);
|
||||
|
||||
await this.updateWorkflowStepOutputSchemas(workspaceId, textAgentIds);
|
||||
|
||||
this.logger.log(
|
||||
`Successfully migrated ${textAgents.length} agent(s) to JSON response format for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
private async findTextFormatCustomAgents(
|
||||
workspaceId: string,
|
||||
): Promise<FlatAgent[]> {
|
||||
const { flatAgentMaps } = await this.workspaceCacheService.getOrRecompute(
|
||||
workspaceId,
|
||||
['flatAgentMaps'],
|
||||
);
|
||||
|
||||
return Object.values(flatAgentMaps.byUniversalIdentifier)
|
||||
.filter(isDefined)
|
||||
.filter((flatAgent) => this.isTextFormatCustomAgent(flatAgent));
|
||||
}
|
||||
|
||||
private isTextFormatCustomAgent(flatAgent: FlatAgent): boolean {
|
||||
if (!flatAgent.isCustom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const responseFormat = flatAgent.responseFormat as
|
||||
| {
|
||||
type?: string;
|
||||
}
|
||||
| null
|
||||
| undefined;
|
||||
|
||||
return !isDefined(responseFormat?.type) || responseFormat.type === 'text';
|
||||
}
|
||||
|
||||
private async migrateAgentsToJson(
|
||||
workspaceId: string,
|
||||
textAgents: FlatAgent[],
|
||||
): Promise<void> {
|
||||
const { workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
agent: {
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: [],
|
||||
flatEntityToUpdate: textAgents.map((textAgent) => ({
|
||||
...textAgent,
|
||||
responseFormat: TEXT_AGENT_DEFAULT_JSON_RESPONSE_FORMAT,
|
||||
})),
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
},
|
||||
);
|
||||
|
||||
if (validateAndBuildResult.status === 'fail') {
|
||||
this.logger.error(
|
||||
`Failed to migrate agents to JSON response format:\n${JSON.stringify(validateAndBuildResult, null, 2)}`,
|
||||
);
|
||||
throw new Error(
|
||||
`Failed to migrate text-format agents for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async updateWorkflowStepOutputSchemas(
|
||||
workspaceId: string,
|
||||
agentIds: string[],
|
||||
): Promise<void> {
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMGlobalManager.getRepository<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
const allVersions = await workflowVersionRepository.find();
|
||||
|
||||
let updatedVersionCount = 0;
|
||||
|
||||
for (const version of allVersions) {
|
||||
if (!version.steps || !Array.isArray(version.steps)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let versionModified = false;
|
||||
|
||||
const updatedSteps = version.steps.map((step: WorkflowAction) => {
|
||||
if (step.type !== 'AI_AGENT') {
|
||||
return step;
|
||||
}
|
||||
|
||||
const agentId = step.settings?.input?.agentId;
|
||||
|
||||
if (!agentId || !agentIds.includes(agentId)) {
|
||||
return step;
|
||||
}
|
||||
|
||||
const currentOutputSchema = step.settings?.outputSchema;
|
||||
|
||||
if (
|
||||
currentOutputSchema &&
|
||||
Object.keys(currentOutputSchema).length > 0
|
||||
) {
|
||||
return step;
|
||||
}
|
||||
|
||||
versionModified = true;
|
||||
|
||||
return {
|
||||
...step,
|
||||
settings: {
|
||||
...step.settings,
|
||||
outputSchema: TEXT_AGENT_DEFAULT_OUTPUT_SCHEMA,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
if (versionModified) {
|
||||
await workflowVersionRepository.update(version.id, {
|
||||
steps: updatedSteps as WorkflowAction[],
|
||||
});
|
||||
updatedVersionCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedVersionCount > 0) {
|
||||
this.logger.log(
|
||||
`Updated output schemas in ${updatedVersionCount} workflow version(s) for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -3,6 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { BackfillDatasourceToWorkspaceCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-backfill-datasource-to-workspace.command';
|
||||
import { BackfillPageLayoutsAndFieldsWidgetViewFieldsCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-backfill-page-layouts-and-fields-widget-view-fields.command';
|
||||
import { MigrateAiAgentTextToJsonResponseFormatCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-migrate-ai-agent-text-to-json-response-format.command';
|
||||
import { UpdateEditLayoutCommandMenuItemLabelCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-update-edit-layout-command-menu-item-label.command';
|
||||
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
@@ -24,11 +25,13 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace
|
||||
providers: [
|
||||
BackfillDatasourceToWorkspaceCommand,
|
||||
BackfillPageLayoutsAndFieldsWidgetViewFieldsCommand,
|
||||
MigrateAiAgentTextToJsonResponseFormatCommand,
|
||||
UpdateEditLayoutCommandMenuItemLabelCommand,
|
||||
],
|
||||
exports: [
|
||||
BackfillDatasourceToWorkspaceCommand,
|
||||
BackfillPageLayoutsAndFieldsWidgetViewFieldsCommand,
|
||||
MigrateAiAgentTextToJsonResponseFormatCommand,
|
||||
UpdateEditLayoutCommandMenuItemLabelCommand,
|
||||
],
|
||||
})
|
||||
|
||||
+3
@@ -27,6 +27,7 @@ import { SeedCliApplicationRegistrationCommand } from 'src/database/commands/upg
|
||||
import { UpdateStandardIndexViewNamesCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-update-standard-index-view-names.command';
|
||||
import { BackfillDatasourceToWorkspaceCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-backfill-datasource-to-workspace.command';
|
||||
import { BackfillPageLayoutsAndFieldsWidgetViewFieldsCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-backfill-page-layouts-and-fields-widget-view-fields.command';
|
||||
import { MigrateAiAgentTextToJsonResponseFormatCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-migrate-ai-agent-text-to-json-response-format.command';
|
||||
import { UpdateEditLayoutCommandMenuItemLabelCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-update-edit-layout-command-menu-item-label.command';
|
||||
import { CoreEngineVersionService } from 'src/engine/core-engine-version/services/core-engine-version.service';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
@@ -72,6 +73,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
// 1.21 Commands
|
||||
protected readonly backfillDatasourceToWorkspaceCommand: BackfillDatasourceToWorkspaceCommand,
|
||||
protected readonly backfillPageLayoutsAndFieldsWidgetViewFieldsCommand: BackfillPageLayoutsAndFieldsWidgetViewFieldsCommand,
|
||||
protected readonly migrateAiAgentTextToJsonResponseFormatCommand: MigrateAiAgentTextToJsonResponseFormatCommand,
|
||||
protected readonly updateEditLayoutCommandMenuItemLabelCommand: UpdateEditLayoutCommandMenuItemLabelCommand,
|
||||
) {
|
||||
super(
|
||||
@@ -108,6 +110,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
const commands_1210: VersionCommands = [
|
||||
this.backfillDatasourceToWorkspaceCommand,
|
||||
this.backfillPageLayoutsAndFieldsWidgetViewFieldsCommand,
|
||||
this.migrateAiAgentTextToJsonResponseFormatCommand,
|
||||
this.updateEditLayoutCommandMenuItemLabelCommand,
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user