Watch
0
0
Fork
You've already forked hyperhive
0

agents: remove the hive-level /model control; the swarm editor sets the model

The per-agent web UI no longer sets the model: the `/model` slash
command, the model picker, `POST /api/model`, `Bus::set_model` and the
`hyperhive-model` writer are gone. The model chip is display-only and
shows where the model comes from (`model_source` in `/api/state`). The
model is set on the swarm (swarm UI agents page, `swarmctl agent
inference set`).

The read side is unchanged: with no swarm value an agent runs its
agent.nix model, else an existing `hyperhive-model` file, else haiku.
Existing files are not touched.

With the picker gone, its model list goes too: `available_models` in
`/api/state`, the ACP model-list filter, and the harness's global
`HIVE_AVAILABLE_MODELS`. The subagent daemon keeps its own
`HIVE_AVAILABLE_MODELS` from mcp.nix, and the `availableModels`
assertion stays.

`/effort` (command, picker, route, file) is unchanged.

Refs #5034
This commit is contained in:
atlas 2026-10-10 23:06:15 +02:00
commit 594693f4c7
26 changed files with 244 additions and 425 deletions

View file

@ -21,7 +21,7 @@ use tokio::sync::broadcast;
use crate::harness_state::{
configured_effort, configured_model, context_window_tokens, local_effort, local_model,
persist_effort, persist_model, read_harness_state, write_harness_state,
persist_effort, read_harness_state, write_harness_state,
};
const CHANNEL_CAPACITY: usize = 256;
@ -115,8 +115,8 @@ pub enum LiveEvent {
/// during the short-lived in-progress window — the client
/// re-fetches only while that flow is active.
StatusChanged { status: String },
/// `/api/model` switched the active claude model. The web UI
/// updates the chip + the per-turn stats sink will key off this
/// The active claude model changed (a swarm value applied or cleared).
/// The web UI updates the chip + the per-turn stats sink will key off this
/// to mark the boundary in its log.
ModelChanged { model: String },
/// `/api/effort` switched the active claude effort level. Applies on
@ -282,8 +282,8 @@ pub struct Bus {
store: Option<Arc<EventStore>>,
/// Current turn-loop state + since-when (unix seconds).
state: Arc<Mutex<(TurnState, i64)>>,
/// Model name passed to `claude --model`. Default `haiku`; the
/// operator can override at runtime via `POST /api/model`.
/// Model name passed to `claude --model`. Default `haiku`; a value the
/// swarm sets replaces it at runtime (`crate::swarm_inference`).
model: Arc<Mutex<String>>,
effort: Arc<Mutex<String>>,
/// Where `model` and `effort` come from, as the swarm is told in this
@ -341,8 +341,7 @@ pub struct Bus {
turn_canceller: Arc<OnceLock<hive_runtime::Canceller>>,
/// The model and effort the session offers, for a runtime that reads them
/// from its session (ACP). Set once by the serve loop when it builds the
/// session; unset on claude, whose pickers list the configured models and
/// `EFFORT_LEVELS`.
/// session; unset on claude, whose effort picker lists `EFFORT_LEVELS`.
session_choices: Arc<OnceLock<hive_runtime::Choices>>,
/// Current fresh-claude-session id (FK to `sessions.id`). Set by the
/// bin loop after minting a session row on a fresh start; stamped onto
@ -407,7 +406,7 @@ impl Bus {
// Write the resolved active model to hyperhive-harness.json on
// startup so hive-c0re can surface the model badge without reading
// hyperhive-model directly. Written once here; updated on every
// set_model() call (runtime override) and emit_status() call.
// apply_model() call and emit_status() call.
write_harness_state(was_rate_limited, was_needs_login, Some(&initial_model));
Self {
tx: Arc::new(tx),
@ -562,7 +561,7 @@ impl Bus {
}
/// Currently-selected claude model name. Read on every turn so a
/// `/model <name>` flip takes effect on the next turn.
/// swarm change takes effect on the next turn.
///
/// # Panics
///
@ -572,27 +571,10 @@ impl Bus {
self.model.lock().unwrap().clone()
}
/// Switch the model for future turns. The current turn (if any)
/// keeps the model it was already running. Persisted to the agent's
/// state dir (`hyperhive-model`) so the override survives harness
/// restart and container rebuild (gone on `--purge`, matching
/// every other piece of agent state).
///
/// # Panics
///
/// Panics if the internal lock is poisoned.
pub fn set_model(&self, name: impl Into<String>) {
let value: String = name.into();
if let Err(e) = persist_model(&value) {
tracing::warn!(error = ?e, "model: persist failed");
}
self.apply_model(value, Source::LocalFile);
}
/// Switch the model for future turns without persisting it, so
/// `hyperhive-model` keeps the agent's local value. For a value the
/// swarm sets (`crate::swarm_inference`). Emits `ModelChanged` even when
/// only `source` changed.
/// Switch the model for future turns. Never writes `hyperhive-model`, so
/// clearing the swarm value finds the agent's local value again. For a
/// value the swarm sets (`crate::swarm_inference`). Emits `ModelChanged`
/// even when only `source` changed.
///
/// # Panics
///