2026-06-22 OWUI Model Selector Cost Descriptions
What I set out to do
Started as a question — what image models are available in Open WebUI, then in OpenRouter — and turned into: “is there a plugin that shows cost in the model selector?” There wasn’t, so the real task became building one: surface input/output cost (and per-image cost for image models) into each model’s description in the OWUI selector.
What I actually did
Investigated the live system instead of guessing. Key findings that shaped the design:
- OWUI’s selector renders
model.info.meta.description, and that field is only populated from OWUI’s DB. Connection (LiteLLM) models get no description, which is why the selector showed only ids. - LiteLLM’s
/v1/model/info(port 4000) is a clean, authoritative cost source:input_cost_per_token/output_cost_per_token/modefor all 346 proxied models. The data was already there thanks tolitellm_openrouter_enrichment.py. - No image model exposes a flat generation $/image anywhere — all token-priced. OpenRouter’s
pricing.imageis the input image cost (it equals the prompt-token price), a red herring. - LiteLLM
modeis unreliable for detecting image models: onlygemini-3.1-flash-imagereportsimage_generationbecause it’s the one explicitly configured withmodel_info.mode; every wildcard model reportschat. The reliable signal is OpenRouter’sarchitecture.output_modalitiescontainingimage.
Designed and built (full brainstorm → spec → TDD):
owui_model_cost_desc.py— runtime library: pure formatters + injector + cached fetches (LiteLLM cost, OpenRouter blurb/modality), 300s TTL, fail-soft.owui_model_cost_desc_patch.py— boot patcher that appends an EOF wrapper aroundget_all_models(marker-guarded, drift-safe — no interior anchor).- 33 pytest cases, all written before implementation.
- Wired two read-only mounts + hashInput into
open-webui.nix, added the patcher topatch-and-start.sh.
Format: $3 in / $15 out per 1M tok — <blurb>; Gemini image models append (~$0.0039/img) from out_cost × 1290; trailing zeros trimmed; blurb collapsed/clipped to 160 chars; non-destructive (never overwrites an existing description).
Verified live: unit tests green, live smoke against real LiteLLM + OpenRouter produced correct descriptions, just check clean, hm switch recreated the container, patcher logged PATCHED, and inspect.getsource(get_all_models) confirmed the wrapper is bound in the running server. Committed as 7fee85f.
What was striking
- The per-image estimate is genuinely Gemini-only. The 1290 figure is Google’s documented per-image output-token charge (≤1024px), not a resolution — and OpenAI image models have no comparable documented count, so estimating them would be a guess on a guess.
- I labeled the estimate
~$0.004 / 1024² imageand got correctly called out:1024²reads as 1,048,576 (pixels), unrelated to the 1290 token count. Two different numbers conflated in one label. Good reminder to keep units honest. - The EOF-append wrapper is a much cleaner patch than splicing into a giant async function body — it only depends on a module-level
get_all_modelsexisting, which is stable across upstream drift.
Related
- 2026-06-22 OWUI Per-Message Cost Tracking — sibling feature (in-chat per-message cost via the global Filter); this one is selector-time, not message-time.
- 2026-06-21 Open WebUI Image Models On Demand