Watch
0
0
Fork
You've already forked hyperhive
0

swarm: show each agent's effective model and effort, and where they came from

Each agent now reports the model and effort it runs on, and where each
comes from (`swarm`, `agent_nix`, `local_file` or `default`), in its
turn-state header on `$SWARM.agent-state.<agent>`: `effort`,
`model_source` and `effort_source` join `model`. The shared
`swarm_queue_client::agent_inference::{Effective, Source}` types spell
those fields for both ends.

The Bus keeps a source next to the model and effort. `Bus::new` takes it
from the local fallback chain (`harness_state::local_model` /
`local_effort`, which now return the source with the value), a `/model`
or `/effort` pick sets `local_file`, and the swarm poller sets `swarm`
or, on a clear, the local chain's source. A swarm value equal to the one
the agent already runs is now applied once, so the source moves to
`swarm` and the header is republished; before, `next()` skipped it as
unchanged.

swarm-controller subscribes to every agent's header with the reader
grant it already holds for the state relay, and keeps the latest
inference fields per agent in memory (`inference::Reported`).
`GET /api/agents/{name}/inference` now answers
`{configured, effective}`, `effective` being `null` until the agent's
first header with these fields since the controller started.
`swarmctl agent inference get` prints both; `set` reads `configured`.

Refs #5034
This commit is contained in:
atlas 2026-10-10 00:00:25 +02:00 • committed by mara
commit d723fac0be
20 changed files with 655 additions and 113 deletions

View file

@ -327,6 +327,16 @@ pub fn agent_state_subject_wildcard() -> String {
agent_state_subject("*")
}
/// The agent an [`agent_state_subject`] names, or `None` for any other
/// subject.
#[must_use]
pub fn agent_from_state_subject(subject: &str) -> Option<&str> {
let agent = subject
.strip_prefix(AGENT_STATE_SUBJECT_PREFIX)?
.strip_prefix('.')?;
(!agent.is_empty() && !agent.contains('.')).then_some(agent)
}
/// Shared by [`agent_state_subject`] and its wildcard.
const AGENT_STATE_SUBJECT_PREFIX: &str = "$SWARM.agent-state";
@ -933,6 +943,22 @@ mod tests {
assert_eq!(subagent_term::subject_wildcard(), "$SWARM.term.*.sub.*");
}
#[test]
fn the_agent_is_read_back_out_of_its_state_subject() {
assert_eq!(
agent_from_state_subject(&agent_state_subject("iris")),
Some("iris")
);
for other in [
"$SWARM.agent-state.",
"$SWARM.agent-state.iris.sub.scout",
"$SWARM.agent-stateiris",
"$SWARM.term.iris",
] {
assert_eq!(agent_from_state_subject(other), None, "{other}");
}
}
#[test]
fn a_tls_client_config_builds_once_the_provider_is_installed() {
install_crypto_provider();