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 =