You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tooling.apache.org by gs...@apache.org on 2026/10/05 21:58:59 UTC
(tooling-llmao) 04/04: models page: admin per-model deployment drill-down and label controls
This is an automated email from the ASF dual-hosted git repository.
gstein pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-llmao.git
commit aff7e2adb3011d7e68469936a4690dfff112c15d
Author: Greg Stein <gs...@gmail.com>
AuthorDate: Mon Oct 5 16:55:40 2026 -0500
models page: admin per-model deployment drill-down and label controls
Site admins on /models now get an expandable Deployments section under the
catalog: one Bootstrap accordion per model (id from the row's post-sort
index), the per-replica table with the same fields as the Fleet tab, the
replica summary in the header, and a set/clear label form on each
self-hosted row posting to /do-set-admin-label.
The needs-attention strip's unknown-box items carry the same label form so a
box that presented the fleet key can be approved without leaving the page.
Label options are small named edicts so EZT reads them with plain attribute
access; the current label is pre-selected via [is] (lower-cased string
equality), which also handles the empty "no label" case.
Co-authored-by: Qwen3.8 via llm.apache.org, managed by Grok Build
---
pages.py | 47 ++++++++++++---
templates/models.ezt | 138 +++++++++++++++++++++++++++++++++++++++++++++
tests/test_pages_models.py | 43 +++++++++++++-
3 files changed, 219 insertions(+), 9 deletions(-)
diff --git a/pages.py b/pages.py
index b8e8e78..94d6e48 100644
--- a/pages.py
+++ b/pages.py
@@ -279,6 +279,29 @@ def _replica_summary(model_name: str, fleet, *, now: float) -> str:
return " / ".join(parts)
+_LABEL_OPTIONS = [
+ edict(value="", text="โ no label โ"),
+ edict(value="approved", text="Approved"),
+ edict(value="reboot_requested", text="Reboot requested"),
+]
+
+
+def _label_fields(fleet, host: str, *, now: float) -> dict:
+ """The label display fields for a strip row. Unknown boxes carry the
+ set/clear control; everything else labels nothing."""
+ if not host:
+ return {"host": "", "label": "", "label_value": "", "label_at": "", "label_options": []}
+ label_value, label_at = fleet.admin_label(host)
+ label_value = label_value or ""
+ return {
+ "host": host,
+ "label": label_value.replace("_", " "),
+ "label_value": label_value,
+ "label_at": _ago(label_at, now) if label_at else "",
+ "label_options": _LABEL_OPTIONS,
+ }
+
+
def attention_items(fleet, *, now: float) -> list:
"""The admin strip: only actionable items, in the order they were listed
in the design (unknown boxes, Stalled, Unhealthy, true skew)."""
@@ -293,6 +316,7 @@ def attention_items(fleet, *, now: float) -> list:
kind="unknown",
what=f"Fleet key presented from {host} (not in fleet.hosts)",
detail=f"seen {_ago(rec['last_seen'], now)} ยท {int(rec['count'])} requests",
+ **_label_fields(fleet, host, now=now),
)
)
cfg = fleet.cfg.fleet
@@ -304,10 +328,12 @@ def attention_items(fleet, *, now: float) -> list:
kind=view.lifecycle,
what=f"{dep.name} is {view.lifecycle.replace('_', ' ')}",
detail=dep.model_name,
+ **_label_fields(fleet, "", now=now),
)
)
for badge in dep.skew:
- items.append(edict(kind="skew", what=SKEW_PHRASE.get(badge, badge), detail=dep.name))
+ phrase = SKEW_PHRASE.get(badge, badge)
+ items.append(edict(kind="skew", what=phrase, detail=dep.name, **_label_fields(fleet, "", now=now)))
for extra in fleet.extra_litellm:
for badge in extra.skew:
items.append(
@@ -315,6 +341,7 @@ def attention_items(fleet, *, now: float) -> list:
kind="skew",
what=SKEW_PHRASE.get(badge, badge),
detail=f"{extra.host}:{extra.port} {extra.model_name}",
+ **_label_fields(fleet, "", now=now),
)
)
return items
@@ -360,6 +387,8 @@ def model_catalog_rows(fleet, catalog: list, *, now: float | None = None, admin:
row.replica_summary = _replica_summary(row.model_name, fleet, now=stamp)
rows.append(row)
rows.sort(key=lambda r: (bool(r.unavailable), (r.display_name or "").lower()))
+ for i, row in enumerate(rows):
+ row.index = i
return rows
@@ -411,7 +440,7 @@ def unknown_fetch_rows(fleet, *, now: float) -> list:
reverse=True,
)
for host, rec in unknown:
- label, label_at = fleet.admin_label(host)
+ label_value, label_at = fleet.admin_label(host)
rows.append(
edict(
host=host,
@@ -441,8 +470,10 @@ def unknown_fetch_rows(fleet, *, now: float) -> list:
row_class="table-warning",
show_add=ezt.boolean(False),
add_enabled=ezt.boolean(False),
- label=label.replace("_", " ") if label else "",
+ label=label_value.replace("_", " ") if label_value else "",
+ label_value=label_value or "",
label_at=_ago(label_at, now) if label_at else "",
+ label_options=_LABEL_OPTIONS,
)
)
return rows
@@ -456,12 +487,12 @@ def deployment_rows(fleet, *, admin: bool, now: float) -> list:
fetched = fleet.config_fetch_at.get(srv.host) if srv else None
# The admin label is a host-scoped annotation; unknown-fetch rows
# above already carry the host and are labeled separately.
- label, label_at = ("", "")
+ label_value, label_at = ("", "")
if admin and srv is not None:
- label, label_at = fleet.admin_label(srv.host)
+ label_value, label_at = fleet.admin_label(srv.host)
+ label_value = label_value or ""
label_at = _ago(label_at, now) if label_at else ""
- if label is not None:
- label = label.replace("_", " ")
+ label = label_value.replace("_", " ")
if srv is not None:
last_ok = srv.last_ok
no_deployment = srv.state == HEALTHY and not dep.in_litellm
@@ -551,7 +582,9 @@ def deployment_rows(fleet, *, admin: bool, now: float) -> list:
show_add=ezt.boolean(admin and dep.self_hosted and not dep.in_litellm),
add_enabled=ezt.boolean(admin and add_refusal(dep) is None),
label=label,
+ label_value=label_value,
label_at=label_at,
+ label_options=_LABEL_OPTIONS,
)
)
return rows
diff --git a/templates/models.ezt b/templates/models.ezt
index 26ab446..3e887e8 100644
--- a/templates/models.ezt
+++ b/templates/models.ezt
@@ -18,6 +18,22 @@
[if-any attention.detail]
<span class="text-muted">โ [attention.detail]</span>
[end]
+ [if-any attention.host]
+ <form method="post" action="/do-set-admin-label" class="d-inline ms-2">
+ <input type="hidden" name="host" value="[attention.host]">
+ <select name="label" class="form-select form-select-sm d-inline-block"
+ style="width: auto; display: inline-block;"
+ aria-label="Label for [attention.host]">
+ [for attention.label_options]
+ <option value="[attention.label_options.value]"[is attention.label_options.value attention.label_value] selected[end]>[attention.label_options.text]</option>
+ [end]
+ </select>
+ [if-any attention.label_at]
+ <span class="text-muted small ms-1">([attention.label_at])</span>
+ [end]
+ <button type="submit" class="btn btn-sm btn-outline-secondary ms-1">Set</button>
+ </form>
+ [end]
</li>
[end]
</ul>
@@ -105,6 +121,128 @@
[else]
<p class="text-muted">No models in the inventory.</p>
[end]
+
+ [if-any is_site_admin]
+ <h2 class="h5 mt-4">Deployments</h2>
+ <p class="text-muted small">
+ Replicas per model. A label notes a host (for example: approved before it
+ is in <code>fleet.hosts</code>).
+ </p>
+ [for models]
+ <div class="accordion accordion-flush mb-3" id="model[models.index]">
+ <div class="accordion-item">
+ <h3 class="accordion-header" id="modelHeading[models.index]">
+ <button class="accordion-button collapsed" type="button"
+ data-bs-toggle="collapse" data-bs-target="#modelDeploy[models.index]"
+ aria-expanded="false" aria-controls="modelDeploy[models.index]">
+ <strong>[models.display_name]</strong>
+ <span class="font-monospace text-muted small ms-1">[models.model_name]</span>
+ <span class="badge text-bg-secondary ms-2">[models.rollup_label]</span>
+ <span class="text-muted small ms-2">[models.replica_summary]</span>
+ </button>
+ </h3>
+ <div id="modelDeploy[models.index]" class="accordion-collapse collapse"
+ aria-labelledby="modelHeading[models.index]">
+ <div class="accordion-body p-2">
+ <div class="table-responsive">
+ <table class="table table-sm table-hover align-middle mb-0">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Listen</th>
+ <th>Public</th>
+ <th>State</th>
+ <th>Context</th>
+ <th>KV cache</th>
+ <th>Last ok</th>
+ <th>LiteLLM health</th>
+ <th>Config request</th>
+ <th>Skew</th>
+ <th>Label</th>
+ </tr>
+ </thead>
+ <tbody>
+ [for models.deployments]
+ <tr class="[models.deployments.row_class]">
+ <td>
+ [models.deployments.name]
+ [if-any models.deployments.unknown][else]
+ [if-any models.deployments.self_hosted][else]
+ <span class="badge text-bg-secondary ms-1">external</span>
+ [end]
+ [end]
+ </td>
+ <td class="font-monospace small">[models.deployments.listen]</td>
+ <td class="font-monospace small">[models.deployments.public]</td>
+ <td>
+ [if-any models.deployments.unknown]
+ [models.deployments.state]
+ [else]
+ [if-any models.deployments.serving]
+ <span class="badge text-bg-success">[models.deployments.state]</span>
+ [end]
+ [if-any models.deployments.no_deployment]
+ <span class="badge text-bg-warning text-dark">[models.deployments.state]</span>
+ [end]
+ [if-any models.deployments.loading]
+ <span class="badge text-bg-secondary">[models.deployments.state]</span>
+ [end]
+ [if-any models.deployments.awaiting]
+ <span class="badge text-bg-info text-dark">[models.deployments.state]</span>
+ [end]
+ [if-any models.deployments.unhealthy]
+ <span class="badge text-bg-danger">[models.deployments.state]</span>
+ [end]
+ [if-any models.deployments.pending]
+ <span class="badge text-bg-light text-dark">[models.deployments.state]</span>
+ [end]
+ [end]
+ [if-any models.deployments.detail]
+ <div class="text-muted small">[models.deployments.detail]</div>
+ [end]
+ </td>
+ <td class="small font-monospace">[models.deployments.context]</td>
+ <td class="small font-monospace">
+ [if-any models.deployments.oversized]
+ <span class="badge text-bg-danger" title="Context window exceeds the measured KV cache. vLLM will hang on a request that needs the space rather than refuse it.">[models.deployments.kv_cache]</span>
+ [else]
+ [models.deployments.kv_cache]
+ [end]
+ </td>
+ <td class="small">[models.deployments.last_ok]</td>
+ <td class="small">[models.deployments.litellm_health]
+ <span class="text-muted">[models.deployments.litellm_health_ago]</span>
+ </td>
+ <td class="small">[models.deployments.config_ago]</td>
+ <td class="small">[models.deployments.skew]</td>
+ <td class="text-nowrap">
+ [if-any models.deployments.host]
+ <form method="post" action="/do-set-admin-label" class="d-inline">
+ <input type="hidden" name="host" value="[models.deployments.host]">
+ <select name="label" class="form-select form-select-sm"
+ aria-label="Label for [models.deployments.host]">
+ [for models.deployments.label_options]
+ <option value="[models.deployments.label_options.value]"[is models.deployments.label_options.value models.deployments.label_value] selected[end]>[models.deployments.label_options.text]</option>
+ [end]
+ </select>
+ <button type="submit" class="btn btn-sm btn-outline-secondary mt-1">Set</button>
+ </form>
+ [if-any models.deployments.label_at]
+ <div class="text-muted small">[models.deployments.label_at]</div>
+ [end]
+ [end]
+ </td>
+ </tr>
+ [end]
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ [end]
+ [end]
</div>
<div class="modal fade" id="modelDetailModal" tabindex="-1"
diff --git a/tests/test_pages_models.py b/tests/test_pages_models.py
index 55ee85c..d0d7b9b 100644
--- a/tests/test_pages_models.py
+++ b/tests/test_pages_models.py
@@ -300,10 +300,49 @@ def test_apply_admin_label_sets_clears_and_rejects():
assert fleet.admin_label("10.0.0.1")[0] is None
-def _render(rows, attention=None) -> str:
+def test_admin_drill_down_renders_deployments_and_label_controls():
+ pages = _app()
+ healthy = _server("gemma")
+ down = _server("gemma", host="10.0.0.2", port=8002, ok=False)
+ fleet = _fleet(healthy, down)
+ # A vouch on the healthy box, so the label select pre-selects Approved.
+ fleet.set_admin_label("10.0.0.1", "approved", now=NOW - 20)
+ rows = pages.model_catalog_rows(fleet, [_catalog("gemma")], now=NOW, admin=True)
+ out = _render(rows, admin=True)
+
+ # The per-model deployment table is present, with one row per replica.
+ assert "Replicas per model" in out
+ assert 'data-bs-target="#modelDeploy0"' in out
+ assert out.count('<tr class="') == 2
+
+ # The label select carries every option and pre-selects the current one.
+ assert 'value="approved" selected' in out
+ assert 'value="reboot_requested"' in out
+ assert 'value=""' in out
+ assert 'name="label"' in out
+ assert 'name="host" value="10.0.0.1"' in out
+ assert "/do-set-admin-label" in out
+ # The replica summary lands in the accordion header.
+ assert "1 healthy / 1 unhealthy" in out
+
+
+def test_attention_strip_renders_the_label_form_for_unknown_boxes():
+ pages = _app()
+ fleet = _fleet(_server("gemma"))
+ fleet.unknown_config_fetches["192.0.2.7"] = {"first_seen": NOW - 100, "last_seen": NOW - 5, "count": 3}
+ fleet.set_admin_label("192.0.2.7", "approved", now=NOW - 5)
+ rows = pages.model_catalog_rows(fleet, [_catalog("gemma")], now=NOW, admin=True)
+ out = _render(rows, attention=pages.attention_items(fleet, now=NOW), admin=True)
+ # The unknown-box item carries the set/clear form, pre-selected on its vouch.
+ assert 'name="host" value="192.0.2.7"' in out
+ assert 'value="approved" selected' in out
+ assert "/do-set-admin-label" in out
+
+
+def _render(rows, attention=None, *, admin=False) -> str:
data = edict(
title="Models",
- is_site_admin=ezt.boolean(False),
+ is_site_admin=ezt.boolean(admin),
reveal_supply=False,
uid="u@example.apache.org",
name="N",
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org