gomlx

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 5, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package gomlx provides an in-process sentence embedder for jess memory, backed by the pure-Go gomlx/compute/gobackend ML runtime.

Status: experimental. Tests download ~90MB of model weights and take a few seconds to warm up; CI skips them with -short.

Index

Constants

View Source
const (
	DefaultModelID = "sentence-transformers/all-MiniLM-L6-v2"
	DefaultDim     = 384
	DefaultSeqLen  = 128
)

Legacy defaults kept for callers that referenced them directly. New code should prefer DefaultModel (models.go) — it bundles the three together and matches the same MiniLM target.

Variables

View Source
var (
	ModelMiniLM_L6_V2 = Model{
		ID:     "sentence-transformers/all-MiniLM-L6-v2",
		Dim:    384,
		SeqLen: 128,
	}
	ModelMiniLM_L12_V2 = Model{
		ID:     "sentence-transformers/all-MiniLM-L12-v2",
		Dim:    384,
		SeqLen: 128,
	}
	ModelMpNetBase_V2 = Model{
		ID:     "sentence-transformers/all-mpnet-base-v2",
		Dim:    768,
		SeqLen: 384,
	}
	ModelBGESmall_EN_V1_5 = Model{
		ID:     "BAAI/bge-small-en-v1.5",
		Dim:    384,
		SeqLen: 512,
	}
	ModelBGEBase_EN_V1_5 = Model{
		ID:     "BAAI/bge-base-en-v1.5",
		Dim:    768,
		SeqLen: 512,
	}
	ModelBGELarge_EN_V1_5 = Model{
		ID:     "BAAI/bge-large-en-v1.5",
		Dim:    1024,
		SeqLen: 512,
	}
	ModelE5Small_V2 = Model{
		ID:     "intfloat/e5-small-v2",
		Dim:    384,
		SeqLen: 512,
	}
	ModelE5Base_V2 = Model{
		ID:     "intfloat/e5-base-v2",
		Dim:    768,
		SeqLen: 512,
	}
	ModelNomicEmbedText_V1_5 = Model{
		ID:     "nomic-ai/nomic-embed-text-v1.5",
		Dim:    768,
		SeqLen: 8192,
	}
	ModelMxbaiEmbedLarge_V1 = Model{
		ID:     "mixedbread-ai/mxbai-embed-large-v1",
		Dim:    1024,
		SeqLen: 512,
	}
)

Known-good embedding models verified working against the GoMLX pure-Go (simplego) backend. All are sentence-transformer or sentence-transformer-compatible models with vocab.txt + onnx/model.onnx in their HF repo.

Performance ordering (fastest → highest quality) and size notes are approximate, measured on Apple M-series CPU:

MiniLM_L6_V2     — ~20ms/call,   90MB, 384-dim, baseline quality
BGESmall_EN_V1_5 — ~25ms/call,  130MB, 384-dim, slightly better
E5Small_V2       — ~30ms/call,  130MB, 384-dim, similar to BGE
BGEBase_EN_V1_5  — ~80ms/call,  440MB, 768-dim, strong quality
MpNetBase_V2     — ~80ms/call,  440MB, 768-dim, longstanding default
NomicEmbedText   — ~150ms/call, 550MB, 768-dim, long-context (8K)
MxbaiEmbedLarge  — ~250ms/call, 670MB, 1024-dim, best quality
BGELarge_EN_V1_5 — ~250ms/call, 1.3GB, 1024-dim, comparable to mxbai

Memory write/read in jess is one embedding per call, so latency matters but not enormously. Quality vs disk footprint is the real choice axis. Default (MiniLM) is the right pick when in doubt — small, fast, well-understood, good enough for the retrieve-relevant-notes-from-a-personal-corpus use case.

View Source
var DefaultModel = ModelMiniLM_L6_V2

DefaultModel is what NewEmbedder uses when Options is left fully empty. Conservative pick: small, fast, well-understood, works for the common case.

Functions

This section is empty.

Types

type Embedder

type Embedder struct {
	// contains filtered or unexported fields
}

Embedder runs sentence-transformers BERT-style embedding models in-process via the pure-Go gomlx/compute/gobackend backend. No subprocess, no external runtime (no .so, no ONNX Runtime), no CGO — talon's single-binary cross-compile story stays intact.

