Download desktop/src/lib/modelOptions.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 6.98 kB
-
https://huggingface.co/chenbhao/codev/resolve/e40073cc959cd4729bf4009662bca374c5c4e266/desktop/src/lib/modelOptions.ts
- Command line
-
hf download hf://chenbhao/codev@e40073cc959cd4729bf4009662bca374c5c4e266/desktop/src/lib/modelOptions.ts
-
curl -L -o modelOptions.ts https://huggingface.co/chenbhao/codev/resolve/e40073cc959cd4729bf4009662bca374c5c4e266/desktop/src/lib/modelOptions.ts
6.98 kB
| /** | |
| * Desktop model options utility. | |
| * | |
| * Mirrors key logic from TUI's src/utils/model/modelOptions.ts | |
| * but adapted for the desktop React context. | |
| * | |
| * Core value: provider-tier-aware model filtering (Max gets Opus, | |
| * Pro gets Sonnet, etc.) and environment-variable overrides that | |
| * TUI supports but the cc-haha /api/models endpoint doesn't expose. | |
| */ | |
| import type { ModelInfo } from '../types/settings' | |
| export type ModelOption = { | |
| value: string | null // null = default | |
| label: string | |
| description: string | |
| descriptionForModel?: string | |
| } | |
| /** Determine the effective auth provider from ~/.claude.json config. */ | |
| export async function getConfigAuthProvider(): Promise<string | null> { | |
| const { invoke } = await import('@tauri-apps/api/core') | |
| try { | |
| const config = await invoke<Record<string, unknown>>('get_claude_config') | |
| return (config?.authProvider as string | null) ?? null | |
| } catch { | |
| return null | |
| } | |
| } | |
| // Provider-specific model strings (simplified from TUI's modelStrings.ts) | |
| // Desktop uses these for display labels when the full TUI module isn't loaded. | |
| export const DESKTOP_MODEL_STRINGS = { | |
| opus46: 'claude-opus-4-6-20250514', | |
| sonnet46: 'claude-sonnet-4-6-20250608', | |
| haiku45: 'claude-haiku-4-5-20250522', | |
| opus45: 'claude-opus-4-5-20241022', | |
| sonnet45: 'claude-sonnet-4-5-20241022', | |
| haiku35: 'claude-haiku-3-5-20241022', | |
| } as const | |
| /** | |
| * Build ModelOption[] for a given provider and subscription tier. | |
| * This mirrors the logic in TUI's getModelOptions() but without the | |
| * full TUI bootstrap/graphbook dependency tree. | |
| * | |
| * @param provider - The active API provider from config | |
| * @param isSubscriber - Whether the user has a claude.ai subscription | |
| * @param subscriptionType - 'max' | 'pro' | 'team' | null | |
| * @param fastMode - Whether fast mode is enabled | |
| * @param availableModels - Existing model list from cc-haha (supplemental) | |
| */ | |
| export function buildModelOptions( | |
| provider: string | null, | |
| _isSubscriber: boolean, | |
| subscriptionType: string | null, | |
| fastMode: boolean, | |
| availableModels: ModelInfo[], | |
| ): ModelOption[] { | |
| const opts: ModelOption[] = [] | |
| // Default always first | |
| opts.push({ | |
| value: null, | |
| label: 'Default (recommended)', | |
| description: getDefaultDescription(subscriptionType), | |
| }) | |
| if (!provider) { | |
| return opts | |
| } | |
| const is3P = provider !== 'firstParty' | |
| switch (provider) { | |
| case 'firstParty': { | |
| // Sonnet 4.6 is default for PAYG / non-subscriber | |
| opts.push(makeSonnet46(is3P)) | |
| // Sonnet 1M if available | |
| opts.push(makeSonnet46_1M(is3P)) | |
| // Opus 4.6 alternatives | |
| if (isOpus1mMergeEnabled(_isSubscriber, subscriptionType)) { | |
| opts.push(makeOpus46_1M(fastMode, is3P)) | |
| } else { | |
| opts.push(makeOpus46(fastMode, is3P)) | |
| } | |
| // Haiku | |
| opts.push(makeHaiku45(is3P)) | |
| break | |
| } | |
| case 'openai': { | |
| // Map to GPT models for display | |
| opts.push({ value: 'gpt-5.4', label: 'GPT-5.4', description: 'GPT-5.4 · Recommended for most coding tasks' }) | |
| opts.push({ value: 'gpt-5.4-mini', label: 'GPT-5.4 Mini', description: 'GPT-5.4 Mini · Fastest OpenAI option' }) | |
| break | |
| } | |
| case 'opencode': { | |
| // OpenCode models - use available list or fall back to defaults | |
| if (availableModels.length > 0) { | |
| for (const m of availableModels) { | |
| opts.push({ value: m.id, label: m.name || m.id, description: m.description || 'OpenCode model' }) | |
| } | |
| } else { | |
| // Fallback defaults from DESKTOP_MODEL_STRINGS | |
| opts.push({ value: 'big-pickle', label: 'Big Pickle', description: '旗舰模型,限时免费,适合复杂任务' }) | |
| opts.push({ value: 'gpt-5-nano', label: 'GPT 5 Nano', description: '永久免费,轻量快速,隐私安全' }) | |
| } | |
| break | |
| } | |
| case 'nvidia': { | |
| if (availableModels.length > 0) { | |
| for (const m of availableModels) { | |
| opts.push({ value: m.id, label: m.name || m.id, description: m.description || 'NVIDIA NIM model' }) | |
| } | |
| } | |
| break | |
| } | |
| case 'local': { | |
| // Local models come from availableModels | |
| for (const m of availableModels) { | |
| opts.push({ value: m.id, label: m.name || m.id, description: m.description || 'Local model' }) | |
| } | |
| break | |
| } | |
| case 'openrouter': { | |
| if (availableModels.length > 0) { | |
| for (const m of availableModels) { | |
| opts.push({ value: m.id, label: m.name || m.id, description: m.description || 'OpenRouter model' }) | |
| } | |
| } | |
| break | |
| } | |
| default: | |
| // For unknown providers, supplement with availableModels | |
| for (const m of availableModels) { | |
| opts.push({ value: m.id, label: m.name || m.id, description: m.description || 'Model' }) | |
| } | |
| break | |
| } | |
| return opts | |
| } | |
| function getDefaultDescription(subscriptionType: string | null): string { | |
| if (subscriptionType === 'max') return 'Opus 4.6 · Most capable for complex work' | |
| if (subscriptionType === 'pro') return 'Sonnet 4.6 · Best for everyday tasks' | |
| return 'Sonnet 4.6 · Best for everyday tasks' | |
| } | |
| function isOpus1mMergeEnabled(_isSubscriber: boolean, subscriptionType: string | null): boolean { | |
| // Only enable 1M for Max/Team Premium subscribers on firstParty | |
| if (subscriptionType === 'max' || subscriptionType === 'team') return true | |
| return false | |
| } | |
| function makeSonnet46(is3P: boolean): ModelOption { | |
| return { | |
| value: 'sonnet', | |
| label: 'Sonnet', | |
| description: `Sonnet 4.6 · Best for everyday tasks${is3P ? '' : ' · $3 / M input, $15 / M output'}`, | |
| descriptionForModel: 'Sonnet 4.6 - best for everyday tasks', | |
| } | |
| } | |
| function makeSonnet46_1M(is3P: boolean): ModelOption { | |
| return { | |
| value: 'sonnet[1m]', | |
| label: 'Sonnet (1M context)', | |
| description: `Sonnet 4.6 with 1M context window${is3P ? '' : ' · $3 / M input, $15 / M output'}`, | |
| descriptionForModel: 'Sonnet 4.6 with 1M context window - for long sessions', | |
| } | |
| } | |
| function makeOpus46(fastMode: boolean, is3P: boolean): ModelOption { | |
| const suffix = fastMode && !is3P ? ' · ⚡ $7.50 / M input, $37.50 / M output' : '' | |
| return { | |
| value: 'opus', | |
| label: 'Opus', | |
| description: `Opus 4.6 · Most capable for complex work${suffix}`, | |
| descriptionForModel: 'Opus 4.6 - most capable for complex work', | |
| } | |
| } | |
| function makeOpus46_1M(fastMode: boolean, is3P: boolean): ModelOption { | |
| const suffix = fastMode && !is3P ? ' · ⚡ $7.50 / M input, $37.50 / M output' : '' | |
| return { | |
| value: 'opus[1m]', | |
| label: 'Opus (1M context)', | |
| description: `Opus 4.6 with 1M context${suffix}`, | |
| descriptionForModel: 'Opus 4.6 with 1M context - most capable for complex work', | |
| } | |
| } | |
| function makeHaiku45(is3P: boolean): ModelOption { | |
| return { | |
| value: 'haiku', | |
| label: 'Haiku', | |
| description: `Haiku 4.5 · Fastest for quick answers${is3P ? '' : ' · $0.08 / M input, $0.40 / M output'}`, | |
| descriptionForModel: 'Haiku 4.5 - fastest for quick answers', | |
| } | |
| } |