3v324v23 Claude commited on
Commit
44bdb86
·
1 Parent(s): 49ddf6f

Recognize ROCmFP4/ROCmFPX GGUF quants

Browse files

These are AMD ROCm floating-point block-quant formats from the
ciru-ai/ROCmFPX fork; stock llama.cpp cannot run them. Add their
effective bits-per-weight to the quant table (4.34 for ROCmFP4,
7.08 for ROCmFPX), derived from the real chadrock3.6 GGUF file
sizes (reproduces 14.82 GiB / 31.42 GiB within rounding).

quant_from_filename already matches the longer names first, so
the chadrock filenames resolve to ROCmFP4/ROCmFPX correctly.
command_preview switches to the rocmfpx-llama-server runner and
adds a note that the pinned ROCmFPX build is required. The UI
fetch handler now syncs the Quantization dropdown to the quant
detected from the chosen GGUF filename. Four new tests.

Co-Authored-By: Claude <noreply@anthropic.com>

Files changed (4) hide show
  1. app.py +24 -8
  2. tests/test_vramcalc.py +35 -0
  3. vramcalc/quant.py +11 -0
  4. vramcalc/report.py +14 -1
app.py CHANGED
@@ -60,25 +60,41 @@ def list_gguf_files(repo_id: str, hf_token: str):
60
 
61
 
62
  def fetch_arch(repo_id: str, filename: str, hf_token: str):
63
- """Range-read a GGUF header from HF and return editable arch fields."""
 
 
 
 
 
 
 
 
 
 
 
64
  if not repo_id or not filename:
65
- return _empty_arch_fields(), "Pick a GGUF file first."
66
  try:
67
  meta = parse_hf_range(
68
  repo_id.strip(), filename, token=hf_token or None
69
  )
70
  except Exception as e: # noqa: BLE001
71
- return _empty_arch_fields(), f"Error reading GGUF header: {e}"
 
72
  if not meta.n_layer:
73
- return _arch_to_fields(meta), (
 
74
  "Parsed header but architecture fields look empty; "
75
- "edit them manually below."
 
76
  )
77
- return _arch_to_fields(meta), (
 
78
  f"Fetched {meta.architecture or 'model'}: "
79
  f"{meta.n_layer} layers, {meta.n_embd} embd, "
80
  f"{meta.n_head}/{meta.n_head_kv} heads, ctx {meta.training_ctx}, "
81
- f"params {meta.params or 'n/a'}."
 
82
  )
83
 
84
 
@@ -354,7 +370,7 @@ def build_ui():
354
  )
355
  fetch_btn.click(
356
  fn=fetch_arch, inputs=[repo_id, file_picker, hf_token],
357
- outputs=[*arch_inputs, fetch_status],
358
  ).then(
359
  fn=_store_arch, inputs=arch_inputs, outputs=arch_state
360
  )
 
60
 
61
 
62
  def fetch_arch(repo_id: str, filename: str, hf_token: str):
63
+ """Range-read a GGUF header from HF and return editable arch fields.
64
+
65
+ Also returns a gr.update for the Quantization dropdown that syncs it to the
66
+ quant detected from the filename (e.g. ROCmFP4/ROCmFPX) — a no-op when the
67
+ filename has no recognized quant token, so the user's manual pick is kept.
68
+ The GGUF header's tensor types for ROCm formats use custom IDs stock
69
+ llama.cpp does not map, so the filename is the reliable source here.
70
+ """
71
+ detected = quant_from_filename(filename) if filename else None
72
+ if detected and detected not in QUANT_CHOICES:
73
+ detected = None
74
+ quant_update = gr.update(value=detected) if detected else gr.update()
75
  if not repo_id or not filename:
76
+ return _empty_arch_fields(), "Pick a GGUF file first.", quant_update
77
  try:
78
  meta = parse_hf_range(
79
  repo_id.strip(), filename, token=hf_token or None
80
  )
81
  except Exception as e: # noqa: BLE001
82
+ return _empty_arch_fields(), f"Error reading GGUF header: {e}", quant_update
83
+ quant_note = f", quant {detected}" if detected else ""
84
  if not meta.n_layer:
85
+ return (
86
+ _arch_to_fields(meta),
87
  "Parsed header but architecture fields look empty; "
88
+ "edit them manually below.",
89
+ quant_update,
90
  )
