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:
@@ -15,7 +15,9 @@ export interface ChatPanelProps {
|
||||
}
|
||||
|
||||
export function ChatPanel({ onOpenDirectives, offline }: ChatPanelProps) {
|
||||
const { msgs, thinking, live, steps, send: sendChat } = useChat()
|
||||
const { msgs, thinking, live, model, steps, send: sendChat } = useChat()
|
||||
// shorten "google/gemma-4-26b-a4b-qat" → "gemma-4-26b" for the header chip
|
||||
const modelLabel = model ? (model.split('/').pop() ?? model).replace(/-(qat|instruct|it|gguf)$/i, '') : 'gemma-4'
|
||||
const [text, setText] = useState('')
|
||||
const scrollRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
@@ -56,7 +58,7 @@ export function ChatPanel({ onOpenDirectives, offline }: ChatPanelProps) {
|
||||
<Icon name="sparkles" size={16} style={{ color: offline ? 'var(--ink-3)' : 'var(--jade)' }} />
|
||||
<span style={{ font: 'var(--text-body-strong)', color: 'var(--ink-1)' }}>Reginald</span>
|
||||
<span style={{ font: '400 11px var(--font-mono)', color: 'var(--ink-3)', marginLeft: 'auto' }}>
|
||||
{offline ? 'offline · queueing' : live ? 'gemma-4 · local' : 'demo · scripted'}
|
||||
{offline ? 'offline · queueing' : live ? `${modelLabel} · local` : 'demo · scripted'}
|
||||
</span>
|
||||
<IconButton icon="history" label="Directive log" size="sm" onClick={onOpenDirectives} />
|
||||
</header>
|
||||
|
||||
@@ -14,6 +14,8 @@ export interface ChatState {
|
||||
thinking: boolean
|
||||
/** true once a model endpoint is confirmed; otherwise the panel echoes the demo reply. */
|
||||
live: boolean
|
||||
/** The loaded model's id when live (for the header). */
|
||||
model: string | null
|
||||
/** Tools Reginald consulted on the last turn (for a subtle activity line). */
|
||||
steps: string[]
|
||||
send: (text: string) => void
|
||||
@@ -31,6 +33,7 @@ export function useChat(): ChatState {
|
||||
const [convo, setConvo] = useState<ChatMessage[]>([])
|
||||
const [thinking, setThinking] = useState(false)
|
||||
const [live, setLive] = useState(false)
|
||||
const [model, setModel] = useState<string | null>(null)
|
||||
const [steps, setSteps] = useState<string[]>([])
|
||||
const convoRef = useRef(convo)
|
||||
convoRef.current = convo
|
||||
@@ -42,6 +45,7 @@ export function useChat(): ChatState {
|
||||
.then((s) => {
|
||||
if (alive && s.configured) {
|
||||
setLive(true)
|
||||
setModel(s.model)
|
||||
setSeed([LIVE_GREETING])
|
||||
}
|
||||
})
|
||||
@@ -97,5 +101,5 @@ export function useChat(): ChatState {
|
||||
[live],
|
||||
)
|
||||
|
||||
return { msgs: [...seed, ...convo], thinking, live, steps, send }
|
||||
return { msgs: [...seed, ...convo], thinking, live, model, steps, send }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user