Files
llmgw/internal/control/reconciler/split.go
T
Ilker ManapandClaude Opus 5.5 e23732fbbb Replicas counted per profile: seven on cards and four on CPUs under one name
A profile's replicas, set on any of a model's profiles, is how many of its
replicas run there; the scaling becomes the sum. The reconciler starts the
ones short of their profile before stopping the ones over, so a change of
split never runs fewer. ctl model replicas sets them, adding a profile on
CPUs to a GGUF model; the router already fills the higher-priority pool
first, which the CPU profile is put below.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 09:21:50 +01:00

65 lines
1.6 KiB
Go

package reconciler
import (
"slices"
"gitea.mynodes.xyz/ilker/llmgw/internal/control/catalog"
)
// splitGaps is, for a model whose replicas are counted per profile, the
// profiles short of theirs and the replicas over: of a profile with more
// than it asks, or of one it no longer has. Counted as reconcileModel
// counts — a replica being retired is not one it has — so a replica
// restarted is replaced on its own profile.
func (r *Reconciler) splitGaps(m catalog.Model) (deficit map[string]bool, surplus []string) {
live := map[string][]string{}
r.mu.Lock()
for _, rep := range r.replicas {
if rep.Model == m.Name && rep.State.Counts() && rep.RetiringAt.IsZero() {
b := catalog.ProfileBase(rep.Profile)
live[b] = append(live[b], rep.ID)
}
}
r.mu.Unlock()
deficit = map[string]bool{}
for _, p := range m.Profiles {
ids := live[p.Name]
delete(live, p.Name)
slices.Sort(ids)
switch {
case len(ids) < p.Replicas:
deficit[p.Name] = true
case len(ids) > p.Replicas:
surplus = append(surplus, ids[p.Replicas:]...)
}
}
for _, ids := range live {
surplus = append(surplus, ids...)
}
slices.Sort(surplus)
return deficit, surplus
}
// pickSurplus is n of the live replicas to stop: those over their
// profile's count first, then any.
func pickSurplus(surplus, live []string, n int) []string {
out := make([]string, 0, n)
for _, id := range surplus {
if len(out) == n {
return out
}
if slices.Contains(live, id) {
out = append(out, id)
}
}
for _, id := range live {
if len(out) == n {
break
}
if !slices.Contains(out, id) {
out = append(out, id)
}
}
return out
}