feat: Add AI Agent workflow action node (#12650)
https://github.com/user-attachments/assets/8593e488-cb00-4fd2-b903-5ba5766e0254 --------- Co-authored-by: Antoine Moreaux <moreaux.antoine@gmail.com> Co-authored-by: martmull <martmull@hotmail.fr> Co-authored-by: Félix Malfait <felix.malfait@gmail.com> Co-authored-by: Baptiste Devessier <baptiste@devessier.fr> Co-authored-by: Joseph Chiang <josephj6802@gmail.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Guillim <guillim@users.noreply.github.com> Co-authored-by: Raphaël Bosi <71827178+bosiraphael@users.noreply.github.com> Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com> Co-authored-by: Marie <51697796+ijreilly@users.noreply.github.com> Co-authored-by: Naifer <161821705+omarNaifer12@users.noreply.github.com> Co-authored-by: prastoin <paul@twenty.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions <github-actions@twenty.com> Co-authored-by: Thomas Trompette <thomas.trompette@sfr.fr> Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com> Co-authored-by: Ajay A Adsule <103304466+AjayAdsule@users.noreply.github.com> Co-authored-by: bosiraphael <raphael.bosi@gmail.com> Co-authored-by: Charles Bochet <charles@twenty.com> Co-authored-by: Marty <91310557+real-marty@users.noreply.github.com> Co-authored-by: Félix Malfait <felix@twenty.com> Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Paul Rastoin <45004772+prastoin@users.noreply.github.com> Co-authored-by: Weiko <corentin@twenty.com> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: nitin <142569587+ehconitin@users.noreply.github.com>
This commit is contained in:
+4
@@ -6,6 +6,7 @@ import {
|
||||
WorkflowStepExecutorException,
|
||||
WorkflowStepExecutorExceptionCode,
|
||||
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
|
||||
import { AiAgentWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/ai-agent/ai-agent.workflow-action';
|
||||
import { CodeWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/code/code.workflow-action';
|
||||
import { FilterWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/filter/filter.workflow-action';
|
||||
import { FormWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/form/form.workflow-action';
|
||||
@@ -29,6 +30,7 @@ export class WorkflowExecutorFactory {
|
||||
private readonly formWorkflowAction: FormWorkflowAction,
|
||||
private readonly filterWorkflowAction: FilterWorkflowAction,
|
||||
private readonly httpRequestWorkflowAction: HttpRequestWorkflowAction,
|
||||
private readonly aiAgentWorkflowAction: AiAgentWorkflowAction,
|
||||
) {}
|
||||
|
||||
get(stepType: WorkflowActionType): WorkflowExecutor {
|
||||
@@ -51,6 +53,8 @@ export class WorkflowExecutorFactory {
|
||||
return this.filterWorkflowAction;
|
||||
case WorkflowActionType.HTTP_REQUEST:
|
||||
return this.httpRequestWorkflowAction;
|
||||
case WorkflowActionType.AI_AGENT:
|
||||
return this.aiAgentWorkflowAction;
|
||||
default:
|
||||
throw new WorkflowStepExecutorException(
|
||||
`Workflow step executor not found for step type '${stepType}'`,
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { AiDriver } from 'src/engine/core-modules/ai/interfaces/ai.interface';
|
||||
|
||||
import { AiModule } from 'src/engine/core-modules/ai/ai.module';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/agent/agent.entity';
|
||||
import { AgentModule } from 'src/engine/metadata-modules/agent/agent.module';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
|
||||
import { AiAgentWorkflowAction } from './ai-agent.workflow-action';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
AgentModule,
|
||||
AiModule.forRoot({
|
||||
useFactory: () => ({ type: AiDriver.OPENAI }),
|
||||
}),
|
||||
TypeOrmModule.forFeature([AgentEntity], 'core'),
|
||||
],
|
||||
providers: [ScopedWorkspaceContextFactory, AiAgentWorkflowAction],
|
||||
exports: [AiAgentWorkflowAction],
|
||||
})
|
||||
export class AiAgentActionModule {}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { WorkflowExecutor } from 'src/modules/workflow/workflow-executor/interfaces/workflow-executor.interface';
|
||||
|
||||
import { AIBillingService } from 'src/engine/core-modules/ai/services/ai-billing.service';
|
||||
import { AgentExecutionService } from 'src/engine/metadata-modules/agent/agent-execution.service';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/agent/agent.entity';
|
||||
import {
|
||||
AgentException,
|
||||
AgentExceptionCode,
|
||||
} from 'src/engine/metadata-modules/agent/agent.exception';
|
||||
import {
|
||||
WorkflowStepExecutorException,
|
||||
WorkflowStepExecutorExceptionCode,
|
||||
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
|
||||
import { WorkflowExecutorInput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-input';
|
||||
import { WorkflowExecutorOutput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-output.type';
|
||||
|
||||
import { isWorkflowAiAgentAction } from './guards/is-workflow-ai-agent-action.guard';
|
||||
|
||||
@Injectable()
|
||||
export class AiAgentWorkflowAction implements WorkflowExecutor {
|
||||
constructor(
|
||||
private readonly agentExecutionService: AgentExecutionService,
|
||||
private readonly aiBillingService: AIBillingService,
|
||||
@InjectRepository(AgentEntity, 'core')
|
||||
private readonly agentRepository: Repository<AgentEntity>,
|
||||
) {}
|
||||
|
||||
async execute({
|
||||
currentStepId,
|
||||
steps,
|
||||
context,
|
||||
}: WorkflowExecutorInput): Promise<WorkflowExecutorOutput> {
|
||||
const step = steps.find((step) => step.id === currentStepId);
|
||||
|
||||
if (!step) {
|
||||
throw new WorkflowStepExecutorException(
|
||||
'Step not found',
|
||||
WorkflowStepExecutorExceptionCode.STEP_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (!isWorkflowAiAgentAction(step)) {
|
||||
throw new WorkflowStepExecutorException(
|
||||
'Step is not an AI Agent action',
|
||||
WorkflowStepExecutorExceptionCode.INVALID_STEP_TYPE,
|
||||
);
|
||||
}
|
||||
|
||||
const { agentId } = step.settings.input;
|
||||
const workspaceId = context.workspaceId as string;
|
||||
|
||||
try {
|
||||
const agent = await this.agentRepository.findOne({
|
||||
where: {
|
||||
id: agentId,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!agent) {
|
||||
throw new AgentException(
|
||||
`Agent with id ${agentId} not found`,
|
||||
AgentExceptionCode.AGENT_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const executionResult = await this.agentExecutionService.executeAgent({
|
||||
agent,
|
||||
context,
|
||||
schema: step.settings.outputSchema,
|
||||
});
|
||||
|
||||
await this.aiBillingService.calculateAndBillUsage(
|
||||
agent.modelId,
|
||||
executionResult.usage,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return { result: executionResult.object };
|
||||
} catch (error) {
|
||||
if (error instanceof AgentException) {
|
||||
return {
|
||||
error: `${error.message} (${error.code})`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
error:
|
||||
error instanceof Error ? error.message : 'AI Agent execution failed',
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import {
|
||||
WorkflowAction,
|
||||
WorkflowActionType,
|
||||
WorkflowAiAgentAction,
|
||||
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
|
||||
export const isWorkflowAiAgentAction = (
|
||||
action: WorkflowAction,
|
||||
): action is WorkflowAiAgentAction => {
|
||||
return action.type === WorkflowActionType.AI_AGENT;
|
||||
};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export type WorkflowAiAgentActionInput = {
|
||||
agentId: string;
|
||||
};
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { WorkflowAiAgentActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/ai-agent/types/workflow-ai-agent-action-input.type';
|
||||
import { BaseWorkflowActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type';
|
||||
|
||||
export type WorkflowAiAgentActionSettings = BaseWorkflowActionSettings & {
|
||||
input: WorkflowAiAgentActionInput;
|
||||
};
|
||||
+3
-1
@@ -1,4 +1,5 @@
|
||||
import { OutputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type';
|
||||
import { WorkflowAiAgentActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/ai-agent/types/workflow-ai-agent-action-settings.type';
|
||||
import { WorkflowCodeActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/code/types/workflow-code-action-settings.type';
|
||||
import { WorkflowFilterActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/filter/types/workflow-filter-action-settings.type';
|
||||
import { WorkflowFormActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/form/types/workflow-form-action-settings.type';
|
||||
@@ -32,4 +33,5 @@ export type WorkflowActionSettings =
|
||||
| WorkflowFindRecordsActionSettings
|
||||
| WorkflowFormActionSettings
|
||||
| WorkflowFilterActionSettings
|
||||
| WorkflowHttpRequestActionSettings;
|
||||
| WorkflowHttpRequestActionSettings
|
||||
| WorkflowAiAgentActionSettings;
|
||||
|
||||
+9
-1
@@ -1,3 +1,4 @@
|
||||
import { WorkflowAiAgentActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/ai-agent/types/workflow-ai-agent-action-settings.type';
|
||||
import { WorkflowCodeActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/code/types/workflow-code-action-settings.type';
|
||||
import { WorkflowFilterActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/filter/types/workflow-filter-action-settings.type';
|
||||
import { WorkflowFormActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/form/types/workflow-form-action-settings.type';
|
||||
@@ -21,6 +22,7 @@ export enum WorkflowActionType {
|
||||
FORM = 'FORM',
|
||||
FILTER = 'FILTER',
|
||||
HTTP_REQUEST = 'HTTP_REQUEST',
|
||||
AI_AGENT = 'AI_AGENT',
|
||||
}
|
||||
|
||||
type BaseWorkflowAction = {
|
||||
@@ -77,6 +79,11 @@ export type WorkflowHttpRequestAction = BaseWorkflowAction & {
|
||||
settings: WorkflowHttpRequestActionSettings;
|
||||
};
|
||||
|
||||
export type WorkflowAiAgentAction = BaseWorkflowAction & {
|
||||
type: WorkflowActionType.AI_AGENT;
|
||||
settings: WorkflowAiAgentActionSettings;
|
||||
};
|
||||
|
||||
export type WorkflowAction =
|
||||
| WorkflowCodeAction
|
||||
| WorkflowSendEmailAction
|
||||
@@ -86,4 +93,5 @@ export type WorkflowAction =
|
||||
| WorkflowFindRecordsAction
|
||||
| WorkflowFormAction
|
||||
| WorkflowFilterAction
|
||||
| WorkflowHttpRequestAction;
|
||||
| WorkflowHttpRequestAction
|
||||
| WorkflowAiAgentAction;
|
||||
|
||||
+4
@@ -1,9 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { BillingModule } from 'src/engine/core-modules/billing/billing.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module';
|
||||
import { WorkflowExecutorFactory } from 'src/modules/workflow/workflow-executor/factories/workflow-executor.factory';
|
||||
import { AiAgentActionModule } from 'src/modules/workflow/workflow-executor/workflow-actions/ai-agent/ai-agent-action.module';
|
||||
import { CodeActionModule } from 'src/modules/workflow/workflow-executor/workflow-actions/code/code-action.module';
|
||||
import { FilterActionModule } from 'src/modules/workflow/workflow-executor/workflow-actions/filter/filter-action.module';
|
||||
import { FormActionModule } from 'src/modules/workflow/workflow-executor/workflow-actions/form/form-action.module';
|
||||
@@ -24,6 +26,8 @@ import { WorkflowRunModule } from 'src/modules/workflow/workflow-runner/workflow
|
||||
BillingModule,
|
||||
FilterActionModule,
|
||||
HttpRequestActionModule,
|
||||
AiAgentActionModule,
|
||||
FeatureFlagModule,
|
||||
],
|
||||
providers: [
|
||||
WorkflowExecutorWorkspaceService,
|
||||
|
||||
Reference in New Issue
Block a user