diff --git a/src/components/NewSession.tsx b/src/components/NewSession.tsx index e23420c..799b06c 100644 --- a/src/components/NewSession.tsx +++ b/src/components/NewSession.tsx @@ -1,11 +1,13 @@ import { useState, useEffect } from 'preact/hooks' -import { getBackends, getAgents } from '../lib/api' +import { getBackends, getAgents, fetchModels, loadModelsCache } from '../lib/api' -interface CreateParams { +export interface CreateParams { + name?: string cwd: string backend: string model: string agent: string + [key: string]: string | undefined } interface Props { @@ -13,29 +15,33 @@ interface Props { onCreate: (p: CreateParams) => Promise } -const DEFAULT_MODELS: Record = { - anthropic: 'claude-opus-4-5', - openai: 'gpt-4o', - ollama: 'llama3', - codewhisperer: 'anthropic.claude-3-5-sonnet-20241022-v2:0', - copilot: 'gpt-4o', -} + + +const ADVANCED_FIELDS = [ + 'maxTokens', 'maxCompletionTokens', 'temperature', 'topP', 'topK', + 'minP', 'topA', 'frequencyPenalty', 'presencePenalty', 'repetitionPenalty', + 'reasoning', 'reasoningEffort', 'includeReasoning', 'responseFormat', + 'stop', 'verbosity', +] as const export function NewSession({ onClose, onCreate }: Props) { + const [name, setName] = useState('') const [cwd, setCwd] = useState('~') const [backend, setBackend] = useState('anthropic') - const [model, setModel] = useState(DEFAULT_MODELS['anthropic']) + const [model, setModel] = useState('') const [agent, setAgent] = useState('default') const [backends, setBackends] = useState([]) const [agents, setAgents] = useState(['default']) + const [modelCache, setModelCache] = useState(loadModelsCache) + const [advanced, setAdvanced] = useState>({}) const [loading, setLoading] = useState(false) useEffect(() => { + fetchModels().then(setModelCache).catch(() => {}) getBackends().then(bs => { if (bs.length > 0) { setBackends(bs) setBackend(bs[0]) - setModel(DEFAULT_MODELS[bs[0]] ?? '') } }).catch(() => {}) getAgents().then(as => { @@ -43,17 +49,36 @@ export function NewSession({ onClose, onCreate }: Props) { }).catch(() => {}) }, []) + // Models for selected backend, sorted + const models = (modelCache[backend] ?? []).slice().sort() + + // Auto-select first model when backend or cache changes + useEffect(() => { + if (models.length) setModel(models[0]) + }, [backend, modelCache]) + const handleBackend = (b: string) => { setBackend(b) - setModel(DEFAULT_MODELS[b] ?? '') + const cached = modelCache[b] + if (cached?.length) setModel(cached[0]) } + const setAdv = (key: string, val: string) => + setAdvanced(prev => ({ ...prev, [key]: val })) + const handleSubmit = async (e: Event) => { e.preventDefault() if (!cwd.trim() || !model.trim()) return setLoading(true) try { - await onCreate({ cwd: cwd.trim(), backend, model: model.trim(), agent }) + const params: CreateParams = { + ...(name.trim() ? { name: name.trim() } : {}), + cwd: cwd.trim(), backend, model: model.trim(), agent, + } + for (const [k, v] of Object.entries(advanced)) { + if (v.trim()) params[k] = v.trim() + } + await onCreate(params) } finally { setLoading(false) } @@ -72,6 +97,17 @@ export function NewSession({ onClose, onCreate }: Props) {
+
+ + setName((e.target as HTMLInputElement).value)} + placeholder="optional" + autofocus + /> +
+
setCwd((e.target as HTMLInputElement).value)} placeholder="~/src/myproject" required - autofocus />
@@ -109,15 +144,33 @@ export function NewSession({ onClose, onCreate }: Props) {
- setModel((e.target as HTMLInputElement).value)} - placeholder="model name" + onChange={e => setModel((e.target as HTMLSelectElement).value)} required - /> + > + {models.map(m => ( + + ))} +
+
+ Advanced +
+ {ADVANCED_FIELDS.map(key => ( +
+ + setAdv(key, (e.target as HTMLInputElement).value)} + /> +
+ ))} +
+
+