mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
feat(HTTP Request Tool Node): Use DynamicStructuredTool with models supporting it (no-changelog) (#10246)
This commit is contained in:
169
packages/@n8n/nodes-langchain/utils/N8nTool.test.ts
Normal file
169
packages/@n8n/nodes-langchain/utils/N8nTool.test.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import { N8nTool } from './N8nTool';
|
||||
import { createMockExecuteFunction } from 'n8n-nodes-base/test/nodes/Helpers';
|
||||
import { z } from 'zod';
|
||||
import type { INode } from 'n8n-workflow';
|
||||
import { DynamicStructuredTool, DynamicTool } from '@langchain/core/tools';
|
||||
|
||||
const mockNode: INode = {
|
||||
id: '1',
|
||||
name: 'Mock node',
|
||||
typeVersion: 2,
|
||||
type: 'n8n-nodes-base.mock',
|
||||
position: [60, 760],
|
||||
parameters: {
|
||||
operation: 'test',
|
||||
},
|
||||
};
|
||||
|
||||
describe('Test N8nTool wrapper as DynamicStructuredTool', () => {
|
||||
it('should wrap a tool', () => {
|
||||
const func = jest.fn();
|
||||
|
||||
const ctx = createMockExecuteFunction({}, mockNode);
|
||||
|
||||
const tool = new N8nTool(ctx, {
|
||||
name: 'Dummy Tool',
|
||||
description: 'A dummy tool for testing',
|
||||
func,
|
||||
schema: z.object({
|
||||
foo: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
expect(tool).toBeInstanceOf(DynamicStructuredTool);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test N8nTool wrapper - DynamicTool fallback', () => {
|
||||
it('should convert the tool to a dynamic tool', () => {
|
||||
const func = jest.fn();
|
||||
|
||||
const ctx = createMockExecuteFunction({}, mockNode);
|
||||
|
||||
const tool = new N8nTool(ctx, {
|
||||
name: 'Dummy Tool',
|
||||
description: 'A dummy tool for testing',
|
||||
func,
|
||||
schema: z.object({
|
||||
foo: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
const dynamicTool = tool.asDynamicTool();
|
||||
|
||||
expect(dynamicTool).toBeInstanceOf(DynamicTool);
|
||||
});
|
||||
|
||||
it('should format fallback description correctly', () => {
|
||||
const func = jest.fn();
|
||||
|
||||
const ctx = createMockExecuteFunction({}, mockNode);
|
||||
|
||||
const tool = new N8nTool(ctx, {
|
||||
name: 'Dummy Tool',
|
||||
description: 'A dummy tool for testing',
|
||||
func,
|
||||
schema: z.object({
|
||||
foo: z.string(),
|
||||
bar: z.number().optional(),
|
||||
qwe: z.boolean().describe('Boolean description'),
|
||||
}),
|
||||
});
|
||||
|
||||
const dynamicTool = tool.asDynamicTool();
|
||||
|
||||
expect(dynamicTool.description).toContain('foo: (description: , type: string, required: true)');
|
||||
expect(dynamicTool.description).toContain(
|
||||
'bar: (description: , type: number, required: false)',
|
||||
);
|
||||
|
||||
expect(dynamicTool.description).toContain(
|
||||
'qwe: (description: Boolean description, type: boolean, required: true)',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle empty parameter list correctly', () => {
|
||||
const func = jest.fn();
|
||||
|
||||
const ctx = createMockExecuteFunction({}, mockNode);
|
||||
|
||||
const tool = new N8nTool(ctx, {
|
||||
name: 'Dummy Tool',
|
||||
description: 'A dummy tool for testing',
|
||||
func,
|
||||
schema: z.object({}),
|
||||
});
|
||||
|
||||
const dynamicTool = tool.asDynamicTool();
|
||||
|
||||
expect(dynamicTool.description).toEqual('A dummy tool for testing');
|
||||
});
|
||||
|
||||
it('should parse correct parameters', async () => {
|
||||
const func = jest.fn();
|
||||
|
||||
const ctx = createMockExecuteFunction({}, mockNode);
|
||||
|
||||
const tool = new N8nTool(ctx, {
|
||||
name: 'Dummy Tool',
|
||||
description: 'A dummy tool for testing',
|
||||
func,
|
||||
schema: z.object({
|
||||
foo: z.string().describe('Foo description'),
|
||||
bar: z.number().optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
const dynamicTool = tool.asDynamicTool();
|
||||
|
||||
const testParameters = { foo: 'some value' };
|
||||
|
||||
await dynamicTool.func(JSON.stringify(testParameters));
|
||||
|
||||
expect(func).toHaveBeenCalledWith(testParameters);
|
||||
});
|
||||
|
||||
it('should recover when 1 parameter is passed directly', async () => {
|
||||
const func = jest.fn();
|
||||
|
||||
const ctx = createMockExecuteFunction({}, mockNode);
|
||||
|
||||
const tool = new N8nTool(ctx, {
|
||||
name: 'Dummy Tool',
|
||||
description: 'A dummy tool for testing',
|
||||
func,
|
||||
schema: z.object({
|
||||
foo: z.string().describe('Foo description'),
|
||||
}),
|
||||
});
|
||||
|
||||
const dynamicTool = tool.asDynamicTool();
|
||||
|
||||
const testParameter = 'some value';
|
||||
|
||||
await dynamicTool.func(testParameter);
|
||||
|
||||
expect(func).toHaveBeenCalledWith({ foo: testParameter });
|
||||
});
|
||||
|
||||
it('should recover when JS object is passed instead of JSON', async () => {
|
||||
const func = jest.fn();
|
||||
|
||||
const ctx = createMockExecuteFunction({}, mockNode);
|
||||
|
||||
const tool = new N8nTool(ctx, {
|
||||
name: 'Dummy Tool',
|
||||
description: 'A dummy tool for testing',
|
||||
func,
|
||||
schema: z.object({
|
||||
foo: z.string().describe('Foo description'),
|
||||
}),
|
||||
});
|
||||
|
||||
const dynamicTool = tool.asDynamicTool();
|
||||
|
||||
await dynamicTool.func('{ foo: "some value" }');
|
||||
|
||||
expect(func).toHaveBeenCalledWith({ foo: 'some value' });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user