Estimate and persist cumulative model cost for each thread.
The graph owns the durable total. CostTrackingMiddleware writes ordinary graph
deltas, while prepare_operation_cost gives server-owned operations a
rollback-safe delta to commit with their state update. Each cost update therefore
rides a graph checkpoint and works for local, headless, and remote execution
without a client-side state update -- the middleware also runs in local
in-process agents, where there is no server and the delta rides the local
checkpoint.
The client is a reader: it renders the streamed total and never maintains its own
lifetime figure.
Side questions and thread naming use isolated recorders and persist their subtotal
in the sessions database (see btw_cost). They can finish after the graph stops,
so their spend cannot depend on a later checkpoint. The client combines the
independently reported subtotals only for presentation.
Coverage is not limited to the agent's own model node. Offload/summarization and
the Auto mode classifier invoke a model directly, outside after_model, and
subagents run their own graph. _SessionCostRecorder — a callback handler
installed process-wide for every model request (see _install_recorder) —
collects one record per completed request, keyed by thread, and
CostTrackingMiddleware drains and prices those records on the main agent's
checkpoint path. New side invokes are covered with no extra wiring.
The recorder only collects; the middleware alone prices and writes. The agent's own response is still priced from state, but only when the recorder did not already charge that message ID, so a request is never counted twice. That fallback keeps main-agent cost correct even for a model that never fires callbacks.
Nested agents first checkpoint their own spend on the same private channel. That makes a completed model call durable before a later tool approval can interrupt the subgraph. When the subagent finishes, its middleware transfers the accumulated delta through an owner-scoped state entry. The subagent tool checkpoints that entry on the parent graph even when a sibling interrupts, while the private total itself remains isolated between graphs.
Every caller uses estimate_cost, the only function that imports or calls
genai-prices. The import is lazy so the package and its bundled pricing data
stay off the CLI startup path. On that first successful import a daemon-thread
updater starts refreshing the catalog from upstream hourly (see
_start_price_updater); DEEPAGENTS_CODE_PRICES_AUTO_UPDATE=0 or
[update].prices_auto_update = false in config.toml opts out, and
DEEPAGENTS_CODE_OFFLINE suppresses it along with every other network fetch.
When the active genai-prices catalog -- the bundled data, or the auto-updated
snapshot once one is installed -- has no rates for a model, a local override
catalog is consulted as a fallback-on-miss (see _override_price): the user's
own ~/.deepagents/prices.json first, then a maintainer-curated file shipped
as package data. PRICING.md documents the former for users and
bundled_prices.README.md the latter for maintainers. Unsupported models and
malformed usage return None; pricing must never interrupt a model turn.
Disable network downloads of managed binaries (e.g. ripgrep).
Parsed by is_env_truthy: accepts 1, true, yes, on as enabled. When
truthy, managed_tools.ensure_ripgrep will not attempt to download a binary
and falls back to the existing missing-tool notification + slow Python regex
path.
Custom-stream event type carrying provisional nested model usage.
Current shape version for nested model-usage events.
Custom-stream event type carrying the thread's absolute cumulative cost.
Emitted by the durable writer so the status bar can track spend live without
re-pricing anything. The payload is {"type": ..., "total": <usd>, "thread_id": <id>, "pricing_ok": <bool>, "breakdown": <optional mapping>}; total is the
full thread lifetime estimate, never a delta, so a client that misses an event
still converges on the next one. breakdown is likewise an absolute,
checkpointed estimate when available. thread_id lets a client that has since
switched threads discard a total belonging to the previous one. pricing_ok
reports whether price data loaded in the process that actually did the pricing,
which is the only way a client can tell a broken remote install from models with
no published rates.
Version of the private checkpoint and streamed cost-breakdown mapping.
Return whether env var name is set to a recognizably truthy value.
Unlike bool(os.environ.get(name)), this does not treat "0" or
"false" as enabled. Use this for on/off flags where the user would
reasonably expect VAR=0 to mean "disabled".
Return normalized cache reads and generic, 5m, and 1h writes.
Report whether genai-prices is currently able to price a request.
Note that True also covers "not yet attempted", so callers must only
consult this after pricing has been tried -- otherwise a session that has
priced nothing reads as healthy.
Estimate one model request's total cost in USD.
Preserves the established public interface while the private structured estimate powers checkpointed debugger detail.
Resolve the model and provider attached to a streamed model message.
Price model calls made by a server operation without committing them.
The caller must persist PreparedOperationCost.update atomically with the
operation state, or call rollback() if that write fails or is abandoned.
A prepare with a zero delta still consumes its records, so an abandoned
prepare must roll back even when it has nothing to write.
Extends agent state with per-checkpoint facts restored on resume.
Inherits the shared goal/rubric channels from GoalRubricChannels and adds
the channels unique to resume: the after-model token/spec facts and the
pending-goal proposal awaiting acceptance.
Additive token and estimated-cost accounting for one or more requests.
A priced side-operation delta pending checkpoint persistence.
Use exactly once: every prepare must be either committed (its update
persisted) or rolled back. _drain_recorded_costs is destructive, so a
prepare that is neither deletes that spend from the thread's lifetime total
permanently, and nothing can detect the loss afterwards.
_settled is init=False so a caller cannot construct a pre-neutralized
instance whose rollback is already a no-op. The class stays unfrozen only
because marking settlement on a frozen dataclass needs
object.__setattr__, which this project's lint rules reject.
Agent state extended with the cumulative thread-cost channel.
Own the thread's cumulative _session_cost_usd checkpoint value.
The main agent owns the thread total. Nested instances checkpoint local deltas before an interrupt can pause their graph, then transfer the completed subagent total through state for its owning parent graph to checkpoint.