Skip to content

[v5] RFC: backend capacity, model inventory, and placement #5698

Description

@hanthor

Retitled and rewritten. The first draft led with an implementation and a
cute codename. Both were wrong. What matters is the four capabilities below —
and the one that makes the rest age well is that nothing about models can be
hardcoded
. Models ship faster than any table in any repo survives.

Summary

Hive's governor decides when to kick an agent. Nothing decides which
backend and model it should be pointed at
, or whether that backend can
serve right now
. So a hive with a dry DeepSeek balance, a capped Codex
account, or a lapsed Claude session keeps kicking agents into backends that
cannot answer. They fail quietly and look healthy.

We have run an external component covering this for ~6 weeks. This RFC is not a
request to merge it — it is 1400 lines of bash against hive's HTTP API. It is a
report on the four capabilities the problem actually decomposes into, and
evidence for each.

The four capabilities

1. Usage probe — normalized headroom per backend

"How much is left" has a different answer shape per provider, and none is a
token count:

Backend Where headroom lives Shape
claude GET /api/oauth/usage with the OAuth token percent + resets_at, three limits
codex /status typed into a live TUI "NN% left, resets HH:MM on DD Mon"
agy agy --print /usage two pools × weekly/5-hour, percent remaining
deepseek GET /user/balance dollars of prepaid credit

A model-scoped cap is not provider exhaustion. Anthropic returns session,
weekly_all, and weekly_scoped — the last carries scope.model and caps one
model class. Taking the max across all three parked our entire fleet while
claude-sonnet-5 and claude-opus-5 both answered normally. Headroom must
come from unscoped limits, with scoped caps reported separately so
placement can avoid that model rather than the whole provider.

Probes that read a provider through a consumer of it will deadlock. This
bit us four separate times: google required an unpaused agent though its probe
is headless; openai skipped paused agents though a paused agent's CLI is still
running; contributors parked every worker and then could not measure the
provider needed to restore them; an emptied credential file reported "unknown"
instead of "exhausted". If hive builds this, that is the invariant to design
against.

2. Model inventory — what a backend can run right now

This is the capability we have least of, and the one that decides whether the
rest ages well. Today we hardcode model IDs in a table. That table is wrong the
week a model ships.

The backends are wildly inconsistent, which is exactly why it belongs behind
one interface:

Backend Enumerable? How
claude yes, structured GET /v1/models with the OAuth token — returns id + display_name
deepseek yes, structured OpenAI-compatible GET /models
agy prose only agy --print /models renders a human table, not a list
codex no --model takes a string; nothing enumerates

Verified live on our fleet — Anthropic returns claude-fable-5-1,
claude-opus-5, claude-sonnet-5, claude-opus-4-8, … and DeepSeek returns
deepseek-v4-flash, deepseek-v4-pro, deepseek-v4-flash-vision-exp.

Two of four can answer precisely. Hive is better placed than any operator to
normalize that and to push the other two upstream.

A related, cheap win: validate a model id against the inventory before
setting it.
A model that a backend cannot run is a hard startup failure that
currently surfaces as a dead pane.

3. Tiering — ranked by benchmark, refreshed, never hardcoded

Placement needs to know which models are interchangeable in competence, so an
agent can move sideways rather than being silently downgraded. That ranking
cannot live in a repo:

  • Our table has 14 hardcoded rungs. Every one is a guess with a shelf life.
  • Our refresher (pull rankings from a benchmark API, rebuild the table) has
    never successfully run — it needs an API key nobody set, and it fails
    quietly, so the fleet has always used the stale built-in table.

Two things we got wrong that are worth encoding as warnings:

  • Bigger is not better for agent work. DeepSeek's own release notes say not
    to move to V4-Pro for agent tasks on price grounds — Flash leads the agent
    suites. A naive "rank by size/price" tiering inverts this.
  • Benchmarks measure agent+model pairs, not models. The same model swings
    ~8 points between harnesses. A ranking that ignores the harness does not
    predict the fleet running these CLIs.

So: auto-tier from an external agentic benchmark, refresh on a schedule, and
make the source pluggable
— but treat the result as a proposal, not an
auto-adopted change. Which leads to:

4. Operator-editable policy

Ours is a bash heredoc. That is the wrong shape for something an operator must
tune. The decisions a human needs to make:

  • which tier each agent requires (a competence floor, not a cost knob)
  • provider preference order — ours puts free before metered, which is not
    universal
  • per-provider exhaustion thresholds, since a shared subscription and a private
    one deserve different caution
  • whether a newly-benchmarked model is adopted automatically or offered

And one behaviour worth stating explicitly, because it is counter-intuitive:
rotate on exhaustion, never on preference. If a provider is usable and the
current rung is legitimate for the agent's tier, leave it alone even when
something better-ranked exists — otherwise the fleet churns every tick chasing
whichever pool is momentarily cheapest.

What we would upstream first

  1. Document the atomic PUT /api/config/agent/{name}/models as the
    preferred path over switch then model. Two calls with no transaction
    leave codex --model claude-opus-5 — unlaunchable — when the second times
    out. It is also ~2.4s versus ~45s. Pure docs; removes a live footgun.
  2. A model-inventory interface a backend can implement, plus validation of
    a model id before it is set. Two of four backends can already answer.
  3. A headroom interface, unscoped-vs-scoped separated, with the
    consumer-deadlock invariant written down.
  4. Exhaustion as an agent condition, alongside Authenticated /
    Producing. Hive detects and reports agent state well; it has nowhere to
    say "the backend is out of quota".
  5. Optional placement engine, off by default, consuming 2–4.

1–4 are useful even if nobody builds 5. We are not asking hive to adopt our
tier rankings — those are ours, and they change.

Environment

Two spokes + self-hosted hub on Talos, ACMM L5/L6, backends claude / agy /
codex / pi across Claude Max, Google AI Pro, ChatGPT Plus and DeepSeek prepaid.
Related: #5632, #5633, #5661, #5690, #5691.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions