Skip to content

Commit fd45101

Browse files
jorbenclaude
andauthored
fix(llm): 🐛 修复 max_tokens 为无效值时导致的 API 错误 (#45)
修复所有 LLM 客户端中 maxTokens 检查逻辑,避免将无效值 (0, null, undefined) 传递给 API 导致错误。 问题:当 maxTokens 为 0 或 null 时,原代码 !== undefined 判断为 true,导致 API 报错 "max_tokens: must be greater than or equal to 1" 修复:将检查条件改为确保 maxTokens 是大于 0 的有效数字: - AnthropicClient.ts - OpenAIClient.ts - GeminiClient.ts - OpenAIResponsesClient.ts - OllamaClient.ts 更新 AnthropicClient 测试以匹配新的 system 消息格式。 Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent fabfd3e commit fd45101

6 files changed

Lines changed: 52 additions & 22 deletions

File tree

src/core/infrastructure/adapters/llm/AnthropicClient.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,28 +40,40 @@ export class AnthropicClient extends LLMClient {
4040
stream: normalizedOptions.stream || false
4141
};
4242

43-
// 只在提供了 maxTokens 时才添加到请求体
44-
if (normalizedOptions.maxTokens !== undefined) {
43+
// 只在提供了有效的 maxTokens 时才添加到请求体
44+
if (normalizedOptions.maxTokens && normalizedOptions.maxTokens > 0) {
4545
requestBody.max_tokens = normalizedOptions.maxTokens;
4646
}
4747

48-
// Anthropic支持System指令,而不是作为一个普通消息
48+
// Anthropic支持System指令,使用对象数组格式支持 cache_control
4949
const systemMessage = normalizedOptions.messages.find(msg => msg.role === 'system');
50+
let systemText = '';
5051
if (systemMessage) {
51-
const systemContent = Array.isArray(systemMessage.content)
52+
systemText = Array.isArray(systemMessage.content)
5253
? systemMessage.content.filter(c => c.type === 'text').map(c => (c as TextContent).text).join('\n')
5354
: systemMessage.content.type === 'text' ? (systemMessage.content as TextContent).text : '';
55+
}
5456

55-
if (systemContent) {
56-
requestBody.system = systemContent;
57+
// Claude没有直接的JSON响应格式,但可以通过系统指令引导
58+
if (normalizedOptions.response_format?.type === 'json_object') {
59+
if (!systemText) {
60+
systemText = "请以有效的JSON格式提供响应。";
61+
} else {
62+
systemText += "\n\n请以有效的JSON格式提供响应。";
5763
}
5864
}
5965

60-
// Claude没有直接的JSON响应格式,但可以通过系统指令引导
61-
if (normalizedOptions.response_format?.type === 'json_object' && !requestBody.system) {
62-
requestBody.system = "请以有效的JSON格式提供响应。";
63-
} else if (normalizedOptions.response_format?.type === 'json_object' && requestBody.system) {
64-
requestBody.system += "\n\n请以有效的JSON格式提供响应。";
66+
// 使用对象数组格式,支持 cache_control
67+
if (systemText) {
68+
requestBody.system = [
69+
{
70+
type: 'text',
71+
text: systemText,
72+
cache_control: {
73+
type: 'ephemeral'
74+
}
75+
}
76+
];
6577
}
6678

6779
const response = await fetch(endpoint, {

src/core/infrastructure/adapters/llm/GeminiClient.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ export class GeminiClient extends LLMClient {
3939
}
4040
};
4141

42-
// 只在提供了 maxTokens 时才添加到请求体
43-
if (normalizedOptions.maxTokens !== undefined) {
42+
// 只在提供了有效的 maxTokens 时才添加到请求体
43+
if (normalizedOptions.maxTokens && normalizedOptions.maxTokens > 0) {
4444
requestBody.generationConfig.maxOutputTokens = normalizedOptions.maxTokens;
4545
}
4646

src/core/infrastructure/adapters/llm/OllamaClient.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ export class OllamaClient extends LLMClient {
4141
requestBody.options.temperature = normalizedOptions.temperature;
4242
}
4343

44-
if (normalizedOptions.maxTokens !== undefined) {
44+
// 只在提供了有效的 maxTokens 时才添加到请求体
45+
if (normalizedOptions.maxTokens && normalizedOptions.maxTokens > 0) {
4546
requestBody.options.num_predict = normalizedOptions.maxTokens;
4647
}
4748

src/core/infrastructure/adapters/llm/OpenAIClient.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ export class OpenAIClient extends LLMClient {
3737
stream: normalizedOptions.stream || false
3838
};
3939

40-
// 只在提供了 maxTokens 时才添加到请求体
41-
if (normalizedOptions.maxTokens !== undefined) {
40+
// 只在提供了有效的 maxTokens 时才添加到请求体
41+
if (normalizedOptions.maxTokens && normalizedOptions.maxTokens > 0) {
4242
requestBody.max_tokens = normalizedOptions.maxTokens;
4343
}
4444

src/core/infrastructure/adapters/llm/OpenAIResponsesClient.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ export class OpenAIResponsesClient extends LLMClient {
3838
stream: normalizedOptions.stream || false
3939
};
4040

41-
// 只在提供了 maxTokens 时才添加到请求体
42-
if (normalizedOptions.maxTokens !== undefined) {
41+
// 只在提供了有效的 maxTokens 时才添加到请求体
42+
if (normalizedOptions.maxTokens && normalizedOptions.maxTokens > 0) {
4343
requestBody.max_tokens = normalizedOptions.maxTokens;
4444
}
4545

src/core/infrastructure/adapters/llm/__tests__/AnthropicClient.test.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,13 @@ describe('AnthropicClient', () => {
6767
const callArgs = (global.fetch as any).mock.calls[0][1]
6868
const body = JSON.parse(callArgs.body)
6969

70-
expect(body.system).toBe('You are a helpful assistant')
70+
expect(body.system).toEqual([
71+
{
72+
type: 'text',
73+
text: 'You are a helpful assistant',
74+
cache_control: { type: 'ephemeral' }
75+
}
76+
])
7177
expect(body.messages).toHaveLength(1)
7278
expect(body.messages[0].role).toBe('user')
7379
})
@@ -148,7 +154,13 @@ describe('AnthropicClient', () => {
148154
const callArgs = (global.fetch as any).mock.calls[0][1]
149155
const body = JSON.parse(callArgs.body)
150156

151-
expect(body.system).toContain('JSON')
157+
expect(body.system).toEqual([
158+
{
159+
type: 'text',
160+
text: '请以有效的JSON格式提供响应。',
161+
cache_control: { type: 'ephemeral' }
162+
}
163+
])
152164
})
153165

154166
it('should append JSON instruction to existing system prompt', async () => {
@@ -173,8 +185,13 @@ describe('AnthropicClient', () => {
173185
const callArgs = (global.fetch as any).mock.calls[0][1]
174186
const body = JSON.parse(callArgs.body)
175187

176-
expect(body.system).toContain('You are helpful')
177-
expect(body.system).toContain('JSON')
188+
expect(body.system).toEqual([
189+
{
190+
type: 'text',
191+
text: 'You are helpful\n\n请以有效的JSON格式提供响应。',
192+
cache_control: { type: 'ephemeral' }
193+
}
194+
])
178195
})
179196
})
180197

0 commit comments

Comments
 (0)