refactor: 将 codex provider 转换工具迁移至 @ant/model-provider 包

将纯转换工具(callIds、modelMapping、convertMessages、convertTools)
从 src/services/api/codex/ 迁移到 packages/@ant/model-provider/src/providers/codex/,
与 OpenAI/Gemini/Grok provider 保持一致的代码组织模式。同时修复了
streaming.test.ts 中缺失的 mock 导出(logAntError、context 常量、langfuse 导出)。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
claude-code-best
2026-04-26 15:35:54 +08:00
parent 7d4b27c01a
commit 00cf974a4b
10 changed files with 65 additions and 21 deletions

View File

@@ -61,3 +61,10 @@ export { anthropicMessagesToOpenAI } from './shared/openaiConvertMessages.js'
export type { ConvertMessagesOptions } from './shared/openaiConvertMessages.js'
export { anthropicToolsToOpenAI, anthropicToolChoiceToOpenAI } from './shared/openaiConvertTools.js'
export { adaptOpenAIStreamToAnthropic } from './shared/openaiStreamAdapter.js'
// Codex provider utilities
export { normalizeCodexCallId, resolveCodexCallId, createCodexFallbackCallId } from './providers/codex/callIds.js'
export { resolveCodexModel, resolveCodexMaxTokens } from './providers/codex/modelMapping.js'
export { anthropicMessagesToCodexInput } from './providers/codex/convertMessages.js'
export type { CodexImageConversionOptions } from './providers/codex/convertMessages.js'
export { anthropicToolsToCodex } from './providers/codex/convertTools.js'

View File

@@ -4,7 +4,7 @@ import type {
ResponseInputItem,
ResponseInputText,
} from 'openai/resources/responses/responses.mjs'
import type { Message } from '../../../types/message.js'
import type { Message } from '../../types/index.js'
import {
normalizeCodexCallId,
resolveCodexCallId,

View File

@@ -1,7 +1,6 @@
import { describe, expect, test } from 'bun:test'
import { createAssistantMessage, createUserMessage } from '../../../../utils/messages.js'
import { anthropicMessagesToCodexInput } from '../convertMessages.js'
import { anthropicToolsToCodex } from '../convertTools.js'
import { anthropicMessagesToCodexInput, anthropicToolsToCodex } from '@ant/model-provider'
describe('anthropicMessagesToCodexInput', () => {
test('replays assistant tool calls and user tool results in order', async () => {

View File

@@ -97,33 +97,68 @@ mock.module('../client.js', () => ({
}),
}))
mock.module('../convertMessages.js', () => ({
anthropicMessagesToCodexInput: () => [],
}))
mock.module('../convertTools.js', () => ({
anthropicToolsToCodex: () => [],
}))
mock.module('../model.js', () => ({
resolveCodexModel: () => 'gpt-5.4',
resolveCodexMaxTokens: () => 4096,
}))
// Mock only model resolution — conversion functions can use real implementations
// since the client mock controls API responses.
mock.module('@ant/model-provider', () => {
// Import the real module to preserve conversion functions
const real = require('@ant/model-provider')
return {
...real,
resolveCodexModel: () => 'gpt-5.4',
resolveCodexMaxTokens: () => 4096,
}
})
mock.module('../../../../utils/context.js', () => ({
MODEL_CONTEXT_WINDOW_DEFAULT: 200_000,
COMPACT_MAX_OUTPUT_TOKENS: 20_000,
CAPPED_DEFAULT_MAX_TOKENS: 8_000,
ESCALATED_MAX_TOKENS: 64_000,
is1mContextDisabled: () => false,
has1mContext: () => false,
modelSupports1M: () => false,
getContextWindowForModel: () => 200_000,
getSonnet1mExpTreatmentEnabled: () => false,
calculateContextPercentages: () => ({}),
getModelMaxOutputTokens: () => ({ upperLimit: 4096 }),
getMaxThinkingTokensForModel: () => 0,
}))
mock.module('../../../../utils/api.js', () => ({
toolToAPISchema: async () => ({}),
appendSystemContext: () => {},
prependUserContext: () => {},
logAPIPrefix: () => {},
splitSysPromptPrefix: () => ({ prefix: '', rest: [] }),
logContextMetrics: async () => {},
normalizeToolInput: (input: any) => input,
normalizeToolInputForAPI: (input: any) => input,
}))
mock.module('../../../../utils/debug.js', () => ({
mock.module('src/utils/debug.ts', () => ({
getMinDebugLogLevel: () => 'debug' as const,
isDebugMode: () => false,
enableDebugLogging: () => false,
getDebugFilter: () => null,
isDebugToStdErr: () => false,
getDebugFilePath: () => null as string | null,
setHasFormattedOutput: () => {},
getHasFormattedOutput: () => false,
flushDebugLogs: async () => {},
logForDebugging: () => {},
getDebugLogPath: () => '/tmp/mock-debug.log',
logAntError: () => {},
}))
mock.module('../../../../services/langfuse/tracing.js', () => ({
createTrace: () => null,
recordLLMObservation: () => {},
recordToolObservation: () => {},
createToolBatchSpan: () => null,
endToolBatchSpan: () => {},
createSubagentTrace: () => null,
createChildSpan: () => null,
endTrace: () => {},
}))
mock.module('../../../../services/langfuse/convert.js', () => ({

View File

@@ -27,15 +27,18 @@ import {
convertOutputToLangfuse,
convertToolsToLangfuse,
} from '../../../services/langfuse/convert.js'
import { anthropicMessagesToCodexInput } from './convertMessages.js'
import { anthropicToolsToCodex } from './convertTools.js'
import {
anthropicMessagesToCodexInput,
anthropicToolsToCodex,
resolveCodexMaxTokens,
resolveCodexModel,
} from '@ant/model-provider'
import { getCodexClient } from './client.js'
import { uploadCodexBase64Image } from './imageUpload.js'
import {
getCodexConfigurationError,
normalizeCodexError,
} from './errors.js'
import { resolveCodexMaxTokens, resolveCodexModel } from './model.js'
import { sanitizeCodexRequest } from './preflight.js'
import {
addCodexUsage,

View File

@@ -4,7 +4,7 @@ import type {
ResponseInputItem,
Tool,
} from 'openai/resources/responses/responses.mjs'
import { normalizeCodexCallId } from './callIds.js'
import { normalizeCodexCallId } from '@ant/model-provider'
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value)

View File

@@ -14,7 +14,7 @@ import {
normalizeContentFromAPI,
} from '../../../utils/messages.js'
import { getCodexClient } from './client.js'
import { resolveCodexCallId } from './callIds.js'
import { resolveCodexCallId } from '@ant/model-provider'
import { toStreamingCodexRequest } from './preflight.js'
export type RawAssistantBlock =