Skip to content

Releases: SmetDenis/openwebui-token-usage-display

v2.8.0

Choose a tag to compare

@github-actions github-actions released this 11 Oct 12:02
9895be7
  • Checked against Open WebUI 0.12.0: no change needed. Usage merging, the outlet body (apart from the new finish_reason below), the model listing and connection settings the context probe reads, status/citation persistence and the frontend render gates are as in 0.11.x. The plugin loads and runs in 0.12.0's rewritten function loader.
  • Truncation marker (✂️ truncated). Open WebUI 0.12.0 tells filters why the model stopped. When it stopped because it hit its output-token limit, the line now says so next to the output count: ⬇︎ 4,096 · ✂️ truncated · Σ ..., so a cut-off answer is not mistaken for a complete one. Details:
    • New admin valve show_finish_reason (on by default) and metric key finish, placed right after output in the default order so it survives the one-line clamp of the status line. If you saved a custom display_order, the new key is appended at the end, as with any unlisted metric - add finish where you want it. Icons: ✂️ (emoji) / ✂︎ (simple); with icon_style: off it reads truncated.
    • Shown only for the length stop reason. A normal stop, a turn that ended on a tool call (paused for approval, or stopped at the tool-call limit) and a provider content filter show nothing.
    • Needs Open WebUI 0.12.0+ and a Chat Completions (OpenAI-compatible) or Ollama model, or a pipe function that passes such a response through. Not shown for the Responses API: Open WebUI passes no stop reason for its streams and a made-up stop for its non-streaming replies (as it does for pipes returning plain text). Older Open WebUI versions pass none at all. On a tool turn it is the stop reason of the last request.
    • debug_mode: new top-level finish_reason field (null when Open WebUI sent none).

What's Changed

  • feat: Add truncation marker for OWUI 0.12.0 and release every merge by @SmetDenis in #29

Full Changelog: 2.7.0...v2.8.0

2.7.0

Choose a tag to compare

@SmetDenis SmetDenis released this 10 Oct 18:33
868c8ac

What's Changed

  • deps: Bump pydantic from 2.13.4 to 2.13.5 by @dependabot[bot] in #20
  • fix(context): use last round, not cross-round sum, for window used by @jtoland11 in #27
  • fix(context): Scope the last-round window fix and cut v2.7.0 by @SmetDenis in #28
  • ci: Bump astral-sh/setup-uv from 10.1.0 to 10.2.0 by @dependabot[bot] in #22
  • deps: bump ruff from 0.16.7 to 0.16.10 by @dependabot[bot] in #25
  • deps: bump mypy from 2.3.1 to 2.4.0 by @dependabot[bot] in #24

New Contributors

Full Changelog: v2.6.0...2.7.0

v2.6.0

Choose a tag to compare