91
+ return (
92
+ _arch_to_fields(meta),
93
  f"Fetched {meta.architecture or 'model'}: "
94
  f"{meta.n_layer} layers, {meta.n_embd} embd, "
95
  f"{meta.n_head}/{meta.n_head_kv} heads, ctx {meta.training_ctx}, "
96
+ f"params {meta.params or 'n/a'}{quant_note}.",
97
+ quant_update,
98
  )
99
 
100
 
 
370
  )
371
  fetch_btn.click(
372
  fn=fetch_arch, inputs=[repo_id, file_picker, hf_token],
373
+ outputs=[*arch_inputs, fetch_status, quant],
374
  ).then(
375
  fn=_store_arch, inputs=arch_inputs, outputs=arch_state
376
  )
tests/test_vramcalc.py CHANGED
@@ -24,6 +24,11 @@ def test_quant_table_has_common_types():
24
  assert q in QUANT_BPW
25
 
26
 
 
 
 
 
 
27
  def test_quant_from_filename():
28
  assert quant_from_filename("Llama-3-8B-Instruct-Q4_K_M.gguf") == "Q4_K_M"
29
  assert quant_from_filename("model.Q5_K_S.gguf") == "Q5_K_S"
@@ -32,6 +37,25 @@ def test_quant_from_filename():
32
  assert quant_from_filename("model.gguf") is None
33
 
34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  def test_weight_bytes():
36
  # 8B params at Q4_K_M (4.84375 bpw) -> ~4.84 GB
37
  b = weight_bytes(8_000_000_000, "Q4_K_M")
@@ -224,6 +248,17 @@ def test_command_preview_yarn_when_target_exceeds():
224
  assert "--rope-freq-scale 0.5" in cmd
225
 
226
 
 
 
 
 
 
 
 
 
 
 
 
227
  def test_format_bytes():
228
  assert "GiB" in format_bytes(5 << 30)
229
  assert "MiB" in format_bytes(5 << 20)
 
24
  assert q in QUANT_BPW
25
 
26
 
27
+ def test_quant_table_has_rocmfp_types():
28
+ for q in ["ROCmFP4", "ROCmFPX"]:
29
+ assert q in QUANT_BPW
30
+
31
+
32
  def test_quant_from_filename():
33
  assert quant_from_filename("Llama-3-8B-Instruct-Q4_K_M.gguf") == "Q4_K_M"
34
  assert quant_from_filename("model.Q5_K_S.gguf") == "Q5_K_S"
 
37
  assert quant_from_filename("model.gguf") is None
38
 
39
 
40
+ def test_quant_from_filename_rocmfp():
41
+ # Real chadrock filenames — case-insensitive token match.
42
+ assert quant_from_filename(
43
+ "CHADROCK3.6-27B-Coder-MTP-ROCmFP4-STRIX_LEAN.gguf"
44
+ ) == "ROCmFP4"
45
+ assert quant_from_filename(
46
+ "CHADROCK3.6-35B-A3B-Coder-MTP-ROCmFPX-MoEQuality-7.08BPW.gguf"
47
+ ) == "ROCmFPX"
48
+ # plain ROCm with no FP version must not falsely match ROCmFP4/FPX
49
+ assert quant_from_filename("model-ROCM.gguf") is None
50
+
51
+
52
+ def test_weight_bytes_rocmfp4_matches_real_file():
53
+ # 27B Strix Lean: 14,817,251,680 bytes for 27,320,697,856 tensor elems.
54
+ # Effective bpw from the real file -> weight estimate reproduces file size.
55
+ b = weight_bytes(27_320_697_856, "ROCmFP4")
56
+ assert 14.6e9 < b < 15.0e9 # ~14.82 GiB file size
57
+
58
+
59
  def test_weight_bytes():
60
  # 8B params at Q4_K_M (4.84375 bpw) -> ~4.84 GB
61
  b = weight_bytes(8_000_000_000, "Q4_K_M")
 
248
  assert "--rope-freq-scale 0.5" in cmd
249
 
250
 
