2024-10-22 01:46:58 -07:00
|
|
|
import type { Callbacks } from '@langchain/core/callbacks/manager';
|
|
|
|
import type { BaseLanguageModel } from '@langchain/core/language_models/base';
|
|
|
|
import type { AIMessage } from '@langchain/core/messages';
|
2024-11-06 08:24:43 -08:00
|
|
|
import { BaseOutputParser, OutputParserException } from '@langchain/core/output_parsers';
|
|
|
|
import type { PromptTemplate } from '@langchain/core/prompts';
|
2024-10-28 03:37:23 -07:00
|
|
|
import type { ISupplyDataFunctions } from 'n8n-workflow';
|
2024-10-22 01:46:58 -07:00
|
|
|
import { NodeConnectionType } from 'n8n-workflow';
|
|
|
|
|
|
|
|
import type { N8nStructuredOutputParser } from './N8nStructuredOutputParser';
|
|
|
|
import { logAiEvent } from '../helpers';
|
|
|
|
|
|
|
|
export class N8nOutputFixingParser extends BaseOutputParser {
|
|
|
|
lc_namespace = ['langchain', 'output_parsers', 'fix'];
|
|
|
|
|
|
|
|
constructor(
|
2024-10-28 03:37:23 -07:00
|
|
|
private context: ISupplyDataFunctions,
|
|
|
|
private model: BaseLanguageModel,
|
|
|
|
private outputParser: N8nStructuredOutputParser,
|
2024-11-06 08:24:43 -08:00
|
|
|
private fixPromptTemplate: PromptTemplate,
|
2024-10-22 01:46:58 -07:00
|
|
|
) {
|
|
|
|
super();
|
|
|
|
}
|
|
|
|
|
|
|
|
getRetryChain() {
|
2024-11-06 08:24:43 -08:00
|
|
|
return this.fixPromptTemplate.pipe(this.model);
|
2024-10-22 01:46:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attempts to parse the completion string using the output parser.
|
|
|
|
* If the initial parse fails, it tries to fix the output using a retry chain.
|
|
|
|
* @param completion The string to be parsed
|
|
|
|
* @returns The parsed response
|
|
|
|
* @throws Error if both parsing attempts fail
|
|
|
|
*/
|
|
|
|
async parse(completion: string, callbacks?: Callbacks) {
|
|
|
|
const { index } = this.context.addInputData(NodeConnectionType.AiOutputParser, [
|
|
|
|
[{ json: { action: 'parse', text: completion } }],
|
|
|
|
]);
|
|
|
|
|
|
|
|
try {
|
|
|
|
// First attempt to parse the completion
|
|
|
|
const response = await this.outputParser.parse(completion, callbacks, (e) => e);
|
2024-10-28 03:37:23 -07:00
|
|
|
logAiEvent(this.context, 'ai-output-parsed', { text: completion, response });
|
2024-10-22 01:46:58 -07:00
|
|
|
|
|
|
|
this.context.addOutputData(NodeConnectionType.AiOutputParser, index, [
|
|
|
|
[{ json: { action: 'parse', response } }],
|
|
|
|
]);
|
|
|
|
|
|
|
|
return response;
|
|
|
|
} catch (error) {
|
2024-11-06 08:24:43 -08:00
|
|
|
if (!(error instanceof OutputParserException)) {
|
|
|
|
throw error;
|
|
|
|
}
|
2024-10-22 01:46:58 -07:00
|
|
|
try {
|
|
|
|
// Second attempt: use retry chain to fix the output
|
|
|
|
const result = (await this.getRetryChain().invoke({
|
|
|
|
completion,
|
2024-11-06 08:24:43 -08:00
|
|
|
error: error.message,
|
2024-10-22 01:46:58 -07:00
|
|
|
instructions: this.getFormatInstructions(),
|
|
|
|
})) as AIMessage;
|
|
|
|
|
|
|
|
const resultText = result.content.toString();
|
|
|
|
const parsed = await this.outputParser.parse(resultText, callbacks);
|
|
|
|
|
|
|
|
// Add the successfully parsed output to the context
|
|
|
|
this.context.addOutputData(NodeConnectionType.AiOutputParser, index, [
|
|
|
|
[{ json: { action: 'parse', response: parsed } }],
|
|
|
|
]);
|
|
|
|
|
|
|
|
return parsed;
|
|
|
|
} catch (autoParseError) {
|
|
|
|
// If both attempts fail, add the error to the output and throw
|
|
|
|
this.context.addOutputData(NodeConnectionType.AiOutputParser, index, autoParseError);
|
|
|
|
throw autoParseError;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Method to get the format instructions for the parser.
|
|
|
|
* @returns The format instructions for the parser.
|
|
|
|
*/
|
|
|
|
getFormatInstructions() {
|
|
|
|
return this.outputParser.getFormatInstructions();
|
|
|
|
}
|
|
|
|
|
|
|
|
getSchema() {
|
|
|
|
return this.outputParser.schema;
|
|
|
|
}
|
|
|
|
}
|