vipera-ai 1.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vipera_ai-1.2.0/.gitignore +50 -0
- vipera_ai-1.2.0/CHANGELOG.md +87 -0
- vipera_ai-1.2.0/LICENSE +21 -0
- vipera_ai-1.2.0/PKG-INFO +458 -0
- vipera_ai-1.2.0/README.md +411 -0
- vipera_ai-1.2.0/docs/README.md +52 -0
- vipera_ai-1.2.0/pyproject.toml +98 -0
- vipera_ai-1.2.0/src/vipera/__init__.py +1 -0
- vipera_ai-1.2.0/src/vipera/cli.py +121 -0
- vipera_ai-1.2.0/src/vipera/commands/__init__.py +1 -0
- vipera_ai-1.2.0/src/vipera/commands/bench_cmd.py +196 -0
- vipera_ai-1.2.0/src/vipera/commands/chat_cmd.py +125 -0
- vipera_ai-1.2.0/src/vipera/commands/model_cmd.py +723 -0
- vipera_ai-1.2.0/src/vipera/commands/serve_cmd.py +300 -0
- vipera_ai-1.2.0/src/vipera/commands/system_cmd.py +156 -0
- vipera_ai-1.2.0/src/vipera/config.py +102 -0
- vipera_ai-1.2.0/src/vipera/engine.py +151 -0
- vipera_ai-1.2.0/src/vipera/gguf_analyzer.py +309 -0
- vipera_ai-1.2.0/src/vipera/hardware.py +63 -0
- vipera_ai-1.2.0/src/vipera/optimizer.py +287 -0
- vipera_ai-1.2.0/src/vipera/planner.py +188 -0
- vipera_ai-1.2.0/src/vipera/py.typed +0 -0
- vipera_ai-1.2.0/src/vipera/registry.py +50 -0
- vipera_ai-1.2.0/src/vipera/server.py +197 -0
- vipera_ai-1.2.0/src/vipera/ui.py +310 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
*.egg-info/
|
|
7
|
+
.eggs/
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
ENV/
|
|
13
|
+
.env
|
|
14
|
+
.pytest_cache/
|
|
15
|
+
.mypy_cache/
|
|
16
|
+
.ruff_cache/
|
|
17
|
+
.coverage
|
|
18
|
+
.coverage.*
|
|
19
|
+
htmlcov/
|
|
20
|
+
coverage.xml
|
|
21
|
+
|
|
22
|
+
# Models & artifacts
|
|
23
|
+
*.gguf
|
|
24
|
+
*.safetensors
|
|
25
|
+
*.bin
|
|
26
|
+
*.pt
|
|
27
|
+
*.pth
|
|
28
|
+
models/
|
|
29
|
+
out/
|
|
30
|
+
*.vipera.json
|
|
31
|
+
|
|
32
|
+
# Secrets
|
|
33
|
+
.env
|
|
34
|
+
.env.*
|
|
35
|
+
*.pem
|
|
36
|
+
*.key
|
|
37
|
+
secrets/
|
|
38
|
+
*.log
|
|
39
|
+
|
|
40
|
+
# OS / IDE
|
|
41
|
+
.DS_Store
|
|
42
|
+
Thumbs.db
|
|
43
|
+
.idea/
|
|
44
|
+
.vscode/
|
|
45
|
+
*.swp
|
|
46
|
+
|
|
47
|
+
# Tooling
|
|
48
|
+
demo.cast
|
|
49
|
+
bench/results/
|
|
50
|
+
.git.backup.*
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.2.0] - 2026-10-20
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- **Planner budgets VRAM from real per-layer sizes** — registering a model now
|
|
14
|
+
stores `layer_bytes` (mean size of one repeating `blk.N` block, read from the
|
|
15
|
+
GGUF index). `size_bytes / n_layers` charged the ~1 GB of `token_embd` +
|
|
16
|
+
`output.weight` to every layer, so a 14B/Q4 model on a 4 GB card now offloads
|
|
17
|
+
one or two more layers at the same VRAM headroom (11 vs 9 at 8K with 3.7 GB
|
|
18
|
+
free).
|
|
19
|
+
- **VRAM plan keeps room for GPU compute buffers** — the planner reserves
|
|
20
|
+
0.3 GB on top of the 1 GB driver reserve. Sizing to the last byte of VRAM
|
|
21
|
+
produced a plan llama.cpp cannot create a context for, and the engine's only
|
|
22
|
+
fallback is CPU-only: on the GTX 1650, 11 offloaded layers load and 12 fail.
|
|
23
|
+
- **Prompt-prefix reuse is now guaranteed by the dependency floor** —
|
|
24
|
+
`llama-cpp-python` is pinned to `>=0.3.36`, the release that keeps the KV
|
|
25
|
+
cache for a shared prompt prefix. On 0.5B, a 1230-token prompt cost 1.5 s
|
|
26
|
+
cold and 0.18 s for the +19-token follow-up turn.
|
|
27
|
+
- **`vipera bench` reports the engine's actual placement** — a GPU load that
|
|
28
|
+
fell back to CPU no longer prints the planned layer count (16K/f16 on a
|
|
29
|
+
4 GB card falls back and now shows 0/40 rather than 9/40).
|
|
30
|
+
|
|
31
|
+
## [1.1.0] - 2026-10-20
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- **Tool calling** — `/v1/chat/completions` accepts `tools` and `tool_choice`,
|
|
35
|
+
forwards `tool_calls` back in both streaming and non-streaming responses, and
|
|
36
|
+
accepts assistant tool-call turns plus `tool` results with `tool_call_id`.
|
|
37
|
+
- **Full OpenAI sampling pass-through** — `stop`, `seed`, `presence_penalty`,
|
|
38
|
+
`frequency_penalty`, `top_k`, `min_p`, `logit_bias`, `logprobs`,
|
|
39
|
+
`top_logprobs`, `response_format`, and `stream_options.include_usage` now
|
|
40
|
+
reach `llama.cpp` instead of being dropped by the request model.
|
|
41
|
+
- **GGUF inspection** — `vipera model inspect <path> [--tensors]` reads the GGUF
|
|
42
|
+
index only and reports architecture, per-layer/category size distribution,
|
|
43
|
+
quantization mix, and warnings for quality-sensitive tensors quantized below
|
|
44
|
+
Q4_K. Backed by `vipera.gguf_analyzer.analyze_gguf()`.
|
|
45
|
+
- **GGUF optimization** — `vipera model imatrix`, `model quantize`, and
|
|
46
|
+
`model optimize` wrap `llama-imatrix` / `llama-quantize` with imatrix support,
|
|
47
|
+
MoE `--prior-weight`, layer pruning, target-size quant selection, binary
|
|
48
|
+
discovery (`--bin-dir`, `VIPERA_LLAMA_BIN`, `PATH`), dry runs, and partial
|
|
49
|
+
output cleanup. Planning helpers live in `vipera.optimizer`.
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
- **Faster readiness** — `serve start` no longer runs the 20-token speed check
|
|
53
|
+
before binding, and the fixed 1.2 s boot animation is gone. The check is now
|
|
54
|
+
opt-in via `vipera serve start --bench`; the `show_bench_on_start` config key
|
|
55
|
+
was removed.
|
|
56
|
+
|
|
57
|
+
## [1.0.0] - 2026-10-17
|
|
58
|
+
|
|
59
|
+
First public release.
|
|
60
|
+
|
|
61
|
+
### Added
|
|
62
|
+
- **OpenAI-compatible HTTP gateway** — `/v1/chat/completions`,
|
|
63
|
+
`/v1/models`, `/health`. Real SSE streaming.
|
|
64
|
+
- **Hardware-aware planner** — reads CPU cores, RAM, GPU name, VRAM and emits
|
|
65
|
+
a `Plan` (`gpu_layers`, `context`, `kv_cache`, `flash_attn`, `n_batch`,
|
|
66
|
+
`use_mlock`, `offload_kqv`).
|
|
67
|
+
- **Engine fallback** — retries with a CPU-safe plan when GPU offload fails at
|
|
68
|
+
load; retries without `mlock` when `mlock` is restricted.
|
|
69
|
+
- **Model manager** — `model search` (HuggingFace), `model import`,
|
|
70
|
+
`model list`, `model rm`. Auto-sets the imported model as default.
|
|
71
|
+
- **CLI chat** — interactive `chat repl` and scriptable `chat oneshot`.
|
|
72
|
+
- **Benchmark** — `bench run` measures prefill (TTFT-based) and generation
|
|
73
|
+
throughput across context sizes.
|
|
74
|
+
- **Diagnostics** — `system doctor` for runtime, library, and hardware;
|
|
75
|
+
`system config` for typed, validated config access.
|
|
76
|
+
- **Rich CLI** — banner, model picker, typewriter streaming.
|
|
77
|
+
|
|
78
|
+
### Known limitations
|
|
79
|
+
- Tool calls / function calling are not implemented.
|
|
80
|
+
- Multi-model serving is not implemented.
|
|
81
|
+
- The `model` field in `/v1/chat/completions` is ignored; one gateway serves
|
|
82
|
+
one model.
|
|
83
|
+
|
|
84
|
+
[Unreleased]: https://github.com/vipera-iso/vipera/compare/v1.2.0...HEAD
|
|
85
|
+
[1.2.0]: https://github.com/vipera-iso/vipera/releases/tag/v1.2.0
|
|
86
|
+
[1.1.0]: https://github.com/vipera-iso/vipera/releases/tag/v1.1.0
|
|
87
|
+
[1.0.0]: https://github.com/vipera-iso/vipera/releases/tag/v1.0.0
|
vipera_ai-1.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vipera
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
vipera_ai-1.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: vipera-ai
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: Modern local AI gateway — OpenAI-compatible, GGUF-native
|
|
5
|
+
Project-URL: Homepage, https://github.com/vipera-iso/vipera
|
|
6
|
+
Project-URL: Documentation, https://github.com/vipera-iso/vipera#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/vipera-iso/vipera
|
|
8
|
+
Project-URL: Issues, https://github.com/vipera-iso/vipera/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/vipera-iso/vipera/blob/main/CHANGELOG.md
|
|
10
|
+
Author: Vipera contributors
|
|
11
|
+
License: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: gateway,gguf,inference,llama.cpp,llm,local-ai,moe,openai,openai-api,streaming
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.11
|
|
26
|
+
Requires-Dist: fastapi>=0.110
|
|
27
|
+
Requires-Dist: huggingface-hub>=0.23
|
|
28
|
+
Requires-Dist: psutil>=5.9
|
|
29
|
+
Requires-Dist: pydantic>=2.6
|
|
30
|
+
Requires-Dist: rich>=13.7
|
|
31
|
+
Requires-Dist: typer>=0.12
|
|
32
|
+
Requires-Dist: uvicorn[standard]>=0.29
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
35
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
36
|
+
Requires-Dist: pre-commit>=3.7; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest-cov>=5; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
39
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
40
|
+
Provides-Extra: gguf
|
|
41
|
+
Requires-Dist: gguf>=0.10; extra == 'gguf'
|
|
42
|
+
Provides-Extra: llama
|
|
43
|
+
Requires-Dist: llama-cpp-python>=0.3.36; extra == 'llama'
|
|
44
|
+
Provides-Extra: nvidia
|
|
45
|
+
Requires-Dist: nvidia-ml-py>=12; extra == 'nvidia'
|
|
46
|
+
Description-Content-Type: text/markdown
|
|
47
|
+
|
|
48
|
+
<div align="center">
|
|
49
|
+
|
|
50
|
+
# Vipera
|
|
51
|
+
|
|
52
|
+
**Modern local AI gateway — OpenAI-compatible, GGUF-native, one command.**
|
|
53
|
+
|
|
54
|
+
[](https://github.com/vipera-iso/vipera/actions/workflows/ci.yml)
|
|
55
|
+
[](https://python.org)
|
|
56
|
+
[](LICENSE)
|
|
57
|
+
[]()
|
|
58
|
+
[](CONTRIBUTING.md)
|
|
59
|
+
|
|
60
|
+
[Quick start](#-quick-start) · [CLI](#-cli-reference) · [HTTP API](#-http-api) · [Docs](#-documentation) · [Contributing](CONTRIBUTING.md)
|
|
61
|
+
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+

|
|
67
|
+
|
|
68
|
+
## What is Vipera?
|
|
69
|
+
|
|
70
|
+
Vipera turns a `.gguf` file into a **production-shaped, OpenAI-compatible HTTP
|
|
71
|
+
endpoint** — in a single command. It reads the hardware it's running on, plans
|
|
72
|
+
which layers belong in VRAM, RAM, or disk, and then drives `llama.cpp` with the
|
|
73
|
+
exact flags that plan implies. No reimplemented runtime, no second inference
|
|
74
|
+
engine, no guessing.
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install "vipera-ai[llama,gguf,nvidia] @ git+https://github.com/vipera-iso/vipera.git"
|
|
78
|
+
vipera model search # download a GGUF from HuggingFace
|
|
79
|
+
vipera serve start # → http://127.0.0.1:8686/v1/chat/completions
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
> The distribution is named `vipera-ai`; it is not on PyPI yet. The PyPI project
|
|
83
|
+
> named `vipera` is an unrelated tool, so do not install that. Use the GitHub
|
|
84
|
+
> source command above, or follow the no-Git
|
|
85
|
+
> [installation guide](docs/install.md).
|
|
86
|
+
|
|
87
|
+

|
|
88
|
+
|
|
89
|
+
That endpoint speaks the OpenAI Chat Completions protocol with real SSE
|
|
90
|
+
streaming, so every existing client — `openai-python`, LangChain, LlamaIndex,
|
|
91
|
+
Continue, the OpenAI Playground — works without code changes.
|
|
92
|
+
|
|
93
|
+
## Why Vipera?
|
|
94
|
+
|
|
95
|
+
Most local LLM tools make you choose between a friendly CLI and a real server.
|
|
96
|
+
Vipera is both, with a planner in the middle that most tools skip.
|
|
97
|
+
|
|
98
|
+
| | **Vipera** | Ollama | `llama.cpp/server` | LM Studio |
|
|
99
|
+
|---|:---:|:---:|:---:|:---:|
|
|
100
|
+
| GGUF-native | ✅ | ✅ | ✅ | ✅ |
|
|
101
|
+
| OpenAI-compatible API | ✅ | ✅ | ✅ | ✅ |
|
|
102
|
+
| **Hardware-aware planner** (VRAM/RAM/disk) | ✅ | ⚠️ partial | ❌ | ⚠️ |
|
|
103
|
+
| Real SSE streaming | ✅ | ✅ | ✅ | ✅ |
|
|
104
|
+
| First-class CLI (no GUI) | ✅ | ✅ | ✅ | ❌ |
|
|
105
|
+
| HuggingFace search built-in | ✅ | ✅ | ❌ | ✅ |
|
|
106
|
+
| Diagnostics (`doctor`) | ✅ | ⚠️ | ❌ | ❌ |
|
|
107
|
+
| Python-native, embeddable | ✅ | ❌ | ❌ | ❌ |
|
|
108
|
+
|
|
109
|
+
Where Vipera differs in practice:
|
|
110
|
+
|
|
111
|
+
- **It plans before it runs.** `vipera system doctor` tells you whether your
|
|
112
|
+
machine can run the model *before* you wait five minutes for a failed load.
|
|
113
|
+
The planner emits `-ngl`, `--n-cpu-moe`, KV quant types, `mlock` and
|
|
114
|
+
`n_batch` from a hardware profile — not a hardcoded default.
|
|
115
|
+
- **It falls back honestly.** If GPU offload fails at load time, the engine
|
|
116
|
+
retries with a CPU-safe plan and tells you what changed. It does not crash,
|
|
117
|
+
and it does not silently degrade.
|
|
118
|
+
- **The CLI and the API are the same stack.** `vipera chat` and
|
|
119
|
+
`/v1/chat/completions` share one engine instance per model — so benchmarks
|
|
120
|
+
you run in the terminal describe the server you actually serve.
|
|
121
|
+
|
|
122
|
+
## ⚡ Quick start
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# 1. install (see docs/install.md for a no-Git option)
|
|
126
|
+
pip install "vipera-ai[llama,gguf,nvidia] @ git+https://github.com/vipera-iso/vipera.git"
|
|
127
|
+
|
|
128
|
+
# 2. fetch a model
|
|
129
|
+
vipera model search
|
|
130
|
+
|
|
131
|
+
# 3. serve it
|
|
132
|
+
vipera serve start
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
That's it. Then point any OpenAI client at `http://127.0.0.1:8686/v1`:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from openai import OpenAI
|
|
139
|
+
|
|
140
|
+
client = OpenAI(base_url="http://127.0.0.1:8686/v1", api_key="none")
|
|
141
|
+
|
|
142
|
+
stream = client.chat.completions.create(
|
|
143
|
+
model="default",
|
|
144
|
+
messages=[{"role": "user", "content": "Explain MoE in one paragraph."}],
|
|
145
|
+
stream=True,
|
|
146
|
+
)
|
|
147
|
+
for chunk in stream:
|
|
148
|
+
print(chunk.choices[0].delta.content or "", end="")
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## 🧭 CLI reference
|
|
152
|
+
|
|
153
|
+
Vipera ships a single `vipera` entry point. Every command is discoverable with
|
|
154
|
+
`vipera <command> --help`.
|
|
155
|
+
|
|
156
|
+
### Top-level
|
|
157
|
+
|
|
158
|
+
| Command | Description |
|
|
159
|
+
|---|---|
|
|
160
|
+
| `vipera` | Start the gateway with the default model. |
|
|
161
|
+
| `vipera -s` / `--select` | Start the gateway with an interactive model picker. |
|
|
162
|
+
| `vipera --version` | Print the version. |
|
|
163
|
+
| `vipera help` | Print the full command reference. |
|
|
164
|
+
|
|
165
|
+
### `vipera model` — manage GGUF artifacts
|
|
166
|
+
|
|
167
|
+
| Command | Description |
|
|
168
|
+
|---|---|
|
|
169
|
+
| `vipera model search` | Search HuggingFace for GGUF models, then download one. |
|
|
170
|
+
| `vipera model import <path> [--name <alias>]` | Copy a local `.gguf` into the Vipera store. |
|
|
171
|
+
| `vipera model inspect <path> [--tensors]` | Analyze GGUF metadata, tensor categories, and quantization mix. |
|
|
172
|
+
| `vipera model imatrix <path> --calibration-data <file>` | Build an importance matrix for quantization. |
|
|
173
|
+
| `vipera model quantize <path> [--target-quant <type>]` | Quantize a GGUF file with llama-quantize. |
|
|
174
|
+
| `vipera model optimize <path> [--target-size <GB>]` | Inspect, build an optional imatrix, and quantize in one step. |
|
|
175
|
+
| `vipera model list` | List every model registered locally. |
|
|
176
|
+
| `vipera model rm <alias> [--yes]` | Remove a model and its file. |
|
|
177
|
+
|
|
178
|
+
### `vipera serve` — HTTP gateway
|
|
179
|
+
|
|
180
|
+
| Command | Description |
|
|
181
|
+
|---|---|
|
|
182
|
+
| `vipera serve start [--model] [--host] [--port] [--profile] [--ctx]` | Load a model and start the OpenAI-compatible server. |
|
|
183
|
+
| `vipera serve status` | Probe the configured host/port for a live gateway. |
|
|
184
|
+
| `vipera serve stop [--force]` | Stop the gateway using its pid file. |
|
|
185
|
+
|
|
186
|
+
### `vipera chat` — terminal chat
|
|
187
|
+
|
|
188
|
+
| Command | Description |
|
|
189
|
+
|---|---|
|
|
190
|
+
| `vipera chat repl [-m <alias>]` | Interactive multi-turn REPL. |
|
|
191
|
+
| `vipera chat oneshot "<prompt>" [-m <alias>]` | One turn, print, exit. |
|
|
192
|
+
|
|
193
|
+
### `vipera bench` — performance
|
|
194
|
+
|
|
195
|
+
| Command | Description |
|
|
196
|
+
|---|---|
|
|
197
|
+
| `vipera bench run [model] [--ctx 512,2048] [--max-tokens N]` | Measure prompt and generation throughput across context sizes. |
|
|
198
|
+
|
|
199
|
+
### `vipera system` — diagnostics & config
|
|
200
|
+
|
|
201
|
+
| Command | Description |
|
|
202
|
+
|---|---|
|
|
203
|
+
| `vipera system doctor` | Runtime, library, and hardware check. |
|
|
204
|
+
| `vipera system config [key] [value]` | View or set configuration keys. |
|
|
205
|
+
|
|
206
|
+
## 🔌 HTTP API
|
|
207
|
+
|
|
208
|
+
### Endpoints
|
|
209
|
+
|
|
210
|
+
| Method | Path | Description |
|
|
211
|
+
|---|---|---|
|
|
212
|
+
| `GET` | `/health` | `{"status": "ok", "model": "<alias>"}` |
|
|
213
|
+
| `GET` | `/v1/models` | OpenAI-shaped model list. |
|
|
214
|
+
| `POST` | `/v1/chat/completions` | Chat completion, streaming or non-streaming. |
|
|
215
|
+
|
|
216
|
+
### curl — non-streaming
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
curl http://127.0.0.1:8686/v1/chat/completions \
|
|
220
|
+
-H "Content-Type: application/json" \
|
|
221
|
+
-d '{
|
|
222
|
+
"model": "default",
|
|
223
|
+
"messages": [{"role": "user", "content": "Hello!"}]
|
|
224
|
+
}'
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### curl — streaming
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
curl -N http://127.0.0.1:8686/v1/chat/completions \
|
|
231
|
+
-H "Content-Type: application/json" \
|
|
232
|
+
-d '{
|
|
233
|
+
"model": "default",
|
|
234
|
+
"messages": [{"role": "user", "content": "Count to 5."}],
|
|
235
|
+
"stream": true
|
|
236
|
+
}'
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### JavaScript
|
|
240
|
+
|
|
241
|
+
```js
|
|
242
|
+
import OpenAI from "openai";
|
|
243
|
+
|
|
244
|
+
const client = new OpenAI({
|
|
245
|
+
baseURL: "http://127.0.0.1:8686/v1",
|
|
246
|
+
apiKey: "none",
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const stream = await client.chat.completions.create({
|
|
250
|
+
model: "default",
|
|
251
|
+
messages: [{ role: "user", content: "hi" }],
|
|
252
|
+
stream: true,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
for await (const chunk of stream) {
|
|
256
|
+
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Compatibility
|
|
261
|
+
|
|
262
|
+
Vipera implements the **OpenAI Chat Completions** schema and SSE framing. The
|
|
263
|
+
`model` field in the request is accepted for client compatibility but ignored:
|
|
264
|
+
one gateway serves one model.
|
|
265
|
+
|
|
266
|
+
## 🧱 Architecture
|
|
267
|
+
|
|
268
|
+
```mermaid
|
|
269
|
+
flowchart LR
|
|
270
|
+
subgraph Client
|
|
271
|
+
C1["openai-python"]
|
|
272
|
+
C2["curl"]
|
|
273
|
+
C3["OpenAI SDK"]
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
subgraph Vipera["Vipera Gateway"]
|
|
277
|
+
S["FastAPI server"]
|
|
278
|
+
E["Engine"]
|
|
279
|
+
P["Planner"]
|
|
280
|
+
H["Hardware probe"]
|
|
281
|
+
R["Registry"]
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
M[("GGUF file")]
|
|
285
|
+
|
|
286
|
+
C1 & C2 & C3 -->|"HTTP / SSE"| S
|
|
287
|
+
S -->|"chat()"| E
|
|
288
|
+
E -->|"reads"| M
|
|
289
|
+
P -->|"flags"| E
|
|
290
|
+
H --> P
|
|
291
|
+
R --> P
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**Layers:**
|
|
295
|
+
|
|
296
|
+
- `vipera/hardware.py` — probes CPU cores, RAM, GPU name, VRAM. No side effects.
|
|
297
|
+
- `vipera/planner.py` — turns `(model meta, hardware)` into a concrete `Plan`
|
|
298
|
+
(`gpu_layers`, `context`, `kv_cache`, `flash_attn`, `n_batch`, `use_mlock`,
|
|
299
|
+
`offload_kqv`).
|
|
300
|
+
- `vipera/engine.py` — wraps `llama_cpp.Llama` from a `Plan`; handles the
|
|
301
|
+
GPU → CPU fallback path and the `mlock` retry.
|
|
302
|
+
- `vipera/server.py` — FastAPI app; real SSE for streaming, threadpool for
|
|
303
|
+
blocking inference. Sync generator returned directly to `StreamingResponse`,
|
|
304
|
+
so chunks flush per token.
|
|
305
|
+
- `vipera/registry.py` — JSON model store at `~/.vipera/registry.json`.
|
|
306
|
+
- `vipera/ui.py` — Rich-based CLI (banner, panels, picker, typewriter stream).
|
|
307
|
+
- `vipera/commands/*` — one file per CLI subcommand.
|
|
308
|
+
|
|
309
|
+
## ⚙️ Configuration
|
|
310
|
+
|
|
311
|
+
Config lives at `~/.vipera/config.json`. Set it via `vipera system config`, or
|
|
312
|
+
edit the JSON — unknown keys are rejected, known keys are type-coerced.
|
|
313
|
+
|
|
314
|
+
| Key | Type | Default | Meaning |
|
|
315
|
+
|---|---|---|---|
|
|
316
|
+
| `host` | `str` | `127.0.0.1` | Gateway bind address. |
|
|
317
|
+
| `port` | `int` | `8686` | Gateway port. |
|
|
318
|
+
| `default_model` | `str \| null` | `null` | Alias used when `--model` is not given. |
|
|
319
|
+
| `profile` | `str` | `auto` | Planner profile (`auto`, `cpu`, `gpu`, `lowmem`). |
|
|
320
|
+
| `ctx` | `int` | `8192` | Context size in tokens. |
|
|
321
|
+
| `kv_cache` | `str` | `q8_0` | KV cache type (`f16`, `q8_0`, `q4_0`, ...). |
|
|
322
|
+
| `flash_attn` | `bool` | `true` | Enable flash attention when GPU present. |
|
|
323
|
+
| `n_batch` | `int` | `512` | Prompt batch size. |
|
|
324
|
+
| `temperature` | `float` | `0.7` | Sampling temperature. |
|
|
325
|
+
| `max_tokens` | `int` | `2048` | Generation cap. |
|
|
326
|
+
| `top_p` | `float` | `0.95` | Nucleus sampling. |
|
|
327
|
+
|
|
328
|
+
**Storage layout:**
|
|
329
|
+
|
|
330
|
+
```
|
|
331
|
+
~/.vipera/
|
|
332
|
+
├── config.json # gateway config
|
|
333
|
+
├── registry.json # model index
|
|
334
|
+
├── models/ # *.gguf artifacts
|
|
335
|
+
└── logs/ # reserved
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Override the home with `VIPERA_HOME=/path/to/dir`.
|
|
339
|
+
|
|
340
|
+
## 🔬 GGUF optimization
|
|
341
|
+
|
|
342
|
+
### Inspect
|
|
343
|
+
|
|
344
|
+
`vipera model inspect` reads the GGUF index only — weights are never loaded
|
|
345
|
+
into memory — and prints the architecture, layer/context/embedding sizes, a
|
|
346
|
+
size breakdown by tensor category (embedding, attention, FFN, expert, norm),
|
|
347
|
+
and the quantization mix.
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
vipera model inspect ~/.vipera/models/model.gguf
|
|
351
|
+
vipera model inspect ~/.vipera/models/model.gguf --tensors # every tensor
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
It warns when quality-sensitive tensors (`attn_v`, `ffn_down`, including their
|
|
355
|
+
`_exps` variants) are quantized below Q4_K. That check is a heuristic, not a
|
|
356
|
+
quality measurement.
|
|
357
|
+
|
|
358
|
+
### Importance matrix and quantization
|
|
359
|
+
|
|
360
|
+
An importance matrix (imatrix) tells `llama-quantize` which weights matter for
|
|
361
|
+
your data, so low-bit quants keep more of the quality. Generate one from a
|
|
362
|
+
plain-text calibration file, then quantize with it:
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
vipera model imatrix model.gguf --calibration-data calibration.txt
|
|
366
|
+
vipera model quantize model.gguf --imatrix model.imatrix.gguf --target-quant q4_k_m
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
`quantize` writes `<model>-vipera-<quant>.gguf` next to the source unless
|
|
370
|
+
`--output` is given. Requantizing an already-quantized file needs
|
|
371
|
+
`--allow-requantize`; expect a quality loss. For MoE models, `--prior-weight`
|
|
372
|
+
raises the expert prior (try 128 for models with many rarely-used experts to
|
|
373
|
+
avoid NaNs) when the installed `llama-quantize` supports the flag.
|
|
374
|
+
|
|
375
|
+
### One-shot optimization
|
|
376
|
+
|
|
377
|
+
`optimize` inspects the file, picks the highest-quality K-quant that fits the
|
|
378
|
+
size budget, optionally builds an imatrix, and quantizes:
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
vipera model optimize model.gguf --target-size 4.5 --calibration-data calibration.txt
|
|
382
|
+
vipera model optimize model.gguf --target-quant q4_k_m
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
The size estimate assumes a roughly uniform per-weight cost; actual output can
|
|
386
|
+
differ, especially for models with large output embeddings or MoE experts.
|
|
387
|
+
`--prune-layers` is passed through to `llama-quantize` and can substantially
|
|
388
|
+
reduce quality.
|
|
389
|
+
|
|
390
|
+
All three commands need `llama-imatrix` and `llama-quantize` from a llama.cpp
|
|
391
|
+
build. Vipera finds them on `PATH`, in `$VIPERA_LLAMA_BIN`, in a few common
|
|
392
|
+
build directories, or via `--bin-dir`. Every command accepts `--dry-run` to
|
|
393
|
+
print the exact `llama.cpp` invocation without running it.
|
|
394
|
+
|
|
395
|
+
## 📊 Benchmarks
|
|
396
|
+
|
|
397
|
+
Vipera measures what the gateway actually produces, on the machine it runs on.
|
|
398
|
+
Numbers below are from the reference box; re-run with `vipera bench run` on
|
|
399
|
+
yours — that is the point.
|
|
400
|
+
|
|
401
|
+
**Reference machine**
|
|
402
|
+
|
|
403
|
+
| | |
|
|
404
|
+
|---|---|
|
|
405
|
+
| CPU | Intel i5-11300H — 4C / 8T |
|
|
406
|
+
| RAM | 15 GiB, ~10 GiB free, no swap |
|
|
407
|
+
| GPU | NVIDIA GTX 1650 Max-Q, 4 GB VRAM |
|
|
408
|
+
| Model | DeepSeek-V2-Lite Q4_K_M (9.65 GB, MoE 15.7B / 2.4B active) |
|
|
409
|
+
| Runtime | llama.cpp CPU build, `-ngl 0` |
|
|
410
|
+
|
|
411
|
+
| Config | Gen t/s | Prompt t/s | Peak RSS |
|
|
412
|
+
|---|---:|---:|---:|
|
|
413
|
+
| CPU only, mmap | 16.0 | 44 | 12.6 GiB |
|
|
414
|
+
| CPU only, `-lm mlock` | 22.9 | — | ~10 GiB |
|
|
415
|
+
| CUDA offload (experts on CPU) | 6.8 | — | — |
|
|
416
|
+
| Vulkan offload | 3.4 | — | — |
|
|
417
|
+
|
|
418
|
+
**Read this as:** on a model that already fits in RAM, this class of gateway
|
|
419
|
+
does not beat `llama.cpp` defaults — the load-flag layer (`mlock`, `n_batch`)
|
|
420
|
+
moves throughput 1.4× while placement does not, because there is nothing to
|
|
421
|
+
place. Vipera earns its keep at the edges: when the model *spills* RAM, when
|
|
422
|
+
the machine has multiple accelerators of different capabilities, or when the
|
|
423
|
+
operator does not know which of forty flags to pass. `vipera bench run` is the
|
|
424
|
+
instrument; the numbers are yours.
|
|
425
|
+
|
|
426
|
+
## 📚 Documentation
|
|
427
|
+
|
|
428
|
+
Start at the [docs index](docs/README.md).
|
|
429
|
+
|
|
430
|
+
- [Installation](docs/install.md) — step-by-step setup for non-technical users.
|
|
431
|
+
- [Architecture](docs/architecture.md) — module map, planner, engine fallback, concurrency.
|
|
432
|
+
- [CLI reference](docs/cli.md) — every command, flag, and default.
|
|
433
|
+
- [HTTP API](docs/http-api.md) — endpoints, schemas, SSE framing, client examples.
|
|
434
|
+
- [Configuration](docs/configuration.md) — config keys, storage layout, registry schema.
|
|
435
|
+
- [Development](docs/development.md) — setup, tests, CI, release process.
|
|
436
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md) — dev setup, PR flow, how to record the demo GIF.
|
|
437
|
+
- [SECURITY.md](SECURITY.md) — reporting vulnerabilities, supported versions.
|
|
438
|
+
- [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) — Contributor Covenant 2.1.
|
|
439
|
+
- [CHANGELOG.md](CHANGELOG.md) — release history.
|
|
440
|
+
|
|
441
|
+
## 🤝 Contributing
|
|
442
|
+
|
|
443
|
+
Issues and PRs are welcome. First-timers: look for
|
|
444
|
+
[`good first issue`](https://github.com/vipera-iso/vipera/labels/good%20first%20issue).
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
git clone https://github.com/vipera-iso/vipera
|
|
448
|
+
cd vipera
|
|
449
|
+
python -m venv .venv && .venv/bin/pip install -e ".[dev,llama,gguf]"
|
|
450
|
+
.venv/bin/pre-commit install
|
|
451
|
+
.venv/bin/pytest -q
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the full guide.
|
|
455
|
+
|
|
456
|
+
## 📄 License
|
|
457
|
+
|
|
458
|
+
MIT © 2026 Vipera — see [LICENSE](LICENSE).
|