@SmetDenis SmetDenis released this 15 Sep 07:26
6ed8798
  • Re-released on 2026-09-15. The first 2.6.0 and 2.6.1 builds (published 2026-09-14) were replaced by this single release, which also contains the context-window work below. If you installed either of them, paste this file again - the version number did not change.
  • Running chat token total (🧮). A new metric shows how many tokens the chat has consumed so far: the sum of every response's Σ on the current branch - the token twin of the 💰Σ cost total, and the only chat-wide number for models that report no cost (Ollama, llama.cpp, other local or free models). Because the model re-reads the whole history on every turn, it counts processed tokens and grows much faster than the context window - it is not the size of the chat (that is 📐). Details:
    • New admin valve show_cumulative_tokens (on by default) and metric key tokens_total, placed right after total in the default order. If you saved a custom display_order, the new key is appended at the end, as with any unlisted metric - add tokens_total where you want it. Icons: 🧮 (emoji) / ΣΣ (simple); compact_numbers applies to it.
    • Hidden when it equals the message Σ (the first turn), like 💰Σ. Marked ≈ when the current turn's Σ is a tiktoken estimate.
    • Past responses are summed from their stored usage with the same cache-aware total as Σ; responses without usage are skipped, not re-estimated.
    • Temporary chats (and API requests without a chat id): Open WebUI hands the filter their history without per-message usage, so both 🧮 and 💰Σ collapse to the current message there (and are hidden). Verified against Open WebUI v0.11.0 and v0.11.3.
    • debug_mode: new cumulative_tokens_debug block (total, estimated, messages_counted, messages_skipped_no_usage); the tokens block carries cumulative / cumulative_est.
  • Fixed: a non-finite token count (NaN / Infinity) in usage no longer breaks the stats line. Python's JSON parser accepts these values, and the plugin converted them with int() on a path that is not exception-guarded, so such a value in the current response aborted the line - and, with the new chat total walking the history, one bad stored response would have aborted it for every later response in the chat. Non-finite numbers are now treated as "not a number" at the single numeric intake (_num), so the affected field is simply omitted. The same class of value in the context_size_map valve (Infinity) is now skipped per entry instead of disabling the whole map.
  • Fixed: a local model showed its trained maximum instead of the server's real context window (#6: Qwen3.6 on llama-server --ctx-size 16384 showed 7.2k/131.1k (6%) instead of 7.2k/16.4k (44%); #14: a llama.cpp model started by llama-swap with --fit showed no real context size). The size came from the built-in table (qwen3 = 131072), and the opt-in llama.cpp / llama-swap probe was ranked below that table, so it never ran for any model family the table knows - setting llamacpp_url could not fix it either. Behind llama-swap three more things stood in the way: llama.cpp's --fit (on by default) picks the window at load time, so the --ctx-size the probe looked for is not in the launch command; llama-swap refuses a plain /props without ?model=; and the probe URL needed the API key typed into a valve. The context size is now resolved in this order: context_size_override -> num_ctx -> your context_size_map -> live connection window -> backend listing -> matched probe -> models.dev -> built-in table -> unmatched probe. Changes:
    • New automatic source: the running window asked through the model's own Open WebUI connection. For a model listed by llama-swap (owned_by: llama-swap) or a llama.cpp router (owned_by: llamacpp with a status block), the plugin reuses that connection's base URL, API key and prefix_id from Open WebUI - no valve, no key copied. Cloud connections are never contacted: they are not detected. Debug context_debug.source: connection_probe, matched_key names the backend and model id. A hit is cached for context_probe_ttl; a miss is not.
    • New automatic source, no configuration and no network: the size the serving backend lists for the model in its /v1/models row, which Open WebUI already passes to the plugin. llama.cpp lists the running window as meta.n_ctx (builds since May 2026), llama-swap does the same when capabilities.context is set in its config, and vLLM lists max_model_len. A live connection answer beats it, because llama-swap's capabilities.context is a config number that may not be the window --fit picked. Debug context_debug.source: backend, matched_key names the field. Workspace/"agent" models read it from their base model's listing entry. It is not cached, so a relisted model applies at once.
    • A probe never loads a model. A model-dispatched request makes llama-swap start the model if it is not running, evicting whatever is. llama-swap is therefore asked /running first and /props?model= only for a model in state ready; a llama.cpp router is asked with autoload=false, which refuses instead of loading. The API key is sent only as a bearer token, and only when the connection itself uses bearer auth; session / OAuth / Entra ID connections are probed without it. Not detected: a connection restricted to hand-typed model ids (Open WebUI keeps no listing row for those) - use the valves or context_size_map there.
    • The valve probe (llamacpp_url / llama_swap_url) now outranks models.dev and the built-in table when its row matches the called model (debug matched_key names the row). llama-swap can run several models at once, and the probe used to take the first /running row regardless of which model Open WebUI called; it now picks the matching row, asks /props?model= for it when it is ready, and falls back to the --ctx-size of its command (e.g. a vLLM upstream without /props). With several running models and no match it reports nothing and falls through to llamacpp_url, if set. llama.cpp is asked /v1/models first (id or alias -> meta.n_ctx), then /props for older builds (matched by the model alias or GGUF file name). A backend's only running model under a different id (an alias) is still used, but only after the tables, so a cloud model is never given a local server's window.
    • Strict probe id matching: a row matches when its id equals the called id or ends it after a connection prefix (prefix.id) or a path (org/id) - no substring match, so a local qwen never claims a cloud qwen3-max.
    • debug_mode: new model.backend_context block with the listing's n_ctx (running), n_ctx_train (trained) and max_model_len, and model.live_backend ([kind, listed id] or null) showing whether the model was recognized as a live-probe candidate.
    • outlet now also accepts __request__ (needed for the workspace-model and connection lookups); its dunder parameters became keyword-only, which is how Open WebUI passes them.
    • Verified against Open WebUI v0.11.3 (connection lookup routers/openai.py:get_openai_connection, 0.10.0+; app.state.config on 0.9.x), llama.cpp master (2026-09-15), llama-swap main (2026-09-13) and vLLM main.
  • Internal (no behavior change): stricter linting (ruff with every rule enabled), the stats-line renderers take a single _Stats object instead of six positional arguments, and the most branchy resolvers were split into helpers. Development tooling moved from requirements-dev.txt to uv with a committed uv.lock, so CI runs exactly the locally locked ruff/mypy/pytest versions (see CONTRIBUTING.md).
  • Idea credit for the chat token total: the fork ArtyCooL/openwebui-token-usage-display.

