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) }) }