Show current LLM model next to each agent in the UI

- engine/agents.all_agents() now enriches each agent row with its
  currently-assigned model (model_for_agent) and provider
  (provider_for_model). Same source of truth as the LLM client.
- web/app.js refreshAgentCards() shows a coloured tag next to the
  agent name: a green '💻 <model>' badge for local Ollama models,
  a yellow '☁ <model>' badge for OpenRouter models. The full
  model name is in the title attribute (hover).
- web/style.css: .model-tag.local / .cloud colour palette.
- Short name helper strips 'org/' prefix and ':latest' suffix for
  readability (e.g. 'anthropic/claude-3.5-haiku' -> 'claude-3.5-haiku').

All 100 tests still pass.

Live: Anchor + Flora show '☁ claude-3.5-haiku' / '☁ gpt-4o-mini',
Lovely + Spark show '💻 gemma4' (running on 192.168.1.245).
This commit is contained in:
Jeuners 2026-06-15 03:15:53 +02:00
parent 23c914d743
commit 2b943aed18
3 changed files with 32 additions and 1 deletions

View file

@ -58,6 +58,17 @@ def bootstrap():
def all_agents():
"""Return live agents with their currently-assigned LLM model."""
from . import llm as llm_mod
agents = _all_agents_raw()
for a in agents:
a["model"] = llm_mod.model_for_agent(a["id"])
a["provider"] = llm_mod.provider_for_model(a["model"])
return agents
def _all_agents_raw():
import sqlite3
c = sqlite3.connect(db.DB_PATH, check_same_thread=False)
c.row_factory = sqlite3.Row
try:

View file

@ -80,6 +80,21 @@ function draw() {
}
}
function shortModel(name) {
if (!name) return '';
// Strip org/ prefix and :tag for compactness
const s = name.replace(/^.*\//, '').replace(/:latest$/, '');
return s;
}
function modelTag(m, provider) {
if (!m) return '';
const isCloud = provider === 'openrouter' || (m || '').includes('/');
const cls = isCloud ? 'cloud' : 'local';
const label = isCloud ? '☁ ' + shortModel(m) : '💻 ' + shortModel(m);
return `<span class="model-tag ${cls}" title="${m}">${label}</span>`;
}
function refreshAgentCards() {
const wrap = document.getElementById('agentList');
wrap.innerHTML = '';
@ -87,7 +102,8 @@ function refreshAgentCards() {
const div = document.createElement('div');
div.className = 'agent-card';
div.innerHTML = `
<h3>${a.name} <small>· ${a.role}</small></h3>
<h3>${a.name}${modelTag(a.model, a.provider)}</h3>
<div style="font-size:11px; color:#8aa1b6;">${a.role}</div>
<div>at (${a.x}, ${a.y}) · ${a.mood}</div>
<div class="bar energy"><i style="width:${a.energy}%"></i></div>
<div class="bar knowledge"><i style="width:${a.knowledge}%"></i></div>

View file

@ -53,6 +53,10 @@ aside h2 { font-size: 12px; text-transform: uppercase; letter-spacing: 1px;
#feed li .why { color: #6c8aa6; font-style: italic; }
.agent-card { background: #0a1018; border: 1px solid #1c2733; padding: 8px; margin: 6px 0; }
.agent-card h3 { margin: 0 0 4px; font-size: 13px; }
.agent-card .model-tag { display: inline-block; font-size: 9px; padding: 1px 5px; border-radius: 3px; margin-left: 4px; vertical-align: middle; font-weight: normal; }
.agent-card .model-tag.local { background: #1a2a1a; color: #6cf0c2; border: 1px solid #2a3a2a; }
.agent-card .model-tag.cloud { background: #2a2a1a; color: #ffd166; border: 1px solid #3a3a1a; }
.agent-card .model-tag.short { background: #1c2733; color: #8aa1b6; }
.agent-card .bar { height: 4px; background: #1c2733; margin: 2px 0; border-radius: 2px; }
.agent-card .bar > i { display: block; height: 100%; border-radius: 2px; }
.bar.energy > i { background: #ffd166; }