Documentation
¶
Overview ¶
Package rembed is a pure-Go embedding inference engine for BERT-family encoder models: text in, L2-normalized embedding vectors out. No cgo, no ONNX Runtime.
emb, err := rembed.Load("models/all-MiniLM-L6-v2")
vecs, err := emb.Embed(ctx, []string{"hello world"})
Index ¶
- type Embedder
- func (e *Embedder) Close() error
- func (e *Embedder) Dim() int
- func (e *Embedder) Embed(ctx context.Context, texts []string) ([][]float32, error)
- func (e *Embedder) EmbedTokens(ctx context.Context, texts []string) ([]TokenEmbeddings, error)
- func (e *Embedder) Model() string
- func (e *Embedder) Quantized() bool
- func (e *Embedder) QuantizedActivations() bool
- func (e *Embedder) Tokenize(text string) []int64
- type Option
- type TokenEmbeddings
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Embedder ¶
type Embedder struct {
// contains filtered or unexported fields
}
Embedder turns texts into fixed-size embedding vectors. It is safe for concurrent use. Latency is the default optimization target: each Embed call fans out across up to GOMAXPROCS cores and keeps a spinning worker pool for its duration, which trades idle-core burn for wall time — see WithWorkers to cap that for throughput-saturated servers.
func Load ¶
Load opens a model. ref may be:
- a local directory: either a converted dir (manifest.json) or a plain Hugging Face repo checkout (config.json + 1_Pooling/... — the manifest is derived on the fly);
- a Hugging Face model id like "sentence-transformers/all-MiniLM-L6-v2" (or explicitly "hf:org/name", which always means the Hub): the files are downloaded straight from the Hub in pure Go into $REMBED_CACHE (default: the user cache dir) and reused on later loads. Set HF_TOKEN for gated repos.
A ref that could be BOTH — an org/name that does not exist locally but whose first segment IS a local directory (e.g. a missing "models/all-MiniLM-L6-v2") — is treated as a missing LOCAL path: a typo must fail as one, not turn into silent network egress carrying HF_TOKEN. Use the "hf:" prefix to force the Hub in that situation.
func (*Embedder) Close ¶ added in v0.3.0
Close releases resources held by the Embedder — the memory-mapped weights file when loaded WithDiskWeights. It is a safe no-op for RAM-loaded models. After Close, the Embedder must not be used.
func (*Embedder) Dim ¶
Dim returns the embedding dimensionality — the WithDim truncation when set, else the model's full hidden size.
func (*Embedder) Embed ¶
Embed returns one embedding per input text, each of length Dim(), L2-normalized when the model manifest says so (true for the sentence-transformers models). Texts are embedded independently, and a batch of several texts is fanned out ACROSS texts (each forward pass serial or lightly parallel) — near-linear throughput scaling with zero padding waste, and results bit-identical to embedding one at a time. ctx is checked before each text's forward pass; a forward already in flight runs to completion. NOTE: a batch call can hold up to min(GOMAXPROCS, WithWorkers) scratch buffers (~25 MB each at max sequence length) simultaneously — size servers with WithWorkers.
func (*Embedder) EmbedTokens ¶ added in v0.3.0
EmbedTokens returns per-token hidden states for each text — the raw material for rerankers, late-interaction (ColBERT-style) retrieval, and custom pooling. Embed remains the API for sentence vectors; nothing here is pooled or normalized. Batches fan out across texts exactly like Embed. All results are held live for the whole call — a 256-text batch of long inputs is ~200 MB of hidden states.
func (*Embedder) Quantized ¶
Quantized reports whether an int8 path is actually active (WithInt8 or WithInt8Activations requested AND every dense weight packed as int8 — the engine falls back to fp32 per-matrix when the CPU or a shape cannot take it).
func (*Embedder) QuantizedActivations ¶ added in v0.3.0
QuantizedActivations reports whether the full u8-activation VNNI path is active for every dense weight.
type Option ¶
type Option func(*loadOptions)
Option configures Load.
func WithDim ¶ added in v0.3.0
WithDim truncates each embedding to its first d dimensions and re-L2-normalizes — Matryoshka Representation Learning (MRL). Models trained for it (EmbeddingGemma: 768→512/256/128; nomic-embed) keep most of their quality at a fraction of the storage and search cost. d must be in [1, full dim]; d equal to the full dim is a no-op. The truncation is a deterministic slice-then-renormalize of the final vector, so it is meaningful only for MRL-trained models — on others it still runs but the shorter vector is not a faithful embedding. Dim() reflects the truncated size.
func WithDiskWeights ¶ added in v0.3.0
func WithDiskWeights() Option
WithDiskWeights memory-maps the model's weights from a pack file on disk instead of loading them into RAM: the OS pages weights in on access and evicts them under memory pressure, so a model larger than RAM runs (disk-bandwidth-bound, but it runs) and resident memory tracks the working set. On first use the safetensors are packed to <dir>/weights.rembedpack (streamed one tensor at a time, so the pack step also fits a small box); later loads mmap it directly. Currently supported for qwen3 (the decoder embedder whose 4B/8B sizes motivate it). The Embedder MUST be Closed to unmap. Numerics are unchanged — only where the bytes live changes.
func WithInt8 ¶
func WithInt8() Option
WithInt8 selects weight-only int8 inference: transformer dense weights are quantized at load (per-output-channel symmetric scales; activations stay float32), cutting per-embed weight traffic ~4× (~42 MB → ~10.5 MB for MiniLM). Token embeddings stay fp32, so RESIDENT model memory drops ~1.5×, not 4×. Embeddings differ slightly from fp32 — see the int8 golden test for the measured bound. On CPUs without AVX2+FMA the engine silently falls back to fp32; check Quantized() when the mode matters.
func WithInt8Activations ¶ added in v0.3.0
func WithInt8Activations() Option
WithInt8Activations selects FULL int8 inference on CPUs with AVX-VNNI (Alder Lake 2021+, and AVX-512-VNNI servers): weights per-channel int8 AND activations quantized per row to u8 at matmul time, so VPDPBUSD does four multiply-accumulates per lane per instruction. Accuracy trades a little further than weight-only int8 — see the measured bound in the golden test — and the engine falls back to weight-only int8 (then fp32) where VNNI or a shape is unavailable; check QuantizedActivations() when the mode matters. Implies WithInt8.
func WithWorkers ¶
WithWorkers caps the number of CPU workers one Embed call uses. The default (0) uses GOMAXPROCS, minimizing single-request latency by keeping every core busy — including a spinning fork-join pool that burns idle-core cycles for the duration of each call (~10× the useful CPU at low concurrency). A server saturating many concurrent Embed calls should set a small cap; WithWorkers(1) is fully serial with zero spinning.
The cap governs every fan-out: the packed SIMD path, the attention and GELU phases, and the unpacked fallback matmul (non-amd64, or weight shapes the packer rejects).
type TokenEmbeddings ¶ added in v0.3.0
type TokenEmbeddings struct {
IDs []int64 // input token ids, including [CLS]/[SEP] framing
Vectors [][]float32 // len(IDs) rows of Dim()
}
TokenEmbeddings is one text's token-level output: the final encoder layer's hidden state for every token (ONNX Runtime's last_hidden_state), unpooled and unnormalized.
Vectors' rows are views into a single backing array — one allocation of len(IDs)×Dim() float32s per text (~786 KB for a 512-token input). Retaining one row keeps the whole text's allocation alive, and rows are capacity-clamped so append on a row cannot bleed into its neighbor.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
rembed
command
Command rembed embeds text, validates against the golden ONNX reference, and benchmarks the engine.
|
Command rembed embeds text, validates against the golden ONNX reference, and benchmarks the engine. |
|
internal
|
|
|
hub
Package hub fetches model files straight from the Hugging Face Hub in pure Go — no Python, no conversion step.
|
Package hub fetches model files straight from the Hugging Face Hub in pure Go — no Python, no conversion step. |
|
mmapfile
Package mmapfile memory-maps a file read-only so its bytes can be handed out as slices without copying into the heap.
|
Package mmapfile memory-maps a file read-only so its bytes can be handed out as slices without copying into the heap. |
|
model
Package model implements the BERT-family encoder forward pass: embeddings(token+position+segment) → N layers (self-attention + FFN, post-LayerNorm) → mean pooling → L2 normalize.
|
Package model implements the BERT-family encoder forward pass: embeddings(token+position+segment) → N layers (self-attention + FFN, post-LayerNorm) → mean pooling → L2 normalize. |
|
packfile
Package packfile stores a model's weights, already widened to float32, in one mmap-friendly file: an 8-byte little-endian header length, a JSON header naming each tensor and its byte range, then the contiguous f32 data.
|
Package packfile stores a model's weights, already widened to float32, in one mmap-friendly file: an 8-byte little-endian header length, a JSON header naming each tensor and its byte range, then the contiguous f32 data. |
|
safetensors
Package safetensors reads the safetensors weight format: an 8-byte little-endian header length, a JSON header mapping tensor names to {dtype, shape, data_offsets}, then the raw tensor data.
|
Package safetensors reads the safetensors weight format: an 8-byte little-endian header length, a JSON header mapping tensor names to {dtype, shape, data_offsets}, then the raw tensor data. |
|
tensor
Package tensor holds the math kernels for the transformer forward pass.
|
Package tensor holds the math kernels for the transformer forward pass. |
|
python
|
|
|
capi
command
Package main is the C ABI for rembed's Python (and any other FFI) bindings, built as a shared library:
|
Package main is the C ABI for rembed's Python (and any other FFI) bindings, built as a shared library: |
|
Package tokenizer implements BERT WordPiece tokenization in pure Go.
|
Package tokenizer implements BERT WordPiece tokenization in pure Go. |
|
bpe
Package bpe implements the byte-level BPE tokenizer used by the RoBERTa/GPT-2 family, ported faithfully from HuggingFace's Python implementation: text is pre-tokenized with GPT-2's pattern, each pre-token's UTF-8 bytes are mapped through the byte-to-unicode table, and adjacent symbols are merged greedily by merge rank until no ranked pair remains.
|
Package bpe implements the byte-level BPE tokenizer used by the RoBERTa/GPT-2 family, ported faithfully from HuggingFace's Python implementation: text is pre-tokenized with GPT-2's pattern, each pre-token's UTF-8 bytes are mapped through the byte-to-unicode table, and adjacent symbols are merged greedily by merge rank until no ranked pair remains. |
|
gemma
Package gemma implements the Gemma tokenizer (EmbeddingGemma and the Gemma 3 family) in pure Go: a SentencePiece-style byte-level-fallback BPE read from the repo's tokenizer.json.
|
Package gemma implements the Gemma tokenizer (EmbeddingGemma and the Gemma 3 family) in pure Go: a SentencePiece-style byte-level-fallback BPE read from the repo's tokenizer.json. |
|
sentencepiece
Package sentencepiece implements the SentencePiece Unigram tokenizer used by the XLM-RoBERTa family (multilingual-e5, paraphrase-multilingual MiniLM, …) in pure Go: a minimal protobuf reader for the .model file, the NMT-NFKC normalizer driven by the model's precompiled charsmap, and Viterbi segmentation over the piece vocabulary — with HF's fairseq id remapping on top.
|
Package sentencepiece implements the SentencePiece Unigram tokenizer used by the XLM-RoBERTa family (multilingual-e5, paraphrase-multilingual MiniLM, …) in pure Go: a minimal protobuf reader for the .model file, the NMT-NFKC normalizer driven by the model's precompiled charsmap, and Viterbi segmentation over the piece vocabulary — with HF's fairseq id remapping on top. |