Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libs/code/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,7 @@ The main cost is the client/server boundary. When debugging, first decide which
- For local setup and debugging, see [`DEVELOPMENT.md`](./DEVELOPMENT.md).
- For command behavior, see [`COMMANDS.md`](./COMMANDS.md).
- For lifecycle hooks (`hooks.json`), see [`HOOKS.md`](./HOOKS.md).
- For cost estimates and local pricing overrides (`prices.json`), see
[`PRICING.md`](./PRICING.md).
- For security boundaries, see [`THREAT_MODEL.md`](./THREAT_MODEL.md).
- For package-specific coding conventions, see [`AGENTS.md`](./AGENTS.md).
105 changes: 105 additions & 0 deletions libs/code/PRICING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Cost estimates and local pricing overrides

`/cost` reports an estimate for the current thread. Rates come from
[genai-prices](https://github.com/pydantic/genai-prices), which ships an offline
catalog and refreshes it hourly in the background (opt out with
`DEEPAGENTS_CODE_PRICES_AUTO_UPDATE=0`, or `[update].prices_auto_update = false`
in `config.toml`).

Estimates are display-only. Nothing in dcode caps spend or gates execution on
them, so a wrong rate costs you an inaccurate number and nothing else.

## When a model has no rates

A model the catalog does not cover is left out of the total. `/cost` distinguishes
that from a genuinely free request: it reports how many of the recorded requests
are included in the figure.

That happens with a newly released model, a self-hosted or proxied endpoint, or a
provider name that does not line up with what genai-prices calls it. If you would
rather see an estimate than a gap, write your own rates to:

```
~/.deepagents/prices.json
```

The file is read once, on the first request that needs it. **Edits take effect on
the next dcode start**, not mid-session.

## File format

`prices.json` uses the same provider-array schema as genai-prices' own
`prices/new_data/v2/data.json`, so an entry can be contributed upstream as-is.
It is a JSON array of providers:

```json
[
{
"id": "my-proxy",
"name": "My proxy",
"api_pattern": "gateway\\.example\\.internal",
"models": [
{
"id": "house-model-v2",
"match": { "equals": "house-model-v2" },
"prices": { "input_mtok": 2.5, "output_mtok": 10.0 }
}
]
}
]
```

Required, and easy to omit:

| Field | Where | Notes |
| --- | --- | --- |
| `id` | provider | Must be the provider id dcode reports for the request — see below. |
| `name` | provider | Any display string. |
| `api_pattern` | provider | Regex against the API URL. Unused by dcode, but the schema requires it. |
| `id` | model | Any identifier; used in log messages. |
| `match` | model | How to recognize the model: `{"equals": ...}`, `{"starts_with": ...}`, `{"contains": ...}`, or `{"regex": ...}`. |
| `prices` | model | At least one rate. |

Rates are per million tokens: `input_mtok`, `output_mtok`, and optionally
`cache_read_mtok`, `cache_write_mtok`, `output_reasoning_mtok`,
`input_audio_mtok`. Only list a bucket you have a real rate for — tokens in an
omitted bucket are billed at the ordinary input or output rate rather than being
dropped.

## Getting the provider id right

This is where a hand-written file usually goes wrong. dcode resolves the
LangChain provider name through its own alias table before looking anything up,
so the id it searches on is not always the string you would expect.

If no provider in your file claims that id, dcode falls back to searching **every**
provider in the file by model id alone. That keeps your entry reachable, but it
also means an entry can price a request that ran against a different provider —
`llama-3.3-70b` costs very different amounts on Bedrock, Together, and Groq. When
that happens you get a warning naming both providers; pin the entry by using the
id from the warning.

## Diagnosing a file that is not working

Everything below is logged to the Debug Console (`Ctrl+\`), each message once per
session:

| What you see | What it means |
| --- | --- |
| Nothing at all, cost still missing | The file is not where dcode looks, or is empty. Both are silent by design. |
| "No pricing override matched model=… though … contributed N model entries" | The file loaded, but nothing matched. The `provider_id` in the message is the post-alias id your entry must use. |
| "Could not parse … as JSON" / "must be an array of providers" | Syntax or shape problem. The payload must be a bare array, not `{"providers": [...]}`. |
| "the entries failed validation against the genai-prices provider schema" | A required field is missing or mistyped. |
| "the request ran against provider …" | Priced by a sweep, possibly from the wrong provider's entry. See above. |
| "its entry matched but its rates were rejected" | A rate is unusable — negative, or large enough to overflow. |

A malformed `prices.json` never breaks a model turn and never disables ordinary
pricing: the bad source is dropped and everything else still prices.

## Built-in stopgaps

dcode also ships a small maintainer-curated catalog for models upstream has not
priced yet. Your file wins over it on a conflicting `(provider id, model id)`
pair, and both are consulted only when genai-prices itself has no rates. See
[`deepagents_code/bundled_prices.README.md`](./deepagents_code/bundled_prices.README.md)
for that policy.
1 change: 1 addition & 0 deletions libs/code/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Do not run `dcode` in a directory you do not trust without a sandbox backend. Fo
- **[Changelog](https://github.com/langchain-ai/deepagents/blob/main/libs/code/CHANGELOG.md)**
- **[Source code](https://github.com/langchain-ai/deepagents/tree/main/libs/code)**
- **[Deep Agents SDK](https://github.com/langchain-ai/deepagents)** — underlying agent harness
- **[Cost estimates & pricing overrides](https://github.com/langchain-ai/deepagents/blob/main/libs/code/PRICING.md)** — where `/cost` gets its rates, and how to supply your own
- [LangChain Academy](https://academy.langchain.com/) — Comprehensive, free courses on LangChain libraries and products, made by the LangChain team.
- [Code of Conduct](https://github.com/langchain-ai/langchain/?tab=coc-ov-file) — community guidelines and standards

Expand Down
80 changes: 80 additions & 0 deletions libs/code/deepagents_code/bundled_prices.README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Built-in pricing overrides

`bundled_prices.json` is the maintainer-curated pricing catalog `cost_tracking`
consults when the active genai-prices catalog — the bundled data, or the
auto-updated snapshot once one is installed — has no rates for a model. It
exists for one situation: a model users already run has shipped, but upstream
does not price it yet.

The file uses the raw provider-array schema of genai-prices'
`prices/new_data/v2/data.json`, so entries are copy-pasteable into an upstream
PR. JSON has no comments; this policy lives here instead:

- Every entry must be backed by an upstream genai-prices PR (or issue) opened
first, and must carry a `price_comments` field linking it (e.g.
`"Stopgap pending pydantic/genai-prices#123"`). An entry without a tracked
upstream path is one nobody will remember to remove.
`test_every_bundled_override_entry_is_priced_and_links_upstream` enforces
this, so a missing link fails the suite rather than the review.
- Remove each entry as soon as upstream's `data.json` covers the model. The
hourly auto-update picks that up well before the release that would bump our
pin, so an entry usually goes inert on merge rather than on release. The
override only fires on a primary-catalog miss, so a stale entry is normally
inert rather than harmful — but that depends on the primary lookup actually
succeeding, which is not guaranteed when the provider id LangChain reports
differs from the one upstream cataloged. In that case a stale entry keeps
billing its own possibly-outdated rate, silently. Dead entries also cost
review time.
- Do not use this file to override rates for models upstream already prices.
Upstream always wins: the override catalog is never consulted when the
primary lookup succeeds.

## Adding an entry

The file ships as an empty array (`[]`), so there is no in-file example. A
minimal one entry:

```json
[
{
"id": "anthropic",
"name": "Anthropic",
"api_pattern": "api\\.anthropic\\.com",
"models": [
{
"id": "claude-example-5",
"match": { "equals": "claude-example-5" },
"price_comments": "Stopgap pending pydantic/genai-prices#123",
"prices": { "input_mtok": 3.0, "output_mtok": 15.0 }
}
]
}
]
```

Required fields, none of which the schema will fill in for you: `id`, `name`,
and `api_pattern` on the provider; `id`, `match`, and `prices` on each model.
`price_comments` exists on both types — put it on the **model**, since that is
what a reviewer needs to trace and what the policy test reads (it falls back to
the provider's).

Getting any of this wrong is quiet: `_build_price_overrides` logs one warning
and drops the source, and the model then shows `$0` — exactly what it showed
before the entry was added. The `provider.id` must be the id genai-prices uses,
because that is what dcode's provider aliasing resolves to.

Beyond `input_mtok` / `output_mtok`, the schema carries `cache_read_mtok`,
`cache_write_mtok`, `output_reasoning_mtok`, `input_audio_mtok`, and tiered
variants. Only publish a bucket you actually have a rate for: tokens in an
omitted bucket stay in the ordinary input or output total rather than being
priced separately.

## User overrides

Users can add their own overrides for models neither catalog covers via
`prices.json` in the dcode user config directory (`~/.deepagents/prices.json`,
same provider-array schema). On conflicting `(provider id, model id)` entries,
the user file wins over this built-in one. See
[`PRICING.md`](https://github.com/langchain-ai/deepagents/blob/main/libs/code/PRICING.md)
for the user-facing documentation — linked by URL because this file ships inside
the wheel, where the repo tree is not there to walk.
1 change: 1 addition & 0 deletions libs/code/deepagents_code/bundled_prices.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Loading