localllm

package
v1.2.3 Latest Latest
Warning

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

Go to latest
Published: Aug 20, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package localllm provides CGO bindings to llama.cpp for local GGUF model inference.

This package enables NornicDB to run embedding models directly without external services like Ollama. It uses llama.cpp compiled as a static library with GPU acceleration (Metal on macOS, CUDA on Linux) and CPU fallback.

Metal Optimizations (Apple Silicon):

  • Configurable flash attention
  • Full model GPU offload by default
  • Unified memory utilization
  • SIMD-optimized CPU fallback

Features:

  • GPU-first with automatic CPU fallback
  • Memory-mapped model loading for low memory footprint
  • Thread-safe embedding generation
  • Batch embedding support

Example:

opts := localllm.DefaultOptions("/models/bge-m3.gguf")
model, err := localllm.LoadModel(opts)
if err != nil {
	log.Fatal(err)
}
defer model.Close()

embedding, err := model.Embed(ctx, "hello world")
// embedding is a normalized []float32

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ContextFeatures added in v1.1.3

type ContextFeatures struct {
	// CtxType selects the context type. 0=default, 1=MTP.
	// Only set to 1 if the model has MTP layers.
	CtxType int
	// PoolingType controls embedding pooling. 1=mean (default for embeddings),
	// 2=cls, 3=last, 4=rank. Use -1 to leave unspecified.
	PoolingType int
	// AttentionType controls attention masking. 0=causal (LLM default),
	// 1=non-causal (BERT-style, default for embeddings).
	AttentionType int
	// FlashAttn controls flash attention. -1=auto, 0=disabled (default), 1=enabled.
	FlashAttn int
}

ContextFeatures configures llama.cpp context parameters that vary by model.

These are passthrough settings from environment variables so that different models (embedding, MTP, generation) can declare the features they need.

Environment variables (per domain):

Embedding:  NORNICDB_EMBEDDING_CTX_TYPE, _POOLING_TYPE, _ATTENTION_TYPE, _FLASH_ATTN
Rerank:     NORNICDB_RERANK_CTX_TYPE,    _POOLING_TYPE, _ATTENTION_TYPE, _FLASH_ATTN
Heimdall:   NORNICDB_HEIMDALL_CTX_TYPE,  _POOLING_TYPE, _ATTENTION_TYPE, _FLASH_ATTN

func DefaultContextFeatures added in v1.1.3

func DefaultContextFeatures() ContextFeatures

DefaultContextFeatures returns defaults optimized for embedding models.

type GenerateParams

type GenerateParams struct {
	MaxTokens   int
	Temperature float32
	TopP        float32
	TopK        int
	StopTokens  []string
}

GenerateParams configures text generation.

func DefaultGenerateParams

func DefaultGenerateParams() GenerateParams

DefaultGenerateParams returns sensible defaults for structured output.

type GenerationModel

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

GenerationModel wraps a GGUF model for text generation.

func LoadGenerationModel

func LoadGenerationModel(opts GenerationOptions) (*GenerationModel, error)

LoadGenerationModel loads a GGUF model for text generation.

func (*GenerationModel) Close

func (g *GenerationModel) Close() error

Close releases all resources.

func (*GenerationModel) Generate

func (g *GenerationModel) Generate(ctx context.Context, prompt string, params GenerateParams) (string, error)

Generate produces a complete response for the prompt.

func (*GenerationModel) GenerateStream

func (g *GenerationModel) GenerateStream(ctx context.Context, prompt string, params GenerateParams, callback func(token string) error) error

GenerateStream produces tokens via callback for streaming. Uses safe decode functions with signal handling and detailed error reporting.

func (*GenerationModel) ModelPath

func (g *GenerationModel) ModelPath() string

ModelPath returns the loaded model path.

type GenerationOptions

type GenerationOptions struct {
	ModelPath   string
	ContextSize int // Max context window (default: 2048)
	BatchSize   int // Processing batch size (default: 512)
	Threads     int // CPU threads (default: NumCPU/2)
	GPULayers   int // GPU offload (-1=auto, 0=CPU)
	Features    ContextFeatures
}

GenerationOptions configures generation model loading.

func DefaultGenerationOptions

func DefaultGenerationOptions(modelPath string) GenerationOptions

DefaultGenerationOptions returns sensible defaults for text generation.

type Model

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

Model wraps a GGUF model for embedding generation.

Thread-safe: The Embed and EmbedBatch methods can be called concurrently, but operations are serialized internally via mutex to prevent race conditions with the underlying C context.

func LoadModel

func LoadModel(opts Options) (*Model, error)

LoadModel loads a GGUF model for embedding generation.

The model is memory-mapped for low memory footprint. GPU layers are automatically offloaded based on Options.GPULayers:

  • -1: Auto-detect GPU and offload all layers (recommended)
  • 0: CPU only (no GPU offload)
  • N: Offload N layers to GPU

Metal Optimization (Apple Silicon):

When running on Apple Silicon with Metal support compiled in:

  • All model layers are offloaded to GPU by default
  • Flash attention defaults to disabled for embedding stability
  • Unified memory is utilized efficiently
  • Typical speedup: 5-10x over CPU-only

Example:

