n8n
Version:
n8n Workflow Automation Tool
326 lines • 17.5 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.ChatHubAgentService = void 0;
const backend_common_1 = require("@n8n/backend-common");
const di_1 = require("@n8n/di");
const promises_1 = require("fs/promises");
const nanoid_1 = require("nanoid");
const uuid_1 = require("uuid");
const chat_hub_agent_repository_1 = require("./chat-hub-agent.repository");
const chat_hub_credentials_service_1 = require("./chat-hub-credentials.service");
const chat_hub_constants_1 = require("./chat-hub.constants");
const chat_hub_attachment_service_1 = require("./chat-hub.attachment.service");
const chat_hub_workflow_service_1 = require("./chat-hub-workflow.service");
const workflow_execution_service_1 = require("../../workflows/workflow-execution.service");
const bad_request_error_1 = require("../../errors/response-errors/bad-request.error");
const chat_hub_settings_service_1 = require("./chat-hub.settings.service");
const chat_hub_tool_service_1 = require("./chat-hub-tool.service");
const not_found_error_1 = require("../../errors/response-errors/not-found.error");
const chat_hub_execution_service_1 = require("./chat-hub-execution.service");
const dynamic_node_parameters_service_1 = require("../../services/dynamic-node-parameters.service");
const workflow_execute_additional_data_1 = require("../../workflow-execute-additional-data");
let ChatHubAgentService = class ChatHubAgentService {
constructor(logger, chatAgentRepository, chatHubCredentialsService, chatHubAttachmentService, chatHubWorkflowService, chatHubExecutionService, workflowExecutionService, chatHubSettingsService, dynamicNodeParametersService, chatHubToolService) {
this.logger = logger;
this.chatAgentRepository = chatAgentRepository;
this.chatHubCredentialsService = chatHubCredentialsService;
this.chatHubAttachmentService = chatHubAttachmentService;
this.chatHubWorkflowService = chatHubWorkflowService;
this.chatHubExecutionService = chatHubExecutionService;
this.workflowExecutionService = workflowExecutionService;
this.chatHubSettingsService = chatHubSettingsService;
this.dynamicNodeParametersService = dynamicNodeParametersService;
this.chatHubToolService = chatHubToolService;
this.logger = this.logger.scoped('chat-hub');
}
async getAgentsByUserIdAsModels(userId) {
const agents = await this.getAgentsByUserId(userId);
return agents.map((agent) => this.convertAgentEntityToModel(agent));
}
convertAgentEntityToModel(agent) {
const suggestedPrompts = agent.suggestedPrompts.filter((p) => p.text.trim().length > 0);
return {
name: agent.name,
description: agent.description ?? null,
icon: agent.icon,
model: {
provider: 'custom-agent',
agentId: agent.id,
},
createdAt: agent.createdAt.toISOString(),
updatedAt: agent.updatedAt.toISOString(),
metadata: (0, chat_hub_constants_1.getModelMetadata)(agent.provider, agent.model),
groupName: null,
groupIcon: null,
...(suggestedPrompts.length > 0 ? { suggestedPrompts } : {}),
};
}
async getAgentsByUserId(userId) {
return await this.chatAgentRepository.getManyByUserId(userId);
}
async getAgentById(id, userId, trx) {
const agent = await this.chatAgentRepository.getOneById(id, userId, trx);
if (!agent) {
throw new not_found_error_1.NotFoundError('Chat agent not found');
}
return agent;
}
async getAgentByIdAsDto(id, userId) {
const agent = await this.getAgentById(id, userId);
const toolIds = await this.chatHubToolService.getToolIdsForAgent(agent.id);
return this.toDto(agent, toolIds);
}
async createAgent(user, data) {
await this.chatHubCredentialsService.ensureCredentialAccess(user, data.credentialId);
const id = (0, uuid_1.v4)();
const agent = await this.chatAgentRepository.createAgent({
id,
name: data.name,
description: data.description ?? null,
icon: data.icon,
suggestedPrompts: data.suggestedPrompts ?? [],
systemPrompt: data.systemPrompt,
ownerId: user.id,
credentialId: data.credentialId,
provider: data.provider,
model: data.model,
files: [],
});
if (data.toolIds.length > 0) {
await this.chatHubToolService.setAgentTools(id, data.toolIds);
}
this.logger.debug(`Chat agent created: ${id} by user ${user.id}`);
return this.toDto(agent, data.toolIds);
}
async updateAgent(id, user, updates) {
const existingAgent = await this.chatAgentRepository.getOneById(id, user.id);
if (!existingAgent) {
throw new not_found_error_1.NotFoundError('Chat agent not found');
}
if (updates.credentialId !== undefined && updates.credentialId !== null) {
await this.chatHubCredentialsService.ensureCredentialAccess(user, updates.credentialId);
}
const updateData = {};
if (updates.name !== undefined)
updateData.name = updates.name;
if (updates.description !== undefined)
updateData.description = updates.description ?? null;
if (updates.icon !== undefined)
updateData.icon = updates.icon;
if (updates.suggestedPrompts !== undefined)
updateData.suggestedPrompts = updates.suggestedPrompts ?? [];
if (updates.systemPrompt !== undefined)
updateData.systemPrompt = updates.systemPrompt;
if (updates.credentialId !== undefined)
updateData.credentialId = updates.credentialId ?? null;
if (updates.provider !== undefined)
updateData.provider = updates.provider;
if (updates.model !== undefined)
updateData.model = updates.model ?? null;
const agent = await this.chatAgentRepository.updateAgent(id, updateData);
if (updates.toolIds !== undefined) {
await this.chatHubToolService.setAgentTools(id, updates.toolIds);
}
this.logger.debug(`Chat agent updated: ${id} by user ${user.id}`);
const toolIds = await this.chatHubToolService.getToolIdsForAgent(agent.id);
return this.toDto(agent, toolIds);
}
toDto(agent, toolIds) {
return {
id: agent.id,
name: agent.name,
description: agent.description,
icon: agent.icon,
suggestedPrompts: agent.suggestedPrompts,
systemPrompt: agent.systemPrompt,
ownerId: agent.ownerId,
credentialId: agent.credentialId,
provider: agent.provider,
model: agent.model,
files: agent.files,
toolIds,
createdAt: agent.createdAt.toISOString(),
updatedAt: agent.updatedAt.toISOString(),
};
}
async deleteAgent(id, userId) {
const existingAgent = await this.chatAgentRepository.getOneById(id, userId);
if (!existingAgent) {
throw new not_found_error_1.NotFoundError('Chat agent not found');
}
try {
await this.deleteEmbeddingsByAgentId(userId, id);
}
catch (error) {
this.logger.warn(`Could not delete embeddings from vector store for agent ${id}. Proceeding with agent deletion.`, { error });
}
await this.chatAgentRepository.deleteAgent(id);
this.logger.debug(`Chat agent deleted: ${id} by user ${userId}`);
}
async ensureSemanticSearchOptions() {
const options = await this.chatHubSettingsService.getSemanticSearchOptions();
if (options === null) {
throw new bad_request_error_1.BadRequestError('No vector store credential configured. Please set up a vector store credential in the Chat Hub settings.');
}
return options;
}
async insertEmbeddings(user, agentId, files, workflowId) {
if (files.length === 0) {
return;
}
try {
const settings = await this.ensureSemanticSearchOptions();
const { workflowData, executionData } = await this.chatAgentRepository.manager.transaction(async (trx) => {
const project = await this.chatHubCredentialsService.findPersonalProject(user, trx);
return await this.chatHubWorkflowService.createEmbeddingsInsertionWorkflow(user, project.id, files, agentId, settings, trx, workflowId);
});
const execution = await this.workflowExecutionService.executeChatWorkflow(user, workflowData, executionData, undefined, false, 'chat');
await this.chatHubExecutionService.waitForExecutionCompletion(execution.executionId);
await this.chatHubExecutionService.ensureWasSuccessfulOrThrow(execution.executionId, 'Could not insert documents');
}
finally {
await this.chatHubWorkflowService.deleteChatWorkflow(workflowId);
}
}
async deleteAllEmbeddingsForUser(userId) {
const options = await this.chatHubSettingsService.getSemanticSearchOptions();
if (!options) {
return;
}
const additionalData = await (0, workflow_execute_additional_data_1.getBase)({ userId });
await this.dynamicNodeParametersService.getActionResult('deleteDocuments', '', additionalData, { name: options.vectorStore.nodeType, version: 1 }, {}, JSON.stringify({ filter: {} }), {
[options.vectorStore.credentialType]: { id: options.vectorStore.credentialId, name: '' },
});
this.logger.debug(`Deleted all embeddings for user ${userId} from vector store`);
}
async deleteEmbeddingsByAgentId(userId, agentId) {
const settings = await this.chatHubSettingsService.getSemanticSearchOptions();
if (!settings) {
return;
}
const additionalData = await (0, workflow_execute_additional_data_1.getBase)({ userId });
await this.dynamicNodeParametersService.getActionResult('deleteDocuments', '', additionalData, { name: settings.vectorStore.nodeType, version: 1 }, {}, JSON.stringify({ filter: { agentId } }), {
[settings.vectorStore.credentialType]: { id: settings.vectorStore.credentialId, name: '' },
});
this.logger.debug(`Deleted embeddings for agent ${agentId} from vector store`);
}
async deleteEmbeddingsByKnowledgeId(user, agentId, fileKnowledgeId) {
const settings = await this.ensureSemanticSearchOptions();
const additionalData = await (0, workflow_execute_additional_data_1.getBase)({ userId: user.id });
await this.dynamicNodeParametersService.getActionResult('deleteDocuments', '', additionalData, { name: settings.vectorStore.nodeType, version: 1 }, {}, JSON.stringify({ filter: { agentId, fileKnowledgeId } }), {
[settings.vectorStore.credentialType]: { id: settings.vectorStore.credentialId, name: '' },
});
this.logger.debug(`Deleted embeddings from vector store (agentId: ${agentId}, knowledgeId: ${fileKnowledgeId})`);
}
async addFilesToAgent(agentId, user, files) {
const agent = await this.getAgentById(agentId, user.id);
const { knowledgeItems, pdfFilesToInsert, workflowId } = await this.prepareFilesForIndexing(files);
const updatedAgent = await this.chatAgentRepository.updateAgent(agentId, {
files: agent.files.concat(knowledgeItems),
});
void this.runIndexingInBackground(agentId, knowledgeItems.map((i) => i.id), pdfFilesToInsert, workflowId, user);
const toolIds = await this.chatHubToolService.getToolIdsForAgent(agentId);
this.logger.debug(`Added ${files.length} file(s) to agent ${agentId} by user ${user.id}`);
return this.toDto(updatedAgent, toolIds);
}
async runIndexingInBackground(agentId, knowledgeIds, pdfFilesToInsert, workflowId, user) {
try {
await this.insertEmbeddings(user, agentId, pdfFilesToInsert, workflowId);
await this.updateKnowledgeItemStatuses(agentId, knowledgeIds, 'indexed');
this.logger.debug(`Indexed ${knowledgeIds.length} file(s) for agent ${agentId}`);
}
catch (error) {
this.logger.error(`Failed to index files for agent ${agentId}`, { error });
const errorMessage = error instanceof Error ? error.message : String(error);
await this.updateKnowledgeItemStatuses(agentId, knowledgeIds, 'error', errorMessage).catch((updateError) => {
this.logger.error(`Failed to update file statuses to error for agent ${agentId}`, {
error: updateError,
});
});
}
}
async updateKnowledgeItemStatuses(agentId, knowledgeIds, status, error) {
const agent = await this.chatAgentRepository.findOne({ where: { id: agentId } });
if (!agent) {
return;
}
const updatedFiles = agent.files.map((f) => knowledgeIds.includes(f.id) ? { ...f, status, error } : f);
await this.chatAgentRepository.updateAgent(agentId, { files: updatedFiles });
}
async deleteAgentFile(agentId, user, fileKnowledgeId) {
const agent = await this.getAgentById(agentId, user.id);
const fileItem = agent.files.find((f) => f.id === fileKnowledgeId);
if (!fileItem) {
throw new not_found_error_1.NotFoundError('File not found');
}
try {
await this.deleteEmbeddingsByKnowledgeId(user, agentId, fileItem.id);
}
catch (error) {
this.logger.warn(`Could not delete embeddings from vector store for file "${fileItem.fileName}" (agentId: ${agentId}, knowledgeId: ${fileItem.id}). Proceeding with file deletion.`, { error });
}
await this.chatAgentRepository.updateAgent(agentId, {
files: agent.files.filter((f) => f.id !== fileKnowledgeId),
});
this.logger.debug(`Deleted file "${fileItem.fileName}" from agent ${agentId} by user ${user.id}`);
}
async prepareFilesForIndexing(files) {
const knowledgeItems = [];
const pdfFilesToInsert = [];
const workflowId = (0, uuid_1.v4)();
const settings = await this.ensureSemanticSearchOptions();
for (const file of files) {
if (file.mimetype !== 'application/pdf') {
throw new bad_request_error_1.BadRequestError(`Unsupported mime-type: ${file.mimetype}`);
}
}
for (const file of files) {
const originalName = Buffer.from(file.originalname, 'latin1').toString('utf8');
const buffer = file.buffer ?? (await (0, promises_1.readFile)(file.path));
if (file.path) {
await (0, promises_1.unlink)(file.path).catch((error) => {
this.logger.warn(`Could not unlink file in temporary path. File: ${originalName}`, {
error,
});
});
}
const storedFile = await this.chatHubAttachmentService.storeTemporaryExecutionFile(workflowId, buffer, file.mimetype, originalName);
const knowledgeId = (0, nanoid_1.nanoid)();
knowledgeItems.push({
id: knowledgeId,
type: 'embedding',
mimeType: file.mimetype,
fileName: storedFile.fileName ?? originalName,
provider: settings.embeddingModel.provider,
status: 'indexing',
createdAt: new Date().toISOString(),
});
pdfFilesToInsert.push({ attachment: storedFile, knowledgeId });
}
return { knowledgeItems, pdfFilesToInsert, workflowId };
}
};
exports.ChatHubAgentService = ChatHubAgentService;
exports.ChatHubAgentService = ChatHubAgentService = __decorate([
(0, di_1.Service)(),
__metadata("design:paramtypes", [backend_common_1.Logger,
chat_hub_agent_repository_1.ChatHubAgentRepository,
chat_hub_credentials_service_1.ChatHubCredentialsService,
chat_hub_attachment_service_1.ChatHubAttachmentService,
chat_hub_workflow_service_1.ChatHubWorkflowService,
chat_hub_execution_service_1.ChatHubExecutionService,
workflow_execution_service_1.WorkflowExecutionService,
chat_hub_settings_service_1.ChatHubSettingsService,
dynamic_node_parameters_service_1.DynamicNodeParametersService,
chat_hub_tool_service_1.ChatHubToolService])
], ChatHubAgentService);
//# sourceMappingURL=chat-hub-agent.service.js.map