n8n
Version:
n8n Workflow Automation Tool
153 lines • 7.19 kB
JavaScript
;
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.AgentExecutionService = void 0;
exports.threadBelongsTo = threadBelongsTo;
const backend_common_1 = require("@n8n/backend-common");
const di_1 = require("@n8n/di");
const n8n_memory_1 = require("./integrations/n8n-memory");
const agent_execution_repository_1 = require("./repositories/agent-execution.repository");
const agent_execution_thread_repository_1 = require("./repositories/agent-execution-thread.repository");
let AgentExecutionService = class AgentExecutionService {
constructor(logger, agentExecutionRepository, agentExecutionThreadRepository, n8nMemory) {
this.logger = logger;
this.agentExecutionRepository = agentExecutionRepository;
this.agentExecutionThreadRepository = agentExecutionThreadRepository;
this.n8nMemory = n8nMemory;
}
async recordMessage(params) {
const { threadId, agentId, agentName, projectId, record, source, hitlStatus } = params;
const { thread, created } = await this.agentExecutionThreadRepository.findOrCreate(threadId, agentId, agentName, projectId);
if (!created) {
await this.agentExecutionThreadRepository.bumpUpdatedAt(threadId);
if (!thread.title) {
await this.syncTitleFromMemory(threadId);
}
}
const cleanedMessage = params.userMessage
.replace(/<@[A-Z0-9]+>/gi, `@${agentName}`)
.replace(/@[A-Z0-9]{8,}/gi, `@${agentName}`)
.trim();
const status = record.error ? 'error' : 'success';
const startedAt = new Date(record.startTime);
const stoppedAt = new Date(record.startTime + record.duration);
const inserted = await this.agentExecutionRepository.save(this.agentExecutionRepository.create({
threadId,
status,
startedAt,
stoppedAt,
duration: record.duration,
userMessage: cleanedMessage,
assistantResponse: record.assistantResponse,
model: record.model,
promptTokens: record.usage?.promptTokens ?? null,
completionTokens: record.usage?.completionTokens ?? null,
totalTokens: record.usage?.totalTokens ?? null,
cost: record.totalCost,
toolCalls: record.toolCalls.length > 0 ? record.toolCalls : null,
timeline: record.timeline.length > 0 ? record.timeline : null,
error: record.error,
hitlStatus: hitlStatus ?? null,
workingMemory: record.workingMemory,
source: source ?? null,
}));
if (hitlStatus === 'resumed' && record.model) {
await this.backfillSuspendedExecutions(threadId, record.model);
}
if (record.usage) {
await this.agentExecutionThreadRepository.incrementUsage(threadId, record.usage.promptTokens, record.usage.completionTokens, record.totalCost ?? 0, record.duration);
}
this.logger.debug('Recorded agent execution', {
executionId: inserted.id,
threadId,
agentId,
status,
duration: record.duration,
});
if (created) {
await this.syncTitleFromMemory(threadId);
}
return inserted.id;
}
async backfillSuspendedExecutions(threadId, model) {
const candidates = await this.agentExecutionRepository.findSuspendedWithoutModel(threadId);
if (candidates.length === 0)
return;
await this.agentExecutionRepository.backfillModel(candidates.map((c) => c.id), model);
}
async syncTitleFromMemory(threadId) {
try {
const memoryThread = await this.n8nMemory.getThread(threadId);
if (memoryThread?.title) {
const emoji = memoryThread.metadata && typeof memoryThread.metadata.emoji === 'string'
? memoryThread.metadata.emoji
: null;
await this.agentExecutionThreadRepository.update(threadId, {
title: memoryThread.title,
...(emoji && { emoji }),
});
}
}
catch {
}
}
async deleteThread(projectId, threadId) {
const thread = await this.agentExecutionThreadRepository.findOneBy({
id: threadId,
projectId,
});
if (!thread)
return false;
await this.n8nMemory.deleteThread(threadId);
await this.agentExecutionThreadRepository.delete({ id: threadId });
return true;
}
async getThreads(projectId, limit, cursor, agentId) {
const page = await this.agentExecutionThreadRepository.findByProjectIdPaginated(projectId, limit, cursor, agentId);
if (page.threads.length === 0) {
return { threads: [], nextCursor: page.nextCursor };
}
const messageMap = await this.agentExecutionRepository.findFirstUserMessageByThreadIds(page.threads.map((t) => t.id));
return {
...page,
threads: page.threads.map((t) => ({
...t,
firstMessage: messageMap.get(t.id) ?? null,
})),
};
}
async getThreadDetail(threadId, projectId, agentId) {
const thread = await this.agentExecutionThreadRepository.findOneBy({ id: threadId });
if (!thread || !threadBelongsTo(thread, projectId, agentId))
return null;
const executions = await this.agentExecutionRepository.findByThreadIdOrdered(threadId);
return { thread, executions };
}
async findThreadById(threadId) {
return await this.agentExecutionThreadRepository.findOneBy({ id: threadId });
}
};
exports.AgentExecutionService = AgentExecutionService;
exports.AgentExecutionService = AgentExecutionService = __decorate([
(0, di_1.Service)(),
__metadata("design:paramtypes", [backend_common_1.Logger,
agent_execution_repository_1.AgentExecutionRepository,
agent_execution_thread_repository_1.AgentExecutionThreadRepository,
n8n_memory_1.N8nMemory])
], AgentExecutionService);
function threadBelongsTo(thread, projectId, agentId) {
if (thread.projectId !== projectId)
return false;
if (agentId && thread.agentId !== agentId)
return false;
return true;
}
//# sourceMappingURL=agent-execution.service.js.map