Watch
0
0
Fork
You've already forked hyperhive
0

swarm-ui: per-agent cap on concurrently running subagents

Nothing capped how many subagents an agent runs at once; the operator
wants to set that per agent from swarm-ui (#5234, shape (b)).

AgentInference gains `max_subagents: Option<u32>`. An absent field
decodes as None, so every stored value still parses and means "no cap",
which is today's behaviour.

swarm-controller accepts 1..=32 on PUT. 0 would refuse every start; 32
is far more concurrent claude sessions than one agent container has
memory for, so anything above it is a typo, not a cap.

The subagent daemon already read the agent's agent-inference key for
subagent_models; it now reads it on every start and continue (not only
those naming a model) and refuses a new run once the cap's worth of
names is tracked, counted under the same lock as the name reservation.
Every tracked slot counts: running, starting, a goal run between turns,
and an interrupted one still exiting. A goal re-prompt never goes
through the reservation, so it is never refused. A failed read applies
no cap, as it falls back for models.

swarmctl's `inference set` does GET, merge, PUT; its mirror type now
carries the field so a set from the CLI keeps a cap set in swarm-ui
instead of dropping it, and `get` prints it.
This commit is contained in:
atlas 2026-10-13 19:24:13 +02:00 • committed by mara
commit 9a71d1e094
10 changed files with 393 additions and 94 deletions

View file

@ -1,7 +1,8 @@
# Per-agent inference settings
or a model name with whitespace or a leading `-`, or a `max_subagents` outside 1 to 32 |# Per-agent inference settings
The swarm can set the model and reasoning effort each agent's turns run on,
plus the models it may start subagents on. A field you set wins over the
plus the models it may start subagents on and how many subagents it may run
at once. A field you set wins over the
agent's own value; a field you leave unset keeps the agent on its own value,
so an agent you set nothing for runs as it does today.
@ -20,6 +21,10 @@ swarmctl agent inference clear iris
agent's `availableModels` as the models its subagent `start` and
`continue` calls may name, read on each call
([`subagent.md`](../tools/subagent.md#which-models-a-subagent-may-run-on)).
- You set the subagent cap, `max_subagents`, in the swarm UI's inference
dialog; `set` has no flag for it and keeps the stored one. It's a whole
number from 1 to 32; unset means no cap
([`subagent.md`](../tools/subagent.md#how-many-subagents-may-run-at-once)).
- `get` shows each field, naming the unset ones (or says the swarm sets
nothing for the agent), and then the model and effort the agent reports
running on, each with where it comes from.
@ -39,6 +44,7 @@ command.
model: opus
effort: (not set; the agent's own)
subagent_models: (not set; the agent's own)
max_subagents: (not set; no cap)
running model: opus (swarm)
running effort: medium (local_file)
```
@ -83,7 +89,12 @@ The settings live in the `agent-inference` KV bucket, one key per agent,
which swarm-controller alone writes:
```json
{ "model": "opus", "effort": "high", "subagent_models": ["haiku", "sonnet"] }
{
"model": "opus",
"effort": "high",
"subagent_models": ["haiku", "sonnet"],
"max_subagents": 3
}
```
Every field is optional. The agent runs the same with `{}` as with no key;
@ -108,8 +119,8 @@ in-memory model or effort without writing `hyperhive-model` or
`hyperhive-effort`, which keep the agent's own value.
The subagent daemon (`hive-subagent-mcp`'s `swarm_models`) reads
`subagent_models` with the same direct get, under the same credential, on
each `start` or `continue` that names a model.
`subagent_models` and `max_subagents` with the same direct get, under the
same credential, on each `start` or `continue`.
`effective` comes from the agent's turn-state header on
`$SWARM.agent-state.<agent>` ([`README.md`](README.md)), which carries