Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
115 lines
3.6 KiB
Go
115 lines
3.6 KiB
Go
package adminapi
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"time"
|
|
|
|
"gitea.mynodes.xyz/ilker/llmgw/internal/control/catalog"
|
|
)
|
|
|
|
// PresetsView is the models that come with llmgw, and whether the leader adds
|
|
// them by itself.
|
|
type PresetsView struct {
|
|
// Auto is whether a preset is added when a node fits it and the catalog
|
|
// has no model that makes what it makes (presets.auto).
|
|
Auto bool `json:"auto"`
|
|
Presets []PresetView `json:"presets"`
|
|
}
|
|
|
|
// PresetView is one preset as it stands in this cluster.
|
|
type PresetView struct {
|
|
Name string `json:"name"`
|
|
Makes string `json:"makes"`
|
|
Description string `json:"description"`
|
|
Card string `json:"card"`
|
|
Licence catalog.License `json:"licence"`
|
|
// Fits is whether some node has a card that meets the preset's need;
|
|
// FitsClasses names those classes.
|
|
Fits bool `json:"fits"`
|
|
FitsClasses []string `json:"fits_classes,omitempty"`
|
|
InCatalog bool `json:"in_catalog"`
|
|
// AddedBy is "auto" when the leader added it, who did otherwise, ""
|
|
// when it was not added as a preset; AddedAt is when.
|
|
AddedBy string `json:"added_by,omitempty"`
|
|
AddedAt time.Time `json:"added_at,omitzero"`
|
|
// Declined is whether somebody removed it after it was added, so it is
|
|
// not added again by itself; adding it again clears that.
|
|
Declined bool `json:"declined"`
|
|
}
|
|
|
|
// PresetsAutoRequest turns the automatic adding of presets on or off.
|
|
type PresetsAutoRequest struct {
|
|
On bool `json:"on"`
|
|
}
|
|
|
|
// errPresetsUnavailable is a control plane without the store presets are
|
|
// kept in.
|
|
var errPresetsUnavailable = errors.New("this control plane has no store, so presets cannot be added here")
|
|
|
|
// Presets is the presets as they stand in this cluster.
|
|
func (a *API) Presets(ctx context.Context) (PresetsView, error) {
|
|
return PresetsView{}, errPresetsUnavailable
|
|
}
|
|
|
|
// AddPreset adds the preset of that name, and clears its decline.
|
|
func (a *API) AddPreset(ctx context.Context, name, by string) error {
|
|
return errPresetsUnavailable
|
|
}
|
|
|
|
// SetAutoPresets turns presets.auto on or off.
|
|
func (a *API) SetAutoPresets(ctx context.Context, on bool, by string) error {
|
|
return errPresetsUnavailable
|
|
}
|
|
|
|
func (a *API) registerPresets(mux Mux) {
|
|
mux.HandleFunc("GET /api/v1/presets", func(w http.ResponseWriter, r *http.Request) {
|
|
v, err := a.Presets(r.Context())
|
|
if err != nil {
|
|
writeError(w, presetStatus(err), err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, v)
|
|
})
|
|
mux.HandleFunc("POST /api/v1/presets/{name}", func(w http.ResponseWriter, r *http.Request) {
|
|
if err := a.AddPreset(r.Context(), r.PathValue("name"), actorOf(r)); err != nil {
|
|
writeError(w, presetStatus(err), err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, ModelAdded{Added: r.PathValue("name")})
|
|
})
|
|
mux.HandleFunc("PUT /api/v1/presets/auto", func(w http.ResponseWriter, r *http.Request) {
|
|
var req PresetsAutoRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
if err := a.SetAutoPresets(r.Context(), req.On, actorOf(r)); err != nil {
|
|
writeError(w, presetStatus(err), err.Error())
|
|
return
|
|
}
|
|
v, err := a.Presets(r.Context())
|
|
if err != nil {
|
|
writeError(w, presetStatus(err), err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, v)
|
|
})
|
|
}
|
|
|
|
// errNoSuchPreset is a name no preset has.
|
|
var errNoSuchPreset = errors.New("no preset of that name")
|
|
|
|
func presetStatus(err error) int {
|
|
switch {
|
|
case errors.Is(err, errPresetsUnavailable):
|
|
return http.StatusNotImplemented
|
|
case errors.Is(err, errNoSuchPreset):
|
|
return http.StatusNotFound
|
|
default:
|
|
return http.StatusBadRequest
|
|
}
|
|
}
|