Skip to content

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