Skip to content

Commit 8e6360b

Browse files
Evsdrgdeepseek-v4-pro[1m]
authored andcommitted
feat: Grok 适配完善 — 防御性 usage 合并 + thinking 自动检测 (claude-code-best#1234)
* feat: Grok 适配完善 — 防御性 usage 合并 + thinking 自动检测 1. 提取 updateOpenAIUsage 到共享模块 openaiShared.ts,供 OpenAI 和 Grok 两条路径复用,消除 Grok 中重复的 spread 漏洞。 2. 在 requestBody.ts 的 isOpenAIThinkingEnabled() 中增加 Grok 模型 自动检测(模型名含 "grok"),与 DeepSeek/MiMo 并列。 3. messaging 层的 reasoning_content 回传(openaiConvertMessages.ts) 和流解析(openaiStreamAdapter.ts)无需修改,Grok 与 DeepSeek/MiMo 共用相同的 reasoning_content 字段协议。 Co-Authored-By: deepseek-v4-pro[1m] <deepseek-ai@claude-code-best.win> * fix: 回退 Grok 从 isOpenAIThinkingEnabled 的自动检测 Grok 推理模型(如 grok-4.20-reasoning)自动进行推理,不需要 thinking/enable_thinking 请求参数。发送这些参数虽大概率被忽略 (OpenAI SDK 透传 unknown keys),但属于不正确行为。 Co-Authored-By: deepseek-v4-pro[1m] <deepseek-ai@claude-code-best.win> --------- Co-authored-by: deepseek-v4-pro[1m] <deepseek-ai@claude-code-best.win>
1 parent 13347e5 commit 8e6360b

4 files changed

Lines changed: 54 additions & 4 deletions

File tree

src/services/api/grok/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {
1212
ChatCompletionCreateParamsStreaming,
1313
} from 'openai/resources/chat/completions/completions.mjs'
1414
import { getGrokClient } from './client.js'
15+
import { updateOpenAIUsage } from '../openai/openaiShared.js'
1516
import {
1617
anthropicMessagesToOpenAI,
1718
anthropicToolsToOpenAI,
@@ -136,7 +137,7 @@ export async function* queryModelGrok(
136137
partialMessage = (event as any).message
137138
ttftMs = Date.now() - start
138139
if ((event as any).message?.usage) {
139-
usage = { ...usage, ...(event as any).message.usage }
140+
usage = updateOpenAIUsage(usage, (event as any).message.usage)
140141
}
141142
break
142143
}
@@ -192,7 +193,7 @@ export async function* queryModelGrok(
192193
case 'message_delta': {
193194
const deltaUsage = (event as any).usage
194195
if (deltaUsage) {
195-
usage = { ...usage, ...deltaUsage }
196+
usage = updateOpenAIUsage(usage, deltaUsage)
196197
}
197198
break
198199
}

src/services/api/openai/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
import type { AgentId } from '../../../types/ids.js'
1111
import type { Tools } from '../../../Tool.js'
1212
import { getOpenAIClient } from './client.js'
13+
import { updateOpenAIUsage } from './openaiShared.js'
1314
import {
1415
anthropicMessagesToOpenAI,
1516
resolveOpenAIModel,
@@ -449,7 +450,7 @@ export async function* queryModelOpenAI(
449450
case 'message_delta': {
450451
const deltaUsage = (event as any).usage
451452
if (deltaUsage) {
452-
usage = { ...usage, ...deltaUsage }
453+
usage = updateOpenAIUsage(usage, deltaUsage)
453454
}
454455
if ((event as any).delta?.stop_reason != null) {
455456
stopReason = (event as any).delta.stop_reason
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Shared utilities for OpenAI-compatible API paths.
3+
*
4+
* Both the OpenAI path (queryModelOpenAI) and Grok path (queryModelGrok) use
5+
* the same adapters (openaiStreamAdapter, openaiConvertMessages), so the event
6+
* processing logic should be shared rather than duplicated.
7+
*/
8+
9+
/**
10+
* Merge a delta usage into the accumulated usage, preserving cache-related
11+
* fields from previous values when the delta carries explicit zeroes or
12+
* undefined values.
13+
*
14+
* Mirrors updateUsage() in claude.ts: a future adapter change that omits
15+
* cache fields from certain streaming events should not silently zero the
16+
* accumulated counters.
17+
*/
18+
export function updateOpenAIUsage(
19+
current: {
20+
input_tokens: number
21+
output_tokens: number
22+
cache_creation_input_tokens: number
23+
cache_read_input_tokens: number
24+
},
25+
delta: {
26+
input_tokens?: number
27+
output_tokens?: number
28+
cache_creation_input_tokens?: number
29+
cache_read_input_tokens?: number
30+
},
31+
): typeof current {
32+
return {
33+
input_tokens: delta.input_tokens ?? current.input_tokens,
34+
output_tokens: delta.output_tokens ?? current.output_tokens,
35+
cache_creation_input_tokens:
36+
delta.cache_creation_input_tokens !== undefined &&
37+
delta.cache_creation_input_tokens > 0
38+
? delta.cache_creation_input_tokens
39+
: current.cache_creation_input_tokens,
40+
cache_read_input_tokens:
41+
delta.cache_read_input_tokens !== undefined &&
42+
delta.cache_read_input_tokens > 0
43+
? delta.cache_read_input_tokens
44+
: current.cache_read_input_tokens,
45+
}
46+
}

src/services/api/openai/requestBody.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ export function isOpenAIThinkingEnabled(model: string): boolean {
2323
if (isEnvDefinedFalsy(process.env.OPENAI_ENABLE_THINKING)) return false
2424
// Explicit enable
2525
if (isEnvTruthy(process.env.OPENAI_ENABLE_THINKING)) return true
26-
// Auto-detect from model name (DeepSeek and MiMo models support thinking mode)
26+
// Auto-detect from model name (DeepSeek and MiMo models support thinking mode).
27+
// Grok is intentionally excluded — Grok reasoning models reason automatically
28+
// and do NOT require thinking/enable_thinking request body parameters.
2729
const modelLower = model.toLowerCase()
2830
return modelLower.includes('deepseek') || modelLower.includes('mimo')
2931
}

0 commit comments

Comments
 (0)