Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
85 lines
2.5 KiB
Go
85 lines
2.5 KiB
Go
package adminapi
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
|
|
"gitea.mynodes.xyz/ilker/llmgw/internal/control/catalog"
|
|
)
|
|
|
|
func (a *API) registerCanary(mux *http.ServeMux) {
|
|
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 struct {
|
|
Percent int `json:"percent"`
|
|
}
|
|
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, map[string]string{"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, map[string]string{"result": msg})
|
|
})
|
|
mux.HandleFunc("POST /api/v1/models/{name}/promote", func(w http.ResponseWriter, r *http.Request) {
|
|
if !need(w) {
|
|
return
|
|
}
|
|
job, err := a.serve.StartPromote(r.PathValue("name"), actorOf(r))
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusAccepted, job)
|
|
})
|
|
}
|