https://github.com/magomedcoder/gguf.go
Запуск ML-моделей в формате GGUF на чистом Go
https://github.com/magomedcoder/gguf.go
gguf gguf-model gguf-model-support gguf-models go golang
Last synced: 25 days ago
JSON representation
Запуск ML-моделей в формате GGUF на чистом Go
- Host: GitHub
- URL: https://github.com/magomedcoder/gguf.go
- Owner: magomedcoder
- Created: 2026-06-07T19:58:57.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-06-17T21:42:57.000Z (about 1 month ago)
- Last Synced: 2026-06-17T23:21:44.334Z (about 1 month ago)
- Topics: gguf, gguf-model, gguf-model-support, gguf-models, go, golang
- Language: Go
- Homepage:
- Size: 123 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# gguf.go - запуск ML-моделей в формате **GGUF** на чистом **Go**.
> **Ранний этап разработки.**
**gguf.go** - лёгковесный способ запуска GGUF-моделей на языке Go без llama.cpp.
Формат **GGUF** используется в экосистеме llama.cpp.
[GGUF-FORMAT.md](GGUF-FORMAT.md) - описание формата GGUF
Проект можно использовать как **библиотеку** (inference из Go-кода) и как **HTTP-сервер** (`gguf serve` или пакет `server`).
**Внешних Go-зависимостей нет** - только стандартная библиотека.
Опционально: **CUDA** через Driver API (`libcuda.so`, сборка `-tags cuda`, CGO).
## Что уже работает
- парсинг GGUF v2/v3 (`info`, `inspect`), memory-map (`LoadMapped`, zero-copy `RawView`);
- деквантизация и matmul: Q8_0, Q4_0, Q4_K;
- базовые ops: RoPE, RMSNorm, GQA attention, SwiGLU;
- forward pass **Qwen3** + KV-cache;
- tokenizer BPE из метаданных GGUF;
- chat template ChatML/Qwen (`--chat`, `--thinking`, `FormatChatUser`);
- генерация текста: `gguf run` (prefill + greedy / temperature / top-k / top-p);
- HTTP-сервер: `gguf serve` (`/generate`, `/models`, JSON + SSE streaming);
- **CUDA offload** (`-ngl N`): matmul первых N transformer-слоёв на GPU (сборка `-tags cuda`).
---
На текущем этапе для разработки и тестирования используется Qwen3-0.6B-Q8_0.gguf
```bash
mkdir -p models
curl -L -o models/Qwen3-0.6B-Q8_0.gguf https://huggingface.co/Qwen/Qwen3-0.6B-GGUF/resolve/main/Qwen3-0.6B-Q8_0.gguf
```
---
## Сборка
### Локально (Go 1.26)
```bash
go build -o build/gguf ./cmd/gguf
```
Без CGO - кросс-компиляция на любую платформу
### CUDA (NVIDIA GPU, опционально)
Требуется драйвер NVIDIA (`libcuda.so`) и CGO.
CUDA Toolkit не нужен - используется Driver API через `dlopen`.
```bash
CGO_ENABLED=1 go build -tags cuda -o build/gguf ./cmd/gguf
```
Проверка GPU matmul:
```bash
CGO_ENABLED=1 go test -tags=cuda ./pkg/gpu/cuda/...
```
`-ngl N` - matmul первых N transformer-слоёв на GPU (макс. `block_count` из `gguf inspect`; Qwen3-0.6B - 28).
Сейчас на GPU только matmul: Q8_0 без деквантизации в FP32, остальные типы - через FP32. Attention, norm и RoPE - на CPU. Q8_0 kernel требует GPU sm_70+.
Без `-tags cuda` при `-ngl > 0` будет ошибка `gpu: CUDA недоступна`.
### Через Docker
Multi-stage `Dockerfile`: CPU-кросс-компиляция по умолчанию, отдельные target'ы для CUDA.
**CPU (по умолчанию)** - все платформы, `CGO_ENABLED=0`:
```bash
docker build -t gguf-build .
docker run --rm -v "$(pwd)/build:/out" gguf-build
```
**CUDA** - только `linux-amd64/gguf-cuda`:
```bash
docker build --target cuda -t gguf-cuda .
docker run --rm -v "$(pwd)/build:/out" gguf-cuda
```
**CPU + CUDA**:
```bash
docker build --target release -t gguf-release .
docker run --rm -v "$(pwd)/build:/out" gguf-release
```
| Target / режим | Результат |
|----------------|-------------------------------|
| *(default)* | CPU-бинарники всех платформ |
| `cuda` | `build/linux-amd64/gguf-cuda` |
| `release` | CPU + `gguf-cuda` |
| Платформа | CPU-бинарник | CUDA-бинарник (target `cuda` / `release`) |
|---------------|--------------------------------|-------------------------------------------|
| Linux amd64 | `build/linux-amd64/gguf` | `build/linux-amd64/gguf-cuda` |
| Linux arm64 | `build/linux-arm64/gguf` | - |
| Windows amd64 | `build/windows-amd64/gguf.exe` | - |
| Windows arm64 | `build/windows-arm64/gguf.exe` | - |
| macOS amd64 | `build/darwin-amd64/gguf` | - |
| macOS arm64 | `build/darwin-arm64/gguf` | - |
> **Примечание.** Путь к бинарнику зависит от способа сборки:
> - локально CPU: `./build/gguf`
> - локально CUDA: `./build/gguf` (с `-tags cuda`)
> - Docker CPU: `./build/-/gguf` (на Windows: `gguf.exe`)
> - Docker CUDA: `./build/linux-amd64/gguf-cuda`
---
## CLI
### `gguf info`
Краткая сводка о модели: версия GGUF, архитектура, имя, число тензоров, размер весов, длина контекста.
```bash
./build/gguf info -m ./models/Qwen3-0.6B-Q8_0.gguf
```
| Флаг | Описание |
|------|----------------------|
| `-m` | путь к файлу `.gguf` |
### `gguf inspect`
Полный дамп метаданных и списка тензоров (имя, тип, размерности, размер в байтах).
```bash
./build/gguf inspect ./models/Qwen3-0.6B-Q8_0.gguf
```
Аргумент - путь к файлу, без флагов.
### `gguf run`
Генерация текста: prefill промпта -> autoregressive decode -> вывод в stdout.
```bash
./build/gguf run -m ./models/Qwen3-0.6B-Q8_0.gguf --chat -p "Привет" -n 64
```
| Флаг | По умолчанию | Описание |
|--------------|--------------|-------------------------------------------------|
| `-m` | - | путь к файлу `.gguf` |
| `-p` | - | текст промпта |
| `-n` | `128` | максимум новых токенов |
| `--temp` | `0` | температура sampling (`0` = greedy) |
| `--top-k` | `0` | top-k (`0` = выключено) |
| `--top-p` | `1` | nucleus sampling (`1` = выключено) |
| `--seed` | `0` | seed PRNG |
| `--chat` | `false` | обернуть промпт в ChatML/Qwen template |
| `--thinking` | `false` | режим размышления Qwen3 (с `--chat`) |
| `-ngl` | `0` | matmul N transformer-слоёв на GPU (CUDA-сборка) |
Для **Qwen3 Instruct** используйте `--chat`, иначе модель ответит некорректно.
Пример с sampling:
```bash
./build/gguf run -m ./models/Qwen3-0.6B-Q8_0.gguf --chat -p "Привет" -n 64 --temp 0.7 --top-k 40 --top-p 0.9 --seed 42
```
С размышлением:
```bash
./build/gguf run -m ./models/Qwen3-0.6B-Q8_0.gguf --chat --thinking -p "Привет" -n 64
```
С GPU offload (28 слоёв, CUDA-сборка):
```bash
./build/gguf run -m ./models/Qwen3-0.6B-Q8_0.gguf --chat -p "Привет" -ngl 28
```
### `gguf serve`
HTTP-сервер для генерации текста по API.
Graceful shutdown по `Ctrl+C` (SIGINT/SIGTERM).
```bash
./build/gguf serve -m ./models/Qwen3-0.6B-Q8_0.gguf --addr 127.0.0.1:8000
```
| Флаг | По умолчанию | Описание |
|----------|------------------|-------------------------------------------------|
| `-m` | - | путь к файлу `.gguf` |
| `--addr` | `127.0.0.1:8000` | адрес HTTP-сервера |
| `-ngl` | `0` | matmul N transformer-слоёв на GPU (CUDA-сборка) |
#### API
| Метод | Путь | Описание |
|-------|------------------------|---------------------------------|
| GET | `/models` | метаданные загруженной модели |
| POST | `/generate` | генерация текста (JSON или SSE) |
| POST | `/completion` | алиас `/generate` |
| POST | `/v1/chat/completions` | chat API (messages + stream) |
Тело `POST /generate` (`Content-Type: application/json`):
| Поле | Тип | По умолчанию | Описание |
|---------------|--------|--------------|-----------------------------|
| `prompt` | string | - | текст запроса (обязательно) |
| `max_tokens` | int | `128` | максимум новых токенов |
| `temperature` | float | `0` | `0` = greedy |
| `top_k` | int | `0` | top-k sampling |
| `top_p` | float | `1` | nucleus sampling |
| `seed` | uint | `0` | seed PRNG |
| `chat` | bool | `false` | ChatML/Qwen template |
| `stream` | bool | `false` | SSE streaming |
| `system` | string | - | system prompt (с `chat`) |
| `thinking` | bool | `false` | режим размышления Qwen3 |
Ответ без stream:
```json
{
"text": "...",
"tokens": 32
}
```
Streaming (SSE) - события `data: {"token":"..."}` и в конце `data: {"done":true}`.
Примеры:
```bash
curl -s localhost:8000/generate -H 'Content-Type: application/json' -d '{"prompt":"Привет","chat":true, "max_tokens":32}'
curl -N localhost:8000/generate -H 'Content-Type: application/json' -d '{"prompt":"Привет","chat":true,"stream":true,"max_tokens":32}'
curl -s localhost:8000/models
curl -s localhost:8000/v1/chat/completions -H 'Content-Type: application/json' -d '{"messages":[{"role":"user","content":"Привет"}],"max_tokens":32}'
```
Пример ответа `/v1/chat/completions`:
```json
{
"object": "chat.completion",
"choices": [
{
"message": {
"role": "assistant",
"content": "..."
}
}
],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 5,
"total_tokens": 15
}
}
```
---
## Утилиты для отладки
### `debugtok`
Проверяет encode промпта и logits после prefill: top-5 токенов и greedy-следующий.
```bash
go run ./cmd/debugtok ./models/Qwen3-0.6B-Q8_0.gguf "Hello"
```
### `vocab`
Показывает конфиг Qwen3 (`head_dim`, число heads) и ID special tokens в словаре.
```bash
go run ./cmd/vocab ./models/Qwen3-0.6B-Q8_0.gguf
```
---
## Использование как библиотеки
### Inference
```go
import "github.com/magomedcoder/gguf.go"
engine, err := gguf.Load("./models/Qwen3-0.6B-Q8_0.gguf", gguf.LoadOptions{
NGL: 0, // matmul N слоёв на GPU; <= block_count, нужна CUDA-сборка
})
ctx, err := engine.NewContext()
prompt, err := gguf.FormatChatUser("Привет", gguf.ChatOptions{
Metadata: engine.Metadata(),
})
text, err := ctx.Generate(prompt, gguf.GenerateParams{
MaxTokens: 128,
Sampler: gguf.Greedy,
})
```
### Пошаговый decode
```go
sess, err := ctx.StartGeneration(prompt)
for i := 0; i < maxTokens; i++ {
id, err := sess.DecodeStep(gguf.Greedy)
if id < 0 {
break
}
fmt.Print(sess.DecodeToken(id))
}
```
### Sampling с temperature / top-k / top-p
```go
sampler := gguf.NewSampler(gguf.SamplerConfig{
Temp: 0.7,
TopK: 40,
TopP: 0.9,
Seed: 42,
})
text, err := ctx.Generate(prompt, gguf.GenerateParams{
MaxTokens: 64,
Sampler: sampler,
})
```
### Загрузка через mmap (zero-copy веса)
```go
engine, err := gguf.LoadMapped("./models/Qwen3-0.6B-Q8_0.gguf", gguf.LoadOptions{NGL: 0})
```
### GPU offload из кода
```go
engine, err := gguf.Load("./models/Qwen3-0.6B-Q8_0.gguf", gguf.LoadOptions{
NGL: 28, // нужна сборка -tags cuda
})
```
### Парсинг GGUF без inference
```go
import "github.com/magomedcoder/gguf.go"
r, err := gguf.OpenFile("./models/Qwen3-0.6B-Q8_0.gguf")
arch, _ := r.Metadata.String("general.architecture")
```