n8n
Version:
n8n Workflow Automation Tool
328 lines • 14.6 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AgentExecutionOrchestratorService = void 0;
const api_types_1 = require("@n8n/api-types");
const backend_common_1 = require("@n8n/backend-common");
const di_1 = require("@n8n/di");
const n8n_workflow_1 = require("n8n-workflow");
const external_hooks_1 = require("../../external-hooks");
const telemetry_1 = require("../../telemetry");
const agent_execution_service_1 = require("./agent-execution.service");
const agent_run_tracing_service_1 = require("./agent-run-tracing.service");
const agent_runtime_cache_service_1 = require("./agent-runtime-cache.service");
const execution_recorder_1 = require("./execution-recorder");
const integration_message_context_service_1 = require("./integrations/integration-message-context.service");
const n8n_checkpoint_storage_1 = require("./integrations/n8n-checkpoint-storage");
const agent_execution_counter_1 = require("./utils/agent-execution-counter");
const agent_stream_1 = require("./utils/agent-stream");
const execution_to_message_mapper_1 = require("./utils/execution-to-message-mapper");
function getMaxIterationsChunks() {
const id = crypto.randomUUID();
return [
{ type: 'text-start', id },
{
type: 'text-delta',
id,
delta: 'The agent has reached the maximum number of iterations and has stopped.',
},
{ type: 'text-end', id },
];
}
let AgentExecutionOrchestratorService = class AgentExecutionOrchestratorService {
constructor(logger, n8nCheckpointStorage, agentExecutionService, telemetry, runtimeCacheService, integrationMessageContextService, agentRunTracingService, externalHooks) {
this.logger = logger;
this.n8nCheckpointStorage = n8nCheckpointStorage;
this.agentExecutionService = agentExecutionService;
this.telemetry = telemetry;
this.runtimeCacheService = runtimeCacheService;
this.integrationMessageContextService = integrationMessageContextService;
this.agentRunTracingService = agentRunTracingService;
this.externalHooks = externalHooks;
}
async getConversationHistory(params) {
const { threadId, projectId, agentId } = params;
const detail = await this.agentExecutionService.getThreadDetail(threadId, projectId, agentId);
if (!detail)
return null;
return (0, execution_to_message_mapper_1.executionsToMessagesDto)(detail.executions);
}
async *resumeForChat(config) {
const { agentId, projectId, runId, toolCallId, resumeData, integrationType, user, usePublishedVersion = true, onExecutionRecorded, } = config;
const checkpointStatus = await this.n8nCheckpointStorage.getStatus(runId);
if (checkpointStatus.status === 'expired') {
throw new n8n_workflow_1.UserError(`Checkpoint ${runId} is expired and cannot be resumed`);
}
if (checkpointStatus.status === 'not-found') {
throw new n8n_workflow_1.UserError(`Checkpoint ${runId} not found and cannot be resumed`);
}
const memoryScope = checkpointStatus.checkpoint?.persistence;
if (!memoryScope) {
throw new n8n_workflow_1.UserError(`Checkpoint ${runId} has no memory data and cannot be resumed`);
}
const threadId = memoryScope.threadId;
const runtime = await this.runtimeCacheService.getRuntime({
agentId,
projectId,
usePublishedVersion,
integrationType,
user: usePublishedVersion ? undefined : user,
});
const { agent: agentInstance, toolRegistry } = runtime;
const recorder = new execution_recorder_1.ExecutionRecorder(toolRegistry);
const runType = usePublishedVersion ? 'production' : 'test';
try {
const suspendedExecution = this.agentRunTracingService.enabled
? await this.agentExecutionService.findLatestSuspendedRun(threadId)
: undefined;
const tracing = await this.agentRunTracingService.build({
agentId,
projectId,
threadId,
userId: user?.id,
source: suspendedExecution?.source ?? 'unknown',
modelId: (0, agent_run_tracing_service_1.modelIdFromSnapshot)(agentInstance.snapshot.model),
});
const resultStream = await agentInstance.resume('stream', resumeData, {
runId,
toolCallId,
executionCounter: (0, agent_execution_counter_1.createAgentExecutionCounter)(this.telemetry, {
agentId,
userId: user?.id,
}),
...(tracing ? { telemetry: tracing } : {}),
});
for await (const value of (0, agent_stream_1.streamAgentChunks)(resultStream.stream)) {
recorder.record(value);
yield value;
}
}
catch (error) {
recorder.record({ type: 'error', error });
recorder.record({ type: 'finish', finishReason: 'error' });
throw error;
}
finally {
const messageRecord = recorder.getMessageRecord();
await this.persistRecordedExecution({
onExecutionRecorded,
failureMessage: 'Failed to record resumed agent execution',
params: {
threadId,
agentId,
agentName: agentInstance.name,
projectId,
userMessage: null,
record: messageRecord,
hitlStatus: recorder.suspended ? 'suspended' : 'resumed',
telemetry: {
runType,
configuration: runtime.telemetryConfiguration,
},
},
});
}
}
async *executeForChat(config) {
const { agentId, projectId, message, user, memory, onExecutionRecorded } = config;
const runtime = await this.runtimeCacheService.getRuntime({
agentId,
projectId,
integrationType: api_types_1.N8N_CHAT_INTEGRATION_TYPE,
user,
});
await this.integrationMessageContextService.setLatest(memory.threadId, memory.resourceId, {
integrationConnectionId: api_types_1.N8N_CHAT_INTEGRATION_TYPE,
platform: api_types_1.N8N_CHAT_INTEGRATION_TYPE,
target: { type: 'dm', userId: user.id, threadId: memory.threadId },
interactingUserId: user.id,
updatedAt: new Date().toISOString(),
});
yield* this.streamChatResponse({
agentInstance: runtime.agent,
toolRegistry: runtime.toolRegistry,
agentId,
userId: user.id,
message,
memory,
projectId: runtime.projectId,
telemetry: {
runType: 'test',
configuration: runtime.telemetryConfiguration,
},
onExecutionRecorded,
});
}
async *executeForChatPublished(config) {
const { agentId, projectId, message, memory, integrationType } = config;
await this.externalHooks.run('agent.preExecute', [agentId]);
const runtime = await this.runtimeCacheService.getRuntime({
agentId,
projectId,
integrationType,
usePublishedVersion: true,
});
yield* this.streamChatResponse({
agentInstance: runtime.agent,
toolRegistry: runtime.toolRegistry,
agentId,
message,
memory,
projectId: runtime.projectId,
source: integrationType,
telemetry: {
runType: 'production',
configuration: runtime.telemetryConfiguration,
},
});
}
async *executeForTaskPublished(config) {
const { agentId, projectId, message, memory, taskId, taskVersionId } = config;
await this.externalHooks.run('agent.preExecute', [agentId]);
const runtime = await this.runtimeCacheService.getRuntime({
agentId,
projectId,
integrationType: 'task',
usePublishedVersion: true,
});
yield* this.streamChatResponse({
agentInstance: runtime.agent,
toolRegistry: runtime.toolRegistry,
agentId,
message,
memory,
projectId: runtime.projectId,
source: 'task',
taskId,
taskVersionId,
telemetry: {
runType: 'production',
configuration: runtime.telemetryConfiguration,
},
});
}
async *executeForTaskNow(config) {
const { agentId, projectId, user, message, memory, taskId } = config;
const runtime = await this.runtimeCacheService.getRuntime({
agentId,
projectId,
user,
});
yield* this.streamChatResponse({
agentInstance: runtime.agent,
toolRegistry: runtime.toolRegistry,
agentId,
userId: user.id,
message,
memory,
projectId: runtime.projectId,
source: 'task',
taskId,
telemetry: {
runType: 'test',
configuration: runtime.telemetryConfiguration,
},
});
}
async *streamChatResponse(config) {
const { agentInstance, toolRegistry, agentId, userId, message, memory, projectId, source, taskId, taskVersionId, telemetry, onExecutionRecorded, } = config;
const { threadId, resourceId } = memory;
const recorder = new execution_recorder_1.ExecutionRecorder(toolRegistry);
try {
const tracing = await this.agentRunTracingService.build({
agentId,
projectId,
threadId,
userId,
source: source ?? 'test',
modelId: (0, agent_run_tracing_service_1.modelIdFromSnapshot)(agentInstance.snapshot.model),
});
const resultStream = await agentInstance.stream(message, {
persistence: { threadId, resourceId },
executionCounter: (0, agent_execution_counter_1.createAgentExecutionCounter)(this.telemetry, { agentId, userId }),
...(tracing ? { telemetry: tracing } : {}),
});
for await (const value of (0, agent_stream_1.streamAgentChunks)(resultStream.stream)) {
recorder.record(value);
if (value.type === 'tool-call-suspended') {
this.logger.info('Chat: tool-call-suspended chunk received', {
agentId,
toolCallId: value.toolCallId,
toolName: value.toolName,
});
}
if (value.type === 'finish' && value.finishReason === 'max-iterations') {
for (const chunk of getMaxIterationsChunks()) {
recorder.record(chunk);
yield chunk;
}
}
yield value;
}
}
catch (error) {
recorder.record({ type: 'error', error });
recorder.record({ type: 'finish', finishReason: 'error' });
throw error;
}
finally {
const messageRecord = recorder.getMessageRecord();
await this.persistRecordedExecution({
onExecutionRecorded,
failureMessage: 'Failed to record agent execution',
params: {
threadId,
agentId,
agentName: agentInstance.name,
projectId,
userMessage: message,
record: messageRecord,
hitlStatus: recorder.suspended ? 'suspended' : undefined,
source,
taskId,
taskVersionId,
telemetry,
},
});
}
}
async persistRecordedExecution(args) {
const { onExecutionRecorded, params, failureMessage } = args;
const persist = async () => {
const executionId = await this.agentExecutionService.recordMessage(params);
onExecutionRecorded?.(executionId);
};
const logFailure = (error) => {
this.logger.warn(failureMessage, {
agentId: params.agentId,
threadId: params.threadId,
error: error instanceof Error ? error.message : String(error),
});
};
if (onExecutionRecorded) {
try {
await persist();
}
catch (error) {
logFailure(error);
}
}
else {
void persist().catch(logFailure);
}
}
};
exports.AgentExecutionOrchestratorService = AgentExecutionOrchestratorService;
exports.AgentExecutionOrchestratorService = AgentExecutionOrchestratorService = __decorate([
(0, di_1.Service)(),
__metadata("design:paramtypes", [backend_common_1.Logger, n8n_checkpoint_storage_1.N8NCheckpointStorage, agent_execution_service_1.AgentExecutionService, telemetry_1.Telemetry, agent_runtime_cache_service_1.AgentRuntimeCacheService, integration_message_context_service_1.IntegrationMessageContextService, agent_run_tracing_service_1.AgentRunTracingService, external_hooks_1.ExternalHooks])
], AgentExecutionOrchestratorService);
//# sourceMappingURL=agent-execution-orchestrator.service.js.map