summaryrefslogtreecommitdiff
path: root/gguf-py/scripts/gguf-dump.py
diff options
context:
space:
mode:
Diffstat (limited to 'gguf-py/scripts/gguf-dump.py')
-rwxr-xr-xgguf-py/scripts/gguf-dump.py116
1 files changed, 116 insertions, 0 deletions
diff --git a/gguf-py/scripts/gguf-dump.py b/gguf-py/scripts/gguf-dump.py
new file mode 100755
index 00000000..5141873d
--- /dev/null
+++ b/gguf-py/scripts/gguf-dump.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python3
+from __future__ import annotations
+
+import argparse
+import os
+import sys
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+# Necessary to load the local gguf package
+if "NO_LOCAL_GGUF" not in os.environ and (Path(__file__).parent.parent.parent / 'gguf-py').exists():
+ sys.path.insert(0, str(Path(__file__).parent.parent))
+
+from gguf import GGUFReader, GGUFValueType # noqa: E402
+
+
+def get_file_host_endian(reader: GGUFReader) -> tuple[str, str]:
+ host_endian = 'LITTLE' if np.uint32(1) == np.uint32(1).newbyteorder("<") else 'BIG'
+ if reader.byte_order == 'S':
+ file_endian = 'BIG' if host_endian == 'LITTLE' else 'LITTLE'
+ else:
+ file_endian = host_endian
+ return (host_endian, file_endian)
+
+
+# For more information about what field.parts and field.data represent,
+# please see the comments in the modify_gguf.py example.
+def dump_metadata(reader: GGUFReader, args: argparse.Namespace) -> None:
+ host_endian, file_endian = get_file_host_endian(reader)
+ print(f'* File is {file_endian} endian, script is running on a {host_endian} endian host.')
+ print(f'\n* Dumping {len(reader.fields)} key/value pair(s)')
+ for n, field in enumerate(reader.fields.values(), 1):
+ if not field.types:
+ pretty_type = 'N/A'
+ elif field.types[0] == GGUFValueType.ARRAY:
+ nest_count = len(field.types) - 1
+ pretty_type = '[' * nest_count + str(field.types[-1].name) + ']' * nest_count
+ else:
+ pretty_type = str(field.types[-1].name)
+ print(f' {n:5}: {pretty_type:10} | {len(field.data):8} | {field.name}', end = '')
+ if len(field.types) == 1:
+ curr_type = field.types[0]
+ if curr_type == GGUFValueType.STRING:
+ print(' = {0}'.format(repr(str(bytes(field.parts[-1]), encoding='utf8')[:60])), end = '')
+ elif field.types[0] in reader.gguf_scalar_to_np:
+ print(' = {0}'.format(field.parts[-1][0]), end = '')
+ print()
+ if args.no_tensors:
+ return
+ print(f'\n* Dumping {len(reader.tensors)} tensor(s)')
+ for n, tensor in enumerate(reader.tensors, 1):
+ prettydims = ', '.join('{0:5}'.format(d) for d in list(tensor.shape) + [1] * (4 - len(tensor.shape)))
+ print(f' {n:5}: {tensor.n_elements:10} | {prettydims} | {tensor.tensor_type.name:7} | {tensor.name}')
+
+
+def dump_metadata_json(reader: GGUFReader, args: argparse.Namespace) -> None:
+ import json
+ host_endian, file_endian = get_file_host_endian(reader)
+ metadata: dict[str, Any] = {}
+ tensors: dict[str, Any] = {}
+ result = {
+ "filename": args.model,
+ "endian": file_endian,
+ "metadata": metadata,
+ "tensors": tensors,
+ }
+ for idx, field in enumerate(reader.fields.values()):
+ curr: dict[str, Any] = {
+ "index": idx,
+ "type": field.types[0].name if field.types else 'UNKNOWN',
+ "offset": field.offset,
+ }
+ metadata[field.name] = curr
+ if field.types[:1] == [GGUFValueType.ARRAY]:
+ curr["array_types"] = [t.name for t in field.types][1:]
+ if not args.json_array:
+ continue
+ itype = field.types[-1]
+ if itype == GGUFValueType.STRING:
+ curr["value"] = [str(bytes(field.parts[idx]), encoding="utf-8") for idx in field.data]
+ else:
+ curr["value"] = [pv for idx in field.data for pv in field.parts[idx].tolist()]
+ elif field.types[0] == GGUFValueType.STRING:
+ curr["value"] = str(bytes(field.parts[-1]), encoding="utf-8")
+ else:
+ curr["value"] = field.parts[-1].tolist()[0]
+ for idx, tensor in enumerate(reader.tensors):
+ tensors[tensor.name] = {
+ "index": idx,
+ "shape": tensor.shape.tolist(),
+ "type": tensor.tensor_type.name,
+ "offset": tensor.field.offset,
+ }
+ json.dump(result, sys.stdout)
+
+
+def main() -> None:
+ parser = argparse.ArgumentParser(description="Dump GGUF file metadata")
+ parser.add_argument("model", type=str, help="GGUF format model filename")
+ parser.add_argument("--no-tensors", action="store_true", help="Don't dump tensor metadata")
+ parser.add_argument("--json", action="store_true", help="Produce JSON output")
+ parser.add_argument("--json-array", action="store_true", help="Include full array values in JSON output (long)")
+ args = parser.parse_args(None if len(sys.argv) > 1 else ["--help"])
+ if not args.json:
+ print(f'* Loading: {args.model}')
+ reader = GGUFReader(args.model, 'r')
+ if args.json:
+ dump_metadata_json(reader, args)
+ else:
+ dump_metadata(reader, args)
+
+
+if __name__ == '__main__':
+ main()