From 00cf974a4b7c78ed893b843038071324a8672907 Mon Sep 17 00:00:00 2001 From: claude-code-best Date: Sun, 26 Apr 2026 15:35:54 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=B0=86=20codex=20provider=20?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E5=B7=A5=E5=85=B7=E8=BF=81=E7=A7=BB=E8=87=B3?= =?UTF-8?q?=20@ant/model-provider=20=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将纯转换工具(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 --- packages/@ant/model-provider/src/index.ts | 7 +++ .../src/providers}/codex/callIds.ts | 0 .../src/providers}/codex/convertMessages.ts | 2 +- .../src/providers}/codex/convertTools.ts | 0 .../src/providers/codex/modelMapping.ts | 0 .../api/codex/__tests__/conversion.test.ts | 3 +- .../api/codex/__tests__/streaming.test.ts | 61 +++++++++++++++---- src/services/api/codex/index.ts | 9 ++- src/services/api/codex/preflight.ts | 2 +- src/services/api/codex/streaming.ts | 2 +- 10 files changed, 65 insertions(+), 21 deletions(-) rename {src/services/api => packages/@ant/model-provider/src/providers}/codex/callIds.ts (100%) rename {src/services/api => packages/@ant/model-provider/src/providers}/codex/convertMessages.ts (99%) rename {src/services/api => packages/@ant/model-provider/src/providers}/codex/convertTools.ts (100%) rename src/services/api/codex/model.ts => packages/@ant/model-provider/src/providers/codex/modelMapping.ts (100%) diff --git a/packages/@ant/model-provider/src/index.ts b/packages/@ant/model-provider/src/index.ts index a4acf428c..6f0ccdbaf 100644 --- a/packages/@ant/model-provider/src/index.ts +++ b/packages/@ant/model-provider/src/index.ts @@ -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' diff --git a/src/services/api/codex/callIds.ts b/packages/@ant/model-provider/src/providers/codex/callIds.ts similarity index 100% rename from src/services/api/codex/callIds.ts rename to packages/@ant/model-provider/src/providers/codex/callIds.ts diff --git a/src/services/api/codex/convertMessages.ts b/packages/@ant/model-provider/src/providers/codex/convertMessages.ts similarity index 99% rename from src/services/api/codex/convertMessages.ts rename to packages/@ant/model-provider/src/providers/codex/convertMessages.ts index 12e465de8..5bb8b1d31 100644 --- a/src/services/api/codex/convertMessages.ts +++ b/packages/@ant/model-provider/src/providers/codex/convertMessages.ts @@ -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, diff --git a/src/services/api/codex/convertTools.ts b/packages/@ant/model-provider/src/providers/codex/convertTools.ts similarity index 100% rename from src/services/api/codex/convertTools.ts rename to packages/@ant/model-provider/src/providers/codex/convertTools.ts diff --git a/src/services/api/codex/model.ts b/packages/@ant/model-provider/src/providers/codex/modelMapping.ts similarity index 100% rename from src/services/api/codex/model.ts rename to packages/@ant/model-provider/src/providers/codex/modelMapping.ts diff --git a/src/services/api/codex/__tests__/conversion.test.ts b/src/services/api/codex/__tests__/conversion.test.ts index 6e5f8aaf8..6ab211ad3 100644 --- a/src/services/api/codex/__tests__/conversion.test.ts +++ b/src/services/api/codex/__tests__/conversion.test.ts @@ -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 () => { diff --git a/src/services/api/codex/__tests__/streaming.test.ts b/src/services/api/codex/__tests__/streaming.test.ts index 70e4eb716..600cb6c1a 100644 --- a/src/services/api/codex/__tests__/streaming.test.ts +++ b/src/services/api/codex/__tests__/streaming.test.ts @@ -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', () => ({ diff --git a/src/services/api/codex/index.ts b/src/services/api/codex/index.ts index eb6e010bf..0bfaabc9d 100644 --- a/src/services/api/codex/index.ts +++ b/src/services/api/codex/index.ts @@ -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, diff --git a/src/services/api/codex/preflight.ts b/src/services/api/codex/preflight.ts index 5f9856458..2c6ec9b8b 100644 --- a/src/services/api/codex/preflight.ts +++ b/src/services/api/codex/preflight.ts @@ -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 { return typeof value === 'object' && value !== null && !Array.isArray(value) diff --git a/src/services/api/codex/streaming.ts b/src/services/api/codex/streaming.ts index dad8ef156..f8a27c0c6 100644 --- a/src/services/api/codex/streaming.ts +++ b/src/services/api/codex/streaming.ts @@ -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 =