Full Changelog: v2.5.2...v2.6.0

v2.5.2

Choose a tag to compare

@SmetDenis SmetDenis released this 22 Jul 19:13
af11087

What's Changed

  • deps: update ruff requirement from >=0.5 to >=0.15.22 by @dependabot[bot] in #4
  • deps: update pytest requirement from >=8.0 to >=9.1.1 by @dependabot[bot] in #2
  • deps: update pytest-cov requirement from >=5.0 to >=7.1.0 by @dependabot[bot] in #3
  • deps: update mypy requirement from >=1.10 to >=2.3.0 by @dependabot[bot] in #1
  • chore(release): Refresh plugin metadata and cut v2.5.2 by @SmetDenis in #5

Full Changelog: v2.5.1...v2.5.2

v2.5.1

Choose a tag to compare

@SmetDenis SmetDenis released this 22 Jul 11:29
5e20fc3
  • context_size_map now takes precedence over the live models.dev fetch. It was previously merged into the built-in table, which is consulted after the models.dev lookup — so with fetch_context_from_modelsdev enabled, an automatic remote match silently overrode your explicit manual entry. It's now resolved in its own tier, right after override/num_ctx and before models.dev and the static table (source user_map in the debug payload), matching how price_map already beats models.dev for cost. Consequences: a map entry now wins outright on any substring match (even against a longer static/models.dev key — the same semantics as price_map); a falsy size (0) is skipped and falls through; parsing is per-entry tolerant (one bad value drops only that entry). It's returned uncached, so edits take effect immediately.

v2.5.0

Choose a tag to compare

@SmetDenis SmetDenis released this 22 Jul 11:29
b88533d
  • Workspace/custom models now infer context size and cost from their base model. Context-window and cost matching previously keyed on the top-level model id, which for a custom model / "agent" is an arbitrary label (e.g. research) that matches no context/price table — so context % and estimated/native cost silently went blank unless you mapped the agent id by hand. Both now resolve the model's base_model_id (the real underlying LLM — the same id the model-name metric shows and the one Open WebUI itself calls) and fall back to the top-level id only when there is no base. A single context_size_map / price_map entry keyed on the base model now covers every agent built on it; direct base models are unaffected (no base_model_id → their own id is used). The debug_mode payload's model.resolved_id and provider guess now reflect the id actually used for matching.

v2.4.0

Choose a tag to compare

