feat: add model selection for AI agents (claude, codex, gemini)

Add a new ModelSelector component that allows users to choose specific model variants for different AI agents. This includes:
- ModelSelector UI component with agent-specific model options
- Model parameter propagation through API, RPC, and spawning layers
- Support for auto model selection (default behavior)
- Localization strings for English and Chinese
- Type definitions for model options per agent
This commit is contained in:
weishu
2026-01-23 11:55:29 +08:00
parent 29551478cc
commit e130dfd24b
13 changed files with 88 additions and 4 deletions
@@ -0,0 +1,34 @@
import type { AgentType } from './types'
import { MODEL_OPTIONS } from './types'
import { useTranslation } from '@/lib/use-translation'
export function ModelSelector(props: {
agent: AgentType
model: string
isDisabled: boolean
onModelChange: (value: string) => void
}) {
const { t } = useTranslation()
const options = MODEL_OPTIONS[props.agent]
return (
<div className="flex flex-col gap-1.5 px-3 py-3">
<label className="text-xs font-medium text-[var(--app-hint)]">
{t('newSession.model')}{' '}
<span className="font-normal">({t('newSession.model.optional')})</span>
</label>
<select
value={props.model}
onChange={(e) => props.onModelChange(e.target.value)}
disabled={props.isDisabled}
className="w-full px-3 py-2 text-sm rounded-lg border border-[var(--app-divider)] bg-[var(--app-bg)] text-[var(--app-text)] focus:outline-none focus:ring-2 focus:ring-[var(--app-link)] disabled:opacity-50"
>
{options.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</div>
)
}
+13
View File
@@ -12,6 +12,7 @@ import { ActionButtons } from './ActionButtons'
import { AgentSelector } from './AgentSelector'
import { DirectorySection } from './DirectorySection'
import { MachineSelector } from './MachineSelector'
import { ModelSelector } from './ModelSelector'
import { SessionTypeSelector } from './SessionTypeSelector'
import { YoloToggle } from './YoloToggle'
@@ -34,6 +35,7 @@ export function NewSession(props: {
const [isDirectoryFocused, setIsDirectoryFocused] = useState(false)
const [pathExistence, setPathExistence] = useState<Record<string, boolean>>({})
const [agent, setAgent] = useState<AgentType>('claude')
const [model, setModel] = useState('auto')
const [yoloMode, setYoloMode] = useState(false)
const [sessionType, setSessionType] = useState<SessionType>('simple')
const [worktreeName, setWorktreeName] = useState('')
@@ -46,6 +48,10 @@ export function NewSession(props: {
}
}, [sessionType])
useEffect(() => {
setModel('auto')
}, [agent])
useEffect(() => {
if (props.machines.length === 0) return
if (machineId && props.machines.find((m) => m.id === machineId)) return
@@ -193,6 +199,7 @@ export function NewSession(props: {
machineId,
directory: directory.trim(),
agent,
model: model !== 'auto' ? model : undefined,
yolo: yoloMode,
sessionType,
worktreeName: sessionType === 'worktree' ? (worktreeName.trim() || undefined) : undefined
@@ -251,6 +258,12 @@ export function NewSession(props: {
isDisabled={isFormDisabled}
onAgentChange={setAgent}
/>
<ModelSelector
agent={agent}
model={model}
isDisabled={isFormDisabled}
onModelChange={setModel}
/>
<YoloToggle
yoloMode={yoloMode}
isDisabled={isFormDisabled}
+21
View File
@@ -1,2 +1,23 @@
export type AgentType = 'claude' | 'codex' | 'gemini'
export type SessionType = 'simple' | 'worktree'
export const MODEL_OPTIONS: Record<AgentType, { value: string; label: string }[]> = {
claude: [
{ value: 'auto', label: 'Auto' },
{ value: 'opus', label: 'Opus' },
{ value: 'sonnet', label: 'Sonnet' },
],
codex: [
{ value: 'auto', label: 'Auto' },
{ value: 'gpt-5.2-codex', label: 'GPT-5.2 Codex' },
{ value: 'gpt-5.2', label: 'GPT-5.2' },
{ value: 'gpt-5.1-codex-max', label: 'GPT-5.1 Codex Max' },
{ value: 'gpt-5.1-codex-mini', label: 'GPT-5.1 Codex Mini' },
],
gemini: [
{ value: 'auto', label: 'Auto' },
{ value: 'gemini-3-pro-preview', label: 'Gemini 3 Pro Preview' },
{ value: 'gemini-2.5-pro', label: 'Gemini 2.5 Pro' },
{ value: 'gemini-2.5-flash', label: 'Gemini 2.5 Flash' },
],
}