feat: Add more AI node info to telemetry (#8827)

This commit is contained in:
Michael Kret 2024-03-07 12:46:07 +02:00 committed by GitHub
parent 0f7ae3f50a
commit ed6dc86d60
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 128 additions and 34 deletions

View file

@ -1083,6 +1083,7 @@ export default defineComponent({
TelemetryHelpers.generateNodesGraph( TelemetryHelpers.generateNodesGraph(
workflowData as IWorkflowBase, workflowData as IWorkflowBase,
this.workflowHelpers.getNodeTypes(), this.workflowHelpers.getNodeTypes(),
{ isCloudDeployment: this.settingsStore.isCloudDeployment },
).nodeGraph, ).nodeGraph,
), ),
}; };
@ -1991,6 +1992,7 @@ export default defineComponent({
TelemetryHelpers.generateNodesGraph( TelemetryHelpers.generateNodesGraph(
workflowData as IWorkflowBase, workflowData as IWorkflowBase,
this.workflowHelpers.getNodeTypes(), this.workflowHelpers.getNodeTypes(),
{ isCloudDeployment: this.settingsStore.isCloudDeployment },
).nodeGraph, ).nodeGraph,
), ),
}; };
@ -2147,6 +2149,7 @@ export default defineComponent({
workflowData.meta && workflowData.meta.instanceId !== currInstanceId workflowData.meta && workflowData.meta.instanceId !== currInstanceId
? workflowData.meta.instanceId ? workflowData.meta.instanceId
: '', : '',
isCloudDeployment: this.settingsStore.isCloudDeployment,
}, },
).nodeGraph, ).nodeGraph,
); );

View file

