mirror of
https://github.com/n8n-io/n8n.git
synced 2024-09-11 13:15:28 +03:00
Use AI Assistant Service
This commit is contained in:
parent
0c18c1e93f
commit
b5bc89cdda
@ -68,6 +68,7 @@
|
|||||||
"@types/convict": "^6.1.1",
|
"@types/convict": "^6.1.1",
|
||||||
"@types/cookie-parser": "^1.4.2",
|
"@types/cookie-parser": "^1.4.2",
|
||||||
"@types/express": "^4.17.21",
|
"@types/express": "^4.17.21",
|
||||||
|
"@types/express-http-proxy": "^1.6.6",
|
||||||
"@types/formidable": "^3.4.5",
|
"@types/formidable": "^3.4.5",
|
||||||
"@types/json-diff": "^1.0.0",
|
"@types/json-diff": "^1.0.0",
|
||||||
"@types/jsonwebtoken": "^9.0.6",
|
"@types/jsonwebtoken": "^9.0.6",
|
||||||
@ -125,6 +126,7 @@
|
|||||||
"express": "4.19.2",
|
"express": "4.19.2",
|
||||||
"express-async-errors": "3.1.1",
|
"express-async-errors": "3.1.1",
|
||||||
"express-handlebars": "7.1.2",
|
"express-handlebars": "7.1.2",
|
||||||
|
"express-http-proxy": "^2.0.0",
|
||||||
"express-openapi-validator": "5.1.6",
|
"express-openapi-validator": "5.1.6",
|
||||||
"express-prom-bundle": "6.6.0",
|
"express-prom-bundle": "6.6.0",
|
||||||
"express-rate-limit": "7.2.0",
|
"express-rate-limit": "7.2.0",
|
||||||
|
@ -92,6 +92,10 @@ export abstract class AbstractServer {
|
|||||||
// Additional configuration in derived classes
|
// Additional configuration in derived classes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async configureProxyEndpoints(): Promise<void> {
|
||||||
|
// Additional configuration in derived classes
|
||||||
|
}
|
||||||
|
|
||||||
private async setupErrorHandlers() {
|
private async setupErrorHandlers() {
|
||||||
const { app } = this;
|
const { app } = this;
|
||||||
|
|
||||||
@ -233,6 +237,9 @@ export abstract class AbstractServer {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup proxy endpoints before body parsing
|
||||||
|
await this.configureProxyEndpoints();
|
||||||
|
|
||||||
// Setup body parsing middleware after the webhook handlers are setup
|
// Setup body parsing middleware after the webhook handlers are setup
|
||||||
this.app.use(bodyParser);
|
this.app.use(bodyParser);
|
||||||
|
|
||||||
|
@ -191,6 +191,12 @@ export class Server extends AbstractServer {
|
|||||||
controllers.forEach((controller) => registerController(app, controller));
|
controllers.forEach((controller) => registerController(app, controller));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async configureProxyEndpoints(): Promise<void> {
|
||||||
|
const aiController = Container.get(AIController);
|
||||||
|
this.app.use('/rest/ai', aiController.debugWithAssistant);
|
||||||
|
registerController(this.app, AIController);
|
||||||
|
}
|
||||||
|
|
||||||
async configure(): Promise<void> {
|
async configure(): Promise<void> {
|
||||||
if (config.getEnv('endpoints.metrics.enable')) {
|
if (config.getEnv('endpoints.metrics.enable')) {
|
||||||
const { MetricsService } = await import('@/services/metrics.service');
|
const { MetricsService } = await import('@/services/metrics.service');
|
||||||
|
@ -19,7 +19,11 @@ import { z } from 'zod';
|
|||||||
import zodToJsonSchema from 'zod-to-json-schema';
|
import zodToJsonSchema from 'zod-to-json-schema';
|
||||||
import { ChatMessageHistory } from 'langchain/stores/message/in_memory';
|
import { ChatMessageHistory } from 'langchain/stores/message/in_memory';
|
||||||
import { ApplicationError } from 'n8n-workflow';
|
import { ApplicationError } from 'n8n-workflow';
|
||||||
import { NO_MORE_SUGGESTIONS_PROMPT, QUICK_ACTIONS, REACT_DEBUG_PROMPT } from '@/aiAssistant/prompts/debug_prompts';
|
import {
|
||||||
|
NO_MORE_SUGGESTIONS_PROMPT,
|
||||||
|
QUICK_ACTIONS,
|
||||||
|
REACT_DEBUG_PROMPT,
|
||||||
|
} from '@/aiAssistant/prompts/debug_prompts';
|
||||||
import {
|
import {
|
||||||
addConversationToHistory,
|
addConversationToHistory,
|
||||||
chatHistory,
|
chatHistory,
|
||||||
@ -35,6 +39,7 @@ import { createInternetSearchTool } from '@/aiAssistant/tools/internet_search.to
|
|||||||
import { prepareDebugUserPrompt } from '@/aiAssistant/utils';
|
import { prepareDebugUserPrompt } from '@/aiAssistant/utils';
|
||||||
import { N8N_BLOG, N8N_COMMUNITY, N8N_MARKETING_WEBSITE } from '@/aiAssistant/constants';
|
import { N8N_BLOG, N8N_COMMUNITY, N8N_MARKETING_WEBSITE } from '@/aiAssistant/constants';
|
||||||
import { getNextUserPrompt } from '@/aiAssistant/intent_detector';
|
import { getNextUserPrompt } from '@/aiAssistant/intent_detector';
|
||||||
|
import proxy from 'express-http-proxy';
|
||||||
|
|
||||||
const errorSuggestionSchema = z.object({
|
const errorSuggestionSchema = z.object({
|
||||||
suggestion: z.object({
|
suggestion: z.object({
|
||||||
@ -97,22 +102,41 @@ export class AIController {
|
|||||||
await this.askAssistant(message, res);
|
await this.askAssistant(message, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debugWithAssistant = proxy('http://127.0.0.1:3000/ai-assistant', {});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debug n8n error using the agent that has access to different tools.
|
* Debug n8n error using the agent that has access to different tools.
|
||||||
* THIS IS THE DEBUG MODE
|
* THIS IS THE DEBUG MODE
|
||||||
*/
|
*/
|
||||||
@Post('/debug-with-assistant', { skipAuth: true })
|
// @Post('/debug-with-assistant', { skipAuth: true })
|
||||||
async debugWithAssistant(req: AIRequest.AssistantDebug, res: express.Response) {
|
// async debugWithAssistant(req: AIRequest.AssistantDebug, res: express.Response) {
|
||||||
const { nodeType, error, errorNode, authType, message, userTraits, nodeInputData, referencedNodesData } = req.body;
|
// const {
|
||||||
resetToolHistory();
|
// nodeType,
|
||||||
if (message) {
|
// error,
|
||||||
await this.askAssistant(`${message}\n`, res, true);
|
// errorNode,
|
||||||
return;
|
// authType,
|
||||||
}
|
// message,
|
||||||
clearChatHistory();
|
// userTraits,
|
||||||
const userPrompt = prepareDebugUserPrompt(nodeType, error, errorNode, authType, userTraits, nodeInputData, referencedNodesData);
|
// nodeInputData,
|
||||||
await this.askAssistant(userPrompt, res, true);
|
// referencedNodesData,
|
||||||
}
|
// } = req.body;
|
||||||
|
// resetToolHistory();
|
||||||
|
// if (message) {
|
||||||
|
// await this.askAssistant(`${message}\n`, res, true);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// clearChatHistory();
|
||||||
|
// const userPrompt = prepareDebugUserPrompt(
|
||||||
|
// nodeType,
|
||||||
|
// error,
|
||||||
|
// errorNode,
|
||||||
|
// authType,
|
||||||
|
// userTraits,
|
||||||
|
// nodeInputData,
|
||||||
|
// referencedNodesData,
|
||||||
|
// );
|
||||||
|
// await this.askAssistant(userPrompt, res, true);
|
||||||
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chat with pinecone vector store that contains n8n documentation.
|
* Chat with pinecone vector store that contains n8n documentation.
|
||||||
@ -170,9 +194,14 @@ export class AIController {
|
|||||||
// ----------------- Tools -----------------
|
// ----------------- Tools -----------------
|
||||||
// Internet search tool setup:
|
// Internet search tool setup:
|
||||||
// - In debug mode, use only forum to search for answers, while in free-chat mode use all websites (and more results)
|
// - In debug mode, use only forum to search for answers, while in free-chat mode use all websites (and more results)
|
||||||
const internetToolWebsites = debug ? [N8N_COMMUNITY] : [N8N_MARKETING_WEBSITE, N8N_BLOG, N8N_COMMUNITY];
|
const internetToolWebsites = debug
|
||||||
|
? [N8N_COMMUNITY]
|
||||||
|
: [N8N_MARKETING_WEBSITE, N8N_BLOG, N8N_COMMUNITY];
|
||||||
const internetToolMaxResults = debug ? 5 : 10;
|
const internetToolMaxResults = debug ? 5 : 10;
|
||||||
const internetSearchTool = createInternetSearchTool(internetToolWebsites, internetToolMaxResults);
|
const internetSearchTool = createInternetSearchTool(
|
||||||
|
internetToolWebsites,
|
||||||
|
internetToolMaxResults,
|
||||||
|
);
|
||||||
const tools = [n8nInfoTool, internetSearchTool];
|
const tools = [n8nInfoTool, internetSearchTool];
|
||||||
const toolNames = tools.map((tool) => tool.name);
|
const toolNames = tools.map((tool) => tool.name);
|
||||||
|
|
||||||
@ -182,7 +211,6 @@ export class AIController {
|
|||||||
? ChatPromptTemplate.fromTemplate(REACT_DEBUG_PROMPT)
|
? ChatPromptTemplate.fromTemplate(REACT_DEBUG_PROMPT)
|
||||||
: ChatPromptTemplate.fromTemplate(REACT_CHAT_PROMPT);
|
: ChatPromptTemplate.fromTemplate(REACT_CHAT_PROMPT);
|
||||||
|
|
||||||
|
|
||||||
const agent = await createReactAgent({
|
const agent = await createReactAgent({
|
||||||
llm: assistantModel,
|
llm: assistantModel,
|
||||||
tools,
|
tools,
|
||||||
@ -202,14 +230,14 @@ export class AIController {
|
|||||||
noMoreHelp = checkIfAllHelpUsed();
|
noMoreHelp = checkIfAllHelpUsed();
|
||||||
// Hard-stop if human asks for too many suggestions
|
// Hard-stop if human asks for too many suggestions
|
||||||
if (noMoreHelp) {
|
if (noMoreHelp) {
|
||||||
userMessage = NO_MORE_SUGGESTIONS_PROMPT;
|
userMessage = NO_MORE_SUGGESTIONS_PROMPT;
|
||||||
} else {
|
} else {
|
||||||
// Detect user intention and map it to the correct prompt
|
// Detect user intention and map it to the correct prompt
|
||||||
const lastFollowUpQuestion = getLastFollowUpQuestion(chatHistory);
|
const lastFollowUpQuestion = getLastFollowUpQuestion(chatHistory);
|
||||||
// Only if there is a follow-up question, we don't want to alter the initial debug prompt
|
// Only if there is a follow-up question, we don't want to alter the initial debug prompt
|
||||||
if (lastFollowUpQuestion) {
|
if (lastFollowUpQuestion) {
|
||||||
const detectorResult = await getNextUserPrompt(userMessage, lastFollowUpQuestion);
|
const detectorResult = await getNextUserPrompt(userMessage, lastFollowUpQuestion);
|
||||||
userMessage = detectorResult.prompt;
|
userMessage = detectorResult.prompt;
|
||||||
increaseHelpCounter(detectorResult.detectedIntent);
|
increaseHelpCounter(detectorResult.detectedIntent);
|
||||||
}
|
}
|
||||||
// First response is also the suggestion, if we want to count that:
|
// First response is also the suggestion, if we want to count that:
|
||||||
@ -241,12 +269,13 @@ export class AIController {
|
|||||||
if (error instanceof Error)
|
if (error instanceof Error)
|
||||||
console.log('>> ⚠️ <<', `Error: Could not parse LLM output: ${error.toString()}`);
|
console.log('>> ⚠️ <<', `Error: Could not parse LLM output: ${error.toString()}`);
|
||||||
|
|
||||||
response = error.toString().replace(/Error: Could not parse LLM output: /, '');
|
response = error.toString().replace(/Error: Could not parse LLM output: /, '');
|
||||||
}
|
}
|
||||||
console.log('>> 🤖 <<', response);
|
console.log('>> 🤖 <<', response);
|
||||||
|
|
||||||
addConversationToHistory(userMessage, response);
|
addConversationToHistory(userMessage, response);
|
||||||
let debugInfo = '--------------------------------- [DEBUG INFO] -----------------------------------\n';
|
let debugInfo =
|
||||||
|
'--------------------------------- [DEBUG INFO] -----------------------------------\n';
|
||||||
debugInfo +=
|
debugInfo +=
|
||||||
toolHistory.n8n_documentation.length > 0
|
toolHistory.n8n_documentation.length > 0
|
||||||
? `📄 N8N DOCS DOCUMENTS USED: \n• ${toolHistory.n8n_documentation.join('\n• ')}\n`
|
? `📄 N8N DOCS DOCUMENTS USED: \n• ${toolHistory.n8n_documentation.join('\n• ')}\n`
|
||||||
|
@ -114,7 +114,7 @@ export const debugWithAssistant = async (
|
|||||||
const headers = {
|
const headers = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
};
|
};
|
||||||
const response = await fetch(`${context.baseUrl}/ai/debug-with-assistant`, {
|
const response = await fetch(`${context.baseUrl}/ai/ai-assistant/error-debug`, {
|
||||||
headers,
|
headers,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
@ -225,7 +225,7 @@ export const debugChatWithAiErrorHelper = async (
|
|||||||
const headers = {
|
const headers = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
};
|
};
|
||||||
const response = await fetch(`${context.baseUrl}/ai/debug-chat`, {
|
const response = await fetch(`${context.baseUrl}/ai/ai-assistant/code-node-debug`, {
|
||||||
headers,
|
headers,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
|
Loading…
Reference in New Issue
Block a user