12.6 Model providers and budgets
packs:ai-agents
After this you can: broker model credentials for an AI Agent with no key on any host, manage the catalog, mark a model approved, and meter spend with token budgets.
Before you start — tick each one:
- Lesson 12.2 — Enrol, own, assign, grant, ceiling (
opschain learn 12.2)
In the GUI: Model providers in the Operations area manages the catalog — the key field is write-only — and Felix's agent sheet links the provider it is bound to; its Budgets tab is the one pane for every allocated token budget (per provider, expandable per agent and grant), mirrored by the dashboard's Model budgets panel. Operations → Providers (the overview, and a model provider's usage dashboard) offers Export CSV — the same model-usage rows opschain agents usage -o csv prints, over the dashboard's current range and grain.
Felix takes on its second job description here: an AI Agent, type
ai, reasoning against a language model. The model's API key is exactly
the credential that should never be copied onto every agent host. A
model provider is the platform's answer — rather than share API keys
with each agent, the agent references a provider record that does the
heavy lifting — and a token budget on its grant bounds what it may
spend. Creating or enabling a provider needs packs:ai-agents, the same
pack an ai-type Felix enrols under; the lane's reference is
opschain docs ai-agents.
1. The custody model
A provider is a first-class admin record beside identity providers and
secrets providers: a code, a kind (anthropic | openai |
azure-foundry-anthropic | azure-openai | openai-compatible), an
endpoint (optional for the hosted kinds, where empty means the kind's
default; required for the Azure kinds and openai-compatible), an
encrypted, write-only API key and an enabled flag — audited writes
and admin-plane authorisation on every verb.
The Azure kinds serve Microsoft estates: azure-foundry-anthropic for
Claude models deployed in Azure AI Foundry (the Anthropic wire — the
Claude adapter is unchanged) and azure-openai for GPT-family models on
Azure OpenAI (the chat-completions wire, via the OpenAI adapter). A
Microsoft 365 or GitHub Copilot seat licence is not model capacity:
brokered calls consume an Azure OpenAI or Foundry deployment you
provision and pay for in Azure. Authentication is by API key in this
release.
Models you host yourself. The openai-compatible kind registers a
model on your own infrastructure — vLLM, Ollama, TGI, llama.cpp server,
LiteLLM or any stack that speaks the OpenAI wire format — under the same
governance as the vendor kinds. The endpoint is required and the
credential is optional: with one stored the gateway sends
Authorization: Bearer, without one no auth header — the usual shape
for an unauthenticated or mTLS-fronted internal endpoint. Endpoints
behind a private certificate authority carry TLS trust options
(--ssl-verify, --ssl-ca-cert). The gateway forwards requests and
responses faithfully and never translates the protocol.
An agent in brokered custody names its provider by catalog code in
its manifest (OPSCHAIN_MODEL_PROVIDER on the agent host) and calls the
platform's model gateway (/api/model/{provider}/…) with its own
session credential; the gateway injects the stored key server-side and
streams the model's answer through. The consequences:
- No model key ever exists on an agent host. A compromised host yields a revocable agent session, not your vendor key.
- Rotation happens here, in one place — update the provider and every agent bound to it is rotated.
- Registration refuses an agent whose manifest names a provider the
catalog does not hold (
warn:agent:model_provider_refused), so a typo fails loudly at enrolment, not silently at first model call. - Exactly one entry, by code. Several entries of one kind coexist
on purpose; an agent uses only the entry it names.
opschain admin agent show felixrenders it as the MODEL PROVIDER row with the entry's kind and live standing;agents listas the PROVIDER column. The MODEL row beside it is the vendor Felix discloses, not the catalog code.
The two other custody modes keep the platform out of the path: the
host's own secret handling delivers ANTHROPIC_API_KEY to the process
(agent-host custody), or your vault serves the key at deploy time
(vault-to-host). Felix discloses its mode either way.
2. Managing the catalog
opschain admin model-providers create demo-anthropic --kind anthropic --credential-env ANTHROPIC_API_KEY
opschain admin model-providers list
CODE KIND ENDPOINT CREDENTIAL DATA-HANDLING ENABLED ID
demo-anthropic anthropic - set (write-only) - yes 019a0d7e-10c2-7f7a-8b53-3b1c9a4f2e01
The key is never passed on the command line — --credential-env names
a variable, --credential-prompt reads it without echo, and
--credential-ref pins a secret-vault:// reference — and reads
report only whether a credential is stored. show <code> prints one
record, update <code> with a supplied credential rotates the key, and
delete <code> removes it (agents referencing it refuse at the gateway
until it is re-created).
The data-handling-approved marker. Each entry carries a review
marker (--data-handling-approved on create or update; the
DATA-HANDLING column). Setting it records that your estate has
reviewed the provider's data-processing terms — training use,
retention, residency, DPA — and approves it for lanes that send
customer-submitted content to a model, such as the Support SME's AI
lane (lesson 12.8). Those lanes refuse to be configured against an
unreviewed entry; a provider you add later inherits the same one-time
check. Ordinary brokered calls over your estate's own content are not
gated by the marker:
opschain admin model-providers update demo-anthropic --data-handling-approved
The column now reads approved.
3. Felix as an AI Agent
Enrol Felix with the ai job description: the manifest declares
type: ai and discloses the model, the adapter, the tool surface and
custody_mode: brokered, naming demo-anthropic as its provider
(opschain agent dev --type ai --model anthropic/claude-sonnet-4-5 --adapter opschain-claude-adapter --tool-surface request_lane,docs --custody brokered is the local shape; lesson 12.7). An ai-type
enrolment hard-depends on packs:agent-governance + packs:ai-agents
and refuses naming every missing pack, so ungoverned AI cannot even
enrol — and the ai type's SUPERVISED ceiling from lesson 12.2 pins it.
Every brokered call is checked (a registered, non-revoked, non-halted
agent whose manifest names the addressed provider) and audited:
info:agent:model_call with the observed input_tokens and
output_tokens, or warn:agent:model_call_refused. Every model
interaction lands in Felix's adapter-transit transcript, redacted
before persistence (lesson 12.3).
4. Token budgets — metering brokered spend
Brokered calls are metered in the provider's own units: the gateway reads token usage — and the model the response actually served — off each response as it streams through, and lands every call in a per-minute usage bucket. A trust grant carries token caps on up to six windows beside its rate and budget caps — a sliding minute (the burst cap) and the local day, week, month, fiscal quarter and fiscal year on the estate's reporting calendar:
opschain admin trust grant --agent felix --project limebank \
--scope /projects/limebank --action 'patch_*' --tier supervised \
--tokens-per-day 500000 --tokens-per-month 8000000 \
--note "AI lane: half a million tokens a day, eight million a month"
The stored grant comes back with tokens_per_day: 500000 and
tokens_per_month: 8000000 (--tokens still works as the day alias);
on an estate with the approval fabric it is a supervised raise and
waits in the inbox like any other (lesson 12.2). Once Felix's spend
reaches a window's cap, further model calls refuse pre-flight — loudly,
with warn:trust:cap_refused naming the grant, the window that failed
first, the provider, the spend and the local boundary it resets at —
until that window resets; at 80% of a cap warn:trust:cap_approaching
warns once for the period. The windows reset on the estate's calendar,
not UTC: set usage.timezone (an IANA zone) and
usage.fiscal_year_start (07-01 for an Australian July year) in
the global settings, or in a project's own settings for a subsidiary on
another calendar.
See what was spent, and set the caps from it:
opschain agents usage --project limebank --by agent # daily tokens by agent, last 30 days
opschain agents usage --project limebank --grain minute --since 1h --by model
opschain agents usage --recommend-caps --grant <grant-id> # p95 × 1.5 per window the grant carries
opschain agents usage --recommend-caps --grant <grant-id> --propose # raise the replacement grant through the funnel
The trust review shows the spend per provider (tokens_by_provider)
and, per token-capped grant, token_caps — observed against cap for
every window with its local reset — with its cost attribution
(cost_centre, from the project's effective metadata); the dashboard's
Model tokens panel and the agent page's Usage tab show the same
buckets, and every agent's row on Governance → Agents → Posture (and
opschain agents posture) carries the same caps and spend beside its
tier, owner, release and last action — the inventory of every agent
enrolled in this estate (opschain docs agent-posture). The caps belong to the governed agent identity — owned,
killable, transcribed — not to an API key. Tokens and calls are the
platform's measure; -o csv hands the buckets to whoever prices them
(opschain docs provider-usage).
A ceiling on the provider itself
Grant caps bound each agent; nothing in them bounds the provider as a whole, so ten agents with a generous daily cap each can together spend more with one vendor than the estate meant to. The provider record carries its own ceiling on the same six windows — a budget across every agent and every call brokered through it, with no estate default (a ceiling is a fact of one provider's contract):
opschain admin model-providers update demo-anthropic \
--tokens-per-day 2000000 --tokens-per-month 40000000
opschain admin model-providers show demo-anthropic # Token budget: 2000000 per day, 40000000 per month
Both apply and the tighter wins: the gateway checks every window of
Felix's grants first, then every window of the provider, and the first
one over its ceiling refuses by name — an agent over its own cap is told
so, while a provider at its ceiling answers model provider demo-anthropic: tokens per local day ceiling of 2000000 reached — resets at … to every agent through it. The same two events carry the news,
with scope: provider instead of scope: grant. Read the budget on the
provider's dashboard (Administration → Providers → the provider's
page: a gauge per window beside its token rows), with
opschain admin providers usage --kind model --code demo-anthropic
(the ceiling, the spend and what remains per window) or from the
opschain.provider.token_budget_remaining gauge your OTel receiver
gets. In the GUI the provider dialog's Token budget group sets each
window to Unlimited or a custom ceiling, and the provider's Agent
token caps panel reads the grant caps beside it. The whole picture —
every provider's allocated tokens per day across its agents, the spend
today, the headroom, the provider's own day ceiling and an
over-allocation marker, expandable per agent and grant — is the
Budgets tab of Administration → Model providers, with the
dashboard's Model budgets panel as its summary.
5. Entitlement and lapse
On licence lapse, stored providers keep serving existing agents and only new registrations and the enabling of a disabled record refuse — a lapse never bricks a running fleet. The halt scalpels of lesson 12.5 apply to an AI Agent unchanged, and a model call during a halt is refused with the same typed reason in its error JSON.
Check yourself
- Where does the model key live, and what happens when Felix's token budget runs out?
- Which event carries the tokens of one brokered call, and which row of
opschain admin agent show felixnames the catalog entry those calls go through?
What you learned
- A model provider is a catalog record with a write-only key; a brokered agent names it by code and never holds the key.
- Five kinds cover the hosted vendors, the Azure deployments and any OpenAI-compatible stack you host yourself.
- The data-handling-approved marker gates lanes that send customer-submitted content to a model.
- A grant's token caps meter spend at the gateway on six windows — a sliding minute and the local day, week, month, fiscal quarter and fiscal year on the estate's reporting calendar; the first window over its cap refuses with
warn:trust:cap_refusednaming the window and its local reset,warn:trust:cap_approachingwarns at 80%,opschain agents usageshows the buckets and--recommend-capssets the caps from them. - A provider record can carry its own ceiling on the same six windows — a budget across every agent and call through it, checked after the grant windows (both apply, the tighter wins), refused by name, read on the provider's dashboard and with
opschain admin providers usage --kind model --code <code>.
Next: lesson 12.7 — Build one: the SDK and agent dev (opschain learn 12.7), or back to your track (opschain learn).