Files
llmgw/internal/web/admin_nodeengine.go
T
Ilker ManapandClaude Opus 5.5 4f280a3546
Release / build (push) Successful in 1m12s
Release / notes (push) Successful in 3s
CI / test (push) Successful in 3m55s
Give each GPU node a vLLM build it can run, chosen on the cluster page
A node that cannot run the CUDA 13 build installs the same version's
CUDA 12.9 build from vLLM's GitHub release, with PyTorch's cu129 torch;
the install computes on the GPU before it counts. The build can be
chosen per node — auto, cu13, cu129, none — on the cluster page, with
ctl engine-build, or over the admin API; the control plane keeps the
choice and tells the node at once.

Refs #2.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 12:39:14 +02:00

50 lines
1.7 KiB
Go

package web
import (
"context"
"net/http"
"net/url"
"gitea.mynodes.xyz/ilker/llmgw/internal/store"
)
// NodeEngineAdmin chooses an engine's build for a node: "auto", a build
// such as "cu129", or "none".
type NodeEngineAdmin interface {
SetNodeEngine(ctx context.Context, node, engine, build, by string) error
}
// RegisterNodeEngines mounts the choice of engine builds on the cluster
// page: for an administrator, with the session's CSRF token, and answered by
// going back to the page with what happened.
func (p *AccountPages) RegisterNodeEngines(mux *http.ServeMux, a NodeEngineAdmin) {
mux.HandleFunc("POST /admin/nodes/{id}/engines", p.adminOnly(func(w http.ResponseWriter, r *http.Request, admin store.User) {
sess, _, _ := p.session(r)
if !p.accounts.CheckCSRF(sess, r.FormValue("csrf")) {
http.Error(w, "bad request", http.StatusBadRequest)
return
}
node, engine, build := r.PathValue("id"), r.FormValue("engine"), r.FormValue("build")
q := url.Values{}
if err := a.SetNodeEngine(r.Context(), node, engine, build, admin.Email); err != nil {
p.log.Warn("an engine build was not chosen", "admin", admin.Email, "node", node, "err", err)
q.Set("problem", node+": "+err.Error())
} else {
p.log.Info("an engine build was chosen", "admin", admin.Email, "node", node, "engine", engine, "build", build)
q.Set("notice", node+" installs "+engine+" "+buildWords(build)+
"; the node is told now, and installs it in the background")
}
http.Redirect(w, r, "/admin?"+q.Encode(), http.StatusSeeOther)
}))
}
func buildWords(b string) string {
switch b {
case "auto":
return "in the build its hardware runs"
case "none":
return "no longer"
}
return "built for " + map[string]string{"cu13": "CUDA 13", "cu129": "CUDA 12.9"}[b]
}