@SmetDenis SmetDenis released this 22 Jul 11:29
ab8c410
  • debug_mode: provider cost_details. When the provider reports a cost breakdown next to the total (OpenRouter's usage.cost_details — upstream_inference_cost, cache_discount), it's now surfaced under the cost_debug block. The displayed cost still uses the authoritative top-level cost; this is diagnostics only (the provider-vs-upstream split and any cache discount).
  • debug_mode: web-search usage hint. A new web_search block flags whether the response carried server-side web-search citations (url_citation → OWUI sources), with a citation count and the domains. It's a heads-up that a provider-side search/tool surcharge may not be fully reflected in usage.cost (and BYOK engines bill outside the gateway entirely) — cross-check the Activity page. Detection only; the visible stats line is byte-identical.
  • Docs: added a "Does the cost include tool / function / MCP calls?" section (Open-WebUI-orchestrated tool turns are summed across rounds into the shown cost; only a provider's own server-side tool surcharge outside usage.cost is invisible), and strengthened the estimate-mode caveat — a token×price estimate can drift several-fold under a gateway's routing / tier / cache pricing, so auto (native, authoritative) is preferred.

v2.3.0

Choose a tag to compare

@SmetDenis SmetDenis released this 22 Jul 11:29
f3b2a0a
  • Configurable metric order. New admin display_order valve sets the order of the stats line via a comma-separated list of metric keys (input, output, total, reasoning, cached, audio, context, time, tps, cost, cost_total, model, source). The field is pre-filled with the full default order for discoverability — reorder or trim it (empty also means default, so it stays fully backward-compatible). Ordering only — the existing show_* toggles still decide visibility (removing a key does not hide it, it moves to the end), and enabled-but-unlisted metrics are appended in the default order. Unknown / misspelled keys are ignored and surfaced under a new display_order block in the debug_mode payload.
  • Configurable line appearance. New admin valves: separator (string between items, default ·); icon_style — emoji (default), simple (monochrome unicode, e.g. ↑ ↓ Σ, context severity ○ ◐ ●), or off (bare values, no icons); and compact_numbers (abbreviate the six token counters as k/M, matching the context-window style). All default to the previous behavior, so the line is byte-identical out of the box.
  • Cost display threshold. New cost_min_display valve hides message/cumulative cost below a USD threshold (default 0.0 shows everything, including $0.00).
  • Metric order is now admin-only — the earlier per-user override was dropped as unnecessary; UserValves.enabled (hide the whole line for yourself) is unchanged.

v2.2.0

Choose a tag to compare

@SmetDenis SmetDenis released this 22 Jul 11:29
ef163e6
  • Fixed Anthropic cache double-counting behind a proxy (LiteLLM, etc.). Some proxies fold Anthropic's cache tokens into prompt_tokens while still reporting the top-level cache_read_input_tokens / cache_creation_input_tokens; the plugin previously treated those as additional to input, inflating both the total token count and the estimated cost (roughly doubling the cached portion). Cache accounting is now unified around an uncached fresh_input: cache is detected as a subset of input (OpenAI / DeepSeek / Gemini / Anthropic-via-proxy) or on top of it (native Anthropic), so totals and estimated cost match regardless of how the same call is routed. Provider-reported (auto) cost was never affected.
  • Richer debug_mode payload for troubleshooting model/cost issues (still copyable via the "Token Usage & Cost Display - Debug info" source + server log; the visible stats line is unchanged). Four new blocks:
    • model — sanitized, safe-to-share identity of the selected model: id/name/base_model_id, owned_by, connection_type, provider, preset/is_pipe/has_url_idx, a best-effort provider_guess, the function_calling mode and other OWUI params. Uses a strict whitelist, so no prompt text, user/session ids, access grants, base URLs or API keys ever appear. Generation params (temperature/top_p/…) are marked unavailable on purpose — Open WebUI strips them before the filter runs.
    • cost_debug — why the cost came out the way it did: the cost_mode, whether a native provider cost was found (and from which key), the resolved price source (price_map/models.dev/static/none) with the exact matched key (null is the tell-tale for an id that matches no price entry), the applied per-1M rates, and a full per-component breakdown (billable input, cached, cache-write, output → USD each).
    • context_debug — how the context-window size was resolved (override/num_ctx/user_map/models.dev/static_table/probe) and which key matched, plus size/used/percent.
    • valves — a snapshot of the current admin configuration to reproduce issues from a single payload; the two optional local-backend URLs (llamacpp_url, llama_swap_url) are masked.

v2.1.0

Choose a tag to compare

@SmetDenis SmetDenis released this 22 Jul 11:29
ef163e6
  • Message & chat cost. New cost_mode valve: auto (default) shows the provider's own reported cost only (OpenRouter usage.cost, LiteLLM, ...) and never fetches or guesses; estimate additionally approximates cost from models.dev prices (always marked ≈), with correct cached/reasoning and Anthropic cache-read/write pricing; off disables cost entirely. Shows the per-message cost (💰) and a running chat total (💰Σ) in USD.
  • Optional live models.dev price fetch (api.json, exact per-provider prices, cached ~24h, on by default in estimate mode) with a built-in offline price table as fallback and a manual price_map override that always wins.
  • debug_mode is now copyable in-chat: the stats line just gets a small (debug) marker, and the full diagnostic payload is attached as a "Token Usage & Cost Display - Debug info" source whose modal renders it as a JSON block with a Copy button and persists with the message (still mirrored to the server log). The previous status line was clamped to one line and couldn't be selected.
  • Minimum lowered to Open WebUI 0.9.0 (was 0.10.0+): the data contracts the plugin relies on - per-message usage in the outlet (since 0.9.0) and normalized usage (since 0.8.0) - predate 0.10, so it runs on 0.9.x with graceful degradation; only provider-reported cost in auto mode needs 0.10.0+ (use estimate on older versions).
  • debug_mode now writes the full payload to the server/container logs (copyable, never truncated) and shows only a short summary in the chat status line - the previous status-only output was clamped to one line and couldn't be selected.