Zerfoo is a Go 1.25 generics-based machine learning framework. It supports CPU and CUDA GPU execution through a unified compute engine interface. All layers, models, and training loops delegate computation to the Engine interface, enabling transparent hardware acceleration without modifying application code.
The framework targets importing and running open weights models (Gemma 3, Kimi-VL, SigLIP) from ONNX via the companion ZMF model format.
Module: github.com/zerfoo/zerfoo
tensor/ TensorNumeric[T], Storage[T], type constraints (Numeric, Float, Addable)
numeric/ Type-specific arithmetic (float32/64, float8, float16, int8, uint8), quantization
compute/ Engine[T] interface, CPUEngine, GPUEngine (//go:build cuda)
graph/ Computation graph, Node[T] interface, Builder, Parameter, topological execution
model/ Model[T], ZMF loader/exporter, global layer registry, plugin registry
layers/ Neural network layers organized by family (18 sub-packages)
layers/core/ Add, Sub, Mul, MatMul, MatMulNBits, Cast, Concat, Constant, Conv2d, Dense,
FFN, FiLM, GlobalAvgPool, Linear, LMHead, MoE, Pad, Polynomial, Reshape,
Resize, RotaryEmbedding, Shape, Slice, SpectralFingerprint, TopK, Unsqueeze, Bias
layers/activations/ ReLU, LeakyReLU, Sigmoid, Tanh, Gelu, FastGelu, Erf, Softmax, SwiGLU
layers/attention/ AttentionHead, GlobalAttention, GroupQueryAttention, LocalAttention, QKNorm, SDPA
layers/normalization/ BatchNorm, LayerNorm, RMSNorm, SimplifiedLayerNorm, SkipSimplifiedLayerNorm
layers/embeddings/ TokenEmbedding, RotaryPositionalEmbedding
layers/gather/ Gather (embedding-table lookup)
layers/transpose/ Transpose
layers/reducesum/ ReduceSum
layers/regularization/ Dropout
layers/components/ GradientComputer, MatrixMultiplier, WeightInitializer
layers/transformer/ TransformerBlock
layers/recurrent/ RNN
layers/sequence/ S4 (structured state space)
layers/hrm/ HModule, LModule (hierarchical recurrent model)
layers/features/ SpectralFeature
layers/tokenizers/ TokenizerNode
layers/registry/ RegisterAll() -- central wiring of all layers into the model registry
training/ Trainer[T], DefaultTrainer, GradientStrategy, workflow interfaces
training/optimizer/ Optimizer[T] interface, AdamW[T], SGD[T]
training/loss/ Loss[T] interface, MSE[T], CrossEntropyLoss[T]
distributed/ gRPC-based distributed training: AllReduce, Barrier, Broadcast, TLS
distributed/coordinator/ Coordinator gRPC server with worker registry and checkpoint tracking
distributed/pb/ Generated protobuf/gRPC bindings
device/ Device, Allocator interfaces (CPU + CUDA)
config/ Generic JSON config loader with env var overrides and validation
health/ HTTP health server (/healthz, /readyz, /debug/pprof/)
metrics/ ML evaluation metrics (Pearson, Spearman, MSE, RMSE, MAE)
metrics/runtime/ Operational instrumentation (Counter, Gauge, Histogram, InMemoryCollector)
log/ Structured leveled logging (Debug/Info/Warn/Error, text/JSON)
shutdown/ Ordered shutdown coordinator with reverse-order Closer execution
cmd/ CLI binaries and framework
cmd/zerfoo/ Main binary (predict, tokenize, worker, pull, run, serve subcommands)
cmd/cli/ Command interface, CommandRegistry, CLI runner, pull/run/serve commands
cmd/zerfoo-predict/ Standalone predict binary
cmd/zerfoo-tokenize/ Standalone tokenize binary
cmd/bench-compare/ Benchmark comparison tool
cmd/coverage-gate/ CI coverage enforcement tool
inference/ High-level inference API: Load, Generate, GenerateStream, Chat, Embed
generate/ Autoregressive generation loop, sampling (temp, topK, topP, repetition), streaming
registry/ Model registry with local cache, Pull/Get/List/Delete interface
serve/ OpenAI-compatible HTTP server (chat completions, completions, models, SSE streaming)
pkg/tokenizer/ BPE tokenizer loading from tokenizer.json, WhitespaceTokenizer for testing
data/ Dataset container (Sample, Batch, normalization)
features/ Time-series feature transformers (Lag, Rolling, FFT)
types/ Shared type definitions (BackwardMode)
internal/xblas/ CPU BLAS wrappers (gonum GEMM for float32/64; upcast for float16/float8)
internal/cuda/ CUDA runtime CGO bindings (//go:build cuda)
internal/cublas/ cuBLAS CGO bindings (//go:build cuda)
internal/cuda/kernels/ CUDA kernel source (.cu) and Go wrappers
testing/testutils/ Test assertion helpers, MockEngine, custom mocks
tests/ Parity tests (env-var gated model forward pass tests)
cmd/* --> model --> graph --> compute --> tensor
| | | |
| | numeric.Arithmetic[T]
| | Storage[T]
| types / \
| CPUStorage[T] GPUStorage[T]
layers/* |
| internal/cuda
graph.Node[T]
Key invariant: layers never access tensor data directly for computation. All arithmetic goes through Engine[T]. This enables transparent CPU/GPU switching.
zerfoo/must not importzonnx/oronnx/(verified bymake verify-architecture).zonnx/must not importgithub.com/zerfoo/zerfoo(decoupled viazmfformat).- CUDA code is gated behind
//go:build cuda. Non-CUDA builds compile without GPU dependencies.
Three type constraints govern the generics:
tensor.Numeric-- full union: int types, uint8, float32/64, float8.Float8, float16.Float16, float16.BFloat16tensor.Float-- standard Go floats only: float32, float64tensor.Addable-- types supporting native Go operators (+, -, *); excludes custom minifloats
Custom minifloats (float8, float16, bfloat16) require numeric.Arithmetic[T]
for all operations since Go operators do not work on defined types.
The compute engine is the central abstraction. Every layer receives an Engine at construction time and delegates all computation to it.
type Engine[T tensor.Numeric] interface {
Ops() numeric.Arithmetic[T]
// Unary
UnaryOp(ctx, a, op, dst...) (*Tensor, error)
// Binary elementwise (with broadcasting)
Add(ctx, a, b, dst...) (*Tensor, error)
Sub(ctx, a, b, dst...) (*Tensor, error)
Mul(ctx, a, b, dst...) (*Tensor, error)
Div(ctx, a, b, dst...) (*Tensor, error)
Pow(ctx, base, exp, dst...) (*Tensor, error)
// Scalar ops
AddScalar(ctx, a, scalar, dst...) (*Tensor, error)
MulScalar(ctx, a, scalar, dst...) (*Tensor, error)
DivScalar(ctx, a, scalar, dst...) (*Tensor, error)
// Matrix
MatMul(ctx, a, b, dst...) (*Tensor, error)
Transpose(ctx, a, axes, dst...) (*Tensor, error)
// Activations and math
Tanh(ctx, a, dst...) (*Tensor, error)
TanhPrime(ctx, a, upstream, dst...) (*Tensor, error)
Softmax(ctx, a, axis, dst...) (*Tensor, error)
Exp(ctx, a, dst...) (*Tensor, error)
Log(ctx, a, dst...) (*Tensor, error)
Sqrt(ctx, a, dst...) (*Tensor, error)
Rsqrt(ctx, a, dst...) (*Tensor, error)
// Reductions
Sum(ctx, a, axis, keepDims, dst...) (*Tensor, error)
ReduceSum(ctx, a, axis, keepDims, dst...) (*Tensor, error)
ReduceMean(ctx, a, axis, keepDims, dst...) (*Tensor, error)
// Tensor manipulation
Reshape(ctx, a, shape, dst...) (*Tensor, error)
Split(ctx, a, numSplits, axis) ([]*Tensor, error)
Concat(ctx, tensors, axis, dst...) (*Tensor, error)
Repeat(ctx, a, axis, reps, dst...) (*Tensor, error)
// Initialization and copy
Zero(ctx, a) error
Zeros(ctx, a, shape) error
Fill(ctx, a, value) error
Copy(ctx, dst, src) error
RandomUniform(ctx, t, min, max) error
// Embedding
Gather(ctx, params, indices, output) error
ScatterAdd(ctx, dTable, indices, dOut) error
// Encoding
OneHot(ctx, input, depth, dst...) (*Tensor, error)
}All methods accept context.Context for cancellation and timeout support.
Binary ops support broadcasting. Optional dst parameters enable in-place
operation when callers want to reuse existing tensor memory.
Implementations:
CPUEngine[T]: Usesnumeric.Arithmetic[T]for element-wise ops,internal/xblas(gonum BLAS) for MatMul,parallelFor()for multi-core. Integrated withmetrics.Collector,log.Logger, andMemoryTracker.GPUEngine[T]: CUDA kernels for float32. CPU fallback for other types. 20 methods have native GPU implementations; the rest use CPU fallback by design.
type Node[T tensor.Numeric] interface {
OpType() string
Attributes() map[string]interface{}
Forward(ctx, inputs...) (*Tensor, error)
Backward(ctx, mode, outputGradient, inputs...) ([]*Tensor, error)
Parameters() []*Parameter[T]
OutputShape() []int
}Every layer implements Node[T]. The graph Builder connects nodes into a DAG.
Graph.Forward and Graph.Backward are safe for concurrent use; a
sync.Mutex serializes access to the internal memo cache.
type Storage[T Numeric] interface {
Len() int
Slice() []T // CPU: zero-copy. GPU: copies D2H.
Set(data []T) // CPU: direct assign. GPU: copies H2D.
DeviceType() device.Type
}- CPUStorage[T]: Wraps a Go
[]Tslice.Slice()returns the underlying slice directly (zero copy).DeviceType()returnsdevice.CPU. - GPUStorage[T]: Wraps a CUDA device pointer (
unsafe.Pointer).Slice()allocates a host slice and copies D2H.Set()copies H2D.Ptr()returns the device pointer for kernel dispatch.TrySlice()/TrySet()return errors instead of logging.
type Arithmetic[T any] interface {
Add(a, b T) T
Sub(a, b T) T
Mul(a, b T) T
Div(a, b T) T
Tanh(a T) T
Sigmoid(a T) T
ReLU(a T) T
LeakyReLU(a T, alpha float64) T
Exp(a T) T
Log(a T) T
Pow(a T, n float64) T
Sqrt(a T) T
Sum(a []T) T
Abs(a T) T
IsZero(a T) bool
GreaterThan(a, b T) bool
FromFloat32(f float32) T
FromFloat64(f float64) T
One() T
}Concrete implementations: Float32Ops, Float64Ops, Int8Ops, Uint8Ops,
Float16Ops, Float8Ops.
type InternalStrategy[T tensor.Numeric] interface {
Init(rank, size int, coordinatorAddress string) error
AllReduceGradients(gradients map[string]*Tensor) error
Barrier() error
BroadcastTensor(t *Tensor, rootRank int) error
Rank() int
Size() int
Shutdown()
}gRPC-based with coordinator pattern. Workers register with a coordinator,
exchange peer addresses, then communicate directly for gradient reduction.
TLS/mTLS is supported via distributed.TLSConfig.
Layers register with the model package via builder functions:
type LayerBuilder[T tensor.Numeric] func(
engine compute.Engine[T],
ops numeric.Arithmetic[T],
name string,
params map[string]*graph.Parameter[T],
attrs map[string]interface{},
) (graph.Node[T], error)layers/registry.RegisterAll() is the single entry point that wires all
standard layers (including FFN) into model.RegisterLayer[T]. No layer
package uses init() for registration. The ZMF model loader uses this
registry to reconstruct graphs from serialized specs.
- CUDA Toolkit 12.x (libcudart, development headers)
- cuBLAS library (libcublas)
- NVIDIA GPU with Compute Capability >= 7.0 (Volta/Turing or newer)
- GCC/G++ (for CGO linking)
go build -tags cuda ./...
go test -tags cuda ./...
Compile CUDA kernels:
cd internal/cuda/kernels/
make # default: sm_75 (GCP T4)
make CUDA_ARCH=sm_120 # DGX Spark (Blackwell GB10)
make CUDA_ARCH=sm_70 # V100
This produces libkernels.a from elementwise.cu using nvcc -O2 -arch=$(CUDA_ARCH).
| Category | Operations | Backend |
|---|---|---|
| Matrix | MatMul (2D and batched) | cuBLAS Sgemm |
| Element-wise | Add, Sub, Mul, Div, Pow | Custom CUDA kernels |
| Scalar | AddScalar, MulScalar, DivScalar | Custom CUDA kernels |
| Activation | Tanh, TanhPrime | Custom CUDA kernels |
| Math | Exp, Log, Sqrt, Rsqrt | Custom CUDA kernels |
| Reduction | Sum, ReduceSum, ReduceMean | Custom CUDA kernels (shared memory) |
| Other | Softmax, Fill | Custom CUDA kernels |
These delegate to CPUEngine by design (not compute-bound or require Go runtime):
- UnaryOp (Go function pointers)
- Transpose (metadata-only)
- Zero, Zeros, Copy
- Reshape, Split, Concat, Repeat
- Gather, ScatterAdd (integer indexing)
- OneHot, RandomUniform
GPU operations produce tensors with GPUStorage, keeping data on-device
between chained operations. Only the first input (if CPU-backed) does an H2D
copy, and only the final result (when .Data() is called) does a D2H copy.
CPU Input -> H2D (via pool) -> Kernel -> GPUStorage output
|
GPUStorage input (zero-copy) -> Kernel -> GPUStorage output
|
.Data() -> D2H copy -> CPU slice
Key helpers:
getDevicePtr: GPUStorage returns device pointer directly (zero-copy). CPUStorage allocates from memory pool and copies H2D.makeGPUResult: Creates output tensors with GPUStorage wrapping device pointer.
internal/cuda/mempool.go: Size-bucketed free-list allocator. Reuses
previously freed device memory, avoiding per-operation cudaMalloc/cudaFree.
Mutex-synchronized. Drained on GPUEngine.Close().
Dedicated non-default stream for async kernel execution. All kernels and cuBLAS
operations execute on this stream. Created in NewGPUEngine(), destroyed in
Close().
When cudaMalloc fails (OOM), GPU operations fall back to CPUEngine
transparently. An atomic counter (OOMFallbackCount()) tracks fallback frequency.
cuBLAS operates in column-major order. To compute C = A * B in row-major:
- Observe that for row-major matrices, A_row = A_col^T
- Call cublasSgemm with B as first argument, A as second, swapping m and n
- This avoids explicit transposition and works for any matrix dimensions.
compute/
gpu_engine.go GPUEngine (pool, stream, cuBLAS) (//go:build cuda)
gpu_kernels.go getDevicePtr, makeGPUResult, kernel dispatch (//go:build cuda)
tensor/
storage.go Storage[T] interface, CPUStorage[T], NewWithStorage
gpu_storage.go GPUStorage[T], TrySlice/TrySet (//go:build cuda)
transfer.go ToGPU/ToCPU helpers (//go:build cuda)
device/
cuda_device.go CUDA device abstraction (//go:build cuda)
cuda_allocator.go CUDA memory allocator (//go:build cuda)
internal/cuda/
runtime.go CUDA runtime + Stream bindings (//go:build cuda)
mempool.go Size-bucketed device memory pool (//go:build cuda)
kernels/
elementwise.cu CUDA kernel source (17 kernels, stream-aware)
elementwise.go CGO bindings for kernels (//go:build cuda)
Makefile nvcc compilation
internal/cublas/
cublas.go cuBLAS + SetStream bindings (//go:build cuda)
CGO linker flags:
internal/cuda/runtime.go: -lcudart
internal/cublas/cublas.go: -lcublas
internal/cuda/kernels/*.go: -L${SRCDIR} -lkernels -lcudart -lstdc++
- MatMul: 1e-5 relative error
- Element-wise ops: 1e-6 relative error
- Reductions (Sum, Mean): 1e-5 relative error
| GPU | Arch | CUDA_ARCH | Memory | Platform |
|---|---|---|---|---|
| Tesla T4 | Turing | sm_75 | 16 GB | GCP |
| L4 | Ada Lovelace | sm_89 | 24 GB | GCP |
| Tesla V100 | Volta | sm_70 | 16 GB | GCP |
| A100 | Ampere | sm_80 | 40/80 GB | GCP |
| DGX Spark GB10 | Blackwell | sm_120 | 128 GB unified | Local (ARM64) |
HuggingFace ONNX model
|
v
zonnx (ONNX-to-ZMF converter, separate repo)
|
v
ZMF file (github.com/zerfoo/zmf format)
|
v
model.LoadModelFromZMF[T](engine, ops, path) -> Model[T]{Graph, ZMFVersion}
|
v
graph.Graph[T].Forward(ctx, inputs...)
ZMF (Zerfoo Model Format) is a protobuf-based container storing:
- Graph topology: nodes with op types, input/output edges, attributes
- Parameters: named tensors with shape, dtype, and serialized data
- Metadata: version, source model info
The model package deserializes ZMF files and reconstructs graph.Graph[T]
using the registered LayerBuilder[T] functions from layers/registry.
- Gemma 3: Full text decoder pipeline. Parity test:
tests/parity/gemma3_test.go - SigLIP: Vision encoder (patch embedding + transformer). Parity test:
tests/parity/siglip_test.go - Kimi-VL: Vision-language connector. Parity test:
tests/parity/siglip_test.go
Core operators implemented for model import:
- MatMul, MatMulNBits (4-bit quantized), Conv2d, Dense, Linear, LMHead
- RMSNorm, LayerNorm, SimplifiedLayerNorm, SkipSimplifiedLayerNorm, BatchNorm
- GroupQueryAttention, ScaledDotProductAttention, QKNorm
- RotaryEmbedding, TokenEmbedding
- Gelu, FastGelu, SwiGLU, Sigmoid, Softmax, Erf
- MoEGate, MixtureOfExperts
- Cast, Concat, Reshape, Slice, Unsqueeze, Shape, Pad, Resize, TopK
- Constant, GlobalAvgPool, Gather, ReduceSum, Transpose, Dropout
Zerfoo loads configuration from a JSON file with optional environment variable
overrides. Use config.LoadWithEnv[T](path, prefix) in code, or pass a JSON
file path to the CLI.
Engine configuration:
| Field | JSON key | Env var | Default | Description |
|---|---|---|---|---|
| Device | device | DEVICE | -- | "cpu" or "cuda" |
| MemoryLimitMB | memory_limit_mb | MEMORY_LIMIT_MB | 0 | Max memory in MB (0=unlimited) |
| LogLevel | log_level | LOG_LEVEL | "info" | debug, info, warn, error |
Training configuration:
| Field | JSON key | Env var | Default | Description |
|---|---|---|---|---|
| BatchSize | batch_size | BATCH_SIZE | -- | Training batch size |
| LearningRate | learning_rate | LEARNING_RATE | -- | Learning rate |
| Optimizer | optimizer | OPTIMIZER | -- | "sgd" or "adam" |
| Epochs | epochs | EPOCHS | 0 | Number of epochs |
| CheckpointInterval | checkpoint_interval | CHECKPOINT_INTERVAL | 0 | Steps between checkpoints |
Distributed configuration:
| Field | JSON key | Env var | Default | Description |
|---|---|---|---|---|
| CoordinatorAddress | coordinator_address | COORDINATOR_ADDRESS | -- | Host:port of coordinator |
| TimeoutSeconds | timeout_seconds | TIMEOUT_SECONDS | 0 | RPC timeout in seconds |
| TLSEnabled | tls_enabled | TLS_ENABLED | false | Enable TLS for gRPC |
- Load configuration from file (apply env overrides).
- Create the compute engine (CPU or GPU).
- Set memory limit if configured.
- Register the engine with the shutdown coordinator.
- Start the health check server (default :8081).
- If distributed: start the gRPC server, connect to peers.
- Load the model and begin serving.
The health server exposes:
GET /healthz-- Liveness probe. Returns 200 if the process is alive.GET /readyz-- Readiness probe. Returns 200 if all checks pass, 503 otherwise.GET /debug/pprof/-- pprof index for profiling.
Zerfoo handles SIGINT and SIGTERM for graceful shutdown:
- Signal received.
- Shutdown coordinator triggered.
- Closers called in reverse registration order (distributed strategy disconnects, engine releases resources).
- Root context canceled.
- Process exits.
Structured leveled logging via log.Logger:
- DEBUG: Detailed operation-level information (tensor shapes, timing).
- INFO: Startup, configuration, connection events.
- WARN: Recoverable errors (e.g. OOM fallback from GPU to CPU).
- ERROR: Unrecoverable errors (connection failures, invalid configuration).
Output formats: text or JSON.
Operational instrumentation via metrics/runtime.Collector:
- Counters:
op_count_<OpName>,allreduce_count - Histograms:
op_duration_seconds,allreduce_duration_seconds - Access via
collector.Snapshot()
ML evaluation metrics via metrics.CalculateMetrics(): Pearson, Spearman,
MSE, RMSE, MAE.
For production gRPC, configure distributed.TLSConfig:
tlsCfg := &distributed.TLSConfig{
CACertPath: "/path/to/ca.pem",
CertPath: "/path/to/server.pem",
KeyPath: "/path/to/server-key.pem",
}For mutual TLS (mTLS), provide client cert/key on both sides.
Target: >= 95% statement coverage for all testable packages.
Documented exceptions (unreachable tensor.New error paths):
- layers/gather: 93.1%
- layers/embeddings: 93.5%
- layers/features: 93.8%
- testing/testutils: 94.5%
- Table-driven tests using standard
testingpackage (no testify). - Parity tests comparing GPU vs CPU output for every GPU-accelerated method.
- Model parity tests gated by env vars (GEMMA3_ZMF_PATH, SIGLIP_ZMF_PATH, KIMI_CONNECTOR_ZMF_PATH, LLAMA3_ZMF_PATH, MISTRAL_ZMF_PATH, QWEN25_ZMF_PATH, PHI4_ZMF_PATH, DEEPSEEK_ZMF_PATH).
- Parity tests cover 6 model families: Gemma 3, Llama 3, Mistral, Qwen 2.5, Phi-4, DeepSeek V3.
- Integration tests for cross-package workflows.
- Numerical gradient checking via finite differences.
- MockEngine for unit testing layers in isolation.
| Package | Reason |
|---|---|
| distributed/pb/ | Generated protobuf code |
| cmd/zerfoo/ | Main entrypoint, no testable logic |
| cmd/zerfoo-predict/ | Main entrypoint; logic in cmd/cli/ |
| cmd/zerfoo-tokenize/ | Main entrypoint; logic in pkg/tokenizer/ |
| types/ | Type definitions only |
# 1. Compile CUDA kernels
cd internal/cuda/kernels && make
# 2. Run GPU test suite
go test -tags cuda -count=1 -v \
./compute/ ./tensor/ ./internal/cuda/... ./internal/cublas/... ./device/
# 3. Run parity tests (GPU vs CPU)
go test -tags cuda -run Parity -v ./compute/
Direct (go.mod):
- gonum.org/v1/gonum (BLAS)
- google.golang.org/grpc + protobuf (distributed training)
- github.com/zerfoo/zmf (model format)
- github.com/zerfoo/float16, float8 (custom numeric types)
- github.com/google/go-cmp (test comparisons)
| Target | Purpose |
|---|---|
| test | Full test suite |
| test-coverage | Coverage report (coverage.out) |
| coverage-report | HTML coverage visualization |
| proto | gRPC protobuf compilation |
| format | gofmt + goimports + gofumpt |
| lint | golangci-lint check |
| lint-fix | Auto-fix lint issues |
| check | Full QA pipeline |
| ci | Full CI simulation |
| verify-architecture | Enforce zerfoo/zonnx decoupling |
- Runs
golangci-linton staged packages. - Runs
go test ./.... - Rejects commits touching files in multiple directories.
- Unit tests on push/PR to main (excludes parity tests).
- golangci-lint with 5m timeout.
- Parity tests (currently non-blocking, gated by env vars).
- Nightly toy training pipeline.
"memory limit exceeded": Total tensor allocation exceeds configured
memory_limit_mb. Increase the limit, reduce batch size, or set to 0
(unlimited).
"input tensor cannot be nil": An operation received a nil tensor. Verify
all tensors are initialized with tensor.New and check prior operations for
errors.
"invalid shapes for matrix multiplication": Inner dimensions incompatible
for MatMul. For A @ B, A must be [..., m, k] and B must be [..., k, n]. Use
Reshape to fix shape mismatches.
"context canceled" / "context deadline exceeded": Operation took too long or parent context was canceled. Increase the timeout, or note that this is expected during SIGTERM shutdown.
"CUDA not found" / build fails with cuda tag: Install CUDA Toolkit 12.x.
Ensure nvcc is in PATH. Set CUDA_HOME if non-standard location.
GPU OOM: Operations fail with CUDA allocation errors or log "GPU OOM
fallback to CPU" at WARN level. Reduce batch size, lower memory_limit_mb,
or monitor with nvidia-smi -l 1.
CUDA driver version mismatch: Check nvidia-smi for driver version and
nvcc --version for CUDA version. Refer to NVIDIA CUDA compatibility matrix.
"connection refused": Verify coordinator is running on the configured address. Check firewall for port 50051/TCP. For TLS: verify certificates are valid.
"transport: authentication handshake failed": TLS certificate mismatch.
Verify both sides use the same CA. Check certificate expiry with
openssl x509 -in cert.pem -noout -dates.
Timeout during Barrier/AllReduce: Check all workers are running. Increase
timeout_seconds. Check for network latency or straggler workers.
# CPU profile (30 seconds)
go tool pprof http://localhost:8081/debug/pprof/profile?seconds=30
# Heap profile
go tool pprof http://localhost:8081/debug/pprof/heap
# Goroutine dump
curl http://localhost:8081/debug/pprof/goroutine?debug=2
- float32 only for GPU -- other types fall back to CPU transparently.
- No broadcasting in GPU kernels -- broadcast cases fall back to CPU.
- Single GPU -- no multi-GPU or distributed GPU support.
- No cuDNN -- all kernels are custom CUDA.
- No mixed precision -- full float32 throughout.
- Default device -- always uses cuda:0, no device selection API.
- Hardware validation pending -- GCP GPU quota request pending.
- float16/float8 GEMM upcasts to float32 -- no native half-precision kernels.
- Generics wiring hardcodes float32 -- registry, worker node, CLI all use float32.
- Embeddings not yet supported -- inference.Embed returns an error (no hidden state access).
- KV cache is optional -- not all graph architectures support it.
- zmf (
github.com/zerfoo/zmf): Zerfoo Model Format protobuf library. - zonnx (
github.com/zerfoo/zonnx): ONNX-to-ZMF converter with per-operator builders. - float16 (
github.com/zerfoo/float16): IEEE 754 float16 and bfloat16 types for Go. - float8 (
github.com/zerfoo/float8): E4M3 float8 type for Go. - gemma3 (
github.com/zerfoo/gemma3): Gemma 3 model support and conversion scripts.
The inference pipeline provides an embeddable Go-native API for model loading and text generation.
Loading: inference.Load(modelID, opts...) resolves a model via registry.ModelRegistry, reads config.json (metadata), tokenizer.json (BPE tokenizer), and model.zmf (weights), then wires a generate.Generator[float32] with a graph.Graph[float32] and compute.CPUEngine[float32].
Generation: generate.Generator.Generate(ctx, prompt, config) runs the autoregressive loop:
- Encode prompt via BPE tokenizer
- Prefill: single forward pass through the full graph
- Decode: loop picking one token at a time using the sampling pipeline
- Sampling: temperature scaling, top-k filtering, top-p (nucleus) sampling, repetition penalty, then softmax + weighted random (or argmax at temperature=0)
- Stop on EOS token, stop string, or max tokens
Streaming: generate.Generator.GenerateStream(ctx, prompt, config, handler) delivers tokens incrementally via generate.TokenStream.OnToken(token, done).
KV Cache: generate.KVCache[T] stores per-layer key/value tensors, passed via context (generate.WithKVCache/generate.GetKVCache). Avoids recomputing attention over prior tokens during autoregressive decode.
Serve: serve.NewServer(model).Handler() returns an http.Handler implementing:
POST /v1/chat/completions-- OpenAI chat completion (non-streaming and SSE)POST /v1/completions-- OpenAI text completion (non-streaming and SSE)GET /v1/models-- model listing
CLI Commands:
zerfoo pull <model-id>-- download and cache a model via registryzerfoo run <model-id>-- interactive chat with streaming outputzerfoo serve --port <port>-- start OpenAI-compatible HTTP server
HuggingFace model (ONNX/SafeTensors)
|
v (zonnx converter)
ZMF file + config.json + tokenizer.json
|
v (registry.Pull / inference.Load)
inference.Model
|
+-- Generate(ctx, prompt) -> string
+-- GenerateStream(ctx, prompt, handler) -> error
+-- Chat(ctx, messages) -> Response
|
v (serve.NewServer)
OpenAI-compatible HTTP API
Phase 9 extends the inference pipeline to support multiple model architectures beyond Gemma 3. Each architecture has distinct attention, normalization, and routing patterns that are handled through the config registry and layer builders.
| Model Family | Attention | RoPE Variant | Normalization | MoE | Config Parser |
|---|---|---|---|---|---|
| Gemma 3 | GQA | Standard | RMSNorm | No | gemma3 |
| LLaMA 3 | GQA | Standard | RMSNorm | No | llama |
| Mistral | GQA | Standard | RMSNorm | No | mistral |
| Qwen 2.5 | GQA | YaRN scaling | RMSNorm | No | qwen2 |
| Phi-4 | GQA | Partial (0.75) | LayerNorm | No | phi |
| DeepSeek V3 | MLA | Standard | RMSNorm | Shared expert | deepseek |
YaRN Scaling (Qwen 2.5): embeddings.WithYaRNScaling(factor, origMaxLen)
classifies frequency bands into low/medium/high and applies differential scaling
to extend context beyond the original training length.
Partial RoPE (Phi-4): embeddings.WithRotaryDimFraction(fraction) rotates
only a fraction of head dimensions, leaving the rest as pass-through. Phi-4
uses fraction=0.75.
Multi-head Latent Attention (DeepSeek V3): attention.MultiHeadLatentAttention
compresses KV into a low-rank latent via down-projection (W_DKV), then
up-projects to keys (W_UK) and values (W_UV), reducing KV cache size.
Shared Expert MoE (DeepSeek V3): core.MixtureOfExperts.SharedExpert runs
one expert on every token and adds its output to the weighted routed sum.
Tied Embeddings (Phi-4, Gemma 3): core.NewTiedLMHead reuses the token
embedding weight matrix (transposed) as the output projection, halving the
parameter count for the LM head.
inference.ConfigRegistry maps model family names to config parsers that extract
ModelMetadata from config.json. Each parser reads architecture-specific fields
(e.g., rope_scaling, partial_rotary_factor, n_shared_experts) and maps them
to the common metadata struct. Global attributes (rope scaling, partial rotation)
are injected via model.WithGlobalAttributes during graph construction.
model.ParamResolver maps architecture-specific weight names (e.g., Llama's
q_proj.weight vs DeepSeek's kv_a_proj.weight) to canonical names used by
Zerfoo layer builders. Called as a fallback during model building when exact
parameter names are not found. See ADR-005.
Stable design decisions extracted from the implementation plan into self-contained
ADR files in docs/adr/.
| ADR | Title | Phase | Key Decision |
|---|---|---|---|
| 001 | Enterprise Production Readiness | 4+7 | Logging, metrics, config, health, CI gates, dead code removal, graph thread safety |
| 002 | Distributed Training Protocol | 5 | Star-topology AllReduce, counter-based Barrier, WorkerNode lifecycle |
| 003 | Open Weights Model Import | 6 | 4-bit weights, Conv2d strategy, MoE design, 13 new operators |
| 004 | Embeddable Inference Library | 8 | BPE tokenizer, KV cache, generation loop, sampling, streaming, serve |
| 005 | Multi-Architecture Support | 9 | Config registry, param resolver, YaRN, partial RoPE, MLA, shared MoE |
| 006 | GPU Engine Architecture | 2-3 | CUDA float32, memory pool, cuBLAS row-major, OOM fallback, parity tolerances |