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