@ -6,18 +6,54 @@ export const LOG_LEVELS = ['silent', 'error', 'warn', 'info', 'debug', 'verbose'
export const CODE_LANGUAGES = ['javaScript', 'python'] as const; export const CODE_LANGUAGES = ['javaScript', 'python'] as const;
export const CODE_EXECUTION_MODES = ['runOnceForAllItems', 'runOnceForEachItem'] as const; export const CODE_EXECUTION_MODES = ['runOnceForAllItems', 'runOnceForEachItem'] as const;
/**
* Nodes whose parameter values may refer to other nodes without expressions.
* Their content may need to be updated when the referenced node is renamed.
*/
export const NODES_WITH_RENAMABLE_CONTENT = new Set([
'n8n-nodes-base.code',
'n8n-nodes-base.function',
'n8n-nodes-base.functionItem',
]);
// Arbitrary value to represent an empty credential value // Arbitrary value to represent an empty credential value
export const CREDENTIAL_EMPTY_VALUE = export const CREDENTIAL_EMPTY_VALUE =
'__n8n_EMPTY_VALUE_7b1af746-3729-4c60-9b9b-e08eb29e58da' as const; '__n8n_EMPTY_VALUE_7b1af746-3729-4c60-9b9b-e08eb29e58da' as const;
export const FORM_TRIGGER_PATH_IDENTIFIER = 'n8n-form'; export const FORM_TRIGGER_PATH_IDENTIFIER = 'n8n-form';
//n8n-nodes-base
export const STICKY_NODE_TYPE = 'n8n-nodes-base.stickyNote';
export const NO_OP_NODE_TYPE = 'n8n-nodes-base.noOp';
export const HTTP_REQUEST_NODE_TYPE = 'n8n-nodes-base.httpRequest';
export const WEBHOOK_NODE_TYPE = 'n8n-nodes-base.webhook';
export const MANUAL_TRIGGER_NODE_TYPE = 'n8n-nodes-base.manualTrigger';
export const ERROR_TRIGGER_NODE_TYPE = 'n8n-nodes-base.errorTrigger';
export const START_NODE_TYPE = 'n8n-nodes-base.start';
export const EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE = 'n8n-nodes-base.executeWorkflowTrigger';
export const CODE_NODE_TYPE = 'n8n-nodes-base.code';
export const FUNCTION_NODE_TYPE = 'n8n-nodes-base.function';
export const FUNCTION_ITEM_NODE_TYPE = 'n8n-nodes-base.functionItem';
export const STARTING_NODE_TYPES = [
MANUAL_TRIGGER_NODE_TYPE,
EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE,
ERROR_TRIGGER_NODE_TYPE,
START_NODE_TYPE,
];
export const SCRIPTING_NODE_TYPES = [FUNCTION_NODE_TYPE, FUNCTION_ITEM_NODE_TYPE, CODE_NODE_TYPE];
/**
* Nodes whose parameter values may refer to other nodes without expressions.
* Their content may need to be updated when the referenced node is renamed.
*/
export const NODES_WITH_RENAMABLE_CONTENT = new Set([
CODE_NODE_TYPE,
FUNCTION_NODE_TYPE,
FUNCTION_ITEM_NODE_TYPE,
]);
//@n8n/n8n-nodes-langchain
export const MANUAL_CHAT_TRIGGER_LANGCHAIN_NODE_TYPE = '@n8n/n8n-nodes-langchain.manualChatTrigger';
export const AGENT_LANGCHAIN_NODE_TYPE = '@n8n/n8n-nodes-langchain.agent';
export const OPENAI_LANGCHAIN_NODE_TYPE = '@n8n/n8n-nodes-langchain.openAi';
export const CHAIN_SUMMARIZATION_LANGCHAIN_NODE_TYPE =
'@n8n/n8n-nodes-langchain.chainSummarization';
export const CODE_TOOL_LANGCHAIN_NODE_TYPE = '@n8n/n8n-nodes-langchain.toolCode';
export const WORKFLOW_TOOL_LANGCHAIN_NODE_TYPE = '@n8n/n8n-nodes-langchain.toolWorkflow';
export const LANGCHAIN_CUSTOM_TOOLS = [
CODE_TOOL_LANGCHAIN_NODE_TYPE,
WORKFLOW_TOOL_LANGCHAIN_NODE_TYPE,
];

View file

@ -2238,6 +2238,7 @@ export interface INodeGraphItem {
src_node_id?: string; src_node_id?: string;
src_instance_id?: string; src_instance_id?: string;
agent?: string; //@n8n/n8n-nodes-langchain.agent agent?: string; //@n8n/n8n-nodes-langchain.agent
prompts?: IDataObject[] | IDataObject; //ai node's prompts, cloud only
} }
export interface INodeNameIndex { export interface INodeNameIndex {

View file

@ -8,10 +8,18 @@ import type {
INodesGraphResult, INodesGraphResult,
IWorkflowBase, IWorkflowBase,
INodeTypes, INodeTypes,
IDataObject,
} from './Interfaces'; } from './Interfaces';
import { ApplicationError } from './errors/application.error'; import { ApplicationError } from './errors/application.error';
import {
const STICKY_NODE_TYPE = 'n8n-nodes-base.stickyNote'; AGENT_LANGCHAIN_NODE_TYPE,
CHAIN_SUMMARIZATION_LANGCHAIN_NODE_TYPE,
HTTP_REQUEST_NODE_TYPE,
LANGCHAIN_CUSTOM_TOOLS,
OPENAI_LANGCHAIN_NODE_TYPE,
STICKY_NODE_TYPE,
WEBHOOK_NODE_TYPE,
} from './Constants';
export function getNodeTypeForName(workflow: IWorkflowBase, nodeName: string): INode | undefined { export function getNodeTypeForName(workflow: IWorkflowBase, nodeName: string): INode | undefined {
return workflow.nodes.find((node) => node.name === nodeName); return workflow.nodes.find((node) => node.name === nodeName);
@ -95,6 +103,7 @@ export function generateNodesGraph(
options?: { options?: {
sourceInstanceId?: string; sourceInstanceId?: string;
nodeIdMap?: { [curr: string]: string }; nodeIdMap?: { [curr: string]: string };
isCloudDeployment?: boolean;
}, },
): INodesGraphResult { ): INodesGraphResult {
const nodeGraph: INodesGraph = { const nodeGraph: INodesGraph = {
@ -158,15 +167,15 @@ export function generateNodesGraph(
nodeItem.src_node_id = options.nodeIdMap[node.id]; nodeItem.src_node_id = options.nodeIdMap[node.id];
} }
if (node.type === '@n8n/n8n-nodes-langchain.agent') { if (node.type === AGENT_LANGCHAIN_NODE_TYPE) {
nodeItem.agent = (node.parameters.agent as string) || 'conversationalAgent'; nodeItem.agent = (node.parameters.agent as string) || 'conversationalAgent';
} else if (node.type === 'n8n-nodes-base.httpRequest' && node.typeVersion === 1) { } else if (node.type === HTTP_REQUEST_NODE_TYPE && node.typeVersion === 1) {
try { try {
nodeItem.domain = new URL(node.parameters.url as string).hostname; nodeItem.domain = new URL(node.parameters.url as string).hostname;
} catch { } catch {
nodeItem.domain = getDomainBase(node.parameters.url as string); nodeItem.domain = getDomainBase(node.parameters.url as string);
} }
} else if (node.type === 'n8n-nodes-base.httpRequest' && node.typeVersion > 1) { } else if (node.type === HTTP_REQUEST_NODE_TYPE && node.typeVersion > 1) {
const { authentication } = node.parameters as { authentication: string }; const { authentication } = node.parameters as { authentication: string };
nodeItem.credential_type = { nodeItem.credential_type = {
@ -182,7 +191,7 @@ export function generateNodesGraph(
nodeItem.domain_base = getDomainBase(url); nodeItem.domain_base = getDomainBase(url);
nodeItem.domain_path = getDomainPath(url); nodeItem.domain_path = getDomainPath(url);
nodeItem.method = node.parameters.requestMethod as string; nodeItem.method = node.parameters.requestMethod as string;
} else if (node.type === 'n8n-nodes-base.webhook') { } else if (node.type === WEBHOOK_NODE_TYPE) {
webhookNodeNames.push(node.name); webhookNodeNames.push(node.name);
} else { } else {
try { try {
@ -216,6 +225,58 @@ export function generateNodesGraph(
} }
} }
if (options?.isCloudDeployment === true) {
if (node.type === OPENAI_LANGCHAIN_NODE_TYPE) {
nodeItem.prompts =
(((node.parameters?.messages as IDataObject) || {}).values as IDataObject[]) || [];
}
if (node.type === AGENT_LANGCHAIN_NODE_TYPE) {
const prompts: IDataObject = {};
if (node.parameters?.text) {
prompts.text = node.parameters.text as string;
}
const nodeOptions = node.parameters?.options as IDataObject;
if (nodeOptions) {
const optionalMessagesKeys = [
'humanMessage',
'systemMessage',
'humanMessageTemplate',
'prefix',
'suffixChat',
'suffix',
'prefixPrompt',
'suffixPrompt',
];
for (const key of optionalMessagesKeys) {
if (nodeOptions[key]) {
prompts[key] = nodeOptions[key] as string;
}
}
}
if (Object.keys(prompts).length) {
nodeItem.prompts = prompts;
}
}
if (node.type === CHAIN_SUMMARIZATION_LANGCHAIN_NODE_TYPE) {
nodeItem.prompts = (
(((node.parameters?.options as IDataObject) || {})
.summarizationMethodAndPrompts as IDataObject) || {}
).values as IDataObject;
}
if (LANGCHAIN_CUSTOM_TOOLS.includes(node.type)) {
nodeItem.prompts = {
description: (node.parameters?.description as string) || '',
};
}
}
nodeGraph.nodes[index.toString()] = nodeItem; nodeGraph.nodes[index.toString()] = nodeItem;
nameIndices[node.name] = index.toString(); nameIndices[node.name] = index.toString();
}); });

View file

@ -52,7 +52,11 @@ import * as NodeHelpers from './NodeHelpers';
import * as ObservableObject from './ObservableObject'; import * as ObservableObject from './ObservableObject';
import { RoutingNode } from './RoutingNode'; import { RoutingNode } from './RoutingNode';
import { Expression } from './Expression'; import { Expression } from './Expression';
import { NODES_WITH_RENAMABLE_CONTENT } from './Constants'; import {
MANUAL_CHAT_TRIGGER_LANGCHAIN_NODE_TYPE,
NODES_WITH_RENAMABLE_CONTENT,
STARTING_NODE_TYPES,
} from './Constants';
import { ApplicationError } from './errors/application.error'; import { ApplicationError } from './errors/application.error';
function dedupe<T>(arr: T[]): T[] { function dedupe<T>(arr: T[]): T[] {
@ -990,7 +994,7 @@ export class Workflow {
nodeType = this.nodeTypes.getByNameAndVersion(node.type, node.typeVersion); nodeType = this.nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
// TODO: Identify later differently // TODO: Identify later differently
if (nodeType.description.name === '@n8n/n8n-nodes-langchain.manualChatTrigger') { if (nodeType.description.name === MANUAL_CHAT_TRIGGER_LANGCHAIN_NODE_TYPE) {
continue; continue;
} }
@ -1002,20 +1006,13 @@ export class Workflow {
} }
} }
const startingNodeTypes = [
'n8n-nodes-base.manualTrigger',
'n8n-nodes-base.executeWorkflowTrigger',
'n8n-nodes-base.errorTrigger',
'n8n-nodes-base.start',
];
const sortedNodeNames = Object.values(this.nodes) const sortedNodeNames = Object.values(this.nodes)
.sort((a, b) => startingNodeTypes.indexOf(a.type) - startingNodeTypes.indexOf(b.type)) .sort((a, b) => STARTING_NODE_TYPES.indexOf(a.type) - STARTING_NODE_TYPES.indexOf(b.type))
.map((n) => n.name); .map((n) => n.name);
for (const nodeName of sortedNodeNames) { for (const nodeName of sortedNodeNames) {
node = this.nodes[nodeName]; node = this.nodes[nodeName];
if (startingNodeTypes.includes(node.type)) { if (STARTING_NODE_TYPES.includes(node.type)) {
if (node.disabled === true) { if (node.disabled === true) {
continue; continue;
} }

View file

@ -28,6 +28,7 @@ import { augmentArray, augmentObject } from './AugmentObject';
import { deepCopy } from './utils'; import { deepCopy } from './utils';
import { getGlobalState } from './GlobalState'; import { getGlobalState } from './GlobalState';
import { ApplicationError } from './errors/application.error'; import { ApplicationError } from './errors/application.error';
import { SCRIPTING_NODE_TYPES } from './Constants';
export function isResourceLocatorValue(value: unknown): value is INodeParameterResourceLocator { export function isResourceLocatorValue(value: unknown): value is INodeParameterResourceLocator {
return Boolean( return Boolean(
@ -35,12 +36,6 @@ export function isResourceLocatorValue(value: unknown): value is INodeParameterR
); );
} }
const SCRIPTING_NODE_TYPES = [
'n8n-nodes-base.function',
'n8n-nodes-base.functionItem',
'n8n-nodes-base.code',
];
const isScriptingNode = (nodeName: string, workflow: Workflow) => { const isScriptingNode = (nodeName: string, workflow: Workflow) => {
const node = workflow.getNode(nodeName); const node = workflow.getNode(nodeName);

View file

@ -15,6 +15,7 @@ import { NodeError } from './abstract/node.error';
import { removeCircularRefs } from '../utils'; import { removeCircularRefs } from '../utils';
import type { ReportingOptions } from './application.error'; import type { ReportingOptions } from './application.error';
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import { NO_OP_NODE_TYPE } from '../Constants';
export interface NodeOperationErrorOptions { export interface NodeOperationErrorOptions {
message?: string; message?: string;
@ -282,7 +283,7 @@ export class NodeApiError extends NodeError {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
this.message = this.message || this.description || UNKNOWN_ERROR_MESSAGE; this.message = this.message || this.description || UNKNOWN_ERROR_MESSAGE;
} }
if (this.node.type === 'n8n-nodes-base.noOp' && this.message === UNKNOWN_ERROR_MESSAGE) { if (this.node.type === NO_OP_NODE_TYPE && this.message === UNKNOWN_ERROR_MESSAGE) {
this.message = `${UNKNOWN_ERROR_MESSAGE_CRED} - ${this.httpCode}`; this.message = `${UNKNOWN_ERROR_MESSAGE_CRED} - ${this.httpCode}`;
} }
} }