masterjedi commited on
Commit
2eaf025
·
1 Parent(s): 3a04cf2

Create ADI GGUF inspector Space

Browse files
Files changed (4) hide show
  1. .gitattributes +1 -0
  2. README.md +5 -7
  3. app.py +376 -0
  4. requirements.txt +3 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.gguf filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,13 +1,11 @@
1
  ---
2
- title: Adi Gguf Inspector
3
- emoji: 👀
4
- colorFrom: yellow
5
- colorTo: purple
6
  sdk: gradio
7
  sdk_version: 6.19.0
8
- python_version: '3.13'
9
  app_file: app.py
10
  pinned: false
11
  ---
12
-
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: ADI GGUF Inspector
3
+ emoji: 🔎
4
+ colorFrom: gray
5
+ colorTo: green
6
  sdk: gradio
7
  sdk_version: 6.19.0
8
+ python_version: '3.12'
9
  app_file: app.py
10
  pinned: false
11
  ---
 
 
app.py ADDED
@@ -0,0 +1,376 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import struct
3
+ from collections import Counter
4
+
5
+ import gradio as gr
6
+ import requests
7
+ from huggingface_hub import HfApi, hf_hub_url
8
+
9
+
10
+ DEFAULT_REPO = "AdvancedDataIntelligence/adi-qwen3.5-4b-glm5.2-general-GGUF"
11
+ DEFAULT_FILE = "adi-qwen3.5-4b-glm5.2-general-q4_k_m.gguf"
12
+ MAX_RANGE_BYTES = 256 * 1024 * 1024
13
+
14
+ GGUF_TYPES = {
15
+ 0: ("UINT8", "B", 1),
16
+ 1: ("INT8", "b", 1),
17
+ 2: ("UINT16", "H", 2),
18
+ 3: ("INT16", "h", 2),
19
+ 4: ("UINT32", "I", 4),
20
+ 5: ("INT32", "i", 4),
21
+ 6: ("FLOAT32", "f", 4),
22
+ 7: ("BOOL", "?", 1),
23
+ 8: ("STRING", None, None),
24
+ 9: ("ARRAY", None, None),
25
+ 10: ("UINT64", "Q", 8),
26
+ 11: ("INT64", "q", 8),
27
+ 12: ("FLOAT64", "d", 8),
28
+ }
29
+
30
+ TENSOR_TYPES = {
31
+ 0: "F32",
32
+ 1: "F16",
33
+ 2: "Q4_0",
34
+ 3: "Q4_1",
35
+ 6: "Q5_0",
36
+ 7: "Q5_1",
37
+ 8: "Q8_0",
38
+ 9: "Q8_1",
39
+ 10: "Q2_K",
40
+ 11: "Q3_K",
41
+ 12: "Q4_K",
42
+ 13: "Q5_K",
43
+ 14: "Q6_K",
44
+ 15: "Q8_K",
45
+ 16: "IQ2_XXS",
46
+ 17: "IQ2_XS",
47
+ 18: "IQ3_XXS",
48
+ 19: "IQ1_S",
49
+ 20: "IQ4_NL",
50
+ 21: "IQ3_S",
51
+ 22: "IQ2_S",
52
+ 23: "IQ4_XS",
53
+ 24: "I8",
54
+ 25: "I16",
55
+ 26: "I32",
56
+ 27: "I64",
57
+ 28: "F64",
58
+ 29: "IQ1_M",
59
+ 30: "BF16",
60
+ 31: "TQ1_0",
61
+ 32: "TQ2_0",
62
+ }
63
+
64
+
65
+ class NeedMoreData(Exception):
66
+ pass
67
+
68
+
69
+ class Reader:
70
+ def __init__(self, data):
71
+ self.data = data
72
+ self.pos = 0
73
+
74
+ def require(self, size):
75
+ if self.pos + size > len(self.data):
76
+ raise NeedMoreData
77
+
78
+ def read(self, size):
79
+ self.require(size)
80
+ chunk = self.data[self.pos : self.pos + size]
81
+ self.pos += size
82
+ return chunk
83
+
84
+ def unpack(self, fmt):
85
+ size = struct.calcsize("<" + fmt)
86
+ return struct.unpack("<" + fmt, self.read(size))[0]
87
+
88
+ def string(self):
89
+ length = self.unpack("Q")
90
+ return self.read(length).decode("utf-8", errors="replace")
91
+
92
+
93
+ def fetch_prefix(repo_id, filename, revision, size):
94
+ token = os.getenv("HF_TOKEN") or os.getenv("HUGGINGFACE_HUB_TOKEN")
95
+ headers = {"Range": f"bytes=0-{size - 1}"}
96
+ if token:
97
+ headers["Authorization"] = f"Bearer {token}"
98
+
99
+ url = hf_hub_url(repo_id=repo_id, filename=filename, revision=revision)
100
+ response = requests.get(url, headers=headers, allow_redirects=True, timeout=60)
101
+ response.raise_for_status()
102
+ return response.content
103
+
104
+
105
+ def file_size(repo_id, filename, revision):
106
+ info = HfApi().model_info(repo_id=repo_id, revision=revision, files_metadata=True)
107
+ for sibling in info.siblings:
108
+ if sibling.rfilename == filename:
109
+ return sibling.size
110
+ return None
111
+
112
+
113
+ def read_scalar(reader, value_type):
114
+ type_name, fmt, size = GGUF_TYPES.get(value_type, (f"UNKNOWN_{value_type}", None, None))
115
+ if value_type == 8:
116
+ return reader.string()
117
+ if value_type == 9:
118
+ item_type = reader.unpack("I")
119
+ item_count = reader.unpack("Q")
120
+ values = []
121
+ for _ in range(min(item_count, 64)):
122
+ values.append(read_scalar(reader, item_type))
123
+ if item_count > 64:
124
+ skip_value(reader, item_type, item_count - 64)
125
+ values.append(f"... {item_count - 64} more")
126
+ return values
127
+ if fmt is None:
128
+ raise ValueError(f"Unsupported GGUF metadata type {type_name}")
129
+ return reader.unpack(fmt)
130
+
131
+
132
+ def skip_value(reader, value_type, count=1):
133
+ for _ in range(count):
134
+ if value_type == 8:
135
+ length = reader.unpack("Q")
136
+ reader.read(length)
137
+ elif value_type == 9:
138
+ item_type = reader.unpack("I")
139
+ item_count = reader.unpack("Q")
140
+ skip_value(reader, item_type, item_count)
141
+ else:
142
+ _name, _fmt, size = GGUF_TYPES.get(value_type, (None, None, None))
143
+ if size is None:
144
+ raise ValueError(f"Unsupported GGUF metadata type {value_type}")
145
+ reader.read(size)
146
+
147
+
148
+ def parse_gguf(data):
149
+ reader = Reader(data)
150
+ if reader.read(4) != b"GGUF":
151
+ raise ValueError("File does not start with GGUF magic bytes.")
152
+
153
+ version = reader.unpack("I")
154
+ tensor_count = reader.unpack("Q")
155
+ metadata_count = reader.unpack("Q")
156
+
157
+ metadata = {}
158
+ metadata_types = {}
159
+ for _ in range(metadata_count):
160
+ key = reader.string()
161
+ value_type = reader.unpack("I")
162
+ metadata[key] = read_scalar(reader, value_type)
163
+ metadata_types[key] = GGUF_TYPES.get(value_type, (str(value_type), None, None))[0]
164
+
165
+ tensor_types = Counter()
166
+ tensor_names = []
167
+ tensor_shapes = []
168
+ for _ in range(tensor_count):
169
+ name = reader.string()
170
+ dims_count = reader.unpack("I")
171
+ dims = [reader.unpack("Q") for _ in range(dims_count)]
172
+ tensor_type_id = reader.unpack("I")
173
+ offset = reader.unpack("Q")
174
+ tensor_type = TENSOR_TYPES.get(tensor_type_id, f"TYPE_{tensor_type_id}")
175
+ tensor_types[tensor_type] += 1
176
+ if len(tensor_names) < 80:
177
+ tensor_names.append(name)
178
+ tensor_shapes.append(
179
+ {
180
+ "name": name,
181
+ "shape": " x ".join(str(d) for d in dims),
182
+ "type": tensor_type,
183
+ "offset": offset,
184
+ }
185
+ )
186
+
187
+ return {
188
+ "version": version,
189
+ "tensor_count": tensor_count,
190
+ "metadata_count": metadata_count,
191
+ "metadata": metadata,
192
+ "metadata_types": metadata_types,
193
+ "tensor_types": dict(tensor_types),
194
+ "tensor_names": tensor_names,
195
+ "tensor_shapes": tensor_shapes,
196
+ "header_bytes_read": reader.pos,
197
+ }
198
+
199
+
200
+ def inspect_gguf(repo_id, filename, revision):
201
+ repo_id = repo_id.strip()
202
+ filename = filename.strip()
203
+ revision = (revision or "main").strip()
204
+ if not repo_id or not filename:
205
+ raise gr.Error("Repo ID and GGUF filename are required.")
206
+
207
+ last_error = None
208
+ data = b""
209
+ for size in (2, 4, 8, 16, 32, 64, 128, 256):
210
+ byte_count = size * 1024 * 1024
211
+ try:
212
+ data = fetch_prefix(repo_id, filename, revision, byte_count)
213
+ parsed = parse_gguf(data)
214
+ break
215
+ except NeedMoreData as exc:
216
+ last_error = exc
217
+ if byte_count >= MAX_RANGE_BYTES:
218
+ raise gr.Error("GGUF header is larger than 256 MB; cannot inspect safely.")
219
+ except requests.HTTPError as exc:
220
+ raise gr.Error(f"Could not fetch GGUF prefix: HTTP {exc.response.status_code}")
221
+ else:
222
+ raise gr.Error(f"Could not parse GGUF metadata: {last_error}")
223
+
224
+ size_bytes = file_size(repo_id, filename, revision)
225
+ summary = summarize(repo_id, filename, revision, size_bytes, parsed)
226
+ metadata_rows = metadata_table(parsed["metadata"], parsed["metadata_types"])
227
+ tensor_rows = tensor_table(parsed["tensor_shapes"])
228
+ return summary, metadata_rows, tensor_rows, parsed
229
+
230
+
231
+ def pick(metadata, suffixes):
232
+ for suffix in suffixes:
233
+ for key, value in metadata.items():
234
+ if key.endswith(suffix):
235
+ return value
236
+ return None
237
+
238
+
239
+ def summarize(repo_id, filename, revision, size_bytes, parsed):
240
+ metadata = parsed["metadata"]
241
+ arch = metadata.get("general.architecture", "unknown")
242
+ model_name = metadata.get("general.name", filename)
243
+ quant = metadata.get("general.file_type")
244
+ quant_name = TENSOR_TYPES.get(quant, quant) if isinstance(quant, int) else quant
245
+ ctx_train = pick(metadata, [".context_length"])
246
+ block_count = pick(metadata, [".block_count"])
247
+ embedding = pick(metadata, [".embedding_length"])
248
+ head_count = pick(metadata, [".attention.head_count"])
249
+ tokenizer = metadata.get("tokenizer.ggml.model", "unknown")
250
+ tensor_mix = ", ".join(
251
+ f"{name}: {count}" for name, count in sorted(parsed["tensor_types"].items())
252
+ )
253
+
254
+ warnings = compatibility_notes(arch, metadata, parsed["tensor_names"])
255
+ size_text = format_bytes(size_bytes) if size_bytes else "unknown"
256
+
257
+ return f"""# {model_name}
258
+
259
+ **Repo:** `{repo_id}`
260
+ **File:** `{filename}`
261
+ **Revision:** `{revision}`
262
+ **Size:** `{size_text}`
263
+ **GGUF version:** `{parsed["version"]}`
264
+ **Architecture:** `{arch}`
265
+ **File type:** `{quant_name}`
266
+ **Tensor count:** `{parsed["tensor_count"]}`
267
+ **Metadata entries:** `{parsed["metadata_count"]}`
268
+ **Header bytes inspected:** `{format_bytes(parsed["header_bytes_read"])}`
269
+
270
+ ## Model Shape
271
+
272
+ - Training/context length: `{ctx_train}`
273
+ - Blocks/layers: `{block_count}`
274
+ - Embedding length: `{embedding}`
275
+ - Attention heads: `{head_count}`
276
+ - Tokenizer: `{tokenizer}`
277
+
278
+ ## Tensor Types
279
+
280
+ `{tensor_mix or "none"}`
281
+
282
+ ## Compatibility Notes
283
+
284
+ {warnings}
285
+ """
286
+
287
+
288
+ def compatibility_notes(arch, metadata, tensor_names):
289
+ lower_arch = str(arch).lower()
290
+ keys = " ".join(metadata.keys()).lower()
291
+ names = " ".join(tensor_names[:80]).lower()
292
+ haystack = f"{lower_arch} {keys} {names}"
293
+ notes = []
294
+
295
+ if "qwen3" in haystack and any(term in haystack for term in ("ssm", "mamba", "gated_delta", "gated-delta")):
296
+ notes.append(
297
+ "- Qwen3/Qwen3.5 hybrid SSM or gated-delta signals detected. Use a very recent llama.cpp build."
298
+ )
299
+ elif "qwen3" in haystack:
300
+ notes.append("- Qwen3-family metadata detected. Prefer recent llama.cpp/llama-cpp-python builds.")
301
+
302
+ if any(term in haystack for term in ("ssm", "mamba", "gated_delta", "gated-delta")):
303
+ notes.append("- SSM/Mamba-style metadata detected; older llama.cpp builds may fail at model load.")
304
+
305
+ if "tokenizer.ggml.tokens" not in metadata:
306
+ notes.append("- Token list is not present in the inspected metadata prefix.")
307
+
308
+ if not notes:
309
+ notes.append("- No obvious metadata-level compatibility red flags found.")
310
+
311
+ return "\n".join(notes)
312
+
313
+
314
+ def metadata_table(metadata, metadata_types):
315
+ rows = []
316
+ for key in sorted(metadata):
317
+ value = metadata[key]
318
+ if isinstance(value, list):
319
+ value_text = f"[{len(value)} items] " + repr(value[:8])
320
+ else:
321
+ value_text = repr(value)
322
+ if len(value_text) > 500:
323
+ value_text = value_text[:497] + "..."
324
+ rows.append([key, metadata_types.get(key, ""), value_text])
325
+ return rows
326
+
327
+
328
+ def tensor_table(tensor_shapes):
329
+ return [[item["name"], item["shape"], item["type"], item["offset"]] for item in tensor_shapes]
330
+
331
+
332
+ def format_bytes(value):
333
+ if value is None:
334
+ return "unknown"
335
+ value = float(value)
336
+ for unit in ("B", "KB", "MB", "GB", "TB"):
337
+ if value < 1024 or unit == "TB":
338
+ return f"{value:.2f} {unit}" if unit != "B" else f"{int(value)} B"
339
+ value /= 1024
340
+
341
+
342
+ with gr.Blocks(title="ADI GGUF Inspector", fill_width=True) as demo:
343
+ gr.Markdown("# ADI GGUF Inspector")
344
+ with gr.Row():
345
+ repo = gr.Textbox(label="Model repo", value=DEFAULT_REPO)
346
+ filename = gr.Textbox(label="GGUF filename", value=DEFAULT_FILE)
347
+ revision = gr.Textbox(label="Revision", value="main")
348
+ inspect_btn = gr.Button("Inspect", variant="primary")
349
+ summary = gr.Markdown()
350
+ with gr.Tabs():
351
+ with gr.Tab("Metadata"):
352
+ metadata = gr.Dataframe(
353
+ headers=["Key", "Type", "Value"],
354
+ datatype=["str", "str", "str"],
355
+ wrap=True,
356
+ interactive=False,
357
+ )
358
+ with gr.Tab("Tensors"):
359
+ tensors = gr.Dataframe(
360
+ headers=["Name", "Shape", "Type", "Offset"],
361
+ datatype=["str", "str", "str", "number"],
362
+ wrap=True,
363
+ interactive=False,
364
+ )
365
+ with gr.Tab("Raw"):
366
+ raw = gr.JSON()
367
+
368
+ inspect_btn.click(
369
+ inspect_gguf,
370
+ inputs=[repo, filename, revision],
371
+ outputs=[summary, metadata, tensors, raw],
372
+ )
373
+
374
+
375
+ if __name__ == "__main__":
376
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio==6.19.0
2
+ huggingface_hub
3
+ requests