mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-20 13:53:32 +08:00
Keep built-in provider presets aligned with vendor-supported model IDs
The provider picker had two separate preset definitions and some defaults had started to drift from the model IDs vendors document for Claude Code / Anthropic-compatible usage. This change moves built-in presets to one server-side JSON source, serves that source through the existing presets API, and makes the desktop settings page consume the API instead of a duplicated frontend constant. The preset defaults were then corrected against vendor docs so the built-in values match documented model IDs and casing. Constraint: Built-in provider defaults must match official vendor Claude Code or Anthropic docs Rejected: Keep duplicated frontend and backend preset lists | values and casing drifted independently Confidence: high Scope-risk: narrow Reversibility: clean Directive: Update src/server/config/providerPresets.json and rerun provider-presets tests before changing built-in provider defaults again Tested: bun test src/server/__tests__/provider-presets.test.ts; cd desktop && bun run lint Not-tested: Manual desktop settings UI interaction after fetching presets from the API
This commit is contained in:
parent
7d1e2a2ee4
commit
e0142b286b
@ -8,7 +8,7 @@ import type {
|
|||||||
TestProviderConfigInput,
|
TestProviderConfigInput,
|
||||||
ProviderTestResult,
|
ProviderTestResult,
|
||||||
} from '../types/provider'
|
} from '../types/provider'
|
||||||
import type { ProviderPreset } from '../config/providerPresets'
|
import type { ProviderPreset } from '../types/providerPreset'
|
||||||
|
|
||||||
type ProvidersResponse = { providers: SavedProvider[]; activeId: string | null }
|
type ProvidersResponse = { providers: SavedProvider[]; activeId: string | null }
|
||||||
type ProviderResponse = { provider: SavedProvider }
|
type ProviderResponse = { provider: SavedProvider }
|
||||||
|
|||||||
@ -1,78 +0,0 @@
|
|||||||
// Provider presets inspired by cc-switch (https://github.com/farion1231/cc-switch)
|
|
||||||
// Original work by Jason Young, MIT License
|
|
||||||
|
|
||||||
import type { ApiFormat } from '../types/provider'
|
|
||||||
|
|
||||||
export type ModelMapping = {
|
|
||||||
main: string
|
|
||||||
haiku: string
|
|
||||||
sonnet: string
|
|
||||||
opus: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProviderPreset = {
|
|
||||||
id: string
|
|
||||||
name: string
|
|
||||||
baseUrl: string
|
|
||||||
apiFormat: ApiFormat
|
|
||||||
defaultModels: ModelMapping
|
|
||||||
needsApiKey: boolean
|
|
||||||
websiteUrl: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PROVIDER_PRESETS: ProviderPreset[] = [
|
|
||||||
{
|
|
||||||
id: 'official',
|
|
||||||
name: 'Claude Official',
|
|
||||||
baseUrl: '',
|
|
||||||
apiFormat: 'anthropic',
|
|
||||||
defaultModels: { main: '', haiku: '', sonnet: '', opus: '' },
|
|
||||||
needsApiKey: false,
|
|
||||||
websiteUrl: 'https://www.anthropic.com/claude-code',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'deepseek',
|
|
||||||
name: 'DeepSeek',
|
|
||||||
baseUrl: 'https://api.deepseek.com/anthropic',
|
|
||||||
apiFormat: 'anthropic',
|
|
||||||
defaultModels: { main: 'DeepSeek-V3.2', haiku: 'DeepSeek-V3.2', sonnet: 'DeepSeek-V3.2', opus: 'DeepSeek-V3.2' },
|
|
||||||
needsApiKey: true,
|
|
||||||
websiteUrl: 'https://platform.deepseek.com',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'zhipuglm',
|
|
||||||
name: 'Zhipu GLM',
|
|
||||||
baseUrl: 'https://open.bigmodel.cn/api/anthropic',
|
|
||||||
apiFormat: 'anthropic',
|
|
||||||
defaultModels: { main: 'glm-5', haiku: 'glm-5', sonnet: 'glm-5', opus: 'glm-5' },
|
|
||||||
needsApiKey: true,
|
|
||||||
websiteUrl: 'https://open.bigmodel.cn',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'kimi',
|
|
||||||
name: 'Kimi',
|
|
||||||
baseUrl: 'https://api.moonshot.cn/anthropic',
|
|
||||||
apiFormat: 'anthropic',
|
|
||||||
defaultModels: { main: 'kimi-k2.5', haiku: 'kimi-k2.5', sonnet: 'kimi-k2.5', opus: 'kimi-k2.5' },
|
|
||||||
needsApiKey: true,
|
|
||||||
websiteUrl: 'https://platform.moonshot.cn',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'minimax',
|
|
||||||
name: 'MiniMax',
|
|
||||||
baseUrl: 'https://api.minimaxi.com/anthropic',
|
|
||||||
apiFormat: 'anthropic',
|
|
||||||
defaultModels: { main: 'MiniMax-M2.7', haiku: 'MiniMax-M2.7', sonnet: 'MiniMax-M2.7', opus: 'MiniMax-M2.7' },
|
|
||||||
needsApiKey: true,
|
|
||||||
websiteUrl: 'https://platform.minimaxi.com',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'custom',
|
|
||||||
name: 'Custom',
|
|
||||||
baseUrl: '',
|
|
||||||
apiFormat: 'anthropic',
|
|
||||||
defaultModels: { main: '', haiku: '', sonnet: '', opus: '' },
|
|
||||||
needsApiKey: true,
|
|
||||||
websiteUrl: '',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
@ -7,9 +7,8 @@ import { Input } from '../components/shared/Input'
|
|||||||
import { Button } from '../components/shared/Button'
|
import { Button } from '../components/shared/Button'
|
||||||
import type { PermissionMode, EffortLevel, ThemeMode } from '../types/settings'
|
import type { PermissionMode, EffortLevel, ThemeMode } from '../types/settings'
|
||||||
import type { Locale } from '../i18n'
|
import type { Locale } from '../i18n'
|
||||||
import { PROVIDER_PRESETS } from '../config/providerPresets'
|
|
||||||
import type { ProviderPreset } from '../config/providerPresets'
|
|
||||||
import type { SavedProvider, UpdateProviderInput, ProviderTestResult, ModelMapping, ApiFormat } from '../types/provider'
|
import type { SavedProvider, UpdateProviderInput, ProviderTestResult, ModelMapping, ApiFormat } from '../types/provider'
|
||||||
|
import type { ProviderPreset } from '../types/providerPreset'
|
||||||
import { AdapterSettings } from './AdapterSettings'
|
import { AdapterSettings } from './AdapterSettings'
|
||||||
import { useAgentStore } from '../stores/agentStore'
|
import { useAgentStore } from '../stores/agentStore'
|
||||||
import { useSessionStore } from '../stores/sessionStore'
|
import { useSessionStore } from '../stores/sessionStore'
|
||||||
@ -100,14 +99,34 @@ function TabButton({ icon, label, active, onClick }: { icon: string; label: stri
|
|||||||
// ─── Provider Settings ──────────────────────────────────────
|
// ─── Provider Settings ──────────────────────────────────────
|
||||||
|
|
||||||
function ProviderSettings() {
|
function ProviderSettings() {
|
||||||
const { providers, activeId, isLoading, fetchProviders, deleteProvider, activateProvider, activateOfficial, testProvider } = useProviderStore()
|
const {
|
||||||
|
providers,
|
||||||
|
activeId,
|
||||||
|
presets,
|
||||||
|
isLoading,
|
||||||
|
isPresetsLoading,
|
||||||
|
fetchProviders,
|
||||||
|
fetchPresets,
|
||||||
|
deleteProvider,
|
||||||
|
activateProvider,
|
||||||
|
activateOfficial,
|
||||||
|
testProvider,
|
||||||
|
} = useProviderStore()
|
||||||
const fetchSettings = useSettingsStore((s) => s.fetchAll)
|
const fetchSettings = useSettingsStore((s) => s.fetchAll)
|
||||||
const t = useTranslation()
|
const t = useTranslation()
|
||||||
const [editingProvider, setEditingProvider] = useState<SavedProvider | null>(null)
|
const [editingProvider, setEditingProvider] = useState<SavedProvider | null>(null)
|
||||||
const [showCreateModal, setShowCreateModal] = useState(false)
|
const [showCreateModal, setShowCreateModal] = useState(false)
|
||||||
const [testResults, setTestResults] = useState<Record<string, { loading: boolean; result?: ProviderTestResult }>>({})
|
const [testResults, setTestResults] = useState<Record<string, { loading: boolean; result?: ProviderTestResult }>>({})
|
||||||
|
|
||||||
useEffect(() => { fetchProviders() }, [fetchProviders])
|
useEffect(() => {
|
||||||
|
void fetchProviders()
|
||||||
|
void fetchPresets()
|
||||||
|
}, [fetchPresets, fetchProviders])
|
||||||
|
|
||||||
|
const presetMap = useMemo(
|
||||||
|
() => new Map(presets.map((preset) => [preset.id, preset])),
|
||||||
|
[presets],
|
||||||
|
)
|
||||||
|
|
||||||
const handleDelete = async (provider: SavedProvider) => {
|
const handleDelete = async (provider: SavedProvider) => {
|
||||||
if (activeId === provider.id) return
|
if (activeId === provider.id) return
|
||||||
@ -144,7 +163,7 @@ function ProviderSettings() {
|
|||||||
<h2 className="text-base font-semibold text-[var(--color-text-primary)]">{t('settings.providers.title')}</h2>
|
<h2 className="text-base font-semibold text-[var(--color-text-primary)]">{t('settings.providers.title')}</h2>
|
||||||
<p className="text-sm text-[var(--color-text-tertiary)] mt-0.5">{t('settings.providers.description')}</p>
|
<p className="text-sm text-[var(--color-text-tertiary)] mt-0.5">{t('settings.providers.description')}</p>
|
||||||
</div>
|
</div>
|
||||||
<Button size="sm" onClick={() => setShowCreateModal(true)}>
|
<Button size="sm" onClick={() => setShowCreateModal(true)} disabled={isPresetsLoading || presets.length === 0}>
|
||||||
<span className="material-symbols-outlined text-[16px]">add</span>
|
<span className="material-symbols-outlined text-[16px]">add</span>
|
||||||
{t('settings.providers.addProvider')}
|
{t('settings.providers.addProvider')}
|
||||||
</Button>
|
</Button>
|
||||||
@ -191,7 +210,7 @@ function ProviderSettings() {
|
|||||||
{providers.map((provider) => {
|
{providers.map((provider) => {
|
||||||
const isActive = activeId === provider.id
|
const isActive = activeId === provider.id
|
||||||
const test = testResults[provider.id]
|
const test = testResults[provider.id]
|
||||||
const preset = PROVIDER_PRESETS.find((p) => p.id === provider.presetId)
|
const preset = presetMap.get(provider.presetId)
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={provider.id}
|
key={provider.id}
|
||||||
@ -255,12 +274,12 @@ function ProviderSettings() {
|
|||||||
|
|
||||||
{/* Create Modal — conditionally rendered so state resets on close */}
|
{/* Create Modal — conditionally rendered so state resets on close */}
|
||||||
{showCreateModal && (
|
{showCreateModal && (
|
||||||
<ProviderFormModal open={true} onClose={() => setShowCreateModal(false)} mode="create" />
|
<ProviderFormModal open={true} onClose={() => setShowCreateModal(false)} mode="create" presets={presets} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Edit Modal */}
|
{/* Edit Modal */}
|
||||||
{editingProvider && (
|
{editingProvider && (
|
||||||
<ProviderFormModal key={editingProvider.id} open={true} onClose={() => setEditingProvider(null)} mode="edit" provider={editingProvider} />
|
<ProviderFormModal key={editingProvider.id} open={true} onClose={() => setEditingProvider(null)} mode="edit" provider={editingProvider} presets={presets} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@ -273,6 +292,7 @@ type ProviderFormProps = {
|
|||||||
onClose: () => void
|
onClose: () => void
|
||||||
mode: 'create' | 'edit'
|
mode: 'create' | 'edit'
|
||||||
provider?: SavedProvider
|
provider?: SavedProvider
|
||||||
|
presets: ProviderPreset[]
|
||||||
}
|
}
|
||||||
|
|
||||||
function requirePreset(preset: ProviderPreset | undefined): ProviderPreset {
|
function requirePreset(preset: ProviderPreset | undefined): ProviderPreset {
|
||||||
@ -282,15 +302,27 @@ function requirePreset(preset: ProviderPreset | undefined): ProviderPreset {
|
|||||||
return preset
|
return preset
|
||||||
}
|
}
|
||||||
|
|
||||||
function ProviderFormModal({ open, onClose, mode, provider }: ProviderFormProps) {
|
function buildFallbackPreset(provider?: SavedProvider): ProviderPreset {
|
||||||
|
return {
|
||||||
|
id: provider?.presetId ?? 'custom',
|
||||||
|
name: provider?.name ?? 'Custom',
|
||||||
|
baseUrl: provider?.baseUrl ?? '',
|
||||||
|
apiFormat: provider?.apiFormat ?? 'anthropic',
|
||||||
|
defaultModels: provider?.models ?? { main: '', haiku: '', sonnet: '', opus: '' },
|
||||||
|
needsApiKey: true,
|
||||||
|
websiteUrl: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ProviderFormModal({ open, onClose, mode, provider, presets }: ProviderFormProps) {
|
||||||
const { createProvider, updateProvider, testConfig } = useProviderStore()
|
const { createProvider, updateProvider, testConfig } = useProviderStore()
|
||||||
const fetchSettings = useSettingsStore((s) => s.fetchAll)
|
const fetchSettings = useSettingsStore((s) => s.fetchAll)
|
||||||
const t = useTranslation()
|
const t = useTranslation()
|
||||||
|
|
||||||
const availablePresets = PROVIDER_PRESETS.filter((p) => p.id !== 'official')
|
const availablePresets = presets.filter((p) => p.id !== 'official')
|
||||||
const fallbackPreset = requirePreset(
|
const fallbackPreset = provider
|
||||||
availablePresets[availablePresets.length - 1] ?? PROVIDER_PRESETS[0],
|
? buildFallbackPreset(provider)
|
||||||
)
|
: requirePreset(availablePresets[availablePresets.length - 1])
|
||||||
const initialPreset = requirePreset(
|
const initialPreset = requirePreset(
|
||||||
provider
|
provider
|
||||||
? availablePresets.find((p) => p.id === provider.presetId) ?? fallbackPreset
|
? availablePresets.find((p) => p.id === provider.presetId) ?? fallbackPreset
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import type {
|
|||||||
TestProviderConfigInput,
|
TestProviderConfigInput,
|
||||||
ProviderTestResult,
|
ProviderTestResult,
|
||||||
} from '../types/provider'
|
} from '../types/provider'
|
||||||
|
import type { ProviderPreset } from '../types/providerPreset'
|
||||||
|
|
||||||
// 与后端 src/server/api/models.ts 的 DEFAULT_MODEL 保持一致:
|
// 与后端 src/server/api/models.ts 的 DEFAULT_MODEL 保持一致:
|
||||||
// 切回"官方"时把聊天页的 currentModel 重置到这个,避免残留第三方 provider
|
// 切回"官方"时把聊天页的 currentModel 重置到这个,避免残留第三方 provider
|
||||||
@ -19,10 +20,13 @@ const OFFICIAL_DEFAULT_MODEL_ID = 'claude-opus-4-7'
|
|||||||
type ProviderStore = {
|
type ProviderStore = {
|
||||||
providers: SavedProvider[]
|
providers: SavedProvider[]
|
||||||
activeId: string | null
|
activeId: string | null
|
||||||
|
presets: ProviderPreset[]
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
|
isPresetsLoading: boolean
|
||||||
error: string | null
|
error: string | null
|
||||||
|
|
||||||
fetchProviders: () => Promise<void>
|
fetchProviders: () => Promise<void>
|
||||||
|
fetchPresets: () => Promise<void>
|
||||||
createProvider: (input: CreateProviderInput) => Promise<SavedProvider>
|
createProvider: (input: CreateProviderInput) => Promise<SavedProvider>
|
||||||
updateProvider: (id: string, input: UpdateProviderInput) => Promise<SavedProvider>
|
updateProvider: (id: string, input: UpdateProviderInput) => Promise<SavedProvider>
|
||||||
deleteProvider: (id: string) => Promise<void>
|
deleteProvider: (id: string) => Promise<void>
|
||||||
@ -35,7 +39,9 @@ type ProviderStore = {
|
|||||||
export const useProviderStore = create<ProviderStore>((set, get) => ({
|
export const useProviderStore = create<ProviderStore>((set, get) => ({
|
||||||
providers: [],
|
providers: [],
|
||||||
activeId: null,
|
activeId: null,
|
||||||
|
presets: [],
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
|
isPresetsLoading: false,
|
||||||
error: null,
|
error: null,
|
||||||
|
|
||||||
fetchProviders: async () => {
|
fetchProviders: async () => {
|
||||||
@ -48,6 +54,16 @@ export const useProviderStore = create<ProviderStore>((set, get) => ({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
fetchPresets: async () => {
|
||||||
|
set({ isPresetsLoading: true, error: null })
|
||||||
|
try {
|
||||||
|
const { presets } = await providersApi.presets()
|
||||||
|
set({ presets, isPresetsLoading: false })
|
||||||
|
} catch (err) {
|
||||||
|
set({ isPresetsLoading: false, error: err instanceof Error ? err.message : String(err) })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
createProvider: async (input) => {
|
createProvider: async (input) => {
|
||||||
const { provider } = await providersApi.create(input)
|
const { provider } = await providersApi.create(input)
|
||||||
await get().fetchProviders()
|
await get().fetchProviders()
|
||||||
|
|||||||
18
desktop/src/types/providerPreset.ts
Normal file
18
desktop/src/types/providerPreset.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import type { ApiFormat } from './provider'
|
||||||
|
|
||||||
|
export type ModelMapping = {
|
||||||
|
main: string
|
||||||
|
haiku: string
|
||||||
|
sonnet: string
|
||||||
|
opus: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProviderPreset = {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
baseUrl: string
|
||||||
|
apiFormat: ApiFormat
|
||||||
|
defaultModels: ModelMapping
|
||||||
|
needsApiKey: boolean
|
||||||
|
websiteUrl: string
|
||||||
|
}
|
||||||
42
src/server/__tests__/provider-presets.test.ts
Normal file
42
src/server/__tests__/provider-presets.test.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { describe, test, expect } from 'bun:test'
|
||||||
|
|
||||||
|
import { handleProvidersApi } from '../api/providers.js'
|
||||||
|
import { PROVIDER_PRESETS } from '../config/providerPresets.js'
|
||||||
|
|
||||||
|
function makeRequest(method: string, urlStr: string): { req: Request; url: URL; segments: string[] } {
|
||||||
|
const url = new URL(urlStr, 'http://localhost:3456')
|
||||||
|
const req = new Request(url.toString(), { method })
|
||||||
|
const segments = url.pathname.split('/').filter(Boolean)
|
||||||
|
return { req, url, segments }
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('provider presets API', () => {
|
||||||
|
test('GET /api/providers/presets returns the configured presets', async () => {
|
||||||
|
const { req, url, segments } = makeRequest('GET', '/api/providers/presets')
|
||||||
|
const response = await handleProvidersApi(req, url, segments)
|
||||||
|
|
||||||
|
expect(response.status).toBe(200)
|
||||||
|
expect(await response.json()).toEqual({ presets: PROVIDER_PRESETS })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('configured presets include built-in official and custom entries', () => {
|
||||||
|
expect(PROVIDER_PRESETS.some((preset) => preset.id === 'official')).toBe(true)
|
||||||
|
expect(PROVIDER_PRESETS.some((preset) => preset.id === 'custom')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('configured presets keep current default model ids aligned with official provider docs', () => {
|
||||||
|
const deepseek = PROVIDER_PRESETS.find((preset) => preset.id === 'deepseek')
|
||||||
|
const zhipu = PROVIDER_PRESETS.find((preset) => preset.id === 'zhipuglm')
|
||||||
|
const kimi = PROVIDER_PRESETS.find((preset) => preset.id === 'kimi')
|
||||||
|
const minimax = PROVIDER_PRESETS.find((preset) => preset.id === 'minimax')
|
||||||
|
|
||||||
|
expect(deepseek?.defaultModels.main).toBe('deepseek-chat')
|
||||||
|
expect(deepseek?.defaultModels.haiku).toBe('deepseek-chat')
|
||||||
|
expect(zhipu?.defaultModels.main).toBe('glm-5.1')
|
||||||
|
expect(zhipu?.defaultModels.haiku).toBe('glm-4.5-air')
|
||||||
|
expect(zhipu?.defaultModels.sonnet).toBe('glm-5-turbo')
|
||||||
|
expect(zhipu?.defaultModels.opus).toBe('glm-5.1')
|
||||||
|
expect(kimi?.defaultModels.main).toBe('kimi-k2.6')
|
||||||
|
expect(minimax?.defaultModels.main).toBe('MiniMax-M2.7')
|
||||||
|
})
|
||||||
|
})
|
||||||
86
src/server/config/providerPresets.json
Normal file
86
src/server/config/providerPresets.json
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "official",
|
||||||
|
"name": "Claude Official",
|
||||||
|
"baseUrl": "",
|
||||||
|
"apiFormat": "anthropic",
|
||||||
|
"defaultModels": {
|
||||||
|
"main": "",
|
||||||
|
"haiku": "",
|
||||||
|
"sonnet": "",
|
||||||
|
"opus": ""
|
||||||
|
},
|
||||||
|
"needsApiKey": false,
|
||||||
|
"websiteUrl": "https://www.anthropic.com/claude-code"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "deepseek",
|
||||||
|
"name": "DeepSeek",
|
||||||
|
"baseUrl": "https://api.deepseek.com/anthropic",
|
||||||
|
"apiFormat": "anthropic",
|
||||||
|
"defaultModels": {
|
||||||
|
"main": "deepseek-chat",
|
||||||
|
"haiku": "deepseek-chat",
|
||||||
|
"sonnet": "deepseek-chat",
|
||||||
|
"opus": "deepseek-chat"
|
||||||
|
},
|
||||||
|
"needsApiKey": true,
|
||||||
|
"websiteUrl": "https://platform.deepseek.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "zhipuglm",
|
||||||
|
"name": "Zhipu GLM",
|
||||||
|
"baseUrl": "https://open.bigmodel.cn/api/anthropic",
|
||||||
|
"apiFormat": "anthropic",
|
||||||
|
"defaultModels": {
|
||||||
|
"main": "glm-5.1",
|
||||||
|
"haiku": "glm-4.5-air",
|
||||||
|
"sonnet": "glm-5-turbo",
|
||||||
|
"opus": "glm-5.1"
|
||||||
|
},
|
||||||
|
"needsApiKey": true,
|
||||||
|
"websiteUrl": "https://open.bigmodel.cn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "kimi",
|
||||||
|
"name": "Kimi",
|
||||||
|
"baseUrl": "https://api.moonshot.cn/anthropic",
|
||||||
|
"apiFormat": "anthropic",
|
||||||
|
"defaultModels": {
|
||||||
|
"main": "kimi-k2.6",
|
||||||
|
"haiku": "kimi-k2.6",
|
||||||
|
"sonnet": "kimi-k2.6",
|
||||||
|
"opus": "kimi-k2.6"
|
||||||
|
},
|
||||||
|
"needsApiKey": true,
|
||||||
|
"websiteUrl": "https://platform.moonshot.cn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "minimax",
|
||||||
|
"name": "MiniMax",
|
||||||
|
"baseUrl": "https://api.minimaxi.com/anthropic",
|
||||||
|
"apiFormat": "anthropic",
|
||||||
|
"defaultModels": {
|
||||||
|
"main": "MiniMax-M2.7",
|
||||||
|
"haiku": "MiniMax-M2.7",
|
||||||
|
"sonnet": "MiniMax-M2.7",
|
||||||
|
"opus": "MiniMax-M2.7"
|
||||||
|
},
|
||||||
|
"needsApiKey": true,
|
||||||
|
"websiteUrl": "https://platform.minimaxi.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "custom",
|
||||||
|
"name": "Custom",
|
||||||
|
"baseUrl": "",
|
||||||
|
"apiFormat": "anthropic",
|
||||||
|
"defaultModels": {
|
||||||
|
"main": "",
|
||||||
|
"haiku": "",
|
||||||
|
"sonnet": "",
|
||||||
|
"opus": ""
|
||||||
|
},
|
||||||
|
"needsApiKey": true,
|
||||||
|
"websiteUrl": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -1,78 +1,31 @@
|
|||||||
// Provider presets inspired by cc-switch (https://github.com/farion1231/cc-switch)
|
// Provider presets inspired by cc-switch (https://github.com/farion1231/cc-switch)
|
||||||
// Original work by Jason Young, MIT License
|
// Original work by Jason Young, MIT License
|
||||||
|
|
||||||
import type { ApiFormat } from '../types/provider.js'
|
import { z } from 'zod'
|
||||||
|
|
||||||
export type ModelMapping = {
|
import providerPresetsJson from './providerPresets.json'
|
||||||
main: string
|
import { ApiFormatSchema } from '../types/provider.js'
|
||||||
haiku: string
|
|
||||||
sonnet: string
|
|
||||||
opus: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProviderPreset = {
|
const ModelMappingSchema = z.object({
|
||||||
id: string
|
main: z.string(),
|
||||||
name: string
|
haiku: z.string(),
|
||||||
baseUrl: string
|
sonnet: z.string(),
|
||||||
apiFormat: ApiFormat
|
opus: z.string(),
|
||||||
defaultModels: ModelMapping
|
})
|
||||||
needsApiKey: boolean
|
|
||||||
websiteUrl: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PROVIDER_PRESETS: ProviderPreset[] = [
|
const ProviderPresetSchema = z.object({
|
||||||
{
|
id: z.string().min(1),
|
||||||
id: 'official',
|
name: z.string().min(1),
|
||||||
name: 'Claude Official',
|
baseUrl: z.string(),
|
||||||
baseUrl: '',
|
apiFormat: ApiFormatSchema,
|
||||||
apiFormat: 'anthropic',
|
defaultModels: ModelMappingSchema,
|
||||||
defaultModels: { main: '', haiku: '', sonnet: '', opus: '' },
|
needsApiKey: z.boolean(),
|
||||||
needsApiKey: false,
|
websiteUrl: z.string(),
|
||||||
websiteUrl: 'https://www.anthropic.com/claude-code',
|
})
|
||||||
},
|
|
||||||
{
|
const ProviderPresetsSchema = z.array(ProviderPresetSchema)
|
||||||
id: 'deepseek',
|
|
||||||
name: 'DeepSeek',
|
export type ModelMapping = z.infer<typeof ModelMappingSchema>
|
||||||
baseUrl: 'https://api.deepseek.com/anthropic',
|
export type ProviderPreset = z.infer<typeof ProviderPresetSchema>
|
||||||
apiFormat: 'anthropic',
|
|
||||||
defaultModels: { main: 'DeepSeek-V3.2', haiku: 'DeepSeek-V3.2', sonnet: 'DeepSeek-V3.2', opus: 'DeepSeek-V3.2' },
|
export const PROVIDER_PRESETS = ProviderPresetsSchema.parse(providerPresetsJson)
|
||||||
needsApiKey: true,
|
|
||||||
websiteUrl: 'https://platform.deepseek.com',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'zhipuglm',
|
|
||||||
name: 'Zhipu GLM',
|
|
||||||
baseUrl: 'https://open.bigmodel.cn/api/anthropic',
|
|
||||||
apiFormat: 'anthropic',
|
|
||||||
defaultModels: { main: 'glm-5', haiku: 'glm-5', sonnet: 'glm-5', opus: 'glm-5' },
|
|
||||||
needsApiKey: true,
|
|
||||||
websiteUrl: 'https://open.bigmodel.cn',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'kimi',
|
|
||||||
name: 'Kimi',
|
|
||||||
baseUrl: 'https://api.moonshot.cn/anthropic',
|
|
||||||
apiFormat: 'anthropic',
|
|
||||||
defaultModels: { main: 'kimi-k2.5', haiku: 'kimi-k2.5', sonnet: 'kimi-k2.5', opus: 'kimi-k2.5' },
|
|
||||||
needsApiKey: true,
|
|
||||||
websiteUrl: 'https://platform.moonshot.cn',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'minimax',
|
|
||||||
name: 'MiniMax',
|
|
||||||
baseUrl: 'https://api.minimaxi.com/anthropic',
|
|
||||||
apiFormat: 'anthropic',
|
|
||||||
defaultModels: { main: 'MiniMax-M2.7', haiku: 'MiniMax-M2.7', sonnet: 'MiniMax-M2.7', opus: 'MiniMax-M2.7' },
|
|
||||||
needsApiKey: true,
|
|
||||||
websiteUrl: 'https://platform.minimaxi.com',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'custom',
|
|
||||||
name: 'Custom',
|
|
||||||
baseUrl: '',
|
|
||||||
apiFormat: 'anthropic',
|
|
||||||
defaultModels: { main: '', haiku: '', sonnet: '', opus: '' },
|
|
||||||
needsApiKey: true,
|
|
||||||
websiteUrl: '',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user