How knights-analytics/hugot works
1. What this repo is
Hugot is a Go-first inference and training library for Hugging Face-style transformer models exported to ONNX. The repository is structured as a reusable library, with backend selection and pipeline behavior controlled at build time and at session creation time.
2. Public entry point and session lifecycle
The root hugot.go plus backend-specific variants (hugot_go.go, hugot_ort.go, hugot_xla.go, and disabled stubs) form the main API surface. A caller creates a Session, attaches one or more pipelines to it, and later destroys the session to release model/runtime resources.
3. Backend boundary is the key architecture seam
The project supports three execution paths:
- pure Go / GoMLX,
- ONNX Runtime,
- OpenXLA.
These are separated with build tags and paired *_disabled.go files so unsupported backends compile cleanly when omitted. This boundary affects everything from model loading to tokenizer choice and whether training is available.
4. Pipeline layer is where user tasks are modeled
The pipelines/ package contains task-specific orchestration for:
- text classification,
- feature extraction,
- token classification,
- question answering,
- zero-shot classification,
- cross-encoder,
- text generation,
- image classification,
- object detection,
- tabular prediction.
Each pipeline is responsible for shaping inputs, invoking the backend model, and transforming raw outputs into task-specific result structs.
5. Execution flow: preprocess → model → postprocess
The common inference flow is: input data enters a pipeline, the tokenizer converts text to model-ready tensors, the backend model executes ONNX inference, and pipeline-specific postprocessing converts scores/logits/boxes/tokens into domain outputs. backends/pipeline.go, backends/tokenizer.go, and backends/model.go define the shared abstractions behind that flow.
6. Tokenizer and model implementations are backend-specific
Tokenization is split between Go and Rust-backed implementations (tokenizer_go.go, tokenizer_rust.go) because ORT/XLA require the Rust tokenizer artifact, while the Go backend can stay cgo-free. Model execution is split across model_ort.go, model_gomlx.go, and their disabled counterparts. This is an important dependency boundary because tokenizer/runtime choice is coupled to the selected backend.
7. Generative and training paths are specialized
Generative inference is handled separately in backends/generative.go and is currently ORT-focused, specifically for text generation. Training/fine-tuning is isolated in hugot_training*.go and backends/model_gomlx.go, with XLA/GoMLX used for converting and training models before serializing back to ONNX. In practice, training support is narrower than inference support and is limited to feature extraction/semantic similarity workflows.
8. Data and model acquisition
downloader.go plus the filesystem abstraction in util/fileutil manage loading model artifacts from local disk or external filesystems. The README notes support for the viant/afs abstraction, which means the model source is not hardwired to POSIX files; object stores like S3 can be plugged in through filesystem adapters.
9. Supporting utilities are small but domain-specific
The util/ subpackages handle common data transformations that multiple pipelines need:
- imageutil for image loading/normalization,
- vectorutil for embedding/vector operations,
- safeconv for defensive numeric conversions,
- fileutil for path/file handling.
These utilities sit below pipelines and above the backend layer.
10. Datasets and test fixtures support training and evaluation
datasets/ contains abstractions for dataset loading and XLA-compatible dataset logic. testcases/ holds embedded JSONL and vector fixtures for semantic similarity, token classification, and classification outputs. These fixtures mirror the repo’s core workflows and are used to validate end-to-end pipeline behavior.
11. Test matrix reflects backend fragmentation
The tests/ tree is split by backend (go, ort, xla, training), which mirrors the architecture rather than the package layout. This indicates the main system boundary is not by feature alone, but by runtime/backend capability.
12. Build, packaging, and runtime dependencies are part of the architecture
The repository includes multiple Dockerfiles, compose files, and GitHub Actions workflows for building, testing, and releasing across backend combinations. External runtime dependencies materially affect the architecture: ONNX Runtime shared libraries, GoMLX/OpenXLA, Rust tokenizers, and CUDA-capable accelerators are all first-class deployment concerns, not optional extras.
13. Architectural takeaway
The repo is best diagrammed as a layered system: API/session entry at the top, task pipelines in the middle, shared preprocess/model/postprocess abstractions underneath, and pluggable execution backends plus filesystem/model-loading infrastructure at the bottom. The most important boundaries are backend selection, tokenizer implementation, and the separation between inference pipelines and the narrower training/generative flows.
Main entry points: model.go Model API · pipeline.go Pipeline Core · textClassification.go Text Pipelines · imageClassification.go Vision Pipelines
Components 23
API & session
Public entry points and lifecycle management
- Hugot API · Root package entry point and session lifecycle API
hugot.go - Backend API · Backend-specific root variants for Go, ORT, and XLA
hugot_go.go - Session · Creates, attaches pipelines to, and destroys runtime resources
hugot.go - Training API · Training and fine-tuning entry points
hugot_training.go
Pipelines
Task-specific inference orchestration
- Pipeline Core · Shared pipeline behavior and task wiring
backends/pipeline.go - Text Pipelines · Classification, extraction, QA, zero-shot, cross-encoder, generation, and token labeling
pipelines/textClassification.go - Vision Pipelines · Image classification and object detection orchestration
pipelines/imageClassification.go - Tabular Pipeline · Tabular prediction orchestration
pipelines/tabular.go
Backends
Shared abstractions and execution implementations
- Tokenizer API · Tokenizer interface and shared tokenization flow
backends/tokenizer.go - Go Tokenizer · Pure Go tokenization path for cgo-free execution
backends/tokenizer_go.go - Rust Tokenizer · Rust-backed tokenizer path used by ORT and XLA
backends/tokenizer_rust.go - Model API · Model loading and inference interface
backends/model.go - ORT Model · ONNX Runtime execution path
backends/model_ort.go - GoMLX Model · GoMLX/OpenXLA execution and training path
backends/model_gomlx.go - Generation Flow · Generative inference path for text generation
backends/generative.go
Data & loading
Model acquisition, datasets, and filesystem access
- Downloader · Loads model artifacts from local or external filesystems
downloader.go - File Access · Filesystem and path abstraction used by model loading
util/fileutil/file.go - Datasets · Dataset loading and XLA-compatible dataset logic
datasets/dataset.go
Support
Utilities, fixtures, and test scaffolding
- Image Utils · Image loading and normalization helpers
util/imageutil/images.go - Vector Ops · Embedding and vector helper functions
util/vectorutil/vectors.go - Fixtures · Embedded JSONL and vector fixtures for end-to-end validation
testcases/embedded/embedded.go - Test Matrix · Backend-specific tests for Go, ORT, XLA, and training
tests/go/hugot_go_test.go
Build & CI
Container, workflow, and release tooling
- CI Builds · Dockerfiles, compose files, and GitHub Actions workflows
.github/workflows/test.yaml
Connections 24
- Hugot API → Session: creates · Public API creates and manages session lifecycle
- Backend API → Hugot API: variants · Build-tagged root packages expose backend-specific API surfaces
- Session → Pipeline Core: attaches · Sessions host one or more pipelines
- Pipeline Core → Text Pipelines: specializes · Shared pipeline layer is specialized into task pipelines
- Pipeline Core → Vision Pipelines: specializes · Shared pipeline layer is specialized into task pipelines
- Pipeline Core → Tabular Pipeline: specializes · Shared pipeline layer is specialized into task pipelines
- Text Pipelines → Tokenizer API: tokenizes via · Text pipelines depend on tokenizer abstraction
- Text Pipelines → Model API: invokes · Text pipelines execute models through the shared model interface
- Vision Pipelines → Image Utils: preprocesses · Vision pipelines rely on image utilities
- Vision Pipelines → Model API: invokes · Vision pipelines execute models through the shared model interface
- Tabular Pipeline → Model API: invokes · Tabular pipelines execute models through the shared model interface
- Tokenizer API → Go Tokenizer: implemented by · Pure Go tokenizer implementation
- Tokenizer API → Rust Tokenizer: implemented by · Rust-backed tokenizer implementation
- Model API → ORT Model: implemented by · ONNX Runtime backend implementation
- Model API → GoMLX Model: implemented by · GoMLX/OpenXLA backend implementation
- Model API → Downloader: loads from · Models are acquired through downloader logic
- Downloader → File Access: uses · Downloader relies on filesystem/file abstraction
- Training API → Datasets: consumes · Training flows use dataset abstractions
- Training API → GoMLX Model: trains with · Training is centered on the GoMLX/OpenXLA path
- Generation Flow → ORT Model: uses · Generative inference is ORT-focused
- Text Pipelines → Vector Ops: uses · Semantic similarity and embedding flows use vector helpers
- Test Matrix → Fixtures: validates against · Repository tests exercise embedded fixtures
- Test Matrix → Pipeline Core: covers · Backend-specific tests validate pipeline orchestration
- CI Builds → Test Matrix: runs · Build and workflow automation executes the backend test matrix