Ctrl K
ComunidadGuías…

internal/autogen/flagtable_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package autogen

import (
	"strings"
	"testing"

	"github.com/quartermaster-labs/quartermaster/internal/config"
)

// intPtr / floatPtr keep the kitchen-sink override below readable.
func intPtr(i int) *int           { return &i }
func floatPtr(f float64) *float64 { return &f }

// Every flag the llama emitter can produce must be in the table. The fixture is
// a kitchen-sink override that turns on every optional flag, so a new emitter
// flag fails here until the table knows it (and therefore until custom-args
// suppression can handle it).
func TestFlagTable_CoversEveryEmittedFlag(t *testing.T) {
	yes := true
	s := Settings{
		ServerExe: "llama-server",
		Threads:   8,
		TtlSec:    600,
		SlotCache: SlotCacheSettings{Enable: true, Path: "C:/cache/slotkv"},
	}
	meta := Metadata{
		Architecture:     "qwen3moe",
		BlockCount:       32,
		IsMoE:            true,
		IsMTP:            true,
		FullAttnInterval: 4,
	}
	row := GgufRow{
		FullPath:   "/models/foo.gguf",
		MmprojPath: "/models/mmproj.gguf",
		DraftPath:  "/models/mtp-draft.gguf",
		DraftKind:  "mtp",
	}
	prof := profile{
		Name:         "foo",
		Target:       24,
		Overhead:     2,
		Ctx:          8192,
		IsLong:       true,
		TensorSplit:  []float64{2, 1},
		Vision:       true,
		CpuMmproj:    true,
		ReasoningFmt: "deepseek",
	}
	ov := &Override{
		Mmap: "off", DirectIo: true, // --load-mode dio
		FlashAttn: "on", Threads: 8, Parallel: 4, Ub: 512,
		KvInRam: true, KvK: "q8_0", KvV: "q8_0", KvKDraft: "q8_0", KvVDraft: "q8_0",
		Spec: "draft-mtp+ngram-mod", SpecDraftNMax: 3, SpecDraftNMin: 1, SpecDefault: true,
		SpecNgramSizeN: 4, SpecNgramSizeM: 8, SpecNgramMinHits: 2,
		ReasoningFmt: "deepseek", ReasoningBudget: 4096,
		CtxCheckpoints: intPtr(6), CheckpointMinStep: 2048,
		Dry: &yes, DryMultiplier: 0.9, DryBase: 1.5, DryAllowedLength: 4,
		Temp: floatPtr(0.7), TopK: intPtr(20), TopP: floatPtr(0.95), MinP: floatPtr(0.05), PresencePenalty: floatPtr(0.2),
		ThreadsBatch: 8, Prio: 1, NoOpOffload: true, NoRepack: true, CacheReuse: 64,
		CacheRamMB: 2048, LogVerbosity: 2, CacheIdleSlots: "on", SwaFull: true,
		ContextShift: "on", SlotPromptSimilarity: 0.5, RopeScaling: "yarn", RopeScale: 2,
		RopeFreqBase: 1e6, YarnOrigCtx: 4096, SplitMode: "layer", TensorSplit: "3,1",
		OverrideTensor: "exps=CPU", ChatTemplateFile: "/models/tmpl.jinja",
		MmprojFile: "/models/mmproj.gguf", SlotCache: &yes,
	}

	lines := buildCmdLines(s, meta, row, prof, 8192, 99, 8, "q8_0", "q8_0", true, ov)
	argv, err := config.SanitizeCommand(strings.Join(lines, " "))
	if err != nil {
		t.Fatalf("generated command does not split: %v", err)
	}
	for i := 0; i < len(argv); i++ {
		tok := argv[i]
		if !strings.HasPrefix(tok, "-") {
			continue
		}
		name, _, inline := SplitFlagToken(tok)
		def, ok := LookupFlag(name)
		if !ok {
			t.Errorf("emitted flag %q is missing from llamaFlagTable (add it there and to the knob-clearing switch)", tok)
			continue
		}
		if def.Value && !inline {
			i++ // skip the value, which may itself start with '-' (negative samplers)
		}
	}
}

// The table must not contain a spelling twice, and every alias must resolve
// back to its own entry.
func TestFlagTable_NoDuplicateSpellings(t *testing.T) {
	seen := map[string]string{}
	for _, d := range llamaFlagTable {
		for _, name := range append([]string{d.Name}, d.Aliases...) {
			if prev, dup := seen[name]; dup {
				t.Errorf("flag %q appears in both %q and %q", name, prev, d.Name)
			}
			seen[name] = d.Name
		}
	}
	for _, d := range llamaFlagTable {
		for _, name := range append([]string{d.Name}, d.Aliases...) {
			got, ok := LookupFlag(name)
			if !ok || got.Name != d.Name {
				t.Errorf("LookupFlag(%q) = %q/%v, want %q", name, got.Name, ok, d.Name)
			}
		}
	}
}

func TestSplitFlagToken(t *testing.T) {
	cases := []struct {
		in       string
		name     string
		value    string
		hasValue bool
	}{
		{"-c", "-c", "", false},
		{"--ctx-size", "--ctx-size", "", false},
		{"--ctx-size=32768", "--ctx-size", "32768", true},
		{"-c=32768", "-c=32768", "", false}, // short '=' form is not a thing
		{"q8_0", "q8_0", "", false},
	}
	for _, c := range cases {
		name, value, has := SplitFlagToken(c.in)
		if name != c.name || value != c.value || has != c.hasValue {
			t.Errorf("SplitFlagToken(%q) = %q/%q/%v, want %q/%q/%v", c.in, name, value, has, c.name, c.value, c.hasValue)
		}
	}
}

func TestOwnedKnobs(t *testing.T) {
	tokens := []string{"--ctx-size", "32768", "--temp", "-0.5", "-cram", "2048", "--foo", "bar", "-m", "/x.gguf"}
	owned := OwnedKnobs(tokens)
	for _, want := range []string{"ctx", "temp", "cacheRam", "model"} {
		if !owned[want] {
			t.Errorf("knob %q should be owned (owned=%v)", want, owned)
		}
	}
	if len(owned) != 4 {
		t.Errorf("owned = %v, want exactly ctx/temp/cacheRam/model", owned)
	}
}

func TestClearOwnedFields(t *testing.T) {
	ov := &Override{
		Ctx: 8192, Mmap: "off", Mlock: true, DirectIo: true,
		Temp: floatPtr(0.5), TopK: intPtr(20), CacheRamMB: 4096, LogVerbosity: 3,
		PreserveThinking: boolPtr(false), CtxCheckpoints: intPtr(6),
	}
	cleared := ClearOwnedFields(ov, map[string]bool{"ctx": true, "loadMode": true, "cacheRam": true, "temp": true})
	if ov.Ctx != 0 || ov.Mmap != "" || ov.Mlock || ov.DirectIo || ov.CacheRamMB != 0 || ov.Temp != nil {
		t.Errorf("owned fields not cleared: %+v", ov)
	}
	if ov.TopK == nil || *ov.TopK != 20 || ov.LogVerbosity != 3 || ov.PreserveThinking == nil || ov.CtxCheckpoints == nil {
		t.Errorf("unowned fields must survive: %+v", ov)
	}
	want := map[string]bool{"ctx": true, "loadMode": true, "cacheRam": true, "temp": true}
	if len(cleared) != len(want) {
		t.Fatalf("cleared = %v, want %v", cleared, want)
	}
	for _, k := range cleared {
		if !want[k] {
			t.Errorf("unexpected cleared knob %q", k)
		}
	}
}

func boolPtr(b bool) *bool { return &b }