agent page: show resolved model as the model badge's tooltip
mara: 'i cannot see if sonnet became claude-sonnet-5 or something else.' /api/state now carries resolved_model (bus.last_resolved_model(), already used by serve_common.rs's turn-stats rollup for the same alias-vs-actual reason) and the model badge shows it as a title tooltip, same pattern as the ctx/cost badges.
This commit is contained in:
parent
a5ea1e523d
commit
c73ce03244
4 changed files with 26 additions and 1 deletions
|
|
@ -192,6 +192,7 @@ export function Root() {
|
||||||
stateTone={turnDef.tone}
|
stateTone={turnDef.tone}
|
||||||
stateTooltip={STATE_TOOLTIPS[effectiveTurnState]}
|
stateTooltip={STATE_TOOLTIPS[effectiveTurnState]}
|
||||||
model={state.model}
|
model={state.model}
|
||||||
|
resolvedModel={state.resolved_model ?? undefined}
|
||||||
availableModels={state.available_models}
|
availableModels={state.available_models}
|
||||||
onSelectModel={(name) => {
|
onSelectModel={(name) => {
|
||||||
postModel(name).then(() => refresh());
|
postModel(name).then(() => refresh());
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,11 @@ export interface StatusChipsProps {
|
||||||
stateTone: BadgeTone;
|
stateTone: BadgeTone;
|
||||||
stateTooltip?: string;
|
stateTooltip?: string;
|
||||||
model: string;
|
model: string;
|
||||||
|
/** Concrete model id the last completed turn actually ran on, e.g.
|
||||||
|
* "claude-sonnet-4-5-20260805" — shown as the badge's tooltip so the
|
||||||
|
* operator can tell what an alias resolved to. `undefined` before any
|
||||||
|
* turn has completed. */
|
||||||
|
resolvedModel?: string;
|
||||||
availableModels: string[];
|
availableModels: string[];
|
||||||
onSelectModel: (name: string) => void;
|
onSelectModel: (name: string) => void;
|
||||||
effort: string;
|
effort: string;
|
||||||
|
|
@ -54,12 +59,14 @@ function Picker({
|
||||||
options,
|
options,
|
||||||
descriptions,
|
descriptions,
|
||||||
onSelect,
|
onSelect,
|
||||||
|
title,
|
||||||
}: {
|
}: {
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
options: string[];
|
options: string[];
|
||||||
descriptions: Record<string, string>;
|
descriptions: Record<string, string>;
|
||||||
onSelect: (v: string) => void;
|
onSelect: (v: string) => void;
|
||||||
|
title?: string;
|
||||||
}) {
|
}) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const dropdownOptions: DropdownOption[] = options.map((name) => ({
|
const dropdownOptions: DropdownOption[] = options.map((name) => ({
|
||||||
|
|
@ -69,7 +76,7 @@ function Picker({
|
||||||
}));
|
}));
|
||||||
return (
|
return (
|
||||||
<div class="status-chip-anchor">
|
<div class="status-chip-anchor">
|
||||||
<Badge label={label} value={value} onClick={() => setOpen((o) => !o)} expanded={open} />
|
<Badge label={label} value={value} onClick={() => setOpen((o) => !o)} expanded={open} title={title} />
|
||||||
<Dropdown
|
<Dropdown
|
||||||
open={open}
|
open={open}
|
||||||
options={dropdownOptions}
|
options={dropdownOptions}
|
||||||
|
|
@ -92,6 +99,7 @@ export function StatusChips({
|
||||||
stateTone,
|
stateTone,
|
||||||
stateTooltip,
|
stateTooltip,
|
||||||
model,
|
model,
|
||||||
|
resolvedModel,
|
||||||
availableModels,
|
availableModels,
|
||||||
onSelectModel,
|
onSelectModel,
|
||||||
effort,
|
effort,
|
||||||
|
|
@ -123,6 +131,7 @@ export function StatusChips({
|
||||||
options={availableModels}
|
options={availableModels}
|
||||||
descriptions={MODEL_DESCRIPTIONS}
|
descriptions={MODEL_DESCRIPTIONS}
|
||||||
onSelect={onSelectModel}
|
onSelect={onSelectModel}
|
||||||
|
title={resolvedModel ? `resolved to ${resolvedModel}` : undefined}
|
||||||
/>
|
/>
|
||||||
{availableEfforts.length ? (
|
{availableEfforts.length ? (
|
||||||
<Picker
|
<Picker
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ export interface AgentState {
|
||||||
turn_state: string;
|
turn_state: string;
|
||||||
turn_state_since: number;
|
turn_state_since: number;
|
||||||
model: string;
|
model: string;
|
||||||
|
resolved_model: string | null;
|
||||||
context_window_tokens: number;
|
context_window_tokens: number;
|
||||||
ctx_usage: TokenUsage | null;
|
ctx_usage: TokenUsage | null;
|
||||||
cost_usage: TokenUsage | null;
|
cost_usage: TokenUsage | null;
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,13 @@ pub(super) async fn api_state(State(state): State<AppState>) -> axum::Json<State
|
||||||
let inbox = recent_inbox(&state.socket).await;
|
let inbox = recent_inbox(&state.socket).await;
|
||||||
let (turn_state, turn_state_since) = state.bus.state_snapshot();
|
let (turn_state, turn_state_since) = state.bus.state_snapshot();
|
||||||
let model = state.bus.model();
|
let model = state.bus.model();
|
||||||
|
// The alias the operator selected (e.g. "sonnet") vs. what the last
|
||||||
|
// turn actually ran on (e.g. "claude-sonnet-4-5-20260805") — mara:
|
||||||
|
// "i cannot see if sonnet became claude-sonnet-5 or something else".
|
||||||
|
// `None` until the first turn completes (no assistant event to
|
||||||
|
// resolve from yet); same source `serve_common.rs`'s turn-stats
|
||||||
|
// rollup already prefers over the requested alias.
|
||||||
|
let resolved_model = state.bus.last_resolved_model();
|
||||||
let context_window_tokens = state.bus.effective_context_window(&model);
|
let context_window_tokens = state.bus.effective_context_window(&model);
|
||||||
let ctx_usage = state.bus.last_ctx_usage();
|
let ctx_usage = state.bus.last_ctx_usage();
|
||||||
let cost_usage = state.bus.last_cost_usage();
|
let cost_usage = state.bus.last_cost_usage();
|
||||||
|
|
@ -51,6 +58,7 @@ pub(super) async fn api_state(State(state): State<AppState>) -> axum::Json<State
|
||||||
turn_state,
|
turn_state,
|
||||||
turn_state_since,
|
turn_state_since,
|
||||||
model,
|
model,
|
||||||
|
resolved_model,
|
||||||
context_window_tokens,
|
context_window_tokens,
|
||||||
ctx_usage,
|
ctx_usage,
|
||||||
cost_usage,
|
cost_usage,
|
||||||
|
|
@ -135,6 +143,12 @@ pub(super) struct StateSnapshot {
|
||||||
/// the operator can see what they just switched to (and what's
|
/// the operator can see what they just switched to (and what's
|
||||||
/// in flight). Mutable at runtime via `POST /api/model`.
|
/// in flight). Mutable at runtime via `POST /api/model`.
|
||||||
model: String,
|
model: String,
|
||||||
|
/// The concrete model id the most recent completed turn actually
|
||||||
|
/// ran on (e.g. `"claude-sonnet-4-5-20260805"`), resolved from that
|
||||||
|
/// turn's assistant events — `model` above is the alias the
|
||||||
|
/// operator picked, which claude itself maps to a specific dated
|
||||||
|
/// snapshot. `None` before any turn has completed.
|
||||||
|
resolved_model: Option<String>,
|
||||||
/// Effective context-window token budget for the current model.
|
/// Effective context-window token budget for the current model.
|
||||||
/// Primary source: API-reported `modelUsage.*.contextWindow` from
|
/// Primary source: API-reported `modelUsage.*.contextWindow` from
|
||||||
/// the last result event (authoritative per-inference active window).
|
/// the last result event (authoritative per-inference active window).
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue