Learn per-model context from /props?model=; only status "loaded" is loaded
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
+16
-10
@@ -30,16 +30,17 @@ type Drainer interface {
|
||||
|
||||
// HostView is one host's row in the hosts view.
|
||||
type HostView struct {
|
||||
Healthy bool `json:"healthy"`
|
||||
Loaded []string `json:"loaded"` // never null
|
||||
LastOK string `json:"last_ok"` // RFC 3339 UTC or ""
|
||||
LastErr string `json:"last_err"`
|
||||
FreeSlots int `json:"free_slots"` // lim.FreeSlots(host)
|
||||
InFlight int `json:"in_flight"` // sum over the host's configured models
|
||||
Queued int `json:"queued"` // same
|
||||
Draining bool `json:"draining"`
|
||||
NCtx int `json:"n_ctx"` // from /props; 0 = unknown
|
||||
Slots int `json:"slots"` // from /props; 0 = unknown
|
||||
Healthy bool `json:"healthy"`
|
||||
Loaded []string `json:"loaded"` // never null
|
||||
LastOK string `json:"last_ok"` // RFC 3339 UTC or ""
|
||||
LastErr string `json:"last_err"`
|
||||
FreeSlots int `json:"free_slots"` // lim.FreeSlots(host)
|
||||
InFlight int `json:"in_flight"` // sum over the host's configured models
|
||||
Queued int `json:"queued"` // same
|
||||
Draining bool `json:"draining"`
|
||||
NCtx int `json:"n_ctx"` // from /props; 0 = unknown
|
||||
Slots int `json:"slots"` // from /props; 0 = unknown
|
||||
Models map[string]health.ModelCtx `json:"models"` // per loaded model; empty object, never null
|
||||
}
|
||||
|
||||
// LeaseView is one lease's row in a route's leases.
|
||||
@@ -114,6 +115,10 @@ func (hx *handler) hostView(name string, s health.Status) HostView {
|
||||
if loaded == nil {
|
||||
loaded = []string{}
|
||||
}
|
||||
models := s.Models
|
||||
if models == nil {
|
||||
models = map[string]health.ModelCtx{}
|
||||
}
|
||||
lastOK := ""
|
||||
if !s.LastOK.IsZero() {
|
||||
lastOK = s.LastOK.UTC().Format(time.RFC3339)
|
||||
@@ -129,6 +134,7 @@ func (hx *handler) hostView(name string, s health.Status) HostView {
|
||||
Draining: hx.d.Draining(name),
|
||||
NCtx: s.NCtx,
|
||||
Slots: s.Slots,
|
||||
Models: models,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package admin_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.wntrmute.dev/kyle/crossbar/internal/admin"
|
||||
"git.wntrmute.dev/kyle/crossbar/internal/health"
|
||||
)
|
||||
|
||||
// The hosts view shows the per-model context the poller learned, and an empty object (never
|
||||
// null) for a host with nothing learned.
|
||||
func TestHostsShowsPerModelContext(t *testing.T) {
|
||||
r := newRig(t)
|
||||
r.hosts.st["alpha"] = health.Status{
|
||||
Healthy: true,
|
||||
Loaded: []string{"m"},
|
||||
NCtx: 0, // a router: the host-level figure stays unknown
|
||||
Models: map[string]health.ModelCtx{"m": {NCtx: 65536, Slots: 2}},
|
||||
}
|
||||
rec := r.do(t, "GET", "/_crossbar/hosts", "")
|
||||
if rec.Code != 200 {
|
||||
t.Fatalf("%d %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
var out map[string]admin.HostView
|
||||
if err := json.Unmarshal(rec.Body.Bytes(), &out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := out["alpha"].Models["m"]; got != (health.ModelCtx{NCtx: 65536, Slots: 2}) {
|
||||
t.Errorf("alpha.models[m] = %+v, want {65536 2}", got)
|
||||
}
|
||||
if out["alpha"].NCtx != 0 {
|
||||
t.Errorf("alpha.n_ctx = %d, want 0 (unknown at host level on a router)", out["alpha"].NCtx)
|
||||
}
|
||||
var raw map[string]json.RawMessage
|
||||
if err := json.Unmarshal(rec.Body.Bytes(), &raw); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if beta := string(raw["beta"]); !strings.Contains(beta, `"models":{}`) {
|
||||
t.Errorf("beta = %s, want \"models\":{} (never null)", beta)
|
||||
}
|
||||
if alpha := string(raw["alpha"]); !strings.Contains(alpha, `"models":{"m":{"n_ctx":65536,"slots":2}}`) {
|
||||
t.Errorf("alpha = %s, want models keyed by id with n_ctx and slots", alpha)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user