-
Notifications
You must be signed in to change notification settings - Fork 9.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(HTTP Request Tool Node): Use DynamicStructuredTool with models s…
…upporting it (no-changelog) (#10246)
- Loading branch information
1 parent
fa17391
commit a936680
Showing
11 changed files
with
382 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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' }); | ||
}); | ||
}); |
Oops, something went wrong.