From cf223af11cd310d75fb8149dc27214071423fa97 Mon Sep 17 00:00:00 2001 From: Petrus de Melo Date: Tue, 9 Jun 2026 03:15:29 -0600 Subject: [PATCH] feat(LmChatAnthropic Node): Add streaming option (#30991) Co-authored-by: oleg --- .../LMChatAnthropic/LmChatAnthropic.node.ts | 10 ++++++ .../test/LmChatAnthropic.test.ts | 36 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/packages/@n8n/nodes-langchain/nodes/llms/LMChatAnthropic/LmChatAnthropic.node.ts b/packages/@n8n/nodes-langchain/nodes/llms/LMChatAnthropic/LmChatAnthropic.node.ts index 144c3abc384..134dc634115 100644 --- a/packages/@n8n/nodes-langchain/nodes/llms/LMChatAnthropic/LmChatAnthropic.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/llms/LMChatAnthropic/LmChatAnthropic.node.ts @@ -434,6 +434,14 @@ export class LmChatAnthropic implements INodeType { }, }, }, + { + displayName: 'Stream Responses', + name: 'streaming', + type: 'boolean', + default: false, + description: + 'Whether the model should stream its response over Server-Sent Events instead of returning a single non-streamed payload. Final output shape is unchanged.', + }, ], }, ], @@ -469,6 +477,7 @@ export class LmChatAnthropic implements INodeType { thinkingBudget?: number; thinkingMode?: 'disabled' | 'adaptive' | 'manual'; effort?: 'low' | 'medium' | 'high' | 'xhigh' | 'max'; + streaming?: boolean; }; const isOpus47Model = modelName.startsWith('claude-opus-4-7'); @@ -577,6 +586,7 @@ export class LmChatAnthropic implements INodeType { onFailedAttempt: makeN8nLlmFailedAttemptHandler(this, gatewayErrorHandler), invocationKwargs, clientOptions, + streaming: options.streaming ?? false, }; // Opus 4.7 rejects temperature/topK/topP at the SDK layer regardless of thinking mode diff --git a/packages/@n8n/nodes-langchain/nodes/llms/LMChatAnthropic/test/LmChatAnthropic.test.ts b/packages/@n8n/nodes-langchain/nodes/llms/LMChatAnthropic/test/LmChatAnthropic.test.ts index 312b21f4656..ade595d7e62 100644 --- a/packages/@n8n/nodes-langchain/nodes/llms/LMChatAnthropic/test/LmChatAnthropic.test.ts +++ b/packages/@n8n/nodes-langchain/nodes/llms/LMChatAnthropic/test/LmChatAnthropic.test.ts @@ -225,6 +225,42 @@ describe('LmChatAnthropic', () => { ); }); + it('should default streaming to false when option is omitted', async () => { + const mockContext = setupMockContext(); + + mockContext.getNodeParameter = vi.fn().mockImplementation((paramName: string) => { + if (paramName === 'model.value') return 'claude-sonnet-4-20250514'; + if (paramName === 'options') return {}; + return undefined; + }); + + await lmChatAnthropic.supplyData.call(mockContext, 0); + + expect(MockedChatAnthropic).toHaveBeenCalledWith( + expect.objectContaining({ + streaming: false, + }), + ); + }); + + it('should pass streaming=true to ChatAnthropic when option is set', async () => { + const mockContext = setupMockContext(); + + mockContext.getNodeParameter = vi.fn().mockImplementation((paramName: string) => { + if (paramName === 'model.value') return 'claude-sonnet-4-20250514'; + if (paramName === 'options') return { streaming: true }; + return undefined; + }); + + await lmChatAnthropic.supplyData.call(mockContext, 0); + + expect(MockedChatAnthropic).toHaveBeenCalledWith( + expect.objectContaining({ + streaming: true, + }), + ); + }); + it('should remove topP from model when not explicitly set', async () => { const mockContext = setupMockContext();