package catalog import ( "strings" "testing" ) func TestCPUProfileMayOmitGPUClass(t *testing.T) { c, err := New([]Model{{ Name: "qwen-0.5b", Source: "/models/qwen.gguf", Profiles: []Profile{{Name: "cpu", Engine: "llamacpp"}}, }}) if err != nil { t.Fatalf("a llama.cpp profile with no GPU class was rejected: %v", err) } m, _ := c.Resolve("qwen-0.5b") p := m.Profiles[0] if !p.CPUOnly() { t.Error("the profile was not recognized as CPU-only") } if p.GPUsPerNode != 0 { t.Errorf("a CPU profile asks for %d GPUs per node", p.GPUsPerNode) } if pp := m.PlacementProfiles()[0]; !pp.CPUOnly() || pp.GPUsPerNode != 0 { t.Errorf("placement sees %+v", pp) } } func TestGPUEngineStillRequiresItsClass(t *testing.T) { // Omitting the class for vLLM is a mistake, and one that would otherwise // surface as a replica placed on a machine with no cards, minutes later. _, err := New([]Model{{ Name: "llama-70b", Source: "meta/llama", Profiles: []Profile{{Name: "tp4", Engine: "vllm", TP: 4}}, }}) if err == nil || !strings.Contains(err.Error(), "gpu_class is required") { t.Errorf("expected the class to be required for vllm: %v", err) } } func TestCPUProfileCannotSpanNodes(t *testing.T) { // llama.cpp has no equivalent of a replica spanning machines, so accepting // this would place shards that never form one replica. _, err := New([]Model{{ Name: "qwen-0.5b", Source: "/models/qwen.gguf", Profiles: []Profile{{Name: "cpu", Engine: "llamacpp", Nodes: 2}}, }}) if err == nil || !strings.Contains(err.Error(), "cannot span") { t.Errorf("expected a multi-node CPU profile to be refused: %v", err) } } func TestCPUProfileReachesTheEngineWithoutGPUs(t *testing.T) { c, err := New([]Model{{ Name: "qwen-0.5b", Source: "/models/qwen.gguf", Profiles: []Profile{{Name: "cpu", Engine: "llamacpp", Args: []string{"--ctx-size", "4096"}}}, }}) if err != nil { t.Fatal(err) } m, _ := c.Resolve("qwen-0.5b") ep, ok := m.EngineProfile("cpu") if !ok { t.Fatal("the engine profile is missing") } if ep.Engine != "llamacpp" || ep.ModelPath != "/models/qwen.gguf" { t.Errorf("engine profile %+v", ep) } if ep.ServedModelName != "qwen-0.5b" { t.Errorf("served as %q", ep.ServedModelName) } }