mirror of
https://github.com/n8n-io/n8n.git
synced 2026-06-02 09:47:00 +02:00
fix(core): Display native web search results in agent session timeline (no-changelog) (#31420)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
d3405acb82
commit
419cfad98f
|
|
@ -3,6 +3,7 @@ import type { TextStreamPart, ToolSet } from 'ai';
|
|||
import { convertChunk } from '../stream';
|
||||
|
||||
type ToolCallChunk = Extract<TextStreamPart<ToolSet>, { type: 'tool-call' }>;
|
||||
type ToolResultChunk = Extract<TextStreamPart<ToolSet>, { type: 'tool-result' }>;
|
||||
|
||||
describe('convertChunk — tool-call invalid/error handling', () => {
|
||||
it('returns a tool-result with isError when c.invalid is true', () => {
|
||||
|
|
@ -78,3 +79,62 @@ describe('convertChunk — tool-call invalid/error handling', () => {
|
|||
expect(result).toMatchObject({ type: 'tool-result', toolName: '', isError: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe('convertChunk — tool-result output passthrough', () => {
|
||||
it('passes a raw array output through verbatim (e.g. native web search results)', () => {
|
||||
const output = [
|
||||
{ title: 'n8n', url: 'https://n8n.io' },
|
||||
{ title: 'Docs', url: 'https://docs.n8n.io' },
|
||||
];
|
||||
const chunk = {
|
||||
type: 'tool-result',
|
||||
toolCallId: 'tc-1',
|
||||
toolName: 'anthropic.web_search_20250305',
|
||||
input: { query: 'n8n' },
|
||||
output,
|
||||
providerExecuted: true,
|
||||
} as unknown as ToolResultChunk;
|
||||
|
||||
expect(convertChunk(chunk)).toEqual({
|
||||
type: 'tool-result',
|
||||
toolCallId: 'tc-1',
|
||||
toolName: 'anthropic.web_search_20250305',
|
||||
output,
|
||||
});
|
||||
});
|
||||
|
||||
it('passes a raw object output through verbatim without unwrapping a coincidental "value" key', () => {
|
||||
const output = { value: 42, unit: 'C' };
|
||||
const chunk = {
|
||||
type: 'tool-result',
|
||||
toolCallId: 'tc-2',
|
||||
toolName: 'get_temperature',
|
||||
input: {},
|
||||
output,
|
||||
} as unknown as ToolResultChunk;
|
||||
|
||||
expect(convertChunk(chunk)).toEqual({
|
||||
type: 'tool-result',
|
||||
toolCallId: 'tc-2',
|
||||
toolName: 'get_temperature',
|
||||
output,
|
||||
});
|
||||
});
|
||||
|
||||
it('falls back to empty strings when toolCallId and toolName are absent', () => {
|
||||
const chunk = {
|
||||
type: 'tool-result',
|
||||
toolCallId: undefined,
|
||||
toolName: undefined,
|
||||
input: {},
|
||||
output: 'plain text result',
|
||||
} as unknown as ToolResultChunk;
|
||||
|
||||
expect(convertChunk(chunk)).toEqual({
|
||||
type: 'tool-result',
|
||||
toolCallId: '',
|
||||
toolName: '',
|
||||
output: 'plain text result',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -96,12 +96,16 @@ export function convertChunk(c: TextStreamPart<ToolSet>): StreamChunk | undefine
|
|||
}
|
||||
|
||||
case 'tool-result':
|
||||
// The fullStream emits the raw tool output here, not the
|
||||
// `{ type, value }` ToolResultOutput wrapper used on the message
|
||||
// side — so pass it through verbatim. Only provider-executed tools
|
||||
// (e.g. native web search) reach this branch; local tool results are
|
||||
// written directly by the runtime and never pass through convertChunk.
|
||||
return {
|
||||
type: 'tool-result',
|
||||
toolCallId: c.toolCallId ?? '',
|
||||
toolName: c.toolName ?? '',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
output: c.output && 'value' in c.output ? (c.output.value as JSONValue) : null,
|
||||
output: c.output,
|
||||
};
|
||||
|
||||
case 'error':
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user