Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
9741646
feat(rocm): AMD ROCm support (gfx1100) + qwen35moe GGUF loader
Aug 25, 2026
6f4974e
fix(gguf): register unmerged CONTROL/USER_DEFINED tokens for atomic e…
Aug 26, 2026
bc62324
fix(server): route Qwen3.6 to the qwen3_coder tool-call parser
Aug 26, 2026
25d7bd8
chore(local): ROCm serve scripts + VS Code tasks/debug configs
Aug 26, 2026
729789a
fix(qwen3.5-moe): GGUF lm_head last-token gather + presence/frequency…
Aug 26, 2026
1f76e88
chore(rocm): drop CI workflow + tinygrad fallback; review cleanup
Aug 26, 2026
8a9d2df
Merge remote-tracking branch 'origin/main' into feat/amd-rocm-gfx1100…
Aug 26, 2026
9ee03a2
Merge remote-tracking branch 'origin/main' into feat/amd-rocm-gfx1100…
Aug 30, 2026
ce11a07
fix(rocm): retain hybrid attention capability flag
Aug 30, 2026
9e7fe72
feat(rocm-perf-parity): nvidia-path integrity pass (Inc 1) - suite de…
Aug 31, 2026
811415e
feat(rocm-perf-parity): decode stage profiler (Inc 2) - FREETOKEN_TOR…
Aug 31, 2026
98eabbd
feat(rocm-perf-parity): fused triton router on ROCm (Inc 3) - fused_t…
Aug 31, 2026
632db88
audit(rocm-perf-parity): checkpoint 1 fixes for Inc 1-3 - stale-JIT-l…
Aug 31, 2026
78e2548
feat(rocm-perf-parity): gguf MMVQ tuning harness + negative result (I…
Aug 31, 2026
0680be7
feat(rocm-perf-parity): decode attention micro-bench + tuning sweep n…
Aug 31, 2026
ef031bf
feat(rocm-perf-parity): graph-capture variant gate + GRAPH CAPTURE NO…
Aug 31, 2026
2f02e55
docs(install-amd): ROCm performance section (Inc 8) - baseline/median…
Aug 31, 2026
7544d66
feat(serve): FT_SERVED_MODEL knob -> --served-model-name (Copilot-for…
Aug 31, 2026
93819f7
feat(rocm): complete Qwen GGUF decode parity path
Sep 2, 2026
ac9c965
Merge remote-tracking branch 'origin/main' into feat/amd-rocm-gfx1100…
Sep 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,13 @@ benchmarks/cross_framework

# local e2e/bench artifacts (harnesses may run with repo cwd)
/results/

# torch.utils.cpp_extension ROCm hipify build artifacts (preprocessed .cu -> .hip,
# and includes rewritten to *_hip.cuh) written next to the GGUF sources.
python/freetoken/kernel/csrc/gguf/*.hip
python/freetoken/kernel/csrc/gguf/*_hip.cuh
python/freetoken/kernel/csrc/gguf/ggml-common_hip.h

# local session junk
nohup.out
.plans/
19 changes: 19 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
// Prevent VS Code from re-introducing CRLF endings into the serve script —
// CRLF was what broke the backslash-newline continuations before.
"files.eol": "\n",
"files.associations": {
"*.sh": "shellscript"
},
"[shellscript]": {
"files.eol": "\n",
"editor.tabSize": 4,
"editor.insertSpaces": false
},
"terminal.integrated.env.linux": {
"PYTHONPATH": "${workspaceFolder}/python"
},
"python.defaultInterpreterPath": "${workspaceFolder}/.venv-rocm/bin/python",
"python.testing.pytestEnabled": true,
"python.testing.pytestArgs": ["tests"]
}
63 changes: 63 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
// FreeToken server lifecycle tasks. All of them call scripts/serve-qwen-moe.sh,
// which builds its argv as a bash array and launches on one line — safe for the
// VS Code integrated shell (no backslash-newline continuations to mangle).
"version": "2.0.0",
"tasks": [
{
"label": "FreeToken: serve (start)",
"type": "shell",
"command": "${workspaceFolder}/scripts/serve-qwen-moe.sh",
"args": ["start"],
"options": {
"cwd": "${workspaceFolder}",
"env": {}
},
"isBackground": true,
"problemMatcher": [],
"presentation": {
"reveal": "always",
"panel": "dedicated",
"clear": true
},
"detail": "Launch the Qwen MoE server (~3-4 min model load), then wait for readiness."
},
{
"label": "FreeToken: serve (stop)",
"type": "shell",
"command": "${workspaceFolder}/scripts/serve-qwen-moe.sh",
"args": ["stop"],
"options": { "cwd": "${workspaceFolder}" },
"problemMatcher": [],
"presentation": { "reveal": "always", "panel": "dedicated" }
},
{
"label": "FreeToken: serve (status)",
"type": "shell",
"command": "${workspaceFolder}/scripts/serve-qwen-moe.sh",
"args": ["status"],
"options": { "cwd": "${workspaceFolder}" },
"problemMatcher": [],
"presentation": { "reveal": "always", "panel": "shared" }
},
{
"label": "FreeToken: serve (follow log)",
"type": "shell",
"command": "${workspaceFolder}/scripts/serve-qwen-moe.sh",
"args": ["log"],
"options": { "cwd": "${workspaceFolder}" },
"isBackground": true,
"problemMatcher": [],
"presentation": { "reveal": "always", "panel": "dedicated" }
},
{
"label": "FreeToken: tests (fast)",
"type": "shell",
"command": "${workspaceFolder}/.venv-rocm/bin/python",
"args": ["-m", "pytest", "-q", "-m", "not slow"],
"options": { "cwd": "${workspaceFolder}", "env": { "PYTHONPATH": "${workspaceFolder}/python" } },
"group": "test",
"problemMatcher": []
}
]
}
83 changes: 83 additions & 0 deletions docs/install-amd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# AMD GPU (ROCm) support

FreeToken targets Linux + NVIDIA CUDA by default. AMD (ROCm) is a supported, tested
configuration with a **single-GPU** milestone: correct functional path first, performance
recovered via HIP ports where safe. This page covers installing and running on RX 7000.

> Status: **experimental.** The default and best-tested path remains CUDA. AMD brings up a
> correct functional path (Triton attention + offload/CPU MoE + portable quant) and is
> recovering performance via the HIP kernel ports.

## Requirements

| Component | Requirement |
| --- | --- |
| OS | Linux x86_64 (Windows WDDM pinned-memory is a known edge, not supported yet) |
| GPU | AMD RX 7000 (RDNA 3, `gfx1100`); RX 9000 (`gfx1201`) is future work |
| ROCm | ROCm toolkit with `hipcc` (`/opt/rocm/bin/hipcc` or on `PATH`) |
| torch | ROCm build, e.g. `torch==2.5.1+rocm6.2` |

The build refuses to mix toolchains: it will **not** silently fall back to `nvcc`/`libcudart`
when only the ROCm toolkit is present, and vice versa.

## Install

```bash
# ROCm torch (PyTorch official ROCm wheels) -- must satisfy the repo's torch>=2.11,<2.12
# build pin, so use the rocm7.2 index (rocm6.2 only carries torch up to 2.5.1).
pip install --index-url https://download.pytorch.org/whl/rocm7.2 \
"torch==2.11.0+rocm7.2" torchvision triton-rocm==3.6.0

# FreeToken with the ROCm extra (builds the native extensions with hipcc)
uv pip install -e ".[rocm]" --no-build-isolation
```

`pip install ".[rocm]"` pulls ROCm-compatible `torch`/`triton`; the NVIDIA-only `[accel]`
packages (`flashinfer`, `sgl-kernel`, `triton_kernels`, Marlin) are **not** installed on AMD
and their backends are rejected with a clean error if requested.

## Verified feature matrix

| Feature | On AMD | Notes |
| --- | --- | --- |
| Attention | `--attention-backend triton` | flashinfer/fa/trtllm are NVIDIA-only and rejected |
| MoE | `--moe-backend offload / cpu / hybrid` | offload needs pinned host memory |
| Quant | BF16, MXFP4, GGUF (Q4_K/Q8_0), Triton inline-dequant NVFP4 | Marlin INT4 / native NVFP4 SASS unavailable |
| NVFP4 checkpoints with no MXFP4 variant | converted to MXFP4 on load (auto) | `--nvfp4-backend auto` → triton/MXFP4 |
| CUDA graphs (decode) | HIP graph capture **if** the capture probe passes | otherwise kernel-launch decode |
| Multi-GPU (RCCL) | out of scope (single-GPU milestone) | |

## CLI behavior on AMD

* `--nvfp4-backend marlin` / `flashinfer` → error (NVIDIA-only). Use `triton` / `auto`.
* `--attention-backend fi` / `fa` / `trtllm` → error (NVIDIA-only). Use `triton` / `auto`.
* `--moe-backend fused` → warning (fused MoE is CUDA-only; falls back to offload/cpu).
* `--nvfp4-backend auto` → resolves to the portable Triton inline-dequant path (or MXFP4
for a converted checkpoint).

## Verify

```bash
ft version # prints an AMD / ROCm banner
ft serve --model Qwen3.6-35B-A3B \
--moe-backend offload --attention-backend triton --nvfp4-backend auto
```

`ldd` of the built `.so` should show `hiprt`/`amdhip64`, not `libcudart`.

## AOT kernel cache

Build the prebuilt `+rocm` kernel-cache wheel (no nvcc needed on the target):

```bash
scripts/build-release-wheels.sh # on a ROCm torch + hipcc box; tags the cache +rocm
```

The runtime refuses to pair a `+rocm` cache with a `+cu130` runtime (and vice versa).

## Notes / limitations

* `nvtx_annotate` is a no-op on ROCm; roctx profiling is future work.
* FP8 / NVFP4-class formats: BF16 / MXFP4 / GGUF are the supported AMD matrix; performance
parity vs CUDA is not guaranteed for NVFP4-class formats.
* Windows AMD is not yet supported (WDDM zero-copy semantics differ).
18 changes: 17 additions & 1 deletion freetoken-kernel-cache/build_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ def _cuda_version_suffix() -> str:
return ""

cuda_version = getattr(torch.version, "cuda", None)
hip_version = getattr(torch.version, "hip", None)
if hip_version:
# ROCm torch: torch.version.cuda is None; tag the cache with +rocm so it pairs
# only with a ROCm runtime (kernel/utils.py._arch_tags enforces the match).
return "+rocm"
if not cuda_version:
return ""
# The tag advertises torch's CUDA; the cache .so link nvcc's libcudart.
Expand Down Expand Up @@ -110,7 +115,18 @@ def _build_jit_cache() -> None:
# 12.0 -> RTX 50 series, RTX PRO 6000 Blackwell (Blackwell, consumer / workstation)
# Override with FREETOKEN_KERNEL_CACHE_ARCHES (space-separated maj.min) or
# TVM_FFI_CUDA_ARCH_LIST directly. Needs an nvcc that supports every listed arch.
if "TVM_FFI_CUDA_ARCH_LIST" not in os.environ:
try:
import torch # noqa: PLC0415

is_rocm_build = bool(getattr(torch.version, "hip", None))
except Exception:
is_rocm_build = False
if is_rocm_build:
# ROCm: the CUDA arch-list is meaningless; the gfx arch is passed through
# kernel/utils.py._arch_flags (--offload-arch), defaulting to the RX 7000
# (gfx1100) target. Env override for other RX 7000 SKUs / future archs.
os.environ.setdefault("FREETOKEN_KERNEL_CACHE_GFX", "gfx1100")
elif "TVM_FFI_CUDA_ARCH_LIST" not in os.environ:
os.environ["TVM_FFI_CUDA_ARCH_LIST"] = os.getenv(
"FREETOKEN_KERNEL_CACHE_ARCHES", "8.0 8.6 8.9 9.0 10.0 12.0"
)
Expand Down
8 changes: 8 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ classifiers = [
"Intended Audience :: Science/Research",
"Operating System :: POSIX :: Linux",
"Environment :: GPU :: NVIDIA CUDA",
"Environment :: GPU :: AMD ROCm",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
Expand Down Expand Up @@ -78,6 +79,13 @@ fi = ["flashinfer-python[cu13]>=0.6,<0.7"]
# renamed from sgl-kernel at 0.4; still imports as `sgl_kernel`, so never co-install both
sgl = ["sglang-kernel==0.4.5"]
accel = ["freetoken[fi,sgl]"]
# ROCm (AMD) install: the NVIDIA-only fi/sgl/Marlin packages are NOT pulled in. torch must
# come from the ROCm wheel index (e.g. `pip install torch==2.11.0+rocm7.2` from
# https://download.pytorch.org/whl/rocm7.2) so the native extensions build against the HIP
# runtime; this extra pins the rest. See docs/install-amd.md.
rocm = [
"triton==3.6.0; platform_system == 'Linux'",
]
# NOTE: the Marlin W4A16 NVFP4 expert-GEMM path (sm_80-99) borrows vLLM's AOT wheel
# (vllm>=0.14,<0.15), which pins transformers>=4.56,<5 and so is INCOMPATIBLE with the
# core transformers>=5.5 requirement. It is therefore not a lockable extra and is left
Expand Down
13 changes: 13 additions & 0 deletions python/freetoken/attention/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,19 @@ def create_triton_backend(config: ModelConfig):
return TritonAttentionBackend(config)


@SUPPORTED_ATTENTION_BACKENDS.register(
"torch",
BackendInfo(
supported_types=frozenset({AttnType.FULL}),
# Debugging/eager ground-truth backend; no package/arch requirements.
),
)
def create_torch_backend(config: ModelConfig):
from .torch import TorchAttentionBackend

return TorchAttentionBackend(config)


@SUPPORTED_ATTENTION_BACKENDS.register(
"dsv4_sparse",
BackendInfo(supported_types=frozenset({AttnType.DSV4})),
Expand Down
Loading