fix: Simplify Structured Output Parser wrapping and fix auto-fixing output parser (#8778)

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
This commit is contained in:
oleg 2024-03-01 08:41:45 +01:00 committed by GitHub
parent 08e2b068fb
commit 7d82dc1ea8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 49 additions and 27 deletions

View file

@ -41,6 +41,7 @@ class N8nStructuredOutputParser<T extends z.ZodTypeAny> extends StructuredOutput
static fromZedJsonSchema(
schema: JSONSchema7,
nodeVersion: number,
): StructuredOutputParser<z.ZodType<object, z.ZodTypeDef, object>> {
// Make sure to remove the description from root schema
const { description, ...restOfSchema } = schema;
@ -51,7 +52,9 @@ class N8nStructuredOutputParser<T extends z.ZodTypeAny> extends StructuredOutput
// eslint-disable-next-line @typescript-eslint/no-implied-eval
const itemSchema = new Function('z', `return (${zodSchemaString})`)(z) as z.ZodSchema<object>;
const returnSchema = z.object({
let returnSchema: z.ZodSchema<object>;
if (nodeVersion === 1) {
returnSchema = z.object({
[STRUCTURED_OUTPUT_KEY]: z
.object({
[STRUCTURED_OUTPUT_OBJECT_KEY]: itemSchema.optional(),
@ -75,6 +78,11 @@ class N8nStructuredOutputParser<T extends z.ZodTypeAny> extends StructuredOutput
},
),
});
} else {
returnSchema = z.object({
output: itemSchema.optional(),
});
}
return N8nStructuredOutputParser.fromZodSchema(returnSchema);
}
@ -85,7 +93,8 @@ export class OutputParserStructured implements INodeType {
name: 'outputParserStructured',
icon: 'fa:code',
group: ['transform'],
version: 1,
version: [1, 1.1],
defaultVersion: 1.1,
description: 'Return data in a defined JSON format',
defaults: {
name: 'Structured Output Parser',
@ -152,11 +161,20 @@ export class OutputParserStructured implements INodeType {
let itemSchema: JSONSchema7;
try {
itemSchema = jsonParse<JSONSchema7>(schema);
// If the 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(this.getNode(), 'Error during parsing of JSON Schema.');
}
const parser = N8nStructuredOutputParser.fromZedJsonSchema(itemSchema);
const nodeVersion = this.getNode().typeVersion;
const parser = N8nStructuredOutputParser.fromZedJsonSchema(itemSchema, nodeVersion);
return {
response: logWrapper(parser, this),

View file

@ -18,7 +18,7 @@ import { BaseChatMemory } from 'langchain/memory';
import type { MemoryVariables } from 'langchain/dist/memory/base';
import { BaseRetriever } from 'langchain/schema/retriever';
import type { FormatInstructionsOptions } from 'langchain/schema/output_parser';
import { BaseOutputParser } from 'langchain/schema/output_parser';
import { BaseOutputParser, OutputParserException } from 'langchain/schema/output_parser';
import { isObject } from 'lodash';
import { N8nJsonLoader } from './N8nJsonLoader';
import { N8nBinaryLoader } from './N8nBinaryLoader';
@ -44,6 +44,10 @@ export async function callMethodAsync<T>(
try {
return await parameters.method.call(this, ...parameters.arguments);
} catch (e) {
// Langchain checks for OutputParserException to run retry chain
// for auto-fixing the output so skip wrapping in this case
if (e instanceof OutputParserException) throw e;
// Propagate errors from sub-nodes
if (e.functionality === 'configuration-node') throw e;
const connectedNode = parameters.executeFunctions.getNode();