first commit
This commit is contained in:
553
node_modules/n8n-workflow/dist/esm/telemetry-helpers.js
generated
vendored
Normal file
553
node_modules/n8n-workflow/dist/esm/telemetry-helpers.js
generated
vendored
Normal file
@@ -0,0 +1,553 @@
|
||||
import { AGENT_LANGCHAIN_NODE_TYPE, AGENT_TOOL_LANGCHAIN_NODE_TYPE, AI_TRANSFORM_NODE_TYPE, CHAIN_LLM_LANGCHAIN_NODE_TYPE, CHAIN_SUMMARIZATION_LANGCHAIN_NODE_TYPE, CHAT_TRIGGER_NODE_TYPE, CODE_NODE_TYPE, EVALUATION_NODE_TYPE, EVALUATION_TRIGGER_NODE_TYPE, EXECUTE_WORKFLOW_NODE_TYPE, FREE_AI_CREDITS_ERROR_TYPE, FREE_AI_CREDITS_USED_ALL_CREDITS_ERROR_CODE, FROM_AI_AUTO_GENERATED_MARKER, HTTP_REQUEST_NODE_TYPE, HTTP_REQUEST_TOOL_LANGCHAIN_NODE_TYPE, LANGCHAIN_CUSTOM_TOOLS, MERGE_NODE_TYPE, OPEN_AI_API_CREDENTIAL_TYPE, OPENAI_LANGCHAIN_NODE_TYPE, STICKY_NODE_TYPE, WEBHOOK_NODE_TYPE, WORKFLOW_TOOL_LANGCHAIN_NODE_TYPE, } from './constants';
|
||||
import { ApplicationError } from '@n8n/errors';
|
||||
import { NodeConnectionTypes } from './interfaces';
|
||||
import { getNodeParameters } from './node-helpers';
|
||||
import { jsonParse } from './utils';
|
||||
import { DEFAULT_EVALUATION_METRIC } from './evaluation-helpers';
|
||||
const isNodeApiError = (error) => typeof error === 'object' && error !== null && 'name' in error && error?.name === 'NodeApiError';
|
||||
export function getNodeTypeForName(workflow, nodeName) {
|
||||
return workflow.nodes.find((node) => node.name === nodeName);
|
||||
}
|
||||
export function isNumber(value) {
|
||||
return typeof value === 'number';
|
||||
}
|
||||
const countPlaceholders = (text) => {
|
||||
const placeholder = /(\{[a-zA-Z0-9_]+\})/g;
|
||||
let returnData = 0;
|
||||
try {
|
||||
const matches = text.matchAll(placeholder);
|
||||
for (const _ of matches)
|
||||
returnData++;
|
||||
}
|
||||
catch (error) { }
|
||||
return returnData;
|
||||
};
|
||||
const countPlaceholdersInParameters = (parameters) => {
|
||||
let returnData = 0;
|
||||
for (const parameter of parameters) {
|
||||
if (!parameter.value) {
|
||||
//count parameters provided by model
|
||||
returnData++;
|
||||
}
|
||||
else {
|
||||
//check if any placeholders in user provided value
|
||||
returnData += countPlaceholders(String(parameter.value));
|
||||
}
|
||||
}
|
||||
return returnData;
|
||||
};
|
||||
function areOverlapping(topLeft, bottomRight, targetPos) {
|
||||
return (targetPos[0] > topLeft[0] &&
|
||||
targetPos[1] > topLeft[1] &&
|
||||
targetPos[0] < bottomRight[0] &&
|
||||
targetPos[1] < bottomRight[1]);
|
||||
}
|
||||
const URL_PARTS_REGEX = /(?<protocolPlusDomain>.*?\..*?)(?<pathname>\/.*)/;
|
||||
export function getDomainBase(raw, urlParts = URL_PARTS_REGEX) {
|
||||
try {
|
||||
const url = new URL(raw);
|
||||
return [url.protocol, url.hostname].join('//');
|
||||
}
|
||||
catch {
|
||||
const match = urlParts.exec(raw);
|
||||
if (!match?.groups?.protocolPlusDomain)
|
||||
return '';
|
||||
return match.groups.protocolPlusDomain;
|
||||
}
|
||||
}
|
||||
function isSensitive(segment) {
|
||||
if (/^v\d+$/.test(segment))
|
||||
return false;
|
||||
return /%40/.test(segment) || /\d/.test(segment) || /^[0-9A-F]{8}/i.test(segment);
|
||||
}
|
||||
export const ANONYMIZATION_CHARACTER = '*';
|
||||
function sanitizeRoute(raw, check = isSensitive, char = ANONYMIZATION_CHARACTER) {
|
||||
return raw
|
||||
.split('/')
|
||||
.map((segment) => (check(segment) ? char.repeat(segment.length) : segment))
|
||||
.join('/');
|
||||
}
|
||||
/**
|
||||
* Return pathname plus query string from URL, anonymizing IDs in route and query params.
|
||||
*/
|
||||
export function getDomainPath(raw, urlParts = URL_PARTS_REGEX) {
|
||||
try {
|
||||
const url = new URL(raw);
|
||||
if (!url.hostname)
|
||||
throw new ApplicationError('Malformed URL');
|
||||
return sanitizeRoute(url.pathname);
|
||||
}
|
||||
catch {
|
||||
const match = urlParts.exec(raw);
|
||||
if (!match?.groups?.pathname)
|
||||
return '';
|
||||
// discard query string
|
||||
const route = match.groups.pathname.split('?').shift();
|
||||
return sanitizeRoute(route);
|
||||
}
|
||||
}
|
||||
function getNumberOfItemsInRuns(runs) {
|
||||
return runs.reduce((total, run) => {
|
||||
const data = run.data ?? {};
|
||||
let count = 0;
|
||||
Object.keys(data).forEach((type) => {
|
||||
const conn = data[type] ?? [];
|
||||
conn.forEach((branch) => {
|
||||
count += (branch ?? []).length;
|
||||
});
|
||||
});
|
||||
return total + count;
|
||||
}, 0);
|
||||
}
|
||||
export function generateNodesGraph(workflow, nodeTypes, options) {
|
||||
const { runData } = options ?? {};
|
||||
const nodeGraph = {
|
||||
node_types: [],
|
||||
node_connections: [],
|
||||
nodes: {},
|
||||
notes: {},
|
||||
is_pinned: Object.keys(workflow.pinData ?? {}).length > 0,
|
||||
};
|
||||
const nameIndices = {};
|
||||
const webhookNodeNames = [];
|
||||
const evaluationTriggerNodeNames = [];
|
||||
const nodes = (workflow.nodes ?? []).filter((node) => node.type === STICKY_NODE_TYPE);
|
||||
const otherNodes = (workflow.nodes ?? []).filter((node) => node.type !== STICKY_NODE_TYPE);
|
||||
nodes.forEach((stickyNote, index) => {
|
||||
const stickyType = nodeTypes.getByNameAndVersion(STICKY_NODE_TYPE, stickyNote.typeVersion);
|
||||
if (!stickyType) {
|
||||
return;
|
||||
}
|
||||
let nodeParameters = {};
|
||||
try {
|
||||
nodeParameters =
|
||||
getNodeParameters(stickyType.description.properties, stickyNote.parameters, true, false, stickyNote, stickyType.description) ?? {};
|
||||
}
|
||||
catch {
|
||||
// prevent node param resolution from failing graph generation
|
||||
}
|
||||
const height = typeof nodeParameters.height === 'number' ? nodeParameters.height : 0;
|
||||
const width = typeof nodeParameters.width === 'number' ? nodeParameters.width : 0;
|
||||
const topLeft = stickyNote.position;
|
||||
const bottomRight = [topLeft[0] + width, topLeft[1] + height];
|
||||
const overlapping = Boolean(otherNodes.find((node) => areOverlapping(topLeft, bottomRight, node.position)));
|
||||
nodeGraph.notes[index] = {
|
||||
overlapping,
|
||||
position: topLeft,
|
||||
height,
|
||||
width,
|
||||
};
|
||||
});
|
||||
// eslint-disable-next-line complexity
|
||||
otherNodes.forEach((node, index) => {
|
||||
nodeGraph.node_types.push(node.type);
|
||||
const nodeItem = {
|
||||
id: node.id,
|
||||
type: node.type,
|
||||
version: node.typeVersion,
|
||||
position: node.position,
|
||||
};
|
||||
const nodeType = nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
|
||||
if (nodeType?.description?.communityNodePackageVersion) {
|
||||
nodeItem.package_version = nodeType.description.communityNodePackageVersion;
|
||||
}
|
||||
if (runData?.[node.name]) {
|
||||
const runs = runData[node.name] ?? [];
|
||||
nodeItem.runs = runs.length;
|
||||
nodeItem.items_total = getNumberOfItemsInRuns(runs);
|
||||
}
|
||||
if (options?.sourceInstanceId) {
|
||||
nodeItem.src_instance_id = options.sourceInstanceId;
|
||||
}
|
||||
if (node.id && options?.nodeIdMap?.[node.id]) {
|
||||
nodeItem.src_node_id = options.nodeIdMap[node.id];
|
||||
}
|
||||
if (node.type === AI_TRANSFORM_NODE_TYPE && options?.isCloudDeployment) {
|
||||
nodeItem.prompts = { instructions: node.parameters.instructions };
|
||||
}
|
||||
else if (node.type === AGENT_LANGCHAIN_NODE_TYPE) {
|
||||
nodeItem.agent = node.parameters.agent ?? 'toolsAgent';
|
||||
if (node.typeVersion >= 2.1) {
|
||||
const options = node.parameters?.options;
|
||||
if (typeof options === 'object' &&
|
||||
options &&
|
||||
'enableStreaming' in options &&
|
||||
options.enableStreaming === false) {
|
||||
nodeItem.is_streaming = false;
|
||||
}
|
||||
else {
|
||||
nodeItem.is_streaming = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (node.type === MERGE_NODE_TYPE) {
|
||||
nodeItem.operation = node.parameters.mode;
|
||||
if (options?.isCloudDeployment && node.parameters.mode === 'combineBySql') {
|
||||
nodeItem.sql = node.parameters.query;
|
||||
}
|
||||
}
|
||||
else if (node.type === HTTP_REQUEST_NODE_TYPE && node.typeVersion === 1) {
|
||||
try {
|
||||
nodeItem.domain = new URL(node.parameters.url).hostname;
|
||||
}
|
||||
catch {
|
||||
nodeItem.domain = getDomainBase(node.parameters.url);
|
||||
}
|
||||
}
|
||||
else if (node.type === HTTP_REQUEST_NODE_TYPE && node.typeVersion > 1) {
|
||||
const { authentication } = node.parameters;
|
||||
nodeItem.credential_type = {
|
||||
none: 'none',
|
||||
genericCredentialType: node.parameters.genericAuthType,
|
||||
predefinedCredentialType: node.parameters.nodeCredentialType,
|
||||
}[authentication];
|
||||
nodeItem.credential_set = node.credentials ? Object.keys(node.credentials).length > 0 : false;
|
||||
const { url } = node.parameters;
|
||||
nodeItem.domain_base = getDomainBase(url);
|
||||
nodeItem.domain_path = getDomainPath(url);
|
||||
nodeItem.method = node.parameters.requestMethod;
|
||||
}
|
||||
else if (HTTP_REQUEST_TOOL_LANGCHAIN_NODE_TYPE === node.type) {
|
||||
if (!nodeItem.toolSettings)
|
||||
nodeItem.toolSettings = {};
|
||||
nodeItem.toolSettings.url_type = 'other';
|
||||
nodeItem.toolSettings.uses_auth = false;
|
||||
nodeItem.toolSettings.placeholders = 0;
|
||||
nodeItem.toolSettings.query_from_model_only = false;
|
||||
nodeItem.toolSettings.headers_from_model_only = false;
|
||||
nodeItem.toolSettings.body_from_model_only = false;
|
||||
const toolUrl = node.parameters?.url ?? '';
|
||||
nodeItem.toolSettings.placeholders += countPlaceholders(toolUrl);
|
||||
const authType = node.parameters?.authentication ?? '';
|
||||
if (authType && authType !== 'none') {
|
||||
nodeItem.toolSettings.uses_auth = true;
|
||||
}
|
||||
if (toolUrl.startsWith('{') && toolUrl.endsWith('}')) {
|
||||
nodeItem.toolSettings.url_type = 'any';
|
||||
}
|
||||
else if (toolUrl.includes('google.com')) {
|
||||
nodeItem.toolSettings.url_type = 'google';
|
||||
}
|
||||
if (node.parameters?.sendBody) {
|
||||
if (node.parameters?.specifyBody === 'model') {
|
||||
nodeItem.toolSettings.body_from_model_only = true;
|
||||
}
|
||||
if (node.parameters?.jsonBody) {
|
||||
nodeItem.toolSettings.placeholders += countPlaceholders(node.parameters?.jsonBody);
|
||||
}
|
||||
if (node.parameters?.parametersBody) {
|
||||
const parameters = (node.parameters?.parametersBody)
|
||||
.values;
|
||||
nodeItem.toolSettings.placeholders += countPlaceholdersInParameters(parameters);
|
||||
}
|
||||
}
|
||||
if (node.parameters?.sendHeaders) {
|
||||
if (node.parameters?.specifyHeaders === 'model') {
|
||||
nodeItem.toolSettings.headers_from_model_only = true;
|
||||
}
|
||||
if (node.parameters?.jsonHeaders) {
|
||||
nodeItem.toolSettings.placeholders += countPlaceholders(node.parameters?.jsonHeaders);
|
||||
}
|
||||
if (node.parameters?.parametersHeaders) {
|
||||
const parameters = (node.parameters?.parametersHeaders)
|
||||
.values;
|
||||
nodeItem.toolSettings.placeholders += countPlaceholdersInParameters(parameters);
|
||||
}
|
||||
}
|
||||
if (node.parameters?.sendQuery) {
|
||||
if (node.parameters?.specifyQuery === 'model') {
|
||||
nodeItem.toolSettings.query_from_model_only = true;
|
||||
}
|
||||
if (node.parameters?.jsonQuery) {
|
||||
nodeItem.toolSettings.placeholders += countPlaceholders(node.parameters?.jsonQuery);
|
||||
}
|
||||
if (node.parameters?.parametersQuery) {
|
||||
const parameters = (node.parameters?.parametersQuery)
|
||||
.values;
|
||||
nodeItem.toolSettings.placeholders += countPlaceholdersInParameters(parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (node.type === WEBHOOK_NODE_TYPE) {
|
||||
webhookNodeNames.push(node.name);
|
||||
const responseMode = node.parameters?.responseMode;
|
||||
nodeItem.response_mode = typeof responseMode === 'string' ? responseMode : 'onReceived';
|
||||
}
|
||||
else if (node.type === CHAT_TRIGGER_NODE_TYPE) {
|
||||
// Capture streaming response mode parameter
|
||||
const options = node.parameters?.options;
|
||||
if (typeof options === 'object' &&
|
||||
options &&
|
||||
'responseMode' in options &&
|
||||
typeof options.responseMode === 'string') {
|
||||
nodeItem.response_mode = options.responseMode;
|
||||
}
|
||||
// Capture public chat setting
|
||||
const isPublic = node.parameters?.public;
|
||||
if (typeof isPublic === 'boolean') {
|
||||
nodeItem.public_chat = isPublic;
|
||||
}
|
||||
}
|
||||
else if (node.type === EXECUTE_WORKFLOW_NODE_TYPE ||
|
||||
node.type === WORKFLOW_TOOL_LANGCHAIN_NODE_TYPE) {
|
||||
if (node.parameters?.workflowId) {
|
||||
nodeItem.workflow_id = node.parameters?.workflowId;
|
||||
}
|
||||
}
|
||||
else if (node.type === EVALUATION_TRIGGER_NODE_TYPE) {
|
||||
evaluationTriggerNodeNames.push(node.name);
|
||||
}
|
||||
else if (node.type === EVALUATION_NODE_TYPE &&
|
||||
options?.isCloudDeployment &&
|
||||
node.parameters?.operation === 'setMetrics') {
|
||||
const metrics = node.parameters?.metrics;
|
||||
// If metrics are not defined, it means the node is using preconfigured metric
|
||||
if (!metrics) {
|
||||
const predefinedMetricKey = node.parameters?.metric ?? DEFAULT_EVALUATION_METRIC;
|
||||
nodeItem.metric_names = [predefinedMetricKey];
|
||||
}
|
||||
else {
|
||||
nodeItem.metric_names = metrics.assignments?.map((metric) => metric.name);
|
||||
}
|
||||
}
|
||||
else if (node.type === CODE_NODE_TYPE) {
|
||||
const { language } = node.parameters;
|
||||
nodeItem.language =
|
||||
language === undefined
|
||||
? 'javascript'
|
||||
: language === 'python'
|
||||
? 'python'
|
||||
: language === 'pythonNative'
|
||||
? 'pythonNative'
|
||||
: 'unknown';
|
||||
}
|
||||
else {
|
||||
try {
|
||||
const nodeType = nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
|
||||
if (nodeType) {
|
||||
const nodeParameters = getNodeParameters(nodeType.description.properties, node.parameters, true, false, node, nodeType.description);
|
||||
if (nodeParameters) {
|
||||
const keys = [
|
||||
'operation',
|
||||
'resource',
|
||||
'mode',
|
||||
];
|
||||
keys.forEach((key) => {
|
||||
if (nodeParameters.hasOwnProperty(key)) {
|
||||
nodeItem[key] = nodeParameters[key]?.toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
if (!(e instanceof Error &&
|
||||
typeof e.message === 'string' &&
|
||||
e.message.includes('Unrecognized node type'))) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options?.isCloudDeployment === true) {
|
||||
if (node.type === OPENAI_LANGCHAIN_NODE_TYPE) {
|
||||
nodeItem.prompts =
|
||||
(node.parameters?.messages ?? {}).values ?? [];
|
||||
}
|
||||
if (node.type === AGENT_LANGCHAIN_NODE_TYPE || node.type === AGENT_TOOL_LANGCHAIN_NODE_TYPE) {
|
||||
const prompts = {};
|
||||
if (node.parameters?.text) {
|
||||
prompts.text = node.parameters.text;
|
||||
}
|
||||
const nodeOptions = node.parameters?.options;
|
||||
if (nodeOptions) {
|
||||
const optionalMessagesKeys = [
|
||||
'humanMessage',
|
||||
'systemMessage',
|
||||
'humanMessageTemplate',
|
||||
'prefix',
|
||||
'suffixChat',
|
||||
'suffix',
|
||||
'prefixPrompt',
|
||||
'suffixPrompt',
|
||||
];
|
||||
for (const key of optionalMessagesKeys) {
|
||||
if (nodeOptions[key]) {
|
||||
prompts[key] = nodeOptions[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Object.keys(prompts).length) {
|
||||
nodeItem.prompts = prompts;
|
||||
}
|
||||
}
|
||||
if (node.type === CHAIN_SUMMARIZATION_LANGCHAIN_NODE_TYPE) {
|
||||
nodeItem.prompts = ((node.parameters?.options ?? {})
|
||||
.summarizationMethodAndPrompts ?? {}).values;
|
||||
}
|
||||
if (LANGCHAIN_CUSTOM_TOOLS.includes(node.type)) {
|
||||
nodeItem.prompts = {
|
||||
description: node.parameters?.description ?? '',
|
||||
};
|
||||
}
|
||||
if (node.type === CHAIN_LLM_LANGCHAIN_NODE_TYPE) {
|
||||
nodeItem.prompts =
|
||||
(node.parameters?.messages ?? {}).messageValues ?? [];
|
||||
}
|
||||
if (node.type === MERGE_NODE_TYPE && node.parameters?.operation === 'combineBySql') {
|
||||
nodeItem.sql = node.parameters?.query;
|
||||
}
|
||||
}
|
||||
nodeGraph.nodes[index.toString()] = nodeItem;
|
||||
nameIndices[node.name] = index.toString();
|
||||
});
|
||||
const getGraphConnectionItem = (startNode, connectionItem) => {
|
||||
return { start: nameIndices[startNode], end: nameIndices[connectionItem.node] };
|
||||
};
|
||||
Object.keys(workflow.connections ?? []).forEach((nodeName) => {
|
||||
const connections = workflow.connections?.[nodeName];
|
||||
if (!connections) {
|
||||
return;
|
||||
}
|
||||
Object.keys(connections).forEach((key) => {
|
||||
connections[key].forEach((element) => {
|
||||
(element ?? []).forEach((element2) => {
|
||||
nodeGraph.node_connections.push(getGraphConnectionItem(nodeName, element2));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return { nodeGraph, nameIndices, webhookNodeNames, evaluationTriggerNodeNames };
|
||||
}
|
||||
export function extractLastExecutedNodeCredentialData(runData) {
|
||||
const nodeCredentials = runData?.data?.executionData?.nodeExecutionStack?.[0]?.node?.credentials;
|
||||
if (!nodeCredentials)
|
||||
return null;
|
||||
const credentialType = Object.keys(nodeCredentials)[0] ?? null;
|
||||
if (!credentialType)
|
||||
return null;
|
||||
const { id } = nodeCredentials[credentialType];
|
||||
if (!id)
|
||||
return null;
|
||||
return { credentialId: id, credentialType };
|
||||
}
|
||||
export const userInInstanceRanOutOfFreeAiCredits = (runData) => {
|
||||
const credentials = extractLastExecutedNodeCredentialData(runData);
|
||||
if (!credentials)
|
||||
return false;
|
||||
if (credentials.credentialType !== OPEN_AI_API_CREDENTIAL_TYPE)
|
||||
return false;
|
||||
const { error } = runData.data.resultData;
|
||||
if (!isNodeApiError(error) || !error.messages[0])
|
||||
return false;
|
||||
const rawErrorResponse = error.messages[0].replace(`${error.httpCode} -`, '');
|
||||
try {
|
||||
const errorResponse = jsonParse(rawErrorResponse);
|
||||
if (errorResponse?.error?.type === FREE_AI_CREDITS_ERROR_TYPE &&
|
||||
errorResponse.error.code === FREE_AI_CREDITS_USED_ALL_CREDITS_ERROR_CODE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
export function resolveAIMetrics(nodes, nodeTypes) {
|
||||
const resolvedNodes = nodes
|
||||
.map((x) => [x, nodeTypes.getByNameAndVersion(x.type, x.typeVersion)])
|
||||
.filter((x) => !!x[1]?.description);
|
||||
const aiNodeCount = resolvedNodes.reduce((acc, x) => acc + Number(x[1].description.codex?.categories?.includes('AI')), 0);
|
||||
if (aiNodeCount === 0)
|
||||
return {};
|
||||
let fromAIOverrideCount = 0;
|
||||
let fromAIExpressionCount = 0;
|
||||
const tools = resolvedNodes.filter((node) => node[1].description.codex?.subcategories?.AI?.includes('Tools'));
|
||||
for (const [node, _] of tools) {
|
||||
// FlatMap to support values in resourceLocators
|
||||
const values = Object.values(node.parameters).flatMap((param) => {
|
||||
if (param && typeof param === 'object' && 'value' in param)
|
||||
param = param.value;
|
||||
return typeof param === 'string' ? param : [];
|
||||
});
|
||||
// Note that we don't match the i in `fromAI` to support lower case i (though we miss fromai)
|
||||
const overrides = values.reduce((acc, value) => acc + Number(value.startsWith(`={{ ${FROM_AI_AUTO_GENERATED_MARKER} $fromA`)), 0);
|
||||
fromAIOverrideCount += overrides;
|
||||
// check for = to avoid scanning lengthy text fields
|
||||
// this will re-count overrides
|
||||
fromAIExpressionCount +=
|
||||
values.reduce((acc, value) => acc + Number(value[0] === '=' && value.includes('$fromA', 2)), 0) - overrides;
|
||||
}
|
||||
return {
|
||||
aiNodeCount,
|
||||
aiToolCount: tools.length,
|
||||
fromAIOverrideCount,
|
||||
fromAIExpressionCount,
|
||||
};
|
||||
}
|
||||
export function resolveVectorStoreMetrics(nodes, nodeTypes, run) {
|
||||
const resolvedNodes = nodes
|
||||
.map((x) => [x, nodeTypes.getByNameAndVersion(x.type, x.typeVersion)])
|
||||
.filter((x) => !!x[1]?.description);
|
||||
const vectorStores = resolvedNodes.filter((x) => x[1].description.codex?.categories?.includes('AI') &&
|
||||
x[1].description.codex?.subcategories?.AI?.includes('Vector Stores'));
|
||||
if (vectorStores.length === 0)
|
||||
return {};
|
||||
const runData = run?.data?.resultData?.runData;
|
||||
const succeededVectorStores = vectorStores.filter((x) => runData?.[x[0].name]?.some((execution) => execution.executionStatus === 'success'));
|
||||
const insertingVectorStores = succeededVectorStores.filter((x) => x[0].parameters?.mode === 'insert');
|
||||
const retrievingVectorStores = succeededVectorStores.filter((x) => ['retrieve-as-tool', 'retrieve', 'load'].find((y) => y === x[0].parameters?.mode));
|
||||
return {
|
||||
insertedIntoVectorStore: insertingVectorStores.length > 0,
|
||||
queriedDataFromVectorStore: retrievingVectorStores.length > 0,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Extract additional debug information if the last executed node was an agent node
|
||||
*/
|
||||
export function extractLastExecutedNodeStructuredOutputErrorInfo(workflow, nodeTypes, runData) {
|
||||
const info = {};
|
||||
if (runData?.data.resultData.error && runData.data.resultData.lastNodeExecuted) {
|
||||
const lastNode = getNodeTypeForName(workflow, runData.data.resultData.lastNodeExecuted);
|
||||
if (lastNode !== undefined) {
|
||||
if (lastNode.type === AGENT_LANGCHAIN_NODE_TYPE && lastNode.parameters.hasOutputParser) {
|
||||
// Add additional debug info for agent node structured output errors
|
||||
const agentOutputError = runData.data.resultData.runData[lastNode.name]?.[0]?.error;
|
||||
if (agentOutputError &&
|
||||
agentOutputError.message === "Model output doesn't fit required format") {
|
||||
info.output_parser_fail_reason = agentOutputError.context
|
||||
?.outputParserFailReason;
|
||||
}
|
||||
if (workflow.connections) {
|
||||
// Count connected tools
|
||||
info.num_tools =
|
||||
Object.keys(workflow.connections).filter((node) => workflow.connections[node]?.[NodeConnectionTypes.AiTool]?.[0]?.some((connectedNode) => connectedNode.node === lastNode.name))?.length ?? 0;
|
||||
// Extract model name from the language model node if connected
|
||||
const languageModelNodeName = Object.keys(workflow.connections).find((node) => workflow.connections[node]?.[NodeConnectionTypes.AiLanguageModel]?.[0]?.some((connectedNode) => connectedNode.node === lastNode.name));
|
||||
if (languageModelNodeName) {
|
||||
const languageModelNode = getNodeTypeForName(workflow, languageModelNodeName);
|
||||
if (languageModelNode) {
|
||||
const nodeType = nodeTypes.getByNameAndVersion(languageModelNode.type, languageModelNode.typeVersion);
|
||||
if (nodeType) {
|
||||
const nodeParameters = getNodeParameters(nodeType.description.properties, languageModelNode.parameters, true, false, languageModelNode, nodeType.description);
|
||||
const modelNameKeys = ['model', 'modelName'];
|
||||
for (const key of modelNameKeys) {
|
||||
if (nodeParameters?.[key]) {
|
||||
info.model_name =
|
||||
typeof nodeParameters[key] === 'string'
|
||||
? nodeParameters[key]
|
||||
: nodeParameters[key].value;
|
||||
if (info.model_name) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
//# sourceMappingURL=telemetry-helpers.js.map
|
||||
Reference in New Issue
Block a user