Files
llmgw/internal/control/adminapi/canary.go
T
Ilker ManapandClaude Opus 5.5 d41104a452 Merge C21 quality suites and gating
Conflicts: docs.go (manual, OpenAPI and suites all kept), modelserve Job
(trace and eval both kept). The eval routes are added to the OpenAPI route
table, update and promote documented with their optional body, and the
document regenerated.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 00:08:33 +02:00

93 lines
2.6 KiB
Go

package adminapi
import (
"encoding/json"
"io"
"net/http"
"gitea.mynodes.xyz/ilker/llmgw/internal/control/catalog"
)
// CanaryPercentRequest changes the share of a model's requests its canary
// takes.
type CanaryPercentRequest struct {
Percent int `json:"percent"`
}
func (a *API) registerCanary(mux Mux) {
need := func(w http.ResponseWriter) bool {
if a.serve == nil {
writeError(w, http.StatusNotImplemented, "a canary needs a mirror and a store, and this control plane lacks one")
return false
}
return true
}
mux.HandleFunc("GET /api/v1/canaries", func(w http.ResponseWriter, r *http.Request) {
if !need(w) {
return
}
writeJSON(w, http.StatusOK, a.serve.Canaries())
})
// Start one: the newer weights fetched and run beside, then given
// their share.
mux.HandleFunc("POST /api/v1/models/{name}/canary", func(w http.ResponseWriter, r *http.Request) {
if !need(w) {
return
}
var cn catalog.Canary
if err := json.NewDecoder(io.LimitReader(r.Body, 1<<12)).Decode(&cn); err != nil {
writeError(w, http.StatusBadRequest, "the request is not valid JSON: "+err.Error())
return
}
job, err := a.serve.StartCanary(r.PathValue("name"), cn, actorOf(r))
if err != nil {
writeError(w, http.StatusBadRequest, err.Error())
return
}
writeJSON(w, http.StatusAccepted, job)
})
// Change its share.
mux.HandleFunc("PUT /api/v1/models/{name}/canary", func(w http.ResponseWriter, r *http.Request) {
if !need(w) {
return
}
var req CanaryPercentRequest
if err := json.NewDecoder(io.LimitReader(r.Body, 1<<12)).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "the request is not valid JSON: "+err.Error())
return
}
msg, err := a.serve.SetCanaryPercent(r.Context(), r.PathValue("name"), req.Percent, actorOf(r))
if err != nil {
writeError(w, http.StatusBadRequest, err.Error())
return
}
writeJSON(w, http.StatusOK, Result{Result: msg})
})
mux.HandleFunc("DELETE /api/v1/models/{name}/canary", func(w http.ResponseWriter, r *http.Request) {
if !need(w) {
return
}
msg, err := a.serve.AbortCanary(r.Context(), r.PathValue("name"), actorOf(r))
if err != nil {
writeError(w, http.StatusBadRequest, err.Error())
return
}
writeJSON(w, http.StatusOK, Result{Result: msg})
})
mux.HandleFunc("POST /api/v1/models/{name}/promote", func(w http.ResponseWriter, r *http.Request) {
if !need(w) {
return
}
ch, ok := readChange(w, r)
if !ok {
return
}
job, err := a.serve.StartPromote(r.PathValue("name"), actorOf(r), ch)
if err != nil {
writeError(w, http.StatusBadRequest, err.Error())
return
}
writeJSON(w, http.StatusAccepted, job)
})
}