From e97d338f3a5128b499c16f6289da2acef29395b5 Mon Sep 17 00:00:00 2001 From: Felix-Ayush <67006255+Ayush7614@users.noreply.github.com> Date: Thu, 30 Jul 2026 04:08:31 +0530 Subject: [PATCH] fix(ai-chat): preserve max_tokens 0 for Alibaba, DeepSeek, and Claude (#3472) These providers used `max_tokens || default`, so an explicit zero was replaced by the fallback. Use nullish coalescing so 0 is forwarded. --- .../providers/alibaba/AlibabaProvider.test.ts | 15 +++++++++++++++ .../ai-chat/providers/alibaba/AlibabaProvider.ts | 2 +- .../providers/claude/ClaudeProvider.test.ts | 16 ++++++++++++++++ .../ai-chat/providers/claude/ClaudeProvider.ts | 2 +- .../providers/deepseek/DeepSeekProvider.test.ts | 15 +++++++++++++++ .../providers/deepseek/DeepSeekProvider.ts | 2 +- 6 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/backend/drivers/ai-chat/providers/alibaba/AlibabaProvider.test.ts b/src/backend/drivers/ai-chat/providers/alibaba/AlibabaProvider.test.ts index 5d66e0a2e..d79f9519c 100644 --- a/src/backend/drivers/ai-chat/providers/alibaba/AlibabaProvider.test.ts +++ b/src/backend/drivers/ai-chat/providers/alibaba/AlibabaProvider.test.ts @@ -225,6 +225,21 @@ describe('AlibabaProvider.complete request shape', () => { expect(createMock.mock.calls[0]![0].max_tokens).toBe(256); }); + it('forwards max_tokens 0 instead of substituting the default 1000', async () => { + const { provider } = makeProvider(); + createMock.mockResolvedValueOnce(baseCompletion); + + await withTestActor(() => + provider.complete({ + model: 'qwen-plus', + messages: [{ role: 'user', content: 'hi' }], + max_tokens: 0, + }), + ); + + expect(createMock.mock.calls[0]![0].max_tokens).toBe(0); + }); + it('forwards temperature when supplied', async () => { const { provider } = makeProvider(); createMock.mockResolvedValueOnce(baseCompletion); diff --git a/src/backend/drivers/ai-chat/providers/alibaba/AlibabaProvider.ts b/src/backend/drivers/ai-chat/providers/alibaba/AlibabaProvider.ts index 14798ae28..a825e36b1 100644 --- a/src/backend/drivers/ai-chat/providers/alibaba/AlibabaProvider.ts +++ b/src/backend/drivers/ai-chat/providers/alibaba/AlibabaProvider.ts @@ -86,7 +86,7 @@ export class AlibabaProvider implements IChatProvider { messages, model: modelUsed.id, ...(tools ? { tools } : {}), - max_tokens: max_tokens || 1000, + max_tokens: max_tokens ?? 1000, temperature, stream, ...(stream diff --git a/src/backend/drivers/ai-chat/providers/claude/ClaudeProvider.test.ts b/src/backend/drivers/ai-chat/providers/claude/ClaudeProvider.test.ts index 63780655d..6e5ac5b51 100644 --- a/src/backend/drivers/ai-chat/providers/claude/ClaudeProvider.test.ts +++ b/src/backend/drivers/ai-chat/providers/claude/ClaudeProvider.test.ts @@ -237,6 +237,22 @@ describe('ClaudeProvider.complete request shape', () => { }); }); + it('forwards max_tokens 0 instead of substituting the model default', async () => { + const { provider } = makeProvider(); + messagesCreateMock.mockResolvedValueOnce(baseResponse); + + await withTestActor(() => + provider.complete({ + model: 'claude-haiku-4-5-20251001', + messages: [{ role: 'user', content: 'hello' }], + max_tokens: 0, + }), + ); + + const [args] = messagesCreateMock.mock.calls[0]!; + expect(args.max_tokens).toBe(0); + }); + it('extracts system messages and forwards them as the top-level `system` field', async () => { const { provider } = makeProvider(); messagesCreateMock.mockResolvedValueOnce(baseResponse); diff --git a/src/backend/drivers/ai-chat/providers/claude/ClaudeProvider.ts b/src/backend/drivers/ai-chat/providers/claude/ClaudeProvider.ts index 01e8c0cd3..309a44133 100644 --- a/src/backend/drivers/ai-chat/providers/claude/ClaudeProvider.ts +++ b/src/backend/drivers/ai-chat/providers/claude/ClaudeProvider.ts @@ -320,7 +320,7 @@ export class ClaudeProvider implements IChatProvider { } = { model: modelUsed.id, max_tokens: Math.floor( - max_tokens || + max_tokens ?? (model === 'claude-3-5-sonnet-20241022' || model === 'claude-3-5-sonnet-20240620' ? 8192 diff --git a/src/backend/drivers/ai-chat/providers/deepseek/DeepSeekProvider.test.ts b/src/backend/drivers/ai-chat/providers/deepseek/DeepSeekProvider.test.ts index 5c7903086..423270069 100644 --- a/src/backend/drivers/ai-chat/providers/deepseek/DeepSeekProvider.test.ts +++ b/src/backend/drivers/ai-chat/providers/deepseek/DeepSeekProvider.test.ts @@ -216,6 +216,21 @@ describe('DeepSeekProvider.complete request shape', () => { expect(createMock.mock.calls[0]![0].max_tokens).toBe(256); }); + it('forwards max_tokens 0 instead of substituting the default 1000', async () => { + const { provider } = makeProvider(); + createMock.mockResolvedValueOnce(baseCompletion); + + await withTestActor(() => + provider.complete({ + model: 'deepseek-v4-flash', + messages: [{ role: 'user', content: 'hi' }], + max_tokens: 0, + }), + ); + + expect(createMock.mock.calls[0]![0].max_tokens).toBe(0); + }); + it('omits the `tools` key entirely when no tools are supplied', async () => { const { provider } = makeProvider(); createMock.mockResolvedValueOnce(baseCompletion); diff --git a/src/backend/drivers/ai-chat/providers/deepseek/DeepSeekProvider.ts b/src/backend/drivers/ai-chat/providers/deepseek/DeepSeekProvider.ts index 53edb1db5..58b0320fe 100644 --- a/src/backend/drivers/ai-chat/providers/deepseek/DeepSeekProvider.ts +++ b/src/backend/drivers/ai-chat/providers/deepseek/DeepSeekProvider.ts @@ -117,7 +117,7 @@ export class DeepSeekProvider implements IChatProvider { messages, model: modelUsed.id, ...(tools ? { tools } : {}), - max_tokens: max_tokens || 1000, + max_tokens: max_tokens ?? 1000, temperature, stream, ...(stream