251
+ def test_command_preview_rocmfp_uses_custom_runner():
252
+ arch = PRESETS["Llama-3 8B"]
253
+ from vramcalc import Inputs
254
+ for q in ("ROCmFP4", "ROCmFPX"):
255
+ cmd = command_preview(arch, Inputs(quant=q, n_ctx=8192,
256
+ gpu_vram_gb=[24.0]))
257
+ # custom runner, not stock llama-server
258
+ assert cmd.startswith("rocmfpx-llama-server")
259
+ assert "ROCmFPX" in cmd # warning note present
260
+
261
+
262
  def test_format_bytes():
263
  assert "GiB" in format_bytes(5 << 30)
264
  assert "MiB" in format_bytes(5 << 20)
vramcalc/quant.py CHANGED
@@ -48,6 +48,17 @@ QUANT_BPW: dict[str, float] = {
48
  "F16": 16.0,
49
  "BF16": 16.0,
50
  "F32": 32.0,
 
 
 
 
 
 
 
 
 
 
 
51
  }
52
 
53
 
 
48
  "F16": 16.0,
49
  "BF16": 16.0,
50
  "F32": 32.0,
51
+ # AMD ROCm floating-point block quants (ciru-ai/ROCmFPX fork types).
52
+ # These are *effective* bits-per-weight = real_file_size * 8 / total_params,
53
+ # measured from the jcbtc/chadrock3.6 ROCmFP4/FPX GGUFs. They are higher than
54
+ # the nominal 4.0/8.0 because the typical model is a hybrid attention+SSM
55
+ # (Mamba) qwen35 with co-stored F32 state/conv tensors and mixed tensor types
56
+ # (the 35B FPX file is mostly Q6_K experts + ROCmFPX attention). Using the
57
+ # effective BPW reproduces the real file size, which is what a VRAM estimate
58
+ # needs. NOTE: stock llama.cpp cannot run these — they require the pinned
59
+ # ciru-ai/ROCmFPX runner; see command_preview for the warning.
60
+ "ROCmFP4": 4.34, # 27B Strix Lean: 14.82 GB / 27.32B params
61
+ "ROCmFPX": 7.08, # 35B A3B MoEQuality: model card states 7.08 BPW
62
  }
63
 
64
 
vramcalc/report.py CHANGED
@@ -160,9 +160,17 @@ def format_bytes(n: float) -> str:
160
  return f"{n:.0f} B"
161
 
162
 
 
 
 
 
 
 
163
  def command_preview(arch: ModelArch, inp: Inputs) -> str:
164
  """Generate a llama.cpp launch command from the current inputs."""
165
- parts = ["llama-server"]
 
 
166
  parts.append(f"-m model-{inp.quant}.gguf")
167
  parts.append(f"-c {inp.n_ctx}")
168
  parts.append("-ngl 999") # full offload assumption
@@ -196,4 +204,9 @@ def command_preview(arch: ModelArch, inp: Inputs) -> str:
196
  parts.extend(yarn_args)
197
  if arch.n_mtp > 0:
198
  parts.append(f"--mtp {arch.n_mtp}")
 
 
 
 
 
199
  return " \\\n ".join(parts)
 
160
  return f"{n:.0f} B"
161
 
162
 
163
+ # AMD ROCm floating-point block quants that stock llama.cpp cannot run. They
164
+ # require the pinned ciru-ai/ROCmFPX runner (see
165
+ # https://github.com/ciru-ai/ROCmFPX). Listed so command_preview can flag it.
166
+ ROCMFP_QUANTS = {"ROCmFP4", "ROCmFPX"}
167
+
168
+
169
  def command_preview(arch: ModelArch, inp: Inputs) -> str:
170
  """Generate a llama.cpp launch command from the current inputs."""
171
+ is_rocmfp = inp.quant in ROCMFP_QUANTS
172
+ runner = "rocmfpx-llama-server" if is_rocmfp else "llama-server"
173
+ parts = [runner]
174
  parts.append(f"-m model-{inp.quant}.gguf")
175
  parts.append(f"-c {inp.n_ctx}")
176
  parts.append("-ngl 999") # full offload assumption
 
204
  parts.extend(yarn_args)
205
  if arch.n_mtp > 0:
206
  parts.append(f"--mtp {arch.n_mtp}")
207
+ if is_rocmfp:
208
+ parts.append(
209
+ f"# NOTE: {inp.quant} needs the ciru-ai/ROCmFPX runner "
210
+ f"(stock llama.cpp cannot read these tensor types)."
211
+ )
212
  return " \\\n ".join(parts)