diff --git a/langchain/src/output_parsers/structured.ts b/langchain/src/output_parsers/structured.ts index 94e5523645a7..a1c3226f7563 100644 --- a/langchain/src/output_parsers/structured.ts +++ b/langchain/src/output_parsers/structured.ts @@ -106,10 +106,14 @@ ${JSON.stringify(zodToJsonSchema(this.schema))} : text.trim(); return await this.schema.parseAsync(JSON.parse(json)); } catch (e) { - throw new OutputParserException( - `Failed to parse. Text: "${text}". Error: ${e}`, - text - ); + try { + return await this.schema.parseAsync(JSON.parse(text.trim())); + } catch (e2) { + throw new OutputParserException( + `Failed to parse. Text: "${text}". Error: ${e2}`, + text + ); + } } } } diff --git a/langchain/src/output_parsers/tests/structured.test.ts b/langchain/src/output_parsers/tests/structured.test.ts new file mode 100644 index 000000000000..95970830b3c9 --- /dev/null +++ b/langchain/src/output_parsers/tests/structured.test.ts @@ -0,0 +1,101 @@ +import { expect, test } from "@jest/globals"; +import { z } from "zod"; +import { StructuredOutputParser } from "../structured.js"; + +test("StructuredOutputParser handles valid JSON wrapped in triple backticks", async () => { + const parser = StructuredOutputParser.fromZodSchema( + z.object({ + name: z.string().describe("Human name"), + age: z.number().describe("Human age"), + }) + ); + const text = '```json\n{"name": "John Doe", "age": 30}```'; + + const result = await parser.parse(text); + + expect(result).toHaveProperty("name", "John Doe"); + expect(result).toHaveProperty("age", 30); +}); + +test("StructuredOutputParser handles JSON without triple backticks", async () => { + const parser = StructuredOutputParser.fromZodSchema( + z.object({ + name: z.string().describe("Human name"), + age: z.number().describe("Human age"), + }) + ); + const text = '{"name": "John Doe", "age": 30}'; + + const result = await parser.parse(text); + + expect(result).toHaveProperty("name", "John Doe"); + expect(result).toHaveProperty("age", 30); +}); + +test("StructuredOutputParser throws error for invalid JSON wrapped in triple backticks", async () => { + const parser = StructuredOutputParser.fromZodSchema( + z.object({ + name: z.string().describe("Human name"), + age: z.number().describe("Human age"), + }) + ); + // Invalid JSON + const text = '```json\n{"name": "John Doe", "age": }```'; + + await expect(parser.parse(text)).rejects.toThrow("Failed to parse"); +}); + +test("StructuredOutputParser throws error for normal text without triple backticks", async () => { + const parser = StructuredOutputParser.fromZodSchema( + z.object({ + name: z.string().describe("Human name"), + age: z.number().describe("Human age"), + }) + ); + // Invalid JSON + const text = "This is just a plain text without JSON."; + + await expect(parser.parse(text)).rejects.toThrow("Failed to parse"); +}); + +test("StructuredOutputParser handles JSON with backticks inside text but not at the start", async () => { + const parser = StructuredOutputParser.fromZodSchema( + z.object({ + name: z.string().describe("Human name"), + age: z.number().describe("Human age"), + }) + ); + const text = 'Some random text ```json\n{"name": "John Doe", "age": 30}```'; + const result = await parser.parse(text); + + expect(result).toHaveProperty("name", "John Doe"); + expect(result).toHaveProperty("age", 30); +}); + +test("StructuredOutputParser handles JSON with backticks inside JSON", async () => { + const parser = StructuredOutputParser.fromZodSchema( + z.object({ + name: z.string().describe("Human name"), + age: z.number().describe("Human age"), + }) + ); + const text = '{"name": "John ```Doe```", "age": 30}'; + + const result = await parser.parse(text); + + expect(result).toHaveProperty("name", "John ```Doe```"); + expect(result).toHaveProperty("age", 30); +}); + +test("StructuredOutputParser throws error for JSON with backticks both inside and outside the JSON", async () => { + const parser = StructuredOutputParser.fromZodSchema( + z.object({ + name: z.string().describe("Human name"), + age: z.number().describe("Human age"), + }) + ); + const text = + 'Some random text ```json\n{"name": "John ```Doe```", "age": 30}```'; + + await expect(parser.parse(text)).rejects.toThrow("Failed to parse"); +});