ctl federation add/list/remove/sync keeps peers in the store (migration 0029); their /v1/models are offered here as external models named PEER/MODEL and kept in step, a model still named by a fallback kept withdrawn. Peers are checked at /ready, timed, and taken out of routing while down. Requests to a peer carry x-llmgw-hop and are refused (508) when they come back to a cluster they passed through; residency and no_store keep requests off peers; X-Request-Id and, with --send-tags, attribution tags go along. ctl model fallbacks sets overflow to a peer. Console view, metrics, admin API routes and docs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
81 lines
2.6 KiB
Go
81 lines
2.6 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"gitea.mynodes.xyz/ilker/llmgw/internal/control/federation"
|
|
)
|
|
|
|
// The console's federation view: the other llmgw clusters whose models are
|
|
// offered here, whether each is up and how far away, as `llmgw ctl
|
|
// federation list` shows them. Peers are added and removed with ctl, which
|
|
// needs a key file on this machine that a form here could not check.
|
|
|
|
func (m consoleModel) federationPanel(pw int) (string, string, string) {
|
|
keys := "→ or Enter open · ↑↓ choose · Q leave"
|
|
if m.inPanel {
|
|
keys = "↑↓ peer · Enter its models · ← back · Q leave"
|
|
}
|
|
v := m.data.federation
|
|
head := "\n " + sDim.Render("Other llmgw clusters whose models are offered here as PEER/MODEL.")
|
|
if v.Cluster != "" {
|
|
head += "\n " + sDim.Render("This cluster's id: "+v.Cluster)
|
|
}
|
|
head += "\n\n"
|
|
if len(v.Peers) == 0 {
|
|
return "Federation", head + " " + sDim.Render("No peers. Add one with: llmgw ctl federation add NAME --url URL --key-file FILE"), keys
|
|
}
|
|
var rows [][]string
|
|
for _, p := range v.Peers {
|
|
health := sOK.Render("up")
|
|
if !p.Up {
|
|
health = sBad.Render("down")
|
|
}
|
|
lat := "-"
|
|
if p.Latency > 0 {
|
|
lat = fmt.Sprintf("%.1fms", p.LatencyMs)
|
|
}
|
|
offered := 0
|
|
for _, md := range p.Models {
|
|
if !md.Withdrawn {
|
|
offered++
|
|
}
|
|
}
|
|
rows = append(rows, []string{p.Name, health, lat, fmt.Sprint(offered), p.URL})
|
|
}
|
|
return "Federation", head + table([]string{"PEER", "HEALTH", "LATENCY", "MODELS", "URL"}, rows, m.row, pw, m.inPanel), keys
|
|
}
|
|
|
|
// peerDialog is one peer whole: how it was last found, and its models.
|
|
func peerDialog(p federation.Status) (string, string) {
|
|
var b strings.Builder
|
|
fmt.Fprintf(&b, "%s\n", p.URL)
|
|
if p.Up {
|
|
fmt.Fprintf(&b, "%s, /ready in %.1fms", sOK.Render("up"), p.LatencyMs)
|
|
} else {
|
|
fmt.Fprintf(&b, "%s: %s", sBad.Render("down"), wrap(p.Error, 80, " "))
|
|
}
|
|
if !p.Checked.IsZero() {
|
|
fmt.Fprintf(&b, ", checked %s ago", time.Since(p.Checked).Round(time.Second))
|
|
}
|
|
tags := "not told who asks"
|
|
if p.SendTags {
|
|
tags = "told this cluster and the project of each request (x-llmgw-tags)"
|
|
}
|
|
fmt.Fprintf(&b, "\ncluster %s, key %s; %s\n\n", p.Cluster, p.KeyFile, tags)
|
|
if len(p.Models) == 0 {
|
|
b.WriteString(sDim.Render("offers this cluster's key no models") + "\n")
|
|
}
|
|
for _, md := range p.Models {
|
|
line := md.Name
|
|
if md.Withdrawn {
|
|
line += sDim.Render(" withdrawn by the peer; kept while a fallback here names it")
|
|
}
|
|
b.WriteString(" " + line + "\n")
|
|
}
|
|
fmt.Fprintf(&b, "\n%s", sDim.Render("llmgw ctl federation sync|remove "+p.Name+" · any key"))
|
|
return p.Name, b.String()
|
|
}
|