feat(LmChatAnthropic Node): Add streaming option (#30991)

Co-authored-by: oleg <me@olegivaniv.com>
This commit is contained in:
Petrus de Melo
2026-06-09 09:15:29 +00:00
committed by GitHub
co-authored by oleg
parent 9bb77cc55e
commit cf223af11c
2 changed files with 46 additions and 0 deletions
@@ -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
@@ -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();