Merge pull request #33 from Askhz/refactor/login-logic
refactor: 精简登录选项,保留 4 个主要登录方式
This commit is contained in:
commit
83beec0b5e
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -38,3 +38,7 @@ data
|
|||
.codex/skills/.system/**
|
||||
!.codex/prompts/
|
||||
!.codex/prompts/**
|
||||
|
||||
.costrict
|
||||
.claude
|
||||
.tmp/
|
||||
|
|
@ -4,6 +4,7 @@ import { getAPIProvider } from '../utils/model/providers.js'
|
|||
import { updateSettingsForSource } from '../utils/settings/settings.js'
|
||||
import { getSettings_DEPRECATED } from '../utils/settings/settings.js'
|
||||
import { applyConfigEnvironmentVariables } from '../utils/managedEnv.js'
|
||||
import { getDefaultSonnetModel, getDefaultOpusModel, getDefaultHaikuModel } from '../utils/model/model.js'
|
||||
|
||||
function getEnvVarForProvider(provider: string): string {
|
||||
switch (provider) {
|
||||
|
|
@ -43,6 +44,28 @@ const call: LocalCommandCall = async (args, context) => {
|
|||
return { type: 'text', value: `Current API provider: ${current}` }
|
||||
}
|
||||
|
||||
// Helper function to set default model based on provider
|
||||
const setDefaultModel = (provider: string) => {
|
||||
const settings = getSettings_DEPRECATED() || {}
|
||||
const currentModel = settings.model
|
||||
|
||||
// Only set default if no model has been explicitly set
|
||||
if (!currentModel) {
|
||||
let defaultModel: string
|
||||
switch (provider) {
|
||||
case 'anthropic':
|
||||
defaultModel = getDefaultSonnetModel()
|
||||
break
|
||||
case 'openai':
|
||||
case 'gemini':
|
||||
case 'grok':
|
||||
default:
|
||||
defaultModel = getDefaultSonnetModel()
|
||||
}
|
||||
updateSettingsForSource('userSettings', { model: defaultModel })
|
||||
}
|
||||
}
|
||||
|
||||
// unset - clear settings, fallback to env vars
|
||||
if (arg === 'unset') {
|
||||
updateSettingsForSource('userSettings', { modelType: undefined })
|
||||
|
|
@ -53,6 +76,7 @@ const call: LocalCommandCall = async (args, context) => {
|
|||
delete process.env.CLAUDE_CODE_USE_OPENAI
|
||||
delete process.env.CLAUDE_CODE_USE_GEMINI
|
||||
delete process.env.CLAUDE_CODE_USE_GROK
|
||||
delete process.env.CLAUDE_CODE_USE_COSTRICT
|
||||
return {
|
||||
type: 'text',
|
||||
value: 'API provider cleared (will use environment variables).',
|
||||
|
|
@ -83,6 +107,7 @@ const call: LocalCommandCall = async (args, context) => {
|
|||
const hasUrl = !!mergedEnv.OPENAI_BASE_URL
|
||||
if (!hasKey || !hasUrl) {
|
||||
updateSettingsForSource('userSettings', { modelType: 'openai' })
|
||||
setDefaultModel('openai')
|
||||
const missing = []
|
||||
if (!hasKey) missing.push('OPENAI_API_KEY')
|
||||
if (!hasUrl) missing.push('OPENAI_BASE_URL')
|
||||
|
|
@ -99,6 +124,7 @@ const call: LocalCommandCall = async (args, context) => {
|
|||
const hasKey = !!(mergedEnv.GROK_API_KEY || mergedEnv.XAI_API_KEY)
|
||||
if (!hasKey) {
|
||||
updateSettingsForSource('userSettings', { modelType: 'grok' })
|
||||
setDefaultModel('grok')
|
||||
return {
|
||||
type: 'text',
|
||||
value: `Switched to Grok provider.\nWarning: Missing env var: GROK_API_KEY (or XAI_API_KEY)\nConfigure it via settings.json env or set manually.`,
|
||||
|
|
@ -113,6 +139,7 @@ const call: LocalCommandCall = async (args, context) => {
|
|||
// GEMINI_BASE_URL is optional (has default)
|
||||
if (!hasKey) {
|
||||
updateSettingsForSource('userSettings', { modelType: 'gemini' })
|
||||
setDefaultModel('gemini')
|
||||
return {
|
||||
type: 'text',
|
||||
value: `Switched to Gemini provider.\nWarning: Missing env var: GEMINI_API_KEY\nConfigure it via /login or set manually.`,
|
||||
|
|
@ -131,8 +158,11 @@ const call: LocalCommandCall = async (args, context) => {
|
|||
delete process.env.CLAUDE_CODE_USE_OPENAI
|
||||
delete process.env.CLAUDE_CODE_USE_GEMINI
|
||||
delete process.env.CLAUDE_CODE_USE_GROK
|
||||
delete process.env.CLAUDE_CODE_USE_COSTRICT
|
||||
// Update settings.json
|
||||
updateSettingsForSource('userSettings', { modelType: arg })
|
||||
// Set default model for the provider
|
||||
setDefaultModel(arg)
|
||||
// Ensure settings.env gets applied to process.env
|
||||
applyConfigEnvironmentVariables()
|
||||
return { type: 'text', value: `API provider set to ${arg}.` }
|
||||
|
|
|
|||
|
|
@ -13,9 +13,11 @@ import { OAuthService } from '../services/oauth/index.js'
|
|||
import { getOauthAccountInfo, validateForceLoginOrg } from '../utils/auth.js'
|
||||
import { logError } from '../utils/log.js'
|
||||
import { getSettings_DEPRECATED, updateSettingsForSource } from '../utils/settings/settings.js'
|
||||
import { getDefaultSonnetModel } from '../utils/model/model.js'
|
||||
import { Select } from './CustomSelect/select.js'
|
||||
import { Spinner } from './Spinner.js'
|
||||
import TextInput from './TextInput.js'
|
||||
import { useSetAppState } from '../state/AppState.js'
|
||||
import { fi } from 'zod/v4/locales'
|
||||
|
||||
type Props = {
|
||||
|
|
@ -28,6 +30,14 @@ type Props = {
|
|||
type OAuthStatus =
|
||||
| { state: 'idle' } // Initial state, waiting to select login method
|
||||
| { state: 'platform_setup' } // Show platform setup info (Bedrock/Vertex/Foundry)
|
||||
| {
|
||||
state: 'costrict_waiting'
|
||||
url: string
|
||||
} // CoStrict OAuth: browser opened, waiting for user to login
|
||||
| {
|
||||
state: 'costrict_model_select'
|
||||
models: Array<{ id: string; name?: string }>
|
||||
} // CoStrict: login done, select a model
|
||||
| {
|
||||
state: 'custom_platform'
|
||||
baseUrl: string
|
||||
|
|
@ -94,6 +104,7 @@ export function ConsoleOAuthFlow({
|
|||
}
|
||||
return { state: 'idle' }
|
||||
})
|
||||
const setAppState = useSetAppState()
|
||||
|
||||
const [pastedCode, setPastedCode] = useState('')
|
||||
const [cursorOffset, setCursorOffset] = useState(0)
|
||||
|
|
@ -273,6 +284,10 @@ export function ConsoleOAuthFlow({
|
|||
}
|
||||
// Reset modelType to anthropic when using OAuth login
|
||||
updateSettingsForSource('userSettings', { modelType: 'anthropic' } as any)
|
||||
// Set default model to sonnet for Anthropic provider
|
||||
updateSettingsForSource('userSettings', { model: getDefaultSonnetModel() } as any)
|
||||
// Clear costrict env var to prevent conflicts
|
||||
delete process.env.CLAUDE_CODE_USE_COSTRICT
|
||||
|
||||
setOAuthStatus({ state: 'success' })
|
||||
void sendNotification(
|
||||
|
|
@ -438,6 +453,7 @@ function OAuthStatusMessage({
|
|||
setLoginWithClaudeAi,
|
||||
onDone,
|
||||
}: OAuthStatusMessageProps): React.ReactNode {
|
||||
const setAppState = useSetAppState()
|
||||
switch (oauthStatus.state) {
|
||||
case 'idle':
|
||||
return (
|
||||
|
|
@ -453,6 +469,16 @@ function OAuthStatusMessage({
|
|||
<Box>
|
||||
<Select
|
||||
options={[
|
||||
{
|
||||
label: (
|
||||
<Text>
|
||||
CoStrict ·{' '}
|
||||
<Text dimColor>Sign in with CoStrict account</Text>
|
||||
{'\n'}
|
||||
</Text>
|
||||
),
|
||||
value: 'costrict',
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<Text>
|
||||
|
|
@ -485,52 +511,72 @@ function OAuthStatusMessage({
|
|||
),
|
||||
value: 'gemini_api',
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<Text>
|
||||
Claude account with subscription ·{' '}
|
||||
<Text dimColor>Pro, Max, Team, or Enterprise</Text>
|
||||
{process.env.USER_TYPE === 'ant' && (
|
||||
<Text>
|
||||
{'\n'}
|
||||
<Text color="warning">[ANT-ONLY]</Text>{' '}
|
||||
<Text dimColor>
|
||||
Please use this option unless you need to login to a
|
||||
special org for accessing sensitive data (e.g.
|
||||
customer data, HIPI data) with the Console option
|
||||
</Text>
|
||||
</Text>
|
||||
)}
|
||||
{'\n'}
|
||||
</Text>
|
||||
),
|
||||
value: 'claudeai',
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<Text>
|
||||
Anthropic Console account ·{' '}
|
||||
<Text dimColor>API usage billing</Text>
|
||||
{'\n'}
|
||||
</Text>
|
||||
),
|
||||
value: 'console',
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<Text>
|
||||
3rd-party platform ·{' '}
|
||||
<Text dimColor>
|
||||
Amazon Bedrock, Microsoft Foundry, or Vertex AI
|
||||
</Text>
|
||||
{'\n'}
|
||||
</Text>
|
||||
),
|
||||
value: 'platform',
|
||||
},
|
||||
]}
|
||||
onChange={value => {
|
||||
if (value === 'custom_platform') {
|
||||
if (value === 'costrict') {
|
||||
void (async () => {
|
||||
try {
|
||||
const { generateState, getCoStrictBaseURL, buildCoStrictLoginURL, pollLoginToken } = await import('../costrict/provider/auth.js')
|
||||
const { generateMachineId, saveCoStrictCredentials } = await import('../costrict/provider/credentials.js')
|
||||
const { extractExpiryFromJWT } = await import('../costrict/provider/token.js')
|
||||
const { updateSettingsForSource } = await import('../utils/settings/settings.js')
|
||||
|
||||
const baseUrl = getCoStrictBaseURL()
|
||||
const state = generateState()
|
||||
const machineId = generateMachineId()
|
||||
const loginUrl = buildCoStrictLoginURL(baseUrl, state, machineId)
|
||||
|
||||
// 打开浏览器
|
||||
const { openBrowser } = await import('../utils/browser.js')
|
||||
await openBrowser(loginUrl)
|
||||
|
||||
setOAuthStatus({ state: 'costrict_waiting', url: loginUrl })
|
||||
|
||||
// 轮询等待登录完成
|
||||
const tokens = await pollLoginToken(baseUrl, state, machineId)
|
||||
|
||||
// 保存凭证
|
||||
const expiryDate = extractExpiryFromJWT(tokens.access_token)
|
||||
await saveCoStrictCredentials({
|
||||
id: 'csc',
|
||||
name: 'CSC Auth',
|
||||
access_token: tokens.access_token,
|
||||
refresh_token: tokens.refresh_token,
|
||||
state,
|
||||
machine_id: machineId,
|
||||
base_url: baseUrl,
|
||||
expiry_date: expiryDate,
|
||||
updated_at: new Date().toISOString(),
|
||||
expired_at: expiryDate ? new Date(expiryDate).toISOString() : undefined,
|
||||
})
|
||||
|
||||
// 设置 modelType 为 costrict
|
||||
updateSettingsForSource('userSettings', { modelType: 'costrict' as any } as any)
|
||||
process.env.CLAUDE_CODE_USE_COSTRICT = '1'
|
||||
|
||||
// 预取模型列表,填充同步缓存,并进入模型选择界面
|
||||
try {
|
||||
const { fetchCoStrictModels } = await import('../costrict/provider/models.js')
|
||||
const models = await fetchCoStrictModels(baseUrl, tokens.access_token)
|
||||
if (models.length > 0) {
|
||||
setOAuthStatus({ state: 'costrict_model_select', models })
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
// 预取失败,直接进入 success
|
||||
}
|
||||
|
||||
setOAuthStatus({ state: 'success' })
|
||||
void onDone()
|
||||
} catch (err: any) {
|
||||
setOAuthStatus({
|
||||
state: 'error',
|
||||
message: err.message || String(err),
|
||||
toRetry: { state: 'idle' },
|
||||
})
|
||||
}
|
||||
})()
|
||||
} else if (value === 'custom_platform') {
|
||||
logEvent('tengu_custom_platform_selected', {})
|
||||
setOAuthStatus({
|
||||
state: 'custom_platform',
|
||||
|
|
@ -562,19 +608,7 @@ function OAuthStatusMessage({
|
|||
sonnetModel: process.env.GEMINI_DEFAULT_SONNET_MODEL ?? '',
|
||||
opusModel: process.env.GEMINI_DEFAULT_OPUS_MODEL ?? '',
|
||||
activeField: 'base_url',
|
||||
})
|
||||
} else if (value === 'platform') {
|
||||
logEvent('tengu_oauth_platform_selected', {})
|
||||
setOAuthStatus({ state: 'platform_setup' })
|
||||
} else {
|
||||
setOAuthStatus({ state: 'ready_to_start' })
|
||||
if (value === 'claudeai') {
|
||||
logEvent('tengu_oauth_claudeai_selected', {})
|
||||
setLoginWithClaudeAi(true)
|
||||
} else {
|
||||
logEvent('tengu_oauth_console_selected', {})
|
||||
setLoginWithClaudeAi(false)
|
||||
}
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
|
@ -696,6 +730,10 @@ function OAuthStatusMessage({
|
|||
})
|
||||
} else {
|
||||
for (const [k, v] of Object.entries(env)) process.env[k] = v
|
||||
// Clear costrict env var to prevent conflicts when using custom platform
|
||||
delete process.env.CLAUDE_CODE_USE_COSTRICT
|
||||
// Set default model to sonnet for this provider
|
||||
updateSettingsForSource('userSettings', { model: getDefaultSonnetModel() } as any)
|
||||
setOAuthStatus({ state: 'success' })
|
||||
void onDone()
|
||||
}
|
||||
|
|
@ -916,6 +954,10 @@ function OAuthStatusMessage({
|
|||
})
|
||||
} else {
|
||||
for (const [k, v] of Object.entries(env)) process.env[k] = v
|
||||
// Clear costrict env var to prevent conflicts when using OpenAI
|
||||
delete process.env.CLAUDE_CODE_USE_COSTRICT
|
||||
// Set default model to sonnet for this provider
|
||||
updateSettingsForSource('userSettings', { model: getDefaultSonnetModel() } as any)
|
||||
setOAuthStatus({ state: 'success' })
|
||||
void onDone()
|
||||
}
|
||||
|
|
@ -1149,6 +1191,10 @@ function OAuthStatusMessage({
|
|||
})
|
||||
} else {
|
||||
for (const [k, v] of Object.entries(env)) process.env[k] = v
|
||||
// Clear costrict env var to prevent conflicts when using Gemini
|
||||
delete process.env.CLAUDE_CODE_USE_COSTRICT
|
||||
// Set default model to sonnet for this provider
|
||||
updateSettingsForSource('userSettings', { model: getDefaultSonnetModel() } as any)
|
||||
setOAuthStatus({ state: 'success' })
|
||||
void onDone()
|
||||
}
|
||||
|
|
@ -1275,6 +1321,62 @@ function OAuthStatusMessage({
|
|||
)
|
||||
}
|
||||
|
||||
case 'costrict_waiting':
|
||||
return (
|
||||
<Box flexDirection="column" gap={1}>
|
||||
<Text>
|
||||
Opening browser for CoStrict login. If it does not open
|
||||
automatically, copy and paste this URL:
|
||||
</Text>
|
||||
<Box marginY={1}>
|
||||
<Text color="suggestion">{oauthStatus.url}</Text>
|
||||
</Box>
|
||||
<Text dimColor>Waiting for authentication...</Text>
|
||||
</Box>
|
||||
)
|
||||
|
||||
case 'costrict_model_select': {
|
||||
const sortedModels = [...oauthStatus.models].sort((a, b) => a.id.localeCompare(b.id));
|
||||
// 直接使用已获取的模型列表渲染选项,避免通用 ModelPicker 因 getAPIProvider() 竞态
|
||||
// 导致显示标准 Anthropic 模型列表而非 CoStrict 动态模型
|
||||
const costrictOptions = sortedModels.map(m => ({
|
||||
value: m.id,
|
||||
label: m.name ?? m.id,
|
||||
description:
|
||||
m.id === 'Auto'
|
||||
? `${Math.round(((m as any).creditDiscount ?? 0) * 100)}% discount`
|
||||
: `${(m as any).creditConsumption ?? '?'}x credit`,
|
||||
}));
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
<Box marginBottom={1} flexDirection="column">
|
||||
<Text color="remember" bold>
|
||||
Select model
|
||||
</Text>
|
||||
<Text dimColor>Login successful. Select a CoStrict model to use:</Text>
|
||||
</Box>
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Select
|
||||
options={costrictOptions}
|
||||
onChange={(value: string) => {
|
||||
process.env.COSTRICT_MODEL = value;
|
||||
setAppState(prev => ({ ...prev, mainLoopModel: value, mainLoopModelForSession: null }));
|
||||
setOAuthStatus({ state: 'success' });
|
||||
void onDone();
|
||||
}}
|
||||
onCancel={() => {
|
||||
const selected = sortedModels[0]?.id ?? '';
|
||||
process.env.COSTRICT_MODEL = selected;
|
||||
setAppState(prev => ({ ...prev, mainLoopModel: selected, mainLoopModelForSession: null }));
|
||||
setOAuthStatus({ state: 'success' });
|
||||
void onDone();
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
case 'platform_setup':
|
||||
return (
|
||||
<Box flexDirection="column" gap={1} marginTop={1}>
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ function getVersion(): string {
|
|||
|
||||
const VERSION = getVersion()
|
||||
|
||||
type CoStrictFetch = typeof fetch & {
|
||||
preconnect?: (url: string | URL) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建自定义 fetch 函数,用于 CoStrict API 请求
|
||||
*
|
||||
|
|
@ -43,7 +47,7 @@ const VERSION = getVersion()
|
|||
* 3. 注入 Authorization 和 CoStrict 特有 headers
|
||||
* 4. 反应性 401 错误恢复(自动重试一次)
|
||||
*/
|
||||
export function createCoStrictFetch() {
|
||||
export function createCoStrictFetch(): CoStrictFetch {
|
||||
const costrictFetch = async (
|
||||
input: RequestInfo | URL,
|
||||
init?: RequestInit,
|
||||
|
|
@ -126,20 +130,21 @@ export function createCoStrictFetch() {
|
|||
}
|
||||
// Bun 原生支持 fetch.preconnect(共享连接池预热)
|
||||
// Node.js 没有 preconnect API,降级为 net.createConnection 做 TCP 预热
|
||||
if (typeof fetch.preconnect === 'function') {
|
||||
costrictFetch.preconnect = fetch.preconnect.bind(fetch)
|
||||
} else {
|
||||
costrictFetch.preconnect = (url: string | URL) => {
|
||||
try {
|
||||
const { hostname, port } = new URL(typeof url === 'string' ? url : url.toString())
|
||||
const { createConnection } = require('node:net') as typeof import('node:net')
|
||||
const sock = createConnection(Number(port) || 443, hostname, () => sock.destroy())
|
||||
sock.setTimeout(3000, () => sock.destroy())
|
||||
sock.on('error', () => sock.destroy())
|
||||
} catch {
|
||||
// 预连接失败不影响正常流程
|
||||
}
|
||||
}
|
||||
}
|
||||
return costrictFetch
|
||||
const costrictFetchWithPreconnect = Object.assign(costrictFetch, {
|
||||
preconnect: typeof fetch.preconnect === 'function'
|
||||
? fetch.preconnect.bind(fetch)
|
||||
: (url: string | URL) => {
|
||||
try {
|
||||
const { hostname, port } = new URL(typeof url === 'string' ? url : url.toString())
|
||||
const { createConnection } = require('node:net') as typeof import('node:net')
|
||||
const sock = createConnection(Number(port) || 443, hostname, () => sock.destroy())
|
||||
sock.setTimeout(3000, () => sock.destroy())
|
||||
sock.on('error', () => sock.destroy())
|
||||
} catch {
|
||||
// 预连接失败不影响正常流程
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return costrictFetchWithPreconnect as CoStrictFetch
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,12 +16,7 @@ import type { Tools } from '../../Tool.js'
|
|||
import type { Options } from '../../services/api/claude.js'
|
||||
import OpenAI from 'openai'
|
||||
import { getProxyFetchOptions } from '../../utils/proxy.js'
|
||||
import { anthropicMessagesToOpenAI } from '../../services/api/openai/convertMessages.js'
|
||||
import {
|
||||
anthropicToolsToOpenAI,
|
||||
anthropicToolChoiceToOpenAI,
|
||||
} from '../../services/api/openai/convertTools.js'
|
||||
import { adaptOpenAIStreamToAnthropic } from '../../services/api/openai/streamAdapter.js'
|
||||
import { anthropicMessagesToOpenAI, anthropicToolsToOpenAI, anthropicToolChoiceToOpenAI, adaptOpenAIStreamToAnthropic } from '@ant/model-provider'
|
||||
import { normalizeMessagesForAPI } from '../../utils/messages.js'
|
||||
import { toolToAPISchema } from '../../utils/api.js'
|
||||
import { logForDebugging } from '../../utils/debug.js'
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ import {
|
|||
} from './model.js'
|
||||
import { has1mContext } from '../context.js'
|
||||
import { getGlobalConfig } from '../config.js'
|
||||
import { hasCoStrictCredentialsSync } from '../../costrict/provider/credentials.js'
|
||||
|
||||
// @[MODEL LAUNCH]: Update all the available and default model option strings below.
|
||||
|
||||
|
|
@ -380,10 +379,8 @@ function getModelOptionsBase(fastMode = false): ModelOption[] {
|
|||
}
|
||||
|
||||
// CoStrict provider: 从缓存中动态展示 CoStrict 可用模型
|
||||
// 也覆盖其他 provider 但用户已有 CoStrict 凭证的场景(modelType 丢失/竞态)
|
||||
const isCoStrictProvider = getAPIProvider() === 'costrict'
|
||||
const hasCoStrictCreds = hasCoStrictCredentialsSync()
|
||||
if (isCoStrictProvider || hasCoStrictCreds) {
|
||||
if (isCoStrictProvider) {
|
||||
// 延迟 require 避免循环依赖,且该模块只在 costrict provider 下加载
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const { getCachedCoStrictModels } =
|
||||
|
|
@ -414,21 +411,27 @@ function getModelOptionsBase(fastMode = false): ModelOption[] {
|
|||
]
|
||||
}
|
||||
|
||||
// 用户没有 CoStrict 凭证,显示推荐登录选项
|
||||
return [getCoStrictLoginOption()]
|
||||
}
|
||||
|
||||
/**
|
||||
* CoStrict 登录推荐选项
|
||||
* 当用户未登录 CoStrict 时显示,提示用户可以登录以使用 CoStrict 模型
|
||||
*/
|
||||
function getCoStrictLoginOption(): ModelOption {
|
||||
return {
|
||||
value: 'costrict-login',
|
||||
label: 'CoStrict (Recommended)',
|
||||
description:
|
||||
'Sign in to CoStrict for more models and better pricing · Run /login and select CoStrict',
|
||||
// PAYG 用户:展示标准模型选项(Sonnet, Opus, Haiku 等)
|
||||
const paygOptions = [getDefaultOptionForUser(fastMode)]
|
||||
const customSonnet = getCustomSonnetOption()
|
||||
if (customSonnet) {
|
||||
paygOptions.push(customSonnet)
|
||||
} else {
|
||||
paygOptions.push(getSonnet46Option())
|
||||
}
|
||||
const customOpus = getCustomOpusOption()
|
||||
if (customOpus) {
|
||||
paygOptions.push(customOpus)
|
||||
} else {
|
||||
paygOptions.push(getOpus46Option(fastMode))
|
||||
}
|
||||
const customHaiku = getCustomHaikuOption()
|
||||
if (customHaiku) {
|
||||
paygOptions.push(customHaiku)
|
||||
} else {
|
||||
paygOptions.push(getHaikuOption())
|
||||
}
|
||||
return paygOptions
|
||||
}
|
||||
|
||||
// @[MODEL LAUNCH]: Add the new model ID to the appropriate family pattern below
|
||||
|
|
@ -549,11 +552,6 @@ export function getModelOptions(fastMode = false): ModelOption[] {
|
|||
} else if (initialMainLoopModel !== null) {
|
||||
customModel = initialMainLoopModel
|
||||
}
|
||||
// 忽略 costrict-login 伪模型值(用户点击了推荐登录选项但未完成登录)
|
||||
if (customModel === 'costrict-login') {
|
||||
return filterModelOptionsByAllowlist(options)
|
||||
}
|
||||
|
||||
if (customModel === null || options.some(opt => opt.value === customModel)) {
|
||||
return filterModelOptionsByAllowlist(options)
|
||||
} else if (customModel === 'opusplan') {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user