style: 完成所有文件的lint

This commit is contained in:
claude-code-best
2026-05-01 21:39:30 +08:00
parent d136872cc9
commit 6182015005
1333 changed files with 68255 additions and 77882 deletions

View File

@@ -230,7 +230,11 @@ import { getInitializationStatus } from '../lsp/manager.js'
import { isToolFromMcpServer } from '../mcp/utils.js'
import { recordLLMObservation } from '../langfuse/index.js'
import type { LangfuseSpan } from '../langfuse/index.js'
import { convertMessagesToLangfuse, convertOutputToLangfuse, convertToolsToLangfuse } from '../langfuse/convert.js'
import {
convertMessagesToLangfuse,
convertOutputToLangfuse,
convertToolsToLangfuse,
} from '../langfuse/convert.js'
import { withStreamingVCR, withVCR } from '../vcr.js'
import { CLIENT_REQUEST_ID_HEADER, getAnthropicClient } from './client.js'
import {
@@ -442,7 +446,7 @@ function configureEffortParams(
betas.push(EFFORT_BETA_HEADER)
} else if (typeof effortValue === 'string') {
// Send string effort level as is
outputConfig.effort = effortValue as "high" | "medium" | "low" | "max"
outputConfig.effort = effortValue as 'high' | 'medium' | 'low' | 'max'
betas.push(EFFORT_BETA_HEADER)
} else if (process.env.USER_TYPE === 'ant') {
// Numeric effort override - ant-only (uses anthropic_internal)
@@ -615,7 +619,8 @@ export function userMessageToMessageParam(
role: 'user',
content: (Array.isArray(message.message!.content)
? [...message.message!.content]
: message.message!.content) as import('@anthropic-ai/sdk/resources/beta/messages/messages.js').BetaContentBlockParam[],
: message.message!
.content) as import('@anthropic-ai/sdk/resources/beta/messages/messages.js').BetaContentBlockParam[],
}
}
@@ -666,7 +671,9 @@ export function assistantMessageToMessageParam(
content:
typeof message.message!.content === 'string'
? message.message!.content
: message.message!.content!.map(stripGeminiProviderMetadata) as BetaContentBlockParam[],
: (message.message!.content!.map(
stripGeminiProviderMetadata,
) as BetaContentBlockParam[]),
}
}
@@ -681,10 +688,8 @@ function stripGeminiProviderMetadata<T extends BetaContentBlockParam | string>(
}
const obj = contentBlock as unknown as Record<string, unknown>
const {
_geminiThoughtSignature: _unusedGeminiThoughtSignature,
...rest
} = obj
const { _geminiThoughtSignature: _unusedGeminiThoughtSignature, ...rest } =
obj
return rest as unknown as T
}
@@ -1343,7 +1348,13 @@ async function* queryModel(
// OpenAI emulates Anthropic's dynamic tool loading client-side. It needs
// the full tool pool so ToolSearchTool can search deferred MCP tools that
// were intentionally filtered out of the initial API tool list above.
yield* queryModelOpenAI(messagesForAPI, systemPrompt, tools, signal, options)
yield* queryModelOpenAI(
messagesForAPI,
systemPrompt,
tools,
signal,
options,
)
return
}
@@ -1362,7 +1373,13 @@ async function* queryModel(
if (getAPIProvider() === 'grok') {
const { queryModelGrok } = await import('./grok/index.js')
yield* queryModelGrok(messagesForAPI, systemPrompt, filteredTools, signal, options)
yield* queryModelGrok(
messagesForAPI,
systemPrompt,
filteredTools,
signal,
options,
)
return
}
@@ -2127,7 +2144,8 @@ async function* queryModel(
})
throw new Error('Content block is not a connector_text block')
}
;(contentBlock as { connector_text: string }).connector_text += delta.connector_text
;(contentBlock as { connector_text: string }).connector_text +=
delta.connector_text
} else {
switch (delta.type) {
case 'citations_delta':
@@ -2206,7 +2224,8 @@ async function* queryModel(
})
throw new Error('Content block is not a thinking block')
}
;(contentBlock as { thinking: string }).thinking += delta.thinking
;(contentBlock as { thinking: string }).thinking +=
delta.thinking
break
}
}
@@ -2298,7 +2317,10 @@ async function* queryModel(
}
// Update cost
const costUSDForPart = calculateUSDCost(resolvedModel, usage as unknown as BetaUsage)
const costUSDForPart = calculateUSDCost(
resolvedModel,
usage as unknown as BetaUsage,
)
costUSD += addToTotalSessionCost(
costUSDForPart,
usage as unknown as BetaUsage,
@@ -2887,10 +2909,14 @@ async function* queryModel(
// message_delta handler before any yield. Fallback pushes to newMessages
// then yields, so tracking must be here to survive .return() at the yield.
if (fallbackMessage) {
const fallbackUsage = fallbackMessage.message.usage as BetaMessageDeltaUsage
const fallbackUsage = fallbackMessage.message
.usage as BetaMessageDeltaUsage
usage = updateUsage(EMPTY_USAGE, fallbackUsage)
stopReason = fallbackMessage.message.stop_reason as BetaStopReason
const fallbackCost = calculateUSDCost(resolvedModel, fallbackUsage as unknown as BetaUsage)
const fallbackCost = calculateUSDCost(
resolvedModel,
fallbackUsage as unknown as BetaUsage,
)
costUSD += addToTotalSessionCost(
fallbackCost,
fallbackUsage as unknown as BetaUsage,
@@ -2946,7 +2972,9 @@ async function* queryModel(
void options.getToolPermissionContext().then(permissionContext => {
logAPISuccessAndDuration({
model:
(newMessages[0]?.message.model as string | undefined) ?? partialMessage?.model ?? options.model,
(newMessages[0]?.message.model as string | undefined) ??
partialMessage?.model ??
options.model,
preNormalizedModel: options.model,
usage,
start,

View File

@@ -19,9 +19,20 @@ import type { SystemPrompt } from '../../../utils/systemPromptType.js'
import type { ThinkingConfig } from '../../../utils/thinking.js'
import type { Options } from '../claude.js'
import { recordLLMObservation } from '../../../services/langfuse/tracing.js'
import { convertMessagesToLangfuse, convertOutputToLangfuse, convertToolsToLangfuse } from '../../../services/langfuse/convert.js'
import {
convertMessagesToLangfuse,
convertOutputToLangfuse,
convertToolsToLangfuse,
} from '../../../services/langfuse/convert.js'
import { streamGeminiGenerateContent } from './client.js'
import { anthropicMessagesToGemini, resolveGeminiModel, adaptGeminiStreamToAnthropic, anthropicToolsToGemini, anthropicToolChoiceToGemini, GEMINI_THOUGHT_SIGNATURE_FIELD } from '@ant/model-provider'
import {
anthropicMessagesToGemini,
resolveGeminiModel,
adaptGeminiStreamToAnthropic,
anthropicToolsToGemini,
anthropicToolChoiceToGemini,
GEMINI_THOUGHT_SIGNATURE_FIELD,
} from '@ant/model-provider'
export async function* queryModelGemini(
messages: Message[],
@@ -209,7 +220,9 @@ export async function* queryModelGemini(
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,
})
}
}

View File

@@ -2,7 +2,7 @@ import { describe, expect, test, beforeEach, afterEach, mock } from 'bun:test'
// Defensive: agent.test.ts can corrupt Bun's src/* path alias at runtime.
mock.module('src/utils/proxy.js', () => ({
getProxyFetchOptions: () => ({} as any),
getProxyFetchOptions: () => ({}) as any,
}))
import { getGrokClient, clearGrokClientCache } from '../client.js'

View File

@@ -1,13 +1,24 @@
import type { BetaToolUnion } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs'
import type { SystemPrompt } from '../../../utils/systemPromptType.js'
import type { Message, StreamEvent, SystemAPIErrorMessage, AssistantMessage } from '../../../types/message.js'
import type {
Message,
StreamEvent,
SystemAPIErrorMessage,
AssistantMessage,
} from '../../../types/message.js'
import type { Tools } from '../../../Tool.js'
import type {
ChatCompletionChunk,
ChatCompletionCreateParamsStreaming,
} from 'openai/resources/chat/completions/completions.mjs'
import { getGrokClient } from './client.js'
import { anthropicMessagesToOpenAI, anthropicToolsToOpenAI, anthropicToolChoiceToOpenAI, adaptOpenAIStreamToAnthropic, resolveGrokModel } from '@ant/model-provider'
import {
anthropicMessagesToOpenAI,
anthropicToolsToOpenAI,
anthropicToolChoiceToOpenAI,
adaptOpenAIStreamToAnthropic,
resolveGrokModel,
} from '@ant/model-provider'
import { normalizeMessagesForAPI } from '../../../utils/messages.js'
import type { SDKAssistantMessageError } from '../../../entrypoints/agentSdkTypes.js'
import { toolToAPISchema } from '../../../utils/api.js'
@@ -15,7 +26,11 @@ import { logForDebugging } from '../../../utils/debug.js'
import { addToTotalSessionCost } from '../../../cost-tracker.js'
import { calculateUSDCost } from '../../../utils/modelCost.js'
import { recordLLMObservation } from '../../../services/langfuse/tracing.js'
import { convertMessagesToLangfuse, convertOutputToLangfuse, convertToolsToLangfuse } from '../../../services/langfuse/convert.js'
import {
convertMessagesToLangfuse,
convertOutputToLangfuse,
convertToolsToLangfuse,
} from '../../../services/langfuse/convert.js'
import type { Options } from '../claude.js'
import { randomUUID } from 'crypto'
import {
@@ -56,11 +71,16 @@ export async function* queryModelGrok(
const standardTools = toolSchemas.filter(
(t): t is BetaToolUnion & { type: string } => {
const anyT = t as unknown as Record<string, unknown>
return anyT.type !== 'advisor_20260301' && anyT.type !== 'computer_20250124'
return (
anyT.type !== 'advisor_20260301' && anyT.type !== 'computer_20250124'
)
},
)
const openaiMessages = anthropicMessagesToOpenAI(messagesForAPI, systemPrompt)
const openaiMessages = anthropicMessagesToOpenAI(
messagesForAPI,
systemPrompt,
)
const openaiTools = anthropicToolsToOpenAI(standardTools)
const openaiToolChoice = anthropicToolChoiceToOpenAI(options.toolChoice)
@@ -70,7 +90,9 @@ export async function* queryModelGrok(
source: options.querySource,
})
logForDebugging(`[Grok] Calling model=${grokModel}, messages=${openaiMessages.length}, tools=${openaiTools.length}`)
logForDebugging(
`[Grok] Calling model=${grokModel}, messages=${openaiMessages.length}, tools=${openaiTools.length}`,
)
const stream = await client.chat.completions.create(
{
@@ -91,7 +113,10 @@ export async function* queryModelGrok(
},
)
const adaptedStream = adaptOpenAIStreamToAnthropic(stream as AsyncIterable<ChatCompletionChunk>, grokModel)
const adaptedStream = adaptOpenAIStreamToAnthropic(
stream as AsyncIterable<ChatCompletionChunk>,
grokModel,
)
const contentBlocks: Record<number, any> = {}
const collectedMessages: AssistantMessage[] = []
@@ -111,7 +136,7 @@ export async function* queryModelGrok(
partialMessage = (event as any).message
ttftMs = Date.now() - start
if ((event as any).message?.usage) {
usage = { ...usage, ...((event as any).message.usage) }
usage = { ...usage, ...(event as any).message.usage }
}
break
}
@@ -175,7 +200,10 @@ export async function* queryModelGrok(
break
}
if (event.type === 'message_stop' && usage.input_tokens + usage.output_tokens > 0) {
if (
event.type === 'message_stop' &&
usage.input_tokens + usage.output_tokens > 0
) {
const costUSD = calculateUSDCost(grokModel, usage as any)
addToTotalSessionCost(costUSD, usage as any, options.model)
}
@@ -210,7 +238,9 @@ export async function* queryModelGrok(
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,
})
}
}

View File

@@ -195,7 +195,8 @@ export function logAPIQuery({
previousRequestId?: string | null
}): void {
const thinkingType = thinkingConfig?.type ?? 'disabled'
const thinkingBudgetTokens = thinkingConfig?.type === 'enabled' ? thinkingConfig.budgetTokens : undefined
const thinkingBudgetTokens =
thinkingConfig?.type === 'enabled' ? thinkingConfig.budgetTokens : undefined
logEvent('tengu_api_query', {
model: model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
messagesLength,
@@ -662,7 +663,9 @@ export function logAPISuccessAndDuration({
let connectorCount = 0
for (const msg of newMessages) {
const contentArr = Array.isArray(msg.message.content) ? msg.message.content : []
const contentArr = Array.isArray(msg.message.content)
? msg.message.content
: []
for (const block of contentArr) {
if (typeof block === 'string') continue
if (block.type === 'text') {
@@ -670,14 +673,19 @@ export function logAPISuccessAndDuration({
} else if (feature('CONNECTOR_TEXT') && isConnectorTextBlock(block)) {
connectorCount++
} else if (block.type === 'thinking') {
thinkingLen += (block as { type: 'thinking'; thinking: string }).thinking.length
thinkingLen += (block as { type: 'thinking'; thinking: string })
.thinking.length
} else if (
block.type === 'tool_use' ||
block.type === 'server_tool_use' ||
(block.type as string) === 'mcp_tool_use'
) {
const inputLen = jsonStringify((block as { input: unknown }).input).length
const sanitizedName = sanitizeToolNameForAnalytics((block as { name: string }).name)
const inputLen = jsonStringify(
(block as { input: unknown }).input,
).length
const sanitizedName = sanitizeToolNameForAnalytics(
(block as { name: string }).name,
)
toolLengths[sanitizedName] =
(toolLengths[sanitizedName] ?? 0) + inputLen
hasToolUse = true

View File

@@ -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()
})
})
})

View File

@@ -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,
})

View File

@@ -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,
})
}
}

View File

@@ -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,
}),
}
}

View File

@@ -459,7 +459,8 @@ export async function checkResponseForCacheBreak(
// assistant message timestamp in the messages array (before the current response)
const lastAssistantMessage = messages.findLast(m => m.type === 'assistant')
const timeSinceLastAssistantMsg = lastAssistantMessage
? Date.now() - new Date(lastAssistantMessage.timestamp as string | number).getTime()
? Date.now() -
new Date(lastAssistantMessage.timestamp as string | number).getTime()
: null
// Skip the first call — no previous value to compare against

View File

@@ -539,10 +539,7 @@ export function getRetryDelay(
}
}
const baseDelay = Math.min(
BASE_DELAY_MS * 2 ** (attempt - 1),
maxDelayMs,
)
const baseDelay = Math.min(BASE_DELAY_MS * 2 ** (attempt - 1), maxDelayMs)
const jitter = Math.random() * 0.25 * baseDelay
return baseDelay + jitter
}