Construction downloads the model + vocab from HuggingFace on first use (cached under HF's standard location: $HF_HOME or ~/.cache/huggingface). Subsequent constructions are warm.

Concurrency: Embed is safe to call from multiple goroutines. The internal exec is shared and protected by a mutex — running embeddings serially for now since gobackend's reentrancy isn't well-characterized and memory writes don't need parallelism.

func NewEmbedder

func NewEmbedder(opts Options) (*Embedder, error)

NewEmbedder downloads + loads a sentence-transformers model and returns an in-process Embedder ready to call.

func (*Embedder) Dim

func (e *Embedder) Dim() int

Dim returns the embedding dimensionality. Implements memory.Embedder.

func (*Embedder) Embed

func (e *Embedder) Embed(ctx context.Context, text string) ([]float32, error)

Embed produces one vector per input text. Implements memory.Embedder.Embed for single-text calls; use EmbedBatch when you have multiple sentences to amortize the model call.

func (*Embedder) EmbedBatch

func (e *Embedder) EmbedBatch(_ context.Context, sentences []string) ([][]float32, error)

EmbedBatch produces one vector per sentence. The model graph was built for a fixed batch dimension at construction time; mismatched batch sizes trigger a re-build (slow). For now batches are always 1 — talon's memory writes are one-at-a-time and recall queries one-at-a-time. Multi-batch support is a TODO.

func (*Embedder) Name

func (e *Embedder) Name() string

Name returns a stable identifier for this embedder, used by memory.Store to tag entries with the embedder that produced their vector. Implements memory.Embedder.

type Model

type Model struct {
	ID     string
	Dim    int
	SeqLen int
}

Model bundles the three things callers need to use an embedding model: the HuggingFace repo ID, the output dimensionality, and the model's positional-embedding limit. Bundling avoids the footgun of passing ModelID alone and forgetting to update Dim.

Pass to Options.Model to construct an Embedder against a known-good model:

emb, _ := gomlx.NewEmbedder(gomlx.Options{Model: gomlx.ModelNomicEmbedText})

Hosts that want a model not on the short list pass ModelID directly and let NewEmbedder auto-detect Dim/SeqLen from the repo's config.json. Pass Dim/SeqLen explicitly when you want to override the auto-detected values.

type Options

type Options struct {
	// Model bundles ID/Dim/SeqLen as one value. Highest-precedence
	// way to specify the model. Use the canonical constants
	// (gomlx.ModelMiniLM_L6_V2, gomlx.ModelNomicEmbedText_V1_5,
	// etc.) for known-good targets, or build your own Model
	// literal for a HF repo not on the list.
	Model Model

	// ModelID is the HuggingFace repo (e.g.
	// "sentence-transformers/all-MiniLM-L6-v2"). Used when Model
	// is the zero value. When Dim/SeqLen below are zero,
	// NewEmbedder auto-detects them from the repo's config.json.
	ModelID string
	// Dim is the embedding dimensionality the model produces.
	// When zero (and Model is unset), auto-detected from
	// config.json's hidden_size.
	Dim int
	// SeqLen is the fixed token length the model graph is built
	// for. When zero (and Model is unset), auto-detected from
	// config.json's max_position_embeddings. Longer texts are
	// truncated; shorter are padded.
	SeqLen int
	// AuthToken is the HuggingFace token (HF_TOKEN env). Required
	// only for gated repos; public models load without it.
	AuthToken string
}

Options configures NewEmbedder. Resolution order, highest precedence first:

  1. Model (a bundled ID/Dim/SeqLen triple) — use one of the known-good constants (ModelNomicEmbedText, etc.).
  2. ModelID + Dim + SeqLen all set — explicit override.
  3. ModelID set with Dim/SeqLen zero — auto-detect Dim/SeqLen from the model's HuggingFace config.json (one extra ~5KB download on first run; cached after).
  4. Everything empty — falls back to DefaultModel (MiniLM-L6-v2).

Resolution happens BEFORE the model download — invalid combinations surface as construction errors, not silent wrong-vector bugs.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL