mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-14 08:34:07 -08:00
Merge remote-tracking branch 'origin/master' into PAY-1681-move-curlconverter-to-frontend
This commit is contained in:
commit
63d9c84bc3
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
|
@ -7,6 +7,7 @@
|
||||||
"EditorConfig.EditorConfig",
|
"EditorConfig.EditorConfig",
|
||||||
"esbenp.prettier-vscode",
|
"esbenp.prettier-vscode",
|
||||||
"mjmlio.vscode-mjml",
|
"mjmlio.vscode-mjml",
|
||||||
"Vue.volar"
|
"Vue.volar",
|
||||||
|
"vitest.explorer"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ If you already have VS Code and Docker installed, you can click [here](https://v
|
||||||
|
|
||||||
#### Node.js
|
#### Node.js
|
||||||
|
|
||||||
[Node.js](https://nodejs.org/en/) version 18.10 or newer is required for development purposes.
|
[Node.js](https://nodejs.org/en/) version 20.15 or newer is required for development purposes.
|
||||||
|
|
||||||
#### pnpm
|
#### pnpm
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { WEBHOOK_NODE_NAME } from '../constants';
|
||||||
|
import { NDV, WorkflowPage } from '../pages';
|
||||||
|
|
||||||
|
const workflowPage = new WorkflowPage();
|
||||||
|
const ndv = new NDV();
|
||||||
|
|
||||||
|
describe('ADO-2270 Save button resets on webhook node open', () => {
|
||||||
|
it('should not reset the save button if webhook node is opened and closed', () => {
|
||||||
|
workflowPage.actions.visit();
|
||||||
|
workflowPage.actions.addInitialNodeToCanvas(WEBHOOK_NODE_NAME);
|
||||||
|
workflowPage.getters.saveButton().click();
|
||||||
|
workflowPage.actions.openNode(WEBHOOK_NODE_NAME);
|
||||||
|
|
||||||
|
ndv.actions.close();
|
||||||
|
|
||||||
|
cy.ifCanvasVersion(
|
||||||
|
() => cy.getByTestId('workflow-save-button').should('not.contain', 'Saved'),
|
||||||
|
() => cy.getByTestId('workflow-save-button').should('contain', 'Saved'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
|
@ -10,8 +10,10 @@
|
||||||
"N8N_RUNNERS_GRANT_TOKEN",
|
"N8N_RUNNERS_GRANT_TOKEN",
|
||||||
"N8N_RUNNERS_N8N_URI",
|
"N8N_RUNNERS_N8N_URI",
|
||||||
"N8N_RUNNERS_MAX_PAYLOAD",
|
"N8N_RUNNERS_MAX_PAYLOAD",
|
||||||
|
"N8N_RUNNERS_MAX_CONCURRENCY",
|
||||||
"NODE_FUNCTION_ALLOW_BUILTIN",
|
"NODE_FUNCTION_ALLOW_BUILTIN",
|
||||||
"NODE_FUNCTION_ALLOW_EXTERNAL"
|
"NODE_FUNCTION_ALLOW_EXTERNAL",
|
||||||
|
"NODE_OPTIONS"
|
||||||
],
|
],
|
||||||
"uid": 2000,
|
"uid": 2000,
|
||||||
"gid": 2000
|
"gid": 2000
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
"dist/**/*"
|
"dist/**/*"
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@n8n/config": "workspace:*",
|
||||||
"n8n-workflow": "workspace:*"
|
"n8n-workflow": "workspace:*"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import type { FrontendBetaFeatures } from '@n8n/config';
|
||||||
import type { ExpressionEvaluatorType, LogLevel, WorkflowSettings } from 'n8n-workflow';
|
import type { ExpressionEvaluatorType, LogLevel, WorkflowSettings } from 'n8n-workflow';
|
||||||
|
|
||||||
export interface IVersionNotificationSettings {
|
export interface IVersionNotificationSettings {
|
||||||
|
@ -169,4 +170,5 @@ export interface FrontendSettings {
|
||||||
security: {
|
security: {
|
||||||
blockFileAccessToN8nFiles: boolean;
|
blockFileAccessToN8nFiles: boolean;
|
||||||
};
|
};
|
||||||
|
betaFeatures: FrontendBetaFeatures[];
|
||||||
}
|
}
|
||||||
|
|
11
packages/@n8n/config/src/configs/frontend.config.ts
Normal file
11
packages/@n8n/config/src/configs/frontend.config.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { Config, Env } from '../decorators';
|
||||||
|
import { StringArray } from '../utils';
|
||||||
|
|
||||||
|
export type FrontendBetaFeatures = 'canvas_v2';
|
||||||
|
|
||||||
|
@Config
|
||||||
|
export class FrontendConfig {
|
||||||
|
/** Which UI experiments to enable. Separate multiple values with a comma `,` */
|
||||||
|
@Env('N8N_UI_BETA_FEATURES')
|
||||||
|
betaFeatures: StringArray<FrontendBetaFeatures> = [];
|
||||||
|
}
|
28
packages/@n8n/config/src/configs/license.config.ts
Normal file
28
packages/@n8n/config/src/configs/license.config.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { Config, Env } from '../decorators';
|
||||||
|
|
||||||
|
@Config
|
||||||
|
export class LicenseConfig {
|
||||||
|
/** License server URL to retrieve license. */
|
||||||
|
@Env('N8N_LICENSE_SERVER_URL')
|
||||||
|
serverUrl: string = 'https://license.n8n.io/v1';
|
||||||
|
|
||||||
|
/** Whether autorenewal for licenses is enabled. */
|
||||||
|
@Env('N8N_LICENSE_AUTO_RENEW_ENABLED')
|
||||||
|
autoRenewalEnabled: boolean = true;
|
||||||
|
|
||||||
|
/** How long (in seconds) before expiry a license should be autorenewed. */
|
||||||
|
@Env('N8N_LICENSE_AUTO_RENEW_OFFSET')
|
||||||
|
autoRenewOffset: number = 60 * 60 * 72; // 72 hours
|
||||||
|
|
||||||
|
/** Activation key to initialize license. */
|
||||||
|
@Env('N8N_LICENSE_ACTIVATION_KEY')
|
||||||
|
activationKey: string = '';
|
||||||
|
|
||||||
|
/** Tenant ID used by the license manager SDK, e.g. for self-hosted, sandbox, embed, cloud. */
|
||||||
|
@Env('N8N_LICENSE_TENANT_ID')
|
||||||
|
tenantId: number = 1;
|
||||||
|
|
||||||
|
/** Ephemeral license certificate. See: https://github.com/n8n-io/license-management?tab=readme-ov-file#concept-ephemeral-entitlements */
|
||||||
|
@Env('N8N_LICENSE_CERT')
|
||||||
|
cert: string = '';
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ export const LOG_SCOPES = [
|
||||||
'redis',
|
'redis',
|
||||||
'scaling',
|
'scaling',
|
||||||
'waiting-executions',
|
'waiting-executions',
|
||||||
|
'task-runner',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type LogScope = (typeof LOG_SCOPES)[number];
|
export type LogScope = (typeof LOG_SCOPES)[number];
|
||||||
|
|
|
@ -42,4 +42,12 @@ export class TaskRunnersConfig {
|
||||||
/** Which task runner to launch from the config */
|
/** Which task runner to launch from the config */
|
||||||
@Env('N8N_RUNNERS_LAUNCHER_RUNNER')
|
@Env('N8N_RUNNERS_LAUNCHER_RUNNER')
|
||||||
launcherRunner: string = 'javascript';
|
launcherRunner: string = 'javascript';
|
||||||
|
|
||||||
|
/** The --max-old-space-size option to use for the runner (in MB). Default means node.js will determine it based on the available memory. */
|
||||||
|
@Env('N8N_RUNNERS_MAX_OLD_SPACE_SIZE')
|
||||||
|
maxOldSpaceSize: string = '';
|
||||||
|
|
||||||
|
/** How many concurrent tasks can a runner execute at a time */
|
||||||
|
@Env('N8N_RUNNERS_MAX_CONCURRENCY')
|
||||||
|
maxConcurrency: number = 5;
|
||||||
}
|
}
|
||||||
|
|
27
packages/@n8n/config/src/configs/security.config.ts
Normal file
27
packages/@n8n/config/src/configs/security.config.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { Config, Env } from '../decorators';
|
||||||
|
|
||||||
|
@Config
|
||||||
|
export class SecurityConfig {
|
||||||
|
/**
|
||||||
|
* Which directories to limit n8n's access to. Separate multiple dirs with semicolon `;`.
|
||||||
|
*
|
||||||
|
* @example N8N_RESTRICT_FILE_ACCESS_TO=/home/user/.n8n;/home/user/n8n-data
|
||||||
|
*/
|
||||||
|
@Env('N8N_RESTRICT_FILE_ACCESS_TO')
|
||||||
|
restrictFileAccessTo: string = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to block access to all files at:
|
||||||
|
* - the ".n8n" directory,
|
||||||
|
* - the static cache dir at ~/.cache/n8n/public, and
|
||||||
|
* - user-defined config files.
|
||||||
|
*/
|
||||||
|
@Env('N8N_BLOCK_FILE_ACCESS_TO_N8N_FILES')
|
||||||
|
blockFileAccessToN8nFiles: boolean = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In a [security audit](https://docs.n8n.io/hosting/securing/security-audit/), how many days for a workflow to be considered abandoned if not executed.
|
||||||
|
*/
|
||||||
|
@Env('N8N_SECURITY_AUDIT_DAYS_ABANDONED_WORKFLOW')
|
||||||
|
daysAbandonedWorkflow: number = 90;
|
||||||
|
}
|
|
@ -6,21 +6,25 @@ import { EventBusConfig } from './configs/event-bus.config';
|
||||||
import { ExternalSecretsConfig } from './configs/external-secrets.config';
|
import { ExternalSecretsConfig } from './configs/external-secrets.config';
|
||||||
import { ExternalStorageConfig } from './configs/external-storage.config';
|
import { ExternalStorageConfig } from './configs/external-storage.config';
|
||||||
import { GenericConfig } from './configs/generic.config';
|
import { GenericConfig } from './configs/generic.config';
|
||||||
|
import { LicenseConfig } from './configs/license.config';
|
||||||
import { LoggingConfig } from './configs/logging.config';
|
import { LoggingConfig } from './configs/logging.config';
|
||||||
import { MultiMainSetupConfig } from './configs/multi-main-setup.config';
|
import { MultiMainSetupConfig } from './configs/multi-main-setup.config';
|
||||||
import { NodesConfig } from './configs/nodes.config';
|
import { NodesConfig } from './configs/nodes.config';
|
||||||
import { PublicApiConfig } from './configs/public-api.config';
|
import { PublicApiConfig } from './configs/public-api.config';
|
||||||
import { TaskRunnersConfig } from './configs/runners.config';
|
import { TaskRunnersConfig } from './configs/runners.config';
|
||||||
export { TaskRunnersConfig } from './configs/runners.config';
|
|
||||||
import { ScalingModeConfig } from './configs/scaling-mode.config';
|
import { ScalingModeConfig } from './configs/scaling-mode.config';
|
||||||
|
import { SecurityConfig } from './configs/security.config';
|
||||||
import { SentryConfig } from './configs/sentry.config';
|
import { SentryConfig } from './configs/sentry.config';
|
||||||
import { TemplatesConfig } from './configs/templates.config';
|
import { TemplatesConfig } from './configs/templates.config';
|
||||||
import { UserManagementConfig } from './configs/user-management.config';
|
import { UserManagementConfig } from './configs/user-management.config';
|
||||||
import { VersionNotificationsConfig } from './configs/version-notifications.config';
|
import { VersionNotificationsConfig } from './configs/version-notifications.config';
|
||||||
import { WorkflowsConfig } from './configs/workflows.config';
|
import { WorkflowsConfig } from './configs/workflows.config';
|
||||||
import { Config, Env, Nested } from './decorators';
|
import { Config, Env, Nested } from './decorators';
|
||||||
export { Config, Env, Nested } from './decorators';
|
|
||||||
|
|
||||||
|
export { Config, Env, Nested } from './decorators';
|
||||||
|
export { TaskRunnersConfig } from './configs/runners.config';
|
||||||
|
export { SecurityConfig } from './configs/security.config';
|
||||||
|
export { FrontendBetaFeatures, FrontendConfig } from './configs/frontend.config';
|
||||||
export { LOG_SCOPES } from './configs/logging.config';
|
export { LOG_SCOPES } from './configs/logging.config';
|
||||||
export type { LogScope } from './configs/logging.config';
|
export type { LogScope } from './configs/logging.config';
|
||||||
|
|
||||||
|
@ -102,4 +106,10 @@ export class GlobalConfig {
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
generic: GenericConfig;
|
generic: GenericConfig;
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
license: LicenseConfig;
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
security: SecurityConfig;
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,6 +231,8 @@ describe('GlobalConfig', () => {
|
||||||
port: 5679,
|
port: 5679,
|
||||||
launcherPath: '',
|
launcherPath: '',
|
||||||
launcherRunner: 'javascript',
|
launcherRunner: 'javascript',
|
||||||
|
maxOldSpaceSize: '',
|
||||||
|
maxConcurrency: 5,
|
||||||
},
|
},
|
||||||
sentry: {
|
sentry: {
|
||||||
backendDsn: '',
|
backendDsn: '',
|
||||||
|
@ -256,6 +258,19 @@ describe('GlobalConfig', () => {
|
||||||
releaseChannel: 'dev',
|
releaseChannel: 'dev',
|
||||||
gracefulShutdownTimeout: 30,
|
gracefulShutdownTimeout: 30,
|
||||||
},
|
},
|
||||||
|
license: {
|
||||||
|
serverUrl: 'https://license.n8n.io/v1',
|
||||||
|
autoRenewalEnabled: true,
|
||||||
|
autoRenewOffset: 60 * 60 * 72,
|
||||||
|
activationKey: '',
|
||||||
|
tenantId: 1,
|
||||||
|
cert: '',
|
||||||
|
},
|
||||||
|
security: {
|
||||||
|
restrictFileAccessTo: '',
|
||||||
|
blockFileAccessToN8nFiles: true,
|
||||||
|
daysAbandonedWorkflow: 90,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should use all default values when no env variables are defined', () => {
|
it('should use all default values when no env variables are defined', () => {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import { NodeOperationError, NodeConnectionType } from 'n8n-workflow';
|
||||||
NodeOperationError,
|
import type {
|
||||||
type IExecuteFunctions,
|
IExecuteFunctions,
|
||||||
type INodeExecutionData,
|
INodeExecutionData,
|
||||||
type INodeType,
|
INodeType,
|
||||||
type INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
type INodeOutputConfiguration,
|
INodeOutputConfiguration,
|
||||||
type SupplyData,
|
SupplyData,
|
||||||
NodeConnectionType,
|
ISupplyDataFunctions,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
// TODO: Add support for execute function. Got already started but got commented out
|
// TODO: Add support for execute function. Got already started but got commented out
|
||||||
|
@ -72,7 +72,7 @@ export const vmResolver = makeResolverFromLegacyOptions({
|
||||||
});
|
});
|
||||||
|
|
||||||
function getSandbox(
|
function getSandbox(
|
||||||
this: IExecuteFunctions,
|
this: IExecuteFunctions | ISupplyDataFunctions,
|
||||||
code: string,
|
code: string,
|
||||||
options?: { addItems?: boolean; itemIndex?: number },
|
options?: { addItems?: boolean; itemIndex?: number },
|
||||||
) {
|
) {
|
||||||
|
@ -354,7 +354,7 @@ export class Code implements INodeType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const code = this.getNodeParameter('code', itemIndex) as { supplyData?: { code: string } };
|
const code = this.getNodeParameter('code', itemIndex) as { supplyData?: { code: string } };
|
||||||
|
|
||||||
if (!code.supplyData?.code) {
|
if (!code.supplyData?.code) {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -177,7 +177,7 @@ export class DocumentBinaryInputLoader implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions): Promise<SupplyData> {
|
||||||
this.logger.debug('Supply Data for Binary Input Loader');
|
this.logger.debug('Supply Data for Binary Input Loader');
|
||||||
const textSplitter = (await this.getInputConnectionData(
|
const textSplitter = (await this.getInputConnectionData(
|
||||||
NodeConnectionType.AiTextSplitter,
|
NodeConnectionType.AiTextSplitter,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -283,7 +283,7 @@ export class DocumentDefaultDataLoader implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const dataType = this.getNodeParameter('dataType', itemIndex, 'json') as 'json' | 'binary';
|
const dataType = this.getNodeParameter('dataType', itemIndex, 'json') as 'json' | 'binary';
|
||||||
const textSplitter = (await this.getInputConnectionData(
|
const textSplitter = (await this.getInputConnectionData(
|
||||||
NodeConnectionType.AiTextSplitter,
|
NodeConnectionType.AiTextSplitter,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { GithubRepoLoader } from '@langchain/community/document_loaders/web/github';
|
import { GithubRepoLoader } from '@langchain/community/document_loaders/web/github';
|
||||||
|
@ -93,7 +93,7 @@ export class DocumentGithubLoader implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
console.log('Supplying data for Github Document Loader');
|
console.log('Supplying data for Github Document Loader');
|
||||||
|
|
||||||
const repository = this.getNodeParameter('repository', itemIndex) as string;
|
const repository = this.getNodeParameter('repository', itemIndex) as string;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ export class DocumentJsonInputLoader implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions): Promise<SupplyData> {
|
||||||
this.logger.debug('Supply Data for JSON Input Loader');
|
this.logger.debug('Supply Data for JSON Input Loader');
|
||||||
const textSplitter = (await this.getInputConnectionData(
|
const textSplitter = (await this.getInputConnectionData(
|
||||||
NodeConnectionType.AiTextSplitter,
|
NodeConnectionType.AiTextSplitter,
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
import { BedrockEmbeddings } from '@langchain/aws';
|
import { BedrockEmbeddings } from '@langchain/aws';
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ export class EmbeddingsAwsBedrock implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('aws');
|
const credentials = await this.getCredentials('aws');
|
||||||
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ export class EmbeddingsAzureOpenAi implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
this.logger.debug('Supply data for embeddings');
|
this.logger.debug('Supply data for embeddings');
|
||||||
const credentials = await this.getCredentials<{
|
const credentials = await this.getCredentials<{
|
||||||
apiKey: string;
|
apiKey: string;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { CohereEmbeddings } from '@langchain/cohere';
|
import { CohereEmbeddings } from '@langchain/cohere';
|
||||||
|
@ -99,7 +99,7 @@ export class EmbeddingsCohere implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
this.logger.debug('Supply data for embeddings Cohere');
|
this.logger.debug('Supply data for embeddings Cohere');
|
||||||
const modelName = this.getNodeParameter('modelName', itemIndex, 'embed-english-v2.0') as string;
|
const modelName = this.getNodeParameter('modelName', itemIndex, 'embed-english-v2.0') as string;
|
||||||
const credentials = await this.getCredentials<{ apiKey: string }>('cohereApi');
|
const credentials = await this.getCredentials<{ apiKey: string }>('cohereApi');
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { GoogleGenerativeAIEmbeddings } from '@langchain/google-genai';
|
import { GoogleGenerativeAIEmbeddings } from '@langchain/google-genai';
|
||||||
|
@ -116,7 +116,7 @@ export class EmbeddingsGoogleGemini implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
this.logger.debug('Supply data for embeddings Google Gemini');
|
this.logger.debug('Supply data for embeddings Google Gemini');
|
||||||
const modelName = this.getNodeParameter(
|
const modelName = this.getNodeParameter(
|
||||||
'modelName',
|
'modelName',
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { HuggingFaceInferenceEmbeddings } from '@langchain/community/embeddings/hf';
|
import { HuggingFaceInferenceEmbeddings } from '@langchain/community/embeddings/hf';
|
||||||
|
@ -81,7 +81,7 @@ export class EmbeddingsHuggingFaceInference implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
this.logger.debug('Supply data for embeddings HF Inference');
|
this.logger.debug('Supply data for embeddings HF Inference');
|
||||||
const model = this.getNodeParameter(
|
const model = this.getNodeParameter(
|
||||||
'modelName',
|
'modelName',
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import type { MistralAIEmbeddingsParams } from '@langchain/mistralai';
|
import type { MistralAIEmbeddingsParams } from '@langchain/mistralai';
|
||||||
|
@ -134,7 +134,7 @@ export class EmbeddingsMistralCloud implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('mistralCloudApi');
|
const credentials = await this.getCredentials('mistralCloudApi');
|
||||||
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
||||||
const options = this.getNodeParameter(
|
const options = this.getNodeParameter(
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { OllamaEmbeddings } from '@langchain/ollama';
|
import { OllamaEmbeddings } from '@langchain/ollama';
|
||||||
|
@ -44,7 +44,7 @@ export class EmbeddingsOllama implements INodeType {
|
||||||
properties: [getConnectionHintNoticeField([NodeConnectionType.AiVectorStore]), ollamaModel],
|
properties: [getConnectionHintNoticeField([NodeConnectionType.AiVectorStore]), ollamaModel],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
this.logger.debug('Supply data for embeddings Ollama');
|
this.logger.debug('Supply data for embeddings Ollama');
|
||||||
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
||||||
const credentials = await this.getCredentials('ollamaApi');
|
const credentials = await this.getCredentials('ollamaApi');
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type INodeProperties,
|
type INodeProperties,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ export class EmbeddingsOpenAi implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
this.logger.debug('Supply data for embeddings');
|
this.logger.debug('Supply data for embeddings');
|
||||||
const credentials = await this.getCredentials('openAiApi');
|
const credentials = await this.getCredentials('openAiApi');
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type INodePropertyOptions,
|
type INodePropertyOptions,
|
||||||
type INodeProperties,
|
type INodeProperties,
|
||||||
type IExecuteFunctions,
|
type ISupplyDataFunctions,
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
|
@ -20,6 +20,10 @@ const modelField: INodeProperties = {
|
||||||
type: 'options',
|
type: 'options',
|
||||||
// eslint-disable-next-line n8n-nodes-base/node-param-options-type-unsorted-items
|
// eslint-disable-next-line n8n-nodes-base/node-param-options-type-unsorted-items
|
||||||
options: [
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Claude 3.5 Sonnet(20241022)',
|
||||||
|
value: 'claude-3-5-sonnet-20241022',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Claude 3 Opus(20240229)',
|
name: 'Claude 3 Opus(20240229)',
|
||||||
value: 'claude-3-opus-20240229',
|
value: 'claude-3-opus-20240229',
|
||||||
|
@ -175,7 +179,7 @@ export class LmChatAnthropic implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('anthropicApi');
|
const credentials = await this.getCredentials('anthropicApi');
|
||||||
|
|
||||||
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ export class LmChatOllama implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('ollamaApi');
|
const credentials = await this.getCredentials('ollamaApi');
|
||||||
|
|
||||||
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
type JsonObject,
|
type JsonObject,
|
||||||
NodeApiError,
|
NodeApiError,
|
||||||
|
@ -242,7 +242,7 @@ export class LmChatOpenAi implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('openAiApi');
|
const credentials = await this.getCredentials('openAiApi');
|
||||||
|
|
||||||
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ export class LmCohere implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('cohereApi');
|
const credentials = await this.getCredentials('cohereApi');
|
||||||
|
|
||||||
const options = this.getNodeParameter('options', itemIndex, {}) as object;
|
const options = this.getNodeParameter('options', itemIndex, {}) as object;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ export class LmOllama implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('ollamaApi');
|
const credentials = await this.getCredentials('ollamaApi');
|
||||||
|
|
||||||
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import { NodeConnectionType } from 'n8n-workflow';
|
import { NodeConnectionType } from 'n8n-workflow';
|
||||||
import type {
|
import type {
|
||||||
IExecuteFunctions,
|
|
||||||
INodeType,
|
INodeType,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
|
ISupplyDataFunctions,
|
||||||
SupplyData,
|
SupplyData,
|
||||||
ILoadOptionsFunctions,
|
ILoadOptionsFunctions,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
@ -229,7 +229,7 @@ export class LmOpenAi implements INodeType {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('openAiApi');
|
const credentials = await this.getCredentials('openAiApi');
|
||||||
|
|
||||||
const modelName = this.getNodeParameter('model', itemIndex, '', {
|
const modelName = this.getNodeParameter('model', itemIndex, '', {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ export class LmOpenHuggingFaceInference implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('huggingFaceApi');
|
const credentials = await this.getCredentials('huggingFaceApi');
|
||||||
|
|
||||||
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
import { ChatBedrockConverse } from '@langchain/aws';
|
import { ChatBedrockConverse } from '@langchain/aws';
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ export class LmChatAwsBedrock implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('aws');
|
const credentials = await this.getCredentials('aws');
|
||||||
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
||||||
const options = this.getNodeParameter('options', itemIndex, {}) as {
|
const options = this.getNodeParameter('options', itemIndex, {}) as {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ export class LmChatAzureOpenAi implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials<{
|
const credentials = await this.getCredentials<{
|
||||||
apiKey: string;
|
apiKey: string;
|
||||||
resourceName: string;
|
resourceName: string;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
|
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
|
||||||
|
@ -113,7 +113,7 @@ export class LmChatGoogleGemini implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('googlePalmApi');
|
const credentials = await this.getCredentials('googlePalmApi');
|
||||||
|
|
||||||
const modelName = this.getNodeParameter('modelName', itemIndex) as string;
|
const modelName = this.getNodeParameter('modelName', itemIndex) as string;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
type ILoadOptionsFunctions,
|
type ILoadOptionsFunctions,
|
||||||
type JsonObject,
|
type JsonObject,
|
||||||
|
@ -124,7 +124,7 @@ export class LmChatGoogleVertex implements INodeType {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('googleApi');
|
const credentials = await this.getCredentials('googleApi');
|
||||||
const privateKey = formatPrivateKey(credentials.privateKey as string);
|
const privateKey = formatPrivateKey(credentials.privateKey as string);
|
||||||
const email = (credentials.email as string).trim();
|
const email = (credentials.email as string).trim();
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ export class LmChatGroq implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('groqApi');
|
const credentials = await this.getCredentials('groqApi');
|
||||||
|
|
||||||
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -172,7 +172,7 @@ export class LmChatMistralCloud implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('mistralCloudApi');
|
const credentials = await this.getCredentials('mistralCloudApi');
|
||||||
|
|
||||||
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
||||||
|
|
|
@ -7,7 +7,7 @@ import type {
|
||||||
SerializedSecret,
|
SerializedSecret,
|
||||||
} from '@langchain/core/load/serializable';
|
} from '@langchain/core/load/serializable';
|
||||||
import type { LLMResult } from '@langchain/core/outputs';
|
import type { LLMResult } from '@langchain/core/outputs';
|
||||||
import type { IDataObject, IExecuteFunctions } from 'n8n-workflow';
|
import type { IDataObject, ISupplyDataFunctions } from 'n8n-workflow';
|
||||||
import { NodeConnectionType } from 'n8n-workflow';
|
import { NodeConnectionType } from 'n8n-workflow';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import type { BaseMessage } from '@langchain/core/messages';
|
import type { BaseMessage } from '@langchain/core/messages';
|
||||||
|
@ -30,8 +30,6 @@ const TIKTOKEN_ESTIMATE_MODEL = 'gpt-4o';
|
||||||
export class N8nLlmTracing extends BaseCallbackHandler {
|
export class N8nLlmTracing extends BaseCallbackHandler {
|
||||||
name = 'N8nLlmTracing';
|
name = 'N8nLlmTracing';
|
||||||
|
|
||||||
executionFunctions: IExecuteFunctions;
|
|
||||||
|
|
||||||
connectionType = NodeConnectionType.AiLanguageModel;
|
connectionType = NodeConnectionType.AiLanguageModel;
|
||||||
|
|
||||||
promptTokensEstimate = 0;
|
promptTokensEstimate = 0;
|
||||||
|
@ -61,11 +59,10 @@ export class N8nLlmTracing extends BaseCallbackHandler {
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
executionFunctions: IExecuteFunctions,
|
private executionFunctions: ISupplyDataFunctions,
|
||||||
options?: { tokensUsageParser: TokensUsageParser },
|
options?: { tokensUsageParser: TokensUsageParser },
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.executionFunctions = executionFunctions;
|
|
||||||
this.options = { ...this.options, ...options };
|
this.options = { ...this.options, ...options };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +135,7 @@ export class N8nLlmTracing extends BaseCallbackHandler {
|
||||||
this.executionFunctions.addOutputData(this.connectionType, runDetails.index, [
|
this.executionFunctions.addOutputData(this.connectionType, runDetails.index, [
|
||||||
[{ json: { ...response } }],
|
[{ json: { ...response } }],
|
||||||
]);
|
]);
|
||||||
void logAiEvent(this.executionFunctions, 'ai-llm-generated-output', {
|
logAiEvent(this.executionFunctions, 'ai-llm-generated-output', {
|
||||||
messages: parsedMessages,
|
messages: parsedMessages,
|
||||||
options: runDetails.options,
|
options: runDetails.options,
|
||||||
response,
|
response,
|
||||||
|
@ -186,7 +183,7 @@ export class N8nLlmTracing extends BaseCallbackHandler {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void logAiEvent(this.executionFunctions, 'ai-llm-errored', {
|
logAiEvent(this.executionFunctions, 'ai-llm-errored', {
|
||||||
error: Object.keys(error).length === 0 ? error.toString() : error,
|
error: Object.keys(error).length === 0 ? error.toString() : error,
|
||||||
runId,
|
runId,
|
||||||
parentRunId,
|
parentRunId,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import type { BufferWindowMemoryInput } from 'langchain/memory';
|
import type { BufferWindowMemoryInput } from 'langchain/memory';
|
||||||
|
@ -134,7 +134,7 @@ export class MemoryBufferWindow implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const contextWindowLength = this.getNodeParameter('contextWindowLength', itemIndex) as number;
|
const contextWindowLength = this.getNodeParameter('contextWindowLength', itemIndex) as number;
|
||||||
const workflowId = this.getWorkflow().id;
|
const workflowId = this.getWorkflow().id;
|
||||||
const memoryInstance = MemoryChatBufferSingleton.getInstance();
|
const memoryInstance = MemoryChatBufferSingleton.getInstance();
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ export class MemoryMotorhead implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('motorheadApi');
|
const credentials = await this.getCredentials('motorheadApi');
|
||||||
const nodeVersion = this.getNode().typeVersion;
|
const nodeVersion = this.getNode().typeVersion;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import type { IExecuteFunctions, INodeType, INodeTypeDescription, SupplyData } from 'n8n-workflow';
|
import type {
|
||||||
|
ISupplyDataFunctions,
|
||||||
|
INodeType,
|
||||||
|
INodeTypeDescription,
|
||||||
|
SupplyData,
|
||||||
|
} from 'n8n-workflow';
|
||||||
import { NodeConnectionType } from 'n8n-workflow';
|
import { NodeConnectionType } from 'n8n-workflow';
|
||||||
import { BufferMemory, BufferWindowMemory } from 'langchain/memory';
|
import { BufferMemory, BufferWindowMemory } from 'langchain/memory';
|
||||||
import { PostgresChatMessageHistory } from '@langchain/community/stores/message/postgres';
|
import { PostgresChatMessageHistory } from '@langchain/community/stores/message/postgres';
|
||||||
|
@ -73,7 +78,7 @@ export class MemoryPostgresChat implements INodeType {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials<PostgresNodeCredentials>('postgres');
|
const credentials = await this.getCredentials<PostgresNodeCredentials>('postgres');
|
||||||
const tableName = this.getNodeParameter('tableName', itemIndex, 'n8n_chat_histories') as string;
|
const tableName = this.getNodeParameter('tableName', itemIndex, 'n8n_chat_histories') as string;
|
||||||
const sessionId = getSessionId(this, itemIndex);
|
const sessionId = getSessionId(this, itemIndex);
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeOperationError,
|
NodeOperationError,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
@ -102,7 +102,7 @@ export class MemoryRedisChat implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('redis');
|
const credentials = await this.getCredentials('redis');
|
||||||
const nodeVersion = this.getNode().typeVersion;
|
const nodeVersion = this.getNode().typeVersion;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import { NodeConnectionType, NodeOperationError } from 'n8n-workflow';
|
import { NodeConnectionType, NodeOperationError } from 'n8n-workflow';
|
||||||
import type { IExecuteFunctions, INodeType, INodeTypeDescription, SupplyData } from 'n8n-workflow';
|
import type {
|
||||||
|
ISupplyDataFunctions,
|
||||||
|
INodeType,
|
||||||
|
INodeTypeDescription,
|
||||||
|
SupplyData,
|
||||||
|
} from 'n8n-workflow';
|
||||||
import { XataChatMessageHistory } from '@langchain/community/stores/message/xata';
|
import { XataChatMessageHistory } from '@langchain/community/stores/message/xata';
|
||||||
import { BufferMemory, BufferWindowMemory } from 'langchain/memory';
|
import { BufferMemory, BufferWindowMemory } from 'langchain/memory';
|
||||||
import { BaseClient } from '@xata.io/client';
|
import { BaseClient } from '@xata.io/client';
|
||||||
|
@ -88,7 +93,7 @@ export class MemoryXata implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('xataApi');
|
const credentials = await this.getCredentials('xataApi');
|
||||||
const nodeVersion = this.getNode().typeVersion;
|
const nodeVersion = this.getNode().typeVersion;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
type ISupplyDataFunctions,
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
|
@ -103,7 +103,7 @@ export class MemoryZep implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials<{
|
const credentials = await this.getCredentials<{
|
||||||
apiKey?: string;
|
apiKey?: string;
|
||||||
apiUrl?: string;
|
apiUrl?: string;
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import type { BaseLanguageModel } from '@langchain/core/language_models/base';
|
import type { BaseLanguageModel } from '@langchain/core/language_models/base';
|
||||||
import { NodeConnectionType } from 'n8n-workflow';
|
import { NodeConnectionType } from 'n8n-workflow';
|
||||||
import type { IExecuteFunctions, INodeType, INodeTypeDescription, SupplyData } from 'n8n-workflow';
|
import type {
|
||||||
|
ISupplyDataFunctions,
|
||||||
|
INodeType,
|
||||||
|
INodeTypeDescription,
|
||||||
|
SupplyData,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
N8nOutputFixingParser,
|
N8nOutputFixingParser,
|
||||||
|
@ -63,7 +68,7 @@ export class OutputParserAutofixing implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const model = (await this.getInputConnectionData(
|
const model = (await this.getInputConnectionData(
|
||||||
NodeConnectionType.AiLanguageModel,
|
NodeConnectionType.AiLanguageModel,
|
||||||
itemIndex,
|
itemIndex,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ export class OutputParserItemList implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const options = this.getNodeParameter('options', itemIndex, {}) as {
|
const options = this.getNodeParameter('options', itemIndex, {}) as {
|
||||||
numberOfItems?: number;
|
numberOfItems?: number;
|
||||||
separator?: string;
|
separator?: string;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import type { JSONSchema7 } from 'json-schema';
|
import type { JSONSchema7 } from 'json-schema';
|
||||||
import {
|
import {
|
||||||
jsonParse,
|
jsonParse,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
NodeOperationError,
|
NodeOperationError,
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
|
@ -122,7 +122,7 @@ export class OutputParserStructured implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const schemaType = this.getNodeParameter('schemaType', itemIndex, '') as 'fromJson' | 'manual';
|
const schemaType = this.getNodeParameter('schemaType', itemIndex, '') as 'fromJson' | 'manual';
|
||||||
// We initialize these even though one of them will always be empty
|
// We initialize these even though one of them will always be empty
|
||||||
// it makes it easer to navigate the ternary operator
|
// it makes it easer to navigate the ternary operator
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ export class RetrieverContextualCompression implements INodeType {
|
||||||
properties: [],
|
properties: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
this.logger.debug('Supplying data for Contextual Compression Retriever');
|
this.logger.debug('Supplying data for Contextual Compression Retriever');
|
||||||
|
|
||||||
const model = (await this.getInputConnectionData(
|
const model = (await this.getInputConnectionData(
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ export class RetrieverMultiQuery implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
this.logger.debug('Supplying data for MultiQuery Retriever');
|
this.logger.debug('Supplying data for MultiQuery Retriever');
|
||||||
|
|
||||||
const options = this.getNodeParameter('options', itemIndex, {}) as { queryCount?: number };
|
const options = this.getNodeParameter('options', itemIndex, {}) as { queryCount?: number };
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import type { VectorStore } from '@langchain/core/vectorstores';
|
import type { VectorStore } from '@langchain/core/vectorstores';
|
||||||
|
@ -56,7 +56,7 @@ export class RetrieverVectorStore implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
this.logger.debug('Supplying data for Vector Store Retriever');
|
this.logger.debug('Supplying data for Vector Store Retriever');
|
||||||
|
|
||||||
const topK = this.getNodeParameter('topK', itemIndex, 4) as number;
|
const topK = this.getNodeParameter('topK', itemIndex, 4) as number;
|
||||||
|
|
|
@ -5,7 +5,7 @@ import type {
|
||||||
IExecuteWorkflowInfo,
|
IExecuteWorkflowInfo,
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
IWorkflowBase,
|
IWorkflowBase,
|
||||||
IExecuteFunctions,
|
ISupplyDataFunctions,
|
||||||
INodeType,
|
INodeType,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
SupplyData,
|
SupplyData,
|
||||||
|
@ -292,15 +292,15 @@ export class RetrieverWorkflow implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
class WorkflowRetriever extends BaseRetriever {
|
class WorkflowRetriever extends BaseRetriever {
|
||||||
lc_namespace = ['n8n-nodes-langchain', 'retrievers', 'workflow'];
|
lc_namespace = ['n8n-nodes-langchain', 'retrievers', 'workflow'];
|
||||||
|
|
||||||
executeFunctions: IExecuteFunctions;
|
constructor(
|
||||||
|
private executeFunctions: ISupplyDataFunctions,
|
||||||
constructor(executeFunctions: IExecuteFunctions, fields: BaseRetrieverInput) {
|
fields: BaseRetrieverInput,
|
||||||
|
) {
|
||||||
super(fields);
|
super(fields);
|
||||||
this.executeFunctions = executeFunctions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async _getRelevantDocuments(
|
async _getRelevantDocuments(
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import type { CharacterTextSplitterParams } from '@langchain/textsplitters';
|
import type { CharacterTextSplitterParams } from '@langchain/textsplitters';
|
||||||
|
@ -63,7 +63,7 @@ export class TextSplitterCharacterTextSplitter implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
this.logger.debug('Supply Data for Text Splitter');
|
this.logger.debug('Supply Data for Text Splitter');
|
||||||
|
|
||||||
const separator = this.getNodeParameter('separator', itemIndex) as string;
|
const separator = this.getNodeParameter('separator', itemIndex) as string;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import type {
|
import type {
|
||||||
|
@ -94,7 +94,7 @@ export class TextSplitterRecursiveCharacterTextSplitter implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
this.logger.debug('Supply Data for Text Splitter');
|
this.logger.debug('Supply Data for Text Splitter');
|
||||||
|
|
||||||
const chunkSize = this.getNodeParameter('chunkSize', itemIndex) as number;
|
const chunkSize = this.getNodeParameter('chunkSize', itemIndex) as number;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { TokenTextSplitter } from '@langchain/textsplitters';
|
import { TokenTextSplitter } from '@langchain/textsplitters';
|
||||||
|
@ -56,7 +56,7 @@ export class TextSplitterTokenSplitter implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
this.logger.debug('Supply Data for Text Splitter');
|
this.logger.debug('Supply Data for Text Splitter');
|
||||||
|
|
||||||
const chunkSize = this.getNodeParameter('chunkSize', itemIndex) as number;
|
const chunkSize = this.getNodeParameter('chunkSize', itemIndex) as number;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { Calculator } from '@langchain/community/tools/calculator';
|
import { Calculator } from '@langchain/community/tools/calculator';
|
||||||
|
@ -43,7 +43,7 @@ export class ToolCalculator implements INodeType {
|
||||||
properties: [getConnectionHintNoticeField([NodeConnectionType.AiAgent])],
|
properties: [getConnectionHintNoticeField([NodeConnectionType.AiAgent])],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions): Promise<SupplyData> {
|
||||||
return {
|
return {
|
||||||
response: logWrapper(new Calculator(), this),
|
response: logWrapper(new Calculator(), this),
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,9 +6,9 @@ import { PythonSandbox } from 'n8n-nodes-base/dist/nodes/Code/PythonSandbox';
|
||||||
import type { Sandbox } from 'n8n-nodes-base/dist/nodes/Code/Sandbox';
|
import type { Sandbox } from 'n8n-nodes-base/dist/nodes/Code/Sandbox';
|
||||||
import { getSandboxContext } from 'n8n-nodes-base/dist/nodes/Code/Sandbox';
|
import { getSandboxContext } from 'n8n-nodes-base/dist/nodes/Code/Sandbox';
|
||||||
import type {
|
import type {
|
||||||
IExecuteFunctions,
|
|
||||||
INodeType,
|
INodeType,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
|
ISupplyDataFunctions,
|
||||||
SupplyData,
|
SupplyData,
|
||||||
ExecutionError,
|
ExecutionError,
|
||||||
IDataObject,
|
IDataObject,
|
||||||
|
@ -175,7 +175,7 @@ export class ToolCode implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const node = this.getNode();
|
const node = this.getNode();
|
||||||
const workflowMode = this.getMode();
|
const workflowMode = this.getMode();
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import type {
|
import type {
|
||||||
IExecuteFunctions,
|
|
||||||
INodeType,
|
INodeType,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
|
ISupplyDataFunctions,
|
||||||
SupplyData,
|
SupplyData,
|
||||||
IHttpRequestMethods,
|
IHttpRequestMethods,
|
||||||
IHttpRequestOptions,
|
IHttpRequestOptions,
|
||||||
|
@ -250,7 +250,7 @@ export class ToolHttpRequest implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const name = this.getNode().name.replace(/ /g, '_');
|
const name = this.getNode().name.replace(/ /g, '_');
|
||||||
try {
|
try {
|
||||||
tryToParseAlphanumericString(name);
|
tryToParseAlphanumericString(name);
|
||||||
|
|
|
@ -10,19 +10,19 @@ describe('ToolHttpRequest', () => {
|
||||||
const helpers = mock<IExecuteFunctions['helpers']>();
|
const helpers = mock<IExecuteFunctions['helpers']>();
|
||||||
const executeFunctions = mock<IExecuteFunctions>({ helpers });
|
const executeFunctions = mock<IExecuteFunctions>({ helpers });
|
||||||
|
|
||||||
describe('Binary response', () => {
|
beforeEach(() => {
|
||||||
beforeEach(() => {
|
jest.resetAllMocks();
|
||||||
jest.resetAllMocks();
|
executeFunctions.getNode.mockReturnValue(
|
||||||
executeFunctions.getNode.mockReturnValue(
|
mock<INode>({
|
||||||
mock<INode>({
|
type: 'n8n-nodes-base.httpRequest',
|
||||||
type: 'n8n-nodes-base.httpRequest',
|
name: 'HTTP Request',
|
||||||
name: 'HTTP Request',
|
typeVersion: 1.1,
|
||||||
typeVersion: 1.1,
|
}),
|
||||||
}),
|
);
|
||||||
);
|
executeFunctions.addInputData.mockReturnValue({ index: 0 });
|
||||||
executeFunctions.addInputData.mockReturnValue({ index: 0 });
|
});
|
||||||
});
|
|
||||||
|
|
||||||
|
describe('Binary response', () => {
|
||||||
it('should return the error when receiving a binary response', async () => {
|
it('should return the error when receiving a binary response', async () => {
|
||||||
helpers.httpRequest.mockResolvedValue({
|
helpers.httpRequest.mockResolvedValue({
|
||||||
body: Buffer.from(''),
|
body: Buffer.from(''),
|
||||||
|
@ -237,4 +237,62 @@ describe('ToolHttpRequest', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Optimize response', () => {
|
||||||
|
it('should extract body from the response HTML', async () => {
|
||||||
|
helpers.httpRequest.mockResolvedValue({
|
||||||
|
body: `<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Test</h1>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Test content
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>`,
|
||||||
|
headers: {
|
||||||
|
'content-type': 'text/html',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
executeFunctions.getNodeParameter.mockImplementation(
|
||||||
|
(paramName: string, _: any, fallback: any) => {
|
||||||
|
switch (paramName) {
|
||||||
|
case 'method':
|
||||||
|
return 'GET';
|
||||||
|
case 'url':
|
||||||
|
return '{url}';
|
||||||
|
case 'options':
|
||||||
|
return {};
|
||||||
|
case 'placeholderDefinitions.values':
|
||||||
|
return [];
|
||||||
|
case 'optimizeResponse':
|
||||||
|
return true;
|
||||||
|
case 'responseType':
|
||||||
|
return 'html';
|
||||||
|
case 'cssSelector':
|
||||||
|
return 'body';
|
||||||
|
default:
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const { response } = await httpTool.supplyData.call(executeFunctions, 0);
|
||||||
|
|
||||||
|
const res = await (response as N8nTool).invoke({
|
||||||
|
url: 'https://httpbin.org/html',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(helpers.httpRequest).toHaveBeenCalled();
|
||||||
|
expect(res).toEqual(
|
||||||
|
JSON.stringify(['<h1>Test</h1> <div> <p> Test content </p> </div>'], null, 2),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Readability } from '@mozilla/readability';
|
import { Readability } from '@mozilla/readability';
|
||||||
import cheerio from 'cheerio';
|
import * as cheerio from 'cheerio';
|
||||||
import { convert } from 'html-to-text';
|
import { convert } from 'html-to-text';
|
||||||
import { JSDOM } from 'jsdom';
|
import { JSDOM } from 'jsdom';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
|
@ -8,12 +8,12 @@ import unset from 'lodash/unset';
|
||||||
import * as mime from 'mime-types';
|
import * as mime from 'mime-types';
|
||||||
import { getOAuth2AdditionalParameters } from 'n8n-nodes-base/dist/nodes/HttpRequest/GenericFunctions';
|
import { getOAuth2AdditionalParameters } from 'n8n-nodes-base/dist/nodes/HttpRequest/GenericFunctions';
|
||||||
import type {
|
import type {
|
||||||
IExecuteFunctions,
|
|
||||||
IDataObject,
|
IDataObject,
|
||||||
IHttpRequestOptions,
|
IHttpRequestOptions,
|
||||||
IRequestOptionsSimplified,
|
IRequestOptionsSimplified,
|
||||||
ExecutionError,
|
ExecutionError,
|
||||||
NodeApiError,
|
NodeApiError,
|
||||||
|
ISupplyDataFunctions,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { NodeConnectionType, NodeOperationError, jsonParse } from 'n8n-workflow';
|
import { NodeConnectionType, NodeOperationError, jsonParse } from 'n8n-workflow';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
@ -28,7 +28,7 @@ import type {
|
||||||
} from './interfaces';
|
} from './interfaces';
|
||||||
import type { DynamicZodObject } from '../../../types/zod.types';
|
import type { DynamicZodObject } from '../../../types/zod.types';
|
||||||
|
|
||||||
const genericCredentialRequest = async (ctx: IExecuteFunctions, itemIndex: number) => {
|
const genericCredentialRequest = async (ctx: ISupplyDataFunctions, itemIndex: number) => {
|
||||||
const genericType = ctx.getNodeParameter('genericAuthType', itemIndex) as string;
|
const genericType = ctx.getNodeParameter('genericAuthType', itemIndex) as string;
|
||||||
|
|
||||||
if (genericType === 'httpBasicAuth' || genericType === 'httpDigestAuth') {
|
if (genericType === 'httpBasicAuth' || genericType === 'httpDigestAuth') {
|
||||||
|
@ -104,7 +104,7 @@ const genericCredentialRequest = async (ctx: IExecuteFunctions, itemIndex: numbe
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const predefinedCredentialRequest = async (ctx: IExecuteFunctions, itemIndex: number) => {
|
const predefinedCredentialRequest = async (ctx: ISupplyDataFunctions, itemIndex: number) => {
|
||||||
const predefinedType = ctx.getNodeParameter('nodeCredentialType', itemIndex) as string;
|
const predefinedType = ctx.getNodeParameter('nodeCredentialType', itemIndex) as string;
|
||||||
const additionalOptions = getOAuth2AdditionalParameters(predefinedType);
|
const additionalOptions = getOAuth2AdditionalParameters(predefinedType);
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ const predefinedCredentialRequest = async (ctx: IExecuteFunctions, itemIndex: nu
|
||||||
};
|
};
|
||||||
|
|
||||||
export const configureHttpRequestFunction = async (
|
export const configureHttpRequestFunction = async (
|
||||||
ctx: IExecuteFunctions,
|
ctx: ISupplyDataFunctions,
|
||||||
credentialsType: 'predefinedCredentialType' | 'genericCredentialType' | 'none',
|
credentialsType: 'predefinedCredentialType' | 'genericCredentialType' | 'none',
|
||||||
itemIndex: number,
|
itemIndex: number,
|
||||||
) => {
|
) => {
|
||||||
|
@ -146,7 +146,7 @@ const defaultOptimizer = <T>(response: T) => {
|
||||||
return String(response);
|
return String(response);
|
||||||
};
|
};
|
||||||
|
|
||||||
const htmlOptimizer = (ctx: IExecuteFunctions, itemIndex: number, maxLength: number) => {
|
const htmlOptimizer = (ctx: ISupplyDataFunctions, itemIndex: number, maxLength: number) => {
|
||||||
const cssSelector = ctx.getNodeParameter('cssSelector', itemIndex, '') as string;
|
const cssSelector = ctx.getNodeParameter('cssSelector', itemIndex, '') as string;
|
||||||
const onlyContent = ctx.getNodeParameter('onlyContent', itemIndex, false) as boolean;
|
const onlyContent = ctx.getNodeParameter('onlyContent', itemIndex, false) as boolean;
|
||||||
let elementsToOmit: string[] = [];
|
let elementsToOmit: string[] = [];
|
||||||
|
@ -214,7 +214,7 @@ const htmlOptimizer = (ctx: IExecuteFunctions, itemIndex: number, maxLength: num
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const textOptimizer = (ctx: IExecuteFunctions, itemIndex: number, maxLength: number) => {
|
const textOptimizer = (ctx: ISupplyDataFunctions, itemIndex: number, maxLength: number) => {
|
||||||
return (response: string | IDataObject) => {
|
return (response: string | IDataObject) => {
|
||||||
if (typeof response === 'object') {
|
if (typeof response === 'object') {
|
||||||
try {
|
try {
|
||||||
|
@ -245,7 +245,7 @@ const textOptimizer = (ctx: IExecuteFunctions, itemIndex: number, maxLength: num
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const jsonOptimizer = (ctx: IExecuteFunctions, itemIndex: number) => {
|
const jsonOptimizer = (ctx: ISupplyDataFunctions, itemIndex: number) => {
|
||||||
return (response: string): string => {
|
return (response: string): string => {
|
||||||
let responseData: IDataObject | IDataObject[] | string = response;
|
let responseData: IDataObject | IDataObject[] | string = response;
|
||||||
|
|
||||||
|
@ -324,7 +324,7 @@ const jsonOptimizer = (ctx: IExecuteFunctions, itemIndex: number) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const configureResponseOptimizer = (ctx: IExecuteFunctions, itemIndex: number) => {
|
export const configureResponseOptimizer = (ctx: ISupplyDataFunctions, itemIndex: number) => {
|
||||||
const optimizeResponse = ctx.getNodeParameter('optimizeResponse', itemIndex, false) as boolean;
|
const optimizeResponse = ctx.getNodeParameter('optimizeResponse', itemIndex, false) as boolean;
|
||||||
|
|
||||||
if (optimizeResponse) {
|
if (optimizeResponse) {
|
||||||
|
@ -469,7 +469,7 @@ const MODEL_INPUT_DESCRIPTION = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateParametersAndOptions = (options: {
|
export const updateParametersAndOptions = (options: {
|
||||||
ctx: IExecuteFunctions;
|
ctx: ISupplyDataFunctions;
|
||||||
itemIndex: number;
|
itemIndex: number;
|
||||||
toolParameters: ToolParameter[];
|
toolParameters: ToolParameter[];
|
||||||
placeholdersDefinitions: PlaceholderDefinition[];
|
placeholdersDefinitions: PlaceholderDefinition[];
|
||||||
|
@ -558,7 +558,7 @@ export const prepareToolDescription = (
|
||||||
};
|
};
|
||||||
|
|
||||||
export const configureToolFunction = (
|
export const configureToolFunction = (
|
||||||
ctx: IExecuteFunctions,
|
ctx: ISupplyDataFunctions,
|
||||||
itemIndex: number,
|
itemIndex: number,
|
||||||
toolParameters: ToolParameter[],
|
toolParameters: ToolParameter[],
|
||||||
requestOptions: IHttpRequestOptions,
|
requestOptions: IHttpRequestOptions,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { SerpAPI } from '@langchain/community/tools/serpapi';
|
import { SerpAPI } from '@langchain/community/tools/serpapi';
|
||||||
|
@ -113,7 +113,7 @@ export class ToolSerpApi implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('serpApi');
|
const credentials = await this.getCredentials('serpApi');
|
||||||
|
|
||||||
const options = this.getNodeParameter('options', itemIndex) as object;
|
const options = this.getNodeParameter('options', itemIndex) as object;
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
import type { IExecuteFunctions, INodeType, INodeTypeDescription, SupplyData } from 'n8n-workflow';
|
import type {
|
||||||
|
INodeType,
|
||||||
|
INodeTypeDescription,
|
||||||
|
ISupplyDataFunctions,
|
||||||
|
SupplyData,
|
||||||
|
} from 'n8n-workflow';
|
||||||
import { NodeConnectionType } from 'n8n-workflow';
|
import { NodeConnectionType } from 'n8n-workflow';
|
||||||
|
|
||||||
import { VectorStoreQATool } from 'langchain/tools';
|
import { VectorStoreQATool } from 'langchain/tools';
|
||||||
|
@ -82,7 +87,7 @@ export class ToolVectorStore implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const name = this.getNodeParameter('name', itemIndex) as string;
|
const name = this.getNodeParameter('name', itemIndex) as string;
|
||||||
const toolDescription = this.getNodeParameter('description', itemIndex) as string;
|
const toolDescription = this.getNodeParameter('description', itemIndex) as string;
|
||||||
const topK = this.getNodeParameter('topK', itemIndex, 4) as number;
|
const topK = this.getNodeParameter('topK', itemIndex, 4) as number;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { WikipediaQueryRun } from '@langchain/community/tools/wikipedia_query_run';
|
import { WikipediaQueryRun } from '@langchain/community/tools/wikipedia_query_run';
|
||||||
|
@ -43,7 +43,7 @@ export class ToolWikipedia implements INodeType {
|
||||||
properties: [getConnectionHintNoticeField([NodeConnectionType.AiAgent])],
|
properties: [getConnectionHintNoticeField([NodeConnectionType.AiAgent])],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions): Promise<SupplyData> {
|
||||||
const WikiTool = new WikipediaQueryRun();
|
const WikiTool = new WikipediaQueryRun();
|
||||||
|
|
||||||
WikiTool.description =
|
WikiTool.description =
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { WolframAlphaTool } from '@langchain/community/tools/wolframalpha';
|
import { WolframAlphaTool } from '@langchain/community/tools/wolframalpha';
|
||||||
|
@ -49,7 +49,7 @@ export class ToolWolframAlpha implements INodeType {
|
||||||
properties: [getConnectionHintNoticeField([NodeConnectionType.AiAgent])],
|
properties: [getConnectionHintNoticeField([NodeConnectionType.AiAgent])],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions): Promise<SupplyData> {
|
||||||
const credentials = await this.getCredentials('wolframAlphaApi');
|
const credentials = await this.getCredentials('wolframAlphaApi');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -6,12 +6,12 @@ import isObject from 'lodash/isObject';
|
||||||
import type { SetField, SetNodeOptions } from 'n8n-nodes-base/dist/nodes/Set/v2/helpers/interfaces';
|
import type { SetField, SetNodeOptions } from 'n8n-nodes-base/dist/nodes/Set/v2/helpers/interfaces';
|
||||||
import * as manual from 'n8n-nodes-base/dist/nodes/Set/v2/manual.mode';
|
import * as manual from 'n8n-nodes-base/dist/nodes/Set/v2/manual.mode';
|
||||||
import type {
|
import type {
|
||||||
IExecuteFunctions,
|
|
||||||
IExecuteWorkflowInfo,
|
IExecuteWorkflowInfo,
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
INodeType,
|
INodeType,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
IWorkflowBase,
|
IWorkflowBase,
|
||||||
|
ISupplyDataFunctions,
|
||||||
SupplyData,
|
SupplyData,
|
||||||
ExecutionError,
|
ExecutionError,
|
||||||
IDataObject,
|
IDataObject,
|
||||||
|
@ -357,7 +357,7 @@ export class ToolWorkflow implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const name = this.getNodeParameter('name', itemIndex) as string;
|
const name = this.getNodeParameter('name', itemIndex) as string;
|
||||||
const description = this.getNodeParameter('description', itemIndex) as string;
|
const description = this.getNodeParameter('description', itemIndex) as string;
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type SupplyData,
|
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import type { Embeddings } from '@langchain/core/embeddings';
|
import type { Embeddings } from '@langchain/core/embeddings';
|
||||||
import { MemoryVectorStoreManager } from '../shared/MemoryVectorStoreManager';
|
import { MemoryVectorStoreManager } from '../shared/MemoryVectorStoreManager';
|
||||||
|
@ -59,7 +59,7 @@ export class VectorStoreInMemoryLoad implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const embeddings = (await this.getInputConnectionData(
|
const embeddings = (await this.getInputConnectionData(
|
||||||
NodeConnectionType.AiEmbedding,
|
NodeConnectionType.AiEmbedding,
|
||||||
itemIndex,
|
itemIndex,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import type { PineconeStoreParams } from '@langchain/pinecone';
|
import type { PineconeStoreParams } from '@langchain/pinecone';
|
||||||
|
@ -84,7 +84,7 @@ export class VectorStorePineconeLoad implements INodeType {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
this.logger.debug('Supplying data for Pinecone Load Vector Store');
|
this.logger.debug('Supplying data for Pinecone Load Vector Store');
|
||||||
|
|
||||||
const namespace = this.getNodeParameter('pineconeNamespace', itemIndex) as string;
|
const namespace = this.getNodeParameter('pineconeNamespace', itemIndex) as string;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {
|
import {
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
@ -81,7 +81,7 @@ export class VectorStoreSupabaseLoad implements INodeType {
|
||||||
|
|
||||||
methods = { listSearch: { supabaseTableNameSearch } };
|
methods = { listSearch: { supabaseTableNameSearch } };
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
this.logger.debug('Supply Supabase Load Vector Store');
|
this.logger.debug('Supply Supabase Load Vector Store');
|
||||||
|
|
||||||
const tableName = this.getNodeParameter('tableName', itemIndex, '', {
|
const tableName = this.getNodeParameter('tableName', itemIndex, '', {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import {
|
import {
|
||||||
NodeConnectionType,
|
NodeConnectionType,
|
||||||
type IExecuteFunctions,
|
|
||||||
type INodeType,
|
type INodeType,
|
||||||
type INodeTypeDescription,
|
type INodeTypeDescription,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
type SupplyData,
|
type SupplyData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import type { IZepConfig } from '@langchain/community/vectorstores/zep';
|
import type { IZepConfig } from '@langchain/community/vectorstores/zep';
|
||||||
|
@ -83,7 +83,7 @@ export class VectorStoreZepLoad implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
this.logger.debug('Supplying data for Zep Load Vector Store');
|
this.logger.debug('Supplying data for Zep Load Vector Store');
|
||||||
|
|
||||||
const collectionName = this.getNodeParameter('collectionName', itemIndex) as string;
|
const collectionName = this.getNodeParameter('collectionName', itemIndex) as string;
|
||||||
|
|
|
@ -7,11 +7,8 @@ export class MemoryVectorStoreManager {
|
||||||
|
|
||||||
private vectorStoreBuffer: Map<string, MemoryVectorStore>;
|
private vectorStoreBuffer: Map<string, MemoryVectorStore>;
|
||||||
|
|
||||||
private embeddings: Embeddings;
|
private constructor(private embeddings: Embeddings) {
|
||||||
|
|
||||||
private constructor(embeddings: Embeddings) {
|
|
||||||
this.vectorStoreBuffer = new Map();
|
this.vectorStoreBuffer = new Map();
|
||||||
this.embeddings = embeddings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getInstance(embeddings: Embeddings): MemoryVectorStoreManager {
|
public static getInstance(embeddings: Embeddings): MemoryVectorStoreManager {
|
||||||
|
|
|
@ -5,12 +5,13 @@ import type { Embeddings } from '@langchain/core/embeddings';
|
||||||
import type { VectorStore } from '@langchain/core/vectorstores';
|
import type { VectorStore } from '@langchain/core/vectorstores';
|
||||||
import { NodeConnectionType, NodeOperationError } from 'n8n-workflow';
|
import { NodeConnectionType, NodeOperationError } from 'n8n-workflow';
|
||||||
import type {
|
import type {
|
||||||
|
IExecuteFunctions,
|
||||||
INodeCredentialDescription,
|
INodeCredentialDescription,
|
||||||
INodeProperties,
|
INodeProperties,
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
IExecuteFunctions,
|
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
SupplyData,
|
SupplyData,
|
||||||
|
ISupplyDataFunctions,
|
||||||
INodeType,
|
INodeType,
|
||||||
ILoadOptionsFunctions,
|
ILoadOptionsFunctions,
|
||||||
INodeListSearchResult,
|
INodeListSearchResult,
|
||||||
|
@ -57,13 +58,13 @@ interface VectorStoreNodeConstructorArgs {
|
||||||
retrieveFields?: INodeProperties[];
|
retrieveFields?: INodeProperties[];
|
||||||
updateFields?: INodeProperties[];
|
updateFields?: INodeProperties[];
|
||||||
populateVectorStore: (
|
populateVectorStore: (
|
||||||
context: IExecuteFunctions,
|
context: ISupplyDataFunctions,
|
||||||
embeddings: Embeddings,
|
embeddings: Embeddings,
|
||||||
documents: Array<Document<Record<string, unknown>>>,
|
documents: Array<Document<Record<string, unknown>>>,
|
||||||
itemIndex: number,
|
itemIndex: number,
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
getVectorStoreClient: (
|
getVectorStoreClient: (
|
||||||
context: IExecuteFunctions,
|
context: ISupplyDataFunctions,
|
||||||
filter: Record<string, never> | undefined,
|
filter: Record<string, never> | undefined,
|
||||||
embeddings: Embeddings,
|
embeddings: Embeddings,
|
||||||
itemIndex: number,
|
itemIndex: number,
|
||||||
|
@ -281,7 +282,7 @@ export const createVectorStoreNode = (args: VectorStoreNodeConstructorArgs) =>
|
||||||
});
|
});
|
||||||
|
|
||||||
resultData.push(...serializedDocs);
|
resultData.push(...serializedDocs);
|
||||||
void logAiEvent(this, 'ai-vector-store-searched', { query: prompt });
|
logAiEvent(this, 'ai-vector-store-searched', { query: prompt });
|
||||||
}
|
}
|
||||||
|
|
||||||
return [resultData];
|
return [resultData];
|
||||||
|
@ -311,7 +312,7 @@ export const createVectorStoreNode = (args: VectorStoreNodeConstructorArgs) =>
|
||||||
try {
|
try {
|
||||||
await args.populateVectorStore(this, embeddings, processedDocuments, itemIndex);
|
await args.populateVectorStore(this, embeddings, processedDocuments, itemIndex);
|
||||||
|
|
||||||
void logAiEvent(this, 'ai-vector-store-populated');
|
logAiEvent(this, 'ai-vector-store-populated');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
@ -365,7 +366,7 @@ export const createVectorStoreNode = (args: VectorStoreNodeConstructorArgs) =>
|
||||||
ids: [documentId],
|
ids: [documentId],
|
||||||
});
|
});
|
||||||
|
|
||||||
void logAiEvent(this, 'ai-vector-store-updated');
|
logAiEvent(this, 'ai-vector-store-updated');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
@ -380,7 +381,7 @@ export const createVectorStoreNode = (args: VectorStoreNodeConstructorArgs) =>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async supplyData(this: IExecuteFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const mode = this.getNodeParameter('mode', 0) as 'load' | 'insert' | 'retrieve';
|
const mode = this.getNodeParameter('mode', 0) as 'load' | 'insert' | 'retrieve';
|
||||||
const filter = getMetadataFiltersValues(this, itemIndex);
|
const filter = getMetadataFiltersValues(this, itemIndex);
|
||||||
const embeddings = (await this.getInputConnectionData(
|
const embeddings = (await this.getInputConnectionData(
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import { pipeline } from 'stream/promises';
|
import { pipeline } from 'stream/promises';
|
||||||
import { createWriteStream } from 'fs';
|
import { createWriteStream } from 'fs';
|
||||||
import type { IBinaryData, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
|
import type {
|
||||||
|
IBinaryData,
|
||||||
|
IExecuteFunctions,
|
||||||
|
INodeExecutionData,
|
||||||
|
ISupplyDataFunctions,
|
||||||
|
} from 'n8n-workflow';
|
||||||
import { NodeOperationError, BINARY_ENCODING } from 'n8n-workflow';
|
import { NodeOperationError, BINARY_ENCODING } from 'n8n-workflow';
|
||||||
|
|
||||||
import type { TextSplitter } from '@langchain/textsplitters';
|
import type { TextSplitter } from '@langchain/textsplitters';
|
||||||
|
@ -26,25 +31,12 @@ const SUPPORTED_MIME_TYPES = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export class N8nBinaryLoader {
|
export class N8nBinaryLoader {
|
||||||
private context: IExecuteFunctions;
|
|
||||||
|
|
||||||
private optionsPrefix: string;
|
|
||||||
|
|
||||||
private binaryDataKey: string;
|
|
||||||
|
|
||||||
private textSplitter?: TextSplitter;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
context: IExecuteFunctions,
|
private context: IExecuteFunctions | ISupplyDataFunctions,
|
||||||
optionsPrefix = '',
|
private optionsPrefix = '',
|
||||||
binaryDataKey = '',
|
private binaryDataKey = '',
|
||||||
textSplitter?: TextSplitter,
|
private textSplitter?: TextSplitter,
|
||||||
) {
|
) {}
|
||||||
this.context = context;
|
|
||||||
this.textSplitter = textSplitter;
|
|
||||||
this.optionsPrefix = optionsPrefix;
|
|
||||||
this.binaryDataKey = binaryDataKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
async processAll(items?: INodeExecutionData[]): Promise<Document[]> {
|
async processAll(items?: INodeExecutionData[]): Promise<Document[]> {
|
||||||
const docs: Document[] = [];
|
const docs: Document[] = [];
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
import { type IExecuteFunctions, type INodeExecutionData, NodeOperationError } from 'n8n-workflow';
|
import {
|
||||||
|
type IExecuteFunctions,
|
||||||
|
type INodeExecutionData,
|
||||||
|
type ISupplyDataFunctions,
|
||||||
|
NodeOperationError,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import type { TextSplitter } from '@langchain/textsplitters';
|
import type { TextSplitter } from '@langchain/textsplitters';
|
||||||
import type { Document } from '@langchain/core/documents';
|
import type { Document } from '@langchain/core/documents';
|
||||||
|
@ -7,17 +12,11 @@ import { TextLoader } from 'langchain/document_loaders/fs/text';
|
||||||
import { getMetadataFiltersValues } from './helpers';
|
import { getMetadataFiltersValues } from './helpers';
|
||||||
|
|
||||||
export class N8nJsonLoader {
|
export class N8nJsonLoader {
|
||||||
private context: IExecuteFunctions;
|
constructor(
|
||||||
|
private context: IExecuteFunctions | ISupplyDataFunctions,
|
||||||
private optionsPrefix: string;
|
private optionsPrefix = '',
|
||||||
|
private textSplitter?: TextSplitter,
|
||||||
private textSplitter?: TextSplitter;
|
) {}
|
||||||
|
|
||||||
constructor(context: IExecuteFunctions, optionsPrefix = '', textSplitter?: TextSplitter) {
|
|
||||||
this.context = context;
|
|
||||||
this.textSplitter = textSplitter;
|
|
||||||
this.optionsPrefix = optionsPrefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
async processAll(items?: INodeExecutionData[]): Promise<Document[]> {
|
async processAll(items?: INodeExecutionData[]): Promise<Document[]> {
|
||||||
const docs: Document[] = [];
|
const docs: Document[] = [];
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import type { DynamicStructuredToolInput } from '@langchain/core/tools';
|
import type { DynamicStructuredToolInput } from '@langchain/core/tools';
|
||||||
import { DynamicStructuredTool, DynamicTool } from '@langchain/core/tools';
|
import { DynamicStructuredTool, DynamicTool } from '@langchain/core/tools';
|
||||||
import type { IExecuteFunctions, IDataObject } from 'n8n-workflow';
|
import type { ISupplyDataFunctions, IDataObject } from 'n8n-workflow';
|
||||||
import { NodeConnectionType, jsonParse, NodeOperationError } from 'n8n-workflow';
|
import { NodeConnectionType, jsonParse, NodeOperationError } from 'n8n-workflow';
|
||||||
import { StructuredOutputParser } from 'langchain/output_parsers';
|
import { StructuredOutputParser } from 'langchain/output_parsers';
|
||||||
import type { ZodTypeAny } from 'zod';
|
import type { ZodTypeAny } from 'zod';
|
||||||
|
@ -45,12 +45,11 @@ ALL parameters marked as required must be provided`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class N8nTool extends DynamicStructuredTool {
|
export class N8nTool extends DynamicStructuredTool {
|
||||||
private context: IExecuteFunctions;
|
constructor(
|
||||||
|
private context: ISupplyDataFunctions,
|
||||||
constructor(context: IExecuteFunctions, fields: DynamicStructuredToolInput) {
|
fields: DynamicStructuredToolInput,
|
||||||
|
) {
|
||||||
super(fields);
|
super(fields);
|
||||||
|
|
||||||
this.context = context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
asDynamicTool(): DynamicTool {
|
asDynamicTool(): DynamicTool {
|
||||||
|
|
|
@ -5,7 +5,13 @@ import type { BaseMessage } from '@langchain/core/messages';
|
||||||
import type { Tool } from '@langchain/core/tools';
|
import type { Tool } from '@langchain/core/tools';
|
||||||
import type { BaseChatMemory } from 'langchain/memory';
|
import type { BaseChatMemory } from 'langchain/memory';
|
||||||
import { NodeConnectionType, NodeOperationError, jsonStringify } from 'n8n-workflow';
|
import { NodeConnectionType, NodeOperationError, jsonStringify } from 'n8n-workflow';
|
||||||
import type { AiEvent, IDataObject, IExecuteFunctions, IWebhookFunctions } from 'n8n-workflow';
|
import type {
|
||||||
|
AiEvent,
|
||||||
|
IDataObject,
|
||||||
|
IExecuteFunctions,
|
||||||
|
ISupplyDataFunctions,
|
||||||
|
IWebhookFunctions,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import { N8nTool } from './N8nTool';
|
import { N8nTool } from './N8nTool';
|
||||||
|
|
||||||
|
@ -20,7 +26,7 @@ function hasMethods<T>(obj: unknown, ...methodNames: Array<string | symbol>): ob
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMetadataFiltersValues(
|
export function getMetadataFiltersValues(
|
||||||
ctx: IExecuteFunctions,
|
ctx: IExecuteFunctions | ISupplyDataFunctions,
|
||||||
itemIndex: number,
|
itemIndex: number,
|
||||||
): Record<string, never> | undefined {
|
): Record<string, never> | undefined {
|
||||||
const options = ctx.getNodeParameter('options', itemIndex, {});
|
const options = ctx.getNodeParameter('options', itemIndex, {});
|
||||||
|
@ -93,7 +99,7 @@ export function getPromptInputByType(options: {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSessionId(
|
export function getSessionId(
|
||||||
ctx: IExecuteFunctions | IWebhookFunctions,
|
ctx: ISupplyDataFunctions | IWebhookFunctions,
|
||||||
itemIndex: number,
|
itemIndex: number,
|
||||||
selectorKey = 'sessionIdType',
|
selectorKey = 'sessionIdType',
|
||||||
autoSelect = 'fromInput',
|
autoSelect = 'fromInput',
|
||||||
|
@ -133,13 +139,13 @@ export function getSessionId(
|
||||||
return sessionId;
|
return sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function logAiEvent(
|
export function logAiEvent(
|
||||||
executeFunctions: IExecuteFunctions,
|
executeFunctions: IExecuteFunctions | ISupplyDataFunctions,
|
||||||
event: AiEvent,
|
event: AiEvent,
|
||||||
data?: IDataObject,
|
data?: IDataObject,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
await executeFunctions.logAiEvent(event, data ? jsonStringify(data) : undefined);
|
executeFunctions.logAiEvent(event, data ? jsonStringify(data) : undefined);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
executeFunctions.logger.debug(`Error logging AI event: ${event}`);
|
executeFunctions.logger.debug(`Error logging AI event: ${event}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import type { Tool } from '@langchain/core/tools';
|
||||||
import { VectorStore } from '@langchain/core/vectorstores';
|
import { VectorStore } from '@langchain/core/vectorstores';
|
||||||
import { TextSplitter } from '@langchain/textsplitters';
|
import { TextSplitter } from '@langchain/textsplitters';
|
||||||
import type { BaseDocumentLoader } from 'langchain/dist/document_loaders/base';
|
import type { BaseDocumentLoader } from 'langchain/dist/document_loaders/base';
|
||||||
import type { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
|
import type { IExecuteFunctions, INodeExecutionData, ISupplyDataFunctions } from 'n8n-workflow';
|
||||||
import { NodeOperationError, NodeConnectionType } from 'n8n-workflow';
|
import { NodeOperationError, NodeConnectionType } from 'n8n-workflow';
|
||||||
|
|
||||||
import { logAiEvent, isToolsInstance, isBaseChatMemory, isBaseChatMessageHistory } from './helpers';
|
import { logAiEvent, isToolsInstance, isBaseChatMemory, isBaseChatMessageHistory } from './helpers';
|
||||||
|
@ -27,7 +27,7 @@ const errorsMap: { [key: string]: { message: string; description: string } } = {
|
||||||
export async function callMethodAsync<T>(
|
export async function callMethodAsync<T>(
|
||||||
this: T,
|
this: T,
|
||||||
parameters: {
|
parameters: {
|
||||||
executeFunctions: IExecuteFunctions;
|
executeFunctions: IExecuteFunctions | ISupplyDataFunctions;
|
||||||
connectionType: NodeConnectionType;
|
connectionType: NodeConnectionType;
|
||||||
currentNodeRunIndex: number;
|
currentNodeRunIndex: number;
|
||||||
method: (...args: any[]) => Promise<unknown>;
|
method: (...args: any[]) => Promise<unknown>;
|
||||||
|
@ -113,7 +113,7 @@ export function logWrapper(
|
||||||
| VectorStore
|
| VectorStore
|
||||||
| N8nBinaryLoader
|
| N8nBinaryLoader
|
||||||
| N8nJsonLoader,
|
| N8nJsonLoader,
|
||||||
executeFunctions: IExecuteFunctions,
|
executeFunctions: IExecuteFunctions | ISupplyDataFunctions,
|
||||||
) {
|
) {
|
||||||
return new Proxy(originalInstance, {
|
return new Proxy(originalInstance, {
|
||||||
get: (target, prop) => {
|
get: (target, prop) => {
|
||||||
|
@ -190,7 +190,7 @@ export function logWrapper(
|
||||||
const payload = { action: 'getMessages', response };
|
const payload = { action: 'getMessages', response };
|
||||||
executeFunctions.addOutputData(connectionType, index, [[{ json: payload }]]);
|
executeFunctions.addOutputData(connectionType, index, [[{ json: payload }]]);
|
||||||
|
|
||||||
void logAiEvent(executeFunctions, 'ai-messages-retrieved-from-memory', { response });
|
logAiEvent(executeFunctions, 'ai-messages-retrieved-from-memory', { response });
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
} else if (prop === 'addMessage' && 'addMessage' in target) {
|
} else if (prop === 'addMessage' && 'addMessage' in target) {
|
||||||
|
@ -207,7 +207,7 @@ export function logWrapper(
|
||||||
arguments: [message],
|
arguments: [message],
|
||||||
});
|
});
|
||||||
|
|
||||||
void logAiEvent(executeFunctions, 'ai-message-added-to-memory', { message });
|
logAiEvent(executeFunctions, 'ai-message-added-to-memory', { message });
|
||||||
executeFunctions.addOutputData(connectionType, index, [[{ json: payload }]]);
|
executeFunctions.addOutputData(connectionType, index, [[{ json: payload }]]);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ export function logWrapper(
|
||||||
arguments: [query, config],
|
arguments: [query, config],
|
||||||
})) as Array<Document<Record<string, any>>>;
|
})) as Array<Document<Record<string, any>>>;
|
||||||
|
|
||||||
void logAiEvent(executeFunctions, 'ai-documents-retrieved', { query });
|
logAiEvent(executeFunctions, 'ai-documents-retrieved', { query });
|
||||||
executeFunctions.addOutputData(connectionType, index, [[{ json: { response } }]]);
|
executeFunctions.addOutputData(connectionType, index, [[{ json: { response } }]]);
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
@ -258,7 +258,7 @@ export function logWrapper(
|
||||||
arguments: [documents],
|
arguments: [documents],
|
||||||
})) as number[][];
|
})) as number[][];
|
||||||
|
|
||||||
void logAiEvent(executeFunctions, 'ai-document-embedded');
|
logAiEvent(executeFunctions, 'ai-document-embedded');
|
||||||
executeFunctions.addOutputData(connectionType, index, [[{ json: { response } }]]);
|
executeFunctions.addOutputData(connectionType, index, [[{ json: { response } }]]);
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
@ -278,7 +278,7 @@ export function logWrapper(
|
||||||
method: target[prop],
|
method: target[prop],
|
||||||
arguments: [query],
|
arguments: [query],
|
||||||
})) as number[];
|
})) as number[];
|
||||||
void logAiEvent(executeFunctions, 'ai-query-embedded');
|
logAiEvent(executeFunctions, 'ai-query-embedded');
|
||||||
executeFunctions.addOutputData(connectionType, index, [[{ json: { response } }]]);
|
executeFunctions.addOutputData(connectionType, index, [[{ json: { response } }]]);
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
@ -323,7 +323,7 @@ export function logWrapper(
|
||||||
arguments: [item, itemIndex],
|
arguments: [item, itemIndex],
|
||||||
})) as number[];
|
})) as number[];
|
||||||
|
|
||||||
void logAiEvent(executeFunctions, 'ai-document-processed');
|
logAiEvent(executeFunctions, 'ai-document-processed');
|
||||||
executeFunctions.addOutputData(connectionType, index, [
|
executeFunctions.addOutputData(connectionType, index, [
|
||||||
[{ json: { response }, pairedItem: { item: itemIndex } }],
|
[{ json: { response }, pairedItem: { item: itemIndex } }],
|
||||||
]);
|
]);
|
||||||
|
@ -349,7 +349,7 @@ export function logWrapper(
|
||||||
arguments: [text],
|
arguments: [text],
|
||||||
})) as string[];
|
})) as string[];
|
||||||
|
|
||||||
void logAiEvent(executeFunctions, 'ai-text-split');
|
logAiEvent(executeFunctions, 'ai-text-split');
|
||||||
executeFunctions.addOutputData(connectionType, index, [[{ json: { response } }]]);
|
executeFunctions.addOutputData(connectionType, index, [[{ json: { response } }]]);
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
@ -373,7 +373,7 @@ export function logWrapper(
|
||||||
arguments: [query],
|
arguments: [query],
|
||||||
})) as string;
|
})) as string;
|
||||||
|
|
||||||
void logAiEvent(executeFunctions, 'ai-tool-called', { query, response });
|
logAiEvent(executeFunctions, 'ai-tool-called', { query, response });
|
||||||
executeFunctions.addOutputData(connectionType, index, [[{ json: { response } }]]);
|
executeFunctions.addOutputData(connectionType, index, [[{ json: { response } }]]);
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
@ -403,7 +403,7 @@ export function logWrapper(
|
||||||
arguments: [query, k, filter, _callbacks],
|
arguments: [query, k, filter, _callbacks],
|
||||||
})) as Array<Document<Record<string, any>>>;
|
})) as Array<Document<Record<string, any>>>;
|
||||||
|
|
||||||
void logAiEvent(executeFunctions, 'ai-vector-store-searched', { query });
|
logAiEvent(executeFunctions, 'ai-vector-store-searched', { query });
|
||||||
executeFunctions.addOutputData(connectionType, index, [[{ json: { response } }]]);
|
executeFunctions.addOutputData(connectionType, index, [[{ json: { response } }]]);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
|
|
@ -2,7 +2,7 @@ import type { Callbacks } from '@langchain/core/callbacks/manager';
|
||||||
import type { BaseLanguageModel } from '@langchain/core/language_models/base';
|
import type { BaseLanguageModel } from '@langchain/core/language_models/base';
|
||||||
import type { AIMessage } from '@langchain/core/messages';
|
import type { AIMessage } from '@langchain/core/messages';
|
||||||
import { BaseOutputParser } from '@langchain/core/output_parsers';
|
import { BaseOutputParser } from '@langchain/core/output_parsers';
|
||||||
import type { IExecuteFunctions } from 'n8n-workflow';
|
import type { ISupplyDataFunctions } from 'n8n-workflow';
|
||||||
import { NodeConnectionType } from 'n8n-workflow';
|
import { NodeConnectionType } from 'n8n-workflow';
|
||||||
|
|
||||||
import type { N8nStructuredOutputParser } from './N8nStructuredOutputParser';
|
import type { N8nStructuredOutputParser } from './N8nStructuredOutputParser';
|
||||||
|
@ -10,23 +10,14 @@ import { NAIVE_FIX_PROMPT } from './prompt';
|
||||||
import { logAiEvent } from '../helpers';
|
import { logAiEvent } from '../helpers';
|
||||||
|
|
||||||
export class N8nOutputFixingParser extends BaseOutputParser {
|
export class N8nOutputFixingParser extends BaseOutputParser {
|
||||||
private context: IExecuteFunctions;
|
|
||||||
|
|
||||||
private model: BaseLanguageModel;
|
|
||||||
|
|
||||||
private outputParser: N8nStructuredOutputParser;
|
|
||||||
|
|
||||||
lc_namespace = ['langchain', 'output_parsers', 'fix'];
|
lc_namespace = ['langchain', 'output_parsers', 'fix'];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
context: IExecuteFunctions,
|
private context: ISupplyDataFunctions,
|
||||||
model: BaseLanguageModel,
|
private model: BaseLanguageModel,
|
||||||
outputParser: N8nStructuredOutputParser,
|
private outputParser: N8nStructuredOutputParser,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.context = context;
|
|
||||||
this.model = model;
|
|
||||||
this.outputParser = outputParser;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getRetryChain() {
|
getRetryChain() {
|
||||||
|
@ -48,7 +39,7 @@ export class N8nOutputFixingParser extends BaseOutputParser {
|
||||||
try {
|
try {
|
||||||
// First attempt to parse the completion
|
// First attempt to parse the completion
|
||||||
const response = await this.outputParser.parse(completion, callbacks, (e) => e);
|
const response = await this.outputParser.parse(completion, callbacks, (e) => e);
|
||||||
void logAiEvent(this.context, 'ai-output-parsed', { text: completion, response });
|
logAiEvent(this.context, 'ai-output-parsed', { text: completion, response });
|
||||||
|
|
||||||
this.context.addOutputData(NodeConnectionType.AiOutputParser, index, [
|
this.context.addOutputData(NodeConnectionType.AiOutputParser, index, [
|
||||||
[{ json: { action: 'parse', response } }],
|
[{ json: { action: 'parse', response } }],
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type { Callbacks } from '@langchain/core/callbacks/manager';
|
import type { Callbacks } from '@langchain/core/callbacks/manager';
|
||||||
import { StructuredOutputParser } from 'langchain/output_parsers';
|
import { StructuredOutputParser } from 'langchain/output_parsers';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import type { IExecuteFunctions } from 'n8n-workflow';
|
import type { ISupplyDataFunctions } from 'n8n-workflow';
|
||||||
import { NodeConnectionType, NodeOperationError } from 'n8n-workflow';
|
import { NodeConnectionType, NodeOperationError } from 'n8n-workflow';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
@ -14,11 +14,11 @@ const STRUCTURED_OUTPUT_ARRAY_KEY = '__structured__output__array';
|
||||||
export class N8nStructuredOutputParser extends StructuredOutputParser<
|
export class N8nStructuredOutputParser extends StructuredOutputParser<
|
||||||
z.ZodType<object, z.ZodTypeDef, object>
|
z.ZodType<object, z.ZodTypeDef, object>
|
||||||
> {
|
> {
|
||||||
context: IExecuteFunctions;
|
constructor(
|
||||||
|
private context: ISupplyDataFunctions,
|
||||||
constructor(context: IExecuteFunctions, zodSchema: z.ZodSchema<object>) {
|
zodSchema: z.ZodSchema<object>,
|
||||||
|
) {
|
||||||
super(zodSchema);
|
super(zodSchema);
|
||||||
this.context = context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lc_namespace = ['langchain', 'output_parsers', 'structured'];
|
lc_namespace = ['langchain', 'output_parsers', 'structured'];
|
||||||
|
@ -39,7 +39,7 @@ export class N8nStructuredOutputParser extends StructuredOutputParser<
|
||||||
get(parsed, STRUCTURED_OUTPUT_KEY) ??
|
get(parsed, STRUCTURED_OUTPUT_KEY) ??
|
||||||
parsed) as Record<string, unknown>;
|
parsed) as Record<string, unknown>;
|
||||||
|
|
||||||
void logAiEvent(this.context, 'ai-output-parsed', { text, response: result });
|
logAiEvent(this.context, 'ai-output-parsed', { text, response: result });
|
||||||
|
|
||||||
this.context.addOutputData(NodeConnectionType.AiOutputParser, index, [
|
this.context.addOutputData(NodeConnectionType.AiOutputParser, index, [
|
||||||
[{ json: { action: 'parse', response: result } }],
|
[{ json: { action: 'parse', response: result } }],
|
||||||
|
@ -56,7 +56,7 @@ export class N8nStructuredOutputParser extends StructuredOutputParser<
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
void logAiEvent(this.context, 'ai-output-parsed', {
|
logAiEvent(this.context, 'ai-output-parsed', {
|
||||||
text,
|
text,
|
||||||
response: e.message ?? e,
|
response: e.message ?? e,
|
||||||
});
|
});
|
||||||
|
@ -73,7 +73,7 @@ export class N8nStructuredOutputParser extends StructuredOutputParser<
|
||||||
static async fromZodJsonSchema(
|
static async fromZodJsonSchema(
|
||||||
zodSchema: z.ZodSchema<object>,
|
zodSchema: z.ZodSchema<object>,
|
||||||
nodeVersion: number,
|
nodeVersion: number,
|
||||||
context: IExecuteFunctions,
|
context: ISupplyDataFunctions,
|
||||||
): Promise<N8nStructuredOutputParser> {
|
): Promise<N8nStructuredOutputParser> {
|
||||||
let returnSchema: z.ZodType<object, z.ZodTypeDef, object>;
|
let returnSchema: z.ZodType<object, z.ZodTypeDef, object>;
|
||||||
if (nodeVersion === 1) {
|
if (nodeVersion === 1) {
|
||||||
|
|
|
@ -22,9 +22,11 @@
|
||||||
"dist/**/*"
|
"dist/**/*"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@n8n/config": "workspace:*",
|
||||||
"n8n-workflow": "workspace:*",
|
"n8n-workflow": "workspace:*",
|
||||||
"n8n-core": "workspace:*",
|
"n8n-core": "workspace:*",
|
||||||
"nanoid": "^3.3.6",
|
"nanoid": "^3.3.6",
|
||||||
|
"typedi": "catalog:",
|
||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
16
packages/@n8n/task-runner/src/config/base-runner-config.ts
Normal file
16
packages/@n8n/task-runner/src/config/base-runner-config.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { Config, Env } from '@n8n/config';
|
||||||
|
|
||||||
|
@Config
|
||||||
|
export class BaseRunnerConfig {
|
||||||
|
@Env('N8N_RUNNERS_N8N_URI')
|
||||||
|
n8nUri: string = '127.0.0.1:5679';
|
||||||
|
|
||||||
|
@Env('N8N_RUNNERS_GRANT_TOKEN')
|
||||||
|
grantToken: string = '';
|
||||||
|
|
||||||
|
@Env('N8N_RUNNERS_MAX_PAYLOAD')
|
||||||
|
maxPayloadSize: number = 1024 * 1024 * 1024;
|
||||||
|
|
||||||
|
@Env('N8N_RUNNERS_MAX_CONCURRENCY')
|
||||||
|
maxConcurrency: number = 5;
|
||||||
|
}
|
10
packages/@n8n/task-runner/src/config/js-runner-config.ts
Normal file
10
packages/@n8n/task-runner/src/config/js-runner-config.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { Config, Env } from '@n8n/config';
|
||||||
|
|
||||||
|
@Config
|
||||||
|
export class JsRunnerConfig {
|
||||||
|
@Env('NODE_FUNCTION_ALLOW_BUILTIN')
|
||||||
|
allowedBuiltInModules: string = '';
|
||||||
|
|
||||||
|
@Env('NODE_FUNCTION_ALLOW_EXTERNAL')
|
||||||
|
allowedExternalModules: string = '';
|
||||||
|
}
|
13
packages/@n8n/task-runner/src/config/main-config.ts
Normal file
13
packages/@n8n/task-runner/src/config/main-config.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { Config, Nested } from '@n8n/config';
|
||||||
|
|
||||||
|
import { BaseRunnerConfig } from './base-runner-config';
|
||||||
|
import { JsRunnerConfig } from './js-runner-config';
|
||||||
|
|
||||||
|
@Config
|
||||||
|
export class MainConfig {
|
||||||
|
@Nested
|
||||||
|
baseRunnerConfig!: BaseRunnerConfig;
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
jsRunnerConfig!: JsRunnerConfig;
|
||||||
|
}
|
|
@ -4,7 +4,6 @@ import fs from 'node:fs';
|
||||||
import { builtinModules } from 'node:module';
|
import { builtinModules } from 'node:module';
|
||||||
|
|
||||||
import { ValidationError } from '@/js-task-runner/errors/validation-error';
|
import { ValidationError } from '@/js-task-runner/errors/validation-error';
|
||||||
import type { JsTaskRunnerOpts } from '@/js-task-runner/js-task-runner';
|
|
||||||
import {
|
import {
|
||||||
JsTaskRunner,
|
JsTaskRunner,
|
||||||
type AllCodeTaskData,
|
type AllCodeTaskData,
|
||||||
|
@ -13,17 +12,27 @@ import {
|
||||||
import type { Task } from '@/task-runner';
|
import type { Task } from '@/task-runner';
|
||||||
|
|
||||||
import { newAllCodeTaskData, newTaskWithSettings, withPairedItem, wrapIntoJson } from './test-data';
|
import { newAllCodeTaskData, newTaskWithSettings, withPairedItem, wrapIntoJson } from './test-data';
|
||||||
|
import type { JsRunnerConfig } from '../../config/js-runner-config';
|
||||||
|
import { MainConfig } from '../../config/main-config';
|
||||||
import { ExecutionError } from '../errors/execution-error';
|
import { ExecutionError } from '../errors/execution-error';
|
||||||
|
|
||||||
jest.mock('ws');
|
jest.mock('ws');
|
||||||
|
|
||||||
|
const defaultConfig = new MainConfig();
|
||||||
|
|
||||||
describe('JsTaskRunner', () => {
|
describe('JsTaskRunner', () => {
|
||||||
const createRunnerWithOpts = (opts: Partial<JsTaskRunnerOpts> = {}) =>
|
const createRunnerWithOpts = (opts: Partial<JsRunnerConfig> = {}) =>
|
||||||
new JsTaskRunner({
|
new JsTaskRunner({
|
||||||
wsUrl: 'ws://localhost',
|
baseRunnerConfig: {
|
||||||
grantToken: 'grantToken',
|
...defaultConfig.baseRunnerConfig,
|
||||||
maxConcurrency: 1,
|
grantToken: 'grantToken',
|
||||||
...opts,
|
maxConcurrency: 1,
|
||||||
|
n8nUri: 'localhost',
|
||||||
|
},
|
||||||
|
jsRunnerConfig: {
|
||||||
|
...defaultConfig.jsRunnerConfig,
|
||||||
|
...opts,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultTaskRunner = createRunnerWithOpts();
|
const defaultTaskRunner = createRunnerWithOpts();
|
||||||
|
|
|
@ -30,6 +30,7 @@ import { makeSerializable } from './errors/serializable-error';
|
||||||
import type { RequireResolver } from './require-resolver';
|
import type { RequireResolver } from './require-resolver';
|
||||||
import { createRequireResolver } from './require-resolver';
|
import { createRequireResolver } from './require-resolver';
|
||||||
import { validateRunForAllItemsOutput, validateRunForEachItemOutput } from './result-validation';
|
import { validateRunForAllItemsOutput, validateRunForEachItemOutput } from './result-validation';
|
||||||
|
import type { MainConfig } from '../config/main-config';
|
||||||
|
|
||||||
export interface JSExecSettings {
|
export interface JSExecSettings {
|
||||||
code: string;
|
code: string;
|
||||||
|
@ -76,23 +77,6 @@ export interface AllCodeTaskData {
|
||||||
additionalData: PartialAdditionalData;
|
additionalData: PartialAdditionalData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JsTaskRunnerOpts {
|
|
||||||
wsUrl: string;
|
|
||||||
grantToken: string;
|
|
||||||
maxConcurrency: number;
|
|
||||||
name?: string;
|
|
||||||
/**
|
|
||||||
* List of built-in nodejs modules that are allowed to be required in the
|
|
||||||
* execution sandbox. Asterisk (*) can be used to allow all.
|
|
||||||
*/
|
|
||||||
allowedBuiltInModules?: string;
|
|
||||||
/**
|
|
||||||
* List of npm modules that are allowed to be required in the execution
|
|
||||||
* sandbox. Asterisk (*) can be used to allow all.
|
|
||||||
*/
|
|
||||||
allowedExternalModules?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
type CustomConsole = {
|
type CustomConsole = {
|
||||||
log: (...args: unknown[]) => void;
|
log: (...args: unknown[]) => void;
|
||||||
};
|
};
|
||||||
|
@ -100,22 +84,20 @@ type CustomConsole = {
|
||||||
export class JsTaskRunner extends TaskRunner {
|
export class JsTaskRunner extends TaskRunner {
|
||||||
private readonly requireResolver: RequireResolver;
|
private readonly requireResolver: RequireResolver;
|
||||||
|
|
||||||
constructor({
|
constructor(config: MainConfig, name = 'JS Task Runner') {
|
||||||
grantToken,
|
super({
|
||||||
maxConcurrency,
|
taskType: 'javascript',
|
||||||
wsUrl,
|
name,
|
||||||
name = 'JS Task Runner',
|
...config.baseRunnerConfig,
|
||||||
allowedBuiltInModules,
|
});
|
||||||
allowedExternalModules,
|
const { jsRunnerConfig } = config;
|
||||||
}: JsTaskRunnerOpts) {
|
|
||||||
super('javascript', wsUrl, grantToken, maxConcurrency, name);
|
|
||||||
|
|
||||||
const parseModuleAllowList = (moduleList: string) =>
|
const parseModuleAllowList = (moduleList: string) =>
|
||||||
moduleList === '*' ? null : new Set(moduleList.split(',').map((x) => x.trim()));
|
moduleList === '*' ? null : new Set(moduleList.split(',').map((x) => x.trim()));
|
||||||
|
|
||||||
this.requireResolver = createRequireResolver({
|
this.requireResolver = createRequireResolver({
|
||||||
allowedBuiltInModules: parseModuleAllowList(allowedBuiltInModules ?? ''),
|
allowedBuiltInModules: parseModuleAllowList(jsRunnerConfig.allowedBuiltInModules ?? ''),
|
||||||
allowedExternalModules: parseModuleAllowList(allowedExternalModules ?? ''),
|
allowedExternalModules: parseModuleAllowList(jsRunnerConfig.allowedExternalModules ?? ''),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,12 @@
|
||||||
import { ApplicationError, ensureError } from 'n8n-workflow';
|
import { ensureError } from 'n8n-workflow';
|
||||||
|
import Container from 'typedi';
|
||||||
|
|
||||||
|
import { MainConfig } from './config/main-config';
|
||||||
import { JsTaskRunner } from './js-task-runner/js-task-runner';
|
import { JsTaskRunner } from './js-task-runner/js-task-runner';
|
||||||
|
|
||||||
let runner: JsTaskRunner | undefined;
|
let runner: JsTaskRunner | undefined;
|
||||||
let isShuttingDown = false;
|
let isShuttingDown = false;
|
||||||
|
|
||||||
type Config = {
|
|
||||||
n8nUri: string;
|
|
||||||
grantToken: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
function readAndParseConfig(): Config {
|
|
||||||
const grantToken = process.env.N8N_RUNNERS_GRANT_TOKEN;
|
|
||||||
if (!grantToken) {
|
|
||||||
throw new ApplicationError('Missing N8N_RUNNERS_GRANT_TOKEN environment variable');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
n8nUri: process.env.N8N_RUNNERS_N8N_URI ?? '127.0.0.1:5679',
|
|
||||||
grantToken,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createSignalHandler(signal: string) {
|
function createSignalHandler(signal: string) {
|
||||||
return async function onSignal() {
|
return async function onSignal() {
|
||||||
if (isShuttingDown) {
|
if (isShuttingDown) {
|
||||||
|
@ -46,16 +31,9 @@ function createSignalHandler(signal: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void (async function start() {
|
void (async function start() {
|
||||||
const config = readAndParseConfig();
|
const config = Container.get(MainConfig);
|
||||||
|
|
||||||
const wsUrl = `ws://${config.n8nUri}/runners/_ws`;
|
runner = new JsTaskRunner(config);
|
||||||
runner = new JsTaskRunner({
|
|
||||||
wsUrl,
|
|
||||||
grantToken: config.grantToken,
|
|
||||||
maxConcurrency: 5,
|
|
||||||
allowedBuiltInModules: process.env.NODE_FUNCTION_ALLOW_BUILTIN,
|
|
||||||
allowedExternalModules: process.env.NODE_FUNCTION_ALLOW_EXTERNAL,
|
|
||||||
});
|
|
||||||
|
|
||||||
process.on('SIGINT', createSignalHandler('SIGINT'));
|
process.on('SIGINT', createSignalHandler('SIGINT'));
|
||||||
process.on('SIGTERM', createSignalHandler('SIGTERM'));
|
process.on('SIGTERM', createSignalHandler('SIGTERM'));
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { ApplicationError, type INodeTypeDescription } from 'n8n-workflow';
|
import { ApplicationError, type INodeTypeDescription } from 'n8n-workflow';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { URL } from 'node:url';
|
|
||||||
import { type MessageEvent, WebSocket } from 'ws';
|
import { type MessageEvent, WebSocket } from 'ws';
|
||||||
|
|
||||||
|
import type { BaseRunnerConfig } from './config/base-runner-config';
|
||||||
import { TaskRunnerNodeTypes } from './node-types';
|
import { TaskRunnerNodeTypes } from './node-types';
|
||||||
import {
|
import {
|
||||||
RPC_ALLOW_LIST,
|
RPC_ALLOW_LIST,
|
||||||
|
@ -42,7 +42,10 @@ export interface RPCCallObject {
|
||||||
const VALID_TIME_MS = 1000;
|
const VALID_TIME_MS = 1000;
|
||||||
const VALID_EXTRA_MS = 100;
|
const VALID_EXTRA_MS = 100;
|
||||||
|
|
||||||
const DEFAULT_MAX_PAYLOAD_SIZE = 1024 * 1024 * 1024;
|
export interface TaskRunnerOpts extends BaseRunnerConfig {
|
||||||
|
taskType: string;
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export abstract class TaskRunner {
|
export abstract class TaskRunner {
|
||||||
id: string = nanoid();
|
id: string = nanoid();
|
||||||
|
@ -63,22 +66,23 @@ export abstract class TaskRunner {
|
||||||
|
|
||||||
nodeTypes: TaskRunnerNodeTypes = new TaskRunnerNodeTypes([]);
|
nodeTypes: TaskRunnerNodeTypes = new TaskRunnerNodeTypes([]);
|
||||||
|
|
||||||
constructor(
|
taskType: string;
|
||||||
public taskType: string,
|
|
||||||
wsUrl: string,
|
maxConcurrency: number;
|
||||||
grantToken: string,
|
|
||||||
private maxConcurrency: number,
|
name: string;
|
||||||
public name?: string,
|
|
||||||
) {
|
constructor(opts: TaskRunnerOpts) {
|
||||||
const url = new URL(wsUrl);
|
this.taskType = opts.taskType;
|
||||||
url.searchParams.append('id', this.id);
|
this.name = opts.name ?? 'Node.js Task Runner SDK';
|
||||||
this.ws = new WebSocket(url.toString(), {
|
this.maxConcurrency = opts.maxConcurrency;
|
||||||
|
|
||||||
|
const wsUrl = `ws://${opts.n8nUri}/runners/_ws?id=${this.id}`;
|
||||||
|
this.ws = new WebSocket(wsUrl, {
|
||||||
headers: {
|
headers: {
|
||||||
authorization: `Bearer ${grantToken}`,
|
authorization: `Bearer ${opts.grantToken}`,
|
||||||
},
|
},
|
||||||
maxPayload: process.env.N8N_RUNNERS_MAX_PAYLOAD
|
maxPayload: opts.maxPayloadSize,
|
||||||
? parseInt(process.env.N8N_RUNNERS_MAX_PAYLOAD)
|
|
||||||
: DEFAULT_MAX_PAYLOAD_SIZE,
|
|
||||||
});
|
});
|
||||||
this.ws.addEventListener('message', this.receiveMessage);
|
this.ws.addEventListener('message', this.receiveMessage);
|
||||||
this.ws.addEventListener('close', this.stopTaskOffers);
|
this.ws.addEventListener('close', this.stopTaskOffers);
|
||||||
|
@ -145,7 +149,7 @@ export abstract class TaskRunner {
|
||||||
case 'broker:inforequest':
|
case 'broker:inforequest':
|
||||||
this.send({
|
this.send({
|
||||||
type: 'runner:info',
|
type: 'runner:info',
|
||||||
name: this.name ?? 'Node.js Task Runner SDK',
|
name: this.name,
|
||||||
types: [this.taskType],
|
types: [this.taskType],
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
"extends": ["../../../tsconfig.json", "../../../tsconfig.backend.json"],
|
"extends": ["../../../tsconfig.json", "../../../tsconfig.backend.json"],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
"baseUrl": "src",
|
"baseUrl": "src",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./*"]
|
"@/*": ["./*"]
|
||||||
|
|
|
@ -17,14 +17,16 @@ const MOCK_ACTIVATION_KEY = 'activation-key';
|
||||||
const MOCK_FEATURE_FLAG = 'feat:sharing';
|
const MOCK_FEATURE_FLAG = 'feat:sharing';
|
||||||
const MOCK_MAIN_PLAN_ID = '1b765dc4-d39d-4ffe-9885-c56dd67c4b26';
|
const MOCK_MAIN_PLAN_ID = '1b765dc4-d39d-4ffe-9885-c56dd67c4b26';
|
||||||
|
|
||||||
describe('License', () => {
|
const licenseConfig: GlobalConfig['license'] = {
|
||||||
beforeAll(() => {
|
serverUrl: MOCK_SERVER_URL,
|
||||||
config.set('license.serverUrl', MOCK_SERVER_URL);
|
autoRenewalEnabled: true,
|
||||||
config.set('license.autoRenewEnabled', true);
|
autoRenewOffset: MOCK_RENEW_OFFSET,
|
||||||
config.set('license.autoRenewOffset', MOCK_RENEW_OFFSET);
|
activationKey: MOCK_ACTIVATION_KEY,
|
||||||
config.set('license.tenantId', 1);
|
tenantId: 1,
|
||||||
});
|
cert: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('License', () => {
|
||||||
let license: License;
|
let license: License;
|
||||||
const instanceSettings = mock<InstanceSettings>({
|
const instanceSettings = mock<InstanceSettings>({
|
||||||
instanceId: MOCK_INSTANCE_ID,
|
instanceId: MOCK_INSTANCE_ID,
|
||||||
|
@ -32,7 +34,10 @@ describe('License', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const globalConfig = mock<GlobalConfig>({ multiMainSetup: { enabled: false } });
|
const globalConfig = mock<GlobalConfig>({
|
||||||
|
license: licenseConfig,
|
||||||
|
multiMainSetup: { enabled: false },
|
||||||
|
});
|
||||||
license = new License(mockLogger(), instanceSettings, mock(), mock(), mock(), globalConfig);
|
license = new License(mockLogger(), instanceSettings, mock(), mock(), mock(), globalConfig);
|
||||||
await license.init();
|
await license.init();
|
||||||
});
|
});
|
||||||
|
@ -66,7 +71,7 @@ describe('License', () => {
|
||||||
mock(),
|
mock(),
|
||||||
mock(),
|
mock(),
|
||||||
mock(),
|
mock(),
|
||||||
mock(),
|
mock<GlobalConfig>({ license: licenseConfig }),
|
||||||
);
|
);
|
||||||
await license.init();
|
await license.init();
|
||||||
expect(LicenseManager).toHaveBeenCalledWith(
|
expect(LicenseManager).toHaveBeenCalledWith(
|
||||||
|
@ -192,17 +197,23 @@ describe('License', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('License', () => {
|
describe('License', () => {
|
||||||
beforeEach(() => {
|
|
||||||
config.load(config.default);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('init', () => {
|
describe('init', () => {
|
||||||
describe('in single-main setup', () => {
|
describe('in single-main setup', () => {
|
||||||
describe('with `license.autoRenewEnabled` enabled', () => {
|
describe('with `license.autoRenewEnabled` enabled', () => {
|
||||||
it('should enable renewal', async () => {
|
it('should enable renewal', async () => {
|
||||||
const globalConfig = mock<GlobalConfig>({ multiMainSetup: { enabled: false } });
|
const globalConfig = mock<GlobalConfig>({
|
||||||
|
license: licenseConfig,
|
||||||
|
multiMainSetup: { enabled: false },
|
||||||
|
});
|
||||||
|
|
||||||
await new License(mockLogger(), mock(), mock(), mock(), mock(), globalConfig).init();
|
await new License(
|
||||||
|
mockLogger(),
|
||||||
|
mock<InstanceSettings>({ instanceType: 'main' }),
|
||||||
|
mock(),
|
||||||
|
mock(),
|
||||||
|
mock(),
|
||||||
|
globalConfig,
|
||||||
|
).init();
|
||||||
|
|
||||||
expect(LicenseManager).toHaveBeenCalledWith(
|
expect(LicenseManager).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({ autoRenewEnabled: true, renewOnInit: true }),
|
expect.objectContaining({ autoRenewEnabled: true, renewOnInit: true }),
|
||||||
|
@ -212,9 +223,14 @@ describe('License', () => {
|
||||||
|
|
||||||
describe('with `license.autoRenewEnabled` disabled', () => {
|
describe('with `license.autoRenewEnabled` disabled', () => {
|
||||||
it('should disable renewal', async () => {
|
it('should disable renewal', async () => {
|
||||||
config.set('license.autoRenewEnabled', false);
|
await new License(
|
||||||
|
mockLogger(),
|
||||||
await new License(mockLogger(), mock(), mock(), mock(), mock(), mock()).init();
|
mock<InstanceSettings>({ instanceType: 'main' }),
|
||||||
|
mock(),
|
||||||
|
mock(),
|
||||||
|
mock(),
|
||||||
|
mock(),
|
||||||
|
).init();
|
||||||
|
|
||||||
expect(LicenseManager).toHaveBeenCalledWith(
|
expect(LicenseManager).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({ autoRenewEnabled: false, renewOnInit: false }),
|
expect.objectContaining({ autoRenewEnabled: false, renewOnInit: false }),
|
||||||
|
@ -228,9 +244,11 @@ describe('License', () => {
|
||||||
test.each(['unset', 'leader', 'follower'])(
|
test.each(['unset', 'leader', 'follower'])(
|
||||||
'if %s status, should disable removal',
|
'if %s status, should disable removal',
|
||||||
async (status) => {
|
async (status) => {
|
||||||
const globalConfig = mock<GlobalConfig>({ multiMainSetup: { enabled: true } });
|
const globalConfig = mock<GlobalConfig>({
|
||||||
|
license: { ...licenseConfig, autoRenewalEnabled: false },
|
||||||
|
multiMainSetup: { enabled: true },
|
||||||
|
});
|
||||||
config.set('multiMainSetup.instanceType', status);
|
config.set('multiMainSetup.instanceType', status);
|
||||||
config.set('license.autoRenewEnabled', false);
|
|
||||||
|
|
||||||
await new License(mockLogger(), mock(), mock(), mock(), mock(), globalConfig).init();
|
await new License(mockLogger(), mock(), mock(), mock(), mock(), globalConfig).init();
|
||||||
|
|
||||||
|
@ -243,9 +261,11 @@ describe('License', () => {
|
||||||
|
|
||||||
describe('with `license.autoRenewEnabled` enabled', () => {
|
describe('with `license.autoRenewEnabled` enabled', () => {
|
||||||
test.each(['unset', 'follower'])('if %s status, should disable removal', async (status) => {
|
test.each(['unset', 'follower'])('if %s status, should disable removal', async (status) => {
|
||||||
const globalConfig = mock<GlobalConfig>({ multiMainSetup: { enabled: true } });
|
const globalConfig = mock<GlobalConfig>({
|
||||||
|
license: { ...licenseConfig, autoRenewalEnabled: false },
|
||||||
|
multiMainSetup: { enabled: true },
|
||||||
|
});
|
||||||
config.set('multiMainSetup.instanceType', status);
|
config.set('multiMainSetup.instanceType', status);
|
||||||
config.set('license.autoRenewEnabled', false);
|
|
||||||
|
|
||||||
await new License(mockLogger(), mock(), mock(), mock(), mock(), globalConfig).init();
|
await new License(mockLogger(), mock(), mock(), mock(), mock(), globalConfig).init();
|
||||||
|
|
||||||
|
@ -255,7 +275,10 @@ describe('License', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('if leader status, should enable renewal', async () => {
|
it('if leader status, should enable renewal', async () => {
|
||||||
const globalConfig = mock<GlobalConfig>({ multiMainSetup: { enabled: true } });
|
const globalConfig = mock<GlobalConfig>({
|
||||||
|
license: licenseConfig,
|
||||||
|
multiMainSetup: { enabled: true },
|
||||||
|
});
|
||||||
config.set('multiMainSetup.instanceType', 'leader');
|
config.set('multiMainSetup.instanceType', 'leader');
|
||||||
|
|
||||||
await new License(mockLogger(), mock(), mock(), mock(), mock(), globalConfig).init();
|
await new License(mockLogger(), mock(), mock(), mock(), mock(), globalConfig).init();
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
import { SecurityConfig } from '@n8n/config';
|
||||||
import { Flags } from '@oclif/core';
|
import { Flags } from '@oclif/core';
|
||||||
import { ApplicationError } from 'n8n-workflow';
|
import { ApplicationError } from 'n8n-workflow';
|
||||||
import { Container } from 'typedi';
|
import { Container } from 'typedi';
|
||||||
|
|
||||||
import config from '@/config';
|
|
||||||
import { RISK_CATEGORIES } from '@/security-audit/constants';
|
import { RISK_CATEGORIES } from '@/security-audit/constants';
|
||||||
import { SecurityAuditService } from '@/security-audit/security-audit.service';
|
import { SecurityAuditService } from '@/security-audit/security-audit.service';
|
||||||
import type { Risk } from '@/security-audit/types';
|
import type { Risk } from '@/security-audit/types';
|
||||||
|
@ -26,7 +26,7 @@ export class SecurityAudit extends BaseCommand {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
'days-abandoned-workflow': Flags.integer({
|
'days-abandoned-workflow': Flags.integer({
|
||||||
default: config.getEnv('security.audit.daysAbandonedWorkflow'),
|
default: Container.get(SecurityConfig).daysAbandonedWorkflow,
|
||||||
description: 'Days for a workflow to be considered abandoned if not executed',
|
description: 'Days for a workflow to be considered abandoned if not executed',
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
|
@ -274,7 +274,7 @@ export abstract class BaseCommand extends Command {
|
||||||
this.license = Container.get(License);
|
this.license = Container.get(License);
|
||||||
await this.license.init();
|
await this.license.init();
|
||||||
|
|
||||||
const activationKey = config.getEnv('license.activationKey');
|
const { activationKey } = this.globalConfig.license;
|
||||||
|
|
||||||
if (activationKey) {
|
if (activationKey) {
|
||||||
const hasCert = (await this.license.loadCertStr()).length > 0;
|
const hasCert = (await this.license.loadCertStr()).length > 0;
|
||||||
|
|
|
@ -199,7 +199,7 @@ export class Start extends BaseCommand {
|
||||||
await this.initOrchestration();
|
await this.initOrchestration();
|
||||||
this.logger.debug('Orchestration init complete');
|
this.logger.debug('Orchestration init complete');
|
||||||
|
|
||||||
if (!config.getEnv('license.autoRenewEnabled') && this.instanceSettings.isLeader) {
|
if (!this.globalConfig.license.autoRenewalEnabled && this.instanceSettings.isLeader) {
|
||||||
this.logger.warn(
|
this.logger.warn(
|
||||||
'Automatic license renewal is disabled. The license will not renew automatically, and access to licensed features may be lost!',
|
'Automatic license renewal is disabled. The license will not renew automatically, and access to licensed features may be lost!',
|
||||||
);
|
);
|
||||||
|
|
|
@ -187,29 +187,6 @@ export const schema = {
|
||||||
doc: 'Public URL where the editor is accessible. Also used for emails sent from n8n.',
|
doc: 'Public URL where the editor is accessible. Also used for emails sent from n8n.',
|
||||||
},
|
},
|
||||||
|
|
||||||
security: {
|
|
||||||
restrictFileAccessTo: {
|
|
||||||
doc: 'If set only files in that directories can be accessed. Multiple directories can be separated by semicolon (";").',
|
|
||||||
format: String,
|
|
||||||
default: '',
|
|
||||||
env: 'N8N_RESTRICT_FILE_ACCESS_TO',
|
|
||||||
},
|
|
||||||
blockFileAccessToN8nFiles: {
|
|
||||||
doc: 'If set to true it will block access to all files in the ".n8n" directory, the static cache dir at ~/.cache/n8n/public, and user defined config files.',
|
|
||||||
format: Boolean,
|
|
||||||
default: true,
|
|
||||||
env: 'N8N_BLOCK_FILE_ACCESS_TO_N8N_FILES',
|
|
||||||
},
|
|
||||||
audit: {
|
|
||||||
daysAbandonedWorkflow: {
|
|
||||||
doc: 'Days for a workflow to be considered abandoned if not executed',
|
|
||||||
format: Number,
|
|
||||||
default: 90,
|
|
||||||
env: 'N8N_SECURITY_AUDIT_DAYS_ABANDONED_WORKFLOW',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
workflowTagsDisabled: {
|
workflowTagsDisabled: {
|
||||||
format: Boolean,
|
format: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
|
@ -411,45 +388,6 @@ export const schema = {
|
||||||
env: 'N8N_DEFAULT_LOCALE',
|
env: 'N8N_DEFAULT_LOCALE',
|
||||||
},
|
},
|
||||||
|
|
||||||
license: {
|
|
||||||
serverUrl: {
|
|
||||||
format: String,
|
|
||||||
default: 'https://license.n8n.io/v1',
|
|
||||||
env: 'N8N_LICENSE_SERVER_URL',
|
|
||||||
doc: 'License server url to retrieve license.',
|
|
||||||
},
|
|
||||||
autoRenewEnabled: {
|
|
||||||
format: Boolean,
|
|
||||||
default: true,
|
|
||||||
env: 'N8N_LICENSE_AUTO_RENEW_ENABLED',
|
|
||||||
doc: 'Whether auto renewal for licenses is enabled.',
|
|
||||||
},
|
|
||||||
autoRenewOffset: {
|
|
||||||
format: Number,
|
|
||||||
default: 60 * 60 * 72, // 72 hours
|
|
||||||
env: 'N8N_LICENSE_AUTO_RENEW_OFFSET',
|
|
||||||
doc: 'How many seconds before expiry a license should get automatically renewed. ',
|
|
||||||
},
|
|
||||||
activationKey: {
|
|
||||||
format: String,
|
|
||||||
default: '',
|
|
||||||
env: 'N8N_LICENSE_ACTIVATION_KEY',
|
|
||||||
doc: 'Activation key to initialize license',
|
|
||||||
},
|
|
||||||
tenantId: {
|
|
||||||
format: Number,
|
|
||||||
default: 1,
|
|
||||||
env: 'N8N_LICENSE_TENANT_ID',
|
|
||||||
doc: 'Tenant id used by the license manager',
|
|
||||||
},
|
|
||||||
cert: {
|
|
||||||
format: String,
|
|
||||||
default: '',
|
|
||||||
env: 'N8N_LICENSE_CERT',
|
|
||||||
doc: 'Ephemeral license certificate',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
hideUsagePage: {
|
hideUsagePage: {
|
||||||
format: Boolean,
|
format: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
|
|
|
@ -1061,6 +1061,7 @@ describe('TelemetryEventRelay', () => {
|
||||||
describe('Community+ registered', () => {
|
describe('Community+ registered', () => {
|
||||||
it('should track `license-community-plus-registered` event', () => {
|
it('should track `license-community-plus-registered` event', () => {
|
||||||
const event: RelayEventMap['license-community-plus-registered'] = {
|
const event: RelayEventMap['license-community-plus-registered'] = {
|
||||||
|
userId: 'user123',
|
||||||
email: 'user@example.com',
|
email: 'user@example.com',
|
||||||
licenseKey: 'license123',
|
licenseKey: 'license123',
|
||||||
};
|
};
|
||||||
|
@ -1068,6 +1069,7 @@ describe('TelemetryEventRelay', () => {
|
||||||
eventService.emit('license-community-plus-registered', event);
|
eventService.emit('license-community-plus-registered', event);
|
||||||
|
|
||||||
expect(telemetry.track).toHaveBeenCalledWith('User registered for license community plus', {
|
expect(telemetry.track).toHaveBeenCalledWith('User registered for license community plus', {
|
||||||
|
user_id: 'user123',
|
||||||
email: 'user@example.com',
|
email: 'user@example.com',
|
||||||
licenseKey: 'license123',
|
licenseKey: 'license123',
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,7 +8,7 @@ import type {
|
||||||
|
|
||||||
import type { AuthProviderType } from '@/databases/entities/auth-identity';
|
import type { AuthProviderType } from '@/databases/entities/auth-identity';
|
||||||
import type { ProjectRole } from '@/databases/entities/project-relation';
|
import type { ProjectRole } from '@/databases/entities/project-relation';
|
||||||
import type { GlobalRole } from '@/databases/entities/user';
|
import type { GlobalRole, User } from '@/databases/entities/user';
|
||||||
import type { IWorkflowDb } from '@/interfaces';
|
import type { IWorkflowDb } from '@/interfaces';
|
||||||
|
|
||||||
import type { AiEventMap } from './ai.event-map';
|
import type { AiEventMap } from './ai.event-map';
|
||||||
|
@ -421,6 +421,7 @@ export type RelayEventMap = {
|
||||||
};
|
};
|
||||||
|
|
||||||
'license-community-plus-registered': {
|
'license-community-plus-registered': {
|
||||||
|
userId: User['id'];
|
||||||
email: string;
|
email: string;
|
||||||
licenseKey: string;
|
licenseKey: string;
|
||||||
};
|
};
|
||||||
|
|
|
@ -236,10 +236,12 @@ export class TelemetryEventRelay extends EventRelay {
|
||||||
}
|
}
|
||||||
|
|
||||||
private licenseCommunityPlusRegistered({
|
private licenseCommunityPlusRegistered({
|
||||||
|
userId,
|
||||||
email,
|
email,
|
||||||
licenseKey,
|
licenseKey,
|
||||||
}: RelayEventMap['license-community-plus-registered']) {
|
}: RelayEventMap['license-community-plus-registered']) {
|
||||||
this.telemetry.track('User registered for license community plus', {
|
this.telemetry.track('User registered for license community plus', {
|
||||||
|
user_id: userId,
|
||||||
email,
|
email,
|
||||||
licenseKey,
|
licenseKey,
|
||||||
});
|
});
|
||||||
|
@ -778,7 +780,7 @@ export class TelemetryEventRelay extends EventRelay {
|
||||||
ldap_allowed: authenticationMethod === 'ldap',
|
ldap_allowed: authenticationMethod === 'ldap',
|
||||||
saml_enabled: authenticationMethod === 'saml',
|
saml_enabled: authenticationMethod === 'saml',
|
||||||
license_plan_name: this.license.getPlanName(),
|
license_plan_name: this.license.getPlanName(),
|
||||||
license_tenant_id: config.getEnv('license.tenantId'),
|
license_tenant_id: this.globalConfig.license.tenantId,
|
||||||
binary_data_s3: isS3Available && isS3Selected && isS3Licensed,
|
binary_data_s3: isS3Available && isS3Selected && isS3Licensed,
|
||||||
multi_main_setup_enabled: this.globalConfig.multiMainSetup.enabled,
|
multi_main_setup_enabled: this.globalConfig.multiMainSetup.enabled,
|
||||||
metrics: {
|
metrics: {
|
||||||
|
|
|
@ -48,8 +48,7 @@ export class License {
|
||||||
*/
|
*/
|
||||||
private renewalEnabled() {
|
private renewalEnabled() {
|
||||||
if (this.instanceSettings.instanceType !== 'main') return false;
|
if (this.instanceSettings.instanceType !== 'main') return false;
|
||||||
|
const autoRenewEnabled = this.globalConfig.license.autoRenewalEnabled;
|
||||||
const autoRenewEnabled = config.getEnv('license.autoRenewEnabled');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In multi-main setup, all mains start off with `unset` status and so renewal disabled.
|
* In multi-main setup, all mains start off with `unset` status and so renewal disabled.
|
||||||
|
@ -75,9 +74,9 @@ export class License {
|
||||||
|
|
||||||
const { instanceType } = this.instanceSettings;
|
const { instanceType } = this.instanceSettings;
|
||||||
const isMainInstance = instanceType === 'main';
|
const isMainInstance = instanceType === 'main';
|
||||||
const server = config.getEnv('license.serverUrl');
|
const server = this.globalConfig.license.serverUrl;
|
||||||
const offlineMode = !isMainInstance;
|
const offlineMode = !isMainInstance;
|
||||||
const autoRenewOffset = config.getEnv('license.autoRenewOffset');
|
const autoRenewOffset = this.globalConfig.license.autoRenewOffset;
|
||||||
const saveCertStr = isMainInstance
|
const saveCertStr = isMainInstance
|
||||||
? async (value: TLicenseBlock) => await this.saveCertStr(value)
|
? async (value: TLicenseBlock) => await this.saveCertStr(value)
|
||||||
: async () => {};
|
: async () => {};
|
||||||
|
@ -96,7 +95,7 @@ export class License {
|
||||||
try {
|
try {
|
||||||
this.manager = new LicenseManager({
|
this.manager = new LicenseManager({
|
||||||
server,
|
server,
|
||||||
tenantId: config.getEnv('license.tenantId'),
|
tenantId: this.globalConfig.license.tenantId,
|
||||||
productIdentifier: `n8n-${N8N_VERSION}`,
|
productIdentifier: `n8n-${N8N_VERSION}`,
|
||||||
autoRenewEnabled: renewalEnabled,
|
autoRenewEnabled: renewalEnabled,
|
||||||
renewOnInit: renewalEnabled,
|
renewOnInit: renewalEnabled,
|
||||||
|
@ -122,7 +121,7 @@ export class License {
|
||||||
|
|
||||||
async loadCertStr(): Promise<TLicenseBlock> {
|
async loadCertStr(): Promise<TLicenseBlock> {
|
||||||
// if we have an ephemeral license, we don't want to load it from the database
|
// if we have an ephemeral license, we don't want to load it from the database
|
||||||
const ephemeralLicense = config.get('license.cert');
|
const ephemeralLicense = this.globalConfig.license.cert;
|
||||||
if (ephemeralLicense) {
|
if (ephemeralLicense) {
|
||||||
return ephemeralLicense;
|
return ephemeralLicense;
|
||||||
}
|
}
|
||||||
|
@ -179,7 +178,7 @@ export class License {
|
||||||
|
|
||||||
async saveCertStr(value: TLicenseBlock): Promise<void> {
|
async saveCertStr(value: TLicenseBlock): Promise<void> {
|
||||||
// if we have an ephemeral license, we don't want to save it to the database
|
// if we have an ephemeral license, we don't want to save it to the database
|
||||||
if (config.get('license.cert')) return;
|
if (this.globalConfig.license.cert) return;
|
||||||
await this.settingsRepository.upsert(
|
await this.settingsRepository.upsert(
|
||||||
{
|
{
|
||||||
key: SETTINGS_LICENSE_CERT_KEY,
|
key: SETTINGS_LICENSE_CERT_KEY,
|
||||||
|
|
|
@ -94,6 +94,7 @@ describe('LicenseService', () => {
|
||||||
.spyOn(axios, 'post')
|
.spyOn(axios, 'post')
|
||||||
.mockResolvedValueOnce({ data: { title: 'Title', text: 'Text', licenseKey: 'abc-123' } });
|
.mockResolvedValueOnce({ data: { title: 'Title', text: 'Text', licenseKey: 'abc-123' } });
|
||||||
const data = await licenseService.registerCommunityEdition({
|
const data = await licenseService.registerCommunityEdition({
|
||||||
|
userId: '123',
|
||||||
email: 'test@ema.il',
|
email: 'test@ema.il',
|
||||||
instanceId: '123',
|
instanceId: '123',
|
||||||
instanceUrl: 'http://localhost',
|
instanceUrl: 'http://localhost',
|
||||||
|
@ -102,6 +103,7 @@ describe('LicenseService', () => {
|
||||||
|
|
||||||
expect(data).toEqual({ title: 'Title', text: 'Text' });
|
expect(data).toEqual({ title: 'Title', text: 'Text' });
|
||||||
expect(eventService.emit).toHaveBeenCalledWith('license-community-plus-registered', {
|
expect(eventService.emit).toHaveBeenCalledWith('license-community-plus-registered', {
|
||||||
|
userId: '123',
|
||||||
email: 'test@ema.il',
|
email: 'test@ema.il',
|
||||||
licenseKey: 'abc-123',
|
licenseKey: 'abc-123',
|
||||||
});
|
});
|
||||||
|
@ -111,6 +113,7 @@ describe('LicenseService', () => {
|
||||||
jest.spyOn(axios, 'post').mockRejectedValueOnce(new AxiosError('Failed'));
|
jest.spyOn(axios, 'post').mockRejectedValueOnce(new AxiosError('Failed'));
|
||||||
await expect(
|
await expect(
|
||||||
licenseService.registerCommunityEdition({
|
licenseService.registerCommunityEdition({
|
||||||
|
userId: '123',
|
||||||
email: 'test@ema.il',
|
email: 'test@ema.il',
|
||||||
instanceId: '123',
|
instanceId: '123',
|
||||||
instanceUrl: 'http://localhost',
|
instanceUrl: 'http://localhost',
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { InstanceSettings } from 'n8n-core';
|
||||||
|
|
||||||
import { Get, Post, RestController, GlobalScope, Body } from '@/decorators';
|
import { Get, Post, RestController, GlobalScope, Body } from '@/decorators';
|
||||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
import { AuthenticatedRequest, AuthlessRequest, LicenseRequest } from '@/requests';
|
import { AuthenticatedRequest, LicenseRequest } from '@/requests';
|
||||||
import { UrlService } from '@/services/url.service';
|
import { UrlService } from '@/services/url.service';
|
||||||
|
|
||||||
import { LicenseService } from './license.service';
|
import { LicenseService } from './license.service';
|
||||||
|
@ -41,11 +41,12 @@ export class LicenseController {
|
||||||
|
|
||||||
@Post('/enterprise/community-registered')
|
@Post('/enterprise/community-registered')
|
||||||
async registerCommunityEdition(
|
async registerCommunityEdition(
|
||||||
_req: AuthlessRequest,
|
req: AuthenticatedRequest,
|
||||||
_res: Response,
|
_res: Response,
|
||||||
@Body payload: CommunityRegisteredRequestDto,
|
@Body payload: CommunityRegisteredRequestDto,
|
||||||
) {
|
) {
|
||||||
return await this.licenseService.registerCommunityEdition({
|
return await this.licenseService.registerCommunityEdition({
|
||||||
|
userId: req.user.id,
|
||||||
email: payload.email,
|
email: payload.email,
|
||||||
instanceId: this.instanceSettings.instanceId,
|
instanceId: this.instanceSettings.instanceId,
|
||||||
instanceUrl: this.urlService.getInstanceBaseUrl(),
|
instanceUrl: this.urlService.getInstanceBaseUrl(),
|
||||||
|
|
|
@ -61,11 +61,13 @@ export class LicenseService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async registerCommunityEdition({
|
async registerCommunityEdition({
|
||||||
|
userId,
|
||||||
email,
|
email,
|
||||||
instanceId,
|
instanceId,
|
||||||
instanceUrl,
|
instanceUrl,
|
||||||
licenseType,
|
licenseType,
|
||||||
}: {
|
}: {
|
||||||
|
userId: User['id'];
|
||||||
email: string;
|
email: string;
|
||||||
instanceId: string;
|
instanceId: string;
|
||||||
instanceUrl: string;
|
instanceUrl: string;
|
||||||
|
@ -83,7 +85,7 @@ export class LicenseService {
|
||||||
licenseType,
|
licenseType,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
this.eventService.emit('license-community-plus-registered', { email, licenseKey });
|
this.eventService.emit('license-community-plus-registered', { userId, email, licenseKey });
|
||||||
return rest;
|
return rest;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof AxiosError) {
|
if (e instanceof AxiosError) {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue