- Track billable tool usage (search, code interpreter, file search, etc.) in
Response.usage. - Calculate all-in cost: tokens + tools + images (+ storage/session if applicable).
- Add native LLMDB metadata for tool pricing, with provider-level defaults and model overrides.
- Keep backward compatibility for existing
costtoken pricing.
- LLMDB: add
pricing_defaultsto provider schema; addpricingto model schema. - ReqLLM usage: add
tool_usage,image_usage, andcostbreakdown; maketotal_costall-in. - ReqLLM cost: new component-based calculator; token-only fallback preserved.
- Providers: extract tool usage counts and image usage where needed.
Add an optional pricing_defaults field to LLMDB.Provider:
pricing_defaults :: %{
currency: String.t(),
components: [pricing_component()]
}Add an optional pricing field to LLMDB.Model:
pricing :: %{
currency: String.t() | nil,
components: [pricing_component()],
merge: :replace | :merge_by_id
}merge default: :merge_by_id.
Each component defines a billable unit. Recommended fields:
pricing_component :: %{
id: String.t(), # Unique within provider/model, e.g. "token.input"
kind: :token | :tool | :image | :storage | :request | :other,
unit: :token | :call | :query | :session | :gb_day | :image | :source | :other,
per: pos_integer(), # e.g. 1_000_000 for tokens, 1_000 for calls
rate: number(), # currency per `per` units
meter: String.t() | nil, # Usage key mapping, e.g. "input_tokens"
tool: atom() | nil, # e.g. :web_search, :file_search, :code_interpreter
size_class: String.t() | nil, # e.g. "1k-2k", "4k" for Gemini image pricing
notes: String.t() | nil
}- Start with provider
pricing_defaults.components. - If model
pricing.merge == :replace: use model components only. - If
:merge_by_id: override provider components by matchingid, and append new ones. currency: model value wins; otherwise provider value.
- OpenAI/Anthropic/xAI tool pricing is consistent across most models.
- Gemini search billing differs by model family → override via model
pricing.
Add structured usage fields to ReqLLM.Response.usage:
%{
input_tokens: integer(),
output_tokens: integer(),
total_tokens: integer(),
reasoning_tokens: integer(),
cached_tokens: integer(),
tool_usage: %{
web_search: %{count: integer(), unit: :call | :query | :source},
google_search: %{count: integer(), unit: :query},
file_search: %{count: integer(), unit: :call},
code_interpreter: %{count: integer(), unit: :session}
},
image_usage: %{
generated: %{count: integer(), size_class: String.t() | nil}
},
cost: %{
tokens: number(),
tools: number(),
images: number(),
storage: number() | nil,
total: number(),
line_items: [%{id: String.t(), cost: number(), count: number()}]
},
total_cost: number() # all-in (tokens + tools + images + storage)
}Notes:
total_costbecomes all-in per user decision.cost.tokens + cost.tools + cost.images (+ cost.storage) == total_cost.
Create a component-based calculator (e.g., ReqLLM.Billing.calculate/2):
Inputs:
usage(normalized usage map)pricing(merged provider defaults + model overrides)
Algorithm:
- Normalize usage counts into a flat map keyed by
meter+ tool/image usage. - For each component:
- Resolve usage count based on
kind/meter/tool/size_class. component_cost = (count / per) * rate.
- Resolve usage count based on
- Sum costs by
kindgroup (tokens/tools/images/storage). - Return
costmap +total_cost.
- If
pricingis nil, fall back to existingReqLLM.Cost.calculate/2(token-only). - If model has
costbut nopricing, auto-derive token components fromcost:token.input,token.output,token.cache_read,token.cache_write,token.reasoning.
- Provider
extract_usage/2returns a usage map includingtool_usageandimage_usagewhere available. ReqLLM.Step.Usagemerges into response usage and feeds the billing calculator.
Google Gemini
- Search grounding: if any
groundingMetadata.webSearchQueriespresent, then- Gemini 2.5 and older:
tool_usage.google_search.count = 1(per prompt) - Gemini 3:
tool_usage.google_search.count = length(queries)(per query)
- Gemini 2.5 and older:
- Gemini image models: count generated images, derive
size_class(1k–2k or 4k) from output dimensions or request params.
Anthropic
- Use
usage.server_tool_use.web_search_requestsastool_usage.web_search.count.
OpenAI
- Use Responses API tool items to count built-in tools (web search, file search, code interpreter).
- For code interpreter billing, count sessions (one response may map to 1 session).
xAI
- Use
response.server_side_tool_usagefor per-tool call counts. usage.num_sources_usedmaps totool_usage.web_searchwith unit:source.
Update ReqLLM.Step.Usage and Provider.Defaults.ResponseBuilder.normalize_usage_fields/1:
- Preserve existing token normalization.
- Pass through
tool_usageandimage_usagewhen present. - Add computed
costand all-intotal_cost.
Ensure metadata collected by stream handlers includes tool usage where available, so StreamResponse.usage/1 yields the same structure.
- During
LLMDB.Engine.finalize/1, apply providerpricing_defaultsto each model. - Merge by
idunless modelpricing.merge == :replace.
- For
config/test.exscustom providers/models, apply the same merge at load time.
- Component calculator: token-only, tool-only, mixed components, merge behavior.
- Usage normalization retains
tool_usageandimage_usage.
- Stub usage responses for Google/Anthropic/xAI/OpenAI and validate
tool_usagecounts.
- Ensure
usage.total_cost== sum of breakdown categories.
usage.total_costbecomes all-in; for clients expecting token-only, addusage.cost.tokensorusage.cost.line_items.- Existing
costmodel field remains valid; it becomes token pricing fallback.
"pricing_defaults": {
"currency": "USD",
"components": [
{"id": "token.input", "kind": "token", "unit": "token", "per": 1000000, "rate": 3.0, "meter": "input_tokens"},
{"id": "token.output", "kind": "token", "unit": "token", "per": 1000000, "rate": 15.0, "meter": "output_tokens"},
{"id": "tool.web_search", "kind": "tool", "unit": "call", "per": 1000, "rate": 10.0, "tool": "web_search"}
]
}"pricing": {
"merge": "merge_by_id",
"components": [
{"id": "tool.google_search", "kind": "tool", "unit": "query", "per": 1000, "rate": 14.0, "tool": "google_search"}
]
}- Add
pricing_defaultsandpricingto LLMDB schemas + validation. - Implement provider-default merge in
LLMDB.Engine.finalize/1. - Add component-based calculator in ReqLLM and wire into
Step.Usage. - Extend usage normalization to include
tool_usage+image_usage+cost. - Update provider extractors (Google/Anthropic/xAI/OpenAI) to emit
tool_usage. - Add tests for calculator + provider extraction.