Skip to content

Commit

Permalink
fix(langchain): Fix structured parser with triple backticks, adds tes…
Browse files Browse the repository at this point in the history
…ts (#7199)

Co-authored-by: Harris <[email protected]>
  • Loading branch information
johnguirgis and Harris authored Nov 17, 2024
1 parent 7124f18 commit 986ab14
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 4 deletions.
12 changes: 8 additions & 4 deletions langchain/src/output_parsers/structured.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
}
}
}
}
Expand Down
101 changes: 101 additions & 0 deletions langchain/src/output_parsers/tests/structured.test.ts
Original file line number Diff line number Diff line change
@@ -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");
});

0 comments on commit 986ab14

Please sign in to comment.