From 4654f69951ea943c2354861609856122fdb84902 Mon Sep 17 00:00:00 2001 From: vinodkiran Date: Mon, 23 Oct 2023 13:16:12 +0530 Subject: [PATCH] New Feature - Output Parsers --- .../nodes/chains/LLMChain/LLMChain.ts | 70 +++++++++++++--- .../csvlist/CSVListOutputParser.ts | 35 ++++++++ .../nodes/outputparsers/csvlist/csv.png | Bin 0 -> 8498 bytes .../customlist/CustomListOutputParser.ts | 55 +++++++++++++ .../nodes/outputparsers/customlist/list.png | Bin 0 -> 5002 bytes .../structured/StructuredOutputParser.ts | 77 ++++++++++++++++++ .../outputparsers/structured/structure.png | Bin 0 -> 3849 bytes 7 files changed, 227 insertions(+), 10 deletions(-) create mode 100644 packages/components/nodes/outputparsers/csvlist/CSVListOutputParser.ts create mode 100644 packages/components/nodes/outputparsers/csvlist/csv.png create mode 100644 packages/components/nodes/outputparsers/customlist/CustomListOutputParser.ts create mode 100644 packages/components/nodes/outputparsers/customlist/list.png create mode 100644 packages/components/nodes/outputparsers/structured/StructuredOutputParser.ts create mode 100644 packages/components/nodes/outputparsers/structured/structure.png diff --git a/packages/components/nodes/chains/LLMChain/LLMChain.ts b/packages/components/nodes/chains/LLMChain/LLMChain.ts index 63994b13..0544365a 100644 --- a/packages/components/nodes/chains/LLMChain/LLMChain.ts +++ b/packages/components/nodes/chains/LLMChain/LLMChain.ts @@ -3,6 +3,8 @@ import { getBaseClasses, handleEscapeCharacters } from '../../../src/utils' import { LLMChain } from 'langchain/chains' import { BaseLanguageModel } from 'langchain/base_language' import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler' +import { BaseOutputParser } from 'langchain/schema/output_parser' +import { ChatPromptTemplate, PromptTemplate, SystemMessagePromptTemplate } from 'langchain/prompts' class LLMChain_Chains implements INode { label: string @@ -19,7 +21,7 @@ class LLMChain_Chains implements INode { constructor() { this.label = 'LLM Chain' this.name = 'llmChain' - this.version = 1.0 + this.version = 2.0 this.type = 'LLMChain' this.icon = 'chain.svg' this.category = 'Chains' @@ -36,6 +38,12 @@ class LLMChain_Chains implements INode { name: 'prompt', type: 'BasePromptTemplate' }, + { + label: 'Output Parser', + name: 'outputParser', + type: 'BaseLLMOutputParser', + optional: true + }, { label: 'Chain Name', name: 'chainName', @@ -87,8 +95,35 @@ class LLMChain_Chains implements INode { async run(nodeData: INodeData, input: string, options: ICommonObject): Promise { const inputVariables = nodeData.instance.prompt.inputVariables as string[] // ["product"] const chain = nodeData.instance as LLMChain - const promptValues = nodeData.inputs?.prompt.promptValues as ICommonObject - const res = await runPrediction(inputVariables, chain, input, promptValues, options, nodeData) + let promptValues = nodeData.inputs?.prompt.promptValues as ICommonObject + const outputParser = nodeData.inputs?.outputParser as BaseOutputParser + if (outputParser && chain.prompt) { + const formatInstructions = outputParser.getFormatInstructions() + chain.prompt.inputVariables.push('format_instructions') + if (chain.prompt instanceof PromptTemplate) { + let pt = chain.prompt + pt.template = pt.template + '\n{format_instructions}' + chain.prompt.partialVariables = { format_instructions: formatInstructions } + // eslint-disable-next-line no-console + console.log('prompt :: ', chain.prompt) + } else if (chain.prompt instanceof ChatPromptTemplate) { + let pt = chain.prompt + pt.promptMessages.forEach((msg) => { + if (msg instanceof SystemMessagePromptTemplate) { + ;(msg.prompt as any).partialVariables = { format_instructions: outputParser.getFormatInstructions() } + ;(msg.prompt as any).template = ((msg.prompt as any).template + '\n{format_instructions}') as string + // eslint-disable-next-line no-console + console.log(msg) + } + }) + //pt.template = pt.template + '\n{format_instructions}' + } + + promptValues = { ...promptValues, format_instructions: outputParser.getFormatInstructions() } + // eslint-disable-next-line no-console + console.log('promptValues :: ', promptValues) + } + const res = await runPrediction(inputVariables, chain, input, promptValues, options, nodeData, outputParser) // eslint-disable-next-line no-console console.log('\x1b[93m\x1b[1m\n*****FINAL RESULT*****\n\x1b[0m\x1b[0m') // eslint-disable-next-line no-console @@ -103,7 +138,8 @@ const runPrediction = async ( input: string, promptValuesRaw: ICommonObject, options: ICommonObject, - nodeData: INodeData + nodeData: INodeData, + outputParser: BaseOutputParser | undefined = undefined ) => { const loggerHandler = new ConsoleCallbackHandler(options.logger) const callbacks = await additionalCallbacks(nodeData, options) @@ -135,10 +171,10 @@ const runPrediction = async ( if (isStreaming) { const handler = new CustomChainHandler(socketIO, socketIOClientId) const res = await chain.call(options, [loggerHandler, handler, ...callbacks]) - return res?.text + return runOutputParser(res?.text, outputParser) } else { const res = await chain.call(options, [loggerHandler, ...callbacks]) - return res?.text + return runOutputParser(res?.text, outputParser) } } else if (seen.length === 1) { // If one inputVariable is not specify, use input (user's question) as value @@ -151,10 +187,10 @@ const runPrediction = async ( if (isStreaming) { const handler = new CustomChainHandler(socketIO, socketIOClientId) const res = await chain.call(options, [loggerHandler, handler, ...callbacks]) - return res?.text + return runOutputParser(res?.text, outputParser) } else { const res = await chain.call(options, [loggerHandler, ...callbacks]) - return res?.text + return runOutputParser(res?.text, outputParser) } } else { throw new Error(`Please provide Prompt Values for: ${seen.join(', ')}`) @@ -163,12 +199,26 @@ const runPrediction = async ( if (isStreaming) { const handler = new CustomChainHandler(socketIO, socketIOClientId) const res = await chain.run(input, [loggerHandler, handler, ...callbacks]) - return res + return runOutputParser(res, outputParser) } else { const res = await chain.run(input, [loggerHandler, ...callbacks]) - return res + return runOutputParser(res, outputParser) } } } +const runOutputParser = async (response: string, outputParser: BaseOutputParser | undefined): Promise => { + if (outputParser) { + const parsedResponse = await outputParser.parse(response) + // eslint-disable-next-line no-console + console.log('**** parsedResponse ****', parsedResponse) + if (typeof parsedResponse === 'object') { + return JSON.stringify(parsedResponse) + } else { + return parsedResponse as string + } + } + return response +} + module.exports = { nodeClass: LLMChain_Chains } diff --git a/packages/components/nodes/outputparsers/csvlist/CSVListOutputParser.ts b/packages/components/nodes/outputparsers/csvlist/CSVListOutputParser.ts new file mode 100644 index 00000000..04911fb8 --- /dev/null +++ b/packages/components/nodes/outputparsers/csvlist/CSVListOutputParser.ts @@ -0,0 +1,35 @@ +import { getBaseClasses, ICommonObject, INode, INodeData, INodeParams } from '../../../src' +import { BaseOutputParser } from 'langchain/schema/output_parser' +import { CommaSeparatedListOutputParser } from 'langchain/output_parsers' + +class CSVListOutputParser implements INode { + label: string + name: string + version: number + description: string + type: string + icon: string + category: string + baseClasses: string[] + inputs: INodeParams[] + credential: INodeParams + + constructor() { + this.label = 'CSV Output Parser' + this.name = 'csvOutputParser' + this.version = 1.0 + this.type = 'CSVListOutputParser' + this.description = 'Parse the output of an LLM call as a comma-separated list of values' + this.icon = 'csv.png' + this.category = 'Output Parser' + this.baseClasses = [this.type, ...getBaseClasses(BaseOutputParser)] + this.inputs = [] + } + + // eslint-disable-next-line unused-imports/no-unused-vars + async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { + return new CommaSeparatedListOutputParser() + } +} + +module.exports = { nodeClass: CSVListOutputParser } diff --git a/packages/components/nodes/outputparsers/csvlist/csv.png b/packages/components/nodes/outputparsers/csvlist/csv.png new file mode 100644 index 0000000000000000000000000000000000000000..41b84e16a04dd8b6b6eb1606f4fa9f5317bffc96 GIT binary patch literal 8498 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 literal 0 HcmV?d00001 diff --git a/packages/components/nodes/outputparsers/customlist/CustomListOutputParser.ts b/packages/components/nodes/outputparsers/customlist/CustomListOutputParser.ts new file mode 100644 index 00000000..db05117e --- /dev/null +++ b/packages/components/nodes/outputparsers/customlist/CustomListOutputParser.ts @@ -0,0 +1,55 @@ +import { getBaseClasses, ICommonObject, INode, INodeData, INodeParams } from '../../../src' +import { BaseOutputParser } from 'langchain/schema/output_parser' +import { CustomListOutputParser as LangchainCustomListOutputParser } from 'langchain/output_parsers' + +class CustomListOutputParser implements INode { + label: string + name: string + version: number + description: string + type: string + icon: string + category: string + baseClasses: string[] + inputs: INodeParams[] + credential: INodeParams + + constructor() { + this.label = 'Custom List Output Parser' + this.name = 'customListOutputParser' + this.version = 1.0 + this.type = 'CustomListOutputParser' + this.description = 'Parse the output of an LLM call as a list of values.' + this.icon = 'list.png' + this.category = 'Output Parser' + this.baseClasses = [this.type, ...getBaseClasses(BaseOutputParser)] + this.inputs = [ + { + label: 'Length', + name: 'length', + type: 'number', + default: 5, + step: 1, + description: 'Number of values to return' + }, + { + label: 'Separator', + name: 'separator', + type: 'string', + description: 'Separator between values', + default: ',' + } + ] + } + + // eslint-disable-next-line unused-imports/no-unused-vars + async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { + const separator = nodeData.inputs?.separator as string + const lengthStr = nodeData.inputs?.length as string + let length = 5 + if (lengthStr) length = parseInt(lengthStr, 10) + return new LangchainCustomListOutputParser({ length: length, separator: separator }) + } +} + +module.exports = { nodeClass: CustomListOutputParser } diff --git a/packages/components/nodes/outputparsers/customlist/list.png b/packages/components/nodes/outputparsers/customlist/list.png new file mode 100644 index 0000000000000000000000000000000000000000..acb4e5d68f200207a97e10ee63125eb4e040fcec GIT binary patch literal 5002 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 { + const structureType = nodeData.inputs?.structureType as string + const structure = nodeData.inputs?.structure as string + let parsedStructure: any | undefined = undefined + if (structure) { + try { + parsedStructure = JSON.parse(structure) + } catch (exception) { + throw new Error('Invalid JSON in StructuredOutputParser: ' + exception) + } + } + if (structureType === 'fromZodSchema') { + return LangchainStructuredOutputParser.fromZodSchema(parsedStructure) + } else { + return LangchainStructuredOutputParser.fromNamesAndDescriptions(parsedStructure) + } + } +} + +module.exports = { nodeClass: StructuredOutputParser } diff --git a/packages/components/nodes/outputparsers/structured/structure.png b/packages/components/nodes/outputparsers/structured/structure.png new file mode 100644 index 0000000000000000000000000000000000000000..c56b2dd7786b2002fe6374d1f00f56fd70df310c GIT binary patch literal 3849 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 F{{fD