mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 09:36:44 +00:00
feat(Custom n8n Workflow Tool Node): Add support for tool input schema (#9470)
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
This commit is contained in:
@@ -1,5 +1,70 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const schemaTypeField: INodeProperties = {
|
||||
displayName: 'Schema Type',
|
||||
name: 'schemaType',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Generate From JSON Example',
|
||||
value: 'fromJson',
|
||||
description: 'Generate a schema from an example JSON object',
|
||||
},
|
||||
{
|
||||
name: 'Define Below',
|
||||
value: 'manual',
|
||||
description: 'Define the JSON schema manually',
|
||||
},
|
||||
],
|
||||
default: 'fromJson',
|
||||
description: 'How to specify the schema for the function',
|
||||
};
|
||||
|
||||
export const jsonSchemaExampleField: INodeProperties = {
|
||||
displayName: 'JSON Example',
|
||||
name: 'jsonSchemaExample',
|
||||
type: 'json',
|
||||
default: `{
|
||||
"some_input": "some_value"
|
||||
}`,
|
||||
noDataExpression: true,
|
||||
typeOptions: {
|
||||
rows: 10,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
schemaType: ['fromJson'],
|
||||
},
|
||||
},
|
||||
description: 'Example JSON object to use to generate the schema',
|
||||
};
|
||||
|
||||
export const inputSchemaField: INodeProperties = {
|
||||
displayName: 'Input Schema',
|
||||
name: 'inputSchema',
|
||||
type: 'json',
|
||||
default: `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"some_input": {
|
||||
"type": "string",
|
||||
"description": "Some input to the function"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
noDataExpression: true,
|
||||
typeOptions: {
|
||||
rows: 10,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
schemaType: ['manual'],
|
||||
},
|
||||
},
|
||||
description: 'Schema to use for the function',
|
||||
};
|
||||
|
||||
export const promptTypeOptions: INodeProperties = {
|
||||
displayName: 'Prompt',
|
||||
name: 'promptType',
|
||||
|
||||
81
packages/@n8n/nodes-langchain/utils/schemaParsing.ts
Normal file
81
packages/@n8n/nodes-langchain/utils/schemaParsing.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { makeResolverFromLegacyOptions } from '@n8n/vm2';
|
||||
import { json as generateJsonSchema } from 'generate-schema';
|
||||
import type { SchemaObject } from 'generate-schema';
|
||||
import type { JSONSchema7 } from 'json-schema';
|
||||
import { JavaScriptSandbox } from 'n8n-nodes-base/dist/nodes/Code/JavaScriptSandbox';
|
||||
import { getSandboxContext } from 'n8n-nodes-base/dist/nodes/Code/Sandbox';
|
||||
import type { IExecuteFunctions } from 'n8n-workflow';
|
||||
import { NodeOperationError, jsonParse } from 'n8n-workflow';
|
||||
|
||||
const vmResolver = makeResolverFromLegacyOptions({
|
||||
external: {
|
||||
modules: ['json-schema-to-zod', 'zod'],
|
||||
transitive: false,
|
||||
},
|
||||
resolve(moduleName, parentDirname) {
|
||||
if (moduleName === 'json-schema-to-zod') {
|
||||
return require.resolve(
|
||||
'@n8n/n8n-nodes-langchain/node_modules/json-schema-to-zod/dist/cjs/jsonSchemaToZod.js',
|
||||
{
|
||||
paths: [parentDirname],
|
||||
},
|
||||
);
|
||||
}
|
||||
if (moduleName === 'zod') {
|
||||
return require.resolve('@n8n/n8n-nodes-langchain/node_modules/zod.cjs', {
|
||||
paths: [parentDirname],
|
||||
});
|
||||
}
|
||||
return;
|
||||
},
|
||||
builtin: [],
|
||||
});
|
||||
|
||||
export function getSandboxWithZod(ctx: IExecuteFunctions, schema: JSONSchema7, itemIndex: number) {
|
||||
const context = getSandboxContext.call(ctx, itemIndex);
|
||||
let itemSchema: JSONSchema7 = schema;
|
||||
try {
|
||||
// If the root type is not defined, we assume it's an object
|
||||
if (itemSchema.type === undefined) {
|
||||
itemSchema = {
|
||||
type: 'object',
|
||||
properties: itemSchema.properties ?? (itemSchema as { [key: string]: JSONSchema7 }),
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
throw new NodeOperationError(ctx.getNode(), 'Error during parsing of JSON Schema.');
|
||||
}
|
||||
|
||||
// Make sure to remove the description from root schema
|
||||
const { description, ...restOfSchema } = itemSchema;
|
||||
const sandboxedSchema = new JavaScriptSandbox(
|
||||
context,
|
||||
`
|
||||
const { z } = require('zod');
|
||||
const { parseSchema } = require('json-schema-to-zod');
|
||||
const zodSchema = parseSchema(${JSON.stringify(restOfSchema)});
|
||||
const itemSchema = new Function('z', 'return (' + zodSchema + ')')(z)
|
||||
return itemSchema
|
||||
`,
|
||||
itemIndex,
|
||||
ctx.helpers,
|
||||
{ resolver: vmResolver },
|
||||
);
|
||||
return sandboxedSchema;
|
||||
}
|
||||
|
||||
export function generateSchema(schemaString: string): JSONSchema7 {
|
||||
const parsedSchema = jsonParse<SchemaObject>(schemaString);
|
||||
|
||||
return generateJsonSchema(parsedSchema) as JSONSchema7;
|
||||
}
|
||||
|
||||
export function throwIfToolSchema(ctx: IExecuteFunctions, error: Error) {
|
||||
if (error?.message?.includes('tool input did not match expected schema')) {
|
||||
throw new NodeOperationError(
|
||||
ctx.getNode(),
|
||||
`${error.message}.
|
||||
This is most likely because some of your tools are configured to require a specific schema. This is not supported by Conversational Agent. Remove the schema from the tool configuration or use Tools agent instead.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user