From 98bda6e337c36ee916d315673dd325e0ac173af4 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 17 Apr 2026 23:22:51 -0500 Subject: [PATCH] OpenRouter: show all 343 models (free + paid) with pricing and filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Endpoint now returns all models, not just free ones - Each model includes: name, context_length, free flag, prompt/completion cost - UI shows pricing: "128K ctx · $2.50/M tok" for paid, "128K ctx · free" for free - Filter dropdown: All Models / Free Only / Paid Only - Search still works alongside the filter - 29 free + 314 paid models available (GPT-5.4, Grok 4.20, Gemini 3.1, etc) Co-Authored-By: Claude Opus 4.6 (1M context) --- llm_team_ui.py | 81 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 19 deletions(-) diff --git a/llm_team_ui.py b/llm_team_ui.py index 1726904..c8dbeec 100644 --- a/llm_team_ui.py +++ b/llm_team_ui.py @@ -5050,8 +5050,15 @@ ADMIN_HTML = r"""
-

Free Models on OpenRouter

- +

Models on OpenRouter

+
+ + +
Click "Fetch Models" to load the list.
@@ -5322,21 +5329,50 @@ async function fetchORModels() { function renderORModels() { const q = (document.getElementById('or-search').value || '').toLowerCase(); - const filtered = q ? orModels.filter(m => m.name.toLowerCase().includes(q) || m.id.toLowerCase().includes(q)) : orModels; + const tier = document.getElementById('or-filter').value; + let filtered = orModels; + if (q) filtered = filtered.filter(m => m.name.toLowerCase().includes(q) || m.id.toLowerCase().includes(q)); + if (tier === 'free') filtered = filtered.filter(m => m.free); + if (tier === 'paid') filtered = filtered.filter(m => !m.free); const el = document.getElementById('or-model-list'); - if (!filtered.length) { el.innerHTML = '
No models found.
'; return; } + if (!filtered.length) { el.textContent = 'No models found.'; return; } const existing = new Set((config.cloud_models||[]).map(m=>m.id)); - el.innerHTML = filtered.map(m => { + el.textContent = ''; + filtered.forEach(function(m) { const added = existing.has('openrouter::'+m.id); const ctx = m.context_length ? (m.context_length/1000).toFixed(0)+'K' : '?'; - return `
- ${m.name} - ${ctx} ctx - ${added - ? '' - : ``} -
`; - }).join(''); + const row = document.createElement('div'); + row.className = 'model-row'; + const nameEl = document.createElement('span'); + nameEl.className = 'name'; + nameEl.textContent = m.name; + const meta = document.createElement('span'); + meta.className = 'meta'; + if (m.free) { + meta.textContent = ctx + ' ctx · free'; + meta.style.color = 'var(--green)'; + } else { + const cost = (m.prompt_cost * 1e6).toFixed(2); + meta.textContent = ctx + ' ctx · $' + cost + '/M tok'; + } + row.appendChild(nameEl); + row.appendChild(meta); + if (added) { + const btn = document.createElement('button'); + btn.className = 'btn btn-sm'; + btn.disabled = true; + btn.style.opacity = '0.4'; + btn.textContent = 'Added'; + row.appendChild(btn); + } else { + const btn = document.createElement('button'); + btn.className = 'btn btn-sm btn-green'; + btn.textContent = 'Add'; + btn.onclick = function() { addOR(m.id, m.name); }; + row.appendChild(btn); + } + el.appendChild(row); + }); } function filterOR() { renderORModels(); } @@ -7205,15 +7241,22 @@ def admin_openrouter_models(): try: r = requests.get("https://openrouter.ai/api/v1/models", headers=headers, timeout=15) r.raise_for_status() - free = [] + models = [] for m in r.json().get("data", []): pricing = m.get("pricing", {}) - if pricing.get("prompt") == "0" and pricing.get("completion") == "0": - free.append({"id": m["id"], "name": m.get("name", m["id"]), - "context_length": m.get("context_length", 0)}) - _or_models_cache["data"] = free + prompt_cost = float(pricing.get("prompt", "0") or "0") + completion_cost = float(pricing.get("completion", "0") or "0") + is_free = prompt_cost == 0 and completion_cost == 0 + models.append({ + "id": m["id"], "name": m.get("name", m["id"]), + "context_length": m.get("context_length", 0), + "free": is_free, + "prompt_cost": prompt_cost, + "completion_cost": completion_cost, + }) + _or_models_cache["data"] = models _or_models_cache["ts"] = now - return jsonify({"models": free}) + return jsonify({"models": models}) except Exception as e: return jsonify({"models": [], "error": str(e)})