Files
zzet--gortex/cmd/gortex/eval_embedders_models.go
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

161 lines
5.7 KiB
Go

// eval_embedders_models.go — registry of next-generation embedder
// model specs that `gortex eval embedders` can probe / benchmark
// alongside the existing MiniLM-L6-v2 ONNX variants. Each spec
// carries an install hint and a loader that returns an actionable
// "not installed" error when its external dependency isn't local —
// so the harness never tries to silently download GB-scale models.
//
// The split: MiniLM-L6-v2 variants live in the existing flow
// (eval_embedders.go); next-gen specs live here. Listing via
// `gortex eval embedders --list` shows both surfaces so an operator
// can pick what to benchmark without guessing.
package main
import (
"fmt"
"os/exec"
"sort"
"strings"
"github.com/spf13/cobra"
)
// modelSpec describes one embedder candidate the registry knows
// about. Loader returns a working provider on success, or an error
// when the model's external dependency is missing — the bench skips
// that row cleanly rather than aborting.
type modelSpec struct {
Name string
Provider string // friendly provider grouping (Google / Alibaba / NVIDIA / Model2Vec / …)
Dim int // embedding dimensionality (target; loader may report different)
InstallHint string
// LoaderCheck cheaply verifies whether the external dependency
// looks installable / loadable. Returns nil on "looks good" and
// the install hint on "missing". Cheap by design so `--list`
// can run it for every spec without paying for an actual load.
LoaderCheck func() error
}
// nextGenModelSpecs is the registry of model specs `gortex eval
// embedders` knows about beyond MiniLM-L6-v2. Adding a new model =
// one entry here + the install hint in the docs. Order is stable so
// the --list output is deterministic.
func nextGenModelSpecs() []modelSpec {
return []modelSpec{
{
Name: "embedding-gemma",
Provider: "Google",
Dim: 768,
InstallHint: "pip install sentence-transformers transformers torch; model: google/embeddinggemma-300m (~1.2 GB first-run download)",
LoaderCheck: pythonModulePresent("sentence_transformers"),
},
{
Name: "qwen3-embedding-8b",
Provider: "Alibaba",
Dim: 4096,
InstallHint: "pip install sentence-transformers transformers torch; model: Qwen/Qwen3-Embedding-8B (~16 GB first-run download; requires a 24 GB+ GPU for inference)",
LoaderCheck: pythonModulePresent("sentence_transformers"),
},
{
Name: "nv-embed-v2",
Provider: "NVIDIA",
Dim: 4096,
InstallHint: "pip install sentence-transformers transformers torch; model: nvidia/NV-Embed-v2 (~15 GB first-run download; gated — accept the licence at huggingface.co/nvidia/NV-Embed-v2 before downloading)",
LoaderCheck: pythonModulePresent("sentence_transformers"),
},
{
Name: "potion-code-16m",
Provider: "Model2Vec",
Dim: 512,
InstallHint: "pip install model2vec; model: minishlab/potion-code-16M (~32 MB first-run download; pure-CPU, no GPU needed)",
LoaderCheck: pythonModulePresent("model2vec"),
},
}
}
// modelSpecByName looks up a spec case-insensitively. Returns nil
// when no spec matches — caller decides whether to error or skip.
func modelSpecByName(name string) *modelSpec {
for _, s := range nextGenModelSpecs() {
if strings.EqualFold(s.Name, name) {
return &s
}
}
return nil
}
// pythonModulePresent returns a LoaderCheck that runs
// `python3 -c "import <module>"`. Fast (sub-100ms) and honest about
// what's installed.
func pythonModulePresent(module string) func() error {
return func() error {
pythons := []string{"python3", "python"}
var lastErr error
for _, py := range pythons {
if _, err := exec.LookPath(py); err != nil {
continue
}
cmd := exec.Command(py, "-c", "import "+module)
if err := cmd.Run(); err == nil {
return nil
} else {
lastErr = err
}
}
if lastErr == nil {
return fmt.Errorf("python3 not on PATH")
}
return fmt.Errorf("python module %q not importable", module)
}
}
// --- list subcommand -----------------------------------------------
// evalEmbeddersListCmd surfaces the model registry without running
// any benchmark — useful in CI to confirm wiring and for users to
// pick a model to actually benchmark.
var evalEmbeddersListCmd = &cobra.Command{
Use: "list",
Short: "List registered embedder model specs (next-gen + MiniLM variants) with availability",
RunE: func(cmd *cobra.Command, _ []string) error {
w := cmd.OutOrStdout()
fmt.Fprintln(w, "# Embedder model registry")
fmt.Fprintln(w)
fmt.Fprintln(w, "## Next-gen specs (Python-backed)")
fmt.Fprintln(w)
fmt.Fprintln(w, "| name | provider | dim | available | install hint |")
fmt.Fprintln(w, "|------|----------|----:|:---------:|--------------|")
specs := nextGenModelSpecs()
// Stable display order: by provider then name.
sort.Slice(specs, func(i, j int) bool {
if specs[i].Provider != specs[j].Provider {
return specs[i].Provider < specs[j].Provider
}
return specs[i].Name < specs[j].Name
})
for _, s := range specs {
avail := "✓"
if s.LoaderCheck != nil {
if err := s.LoaderCheck(); err != nil {
avail = "✗"
}
}
fmt.Fprintf(w, "| %s | %s | %d | %s | %s |\n",
s.Name, s.Provider, s.Dim, avail, s.InstallHint)
}
fmt.Fprintln(w)
fmt.Fprintln(w, "## MiniLM-L6-v2 ONNX variants (in-process via Hugot)")
fmt.Fprintln(w)
fmt.Fprintln(w, "All variants ship in-process — no external install required. Pass to `gortex eval embedders --variants <name>`.")
fmt.Fprintln(w)
for _, name := range []string{"fp32", "o2", "o3", "o4", "qint8_arm64", "qint8_avx512", "quint8_avx2"} {
fmt.Fprintf(w, "- %s\n", name)
}
return nil
},
}
func init() {
evalEmbeddersCmd.AddCommand(evalEmbeddersListCmd)
}