mirror of
https://github.com/paoloanzn/free-code.git
synced 2026-10-21 18:42:50 -05:00
feat: add OpenAI Codex API support with new models
- Add OpenAI as new API provider with support for gpt-5.4, gpt-5.3-codex, gpt-5.4-mini models - Extend model configuration system to include OpenAI provider - Add isCodexSubscriber function for OpenAI authentication detection - Update model options and descriptions for Codex models - Create codex-fetch-adapter for OpenAI API integration - Add OpenAI OAuth configuration and authentication URLs - Update default model selection logic for Codex users 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
0c3f07e67a
commit
c3e5419121
8 files changed
+344
-3
No files matched your search
@@ -35,6 +35,10 @@ export const CLAUDE_AI_PROFILE_SCOPE = 'user:profile' as const
|
||||
const CONSOLE_SCOPE = 'org:create_api_key' as const
|
||||
export const OAUTH_BETA_HEADER = 'oauth-2025-04-20' as const
|
||||
|
||||
// OpenAI OAuth scopes
|
||||
export const OPENAI_API_SCOPE = 'api' as const
|
||||
export const OPENAI_CODEX_SCOPE = 'codex' as const
|
||||
|
||||
// Console OAuth scopes - for API key creation via Console
|
||||
export const CONSOLE_OAUTH_SCOPES = [
|
||||
CONSOLE_SCOPE,
|
||||
@@ -50,6 +54,12 @@ export const CLAUDE_AI_OAUTH_SCOPES = [
|
||||
'user:file_upload',
|
||||
] as const
|
||||
|
||||
// OpenAI OAuth scopes - for OpenAI Codex users
|
||||
export const OPENAI_OAUTH_SCOPES = [
|
||||
OPENAI_API_SCOPE,
|
||||
OPENAI_CODEX_SCOPE,
|
||||
] as const
|
||||
|
||||
// All OAuth scopes - union of all scopes used in Claude CLI
|
||||
// When logging in, request all scopes in order to handle both Console -> Claude.ai redirect
|
||||
// Ensure that `OAuthConsentPage` in apps repo is kept in sync with this list.
|
||||
@@ -68,11 +78,16 @@ type OauthConfig = {
|
||||
* /settings/connectors, and other claude.ai web pages.
|
||||
*/
|
||||
CLAUDE_AI_ORIGIN: string
|
||||
// OpenAI OAuth URLs
|
||||
OPENAI_AUTHORIZE_URL: string
|
||||
OPENAI_TOKEN_URL: string
|
||||
OPENAI_CLIENT_ID: string
|
||||
TOKEN_URL: string
|
||||
API_KEY_URL: string
|
||||
ROLES_URL: string
|
||||
CONSOLE_SUCCESS_URL: string
|
||||
CLAUDEAI_SUCCESS_URL: string
|
||||
OPENAI_SUCCESS_URL: string
|
||||
MANUAL_REDIRECT_URL: string
|
||||
CLIENT_ID: string
|
||||
OAUTH_FILE_SUFFIX: string
|
||||
@@ -88,6 +103,10 @@ const PROD_OAUTH_CONFIG = {
|
||||
// visits for attribution. 307s to claude.ai/oauth/authorize in two hops.
|
||||
CLAUDE_AI_AUTHORIZE_URL: 'https://claude.com/cai/oauth/authorize',
|
||||
CLAUDE_AI_ORIGIN: 'https://claude.ai',
|
||||
// OpenAI OAuth URLs
|
||||
OPENAI_AUTHORIZE_URL: 'https://openai.com/oauth/authorize',
|
||||
OPENAI_TOKEN_URL: 'https://openai.com/oauth/token',
|
||||
OPENAI_CLIENT_ID: 'claude-code-client',
|
||||
TOKEN_URL: 'https://platform.claude.com/v1/oauth/token',
|
||||
API_KEY_URL: 'https://api.anthropic.com/api/oauth/claude_cli/create_api_key',
|
||||
ROLES_URL: 'https://api.anthropic.com/api/oauth/claude_cli/roles',
|
||||
@@ -95,6 +114,8 @@ const PROD_OAUTH_CONFIG = {
|
||||
'https://platform.claude.com/buy_credits?returnUrl=/oauth/code/success%3Fapp%3Dclaude-code',
|
||||
CLAUDEAI_SUCCESS_URL:
|
||||
'https://platform.claude.com/oauth/code/success?app=claude-code',
|
||||
OPENAI_SUCCESS_URL:
|
||||
'https://openai.com/oauth/success?app=claude-code',
|
||||
MANUAL_REDIRECT_URL: 'https://platform.claude.com/oauth/code/callback',
|
||||
CLIENT_ID: '9d1c250a-e61b-44d9-88ed-5944d1962f5e',
|
||||
// No suffix for production config
|
||||
@@ -124,6 +145,10 @@ const STAGING_OAUTH_CONFIG =
|
||||
CLAUDE_AI_AUTHORIZE_URL:
|
||||
'https://claude-ai.staging.ant.dev/oauth/authorize',
|
||||
CLAUDE_AI_ORIGIN: 'https://claude-ai.staging.ant.dev',
|
||||
// OpenAI OAuth URLs (staging)
|
||||
OPENAI_AUTHORIZE_URL: 'https://openai.com/oauth/authorize',
|
||||
OPENAI_TOKEN_URL: 'https://openai.com/oauth/token',
|
||||
OPENAI_CLIENT_ID: 'claude-code-staging-client',
|
||||
TOKEN_URL: 'https://platform.staging.ant.dev/v1/oauth/token',
|
||||
API_KEY_URL:
|
||||
'https://api-staging.anthropic.com/api/oauth/claude_cli/create_api_key',
|
||||
@@ -133,6 +158,8 @@ const STAGING_OAUTH_CONFIG =
|
||||
'https://platform.staging.ant.dev/buy_credits?returnUrl=/oauth/code/success%3Fapp%3Dclaude-code',
|
||||
CLAUDEAI_SUCCESS_URL:
|
||||
'https://platform.staging.ant.dev/oauth/code/success?app=claude-code',
|
||||
OPENAI_SUCCESS_URL:
|
||||
'https://openai.com/oauth/success?app=claude-code-staging',
|
||||
MANUAL_REDIRECT_URL:
|
||||
'https://platform.staging.ant.dev/oauth/code/callback',
|
||||
CLIENT_ID: '22422756-60c9-4084-8eb7-27705fd5cf9a',
|
||||
@@ -160,11 +187,16 @@ function getLocalOauthConfig(): OauthConfig {
|
||||
CONSOLE_AUTHORIZE_URL: `${consoleBase}/oauth/authorize`,
|
||||
CLAUDE_AI_AUTHORIZE_URL: `${apps}/oauth/authorize`,
|
||||
CLAUDE_AI_ORIGIN: apps,
|
||||
// OpenAI OAuth URLs (local dev)
|
||||
OPENAI_AUTHORIZE_URL: 'https://openai.com/oauth/authorize',
|
||||
OPENAI_TOKEN_URL: 'https://openai.com/oauth/token',
|
||||
OPENAI_CLIENT_ID: 'claude-code-local-client',
|
||||
TOKEN_URL: `${api}/v1/oauth/token`,
|
||||
API_KEY_URL: `${api}/api/oauth/claude_cli/create_api_key`,
|
||||
ROLES_URL: `${api}/api/oauth/claude_cli/roles`,
|
||||
CONSOLE_SUCCESS_URL: `${consoleBase}/buy_credits?returnUrl=/oauth/code/success%3Fapp%3Dclaude-code`,
|
||||
CLAUDEAI_SUCCESS_URL: `${consoleBase}/oauth/code/success?app=claude-code`,
|
||||
OPENAI_SUCCESS_URL: 'https://openai.com/oauth/success?app=claude-code-local',
|
||||
MANUAL_REDIRECT_URL: `${consoleBase}/oauth/code/callback`,
|
||||
CLIENT_ID: '22422756-60c9-4084-8eb7-27705fd5cf9a',
|
||||
OAUTH_FILE_SUFFIX: '-local-oauth',
|
||||
|
||||
@@ -43,6 +43,34 @@ export function parseScopes(scopeString?: string): string[] {
|
||||
return scopeString?.split(' ').filter(Boolean) ?? []
|
||||
}
|
||||
|
||||
export function buildOpenAIAuthUrl({
|
||||
codeChallenge,
|
||||
state,
|
||||
port,
|
||||
isManual,
|
||||
}: {
|
||||
codeChallenge: string
|
||||
state: string
|
||||
port: number
|
||||
isManual: boolean
|
||||
}): string {
|
||||
const authUrl = new URL(getOauthConfig().OPENAI_AUTHORIZE_URL)
|
||||
authUrl.searchParams.append('client_id', getOauthConfig().OPENAI_CLIENT_ID)
|
||||
authUrl.searchParams.append('response_type', 'code')
|
||||
authUrl.searchParams.append(
|
||||
'redirect_uri',
|
||||
isManual
|
||||
? getOauthConfig().MANUAL_REDIRECT_URL
|
||||
: `http://localhost:${port}/callback`,
|
||||
)
|
||||
authUrl.searchParams.append('scope', 'openai')
|
||||
authUrl.searchParams.append('code_challenge', codeChallenge)
|
||||
authUrl.searchParams.append('code_challenge_method', 'S256')
|
||||
authUrl.searchParams.append('state', state)
|
||||
|
||||
return authUrl.toString()
|
||||
}
|
||||
|
||||
export function buildAuthUrl({
|
||||
codeChallenge,
|
||||
state,
|
||||
|
||||
@@ -1626,6 +1626,17 @@ export function isClaudeAISubscriber(): boolean {
|
||||
return shouldUseClaudeAIAuth(getClaudeAIOAuthTokens()?.scopes)
|
||||
}
|
||||
|
||||
export function isCodexSubscriber(): boolean {
|
||||
// Check if we're using OpenAI API
|
||||
if (getAPIProvider() !== 'openai') {
|
||||
return false
|
||||
}
|
||||
|
||||
// For now, return true if OpenAI provider is configured
|
||||
// This can be extended to check for specific token scopes when available
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user is authenticated via OpenAI Codex OAuth.
|
||||
* Returns true when a valid Codex access token is present in the config.
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* OpenAI Codex API adapter for Claude Code
|
||||
* Provides compatibility layer between Claude's API expectations and OpenAI's Codex API
|
||||
*/
|
||||
|
||||
import type { Message } from '../types/message.js'
|
||||
import { logError } from './log.js'
|
||||
|
||||
/**
|
||||
* OpenAI message format for API requests
|
||||
*/
|
||||
interface OpenAIMessage {
|
||||
role: 'system' | 'user' | 'assistant'
|
||||
content: string | Array<{
|
||||
type: 'text' | 'image_url'
|
||||
text?: string
|
||||
image_url?: {
|
||||
url: string
|
||||
}
|
||||
}>
|
||||
}
|
||||
|
||||
/**
|
||||
* OpenAI API response format
|
||||
*/
|
||||
interface OpenAIResponse {
|
||||
id: string
|
||||
object: string
|
||||
created: number
|
||||
model: string
|
||||
choices: Array<{
|
||||
index: number
|
||||
message: {
|
||||
role: string
|
||||
content: string
|
||||
}
|
||||
finish_reason: string
|
||||
}>
|
||||
usage: {
|
||||
prompt_tokens: number
|
||||
completion_tokens: number
|
||||
total_tokens: number
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Claude Code message format to OpenAI format
|
||||
*/
|
||||
function convertToOpenAIMessage(message: Message): OpenAIMessage {
|
||||
if (typeof message.content === 'string') {
|
||||
return {
|
||||
role: message.role === 'human' ? 'user' : message.role as 'system' | 'assistant',
|
||||
content: message.content,
|
||||
}
|
||||
}
|
||||
|
||||
// Handle multi-modal content
|
||||
const content: Array<any> = []
|
||||
|
||||
for (const item of message.content) {
|
||||
if (item.type === 'text') {
|
||||
content.push({
|
||||
type: 'text',
|
||||
text: item.text,
|
||||
})
|
||||
} else if (item.type === 'image') {
|
||||
// Convert Anthropic base64 image schema to OpenAI format
|
||||
content.push({
|
||||
type: 'image_url',
|
||||
image_url: {
|
||||
url: item.source.type === 'base64'
|
||||
? `data:${item.source.media_type};base64,${item.source.data}`
|
||||
: item.source.data
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
role: message.role === 'human' ? 'user' : message.role as 'system' | 'assistant',
|
||||
content,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a request to OpenAI Codex API
|
||||
*/
|
||||
export async function fetchCodexResponse(
|
||||
messages: Message[],
|
||||
model: string,
|
||||
options: {
|
||||
apiKey?: string
|
||||
baseUrl?: string
|
||||
stream?: boolean
|
||||
} = {}
|
||||
): Promise<OpenAIResponse> {
|
||||
const { apiKey, baseUrl = 'https://api.openai.com/v1', stream = false } = options
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error('OpenAI API key is required for Codex requests')
|
||||
}
|
||||
|
||||
const openAIMessages = messages.map(convertToOpenAIMessage)
|
||||
|
||||
const requestBody = {
|
||||
model,
|
||||
messages: openAIMessages,
|
||||
stream,
|
||||
temperature: 0.7,
|
||||
max_tokens: 4096,
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${baseUrl}/chat/completions`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${apiKey}`,
|
||||
},
|
||||
body: JSON.stringify(requestBody),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`OpenAI API error: ${response.status} ${response.statusText}`)
|
||||
}
|
||||
|
||||
const data = await response.json() as OpenAIResponse
|
||||
return data
|
||||
} catch (error) {
|
||||
logError(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert OpenAI response to Claude Code format
|
||||
*/
|
||||
export function convertFromOpenAIResponse(response: OpenAIResponse): {
|
||||
content: string
|
||||
usage: {
|
||||
input_tokens: number
|
||||
output_tokens: number
|
||||
}
|
||||
} {
|
||||
const choice = response.choices[0]
|
||||
if (!choice) {
|
||||
throw new Error('No choices in OpenAI response')
|
||||
}
|
||||
|
||||
return {
|
||||
content: choice.message.content,
|
||||
usage: {
|
||||
input_tokens: response.usage.prompt_tokens,
|
||||
output_tokens: response.usage.completion_tokens,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ export const CLAUDE_3_7_SONNET_CONFIG = {
|
||||
bedrock: 'us.anthropic.claude-3-7-sonnet-20250219-v1:0',
|
||||
vertex: 'claude-3-7-sonnet@20250219',
|
||||
foundry: 'claude-3-7-sonnet',
|
||||
openai: 'claude-3-7-sonnet-20250219',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_3_5_V2_SONNET_CONFIG = {
|
||||
@@ -18,6 +19,7 @@ export const CLAUDE_3_5_V2_SONNET_CONFIG = {
|
||||
bedrock: 'anthropic.claude-3-5-sonnet-20241022-v2:0',
|
||||
vertex: 'claude-3-5-sonnet-v2@20241022',
|
||||
foundry: 'claude-3-5-sonnet',
|
||||
openai: 'claude-3-5-sonnet-20241022',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_3_5_HAIKU_CONFIG = {
|
||||
@@ -25,6 +27,7 @@ export const CLAUDE_3_5_HAIKU_CONFIG = {
|
||||
bedrock: 'us.anthropic.claude-3-5-haiku-20241022-v1:0',
|
||||
vertex: 'claude-3-5-haiku@20241022',
|
||||
foundry: 'claude-3-5-haiku',
|
||||
openai: 'claude-3-5-haiku-20241022',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_HAIKU_4_5_CONFIG = {
|
||||
@@ -32,6 +35,7 @@ export const CLAUDE_HAIKU_4_5_CONFIG = {
|
||||
bedrock: 'us.anthropic.claude-haiku-4-5-20251001-v1:0',
|
||||
vertex: 'claude-haiku-4-5@20251001',
|
||||
foundry: 'claude-haiku-4-5',
|
||||
openai: 'claude-haiku-4-5-20251001',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_SONNET_4_CONFIG = {
|
||||
@@ -39,6 +43,7 @@ export const CLAUDE_SONNET_4_CONFIG = {
|
||||
bedrock: 'us.anthropic.claude-sonnet-4-20250514-v1:0',
|
||||
vertex: 'claude-sonnet-4@20250514',
|
||||
foundry: 'claude-sonnet-4',
|
||||
openai: 'claude-sonnet-4-20250514',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_SONNET_4_5_CONFIG = {
|
||||
@@ -46,6 +51,7 @@ export const CLAUDE_SONNET_4_5_CONFIG = {
|
||||
bedrock: 'us.anthropic.claude-sonnet-4-5-20250929-v1:0',
|
||||
vertex: 'claude-sonnet-4-5@20250929',
|
||||
foundry: 'claude-sonnet-4-5',
|
||||
openai: 'claude-sonnet-4-5-20250929',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_OPUS_4_CONFIG = {
|
||||
@@ -53,6 +59,7 @@ export const CLAUDE_OPUS_4_CONFIG = {
|
||||
bedrock: 'us.anthropic.claude-opus-4-20250514-v1:0',
|
||||
vertex: 'claude-opus-4@20250514',
|
||||
foundry: 'claude-opus-4',
|
||||
openai: 'claude-opus-4-20250514',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_OPUS_4_1_CONFIG = {
|
||||
@@ -60,6 +67,7 @@ export const CLAUDE_OPUS_4_1_CONFIG = {
|
||||
bedrock: 'us.anthropic.claude-opus-4-1-20250805-v1:0',
|
||||
vertex: 'claude-opus-4-1@20250805',
|
||||
foundry: 'claude-opus-4-1',
|
||||
openai: 'claude-opus-4-1-20250805',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_OPUS_4_5_CONFIG = {
|
||||
@@ -67,6 +75,7 @@ export const CLAUDE_OPUS_4_5_CONFIG = {
|
||||
bedrock: 'us.anthropic.claude-opus-4-5-20251101-v1:0',
|
||||
vertex: 'claude-opus-4-5@20251101',
|
||||
foundry: 'claude-opus-4-5',
|
||||
openai: 'claude-opus-4-5-20251101',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_OPUS_4_6_CONFIG = {
|
||||
@@ -74,6 +83,7 @@ export const CLAUDE_OPUS_4_6_CONFIG = {
|
||||
bedrock: 'us.anthropic.claude-opus-4-6-v1',
|
||||
vertex: 'claude-opus-4-6',
|
||||
foundry: 'claude-opus-4-6',
|
||||
openai: 'claude-opus-4-6',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_SONNET_4_6_CONFIG = {
|
||||
@@ -81,6 +91,32 @@ export const CLAUDE_SONNET_4_6_CONFIG = {
|
||||
bedrock: 'us.anthropic.claude-sonnet-4-6',
|
||||
vertex: 'claude-sonnet-4-6',
|
||||
foundry: 'claude-sonnet-4-6',
|
||||
openai: 'claude-sonnet-4-6',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
// OpenAI Codex models
|
||||
export const GPT_5_4_CONFIG = {
|
||||
firstParty: 'gpt-5.4',
|
||||
bedrock: 'gpt-5.4',
|
||||
vertex: 'gpt-5.4',
|
||||
foundry: 'gpt-5.4',
|
||||
openai: 'gpt-5.4',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const GPT_5_3_CODEX_CONFIG = {
|
||||
firstParty: 'gpt-5.3-codex',
|
||||
bedrock: 'gpt-5.3-codex',
|
||||
vertex: 'gpt-5.3-codex',
|
||||
foundry: 'gpt-5.3-codex',
|
||||
openai: 'gpt-5.3-codex',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const GPT_5_4_MINI_CONFIG = {
|
||||
firstParty: 'gpt-5.4-mini',
|
||||
bedrock: 'gpt-5.4-mini',
|
||||
vertex: 'gpt-5.4-mini',
|
||||
foundry: 'gpt-5.4-mini',
|
||||
openai: 'gpt-5.4-mini',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
// @[MODEL LAUNCH]: Register the new config here.
|
||||
@@ -96,6 +132,10 @@ export const ALL_MODEL_CONFIGS = {
|
||||
opus41: CLAUDE_OPUS_4_1_CONFIG,
|
||||
opus45: CLAUDE_OPUS_4_5_CONFIG,
|
||||
opus46: CLAUDE_OPUS_4_6_CONFIG,
|
||||
// OpenAI Codex models
|
||||
gpt54: GPT_5_4_CONFIG,
|
||||
gpt53codex: GPT_5_3_CODEX_CONFIG,
|
||||
gpt54mini: GPT_5_4_MINI_CONFIG,
|
||||
} as const satisfies Record<string, ModelConfig>
|
||||
|
||||
export type ModelKey = keyof typeof ALL_MODEL_CONFIGS
|
||||
|
||||
@@ -9,6 +9,7 @@ import { getMainLoopModelOverride } from '../../bootstrap/state.js'
|
||||
import {
|
||||
getSubscriptionType,
|
||||
isClaudeAISubscriber,
|
||||
isCodexSubscriber,
|
||||
isMaxSubscriber,
|
||||
isProSubscriber,
|
||||
isTeamPremiumSubscriber,
|
||||
@@ -190,6 +191,11 @@ export function getDefaultMainLoopModelSetting(): ModelName | ModelAlias {
|
||||
)
|
||||
}
|
||||
|
||||
// Codex users get GPT-5.3 Codex as default
|
||||
if (isCodexSubscriber()) {
|
||||
return getModelStrings().gpt53codex
|
||||
}
|
||||
|
||||
// Max users get Opus as default
|
||||
if (isMaxSubscriber()) {
|
||||
return getDefaultOpusModel() + (isOpus1mMergeEnabled() ? '[1m]' : '')
|
||||
@@ -267,6 +273,16 @@ export function firstPartyNameToCanonical(name: ModelName): ModelShortName {
|
||||
if (name.includes('claude-3-haiku')) {
|
||||
return 'claude-3-haiku'
|
||||
}
|
||||
// OpenAI GPT models
|
||||
if (name.includes('gpt-5.4-mini')) {
|
||||
return 'gpt-5.4-mini'
|
||||
}
|
||||
if (name.includes('gpt-5.4')) {
|
||||
return 'gpt-5.4'
|
||||
}
|
||||
if (name.includes('gpt-5.3-codex')) {
|
||||
return 'gpt-5.3-codex'
|
||||
}
|
||||
const match = name.match(/(claude-(\d+-\d+-)?\w+)/)
|
||||
if (match && match[1]) {
|
||||
return match[1]
|
||||
@@ -293,7 +309,7 @@ export function getClaudeAiUserDefaultModelDescription(
|
||||
fastMode = false,
|
||||
): string {
|
||||
if (isCodexSubscriber()) {
|
||||
return 'Codex 5.2 · Best for everyday coding tasks'
|
||||
return 'GPT-5.3 Codex · Optimized for code generation and understanding'
|
||||
}
|
||||
if (isMaxSubscriber() || isTeamPremiumSubscriber()) {
|
||||
if (isOpus1mMergeEnabled()) {
|
||||
@@ -397,6 +413,12 @@ export function getPublicModelDisplayName(model: ModelName): string | null {
|
||||
return 'Haiku 4.5'
|
||||
case getModelStrings().haiku35:
|
||||
return 'Haiku 3.5'
|
||||
case getModelStrings().gpt54:
|
||||
return 'GPT-5.4'
|
||||
case getModelStrings().gpt53codex:
|
||||
return 'GPT-5.3 Codex'
|
||||
case getModelStrings().gpt54mini:
|
||||
return 'GPT-5.4 Mini'
|
||||
default:
|
||||
return null
|
||||
}
|
||||
@@ -631,6 +653,16 @@ export function getMarketingNameForModel(modelId: string): string | undefined {
|
||||
if (canonical.includes('claude-3-5-haiku')) {
|
||||
return 'Claude 3.5 Haiku'
|
||||
}
|
||||
// OpenAI Codex models
|
||||
if (canonical.includes('gpt-5.4-mini')) {
|
||||
return 'GPT-5.4 Mini'
|
||||
}
|
||||
if (canonical.includes('gpt-5.4')) {
|
||||
return 'GPT-5.4'
|
||||
}
|
||||
if (canonical.includes('gpt-5.3-codex')) {
|
||||
return 'GPT-5.3 Codex'
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { getInitialMainLoopModel } from '../../bootstrap/state.js'
|
||||
import {
|
||||
isClaudeAISubscriber,
|
||||
isCodexSubscriber,
|
||||
isMaxSubscriber,
|
||||
isTeamPremiumSubscriber,
|
||||
} from '../auth.js'
|
||||
@@ -208,6 +209,34 @@ function getHaikuOption(): ModelOption {
|
||||
: getHaiku35Option()
|
||||
}
|
||||
|
||||
// OpenAI Codex model options
|
||||
function getGpt54Option(): ModelOption {
|
||||
return {
|
||||
value: 'gpt-5.4',
|
||||
label: 'GPT-5.4',
|
||||
description: 'GPT-5.4 · Advanced reasoning and code generation',
|
||||
descriptionForModel: 'GPT-5.4 - advanced reasoning and code generation capabilities',
|
||||
}
|
||||
}
|
||||
|
||||
function getGpt53CodexOption(): ModelOption {
|
||||
return {
|
||||
value: 'gpt-5.3-codex',
|
||||
label: 'GPT-5.3 Codex',
|
||||
description: 'GPT-5.3 Codex · Optimized for code generation and understanding',
|
||||
descriptionForModel: 'GPT-5.3 Codex - specialized for code generation and understanding',
|
||||
}
|
||||
}
|
||||
|
||||
function getGpt54MiniOption(): ModelOption {
|
||||
return {
|
||||
value: 'gpt-5.4-mini',
|
||||
label: 'GPT-5.4 Mini',
|
||||
description: 'GPT-5.4 Mini · Fast and efficient for simple tasks',
|
||||
descriptionForModel: 'GPT-5.4 Mini - fast and efficient for simple coding tasks',
|
||||
}
|
||||
}
|
||||
|
||||
function getMaxOpusOption(fastMode = false): ModelOption {
|
||||
return {
|
||||
value: 'opus',
|
||||
@@ -287,6 +316,16 @@ function getModelOptionsBase(fastMode = false): ModelOption[] {
|
||||
]
|
||||
}
|
||||
|
||||
// Codex subscribers get OpenAI model options
|
||||
if (isCodexSubscriber()) {
|
||||
return [
|
||||
getDefaultOptionForUser(),
|
||||
getGpt54Option(),
|
||||
getGpt53CodexOption(),
|
||||
getGpt54MiniOption(),
|
||||
]
|
||||
}
|
||||
|
||||
if (isClaudeAISubscriber()) {
|
||||
if (isMaxSubscriber() || isTeamPremiumSubscriber()) {
|
||||
// Max and Team Premium users: Opus is default, show Sonnet as alternative
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS } from '../../services/analytics/index.js'
|
||||
import { isEnvTruthy } from '../envUtils.js'
|
||||
|
||||
export type APIProvider = 'firstParty' | 'bedrock' | 'vertex' | 'foundry'
|
||||
export type APIProvider = 'firstParty' | 'bedrock' | 'vertex' | 'foundry' | 'openai'
|
||||
|
||||
export function getAPIProvider(): APIProvider {
|
||||
return isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK)
|
||||
@@ -10,7 +10,9 @@ export function getAPIProvider(): APIProvider {
|
||||
? 'vertex'
|
||||
: isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY)
|
||||
? 'foundry'
|
||||
: 'firstParty'
|
||||
: isEnvTruthy(process.env.CLAUDE_CODE_USE_OPENAI)
|
||||
? 'openai'
|
||||
: 'firstParty'
|
||||
}
|
||||
|
||||
export function getAPIProviderForStatsig(): AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS {
|
||||
|
||||
Reference in new issue
Block a user