diff --git a/packages/components/nodes/outputparsers/StructuredOutputParser/StructuredOutputParser.ts b/packages/components/nodes/outputparsers/StructuredOutputParser/StructuredOutputParser.ts index 5162730d..85110e71 100644 --- a/packages/components/nodes/outputparsers/StructuredOutputParser/StructuredOutputParser.ts +++ b/packages/components/nodes/outputparsers/StructuredOutputParser/StructuredOutputParser.ts @@ -3,6 +3,7 @@ import { BaseOutputParser } from '@langchain/core/output_parsers' import { StructuredOutputParser as LangchainStructuredOutputParser } from 'langchain/output_parsers' import { CATEGORY } from '../OutputParserHelpers' import { convertSchemaToZod, getBaseClasses, INode, INodeData, INodeParams } from '../../../src' +import { jsonrepair } from 'jsonrepair' class StructuredOutputParser implements INode { label: string @@ -74,6 +75,14 @@ class StructuredOutputParser implements INode { const zodSchema = z.object(convertSchemaToZod(jsonStructure)) as any const structuredOutputParser = LangchainStructuredOutputParser.fromZodSchema(zodSchema) + const baseParse = structuredOutputParser.parse + + // Fix broken JSON from LLM + structuredOutputParser.parse = (text) => { + const jsonString = text.includes('```') ? text.trim().split(/```(?:json)?/)[1] : text.trim() + return baseParse.call(structuredOutputParser, jsonrepair(jsonString)) + } + // NOTE: When we change Flowise to return a json response, the following has to be changed to: JsonStructuredOutputParser Object.defineProperty(structuredOutputParser, 'autoFix', { enumerable: true, diff --git a/packages/components/nodes/outputparsers/StructuredOutputParserAdvanced/StructuredOutputParserAdvanced.ts b/packages/components/nodes/outputparsers/StructuredOutputParserAdvanced/StructuredOutputParserAdvanced.ts index 69c45ff0..e9f55964 100644 --- a/packages/components/nodes/outputparsers/StructuredOutputParserAdvanced/StructuredOutputParserAdvanced.ts +++ b/packages/components/nodes/outputparsers/StructuredOutputParserAdvanced/StructuredOutputParserAdvanced.ts @@ -3,6 +3,7 @@ import { BaseOutputParser } from '@langchain/core/output_parsers' import { StructuredOutputParser as LangchainStructuredOutputParser } from 'langchain/output_parsers' import { CATEGORY } from '../OutputParserHelpers' import { z } from 'zod' +import { jsonrepair } from 'jsonrepair' class AdvancedStructuredOutputParser implements INode { label: string @@ -62,6 +63,14 @@ class AdvancedStructuredOutputParser implements INode { try { const structuredOutputParser = LangchainStructuredOutputParser.fromZodSchema(zodSchema) + const baseParse = structuredOutputParser.parse + + // Fix broken JSON from LLM + structuredOutputParser.parse = (text) => { + const jsonString = text.includes('```') ? text.trim().split(/```(?:json)?/)[1] : text.trim() + return baseParse.call(structuredOutputParser, jsonrepair(jsonString)) + } + // NOTE: When we change Flowise to return a json response, the following has to be changed to: JsonStructuredOutputParser Object.defineProperty(structuredOutputParser, 'autoFix', { enumerable: true, diff --git a/packages/components/package.json b/packages/components/package.json index e567bc00..5653c5bc 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -92,6 +92,7 @@ "ioredis": "^5.3.2", "jsdom": "^22.1.0", "jsonpointer": "^5.0.1", + "jsonrepair": "^3.11.1", "langchain": "^0.3.5", "langfuse": "3.3.4", "langfuse-langchain": "^3.3.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c74613e3..e485cf68 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -319,6 +319,9 @@ importers: jsonpointer: specifier: ^5.0.1 version: 5.0.1 + jsonrepair: + specifier: ^3.11.1 + version: 3.11.2 langchain: specifier: ^0.3.5 version: 0.3.5(@langchain/anthropic@0.3.7(@langchain/core@0.3.18(openai@4.57.3(encoding@0.1.13)(zod@3.22.4)))(encoding@0.1.13))(@langchain/aws@0.1.2(@aws-sdk/client-sso-oidc@3.624.0(@aws-sdk/client-sts@3.624.0))(@aws-sdk/client-sts@3.624.0)(@langchain/core@0.3.18(openai@4.57.3(encoding@0.1.13)(zod@3.22.4))))(@langchain/cohere@0.0.7(encoding@0.1.13)(openai@4.57.3(encoding@0.1.13)(zod@3.22.4)))(@langchain/core@0.3.18(openai@4.57.3(encoding@0.1.13)(zod@3.22.4)))(@langchain/google-genai@0.1.3(@langchain/core@0.3.18(openai@4.57.3(encoding@0.1.13)(zod@3.22.4)))(zod@3.22.4))(@langchain/google-vertexai@0.1.2(@langchain/core@0.3.18(openai@4.57.3(encoding@0.1.13)(zod@3.22.4)))(encoding@0.1.13)(zod@3.22.4))(@langchain/groq@0.1.2(@langchain/core@0.3.18(openai@4.57.3(encoding@0.1.13)(zod@3.22.4)))(encoding@0.1.13))(@langchain/mistralai@0.0.26(encoding@0.1.13)(openai@4.57.3(encoding@0.1.13)(zod@3.22.4)))(@langchain/ollama@0.1.2(@langchain/core@0.3.18(openai@4.57.3(encoding@0.1.13)(zod@3.22.4))))(axios@1.6.2)(cheerio@1.0.0-rc.12)(encoding@0.1.13)(openai@4.57.3(encoding@0.1.13)(zod@3.22.4))(typeorm@0.3.20(ioredis@5.3.2)(mongodb@6.3.0(gcp-metadata@6.1.0(encoding@0.1.13))(socks@2.8.1))(mysql2@3.11.4)(pg@8.11.3)(redis@4.6.13)(sqlite3@5.1.7)(ts-node@10.9.2(@swc/core@1.4.6)(@types/node@20.12.12)(typescript@5.5.2))) @@ -10807,6 +10810,10 @@ packages: resolution: { integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== } engines: { node: '>=0.10.0' } + jsonrepair@3.11.2: + resolution: { integrity: sha512-ejydGcTq0qKk1r0NUBwjtvswbPFhs19+QEfwSeGwB8KJZ59W7/AOFmQh04c68mkJ+2hGk+OkOmkr2bKG4tGlLQ== } + hasBin: true + jsonwebtoken@9.0.2: resolution: { integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== } engines: { node: '>=12', npm: '>=6' } @@ -29873,6 +29880,8 @@ snapshots: jsonpointer@5.0.1: {} + jsonrepair@3.11.2: {} + jsonwebtoken@9.0.2: dependencies: jws: 3.2.2