Strata's DeepSeek not offered on cards its engine was not built for, and refused there by the agent
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
104b60fbf8
commit
f41c96d30f
13 files changed
+90
-6
No files matched your search
@@ -1896,6 +1896,8 @@ served only from a definition written by hand. In order:
|
||||
**C. Operating it.**
|
||||
- N9. `ctl engine-check strata`: the flags of setup.py and server.py, which drift between versions.
|
||||
- N10. `ctl engine-build NODE strata cuda12|cuda13`, as vLLM has.
|
||||
- N11a. Strata's per-replica state directories (engine-home/strata/<replica>) are left behind when a replica stops;
|
||||
remove them with it.
|
||||
- N11. Disk for the prepared pack and draft layer (and DeepSeek V4.1's 240–280 GB), cleaned with the weights; a
|
||||
first start's preparation within the startup timeout.
|
||||
- N12. docs/install.md: Strata in the engines table, a section with a profile, its memory and first-run time;
|
||||
|
||||
@@ -40,6 +40,9 @@ APIs change between minors without a deprecation period.
|
||||
|
||||
- **A repository that keeps each quantisation in a directory of its own** (ISTA-DASLab's `IQ2_XS/`, `IQ3_S/`) listed
|
||||
no weights: its file tree is now read recursively, by the explorer, the plan and `ctl model serve`.
|
||||
- **DeepSeek V4.1 offered on a card its engine was not built for.** Strata's DeepSeek engine runs on RTX 30 (compute
|
||||
8.6) and newer; on a Turing card the tab and the plan offered it, and Strata's setup refused it once placed. Now
|
||||
neither offers it there, the tab says why, and the agent refuses such a profile before starting it.
|
||||
|
||||
## v0.16.0 — 2026-10-30
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
|
||||
"gitea.mynodes.xyz/ilker/llmgw/internal/strata"
|
||||
)
|
||||
|
||||
// StrataConfig locates a Strata installation.
|
||||
@@ -133,6 +135,13 @@ func (*Strata) Validate(p Profile, gpus []GPUInfo) error {
|
||||
if err := requireComputeCapability(p, gpus, 6.0, "strata"); err != nil {
|
||||
return err
|
||||
}
|
||||
// A family whose engine needs more: said now, rather than by Strata's
|
||||
// setup after the replica was placed here.
|
||||
if fam, ok := strata.FamilyNamed(strata.ArgValue(p.Args, "--family")); ok && fam.MinCC > 0 {
|
||||
if err := requireComputeCapability(p, gpus, fam.MinCC, "strata's "+fam.Title+" engine"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return validateCommon(p, gpus)
|
||||
}
|
||||
|
||||
|
||||
@@ -98,3 +98,16 @@ func TestAStrataNamedByHandIsReportedWithItsVersion(t *testing.T) {
|
||||
t.Error("reported without its engine")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrataRefusesDeepSeekOnACardItsEngineWasNotBuiltFor(t *testing.T) {
|
||||
s := &Strata{}
|
||||
p := Profile{Name: "p", Engine: "strata", ModelPath: "/m", TP: 1, GPUsPerNode: 1, Args: []string{"--family", "deepseek41", "--size", "IST-DAS-1.75bpw"}}
|
||||
gpus := []GPUInfo{{Index: 0, Name: "Quadro RTX 6000", ComputeCapability: "7.5"}}
|
||||
if err := s.Validate(p, gpus); err == nil || !strings.Contains(err.Error(), "8.6") {
|
||||
t.Errorf("DeepSeek on 7.5: %v", err)
|
||||
}
|
||||
p.Args = []string{"--family", "qwen", "--size", "IQ2_XS"}
|
||||
if err := s.Validate(p, gpus); err != nil {
|
||||
t.Errorf("Qwen on 7.5: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -1954,6 +1954,11 @@ func PoolNodesOf(nodes []NodeView) []modelplan.PoolNode {
|
||||
if len(n.Fabrics) > 0 {
|
||||
p.Fabric = n.Fabrics[0]
|
||||
}
|
||||
for _, c := range n.ComputeCapabilities {
|
||||
if v, err := strconv.ParseFloat(c, 64); err == nil && v > p.CC {
|
||||
p.CC = v
|
||||
}
|
||||
}
|
||||
if l := n.Load; l != nil && l.MemTotal > 0 {
|
||||
p.HostFreeGB = int((l.MemTotal - l.MemUsed) >> 30)
|
||||
}
|
||||
|
||||
@@ -55,6 +55,8 @@ type PoolNode struct {
|
||||
Kinds []PoolKind
|
||||
// Engines are the engines the node has installed.
|
||||
Engines []string
|
||||
// CC is its newest card's compute capability, 0 when not known.
|
||||
CC float64
|
||||
}
|
||||
|
||||
// PoolKind is one class of a node's devices.
|
||||
|
||||
@@ -24,12 +24,18 @@ func StrataOptions(family string, arts []Artifact, nodes []PoolNode) []Option {
|
||||
continue
|
||||
}
|
||||
need := float64(size.MemoryGB())
|
||||
fam, _ := strata.FamilyNamed(family)
|
||||
byClass := map[string]*Option{}
|
||||
var order []string
|
||||
for _, n := range nodes {
|
||||
if !slices.Contains(n.Engines, "strata") || n.HostMemGB <= 0 {
|
||||
continue
|
||||
}
|
||||
// A family whose engine needs a newer card: refused by Strata on
|
||||
// this one, after the node was chosen.
|
||||
if fam.MinCC > 0 && n.CC > 0 && n.CC < fam.MinCC {
|
||||
continue
|
||||
}
|
||||
have := float64(n.HostMemGB) * hostShare
|
||||
if need > have {
|
||||
continue
|
||||
|
||||
@@ -30,3 +30,22 @@ func TestStrataIsOfferedOnANodeWithItTheCardAndTheRAM(t *testing.T) {
|
||||
t.Errorf("%q %q", o.Label(), o.MemoryText())
|
||||
}
|
||||
}
|
||||
|
||||
// DeepSeek's engine is built for RTX 30 and newer: not offered on a Turing
|
||||
// card, which Strata's setup refuses after the replica is placed.
|
||||
func TestDeepSeekOnStrataNeedsANewerCard(t *testing.T) {
|
||||
arts := []Artifact{{Name: "IST-DAS-1.75bpw.gguf", Format: "gguf"}}
|
||||
node := func(cc float64) []PoolNode {
|
||||
return []PoolNode{{ID: "n", Class: "6000-24g", GPUMemGB: 24, GPUs: 1, FreeGPUs: 1, HostMemGB: 251,
|
||||
Engines: []string{"strata"}, CC: cc}}
|
||||
}
|
||||
if got := StrataOptions("deepseek41", arts, node(7.5)); len(got) != 0 {
|
||||
t.Errorf("offered on 7.5: %+v", got)
|
||||
}
|
||||
if got := StrataOptions("deepseek41", arts, node(8.9)); len(got) != 1 {
|
||||
t.Errorf("not offered on 8.9: %+v", got)
|
||||
}
|
||||
if got := StrataOptions("qwen", []Artifact{{Name: "IQ2_XS.gguf"}}, node(7.5)); len(got) != 1 {
|
||||
t.Errorf("Qwen not offered on 7.5: %+v", got)
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,10 @@ type Family struct {
|
||||
// Engine is a family that runs on an engine path of its own, with a
|
||||
// download of its own (DeepSeek V4.1).
|
||||
Engine bool
|
||||
// MinCC is the compute capability its engine needs of an NVIDIA card,
|
||||
// above Strata's own 6.0; 0 for none. DeepSeek's engine is built for
|
||||
// RTX 30 (8.6) and newer only.
|
||||
MinCC float64
|
||||
}
|
||||
|
||||
// Size is one of a family's sizes: setup's --model, which llmgw's profile
|
||||
@@ -56,7 +60,7 @@ var Families = []Family{
|
||||
{Name: "gyro", Model: "qwen3.8-flash-next-gyro", Title: "Qwen3.8-Flash-Next (Gyro)", Repo: "agentionai/Qwen3.8-Flash-Next-Gyro-GGUF"},
|
||||
{Name: "nvfp4", Model: "qwen3.8-flash-next-nvfp4", Title: "Qwen3.8-Flash-Next (NVFP4)"},
|
||||
{Name: "qwen36", Model: "qwen3.6-35b-a3b", Title: "Qwen3.6-35B-A3B", Repo: "unsloth/Qwen3.6-35B-A3B-MTP-GGUF"},
|
||||
{Name: "deepseek41", Model: "deepseek-v4.1-flash", Title: "DeepSeek V4.1-Flash", Repo: "ISTA-DASLab/DeepSeek-V4.1-Flash-1017-GSQ-RCO-GGUF", Engine: true},
|
||||
{Name: "deepseek41", Model: "deepseek-v4.1-flash", Title: "DeepSeek V4.1-Flash", Repo: "ISTA-DASLab/DeepSeek-V4.1-Flash-1017-GSQ-RCO-GGUF", Engine: true, MinCC: 8.6},
|
||||
}
|
||||
|
||||
// Sizes are the sizes Strata 0.2.7 runs, and which families have each.
|
||||
|
||||
@@ -65,6 +65,8 @@ func (p *AccountPages) strataView(l *Lang) *strataView {
|
||||
sr.Fits, sr.Runs = true, l.T("explore.strata", opts[0].Class, l.Num(opts[0].NeedGB, 0))
|
||||
case len(v.Nodes) == 0:
|
||||
sr.Runs = l.T("strata.no_node")
|
||||
case f.MinCC > 0 && cardsOlderThan(pool, f.MinCC):
|
||||
sr.Runs = l.T("strata.old_card", l.Num(f.MinCC, 1))
|
||||
default:
|
||||
sr.Runs = l.T("strata.no_room", s.MemoryGB())
|
||||
}
|
||||
@@ -75,6 +77,22 @@ func (p *AccountPages) strataView(l *Lang) *strataView {
|
||||
return v
|
||||
}
|
||||
|
||||
// cardsOlderThan says whether every node with Strata has a card of a
|
||||
// compute capability below cc, as far as they say.
|
||||
func cardsOlderThan(pool []modelplan.PoolNode, cc float64) bool {
|
||||
older := false
|
||||
for _, n := range pool {
|
||||
if !slices.Contains(n.Engines, "strata") {
|
||||
continue
|
||||
}
|
||||
if n.CC == 0 || n.CC >= cc {
|
||||
return false
|
||||
}
|
||||
older = true
|
||||
}
|
||||
return older
|
||||
}
|
||||
|
||||
// serveStrata adds a model of a family Strata fetches itself: its source
|
||||
// the repository, its replica on a card of the class the plan found.
|
||||
func (p *AccountPages) serveStrata(ctx context.Context, l *Lang, by, family, size string) (string, error) {
|
||||
|
||||
@@ -15,7 +15,7 @@ type strataFleet struct{ fakeFleet }
|
||||
|
||||
func (strataFleet) Nodes() []adminapi.NodeView {
|
||||
return []adminapi.NodeView{{ID: "cuda01", State: "ready", GPUTotal: 1, GPUFree: 1, MemoryGB: 251,
|
||||
Classes: []string{"6000-24g"}, Engines: []string{"llamacpp", "strata"}}}
|
||||
Classes: []string{"6000-24g"}, Engines: []string{"llamacpp", "strata"}, ComputeCapabilities: []string{"7.5"}}}
|
||||
}
|
||||
|
||||
func TestTheStrataTabListsWhatRunsHereAndFetchesASize(t *testing.T) {
|
||||
@@ -31,13 +31,14 @@ func TestTheStrataTabListsWhatRunsHereAndFetchesASize(t *testing.T) {
|
||||
|
||||
body := mirrorGet(t, mux, "/admin/mirror?tab=strata").Body.String()
|
||||
for _, want := range []string{"Strata is installed on: cuda01", "Qwen3.8-Flash-Next", "IQ2_XS",
|
||||
"1× 6000-24g with Strata, 52 GB of RAM", `name="size" value="IQ2_XS"`, "Strata fetches these onto the node itself"} {
|
||||
"1× 6000-24g with Strata, 52 GB of RAM", `name="size" value="IQ2_XS"`, "Strata fetches these onto the node itself",
|
||||
"compute capability 8.6 or newer"} {
|
||||
if !strings.Contains(body, want) {
|
||||
t.Errorf("the tab lacks %q", want)
|
||||
}
|
||||
}
|
||||
// Unsloth's Q8_0 keeps 140 GB: it runs here; nothing past the node's RAM does.
|
||||
if strings.Contains(body, `value="UD-Q8_K_XL"`) {
|
||||
if strings.Contains(body, `value="UD-Q8_K_XL"`) || strings.Contains(body, `value="IST-DAS-1.75bpw"`) {
|
||||
t.Error("offered DeepSeek's 306 GB size, which no node holds, and which Strata fetches itself")
|
||||
}
|
||||
|
||||
|
||||
@@ -278,5 +278,6 @@
|
||||
"strata.err.not_servable": "%s is fetched into the mirror first, then served from its version",
|
||||
"strata.msg.served": "%s added: its first start fetches about %s GB onto the node and prepares it, which can take hours on a slow link; Replicas shows how far it is.",
|
||||
"strata.pack_files": "part of Strata's ready-made %s %s pack: served whole from the mirror's Strata tab, Strata fetching it onto the node",
|
||||
"strata.unknown_pack": "a pack made for Strata that Strata 0.2.7 does not know: its setup refuses it, and llama.cpp cannot read it"
|
||||
"strata.unknown_pack": "a pack made for Strata that Strata 0.2.7 does not know: its setup refuses it, and llama.cpp cannot read it",
|
||||
"strata.old_card": "its engine needs an NVIDIA card of compute capability %s or newer (RTX 30 on); the nodes with Strata have older ones"
|
||||
}
|
||||
@@ -271,5 +271,6 @@
|
||||
"strata.err.not_servable": "%s önce aynaya indirilir, sonra sürümünden sunulur",
|
||||
"strata.msg.served": "%s eklendi: ilk açılışında node'a yaklaşık %s GB indirip hazırlar; yavaş bağlantıda saatler sürebilir. İlerleyişi Replikalar gösterir.",
|
||||
"strata.pack_files": "Strata'nın hazır %s %s paketinin parçası: aynanın Strata sekmesinden bütün olarak sunulur, Strata onu node'a kendisi indirir",
|
||||
"strata.unknown_pack": "Strata için hazırlanmış ama Strata 0.2.7'nin tanımadığı bir paket: kurulumu onu reddeder, llama.cpp de okuyamaz"
|
||||
"strata.unknown_pack": "Strata için hazırlanmış ama Strata 0.2.7'nin tanımadığı bir paket: kurulumu onu reddeder, llama.cpp de okuyamaz",
|
||||
"strata.old_card": "motoru compute capability %s ya da daha yeni bir NVIDIA kartı ister (RTX 30 ve sonrası); Strata kurulu node'ların kartları daha eski"
|
||||
}
|
||||
Reference in new issue
Block a user