opts := localllm.DefaultOptions("/models/bge-m3.gguf")
model, err := localllm.LoadModel(opts)
if err != nil {
	log.Fatalf("Failed to load model: %v", err)
}
defer model.Close()

fmt.Printf("Model loaded: %d dimensions\n", model.Dimensions())

func (*Model) ChunkText

func (m *Model) ChunkText(text string, maxTokens, overlap int) ([]string, error)

ChunkText deterministically splits text using the model tokenizer so every returned chunk fits within the provided token cap.

func (*Model) Close

func (m *Model) Close() error

Close releases all resources associated with the model.

After Close is called, the Model must not be used. This properly releases GPU memory on Metal/CUDA.

func (*Model) CountTokens

func (m *Model) CountTokens(text string) (int, error)

CountTokens returns the exact tokenizer count for text using the model's vocab.

func (*Model) Dimensions

func (m *Model) Dimensions() int

Dimensions returns the embedding vector size.

This is determined by the model architecture:

  • BGE-M3: 1024 dimensions
  • E5-large: 1024 dimensions
  • Jina-v2-base-code: 768 dimensions

func (*Model) Embed

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

Embed generates a normalized embedding vector for the given text.

The returned vector is L2-normalized (unit length), suitable for cosine similarity calculations.

Concurrency:

Operations are serialized via mutex because llama.cpp contexts are NOT thread-safe. The C.embed call holds the lock for the duration of GPU/CPU inference (~5-50ms depending on text length and hardware).

For higher throughput under concurrent load, create multiple Model instances (each with its own GPU context). The GPU can process multiple contexts efficiently via kernel scheduling.

GPU Acceleration:

On Apple Silicon with Metal, the embedding is computed on the GPU:

  1. Tokenization (CPU)
  2. Model inference (GPU)
  3. Pooling (GPU)
  4. Normalization (CPU)

Example:

vec, err := model.Embed(ctx, "graph database")
if err != nil {
	return err
}
fmt.Printf("Embedding: %d dimensions\n", len(vec))

func (*Model) EmbedBatch

func (m *Model) EmbedBatch(ctx context.Context, texts []string) ([][]float32, error)

EmbedBatch generates normalized embeddings for multiple texts.

Each text is processed sequentially through the GPU. For maximum throughput with many texts, consider parallel processing with multiple Model instances.

Note: True batch processing (multiple texts in single GPU kernel) would require llama.cpp changes. Current implementation is efficient for moderate batch sizes due to GPU kernel reuse.

Example:

texts := []string{"hello", "world", "test"}
vecs, err := model.EmbedBatch(ctx, texts)
if err != nil {
	return err
}
for i, vec := range vecs {
	fmt.Printf("Text %d: %d dims\n", i, len(vec))
}

func (*Model) EmbedRaw

func (m *Model) EmbedRaw(ctx context.Context, text string) ([]float32, error)

EmbedRaw returns the pooled output from the model without normalizing.

func (*Model) MaxTokens

func (m *Model) MaxTokens() int

MaxTokens returns the effective tokenizer/input limit for this model context.

func (*Model) ModelDescription

func (m *Model) ModelDescription() string

ModelDescription returns a human-readable description of the loaded model.

type Options

type Options struct {
	ModelPath   string
	ContextSize int
	BatchSize   int
	Threads     int
	GPULayers   int
	Features    ContextFeatures
}

Options configures model loading and inference.

Fields:

  • ModelPath: Path to .gguf model file
  • ContextSize: Max context size for tokenization (default: auto from model cap, up to 8192 for embedding)
  • BatchSize: Batch size for processing (default: match effective context size)
  • Threads: CPU threads for inference (default: NumCPU/2, min 4)
  • GPULayers: GPU layer offload (-1=auto/all, 0=CPU only, N=N layers)
  • Features: llama.cpp context features configurable per-model via env

func DefaultOptions

func DefaultOptions(modelPath string) Options

DefaultOptions returns options optimized for embedding generation.

GPU is enabled by default (-1 = auto-detect and use all layers). Set GPULayers to 0 to force CPU-only mode.

For Apple Silicon, this enables full Metal GPU acceleration with:

  • Full model offload
  • Unified memory optimization

Example:

opts := localllm.DefaultOptions("/models/bge-m3.gguf")
opts.GPULayers = 0 // Force CPU mode
model, err := localllm.LoadModel(opts)

func DefaultRerankerOptions added in v1.2.3

func DefaultRerankerOptions(modelPath string) Options

DefaultRerankerOptions returns options for classifier-head GGUF rerankers. Rank pooling attaches the model's classification head and returns its logits.

type RerankerModel

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

RerankerModel wraps a GGUF model for reranking (query, document) pairs. It encodes the query and document as a model-specific classification pair.

func LoadRerankerModel

func LoadRerankerModel(opts Options) (*RerankerModel, error)

LoadRerankerModel loads a GGUF reranker model (e.g. BGE-Reranker-v2-m3). Uses the same loader as embedding models; the model file must be a reranker/classification GGUF that outputs a single score per (query, doc) pair.

func (*RerankerModel) Close

func (r *RerankerModel) Close() error

Close releases the underlying model.

func (*RerankerModel) Score

func (r *RerankerModel) Score(ctx context.Context, query, document string) (float32, error)

Score returns a relevance score in [0, 1] for the (query, document) pair.

Jump to

Keyboard shortcuts

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