-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
core[minor]: Add XML output parser (#4258)
* core[minor]: Add XML output parser * cr * docs * chore: lint files * cr * streaming & docs * cr * chore: lint files
- Loading branch information
1 parent
8ae8fe3
commit cbd3e96
Showing
9 changed files
with
511 additions
and
2 deletions.
There are no files selected for viewing
29 changes: 29 additions & 0 deletions
29
docs/core_docs/docs/modules/model_io/output_parsers/types/xml.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# XML output parser | ||
|
||
The `XMLOutputParser` takes language model output which contains XML and parses it into a JSON object. | ||
|
||
The output parser also supports streaming outputs. | ||
|
||
Currently, the XML parser does not contain support for self closing tags, or attributes on tags. | ||
|
||
## Usage | ||
|
||
import CodeBlock from "@theme/CodeBlock"; | ||
|
||
import IntegrationInstallTooltip from "@mdx_components/integration_install_tooltip.mdx"; | ||
|
||
<IntegrationInstallTooltip></IntegrationInstallTooltip> | ||
|
||
```bash npm2yarn | ||
npm install @langchain/core | ||
``` | ||
|
||
import XMLExample from "@examples/prompts/xml_output_parser.ts"; | ||
|
||
<CodeBlock language="typescript">{XMLExample}</CodeBlock> | ||
|
||
## Streaming | ||
|
||
import XMLStreamingExample from "@examples/prompts/xml_output_parser_streaming.ts"; | ||
|
||
<CodeBlock language="typescript">{XMLStreamingExample}</CodeBlock> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { XMLOutputParser } from "@langchain/core/output_parsers"; | ||
|
||
const XML_EXAMPLE = `<?xml version="1.0" encoding="UTF-8"?> | ||
<userProfile> | ||
<userID>12345</userID> | ||
<name>John Doe</name> | ||
<email>john.doe@example.com</email> | ||
<roles> | ||
<role>Admin</role> | ||
<role>User</role> | ||
</roles> | ||
<preferences> | ||
<theme>Dark</theme> | ||
<notifications> | ||
<email>true</email> | ||
<sms>false</sms> | ||
</notifications> | ||
</preferences> | ||
</userProfile>`; | ||
|
||
const parser = new XMLOutputParser(); | ||
|
||
const result = await parser.invoke(XML_EXAMPLE); | ||
|
||
console.log(JSON.stringify(result, null, 2)); | ||
/* | ||
{ | ||
"userProfile": [ | ||
{ | ||
"userID": "12345" | ||
}, | ||
{ | ||
"name": "John Doe" | ||
}, | ||
{ | ||
"email": "john.doe@example.com" | ||
}, | ||
{ | ||
"roles": [ | ||
{ | ||
"role": "Admin" | ||
}, | ||
{ | ||
"role": "User" | ||
} | ||
] | ||
}, | ||
{ | ||
"preferences": [ | ||
{ | ||
"theme": "Dark" | ||
}, | ||
{ | ||
"notifications": [ | ||
{ | ||
"email": "true" | ||
}, | ||
{ | ||
"sms": "false" | ||
} | ||
] | ||
} | ||
] | ||
} | ||
] | ||
} | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import { XMLOutputParser } from "@langchain/core/output_parsers"; | ||
import { FakeStreamingLLM } from "@langchain/core/utils/testing"; | ||
|
||
const XML_EXAMPLE = `<?xml version="1.0" encoding="UTF-8"?> | ||
<userProfile> | ||
<userID>12345</userID> | ||
<roles> | ||
<role>Admin</role> | ||
<role>User</role> | ||
</roles> | ||
</userProfile>`; | ||
|
||
const parser = new XMLOutputParser(); | ||
|
||
// Define your LLM, in this example we'll use demo streaming LLM | ||
const streamingLLM = new FakeStreamingLLM({ | ||
responses: [XML_EXAMPLE], | ||
}).pipe(parser); // Pipe the parser to the LLM | ||
|
||
const stream = await streamingLLM.stream(XML_EXAMPLE); | ||
for await (const chunk of stream) { | ||
console.log(JSON.stringify(chunk, null, 2)); | ||
} | ||
/* | ||
{} | ||
{ | ||
"userProfile": "" | ||
} | ||
{ | ||
"userProfile": "\n" | ||
} | ||
{ | ||
"userProfile": [ | ||
{ | ||
"userID": "" | ||
} | ||
] | ||
} | ||
{ | ||
"userProfile": [ | ||
{ | ||
"userID": "123" | ||
} | ||
] | ||
} | ||
{ | ||
"userProfile": [ | ||
{ | ||
"userID": "12345" | ||
}, | ||
{ | ||
"roles": "" | ||
} | ||
] | ||
} | ||
{ | ||
"userProfile": [ | ||
{ | ||
"userID": "12345" | ||
}, | ||
{ | ||
"roles": [ | ||
{ | ||
"role": "A" | ||
} | ||
] | ||
} | ||
] | ||
} | ||
{ | ||
"userProfile": [ | ||
{ | ||
"userID": "12345" | ||
}, | ||
{ | ||
"roles": [ | ||
{ | ||
"role": "Admi" | ||
} | ||
] | ||
} | ||
] | ||
} | ||
{ | ||
"userProfile": [ | ||
{ | ||
"userID": "12345" | ||
}, | ||
{ | ||
"roles": [ | ||
{ | ||
"role": "Admin" | ||
}, | ||
{ | ||
"role": "U" | ||
} | ||
] | ||
} | ||
] | ||
} | ||
{ | ||
"userProfile": [ | ||
{ | ||
"userID": "12345" | ||
}, | ||
{ | ||
"roles": [ | ||
{ | ||
"role": "Admin" | ||
}, | ||
{ | ||
"role": "User" | ||
} | ||
] | ||
} | ||
] | ||
} | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { FakeStreamingLLM } from "../../utils/testing/index.js"; | ||
import { XMLOutputParser } from "../xml.js"; | ||
|
||
const XML_EXAMPLE = `<?xml version="1.0" encoding="UTF-8"?> | ||
<userProfile> | ||
<userID>12345</userID> | ||
<email>john.doe@example.com</email> | ||
<roles> | ||
<role>Admin</role> | ||
<role>User</role> | ||
</roles> | ||
<preferences> | ||
<theme>Dark</theme> | ||
<notifications> | ||
<email>true</email> | ||
</notifications> | ||
</preferences> | ||
</userProfile>`; | ||
|
||
const BACKTICK_WRAPPED_XML = `\`\`\`xml\n${XML_EXAMPLE}\n\`\`\``; | ||
|
||
const expectedResult = { | ||
userProfile: [ | ||
{ | ||
userID: "12345", | ||
}, | ||
{ | ||
email: "john.doe@example.com", | ||
}, | ||
{ | ||
roles: [ | ||
{ | ||
role: "Admin", | ||
}, | ||
{ | ||
role: "User", | ||
}, | ||
], | ||
}, | ||
{ | ||
preferences: [ | ||
{ | ||
theme: "Dark", | ||
}, | ||
{ | ||
notifications: [ | ||
{ | ||
email: "true", | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
], | ||
}; | ||
|
||
test("Can parse XML", async () => { | ||
const parser = new XMLOutputParser(); | ||
|
||
const result = await parser.invoke(XML_EXAMPLE); | ||
expect(result).toStrictEqual(expectedResult); | ||
}); | ||
|
||
test("Can parse backtick wrapped XML", async () => { | ||
const parser = new XMLOutputParser(); | ||
|
||
const result = await parser.invoke(BACKTICK_WRAPPED_XML); | ||
expect(result).toStrictEqual(expectedResult); | ||
}); | ||
|
||
test("Can format instructions with passed tags.", async () => { | ||
const tags = ["tag1", "tag2", "tag3"]; | ||
const parser = new XMLOutputParser({ tags }); | ||
|
||
const formatInstructions = parser.getFormatInstructions(); | ||
|
||
expect(formatInstructions).toContain("tag1, tag2, tag3"); | ||
}); | ||
|
||
test("Can parse streams", async () => { | ||
const parser = new XMLOutputParser(); | ||
const streamingLlm = new FakeStreamingLLM({ | ||
responses: [XML_EXAMPLE], | ||
}).pipe(parser); | ||
|
||
const result = await streamingLlm.stream(XML_EXAMPLE); | ||
let finalResult = {}; | ||
for await (const chunk of result) { | ||
console.log(chunk); | ||
finalResult = chunk; | ||
} | ||
expect(finalResult).toStrictEqual(expectedResult); | ||
}); |
Oops, something went wrong.