A model can be stopped and kept, or removed with its files from the mirror and the nodes
CI / test (push) Successful in 10m37s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ilker ManapandClaude Opus 5.5 committed 2026-10-29 20:45:48 +01:00
1 parent 5726ceb84e
commit 59187cdae5
19 files changed
+760 -14

No files matched your search

+42 -1
View File
@@ -60,6 +60,9 @@ type API struct {
controlPlanes func() ControlPlanes
// serve runs registry-to-replica jobs; nil without a mirror and a store.
serve *modelserve.Server
// purgeMirror is the mirror a model removed with its files is removed
// from; nil without one.
purgeMirror PurgeMirror
// history is the Prometheus the console's charts read from.
history History
// audit reads the audit log.
@@ -123,6 +126,8 @@ func (a *API) Register(mux Mux) {
mux.HandleFunc("POST /api/v1/models", a.addModel)
mux.HandleFunc("GET /api/v1/models/{name}", a.getModel)
mux.HandleFunc("DELETE /api/v1/models/{name}", a.removeModel)
mux.HandleFunc("POST /api/v1/models/{name}/stop", a.stopModel)
mux.HandleFunc("POST /api/v1/models/{name}/start", a.startModel)
mux.HandleFunc("POST /api/v1/models/{name}/scale", a.scaleModel)
mux.HandleFunc("POST /api/v1/models/{name}/resources", a.setResources)
a.registerQuotas(mux)
@@ -451,6 +456,8 @@ type ModelView struct {
// answer; 0 when the catalog does not say.
ContextLength int `json:"context_length,omitempty"`
MaxOutput int `json:"max_output,omitempty"`
// Stopped is a model an administrator took out of service, kept.
Stopped bool `json:"stopped,omitempty"`
// Canary is a share of its requests sent to a candidate; CanaryOf is
// set on the candidate, naming the model it is a canary of.
Canary *catalog.Canary `json:"canary,omitempty"`
@@ -843,7 +850,7 @@ func (a *API) Models() []ModelView {
h := health[m.Name]
v := ModelView{
Name: m.Name, Description: m.Description, Aliases: m.Aliases, Source: m.Source,
ContextLength: m.ContextLength, MaxOutput: m.MaxOutput,
ContextLength: m.ContextLength, MaxOutput: m.MaxOutput, Stopped: m.Stopped != nil,
Min: m.Scaling.Min, Max: m.Scaling.Max,
Ready: ready[m.Name], Total: total[m.Name],
Fallbacks: m.Fallbacks,
@@ -945,6 +952,8 @@ type Models interface {
Add(ctx context.Context, m catalog.Model, by string) error
Remove(ctx context.Context, name string) error
Scale(ctx context.Context, name string, s catalog.Scaling, by string) (catalog.Model, error)
Stop(ctx context.Context, name, by string) (catalog.Model, error)
Start(ctx context.Context, name, by string) (catalog.Model, error)
Resources(ctx context.Context, name, profile string, memoryGB, cpuCores int, by string) (catalog.Model, error)
SetClass(ctx context.Context, name, class, by string) (catalog.Model, error)
SetOwner(ctx context.Context, name, owner, by string) (catalog.Model, error)
@@ -1194,6 +1203,8 @@ type ScaleResponse struct {
Max int `json:"max"`
IdleTimeout time.Duration `json:"idle_timeout,omitempty"`
OnDemand bool `json:"on_demand"`
// Stopped is a model taken out of service, kept, until it is started.
Stopped bool `json:"stopped,omitempty"`
}
func (a *API) scaleModel(w http.ResponseWriter, r *http.Request) {
@@ -1227,6 +1238,32 @@ func (a *API) scaleModel(w http.ResponseWriter, r *http.Request) {
})
}
// stopModel takes a model out of service, kept.
func (a *API) stopModel(w http.ResponseWriter, r *http.Request) {
a.stopOrStart(w, r, a.models.Stop)
}
// startModel runs a stopped model again.
func (a *API) startModel(w http.ResponseWriter, r *http.Request) {
a.stopOrStart(w, r, a.models.Start)
}
func (a *API) stopOrStart(w http.ResponseWriter, r *http.Request, f func(context.Context, string, string) (catalog.Model, error)) {
if a.models == nil {
writeError(w, http.StatusNotImplemented, "this control plane has no store")
return
}
m, err := f(r.Context(), r.PathValue("name"), actorOf(r))
if err != nil {
writeError(w, http.StatusBadRequest, err.Error())
return
}
writeJSON(w, http.StatusOK, ScaleResponse{
Model: m.Name, Min: m.Scaling.Min, Max: m.Scaling.Max,
IdleTimeout: m.Scaling.IdleTimeout, OnDemand: m.Scaling.OnDemand(), Stopped: m.Stopped != nil,
})
}
// setResources sets a model's reserved memory and cores. Placed replicas keep
// what they were started with until they are restarted.
func (a *API) setResources(w http.ResponseWriter, r *http.Request) {
@@ -1253,6 +1290,10 @@ func (a *API) removeModel(w http.ResponseWriter, r *http.Request) {
return
}
name := r.PathValue("name")
if r.URL.Query().Get("purge") == "true" {
a.purgeModel(w, r, name)
return
}
if err := a.models.Remove(r.Context(), name); err != nil {
writeError(w, http.StatusBadRequest, err.Error())
return