mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-18 22:35:51 +00:00
style: 完成所有文件的lint
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
import { describe, expect, test, beforeEach, afterEach } from 'bun:test'
|
||||
import { isOpenAIThinkingEnabled, buildOpenAIRequestBody } from '../requestBody.js'
|
||||
import {
|
||||
isOpenAIThinkingEnabled,
|
||||
buildOpenAIRequestBody,
|
||||
} from '../requestBody.js'
|
||||
|
||||
describe('isOpenAIThinkingEnabled', () => {
|
||||
const originalEnv = {
|
||||
@@ -81,7 +84,9 @@ describe('isOpenAIThinkingEnabled', () => {
|
||||
})
|
||||
|
||||
test('returns true when model name is namespaced for deepseek-reasoner', () => {
|
||||
expect(isOpenAIThinkingEnabled('TokenService/deepseek-reasoner')).toBe(true)
|
||||
expect(isOpenAIThinkingEnabled('TokenService/deepseek-reasoner')).toBe(
|
||||
true,
|
||||
)
|
||||
})
|
||||
|
||||
test('returns true when model name is "deepseek-v3.2"', () => {
|
||||
@@ -185,14 +190,20 @@ describe('buildOpenAIRequestBody — thinking params', () => {
|
||||
})
|
||||
|
||||
test('does NOT include thinking params when disabled', () => {
|
||||
const body = buildOpenAIRequestBody({ ...baseParams, enableThinking: false })
|
||||
const body = buildOpenAIRequestBody({
|
||||
...baseParams,
|
||||
enableThinking: false,
|
||||
})
|
||||
expect(body.thinking).toBeUndefined()
|
||||
expect(body.enable_thinking).toBeUndefined()
|
||||
expect(body.chat_template_kwargs).toBeUndefined()
|
||||
})
|
||||
|
||||
test('always includes stream and stream_options', () => {
|
||||
const body = buildOpenAIRequestBody({ ...baseParams, enableThinking: false })
|
||||
const body = buildOpenAIRequestBody({
|
||||
...baseParams,
|
||||
enableThinking: false,
|
||||
})
|
||||
expect(body.stream).toBe(true)
|
||||
expect(body.stream_options).toEqual({ include_usage: true })
|
||||
})
|
||||
@@ -216,7 +227,10 @@ describe('buildOpenAIRequestBody — thinking params', () => {
|
||||
})
|
||||
|
||||
test('excludes temperature when thinking is off and no override', () => {
|
||||
const body = buildOpenAIRequestBody({ ...baseParams, enableThinking: false })
|
||||
const body = buildOpenAIRequestBody({
|
||||
...baseParams,
|
||||
enableThinking: false,
|
||||
})
|
||||
expect(body.temperature).toBeUndefined()
|
||||
})
|
||||
|
||||
@@ -232,8 +246,11 @@ describe('buildOpenAIRequestBody — thinking params', () => {
|
||||
})
|
||||
|
||||
test('excludes tools when empty', () => {
|
||||
const body = buildOpenAIRequestBody({ ...baseParams, enableThinking: false })
|
||||
const body = buildOpenAIRequestBody({
|
||||
...baseParams,
|
||||
enableThinking: false,
|
||||
})
|
||||
expect(body.tools).toBeUndefined()
|
||||
expect(body.tool_choice).toBeUndefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -56,8 +56,12 @@ export function getOpenAIClient(options?: {
|
||||
maxRetries: options?.maxRetries ?? 0,
|
||||
timeout: parseInt(process.env.API_TIMEOUT_MS || String(600 * 1000), 10),
|
||||
dangerouslyAllowBrowser: true,
|
||||
...(process.env.OPENAI_ORG_ID && { organization: process.env.OPENAI_ORG_ID }),
|
||||
...(process.env.OPENAI_PROJECT_ID && { project: process.env.OPENAI_PROJECT_ID }),
|
||||
...(process.env.OPENAI_ORG_ID && {
|
||||
organization: process.env.OPENAI_ORG_ID,
|
||||
}),
|
||||
...(process.env.OPENAI_PROJECT_ID && {
|
||||
project: process.env.OPENAI_PROJECT_ID,
|
||||
}),
|
||||
fetchOptions: getProxyFetchOptions({ forAnthropicAPI: false }),
|
||||
fetch: wrappedFetch,
|
||||
})
|
||||
|
||||
@@ -10,11 +10,15 @@ import type {
|
||||
import type { AgentId } from '../../../types/ids.js'
|
||||
import type { Tools } from '../../../Tool.js'
|
||||
import type { Stream } from 'openai/streaming.mjs'
|
||||
import type {
|
||||
ChatCompletionCreateParamsStreaming,
|
||||
} from 'openai/resources/chat/completions/completions.mjs'
|
||||
import type { ChatCompletionCreateParamsStreaming } from 'openai/resources/chat/completions/completions.mjs'
|
||||
import { getOpenAIClient } from './client.js'
|
||||
import { anthropicMessagesToOpenAI, resolveOpenAIModel, adaptOpenAIStreamToAnthropic, anthropicToolsToOpenAI, anthropicToolChoiceToOpenAI } from '@ant/model-provider'
|
||||
import {
|
||||
anthropicMessagesToOpenAI,
|
||||
resolveOpenAIModel,
|
||||
adaptOpenAIStreamToAnthropic,
|
||||
anthropicToolsToOpenAI,
|
||||
anthropicToolChoiceToOpenAI,
|
||||
} from '@ant/model-provider'
|
||||
import { normalizeMessagesForAPI } from '../../../utils/messages.js'
|
||||
import { toolToAPISchema } from '../../../utils/api.js'
|
||||
import {
|
||||
@@ -24,10 +28,22 @@ import {
|
||||
import { logForDebugging } from '../../../utils/debug.js'
|
||||
import { addToTotalSessionCost } from '../../../cost-tracker.js'
|
||||
import { calculateUSDCost } from '../../../utils/modelCost.js'
|
||||
import { isOpenAIThinkingEnabled, resolveOpenAIMaxTokens, buildOpenAIRequestBody } from './requestBody.js'
|
||||
import {
|
||||
isOpenAIThinkingEnabled,
|
||||
resolveOpenAIMaxTokens,
|
||||
buildOpenAIRequestBody,
|
||||
} from './requestBody.js'
|
||||
import { recordLLMObservation } from '../../../services/langfuse/tracing.js'
|
||||
import { convertMessagesToLangfuse, convertOutputToLangfuse, convertToolsToLangfuse } from '../../../services/langfuse/convert.js'
|
||||
export { isOpenAIThinkingEnabled, resolveOpenAIMaxTokens, buildOpenAIRequestBody }
|
||||
import {
|
||||
convertMessagesToLangfuse,
|
||||
convertOutputToLangfuse,
|
||||
convertToolsToLangfuse,
|
||||
} from '../../../services/langfuse/convert.js'
|
||||
export {
|
||||
isOpenAIThinkingEnabled,
|
||||
resolveOpenAIMaxTokens,
|
||||
buildOpenAIRequestBody,
|
||||
}
|
||||
import { getModelMaxOutputTokens } from '../../../utils/context.js'
|
||||
import type { Options } from '../claude.js'
|
||||
import { randomUUID } from 'crypto'
|
||||
@@ -81,7 +97,9 @@ function prependDeferredToolListIfNeeded(
|
||||
]
|
||||
}
|
||||
|
||||
function isOpenAIConvertibleMessage(msg: Message): msg is AssistantMessage | UserMessage {
|
||||
function isOpenAIConvertibleMessage(
|
||||
msg: Message,
|
||||
): msg is AssistantMessage | UserMessage {
|
||||
return msg.type === 'assistant' || msg.type === 'user'
|
||||
}
|
||||
|
||||
@@ -95,11 +113,24 @@ function assembleFinalAssistantOutputs(params: {
|
||||
contentBlocks: Record<number, any>
|
||||
tools: Tools
|
||||
agentId: string | undefined
|
||||
usage: { input_tokens: number; output_tokens: number; cache_creation_input_tokens: number; cache_read_input_tokens: number }
|
||||
usage: {
|
||||
input_tokens: number
|
||||
output_tokens: number
|
||||
cache_creation_input_tokens: number
|
||||
cache_read_input_tokens: number
|
||||
}
|
||||
stopReason: string | null
|
||||
maxTokens: number
|
||||
}): (AssistantMessage | SystemAPIErrorMessage)[] {
|
||||
const { partialMessage, contentBlocks, tools, agentId, usage, stopReason, maxTokens } = params
|
||||
const {
|
||||
partialMessage,
|
||||
contentBlocks,
|
||||
tools,
|
||||
agentId,
|
||||
usage,
|
||||
stopReason,
|
||||
maxTokens,
|
||||
} = params
|
||||
const outputs: (AssistantMessage | SystemAPIErrorMessage)[] = []
|
||||
|
||||
const allBlocks = Object.keys(contentBlocks)
|
||||
@@ -111,7 +142,11 @@ function assembleFinalAssistantOutputs(params: {
|
||||
outputs.push({
|
||||
message: {
|
||||
...partialMessage,
|
||||
content: normalizeContentFromAPI(allBlocks, tools, agentId as AgentId | undefined),
|
||||
content: normalizeContentFromAPI(
|
||||
allBlocks,
|
||||
tools,
|
||||
agentId as AgentId | undefined,
|
||||
),
|
||||
usage,
|
||||
stop_reason: stopReason,
|
||||
stop_sequence: null,
|
||||
@@ -124,12 +159,15 @@ function assembleFinalAssistantOutputs(params: {
|
||||
}
|
||||
|
||||
if (stopReason === 'max_tokens') {
|
||||
outputs.push(createAssistantAPIErrorMessage({
|
||||
content: `Output truncated: response exceeded the ${maxTokens} token limit. ` +
|
||||
`Set OPENAI_MAX_TOKENS or CLAUDE_CODE_MAX_OUTPUT_TOKENS to override.`,
|
||||
apiError: 'max_output_tokens',
|
||||
error: 'max_output_tokens',
|
||||
}))
|
||||
outputs.push(
|
||||
createAssistantAPIErrorMessage({
|
||||
content:
|
||||
`Output truncated: response exceeded the ${maxTokens} token limit. ` +
|
||||
`Set OPENAI_MAX_TOKENS or CLAUDE_CODE_MAX_OUTPUT_TOKENS to override.`,
|
||||
apiError: 'max_output_tokens',
|
||||
error: 'max_output_tokens',
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
return outputs
|
||||
@@ -217,7 +255,9 @@ export async function* queryModelOpenAI(
|
||||
|
||||
// 8. Convert messages and tools to OpenAI format
|
||||
const enableThinking = isOpenAIThinkingEnabled(openaiModel)
|
||||
const openAIConvertibleMessages = messagesForAPI.filter(isOpenAIConvertibleMessage)
|
||||
const openAIConvertibleMessages = messagesForAPI.filter(
|
||||
isOpenAIConvertibleMessage,
|
||||
)
|
||||
const messagesWithDeferredToolList = prependDeferredToolListIfNeeded(
|
||||
openAIConvertibleMessages,
|
||||
tools,
|
||||
@@ -264,7 +304,10 @@ export async function* queryModelOpenAI(
|
||||
// 3. CLAUDE_CODE_MAX_OUTPUT_TOKENS env var (generic override)
|
||||
// 4. upperLimit default (64000)
|
||||
const { upperLimit } = getModelMaxOutputTokens(openaiModel)
|
||||
const maxTokens = resolveOpenAIMaxTokens(upperLimit, options.maxOutputTokensOverride)
|
||||
const maxTokens = resolveOpenAIMaxTokens(
|
||||
upperLimit,
|
||||
options.maxOutputTokensOverride,
|
||||
)
|
||||
|
||||
// 11. Get client
|
||||
const client = getOpenAIClient({
|
||||
@@ -287,10 +330,7 @@ export async function* queryModelOpenAI(
|
||||
maxTokens,
|
||||
temperatureOverride: options.temperatureOverride,
|
||||
})
|
||||
const stream = await client.chat.completions.create(
|
||||
requestBody,
|
||||
{ signal },
|
||||
)
|
||||
const stream = await client.chat.completions.create(requestBody, { signal })
|
||||
|
||||
// 12. Convert OpenAI stream to Anthropic events, then process into
|
||||
// AssistantMessage + StreamEvent (matching the Anthropic path behavior)
|
||||
@@ -373,8 +413,13 @@ export async function* queryModelOpenAI(
|
||||
// here and injected so tokenCountWithEstimation() can read it.
|
||||
if (partialMessage) {
|
||||
for (const output of assembleFinalAssistantOutputs({
|
||||
partialMessage, contentBlocks, tools, agentId: options.agentId,
|
||||
usage, stopReason, maxTokens,
|
||||
partialMessage,
|
||||
contentBlocks,
|
||||
tools,
|
||||
agentId: options.agentId,
|
||||
usage,
|
||||
stopReason,
|
||||
maxTokens,
|
||||
})) {
|
||||
if (output.type === 'assistant') {
|
||||
collectedMessages.push(output)
|
||||
@@ -424,8 +469,13 @@ export async function* queryModelOpenAI(
|
||||
// Safety: if stream ended without message_stop, assemble and yield whatever we have
|
||||
if (partialMessage) {
|
||||
for (const output of assembleFinalAssistantOutputs({
|
||||
partialMessage, contentBlocks, tools, agentId: options.agentId,
|
||||
usage, stopReason, maxTokens,
|
||||
partialMessage,
|
||||
contentBlocks,
|
||||
tools,
|
||||
agentId: options.agentId,
|
||||
usage,
|
||||
stopReason,
|
||||
maxTokens,
|
||||
})) {
|
||||
yield output
|
||||
}
|
||||
@@ -436,7 +486,9 @@ export async function* queryModelOpenAI(
|
||||
yield createAssistantAPIErrorMessage({
|
||||
content: `API Error: ${errorMessage}`,
|
||||
apiError: 'api_error',
|
||||
error: (error instanceof Error ? error : new Error(String(error))) as unknown as SDKAssistantMessageError,
|
||||
error: (error instanceof Error
|
||||
? error
|
||||
: new Error(String(error))) as unknown as SDKAssistantMessageError,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
* thinking mode. Extracted from index.ts so tests can import them without
|
||||
* triggering heavy module side-effects (OpenAI client, stream adapter, etc.).
|
||||
*/
|
||||
import type {
|
||||
ChatCompletionCreateParamsStreaming,
|
||||
} from 'openai/resources/chat/completions/completions.mjs'
|
||||
import type { ChatCompletionCreateParamsStreaming } from 'openai/resources/chat/completions/completions.mjs'
|
||||
import { isEnvTruthy, isEnvDefinedFalsy } from '../../../utils/envUtils.js'
|
||||
|
||||
/**
|
||||
@@ -44,10 +42,16 @@ export function resolveOpenAIMaxTokens(
|
||||
upperLimit: number,
|
||||
maxOutputTokensOverride?: number,
|
||||
): number {
|
||||
return maxOutputTokensOverride
|
||||
?? (process.env.OPENAI_MAX_TOKENS ? parseInt(process.env.OPENAI_MAX_TOKENS, 10) || undefined : undefined)
|
||||
?? (process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS ? parseInt(process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS, 10) || undefined : undefined)
|
||||
?? upperLimit
|
||||
return (
|
||||
maxOutputTokensOverride ??
|
||||
(process.env.OPENAI_MAX_TOKENS
|
||||
? parseInt(process.env.OPENAI_MAX_TOKENS, 10) || undefined
|
||||
: undefined) ??
|
||||
(process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS
|
||||
? parseInt(process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS, 10) || undefined
|
||||
: undefined) ??
|
||||
upperLimit
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,7 +78,15 @@ export function buildOpenAIRequestBody(params: {
|
||||
enable_thinking?: boolean
|
||||
chat_template_kwargs?: { thinking: boolean }
|
||||
} {
|
||||
const { model, messages, tools, toolChoice, enableThinking, maxTokens, temperatureOverride } = params
|
||||
const {
|
||||
model,
|
||||
messages,
|
||||
tools,
|
||||
toolChoice,
|
||||
enableThinking,
|
||||
maxTokens,
|
||||
temperatureOverride,
|
||||
} = params
|
||||
return {
|
||||
model,
|
||||
messages,
|
||||
@@ -96,8 +108,9 @@ export function buildOpenAIRequestBody(params: {
|
||||
}),
|
||||
// Only send temperature when thinking mode is off (DeepSeek ignores it anyway,
|
||||
// but other providers may respect it)
|
||||
...(!enableThinking && temperatureOverride !== undefined && {
|
||||
temperature: temperatureOverride,
|
||||
}),
|
||||
...(!enableThinking &&
|
||||
temperatureOverride !== undefined && {
|
||||
temperature: temperatureOverride,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user