Image-Text-to-Text
GGUF
llama.cpp
rocm
amd
rocmfp4
rocmfpx
strix-halo
amd-strix-halo
gfx1151
ryzen-ai-max
ryzen-ai-max-395
radeon-8060s
Mixture of Experts
reasoning
multimodal
vision
nex
qwen3.5
quantized
conversational
Instructions to use kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0 # Run inference directly in the terminal: llama cli -hf kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0 # Run inference directly in the terminal: llama cli -hf kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0 # Run inference directly in the terminal: ./llama-cli -hf kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0 # Run inference directly in the terminal: ./build/bin/llama-cli -hf kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0
Use Docker
docker model run hf.co/kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0
- LM Studio
- Jan
- vLLM
How to use kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0
- Ollama
How to use kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF with Ollama:
ollama run hf.co/kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0
- Unsloth Desktop
- Pi
How to use kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0
Configure the model in Pi
# Install Pi: npm install -g @earendil-works/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Docker Model Runner
How to use kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF with Docker Model Runner:
docker model run hf.co/kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0
- Lemonade
How to use kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0
Run and chat with the model
lemonade run user.Nex-N2.5-mini-ROCmFP4-GGUF-Q4_0
List all available models
lemonade list
- Hermes Agent
How to use kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "kingjones777/Nex-N2.5-mini-ROCmFP4-GGUF:Q4_0" \ --custom-provider-id llama-cpp \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
| #!/usr/bin/env python3 | |
| """Render both HF cards from summary.json + judgments.json. Every number comes from the summary; | |
| absent => 'β'. Judgment prose lives in nex_judge.py and only ever references computed values. | |
| usage: nex_render.py <summary.json> <outdir> <judgments.json> <SHA256SUMS_std> <SHA256SUMS_imat> <staged_sizes.json>""" | |
| import json, math, os, sys | |
| S = json.load(open(sys.argv[1])); OUT = sys.argv[2]; os.makedirs(OUT, exist_ok=True) | |
| JUDGE = json.load(open(sys.argv[3])) | |
| SUMS = {} | |
| for sf in sys.argv[4:6]: | |
| if os.path.exists(sf): | |
| for line in open(sf): | |
| h, fn = line.split(maxsplit=1); SUMS[fn.strip()] = h | |
| SIZES = json.load(open(sys.argv[6])) if len(sys.argv) > 6 and os.path.exists(sys.argv[6]) else {} | |
| N = S["model"]; STD, IMAT = S["model_repo_std"], S["model_repo_imat"] | |
| T = S.get("tiers") or {}; B = S.get("binary") or {}; SRC = S.get("source") or {}; AR = S.get("arch") or {} | |
| GiB, MiB = 1024 ** 3, 1024 ** 2 | |
| UB = S.get("n_ubatch") if S.get("n_ubatch") is not None else 1024 | |
| CTX, GEN, REPS = 65536, 256, 3 | |
| STD_TAGS, IMAT_TAGS = ("q106", "q102", "q103"), ("q106i", "q102i", "q103i") | |
| NAMES = {"q106": "STRIX_LEAN", "q102": "COHERENT", "q103": "FAST", | |
| "q106i": "STRIX_LEAN", "q102i": "COHERENT", "q103i": "FAST"} | |
| def g(v, fmt="{:.2f}"): | |
| return "β" if v is None else fmt.format(v) | |
| def gib(b): | |
| return g(None if b is None else b / GiB, "{:.2f} GiB") | |
| def pm(v, e, fmt="{:.4f}"): | |
| return "β" if v is None else (fmt.format(v) + ("" if e is None else " Β± " + fmt.format(e))) | |
| def bench(label): | |
| return next((x for x in S.get("bench") or [] if x["label"] == label), None) | |
| def tg(label): | |
| x = bench(label) | |
| return None if x is None else x.get("tg_median") | |
| def pp(label): | |
| x = bench(label) | |
| return None if x is None else x.get("pp_median") | |
| def gate(label): | |
| return next((x for x in S.get("gates") or [] if x.get("label") == label), None) | |
| def J(k): | |
| return JUDGE.get(k, f"**[JUDGMENT PENDING: {k}]**") | |
| def Jopt(k): | |
| return JUDGE.get(k, "") | |
| def speed_label(tag, dev): | |
| return f"n-{tag}-{dev}" | |
| def prompt_range(workload="code"): | |
| """Prompt tokens processed by the timed requests of one workload (each carries a unique nonce).""" | |
| lo, hi = [], [] | |
| for b in S.get("bench") or []: | |
| if b.get("workload") != workload: | |
| continue | |
| a_ = b.get("prompt_n_min") if b.get("prompt_n_min") is not None else b.get("prompt_n") | |
| z_ = b.get("prompt_n_max") if b.get("prompt_n_max") is not None else b.get("prompt_n") | |
| if a_ is not None and z_ is not None: | |
| lo.append(a_); hi.append(z_) | |
| if not lo: | |
| return "β" | |
| return f"{min(lo):,}" if min(lo) == max(hi) else f"{min(lo):,}β{max(hi):,}" | |
| def tier(tag): | |
| return T.get(tag) or {} | |
| def tier_row(tag): | |
| t = tier(tag); ratio = t.get("ppl_ratio") | |
| r = [f"`{t.get('file') or 'β'}`", g(t.get("ftype"), "{}"), gib(t.get("size_bytes")), g(t.get("bpw")), | |
| pm(t.get("kld_mean"), t.get("kld_err")), g(t.get("same_top_p"), "{:.2f} %"), | |
| pm(t.get("ppl"), t.get("ppl_err")) + ("" if ratio is None else f" (Γ{ratio:.4f})"), | |
| g(tg(speed_label(tag, "rocm"))), g(tg(speed_label(tag, "vk"))), | |
| g(pp(speed_label(tag, "rocm")), "{:.0f}")] | |
| return "| " + " | ".join(r) + " |" | |
| PN_TXT = prompt_range("code") | |
| PROTOCOL = ( | |
| f"Ryzen AI Max+ 395 (MAX-1), ROCm 7.2.4, unpatched `llama-server` at `d3ca537` (see [Quick start](#quick-start)), " | |
| f"`-c {CTX}`, one request at a time (`--parallel 1`), greedy (`temp 0`, `top_k 1`), `ignore_eos` so every arm " | |
| f"generates exactly {GEN} tokens " | |
| f"after a code prompt of {PN_TXT} tokens (the first 30,000 characters of `convert_hf_to_gguf.py` plus an " | |
| f"instruction), a unique nonce per request and `cache_prompt: false` (`cache_n = 0` asserted on " | |
| f"every timed request), 1 warm-up then the median of {REPS}. Decode numbers are the server's own " | |
| f"`predicted_per_second`. Box iced: no other model loaded.") | |
| TABLE_HEAD = ("| File | ftype | Size | BPWβ΄ | KLD vs BF16 βΒ² | Same top-1 β | PPL (Γ BF16) | TG ROCm0 | TG Vulkan0 | PP ROCm0 |\n" | |
| "| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |") | |
| TG_NOTE = f"TG = decode tokens/s after the {PN_TXT}-token code prompt, no draft head. PP = prefill tokens/s on ROCm0." | |
| def quality_blurb(): | |
| b = S.get("bf16") or {} | |
| ch, nc, st = b.get("chunks"), b.get("n_ctx"), b.get("scored_tokens") | |
| if ch is not None and nc is not None and st is not None: | |
| scored = (f"{ch} chunks Γ {nc // 2 - 1:,} scored tokens each β the second half of every window, less its first token β = {st:,}") | |
| else: | |
| scored = "β chunks Γ β scored tokens" | |
| return (f"Quality is graded against the **BF16 GGUF** (reference logits computed on the CPU) on a **held-out** corpus (wikitext-2 *test*, `-c 2048`, " | |
| f"{scored}), never on the imatrix calibration text. **KLD** is the per-token KL divergence of each " | |
| "quant's next-token distribution from BF16's on the same tokens β far more sensitive than perplexity.") | |
| def bf16_row(): | |
| b = S.get("bf16") or {} | |
| return (f"| *BF16 reference* | {g(b.get('ftype'), '{}')} | {gib(b.get('size_bytes'))}Β³ | {g(b.get('bpw_logged'), '{}')} | 0 | 100 % | " | |
| f"{pm(b.get('ppl_paired'), b.get('ppl_paired_err'))}ΒΉ | β | β | β |") | |
| def footnotes(where="below"): | |
| b = S.get("bf16") or {} | |
| return (f"{TG_NOTE}\n" | |
| f"ΒΉ The BF16 PPL shown is the paired base every \"Γ\" ratio is computed against (averaged over the same scored tokens " | |
| f"in the KL-divergence runs). The standalone BF16 run's own summary line reads {pm(b.get('ppl'), b.get('ppl_err'))}.\n" | |
| f"Β² Quality columns: see *Where the quality numbers come from* {where}.\n" | |
| f"Β³ BF16 conversion of the checkpoint; not published.\n" | |
| f"{bpw_note()}") | |
| def bpw_note(): | |
| b, m = S.get("bf16") or {}, S.get("mmproj") or {} | |
| n, v, p = b.get("elements"), m.get("elements"), SRC.get("params") | |
| s = f"β΄ BPW as printed by `llama-quantize`: bits per weight over the {g(n, '{:,}')} weights in each GGUF." | |
| if None not in (n, v, p) and n + v == p: | |
| s += (f" The {p:,}-parameter count above also includes the {v:,}-weight vision tower, which ships in the " | |
| f"projector file.") | |
| return s | |
| YAML = """--- | |
| license: apache-2.0 | |
| base_model: nex-agi/Nex-N2.5-mini | |
| base_model_relation: quantized | |
| pipeline_tag: image-text-to-text | |
| library_name: gguf | |
| tags: | |
| - gguf | |
| - llama.cpp | |
| - rocm | |
| - amd | |
| - rocmfp4 | |
| - rocmfpx | |
| - strix-halo | |
| - amd-strix-halo | |
| - gfx1151 | |
| - ryzen-ai-max | |
| - ryzen-ai-max-395 | |
| - radeon-8060s | |
| - moe | |
| - reasoning | |
| - multimodal | |
| - vision | |
| - nex | |
| - qwen3.5 | |
| - quantized{extra} | |
| --- | |
| """ | |
| def cmake_block(): | |
| commit = B.get("commit") or "d3ca537" | |
| return f"""```bash | |
| git clone https://github.com/charlie12345/ROCmFPX.git && cd ROCmFPX | |
| git checkout {commit} | |
| HIPCXX="$(hipconfig -l)/clang" HIP_PATH="$(hipconfig -R)" \\ | |
| cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \\ | |
| -DGGML_HIP=ON -DGGML_VULKAN=ON -DGPU_TARGETS=gfx1151 \\ | |
| -DGGML_HIP_GRAPHS=ON -DGGML_HIP_NO_VMM=ON -DLLAMA_CURL=OFF | |
| cmake --build build --target llama-server -j | |
| ```""" | |
| def serve_block(model_file): | |
| env = ("env LD_LIBRARY_PATH=$PWD/build/bin:/opt/rocm/lib HSA_OVERRIDE_GFX_VERSION=11.5.1 " | |
| "GGML_HIP_ENABLE_UNIFIED_MEMORY=1 \\\n") | |
| mm = f" --mmproj ~/models/nex/mmproj-{N}-BF16.gguf \\\n" | |
| tpl = (" --chat-template-file ~/models/nex/chat_template_enable_thinking.jinja --reasoning off \\\n") | |
| tail = f" -ngl 999 -fa on -dio --jinja -fit off --parallel 1 -dev ROCm0 \\\n -c {CTX} --host 127.0.0.1 --port 8080" | |
| head = f"build/bin/llama-server \\\n -m ~/models/nex/{model_file} \\\n" | |
| cmd = f"```bash\n{env}{head}{mm}{tpl}{tail}\n```" | |
| w = Jopt("vision_quickstart_warning") | |
| return cmd + ("\n\n" + w if w else "") | |
| def curl_block(): | |
| return """```bash | |
| curl http://127.0.0.1:8080/v1/chat/completions \\ | |
| -H 'Content-Type: application/json' \\ | |
| -d '{ | |
| "messages": [{"role": "user", "content": "Hello"}], | |
| "temperature": 0.7, | |
| "top_p": 0.95, | |
| "top_k": 40, | |
| "chat_template_kwargs": {"enable_thinking": true} | |
| }' | |
| ```""" | |
| def quick_start(model_file, repo): | |
| return f"""**1. Download** | |
| ```bash | |
| hf download {repo} --local-dir ~/models/nex | |
| ``` | |
| **2. Build `llama-server`** β ROCmFPX at the measured commit (ROCm and Vulkan | |
| prerequisites: the project's [build guide](https://github.com/charlie12345/ROCmFPX/blob/{B.get('commit') or 'd3ca537'}/docs/build.md)). No patch. | |
| {cmake_block()} | |
| (The CMake options of the measured build are listed in [Reproduction](#reproduction).) | |
| `d3ca537` is also in the history of the official [ROCmFPX/ROCmFPX](https://github.com/ROCmFPX/ROCmFPX) repository. | |
| **3. Serve** | |
| {serve_block(model_file)} | |
| (`LD_LIBRARY_PATH` avoids a soname clash on machines that also have a Vulkan-only llama.cpp build.) The exact measured | |
| argv is in [Reproduction](#reproduction). | |
| **4. Call** β upstream sampling. Thinking is off unless the request sets `"enable_thinking": true` (as here; drop that | |
| line for a direct answer): | |
| {curl_block()} | |
| | Flag | Why | | |
| | --- | --- | | |
| | `--chat-template-file β¦/chat_template_enable_thinking.jinja` | The model's own template plus one line (see [Reasoning controls](#reasoning-controls)). Without it llama-server leaves the reasoning in `content` and thinking-on tool calls fail ([measured](#tool-calling)). | | |
| | `--reasoning off` | Thinking stays off unless a request passes `"enable_thinking": true`. | | |
| | `--jinja` | Already on by default in this build; keep it on β the reasoning controls (`chat_template_kwargs`) and tool calling rely on the Jinja chat template. | | |
| | `-fit off` | Autofit reads `MemAvailable` on integrated GPUs and can silently shrink context or push tensors to CPU. | | |
| | `-cram <MiB>` | Not set above (default 8 GiB of host RAM for saved prompts). Set it on a shared box β see [Known issues](#known-issues-and-limits). | | |
| | `--mmproj` | Loads the {AR.get('vision_layers', 'β')}-layer vision tower. Drop the flag for text-only. | | |
| Requires a llama.cpp build with ROCmFP4 / ROCmFPX tensor-type support; stock llama.cpp rejects these tensor types.""" | |
| def reasoning_block(): | |
| tf = S.get("template_fix") or {} | |
| shim = (tf.get("shim") or "").rstrip("\n") | |
| shim_md = ("```jinja\n" + shim + "\n```") if shim else "**[JUDGMENT PENDING: template shim]**" | |
| return f"""## Reasoning controls | |
| The model's own chat template switches thinking with `chat_template_kwargs.reasoning_effort` and ignores | |
| `enable_thinking`: | |
| | `reasoning_effort` | What the stock template emits | | |
| | --- | --- | | |
| | `"none"` | empty `<think>\\n\\n</think>` (no thinking) | | |
| | `"high"` | opens `<think>\\n` (always think) | | |
| | `"medium"`, unset, or anything else | opens `<think>` and lets the model decide (adaptive; upstream default is `"medium"`) | | |
| llama-server decides how to split reasoning from the answer by rendering the template with `enable_thinking` on and | |
| off. With this template both renders are the same, so it never extracts the reasoning ([measured](#tool-calling)). | |
| `chat_template_enable_thinking.jinja` in this repo is the model's `chat_template.jinja` (sha256 | |
| `{tf.get('source_sha256') or 'β'}`) with one line added at the top (file sha256 `{tf.get('sha256') or 'β'}`): | |
| {shim_md} | |
| Serve it with `--chat-template-file` and `--reasoning off`. | |
| {J('template_note')} | |
| Upstream serving (SGLang) uses `--reasoning-parser qwen3 --tool-call-parser qwen3_coder`. Recommended sampling: | |
| temperature 0.7, top_p 0.95, top_k 40. | |
| Earlier assistant turns are re-rendered **with** their reasoning (contexts grow faster than with templates that drop | |
| it). With thinking on and a small `max_tokens`, the whole budget can go to reasoning and `content` comes back empty β | |
| raise `max_tokens` before concluding the model is broken. | |
| Tool calls use the XML-style `<tool_call><function=β¦><parameter=β¦>` format, which llama.cpp parses natively | |
| through the Jinja chat template (on by default).""" | |
| def speed_table(): | |
| rows = ["| File | Backend | Workload | Decode tok/s (minβmax) | Prefill tok/s |", | |
| "| --- | --- | --- | ---: | ---: |"] | |
| for tag in STD_TAGS + IMAT_TAGS: | |
| t = tier(tag) | |
| fn = t.get("file") or "β" | |
| for dev, dn in (("rocm", "ROCm0"), ("vk", "Vulkan0")): | |
| b = bench(speed_label(tag, dev)) | |
| if b is None or b.get("tg_median") is None: | |
| rows.append(f"| `{fn}` | {dn} | code | β | β |") | |
| else: | |
| rows.append(f"| `{fn}` | {dn} | code | {b['tg_median']:.2f} ({g(b.get('tg_min'))}β{g(b.get('tg_max'))}) | " | |
| f"{g(b.get('pp_median'), '{:.0f}')} |") | |
| for lab, dn in (("n-q106-rocm-prose", "ROCm0"), ("n-q106-vk-prose", "Vulkan0")): | |
| b = bench(lab) | |
| fn = tier("q106").get("file") or "β" | |
| if b is None or b.get("tg_median") is None: | |
| rows.append(f"| `{fn}` | {dn} | prose | β | β |") | |
| else: | |
| rows.append(f"| `{fn}` | {dn} | prose | {b['tg_median']:.2f} ({g(b.get('tg_min'))}β{g(b.get('tg_max'))}) | " | |
| f"{g(b.get('pp_median'), '{:.0f}')} |") | |
| return "\n".join(rows) | |
| def cache_table(): | |
| x = gate("n-c3-q106") | |
| rows = ["| server | second-request prompt tokens reused | processed | warm reply = cold reply |", | |
| "| --- | ---: | ---: | :---: |"] | |
| if not x: | |
| rows.append("| d3ca537, unpatched | β | β | β |") | |
| return "\n".join(rows) | |
| n, L, ident = x.get("n"), x.get("L"), x.get("identical") | |
| got = sorted({r_.get("warm_cache_n") for r_ in x.get("rows") or []}, key=lambda v: (v is None, v)) | |
| if n is None or L is None or not got or None in got: | |
| rows.append("| d3ca537, unpatched | β | β | β |") | |
| return "\n".join(rows) | |
| if len(got) == 1: | |
| reused, proc = f"**{got[0]:,}** of {L:,} (all {n} pairs)", f"{L - got[0]:,}" | |
| else: | |
| reused, proc = f"{got[0]:,}β{got[-1]:,} of {L:,} (varies across {n} pairs)", f"{L - got[-1]:,}β{L - got[0]:,}" | |
| rows.append(f"| d3ca537, unpatched | {reused} | {proc} | {ident if ident is not None else 'β'}/{n} |") | |
| return "\n".join(rows) | |
| def tools_block(): | |
| t = gate("n-tools-q106") | |
| fx = [gate(l) for l in ("n-tools-q106-roff", "n-tools-q106-roff-r2", "n-tools-q106-roff-r3")] | |
| if not t and not any(fx): | |
| return "_Not measured._" | |
| names = ["multi-arg", "nested-object", "enum", "correct-decline", "multi-turn", "streaming", "parallel"] | |
| rows = ["| check | quick start, thinking ON | quick start, thinking OFF | stock template, thinking ON | " | |
| "stock template, thinking OFF |", | |
| "| --- | :---: | :---: | :---: | :---: |"] | |
| detail = (t or {}).get("detail") or {} | |
| mk = lambda x: "β" if x is None else ("β " if x else "β") | |
| def count(n, think): | |
| vals = [((x or {}).get("detail") or {}).get(f"{n}|think={think}") for x in fx] | |
| if any(v is None for v in vals): | |
| return "β" | |
| return f"{sum(bool(v) for v in vals)}/{len(vals)}" | |
| for n in names: | |
| rows.append(f"| {n} | {count(n, True)} | {count(n, False)} | {mk(detail.get(f'{n}|think=True'))} | " | |
| f"{mk(detail.get(f'{n}|think=False'))} |") | |
| fn = os.path.basename(tier("q106").get("file") or "β") | |
| tot = (f"**{sum(x['passed'] for x in fx)}/{sum(x['total'] for x in fx)}** over three passes with the quick-start " | |
| f"configuration, **{t['passed']}/{t['total']}** with the stock template" | |
| if all(fx) and t and t.get("passed") is not None else "Tool-calling suite") | |
| return (f"{tot}, run on `{fn}`. Quick start = the included template file + `--reasoning off`, thinking switched " | |
| f"with `enable_thinking`; stock = the model's own template, thinking switched with `reasoning_effort` " | |
| f"(`high` / `none`). A check passes only with a native `tool_calls` entry carrying the right arguments " | |
| f"and no raw XML or think tags left in `content`.\n\n" + "\n".join(rows)) | |
| def vision_block(): | |
| on, off = gate("n-vision-q106-faon"), gate("n-vision-q106-faoff") | |
| if not on and not off: | |
| return "_Not measured._" | |
| vp = S.get("vision_probe") or {} | |
| exp_row = next((x for x in (on, off) if x and x.get("expected")), None) | |
| exp_txt = ", ".join(f"`{e.strip()}`" for e in exp_row["expected"].split(",")) if exp_row else "β" | |
| def cell(x): | |
| if not x: | |
| return "β" | |
| exp = x.get("expected") or "" | |
| nexp = len(exp.split(",")) if exp else None | |
| hits = x.get("hits") or [] | |
| n = f"{len(hits)}/{nexp} terms" if nexp is not None else "β" | |
| if x.get("result") == "PASS": | |
| return f"β {n}" | |
| return "β " + ("server stopped" if x.get("server_died") else ("request failed" if x.get("error") else n)) | |
| rows = ["| | `-fa on` | `-fa off` |", "| --- | :---: | :---: |", | |
| f"| STRIX_LEAN + projector | {cell(on)} | {cell(off)} |"] | |
| txt = (f"Probe: a synthetic {vp.get('width', 'β')}Γ{vp.get('height', 'β')} image with a red circle and a blue square " | |
| f"(a model that ignores the image cannot name both), sent to `{tier('q106').get('file') or 'β'}` with " | |
| f"`--mmproj`, temperature 0. Pass = the reply names every expected term ({exp_txt}).\n\n" | |
| + "\n".join(rows) + "\n\n" + J("vision_note")) | |
| ans = next((x for x in (on, off) if x and x.get("result") == "PASS" and x.get("answer")), None) | |
| if ans: | |
| a_ = (ans.get("answer") or "").strip() | |
| which = "`-fa on`" if ans is on else "`-fa off`" | |
| cut = a_[:300] | |
| txt += f"\n\nReply ({which}):\n\n> {cut}" + (" β¦" if len(a_) >= 300 else "") | |
| return txt | |
| def files_table(names): | |
| rows = ["| File | Size | sha256 |", "| --- | ---: | --- |"] | |
| for fn, size in names: | |
| size = SIZES.get(fn, size) | |
| sz = ("β" if size is None else gib(size) if size >= GiB // 10 else | |
| f"{size / MiB:.1f} MiB" if size >= MiB else f"{size / 1024:.1f} KiB") | |
| rows.append(f"| `{fn}` | {sz} | `{SUMS.get(fn, 'β')}` |") | |
| return "\n".join(rows) | |
| def receipts_table(tags, imat=False): | |
| head = ("| File | `output.weight` | `token_embd.weight` | tensors |" | |
| + (" imatrix entries | bytes differ from standard |" if imat else "")) | |
| sep = "| --- | --- | --- | ---: |" + (" ---: | :---: |" if imat else "") | |
| rows = [head, sep] | |
| for k in tags: | |
| t = tier(k) | |
| r = (f"| `{t.get('file') or 'β'}` | {t.get('output_weight') or 'β'} | {t.get('token_embd') or 'β'} | " | |
| f"{g(t.get('tensors'), '{}')} |") | |
| if imat: | |
| dfs = t.get("differs_from_standard") | |
| r += f" {t.get('imatrix_entries') if t.get('imatrix_entries') is not None else 'β'} | " | |
| r += f"{'yes' if dfs else ('no' if dfs is False else 'β')} |" | |
| rows.append(r) | |
| return "\n".join(rows) | |
| def repro(model_file, label): | |
| b = bench(label) | |
| sha = B.get("sha256") or {} | |
| cmd = (b.get("cmd") if b else None) or "β" | |
| return f"""``` | |
| server : {B.get('repo') or 'β'} @ {B.get('commit') or 'β'} | |
| unpatched; build dir {os.path.dirname(B['dir']) if B.get('dir') else 'β'}, Release, Unix Makefiles, GGML_HIP=ON GGML_VULKAN=ON | |
| GGML_HIP_GRAPHS=ON GGML_HIP_NO_VMM=ON GGML_NATIVE=ON AMDGPU_TARGETS=gfx1151 LLAMA_CURL=OFF | |
| CMAKE_HIP_COMPILER=/opt/rocm-7.2.4/lib/llvm/bin/clang | |
| sha256 llama-quantize {sha.get('llama-quantize') or 'β'} | |
| sha256 llama-imatrix {sha.get('llama-imatrix') or 'β'} | |
| sha256 llama-perplexity {sha.get('llama-perplexity') or 'β'} | |
| sha256 llama-server {sha.get('llama-server') or 'β'} | |
| source : {SRC.get('repo') or 'β'} revision {SRC.get('revision') or 'β'} | |
| model : {model_file} (the argv below; every file was measured the same way) | |
| argv : {cmd} | |
| template : the quick-start tool-suite and image rows add --chat-template-file chat_template_enable_thinking.jinja | |
| --reasoning off to this argv (recipe/pipeline/run_tools_roff.sh -> nex_tools_tpl.py; their server logs | |
| read "chat template, thinking = 0"); the speed rows use the stock template | |
| env : LD_LIBRARY_PATH=<build>/bin:/opt/rocm-7.2.4/lib | |
| HSA_OVERRIDE_GFX_VERSION=11.5.1 GGML_HIP_ENABLE_UNIFIED_MEMORY=1 | |
| box : aimax β AMD Ryzen AI Max+ 395 / Radeon 8060S (gfx1151), 124 GiB, GTT 131072 MiB, | |
| kernel 6.17.6-061706-generic, ROCm 7.2.4 | |
| protocol : {PN_TXT}-token code prompt, {GEN} generated tokens, temp 0 / top_k 1, ignore_eos, cache_prompt false, | |
| 1 warm-up + median of {REPS}, no co-resident models (box iced) | |
| measured : {' to '.join(S.get('measured_range') or []) or S.get('measured') or 'β'}, by the pipeline in recipe/ (every raw number in recipe/results_summary.json and recipe/raw/) | |
| ```""" | |
| def methodology_std(): | |
| return f"""```bash | |
| # 1. convert: text model and the vision projector (the checkpoint has no mtp.* tensors) | |
| python convert_hf_to_gguf.py hf --outtype bf16 --model-name {N} --outfile {N}-BF16.gguf | |
| python convert_hf_to_gguf.py hf --outtype bf16 --mmproj --model-name {N} --outfile mmproj-{N}-BF16.gguf | |
| # 2. quantize from BF16 only; the LM head is forced up on every tier and read back by exact tensor name | |
| llama-quantize --output-tensor-type q6_K {N}-BF16.gguf OUT Q4_0_ROCMFP4_STRIX_LEAN 16 | |
| llama-quantize --output-tensor-type q6_K --token-embedding-type q6_K {N}-BF16.gguf OUT Q4_0_ROCMFP4_COHERENT 16 | |
| llama-quantize --output-tensor-type q6_K {N}-BF16.gguf OUT Q4_0_ROCMFP4_FAST 16 | |
| # 3. BF16 reference logits on the CPU only (this build's ROCm0 path computes the BF16 MoE wrong β Known issues) | |
| llama-perplexity -m {N}-BF16.gguf -f wikitext-2-raw/wiki.test.raw -c 2048 -b 2048 --chunks 40 --kl-divergence-base bf16.kld \\ | |
| -dev none -ngl 0 --no-op-offload -t 16 | |
| # 4. grade each shipped file against those logits, on each GPU backend | |
| llama-perplexity -m OUT --kl-divergence-base bf16.kld --kl-divergence -c 2048 -b 2048 -ngl 999 -fa on -dio -dev ROCm0 | |
| llama-perplexity -m OUT --kl-divergence-base bf16.kld --kl-divergence -c 2048 -b 2048 -ngl 999 -fa on -dio -dev Vulkan0 | |
| ``` | |
| Receipts (the built file is the receipt β exact tensor names, never a substring match; `recipe/logs/`): | |
| {receipts_table(STD_TAGS)}""" | |
| def first_rocm_bullet(): | |
| hc = S.get("hub_check") | |
| others = [h for h in (hc or {}).get("header_checks") or [] if h.get("output_weight_type")] | |
| if others: | |
| return "".join( | |
| f"- Another public ROCmFP4 build of this model exists β [{h['repo']}](https://huggingface.co/{h['repo']}): " | |
| f"its `{h['file']}` stores `output.weight` as `{h['output_weight_type']}`" | |
| f"{' and carries no imatrix metadata' if h.get('imatrix_keys') == [] else ''}. Every tier here keeps " | |
| f"`output.weight` at `Q6_K`, and the imatrix builds are a separate repo.\n" for h in others) | |
| if isinstance(hc, dict) and hc.get("rocm_builds_found") == 0: | |
| return ("- **First ROCmFP4 build of this model** β no ROCm or Strix Halo build of Nex-N2.5-mini was on the Hub " | |
| "at publication.\n") | |
| return "" | |
| def intro_arch(): | |
| p = SRC.get("params") | |
| ptxt = f"{p:,} parameters (BF16)" if p is not None else "β parameters" | |
| return (f"{AR.get('layers', 'β')}-layer Qwen3.5 MoE ({AR.get('linear_attn_layers', 'β')} Gated DeltaNet linear-attention + " | |
| f"{AR.get('full_attn_layers', 'β')} full-attention layers), {AR.get('num_experts', 'β')} routed experts / " | |
| f"{AR.get('num_experts_per_tok', 'β')} active, {AR.get('max_position_embeddings', 'β'):,}-token context" | |
| if AR.get("max_position_embeddings") is not None else | |
| f"{AR.get('layers', 'β')}-layer Qwen3.5 MoE, {ptxt}") | |
| def std_card(): | |
| L = tier("q106") | |
| bf = S.get("bf16") or {} | |
| nextn = bf.get("nextn_tensors") | |
| no_mtp = ("the checkpoint ships no `mtp.*` weights" + | |
| (f" (the converted BF16 GGUF reads back {nextn} `nextn` tensors)" if nextn is not None else "")) | |
| return YAML.format(extra="") + f""" | |
| # Nex-N2.5-mini β ROCmFP4 for AMD Strix Halo (gfx1151) | |
| ROCmFP4 / ROCmFPX quantizations of **[nex-agi/Nex-N2.5-mini](https://huggingface.co/nex-agi/Nex-N2.5-mini)** β | |
| {g(SRC.get('params'), '{:,}')} parameters (BF16), {intro_arch()}, text + image β built and measured on an AMD Ryzen AI | |
| Max+ 395 (Radeon 8060S, `gfx1151`). Upstream publishes no GGUF. | |
| {first_rocm_bullet()}- **Vision projector included.** | |
| - **No MTP head.** `mtp_num_hidden_layers: 1` is declared in `config.json`, but {no_mtp}. There is no speculative | |
| decoding on these files. | |
| - Importance-matrix builds of the same three 4-bit tiers: **[{IMAT}](https://huggingface.co/{IMAT})**. | |
| ## Which file should I use? | |
| {PROTOCOL} | |
| {TABLE_HEAD} | |
| {chr(10).join(tier_row(k) for k in STD_TAGS)} | |
| {bf16_row()} | |
| {footnotes()} | |
| {J('std_recommendation')} | |
| {quality_blurb()} | |
| **Where the quality numbers come from.** {J('quality_provenance')} | |
| {J('backend_quality_note')} | |
| ## Quick start | |
| {quick_start(JUDGE.get('std_default') or L.get('file') or 'β', STD)} | |
| {reasoning_block()} | |
| ## Speed | |
| {speed_table()} | |
| {J('speed_note')} | |
| ## Prompt caching | |
| Measured: pairs of requests that share a long code prefix and differ only in the closing instruction. The second | |
| request of each pair runs warm (`cache_prompt: true`, resuming from what the first one left) and then cold | |
| (`cache_prompt: false`), and the two replies are compared byte for byte. Every prompt is padded to one token length so | |
| warm and cold see identical chunking. | |
| {cache_table()} | |
| {J('cache_note')} | |
| ## Tool calling | |
| The template emits the XML-style `<tool_call><function=β¦><parameter=β¦>` format, which llama.cpp parses natively | |
| through the Jinja chat template (on by default). Suite run through `llama-server`, at the checkpoint's recommended | |
| sampling (temperature 0.7, top-p 0.95, top-k 40): | |
| {tools_block()} | |
| {J('tools_note')} | |
| ## Vision | |
| `mmproj-{N}-BF16.gguf` is the {AR.get('vision_layers', 'β')}-layer vision tower (width {AR.get('vision_width', 'β')}), | |
| loaded with `--mmproj`. Its attention follows the server's `-fa` setting, so both settings were checked. | |
| {vision_block()} | |
| ## Memory | |
| {J('memory_note')} | |
| ## Quantization methodology | |
| {methodology_std()} | |
| `tie_word_embeddings` is false, so the output head is a real tensor and `--output-tensor-type q6_K` does real work. | |
| All three tiers pin `output.weight` to `q6_K`; COHERENT also pins `token_embd.weight` to `q6_K`, while STRIX_LEAN and | |
| FAST keep their tier's own embedding type (shown in the receipts). | |
| ## Reproduction | |
| {repro(L.get('file') or 'β', 'n-q106-rocm')} | |
| ## Files | |
| {files_table([(tier(k).get('file'), tier(k).get('size_bytes')) for k in STD_TAGS] + list((S.get('aux') or {}).items()))} | |
| `SHA256SUMS` covers every model file and the chat template file. `recipe/` holds the measurement pipeline (`recipe/pipeline/`), raw per-run | |
| results (`recipe/raw/`), build and receipt logs (`recipe/logs/`), and `results_summary.json` with every measured value | |
| on this card. Architecture facts (layer counts, vocabulary, vision depth) come from the checkpoint's `config.json` at | |
| revision `{SRC.get('revision') or 'β'}`. | |
| ## Known issues and limits | |
| {J('std_known_issues')} | |
| ## License and attribution | |
| Apache-2.0, inherited from the base model. Weights and architecture: **Nex-AGI** | |
| ([nex-agi/Nex-N2.5-mini](https://huggingface.co/nex-agi/Nex-N2.5-mini)). ROCmFP4 / ROCmFPX quantization format and | |
| runtime: the ROCmFPX project. Quantization and measurements: kingjones777. | |
| """ | |
| def imat_effect_table(): | |
| rows = ["| Tier | Build | Size | KLD vs BF16 β | Same top-1 β | PPL (Γ BF16) | 99th-pct KLD |", | |
| "| --- | --- | ---: | ---: | ---: | ---: | ---: |"] | |
| def d(a, b, k, ek): | |
| x, y = tier(a).get(k), tier(b).get(k) | |
| ex, ey = (tier(a).get(ek), tier(b).get(ek)) if ek else (None, None) | |
| if None in (x, y): | |
| return "β" | |
| s = f"{(y - x) / x * 100:+.1f} %" | |
| if ex is not None and ey is not None: | |
| s += f" ({abs(y - x) / math.sqrt(ex * ex + ey * ey):.1f}Ο)" | |
| return s | |
| for base, imat in (("q106", "q106i"), ("q102", "q102i"), ("q103", "q103i")): | |
| for tag, lab in ((base, "standard"), (imat, "**imatrix**")): | |
| x = tier(tag) | |
| ratio = x.get("ppl_ratio") | |
| rows.append(f"| {NAMES[tag]} | {lab} | {gib(x.get('size_bytes'))} | {pm(x.get('kld_mean'), x.get('kld_err'))} | " | |
| f"{g(x.get('same_top_p'), '{:.2f} %')} | {pm(x.get('ppl'), x.get('ppl_err'))} " | |
| f"({'β' if ratio is None else 'Γ%.4f' % ratio}) | {g(x.get('kld_p99'), '{:.4f}')} |") | |
| dpp = ("β" if None in (tier(base).get("same_top_p"), tier(imat).get("same_top_p")) | |
| else f"{tier(imat)['same_top_p'] - tier(base)['same_top_p']:+.2f} pp") | |
| rows.append(f"| | *Ξ imatrix* | | {d(base, imat, 'kld_mean', 'kld_err')} | {dpp} | " | |
| f"{d(base, imat, 'ppl', 'ppl_err')} | {d(base, imat, 'kld_p99', None)} |") | |
| return "\n".join(rows) | |
| def imat_card(): | |
| im = S.get("imatrix") or {} | |
| Li = tier("q106i") | |
| entries = [tier(k).get("imatrix_entries") for k in IMAT_TAGS] | |
| ent = next((e for e in entries if e is not None), None) | |
| ent_txt = g(ent, "{:,}") if (ent is None or len(set(e for e in entries if e is not None)) <= 1) else \ | |
| " / ".join(g(e, "{:,}") for e in entries) | |
| return YAML.format(extra="\n - imatrix") + f""" | |
| # Nex-N2.5-mini β ROCmFP4 **imatrix** for AMD Strix Halo (gfx1151) | |
| Importance-matrix-calibrated ROCmFP4 quantizations of | |
| **[nex-agi/Nex-N2.5-mini](https://huggingface.co/nex-agi/Nex-N2.5-mini)** ({g(SRC.get('params'), '{:,}')} parameters, | |
| {intro_arch()}, text + image). Companion to the standard build **[{STD}](https://huggingface.co/{STD})** β the same | |
| three 4-bit tiers, same vision projector, same unpatched `d3ca537` server; the only difference in the weights is how | |
| each 4-bit block's scale was chosen. There is no MTP head on either repo. | |
| ## What the imatrix changes | |
| ROCmFP4 has an importance-weighted quantizer path: with `--imatrix`, each block's scale is chosen by an exhaustive | |
| search that minimises error **weighted by how strongly the calibration activations use each weight**, instead of the | |
| unweighted default. It changes **which** scales are picked at the **same** bit width and tensor types β so it moves | |
| quality, not size, and per-token compute is identical. | |
| | | | | |
| | --- | --- | | |
| | calibration text | {im.get('calibration') or 'β'} (the widely used community calibration set) | | |
| | computed on | BF16 GGUF, {g(im.get('chunks'), '{}')} chunks Γ {g(im.get('n_ctx'), '{}')} tokens, {im.get('device') or 'β'} | | |
| | entries loaded | {ent_txt} (from the N3 quantize logs) | | |
| | file | `{im.get('file') or 'β'}` (GGUF format), sha256 `{im.get('sha256') or 'β'}` | | |
| ## Measured effect | |
| {quality_blurb()} The calibration text and the grading text are different corpora. | |
| {imat_effect_table()} | |
| Ο = difference divided by the two runs' combined standard error. The two runs score the **same** tokens, so this is | |
| conservative (paired noise is smaller). | |
| {J('imat_verdict')} | |
| **Where the quality numbers come from.** {J('quality_provenance')} | |
| {J('backend_quality_note')} | |
| ## Which file should I use? | |
| {J('imat_recommendation')} | |
| {TABLE_HEAD} | |
| {chr(10).join(tier_row(k) for k in IMAT_TAGS)} | |
| {bf16_row()} | |
| {footnotes("above")} | |
| {J('imat_speed_note')} | |
| Full speed tables (both backends, prose vs code), prompt-cache, tool-calling and vision results are on | |
| the [standard card](https://huggingface.co/{STD}). | |
| ## Quick start | |
| {quick_start(JUDGE.get('imat_default') or Li.get('file') or 'β', IMAT)} | |
| {reasoning_block()} | |
| ## Quantization methodology | |
| ```bash | |
| llama-imatrix -m {N}-BF16.gguf -f calibration_datav3.txt -o {N}.imatrix \\ | |
| -c 512 -b 512 -dev none -ngl 0 --no-op-offload -t 16 | |
| llama-quantize --imatrix {N}.imatrix --output-tensor-type q6_K \\ | |
| {N}-BF16.gguf {N}-imatrix-Q4_0_ROCMFP4_STRIX_LEAN.gguf Q4_0_ROCMFP4_STRIX_LEAN 16 | |
| llama-quantize --imatrix {N}.imatrix --output-tensor-type q6_K --token-embedding-type q6_K \\ | |
| {N}-BF16.gguf {N}-imatrix-Q4_0_ROCMFP4_COHERENT.gguf Q4_0_ROCMFP4_COHERENT 16 | |
| llama-quantize --imatrix {N}.imatrix --output-tensor-type q6_K \\ | |
| {N}-BF16.gguf {N}-imatrix-Q4_0_ROCMFP4_FAST.gguf Q4_0_ROCMFP4_FAST 16 | |
| ``` | |
| Receipts that the weighted path was actually taken, and that each shipped file differs from its standard twin: | |
| {receipts_table(IMAT_TAGS, imat=True)} | |
| ## Reproduction | |
| {repro(Li.get('file') or 'β', 'n-q106i-rocm')} | |
| ## Files | |
| {files_table([(tier(k).get('file'), tier(k).get('size_bytes')) for k in IMAT_TAGS] + [(im.get('file'), im.get('size_bytes'))] + list((S.get('aux') or {}).items()))} | |
| ## Known issues and limits | |
| {J('imat_known_issues')} | |
| ## License and attribution | |
| Apache-2.0, inherited from the base model. Weights and architecture: **Nex-AGI** | |
| ([nex-agi/Nex-N2.5-mini](https://huggingface.co/nex-agi/Nex-N2.5-mini)). Calibration text: bartowski's | |
| `calibration_datav3`. ROCmFP4 / ROCmFPX: the ROCmFPX project. Imatrix, quantization and measurements: kingjones777. | |
| """ | |
| open(os.path.join(OUT, "README_std.md"), "w").write(std_card()) | |
| def fix_anchors(md, other_repo): | |
| """Links to sections that exist only on the other card point there instead of to a missing anchor.""" | |
| import re | |
| slugs = {re.sub(r"[^a-z0-9 -]", "", h.strip().lower()).replace(" ", "-") | |
| for h in re.findall(r"^#{1,6} (.+)$", md, flags=re.M)} | |
| return re.sub(r"\]\(#([a-z0-9-]+)\)", | |
| lambda m: m.group(0) if m.group(1) in slugs else "](https://huggingface.co/%s#%s)" % ( | |
| other_repo, m.group(1)), md) | |
| open(os.path.join(OUT, "README_imat.md"), "w").write(fix_anchors(imat_card(), STD)) | |
| cards = open(os.path.join(OUT, "README_std.md")).read() + open(os.path.join(OUT, "README_imat.md")).read() | |
| pend = sorted(set(x.split("JUDGMENT PENDING: ")[1].split("]")[0] for x in cards.split("**[")[1:] if "JUDGMENT PENDING" in x)) | |
| dash_cells = cards.count("| β |") | |
| print("rendered | bench rows =", len(S.get("bench") or []), "| gates =", len(S.get("gates") or []), | |
| "| pending judgments:", pend, "| 'β' cells:", dash_cells) | |