fix(AI Agent Node): Preserve intermediateSteps when using output parser with non-tool agent (#11363)
Some checks are pending
Test Master / install-and-build (push) Waiting to run
Test Master / Unit tests (18.x) (push) Blocked by required conditions
Test Master / Unit tests (20.x) (push) Blocked by required conditions
Test Master / Unit tests (22.4) (push) Blocked by required conditions
Test Master / Lint (push) Blocked by required conditions
Test Master / Notify Slack on failure (push) Blocked by required conditions
Benchmark Docker Image CI / build (push) Waiting to run

This commit is contained in:
oleg 2024-10-23 16:48:26 +02:00 committed by GitHub
parent 197a1264d1
commit e61a8535aa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 32 additions and 9 deletions

View file

@ -251,7 +251,7 @@ export class Agent implements INodeType {
icon: 'fa:robot',
iconColor: 'black',
group: ['transform'],
version: [1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6],
version: [1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7],
description: 'Generates an action plan and executes it. Can use external tools.',
subtitle:
"={{ { toolsAgent: 'Tools Agent', conversationalAgent: 'Conversational Agent', openAiFunctionsAgent: 'OpenAI Functions Agent', reActAgent: 'ReAct Agent', sqlAgent: 'SQL Agent', planAndExecuteAgent: 'Plan and Execute Agent' }[$parameter.agent] }}",

View file

@ -14,6 +14,7 @@ import {
import { getOptionalOutputParsers } from '../../../../../utils/output_parsers/N8nOutputParser';
import { throwIfToolSchema } from '../../../../../utils/schemaParsing';
import { getTracingConfig } from '../../../../../utils/tracing';
import { extractParsedOutput } from '../utils';
export async function conversationalAgentExecute(
this: IExecuteFunctions,
@ -102,12 +103,12 @@ export async function conversationalAgentExecute(
input = (await prompt.invoke({ input })).value;
}
let response = await agentExecutor
const response = await agentExecutor
.withConfig(getTracingConfig(this))
.invoke({ input, outputParsers });
if (outputParser) {
response = { output: await outputParser.parse(response.output as string) };
response.output = await extractParsedOutput(this, outputParser, response.output as string);
}
returnData.push({ json: response });

View file

@ -15,6 +15,7 @@ import {
import { getConnectedTools, getPromptInputByType } from '../../../../../utils/helpers';
import { getOptionalOutputParsers } from '../../../../../utils/output_parsers/N8nOutputParser';
import { getTracingConfig } from '../../../../../utils/tracing';
import { extractParsedOutput } from '../utils';
export async function openAiFunctionsAgentExecute(
this: IExecuteFunctions,
@ -103,12 +104,12 @@ export async function openAiFunctionsAgentExecute(
input = (await prompt.invoke({ input })).value;
}
let response = await agentExecutor
const response = await agentExecutor
.withConfig(getTracingConfig(this))
.invoke({ input, outputParsers });
if (outputParser) {
response = { output: await outputParser.parse(response.output as string) };
response.output = await extractParsedOutput(this, outputParser, response.output as string);
}
returnData.push({ json: response });

View file

@ -14,6 +14,7 @@ import { getConnectedTools, getPromptInputByType } from '../../../../../utils/he
import { getOptionalOutputParsers } from '../../../../../utils/output_parsers/N8nOutputParser';
import { throwIfToolSchema } from '../../../../../utils/schemaParsing';
import { getTracingConfig } from '../../../../../utils/tracing';
import { extractParsedOutput } from '../utils';
export async function planAndExecuteAgentExecute(
this: IExecuteFunctions,
@ -79,12 +80,12 @@ export async function planAndExecuteAgentExecute(
input = (await prompt.invoke({ input })).value;
}
let response = await agentExecutor
const response = await agentExecutor
.withConfig(getTracingConfig(this))
.invoke({ input, outputParsers });
if (outputParser) {
response = { output: await outputParser.parse(response.output as string) };
response.output = await extractParsedOutput(this, outputParser, response.output as string);
}
returnData.push({ json: response });

View file

@ -19,6 +19,7 @@ import {
import { getOptionalOutputParsers } from '../../../../../utils/output_parsers/N8nOutputParser';
import { throwIfToolSchema } from '../../../../../utils/schemaParsing';
import { getTracingConfig } from '../../../../../utils/tracing';
import { extractParsedOutput } from '../utils';
export async function reActAgentAgentExecute(
this: IExecuteFunctions,
@ -103,12 +104,12 @@ export async function reActAgentAgentExecute(
input = (await prompt.invoke({ input })).value;
}
let response = await agentExecutor
const response = await agentExecutor
.withConfig(getTracingConfig(this))
.invoke({ input, outputParsers });
if (outputParser) {
response = { output: await outputParser.parse(response.output as string) };
response.output = await extractParsedOutput(this, outputParser, response.output as string);
}
returnData.push({ json: response });

View file

@ -0,0 +1,19 @@
import type { BaseOutputParser } from '@langchain/core/output_parsers';
import type { IExecuteFunctions } from 'n8n-workflow';
export async function extractParsedOutput(
ctx: IExecuteFunctions,
outputParser: BaseOutputParser<unknown>,
output: string,
): Promise<Record<string, unknown> | undefined> {
const parsedOutput = (await outputParser.parse(output)) as {
output: Record<string, unknown>;
};
if (ctx.getNode().typeVersion <= 1.6) {
return parsedOutput;
}
// For 1.7 and above, we try to extract the output from the parsed output
// with fallback to the original output if it's not present
return parsedOutput?.output ?? parsedOutput;
}