open-jet 0.4.32__py3-none-win_amd64.whl
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.
- open_jet-0.4.32.data/purelib/megakernel/Makefile +28 -0
- open_jet-0.4.32.data/purelib/megakernel/README.md +203 -0
- open_jet-0.4.32.data/purelib/megakernel/capi.cpp +140 -0
- open_jet-0.4.32.data/purelib/megakernel/draft_vocab_32k.bin +0 -0
- open_jet-0.4.32.data/purelib/megakernel/gguf.h +129 -0
- open_jet-0.4.32.data/purelib/megakernel/main.cu +192 -0
- open_jet-0.4.32.data/purelib/megakernel/prefill.cuh +742 -0
- open_jet-0.4.32.data/purelib/megakernel/prefill_gemm.cuh +429 -0
- open_jet-0.4.32.data/purelib/megakernel/qwen38_mk.cu +2858 -0
- open_jet-0.4.32.data/purelib/megakernel/qwen38_mk.h +60 -0
- open_jet-0.4.32.data/purelib/megakernel/server/mk_server.py +544 -0
- open_jet-0.4.32.data/purelib/megakernel/tools/vocab_freq.cpp +87 -0
- open_jet-0.4.32.data/purelib/open_jet/__init__.py +19 -0
- open_jet-0.4.32.data/purelib/openjet/__init__.py +27 -0
- open_jet-0.4.32.data/purelib/openjet/sdk/__init__.py +71 -0
- open_jet-0.4.32.data/purelib/src/__init__.py +19 -0
- open_jet-0.4.32.data/purelib/src/agent.py +949 -0
- open_jet-0.4.32.data/purelib/src/airgap.py +172 -0
- open_jet-0.4.32.data/purelib/src/api_auth.py +168 -0
- open_jet-0.4.32.data/purelib/src/app.py +4177 -0
- open_jet-0.4.32.data/purelib/src/app_paths.py +39 -0
- open_jet-0.4.32.data/purelib/src/app_rendering.py +326 -0
- open_jet-0.4.32.data/purelib/src/app_telemetry.py +151 -0
- open_jet-0.4.32.data/purelib/src/benchmark.py +527 -0
- open_jet-0.4.32.data/purelib/src/cli.py +717 -0
- open_jet-0.4.32.data/purelib/src/codex_auth.py +362 -0
- open_jet-0.4.32.data/purelib/src/commands.py +1850 -0
- open_jet-0.4.32.data/purelib/src/completion.py +202 -0
- open_jet-0.4.32.data/purelib/src/config.py +562 -0
- open_jet-0.4.32.data/purelib/src/context/__init__.py +88 -0
- open_jet-0.4.32.data/purelib/src/context_index.py +199 -0
- open_jet-0.4.32.data/purelib/src/data/token_estimator_hardware.json +138 -0
- open_jet-0.4.32.data/purelib/src/data/token_estimator_models.json +118 -0
- open_jet-0.4.32.data/purelib/src/device_sources.py +551 -0
- open_jet-0.4.32.data/purelib/src/embed_catalog.py +245 -0
- open_jet-0.4.32.data/purelib/src/executor.py +854 -0
- open_jet-0.4.32.data/purelib/src/hardware.py +456 -0
- open_jet-0.4.32.data/purelib/src/harness.py +1414 -0
- open_jet-0.4.32.data/purelib/src/harness_debug.py +64 -0
- open_jet-0.4.32.data/purelib/src/hybrid.py +209 -0
- open_jet-0.4.32.data/purelib/src/litellm_client.py +219 -0
- open_jet-0.4.32.data/purelib/src/llama_server.py +908 -0
- open_jet-0.4.32.data/purelib/src/mcp_support/__init__.py +8 -0
- open_jet-0.4.32.data/purelib/src/mcp_support/cli.py +74 -0
- open_jet-0.4.32.data/purelib/src/mcp_support/config.py +372 -0
- open_jet-0.4.32.data/purelib/src/mcp_support/manager.py +500 -0
- open_jet-0.4.32.data/purelib/src/mcp_support/redaction.py +51 -0
- open_jet-0.4.32.data/purelib/src/mcp_support/results.py +121 -0
- open_jet-0.4.32.data/purelib/src/mcp_support/schema.py +104 -0
- open_jet-0.4.32.data/purelib/src/megakernel_build.py +112 -0
- open_jet-0.4.32.data/purelib/src/memory_reflection.py +259 -0
- open_jet-0.4.32.data/purelib/src/model_profiles.py +170 -0
- open_jet-0.4.32.data/purelib/src/multimodal.py +197 -0
- open_jet-0.4.32.data/purelib/src/observation/__init__.py +30 -0
- open_jet-0.4.32.data/purelib/src/observation/bridge.py +67 -0
- open_jet-0.4.32.data/purelib/src/observation/processors.py +517 -0
- open_jet-0.4.32.data/purelib/src/observation/store.py +86 -0
- open_jet-0.4.32.data/purelib/src/openai_codex_client.py +173 -0
- open_jet-0.4.32.data/purelib/src/openrouter_catalog.py +225 -0
- open_jet-0.4.32.data/purelib/src/peripherals/__init__.py +45 -0
- open_jet-0.4.32.data/purelib/src/peripherals/audio.py +178 -0
- open_jet-0.4.32.data/purelib/src/peripherals/camera.py +65 -0
- open_jet-0.4.32.data/purelib/src/peripherals/discovery.py +237 -0
- open_jet-0.4.32.data/purelib/src/peripherals/sensors.py +70 -0
- open_jet-0.4.32.data/purelib/src/peripherals/system.py +43 -0
- open_jet-0.4.32.data/purelib/src/peripherals/types.py +118 -0
- open_jet-0.4.32.data/purelib/src/persistent_memory.py +391 -0
- open_jet-0.4.32.data/purelib/src/project_setup.py +226 -0
- open_jet-0.4.32.data/purelib/src/provisioning.py +1559 -0
- open_jet-0.4.32.data/purelib/src/runtime/__init__.py +33 -0
- open_jet-0.4.32.data/purelib/src/runtime_client.py +30 -0
- open_jet-0.4.32.data/purelib/src/runtime_limits.py +292 -0
- open_jet-0.4.32.data/purelib/src/runtime_protocol.py +635 -0
- open_jet-0.4.32.data/purelib/src/runtime_registry.py +117 -0
- open_jet-0.4.32.data/purelib/src/sdk/__init__.py +81 -0
- open_jet-0.4.32.data/purelib/src/sdk/fix.py +1359 -0
- open_jet-0.4.32.data/purelib/src/sdk/recommendations.py +171 -0
- open_jet-0.4.32.data/purelib/src/sdk/session.py +1061 -0
- open_jet-0.4.32.data/purelib/src/sdk/tok_s.py +464 -0
- open_jet-0.4.32.data/purelib/src/self_update.py +221 -0
- open_jet-0.4.32.data/purelib/src/service_controller.py +1001 -0
- open_jet-0.4.32.data/purelib/src/session_logging.py +1163 -0
- open_jet-0.4.32.data/purelib/src/session_state.py +239 -0
- open_jet-0.4.32.data/purelib/src/setup.py +928 -0
- open_jet-0.4.32.data/purelib/src/setup_memory.py +420 -0
- open_jet-0.4.32.data/purelib/src/shell_targets.py +175 -0
- open_jet-0.4.32.data/purelib/src/skills/__init__.py +16 -0
- open_jet-0.4.32.data/purelib/src/skills/discovery.py +118 -0
- open_jet-0.4.32.data/purelib/src/skills/model.py +134 -0
- open_jet-0.4.32.data/purelib/src/skills/parser.py +193 -0
- open_jet-0.4.32.data/purelib/src/skills/registry.py +79 -0
- open_jet-0.4.32.data/purelib/src/skills/tools.py +68 -0
- open_jet-0.4.32.data/purelib/src/skills/validate.py +104 -0
- open_jet-0.4.32.data/purelib/src/skills_hub/__init__.py +17 -0
- open_jet-0.4.32.data/purelib/src/skills_hub/installer.py +106 -0
- open_jet-0.4.32.data/purelib/src/skills_hub/lockfile.py +48 -0
- open_jet-0.4.32.data/purelib/src/skills_hub/model.py +123 -0
- open_jet-0.4.32.data/purelib/src/skills_hub/scanner.py +84 -0
- open_jet-0.4.32.data/purelib/src/skills_hub/sources/__init__.py +5 -0
- open_jet-0.4.32.data/purelib/src/skills_hub/sources/base.py +12 -0
- open_jet-0.4.32.data/purelib/src/skills_hub/sources/github.py +7 -0
- open_jet-0.4.32.data/purelib/src/skills_hub/sources/official.py +7 -0
- open_jet-0.4.32.data/purelib/src/skills_hub/sources/url.py +7 -0
- open_jet-0.4.32.data/purelib/src/skills_hub/taps.py +27 -0
- open_jet-0.4.32.data/purelib/src/skills_registry.py +216 -0
- open_jet-0.4.32.data/purelib/src/surfaces/__init__.py +5 -0
- open_jet-0.4.32.data/purelib/src/surfaces/command_specs.py +85 -0
- open_jet-0.4.32.data/purelib/src/surfaces/tui.py +14 -0
- open_jet-0.4.32.data/purelib/src/swap_llama.py +121 -0
- open_jet-0.4.32.data/purelib/src/swap_manager.py +185 -0
- open_jet-0.4.32.data/purelib/src/system_metrics.py +328 -0
- open_jet-0.4.32.data/purelib/src/theme.py +93 -0
- open_jet-0.4.32.data/purelib/src/tool_executor.py +762 -0
- open_jet-0.4.32.data/purelib/src/tools/__init__.py +53 -0
- open_jet-0.4.32.data/purelib/src/tools/registry.py +472 -0
- open_jet-0.4.32.data/purelib/src/tui_bin/windows-x64/SHA256 +1 -0
- open_jet-0.4.32.data/purelib/src/tui_bin/windows-x64/openjet-tui.exe +4 -0
- open_jet-0.4.32.data/purelib/src/tui_bin/windows-x64/theme/dark.json +90 -0
- open_jet-0.4.32.data/purelib/src/tui_bin/windows-x64/theme/light.json +89 -0
- open_jet-0.4.32.data/purelib/src/tui_launcher.py +133 -0
- open_jet-0.4.32.data/purelib/src/tui_server.py +153 -0
- open_jet-0.4.32.data/purelib/src/voice_input.py +294 -0
- open_jet-0.4.32.data/purelib/src/workflows/__init__.py +36 -0
- open_jet-0.4.32.data/purelib/src/workflows/bindings.py +83 -0
- open_jet-0.4.32.data/purelib/src/workflows/daemon.py +239 -0
- open_jet-0.4.32.data/purelib/src/workflows/reports.py +76 -0
- open_jet-0.4.32.data/purelib/src/workflows/runner.py +295 -0
- open_jet-0.4.32.data/purelib/src/workflows/specs.py +165 -0
- open_jet-0.4.32.data/purelib/src/workflows/state.py +262 -0
- open_jet-0.4.32.dist-info/METADATA +289 -0
- open_jet-0.4.32.dist-info/RECORD +135 -0
- open_jet-0.4.32.dist-info/WHEEL +5 -0
- open_jet-0.4.32.dist-info/entry_points.txt +3 -0
- open_jet-0.4.32.dist-info/licenses/LICENSE +235 -0
- open_jet-0.4.32.dist-info/top_level.txt +4 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
LLAMA ?= ../llama.cpp
|
|
2
|
+
NVCC ?= /usr/local/cuda/bin/nvcc
|
|
3
|
+
# the first GPU's architecture (sm_86 RTX 30, sm_89 RTX 40, sm_120 RTX 50); sm_86 if nvidia-smi is missing
|
|
4
|
+
ARCH ?= $(or $(shell nvidia-smi --query-gpu=compute_cap --format=csv,noheader 2>/dev/null | head -n1 | tr -d '. '),86)
|
|
5
|
+
ARCH := sm_$(patsubst sm_%,%,$(ARCH))
|
|
6
|
+
NVFLAGS = -O3 -std=c++17 -arch=$(ARCH) -lineinfo -Xptxas -dlcm=cg -Xptxas -v -Xcompiler -fPIC
|
|
7
|
+
|
|
8
|
+
all: qwen38_mk libqwen38mk.so tools/vocab_freq
|
|
9
|
+
|
|
10
|
+
# ptxas register / spill report for every phase function goes to ptxas.log
|
|
11
|
+
qwen38_mk.o: qwen38_mk.cu qwen38_mk.h gguf.h prefill.cuh prefill_gemm.cuh
|
|
12
|
+
$(NVCC) $(NVFLAGS) -c qwen38_mk.cu -o $@ 2> ptxas.log || (cat ptxas.log; false)
|
|
13
|
+
|
|
14
|
+
qwen38_mk: qwen38_mk.o main.cu
|
|
15
|
+
$(NVCC) -O3 -std=c++17 -arch=$(ARCH) -I$(LLAMA)/include -I$(LLAMA)/ggml/include main.cu qwen38_mk.o \
|
|
16
|
+
-L$(LLAMA)/build/bin -lllama -Xlinker -rpath -Xlinker $(abspath $(LLAMA)/build/bin) -o $@
|
|
17
|
+
|
|
18
|
+
# engine + tokenizer as a shared library with a C API (capi.cpp), used by server/mk_server.py
|
|
19
|
+
libqwen38mk.so: qwen38_mk.o capi.cpp qwen38_mk.h
|
|
20
|
+
$(NVCC) -O3 -std=c++17 -arch=$(ARCH) -shared -Xcompiler -fPIC -I$(LLAMA)/include -I$(LLAMA)/ggml/include capi.cpp qwen38_mk.o \
|
|
21
|
+
-L$(LLAMA)/build/bin -lllama -Xlinker -rpath -Xlinker $(abspath $(LLAMA)/build/bin) -o $@
|
|
22
|
+
|
|
23
|
+
tools/vocab_freq: tools/vocab_freq.cpp
|
|
24
|
+
g++ -O2 -std=c++17 -I$(LLAMA)/include -I$(LLAMA)/ggml/include $< -L$(LLAMA)/build/bin -lllama \
|
|
25
|
+
-Wl,-rpath,$(abspath $(LLAMA)/build/bin) -o $@
|
|
26
|
+
|
|
27
|
+
clean:
|
|
28
|
+
rm -f qwen38_mk libqwen38mk.so qwen38_mk.o ptxas.log tools/vocab_freq
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# Qwen3.8-27B megakernel (RTX 30/40/50 series)
|
|
2
|
+
|
|
3
|
+
A specialised CUDA inference engine for `Qwen3.8-27B-Q4_K_M.gguf` (llama.cpp arch `qwen35`:
|
|
4
|
+
48 Gated-DeltaNet + 16 gated-attention layers, one MTP block) on RTX 30, 40 and 50-series GPUs
|
|
5
|
+
with 24 GB+ (sm_86, sm_89, sm_120; tuned and benchmarked on an RTX 3090), with an OpenAI-compatible server that open-jet uses in place of `llama-server`
|
|
6
|
+
(`llama_backend: auto`, see [docs/runtimes/llama-cpp.md](../docs/runtimes/llama-cpp.md)).
|
|
7
|
+
|
|
8
|
+
Decoding runs an entire speculative cycle in one persistent cooperative kernel launch:
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
main model over T = 1 + n_draft tokens -> lm_head + verification on device (greedy, or exact
|
|
12
|
+
speculative sampling) -> MTP catch-up rows for the accepted tokens -> n_draft MTP draft steps
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The ~340 phases of a cycle (projections, DeltaNet core, attention, FFN, lm_head, MTP) are separated
|
|
16
|
+
by a software grid barrier; the host only reads back the accepted tokens. Prompts go through
|
|
17
|
+
batched tensor-core kernels instead (see Prefill).
|
|
18
|
+
|
|
19
|
+
## Results
|
|
20
|
+
|
|
21
|
+
RTX 3090, against `llama-server` with open-jet's flags (`--spec-type draft-mtp --spec-draft-n-max 2
|
|
22
|
+
-fa on -ctk q8_0 -ctv q8_0`). Back-to-back on an otherwise idle GPU; numbers vary with prompt
|
|
23
|
+
predictability (draft acceptance) and other GPU load.
|
|
24
|
+
|
|
25
|
+
**Reasoning mode as open-jet runs it** (thinking on, xhigh effort, temperature 1.0, top_p 0.95,
|
|
26
|
+
top_k 20; 5 coding prompts x 2 seeds, up to 1024 tokens each):
|
|
27
|
+
|
|
28
|
+
| | llama.cpp | megakernel | |
|
|
29
|
+
|---|---|---|---|
|
|
30
|
+
| decode through the HTTP servers (`/v1/chat/completions`) | 50.9 tok/s | **78.0 tok/s** | **1.53x** |
|
|
31
|
+
| decode, CLI (`bench/think_bench.py`, `-d 4`) | 50.7 tok/s | 77.4 tok/s | 1.53x |
|
|
32
|
+
|
|
33
|
+
**Greedy decode, 4 chat prompts x 512 tokens** (`bench/code_bench.py`):
|
|
34
|
+
|
|
35
|
+
| prompt | llama.cpp | megakernel `-d 3` | megakernel `-d 4` |
|
|
36
|
+
|---|---|---|---|
|
|
37
|
+
| Python function (code) | 72.5 | 126.3 | **139.7** (1.93x) |
|
|
38
|
+
| hash map explanation | 64.1 | 107.4 | 106.5 |
|
|
39
|
+
| short story | 52.8 | 66.8 | 70.7 |
|
|
40
|
+
| Flask to FastAPI plan | 63.2 | 102.6 | 109.8 |
|
|
41
|
+
| **mean** | 63.1 | 100.8 | **106.7** (1.69x) |
|
|
42
|
+
|
|
43
|
+
Without speculation: llama.cpp 40.4 tok/s, megakernel 42.9 (`-d 0`): the decode itself is close to
|
|
44
|
+
the memory-bandwidth limit for both; the lead comes from verifying 4-5 drafted tokens per pass at
|
|
45
|
+
almost no extra cost.
|
|
46
|
+
|
|
47
|
+
**A source file in the prompt, then a coding task** (greedy, 256 tokens; prompt = batched prefill of
|
|
48
|
+
the whole prompt incl. the first token):
|
|
49
|
+
|
|
50
|
+
| context | decode llama.cpp | decode megakernel | prompt llama.cpp | prompt megakernel |
|
|
51
|
+
|---|---|---|---|---|
|
|
52
|
+
| 1.8K | 57.4 | 84.8 | 1050 | 1595 |
|
|
53
|
+
| 3.6K | 56.3 | 95.3 | 1124 | 1595 |
|
|
54
|
+
| 7.8K | 54.8 | 85.8 | 1140 | 1533 |
|
|
55
|
+
| 15.9K | 55.7 | 86.5 | 1126 | 1466 |
|
|
56
|
+
| 29.5K | 52.3 | 72.7 | 1062 | 1236 |
|
|
57
|
+
|
|
58
|
+
(tok/s; measured before the sequence-token draft vocabulary and double-buffered attention below,
|
|
59
|
+
which add 1-8% to the megakernel's decode here.)
|
|
60
|
+
|
|
61
|
+
**Pasting a file into the next turn of a conversation** (prompt processing time):
|
|
62
|
+
|
|
63
|
+
| new tokens | llama.cpp (prompt cache) | megakernel |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| 2.7K | 2.41 s | 1.68 s |
|
|
66
|
+
| 12K | 10.45 s | 8.10 s |
|
|
67
|
+
|
|
68
|
+
With the q8_0 KV cache (chosen automatically for contexts above ~80K tokens, e.g. open-jet's 100K)
|
|
69
|
+
decode is ~1-2% and prefill 2-7% slower than with fp16.
|
|
70
|
+
|
|
71
|
+
**Correctness.** Teacher-forced on llama.cpp's own outputs, the megakernel's greedy prediction
|
|
72
|
+
agrees at 99.3-100% of positions (`--score`: 99.6-100%); the few disagreements are near-ties from
|
|
73
|
+
different float rounding. Speculative verification is exact: greedy output is the main model's
|
|
74
|
+
greedy choice, and with sampling the token distribution is the same as without drafts (2000-sample
|
|
75
|
+
test per position). Verifying 1 vs 4 tokens per pass rounds slightly differently, so on near-ties
|
|
76
|
+
the output can differ between `-d` settings.
|
|
77
|
+
|
|
78
|
+
## Build / run
|
|
79
|
+
|
|
80
|
+
Requires CUDA 12+ (12.8+ for RTX 50 series; built with 13.2), an sm_86, sm_89 or sm_120 GPU with 24 GB, and the repo's llama.cpp build (only
|
|
81
|
+
`libllama`, for tokenization, loaded vocab-only). `openjet setup` builds `libqwen38mk.so` automatically
|
|
82
|
+
(this source ships in the open-jet wheel); building by hand:
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
cd megakernel
|
|
86
|
+
make # qwen38_mk (CLI), libqwen38mk.so (server library), tools/vocab_freq; ~2 min
|
|
87
|
+
./qwen38_mk -m ../models/Qwen3.8-27B-Q4_K_M.gguf -p "Write quicksort in C" -n 512 \
|
|
88
|
+
-d 4 --draft-vocab draft_vocab_32k.bin --think --temp 1.0 --top-p 0.95 --top-k 20
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
CLI flags: `-d` drafts per cycle (0-4; with 4 the depth drops to 3 for a cycle after a rejected
|
|
92
|
+
draft), `-c` KV capacity, `--kv f16|q8|auto` (KV cache format), `--think [xhigh|medium|low]`
|
|
93
|
+
(reasoning mode; the chat template's default effort is xhigh), `--temp` / `--top-p` / `--top-k` /
|
|
94
|
+
`--min-p` / `--seed` (sampling; default greedy), `--raw` (no chat template), `--ids`,
|
|
95
|
+
`--prompt-ids f` (prompt as token ids), `--turn2 text` / `--turn2-file f` (a second user turn
|
|
96
|
+
appended to the conversation), `--score ids.txt` (teacher-forced agreement), `MK_PROFILE=1`
|
|
97
|
+
(per-phase timing, including a "min-composed" estimate that filters out time-slicing by other GPU
|
|
98
|
+
users), `MK_PF_PROFILE=1` (per-kernel prefill timing), `MK_PREFILL=mk` (prefill through the decode
|
|
99
|
+
megakernel instead).
|
|
100
|
+
|
|
101
|
+
Benchmarks: `bench/think_bench.py` (reasoning mode, both engines), `bench/code_bench.py` (greedy
|
|
102
|
+
decode, context sweep, second turn), `bench/bench.py` (decode on the 4 chat prompts);
|
|
103
|
+
`bench/*_bench.cu` are GEMV / GEMM microbenchmarks.
|
|
104
|
+
|
|
105
|
+
## Server
|
|
106
|
+
|
|
107
|
+
`server/mk_server.py` serves the engine (through `libqwen38mk.so`, the C API in `capi.cpp`) with an
|
|
108
|
+
OpenAI-compatible `/v1/chat/completions`, streaming or not, plus `/health` and `/v1/models`.
|
|
109
|
+
open-jet starts it itself; standalone:
|
|
110
|
+
|
|
111
|
+
```sh
|
|
112
|
+
python3 server/mk_server.py -m ../models/Qwen3.8-27B-Q4_K_M.gguf --port 18080 -c 100000
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
- Prompts are rendered with the chat template stored in the GGUF (jinja2): `chat_template_kwargs`
|
|
116
|
+
`enable_thinking` / `preserve_thinking` / `reasoning_effort`, OpenAI `tools`. Reasoning is streamed
|
|
117
|
+
as `reasoning_content`, Qwen's XML tool calls are returned as structured `tool_calls`.
|
|
118
|
+
- Successive requests of a conversation only prefill what is new: the prompt is matched against the
|
|
119
|
+
engine's sequence (prompts + emitted tokens), and when the re-rendered history diverges after the
|
|
120
|
+
end of the previous prompt (e.g. a reformatted tool call) the engine rewinds to a checkpoint
|
|
121
|
+
saved there. Anything else starts over (batched prefill, ~1500 tok/s).
|
|
122
|
+
- Sampling: temperature, top_p, top_k (<= 32), min_p, seed; presence / repetition penalties are
|
|
123
|
+
ignored (with a warning). One request at a time.
|
|
124
|
+
- KV cache: fp16, or q8_0 when fp16 does not fit the requested context (`--kv auto|f16|q8`).
|
|
125
|
+
|
|
126
|
+
## How it gets its speed
|
|
127
|
+
|
|
128
|
+
- **Weights stay in their GGUF k-quant encoding** (Q4_K / Q5_K / Q6_K / Q8_0), read once per
|
|
129
|
+
cycle regardless of T. Q6_K and Q8_0 are re-laid-out at load time (values unchanged) so every
|
|
130
|
+
lane does aligned 16/8-byte loads; Q6_K blocks carry their scales and `d` in one 8-byte word.
|
|
131
|
+
- **dp4a GEMV with shared activations**: activations are int8 (per-16/32 scales, as llama.cpp's
|
|
132
|
+
q8_1), each warp streams 2-4 weight rows at once and reuses every shared-memory activation
|
|
133
|
+
load across them, so T = 4 costs almost the same memory time as T = 1. Weight loads bypass L1
|
|
134
|
+
and are marked L2 evict-first.
|
|
135
|
+
- **No launches, few round trips**: activations are re-quantised by the producing phase
|
|
136
|
+
(including the next RMS norm's weight), so consumers only async-copy (`cp.async`) them.
|
|
137
|
+
- **Gated-DeltaNet rollback by lazy replay**: the recurrent state is committed only through the
|
|
138
|
+
surely-accepted token; accepted drafts are replayed from a small (k, v, g, beta) ring at the
|
|
139
|
+
start of the next cycle, so rejected drafts cost no state snapshots or extra state traffic.
|
|
140
|
+
- **Attention**: split-K flash decoding with tensor-core MMAs (Q.K^T and P.V in fp16 with fp32
|
|
141
|
+
accumulation, online softmax), double-buffered 32-position K/V tiles in shared memory (the next
|
|
142
|
+
tile streams in while the current one is processed: ~5% faster cycles at 30K context).
|
|
143
|
+
- **MTP drafting on device**, with the draft lm_head restricted to the 32,768 most frequent
|
|
144
|
+
tokens (`draft_vocab_32k.bin`, built by `tools/vocab_freq` from local code + docs; covers
|
|
145
|
+
~99.4% of tokens), plus the tokens of the current sequence that are missing from it, added on the
|
|
146
|
+
fly (identifiers of pasted code: +3-8% decode speed with code in context). Verification always
|
|
147
|
+
uses the full vocabulary, so this affects only the acceptance rate, never the output.
|
|
148
|
+
- **Sampling** keeps the 32 best logits of each verified row; a draft is accepted with probability
|
|
149
|
+
p(draft) and a rejected one is resampled from the rest of the distribution, which keeps the
|
|
150
|
+
sampled distribution exact.
|
|
151
|
+
- **L2 warm-up**: before each barrier, blocks prefetch (evict-last) what their next latency-bound
|
|
152
|
+
phase reads first (DeltaNet state, conv weights, norm weights, RoPE rows, first K/V tile).
|
|
153
|
+
|
|
154
|
+
## Prefill
|
|
155
|
+
|
|
156
|
+
A prompt (all but its last token) is processed in chunks of up to 2048 tokens by ordinary kernel
|
|
157
|
+
launches (`prefill.cuh`), which leave exactly the state the megakernel expects (trunk and MTP K/V
|
|
158
|
+
caches, committed DeltaNet state, conv ring, hidden-state history); the last prompt token then goes
|
|
159
|
+
through the megakernel, which produces the first token and the first MTP drafts. The same path
|
|
160
|
+
appends to a running conversation (`MKEngine::prefill` on a non-empty sequence, e.g. the next user
|
|
161
|
+
turn): it starts at the committed DeltaNet position and also re-feeds the last emitted token and any
|
|
162
|
+
accepted drafts not yet folded into the recurrent state; the output is token-identical to
|
|
163
|
+
processing the whole conversation as one fresh prompt.
|
|
164
|
+
|
|
165
|
+
- **GEMMs on fp16 tensor cores straight from the k-quants** (`prefill_gemm.cuh`, ~85% of prefill
|
|
166
|
+
time): 128 x 128 tiles, weights stay in their GGUF blocks and are dequantised to fp16 in
|
|
167
|
+
registers (exact small integers via the `0x6400` trick, then one fp16 scale and min per
|
|
168
|
+
sub-block); activations are fp16 with the elements of each 16-group permuted so that a 4-byte
|
|
169
|
+
weight word is directly an MMA A fragment. Products accumulate in fp16 over k = 128, then in fp32.
|
|
170
|
+
~100-108 TFLOPS on all four quant types (`bench/mmq_bench.cu`). FFN gate and up share one GEMM
|
|
171
|
+
whose epilogue writes `silu(gate) * up` as the down projection's fp16 input.
|
|
172
|
+
- **DeltaNet in chunked (WY) form on tensor cores**: per 32-token chunk, `A = tril(beta K K^T)`
|
|
173
|
+
with the gate decays, `T = (I + A)^-1` (one warp, forward substitution), then
|
|
174
|
+
`U = T beta V - T beta gamma (K S)`, `O = gamma Q S + P U` and `S <- gamma_last S + K^T (decay U)`.
|
|
175
|
+
Value columns are independent, so a block owns (head, 64 columns) and each warp keeps its 128 x 8
|
|
176
|
+
slice of the state in fp32 MMA accumulators, turning it (and K S, U) into fp16 MMA operands
|
|
177
|
+
in registers with `movmatrix`; gates are carried as log g, so tiny or zero gates are exact.
|
|
178
|
+
~2% of prefill time; 3.5e-4 relative error against a float64 reference of the recurrence.
|
|
179
|
+
- **Attention**: causal flash attention over the KV cache on tensor cores.
|
|
180
|
+
|
|
181
|
+
## Limitations
|
|
182
|
+
|
|
183
|
+
- Specialised to this model's shapes and quant mix (27B, Q4_K_M as published by unsloth); the loader
|
|
184
|
+
rejects any other model. Tuned on sm_86 (RTX 3090); open-jet enables it on sm_86, sm_89 and sm_120
|
|
185
|
+
GPUs with 24 GB or more. `make` builds for the installed GPU (`make ARCH=sm_89` to override).
|
|
186
|
+
- Sampling supports temperature, top-k (<= 32), top-p and min-p (llama.cpp's order); no presence /
|
|
187
|
+
repetition penalties yet (open-jet's non-thinking preset uses a presence penalty of 1.5).
|
|
188
|
+
- Text only; one sequence and one checkpoint: a conversation is reused while it grows (or diverges
|
|
189
|
+
only after the end of the previous prompt); editing an earlier part, e.g. context compaction,
|
|
190
|
+
re-prefills it. No slot save / restore (open-jet's low-memory shell swap falls back to
|
|
191
|
+
re-prompting).
|
|
192
|
+
|
|
193
|
+
## Files
|
|
194
|
+
|
|
195
|
+
- `qwen38_mk.cu` / `qwen38_mk.h` - loader, repacking, the megakernel and the `MKEngine` API
|
|
196
|
+
- `prefill.cuh` / `prefill_gemm.cuh` - batched prefill kernels and their quantised fp16 GEMM
|
|
197
|
+
- `capi.cpp` - C API (engine + libllama tokenizer) for `libqwen38mk.so`
|
|
198
|
+
- `server/mk_server.py` - OpenAI-compatible HTTP server
|
|
199
|
+
- `main.cu` - CLI (tokenization via libllama)
|
|
200
|
+
- `gguf.h` - minimal mmap GGUF reader
|
|
201
|
+
- `draft_vocab_32k.bin` - MTP draft-head token subset (int32 ids)
|
|
202
|
+
- `tools/vocab_freq.cpp` - builds a draft vocabulary from a list of text files
|
|
203
|
+
- `bench/` - benchmarks (see Build / run)
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// C API over MKEngine plus libllama's tokenizer (vocab only), for the HTTP server (server/mk_server.py).
|
|
2
|
+
#include <algorithm>
|
|
3
|
+
#include <cstring>
|
|
4
|
+
#include <exception>
|
|
5
|
+
#include <stdexcept>
|
|
6
|
+
#include <string>
|
|
7
|
+
#include <vector>
|
|
8
|
+
|
|
9
|
+
#include "llama.h"
|
|
10
|
+
#include "qwen38_mk.h"
|
|
11
|
+
|
|
12
|
+
namespace {
|
|
13
|
+
struct Handle {
|
|
14
|
+
MKEngine * eng = nullptr;
|
|
15
|
+
llama_model * lm = nullptr;
|
|
16
|
+
const llama_vocab * vocab = nullptr;
|
|
17
|
+
std::string err;
|
|
18
|
+
};
|
|
19
|
+
thread_local std::string g_err;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
extern "C" {
|
|
23
|
+
|
|
24
|
+
const char * mk_last_error() { return g_err.c_str(); }
|
|
25
|
+
|
|
26
|
+
void * mk_new(const char * model, int ctx, int n_draft, const char * draft_vocab, int kv_mode) {
|
|
27
|
+
try {
|
|
28
|
+
llama_backend_init();
|
|
29
|
+
llama_model_params mp = llama_model_default_params();
|
|
30
|
+
mp.vocab_only = true;
|
|
31
|
+
auto * h = new Handle;
|
|
32
|
+
h->lm = llama_model_load_from_file(model, mp);
|
|
33
|
+
if (!h->lm) throw std::runtime_error("failed to load the vocabulary");
|
|
34
|
+
h->vocab = llama_model_get_vocab(h->lm);
|
|
35
|
+
h->eng = new MKEngine(model, ctx, n_draft, draft_vocab ? draft_vocab : "", kv_mode);
|
|
36
|
+
return h;
|
|
37
|
+
} catch (const std::exception & e) {
|
|
38
|
+
g_err = e.what();
|
|
39
|
+
return nullptr;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
void mk_free(void * hp) {
|
|
44
|
+
auto * h = static_cast<Handle *>(hp);
|
|
45
|
+
delete h->eng;
|
|
46
|
+
llama_model_free(h->lm);
|
|
47
|
+
delete h;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// chat template stored in the GGUF (tokenizer.chat_template); returns its length, copies up to cap
|
|
51
|
+
int mk_chat_template(void * hp, char * buf, int cap) {
|
|
52
|
+
auto * h = static_cast<Handle *>(hp);
|
|
53
|
+
const char * t = llama_model_chat_template(h->lm, nullptr);
|
|
54
|
+
if (!t) return -1;
|
|
55
|
+
const int n = (int) strlen(t);
|
|
56
|
+
if (buf && cap > 0) memcpy(buf, t, std::min(n, cap));
|
|
57
|
+
return n;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// text -> token ids (special tokens such as <|im_start|> parsed); returns the count (negative: -needed)
|
|
61
|
+
int mk_tokenize(void * hp, const char * text, int len, int * out, int cap) {
|
|
62
|
+
auto * h = static_cast<Handle *>(hp);
|
|
63
|
+
return llama_tokenize(h->vocab, text, len, out, cap, false, true);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// token -> its bytes (special tokens rendered as text); returns the byte count
|
|
67
|
+
int mk_piece(void * hp, int tok, char * buf, int cap) {
|
|
68
|
+
auto * h = static_cast<Handle *>(hp);
|
|
69
|
+
return llama_token_to_piece(h->vocab, tok, buf, cap, 0, true);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
int mk_is_eog(void * hp, int tok) { return llama_vocab_is_eog(static_cast<Handle *>(hp)->vocab, tok) ? 1 : 0; }
|
|
73
|
+
|
|
74
|
+
int mk_set_sampling(void * hp, float temp, float top_p, int top_k, float min_p, unsigned long long seed) {
|
|
75
|
+
try {
|
|
76
|
+
static_cast<Handle *>(hp)->eng->set_sampling(temp, top_p, top_k, min_p, seed);
|
|
77
|
+
return 0;
|
|
78
|
+
} catch (const std::exception & e) {
|
|
79
|
+
g_err = e.what();
|
|
80
|
+
return -1;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// appends tokens to the sequence; returns the first generated token (negative on error)
|
|
85
|
+
int mk_prefill(void * hp, const int * toks, int n) {
|
|
86
|
+
try {
|
|
87
|
+
return static_cast<Handle *>(hp)->eng->prefill(std::vector<int>(toks, toks + n));
|
|
88
|
+
} catch (const std::exception & e) {
|
|
89
|
+
g_err = e.what();
|
|
90
|
+
return -1;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// one decode cycle; writes 1..n_draft+1 tokens; returns the count (negative on error)
|
|
95
|
+
int mk_step(void * hp, int * out, int cap) {
|
|
96
|
+
try {
|
|
97
|
+
std::vector<int> v;
|
|
98
|
+
static_cast<Handle *>(hp)->eng->step(v);
|
|
99
|
+
const int n = std::min((int) v.size(), cap);
|
|
100
|
+
for (int i = 0; i < n; i++) out[i] = v[i];
|
|
101
|
+
return n;
|
|
102
|
+
} catch (const std::exception & e) {
|
|
103
|
+
g_err = e.what();
|
|
104
|
+
return -1;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
void mk_reset(void * hp) { static_cast<Handle *>(hp)->eng->reset(); }
|
|
109
|
+
|
|
110
|
+
int mk_truncate(void * hp, int len) {
|
|
111
|
+
try {
|
|
112
|
+
static_cast<Handle *>(hp)->eng->truncate(len);
|
|
113
|
+
return 0;
|
|
114
|
+
} catch (const std::exception & e) {
|
|
115
|
+
g_err = e.what();
|
|
116
|
+
return -1;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// copies the sequence so far (up to cap tokens); returns its length
|
|
121
|
+
int mk_sequence(void * hp, int * out, int cap) {
|
|
122
|
+
const auto & s = static_cast<Handle *>(hp)->eng->sequence();
|
|
123
|
+
const int n = (int) s.size();
|
|
124
|
+
if (out)
|
|
125
|
+
for (int i = 0; i < std::min(n, cap); i++) out[i] = s[i];
|
|
126
|
+
return n;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
int mk_committed(void * hp) { return static_cast<Handle *>(hp)->eng->committed(); }
|
|
130
|
+
int mk_checkpoint_pos(void * hp) { return static_cast<Handle *>(hp)->eng->checkpoint_pos(); }
|
|
131
|
+
int mk_rewind_to_checkpoint(void * hp) { return static_cast<Handle *>(hp)->eng->rewind_to_checkpoint() ? 0 : -1; }
|
|
132
|
+
|
|
133
|
+
void mk_stats(void * hp, long * cycles, long * drafted, long * accepted) {
|
|
134
|
+
const auto & s = static_cast<Handle *>(hp)->eng->stats;
|
|
135
|
+
*cycles = s.cycles;
|
|
136
|
+
*drafted = s.drafted;
|
|
137
|
+
*accepted = s.accepted;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
} // extern "C"
|
|
Binary file
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// Minimal read-only GGUF parser (mmap based). Only what the megakernel loader needs.
|
|
2
|
+
#pragma once
|
|
3
|
+
|
|
4
|
+
#include <cstdint>
|
|
5
|
+
#include <cstdio>
|
|
6
|
+
#include <cstdlib>
|
|
7
|
+
#include <cstring>
|
|
8
|
+
#include <map>
|
|
9
|
+
#include <stdexcept>
|
|
10
|
+
#include <string>
|
|
11
|
+
#include <vector>
|
|
12
|
+
|
|
13
|
+
#include <fcntl.h>
|
|
14
|
+
#include <sys/mman.h>
|
|
15
|
+
#include <sys/stat.h>
|
|
16
|
+
#include <unistd.h>
|
|
17
|
+
|
|
18
|
+
namespace gguf {
|
|
19
|
+
|
|
20
|
+
enum GType : uint32_t { F32 = 0, F16 = 1, Q8_0 = 8, Q4_K = 12, Q5_K = 13, Q6_K = 14 };
|
|
21
|
+
|
|
22
|
+
struct Tensor {
|
|
23
|
+
std::string name;
|
|
24
|
+
uint32_t type = 0;
|
|
25
|
+
std::vector<uint64_t> ne;
|
|
26
|
+
uint64_t offset = 0;
|
|
27
|
+
const uint8_t * data = nullptr;
|
|
28
|
+
uint64_t nbytes = 0;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
inline uint64_t type_row_bytes(uint32_t type, uint64_t n) {
|
|
32
|
+
switch (type) {
|
|
33
|
+
case F32: return n * 4;
|
|
34
|
+
case F16: return n * 2;
|
|
35
|
+
case Q8_0: return n / 32 * 34;
|
|
36
|
+
case Q4_K: return n / 256 * 144;
|
|
37
|
+
case Q5_K: return n / 256 * 176;
|
|
38
|
+
case Q6_K: return n / 256 * 210;
|
|
39
|
+
default: throw std::runtime_error("unsupported ggml type " + std::to_string(type));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
struct File {
|
|
44
|
+
const uint8_t * base = nullptr;
|
|
45
|
+
size_t size = 0;
|
|
46
|
+
std::map<std::string, Tensor> tensors;
|
|
47
|
+
std::map<std::string, uint64_t> ints;
|
|
48
|
+
std::map<std::string, std::string> strs;
|
|
49
|
+
|
|
50
|
+
explicit File(const std::string & path) {
|
|
51
|
+
int fd = open(path.c_str(), O_RDONLY);
|
|
52
|
+
if (fd < 0) throw std::runtime_error("cannot open " + path);
|
|
53
|
+
struct stat st;
|
|
54
|
+
fstat(fd, &st);
|
|
55
|
+
size = (size_t) st.st_size;
|
|
56
|
+
void * p = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0);
|
|
57
|
+
close(fd);
|
|
58
|
+
if (p == MAP_FAILED) throw std::runtime_error("mmap failed");
|
|
59
|
+
base = (const uint8_t *) p;
|
|
60
|
+
parse();
|
|
61
|
+
}
|
|
62
|
+
~File() { if (base) munmap((void *) base, size); }
|
|
63
|
+
|
|
64
|
+
const Tensor & get(const std::string & name) const {
|
|
65
|
+
auto it = tensors.find(name);
|
|
66
|
+
if (it == tensors.end()) throw std::runtime_error("missing tensor " + name);
|
|
67
|
+
return it->second;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private:
|
|
71
|
+
size_t pos = 0;
|
|
72
|
+
template <class T> T rd() { T v; memcpy(&v, base + pos, sizeof(T)); pos += sizeof(T); return v; }
|
|
73
|
+
std::string rds() { uint64_t n = rd<uint64_t>(); std::string s((const char *) base + pos, n); pos += n; return s; }
|
|
74
|
+
|
|
75
|
+
void skip_val(uint32_t t, const std::string & key, bool top) {
|
|
76
|
+
switch (t) {
|
|
77
|
+
case 0: case 1: case 7: { uint8_t v = rd<uint8_t>(); if (top) ints[key] = v; break; }
|
|
78
|
+
case 2: case 3: pos += 2; break;
|
|
79
|
+
case 4: { uint32_t v = rd<uint32_t>(); if (top) ints[key] = v; break; }
|
|
80
|
+
case 5: { int32_t v = rd<int32_t>(); if (top) ints[key] = (uint64_t) v; break; }
|
|
81
|
+
case 6: pos += 4; break;
|
|
82
|
+
case 8: { std::string s = rds(); if (top) strs[key] = s; break; }
|
|
83
|
+
case 9: {
|
|
84
|
+
uint32_t et = rd<uint32_t>();
|
|
85
|
+
uint64_t n = rd<uint64_t>();
|
|
86
|
+
for (uint64_t i = 0; i < n; i++) skip_val(et, key, false);
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
case 10: { uint64_t v = rd<uint64_t>(); if (top) ints[key] = v; break; }
|
|
90
|
+
case 11: { int64_t v = rd<int64_t>(); if (top) ints[key] = (uint64_t) v; break; }
|
|
91
|
+
case 12: pos += 8; break;
|
|
92
|
+
default: throw std::runtime_error("bad gguf kv type");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
void parse() {
|
|
97
|
+
if (memcmp(base, "GGUF", 4) != 0) throw std::runtime_error("not a gguf file");
|
|
98
|
+
pos = 4;
|
|
99
|
+
rd<uint32_t>();
|
|
100
|
+
uint64_t n_tensors = rd<uint64_t>();
|
|
101
|
+
uint64_t n_kv = rd<uint64_t>();
|
|
102
|
+
for (uint64_t i = 0; i < n_kv; i++) {
|
|
103
|
+
std::string key = rds();
|
|
104
|
+
uint32_t t = rd<uint32_t>();
|
|
105
|
+
skip_val(t, key, true);
|
|
106
|
+
}
|
|
107
|
+
std::vector<Tensor> infos;
|
|
108
|
+
for (uint64_t i = 0; i < n_tensors; i++) {
|
|
109
|
+
Tensor t;
|
|
110
|
+
t.name = rds();
|
|
111
|
+
uint32_t nd = rd<uint32_t>();
|
|
112
|
+
for (uint32_t d = 0; d < nd; d++) t.ne.push_back(rd<uint64_t>());
|
|
113
|
+
t.type = rd<uint32_t>();
|
|
114
|
+
t.offset = rd<uint64_t>();
|
|
115
|
+
infos.push_back(t);
|
|
116
|
+
}
|
|
117
|
+
uint64_t align = ints.count("general.alignment") ? ints["general.alignment"] : 32;
|
|
118
|
+
size_t data_start = (pos + align - 1) / align * align;
|
|
119
|
+
for (auto & t : infos) {
|
|
120
|
+
t.data = base + data_start + t.offset;
|
|
121
|
+
uint64_t rows = 1;
|
|
122
|
+
for (size_t d = 1; d < t.ne.size(); d++) rows *= t.ne[d];
|
|
123
|
+
t.nbytes = type_row_bytes(t.type, t.ne[0]) * rows;
|
|
124
|
+
tensors[t.name] = t;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
} // namespace gguf
|