Decouple http node from workflows (#13272)
- Added a generic HTTP request tool, allowing agents and workflows to make HTTP requests to external APIs with configurable method, headers, and body. - Decoupled HTTP request workflow nodes from workflow-specific types and factories, introducing a generic tool interface. - Updated agent system prompts to include explicit guidance for the HTTP request tool, including when and how to use it, and how to communicate limitations. ### Demo https://github.com/user-attachments/assets/129bc445-a277-4a19-95ab-09f890f8f051
This commit is contained in:
+3
-3
@@ -10,13 +10,13 @@ import { AiAgentWorkflowAction } from 'src/modules/workflow/workflow-executor/wo
|
||||
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';
|
||||
import { HttpRequestWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/http-request/http-request.workflow-action';
|
||||
import { SendEmailWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/send-email.workflow-action';
|
||||
import { CreateRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/create-record.workflow-action';
|
||||
import { DeleteRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/delete-record.workflow-action';
|
||||
import { FindRecordsWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/find-records.workflow-action';
|
||||
import { UpdateRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/update-record.workflow-action';
|
||||
import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
import { WorkflowActionAdapter } from 'src/modules/workflow/workflow-executor/workflow-actions/workflow-action-adapter';
|
||||
|
||||
@Injectable()
|
||||
export class WorkflowActionFactory {
|
||||
@@ -29,7 +29,7 @@ export class WorkflowActionFactory {
|
||||
private readonly findRecordsWorkflowAction: FindRecordsWorkflowAction,
|
||||
private readonly formWorkflowAction: FormWorkflowAction,
|
||||
private readonly filterWorkflowAction: FilterWorkflowAction,
|
||||
private readonly httpRequestWorkflowAction: HttpRequestWorkflowAction,
|
||||
private readonly workflowActionAdapter: WorkflowActionAdapter,
|
||||
private readonly aiAgentWorkflowAction: AiAgentWorkflowAction,
|
||||
) {}
|
||||
|
||||
@@ -52,7 +52,7 @@ export class WorkflowActionFactory {
|
||||
case WorkflowActionType.FILTER:
|
||||
return this.filterWorkflowAction;
|
||||
case WorkflowActionType.HTTP_REQUEST:
|
||||
return this.httpRequestWorkflowAction;
|
||||
return this.workflowActionAdapter;
|
||||
case WorkflowActionType.AI_AGENT:
|
||||
return this.aiAgentWorkflowAction;
|
||||
default:
|
||||
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { HttpRequestWorkflowAction } from './http-request.workflow-action';
|
||||
|
||||
@Module({
|
||||
providers: [HttpRequestWorkflowAction],
|
||||
exports: [HttpRequestWorkflowAction],
|
||||
})
|
||||
export class HttpRequestActionModule {}
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isString } from '@sniptt/guards';
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
|
||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
|
||||
|
||||
import {
|
||||
WorkflowStepExecutorException,
|
||||
WorkflowStepExecutorExceptionCode,
|
||||
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
|
||||
import { WorkflowActionInput } from 'src/modules/workflow/workflow-executor/types/workflow-action-input';
|
||||
import { WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
|
||||
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
|
||||
|
||||
import { isWorkflowHttpRequestAction } from './guards/is-workflow-http-request-action.guard';
|
||||
import { WorkflowHttpRequestActionInput } from './types/workflow-http-request-action-input.type';
|
||||
|
||||
@Injectable()
|
||||
export class HttpRequestWorkflowAction implements WorkflowAction {
|
||||
async execute({
|
||||
currentStepId,
|
||||
steps,
|
||||
context,
|
||||
}: WorkflowActionInput): Promise<WorkflowActionOutput> {
|
||||
const step = steps.find((step) => step.id === currentStepId);
|
||||
|
||||
if (!step) {
|
||||
throw new WorkflowStepExecutorException(
|
||||
'Step not found',
|
||||
WorkflowStepExecutorExceptionCode.STEP_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
if (!isWorkflowHttpRequestAction(step)) {
|
||||
throw new WorkflowStepExecutorException(
|
||||
'Step is not an HTTP Request action',
|
||||
WorkflowStepExecutorExceptionCode.INVALID_STEP_TYPE,
|
||||
);
|
||||
}
|
||||
|
||||
const workflowActionInput = resolveInput(
|
||||
step.settings.input,
|
||||
context,
|
||||
) as WorkflowHttpRequestActionInput;
|
||||
|
||||
const { url, method, headers, body } = workflowActionInput;
|
||||
|
||||
try {
|
||||
const axiosConfig: AxiosRequestConfig = {
|
||||
url,
|
||||
method: method,
|
||||
headers,
|
||||
};
|
||||
|
||||
if (['POST', 'PUT', 'PATCH'].includes(method) && body) {
|
||||
const parsedBody = isString(body) ? JSON.parse(body) : body;
|
||||
|
||||
axiosConfig.data = parsedBody;
|
||||
}
|
||||
|
||||
const response = await axios(axiosConfig);
|
||||
|
||||
return { result: response.data };
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
return {
|
||||
error: error.response?.data || error.message || 'HTTP request failed',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
error: error instanceof Error ? error.message : 'HTTP request failed',
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
|
||||
|
||||
import { TOOLS } from 'src/engine/core-modules/tool/constants/tools.const';
|
||||
import { ToolType } from 'src/engine/core-modules/tool/enums/tool-type.enum';
|
||||
import { ToolInput } from 'src/engine/core-modules/tool/types/tool-input.type';
|
||||
import { WorkflowActionInput } from 'src/modules/workflow/workflow-executor/types/workflow-action-input';
|
||||
import { WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
|
||||
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
|
||||
import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
|
||||
@Injectable()
|
||||
export class WorkflowActionAdapter implements WorkflowAction {
|
||||
async execute({
|
||||
currentStepId,
|
||||
steps,
|
||||
context,
|
||||
}: WorkflowActionInput): Promise<WorkflowActionOutput> {
|
||||
const step = steps.find((step) => step.id === currentStepId);
|
||||
|
||||
if (!step) {
|
||||
throw new Error('Step not found');
|
||||
}
|
||||
|
||||
const toolType = this.mapWorkflowActionTypeToToolType(step.type);
|
||||
|
||||
if (!toolType) {
|
||||
throw new Error(
|
||||
`No tool mapping found for workflow action type: ${step.type}`,
|
||||
);
|
||||
}
|
||||
|
||||
const tool = TOOLS.get(toolType);
|
||||
|
||||
if (!tool) {
|
||||
throw new Error(
|
||||
`Tool for action type ${step.type} not found in registry`,
|
||||
);
|
||||
}
|
||||
|
||||
const toolInput = resolveInput(step.settings.input, context) as ToolInput;
|
||||
|
||||
const toolOutput = await tool.execute(toolInput);
|
||||
|
||||
return {
|
||||
result: toolOutput.result as object,
|
||||
error: toolOutput.error,
|
||||
};
|
||||
}
|
||||
|
||||
private mapWorkflowActionTypeToToolType(
|
||||
actionType: WorkflowActionType,
|
||||
): ToolType | null {
|
||||
const mapping: Partial<Record<WorkflowActionType, ToolType>> = {
|
||||
[WorkflowActionType.HTTP_REQUEST]: ToolType.HTTP_REQUEST,
|
||||
};
|
||||
|
||||
return mapping[actionType] || null;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -9,9 +9,9 @@ import { AiAgentActionModule } from 'src/modules/workflow/workflow-executor/work
|
||||
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';
|
||||
import { HttpRequestActionModule } from 'src/modules/workflow/workflow-executor/workflow-actions/http-request/http-request-action.module';
|
||||
import { SendEmailActionModule } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/send-email-action.module';
|
||||
import { RecordCRUDActionModule } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/record-crud-action.module';
|
||||
import { WorkflowActionAdapter } from 'src/modules/workflow/workflow-executor/workflow-actions/workflow-action-adapter';
|
||||
import { WorkflowExecutorWorkspaceService } from 'src/modules/workflow/workflow-executor/workspace-services/workflow-executor.workspace-service';
|
||||
import { WorkflowRunModule } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.module';
|
||||
|
||||
@@ -25,7 +25,6 @@ import { WorkflowRunModule } from 'src/modules/workflow/workflow-runner/workflow
|
||||
WorkflowRunModule,
|
||||
BillingModule,
|
||||
FilterActionModule,
|
||||
HttpRequestActionModule,
|
||||
AiAgentActionModule,
|
||||
FeatureFlagModule,
|
||||
],
|
||||
@@ -33,6 +32,7 @@ import { WorkflowRunModule } from 'src/modules/workflow/workflow-runner/workflow
|
||||
WorkflowExecutorWorkspaceService,
|
||||
ScopedWorkspaceContextFactory,
|
||||
WorkflowActionFactory,
|
||||
WorkflowActionAdapter,
|
||||
],
|
||||
exports: [WorkflowExecutorWorkspaceService],
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user