feat: Reginald follows the model you load (auto-detect) + shows it in the header
Instead of a hardcoded model name (which forces LM Studio to JIT-swap your loaded model out — and fails when a big model already fills memory), resolve the model at request time: an explicit env override wins, else ask the server which model is *loaded* (LM Studio's native /api/v0/models), else the first non-embedding model, else a default. Reginald now uses whatever you load, no config churn. - main/model.ts: resolveLoadedModel() drives both model:status and model:chat; COMMITEA_MODEL_SMALL still overrides. - useChat exposes the resolved model id; the panel header shows it (google/gemma-4-26b-a4b-qat → "gemma-4-26b-a4b · local"). - live-reginald e2e: header assertion relaxed to the loaded model; timeouts raised for a slow big local model (~2 calls/turn + a reconcile). Verified: 14 fixture e2e green; live e2e drives the app against the loaded gemma-4-26b — "What now?" → "You should work on #2 … on the critical path, unblocks #33 and #4" (the real scheduler pick), header shows the live model. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -28,23 +28,57 @@ function resolveModelRouter(): ModelRouter | null {
|
||||
const baseUrl = process.env.COMMITEA_MODEL_URL ?? 'http://localhost:1234/v1'
|
||||
if (!baseUrl) return null
|
||||
return {
|
||||
small: { baseUrl, model: process.env.COMMITEA_MODEL_SMALL ?? 'google/gemma-4-e4b' },
|
||||
big: { baseUrl, model: process.env.COMMITEA_MODEL_BIG ?? 'qwen/qwen3.6-35b-a3b' },
|
||||
small: { baseUrl, model: process.env.COMMITEA_MODEL_SMALL ?? '' },
|
||||
big: { baseUrl, model: process.env.COMMITEA_MODEL_BIG ?? '' },
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve which model to actually ask for. An explicit env override wins;
|
||||
* otherwise ask the server which model is *loaded* (LM Studio's native
|
||||
* `/api/v0/models`) so Reginald follows whatever you load — no config churn on a
|
||||
* model switch. Falls back to the first non-embedding model, then a sane default.
|
||||
*/
|
||||
async function resolveLoadedModel(baseUrl: string, override: string): Promise<string> {
|
||||
if (override) return override
|
||||
const root = baseUrl.replace(/\/v1\/?$/, '')
|
||||
try {
|
||||
const res = await fetch(`${root}/api/v0/models`)
|
||||
if (res.ok) {
|
||||
const data = (await res.json()) as { data?: { id: string; state?: string; type?: string }[] }
|
||||
const loaded = (data.data ?? []).find((m) => m.state === 'loaded' && m.type !== 'embeddings')
|
||||
if (loaded) return loaded.id
|
||||
}
|
||||
} catch {
|
||||
// native API unavailable — fall through to the OpenAI listing
|
||||
}
|
||||
try {
|
||||
const res = await fetch(`${baseUrl.replace(/\/+$/, '')}/models`)
|
||||
if (res.ok) {
|
||||
const data = (await res.json()) as { data?: { id: string }[] }
|
||||
const first = (data.data ?? []).find((m) => !/embed/i.test(m.id))
|
||||
if (first) return first.id
|
||||
}
|
||||
} catch {
|
||||
// ignore — use the default
|
||||
}
|
||||
return 'google/gemma-4-e4b'
|
||||
}
|
||||
|
||||
export function registerModelIpc(): void {
|
||||
const router = resolveModelRouter()
|
||||
|
||||
ipcMain.handle('model:status', () => ({
|
||||
configured: !!router,
|
||||
model: router?.small.model ?? null,
|
||||
}))
|
||||
ipcMain.handle('model:status', async () => {
|
||||
if (!router) return { configured: false, model: null }
|
||||
const model = await resolveLoadedModel(router.small.baseUrl, router.small.model)
|
||||
return { configured: true, model }
|
||||
})
|
||||
|
||||
ipcMain.handle('model:chat', async (_event, messages: ChatMessage[]) => {
|
||||
if (!router) return { ok: false as const, reason: 'unconfigured' as const }
|
||||
const client = getGiteaClient()
|
||||
const chat = createChatClient(router.small, fetch)
|
||||
const model = await resolveLoadedModel(router.small.baseUrl, router.small.model)
|
||||
const chat = createChatClient({ ...router.small, model }, fetch)
|
||||
|
||||
const execute = async (name: string, args: unknown) => {
|
||||
if (name !== 'query_project') return { error: `unknown tool: ${name}` }
|
||||
|
||||
Reference in New Issue
Block a user