deepbom 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
deepbom-0.1.0/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ Copyright (C) 2026 Jun-Hwan Kwon. All rights reserved.
2
+
3
+ This repository and its generated software artifacts are currently provided as
4
+ private research software. No license is granted to copy, modify, distribute,
5
+ sublicense, reverse engineer, or create derivative works from the source code,
6
+ WebAssembly modules, generated JavaScript bindings, or packaged executables,
7
+ except where a separate file or component carries an explicit license.
8
+
9
+ Access to the hosted service does not grant a software or implementation
10
+ license.
11
+
12
+ Future public releases may license selected contracts, conformance fixtures,
13
+ validation data, or automation clients separately. A license applies only to
14
+ the files and versions that explicitly carry it. Third-party dependencies and
15
+ model artifacts remain subject to their respective licenses.
deepbom-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,139 @@
1
+ Metadata-Version: 2.4
2
+ Name: deepbom
3
+ Version: 0.1.0
4
+ Summary: Deployment-artifact inspection for on-device neural network models
5
+ Author: Jun-Hwan Kwon
6
+ License: Copyright (C) 2026 Jun-Hwan Kwon. All rights reserved.
7
+
8
+ This repository and its generated software artifacts are currently provided as
9
+ private research software. No license is granted to copy, modify, distribute,
10
+ sublicense, reverse engineer, or create derivative works from the source code,
11
+ WebAssembly modules, generated JavaScript bindings, or packaged executables,
12
+ except where a separate file or component carries an explicit license.
13
+
14
+ Access to the hosted service does not grant a software or implementation
15
+ license.
16
+
17
+ Future public releases may license selected contracts, conformance fixtures,
18
+ validation data, or automation clients separately. A license applies only to
19
+ the files and versions that explicitly carry it. Third-party dependencies and
20
+ model artifacts remain subject to their respective licenses.
21
+
22
+ Project-URL: Homepage, https://deepbom.org
23
+ Keywords: gguf,safetensors,tflite,onnx,coreml,on-device,edge-ai,quantization,ml-bom,static-analysis
24
+ Classifier: Development Status :: 3 - Alpha
25
+ Classifier: Intended Audience :: Developers
26
+ Classifier: Intended Audience :: Science/Research
27
+ Classifier: License :: OSI Approved :: ISC License (ISCL)
28
+ Classifier: Programming Language :: Python :: 3
29
+ Classifier: Programming Language :: Python :: 3.9
30
+ Classifier: Programming Language :: Python :: 3.10
31
+ Classifier: Programming Language :: Python :: 3.11
32
+ Classifier: Programming Language :: Python :: 3.12
33
+ Classifier: Programming Language :: Python :: 3.13
34
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
35
+ Classifier: Topic :: Software Development :: Quality Assurance
36
+ Classifier: Typing :: Typed
37
+ Requires-Python: >=3.9
38
+ Description-Content-Type: text/markdown
39
+ License-File: LICENSE
40
+ Dynamic: license-file
41
+
42
+ # deepbom
43
+
44
+ Deployment-artifact inspection for on-device neural network models.
45
+
46
+ Identifies model artifact formats from their container signature and reads the
47
+ contracts that can be decoded without loading tensor payload values.
48
+
49
+ ```console
50
+ $ pip install deepbom
51
+ $ deepbom inspect model.safetensors
52
+
53
+ model.safetensors
54
+ sha256 a35fd03f52c12f4e78a246bec1927e9a169377fbb8905dc13165d285010e7a44
55
+ format safetensors size 2.6 MB
56
+ evidence u64 header length followed by a JSON header
57
+
58
+ tensors
59
+ count 38
60
+ parameters 1,377,408
61
+ stored payload 2.6 MB
62
+ dtypes F16 x38
63
+
64
+ Container-level facts only. No tensor payload values were read.
65
+ ```
66
+
67
+ ```console
68
+ $ deepbom inspect model.gguf
69
+
70
+ model.gguf
71
+ sha256 cb95a6e10f28b76a1dd71c15560dec5a5eee8943f591ef45d11c129786b22cff
72
+ format gguf size 509.0 KB
73
+ evidence magic "GGUF" at offset 0
74
+
75
+ container
76
+ gguf version 3
77
+ tensors 39
78
+ metadata 26 / 26 (complete)
79
+ architecture llama
80
+ file type 2
81
+ quant version 2
82
+ ```
83
+
84
+ ## What it reads
85
+
86
+ | Format | Reported |
87
+ | --- | --- |
88
+ | **SafeTensors** | tensor inventory, dtypes, shapes, parameter count, stored payload bytes, metadata, header/file size agreement |
89
+ | **GGUF** | version, tensor count, full metadata key/value inventory, architecture, file type, quantization version |
90
+ | TFLite, ONNX, Core ML | format identification and SHA-256 only |
91
+
92
+ Format is decided from container evidence — FlatBuffer identifier, magic
93
+ bytes, header structure — never from the filename extension. ONNX and Core ML
94
+ are separated by their protobuf field layout rather than guessed.
95
+
96
+ ## Usage
97
+
98
+ ```console
99
+ deepbom inspect <file>
100
+ deepbom inspect <file> --json
101
+ deepbom --version
102
+ ```
103
+
104
+ As a library:
105
+
106
+ ```python
107
+ from deepbom import inspect
108
+
109
+ artifact = inspect("model.gguf")
110
+ print(artifact.format, artifact.sha256)
111
+ print(artifact.detail["architecture"])
112
+ print(artifact.to_dict())
113
+ ```
114
+
115
+ ## Scope
116
+
117
+ This package is pure Python with no dependencies. It reports **container-level
118
+ facts only**: what the header and directory structures determine. Tensor
119
+ payload values are never read, and nothing is inferred that the container does
120
+ not state.
121
+
122
+ Graph structure, quantization contracts, predicted delegate placement,
123
+ target-profile cost projections and CycloneDX ML-BOM export are **not** part of
124
+ this package. For TFLite graph analysis:
125
+
126
+ ```console
127
+ npx deepbom audit model.tflite
128
+ ```
129
+
130
+ ONNX, Core ML and runtime evidence are available in the browser version at
131
+ <https://deepbom.org>.
132
+
133
+ ## Privacy
134
+
135
+ No network access. Model bytes, filenames and results are never uploaded.
136
+
137
+ ## License
138
+
139
+ ISC — see [LICENSE](./LICENSE).
@@ -0,0 +1,98 @@
1
+ # deepbom
2
+
3
+ Deployment-artifact inspection for on-device neural network models.
4
+
5
+ Identifies model artifact formats from their container signature and reads the
6
+ contracts that can be decoded without loading tensor payload values.
7
+
8
+ ```console
9
+ $ pip install deepbom
10
+ $ deepbom inspect model.safetensors
11
+
12
+ model.safetensors
13
+ sha256 a35fd03f52c12f4e78a246bec1927e9a169377fbb8905dc13165d285010e7a44
14
+ format safetensors size 2.6 MB
15
+ evidence u64 header length followed by a JSON header
16
+
17
+ tensors
18
+ count 38
19
+ parameters 1,377,408
20
+ stored payload 2.6 MB
21
+ dtypes F16 x38
22
+
23
+ Container-level facts only. No tensor payload values were read.
24
+ ```
25
+
26
+ ```console
27
+ $ deepbom inspect model.gguf
28
+
29
+ model.gguf
30
+ sha256 cb95a6e10f28b76a1dd71c15560dec5a5eee8943f591ef45d11c129786b22cff
31
+ format gguf size 509.0 KB
32
+ evidence magic "GGUF" at offset 0
33
+
34
+ container
35
+ gguf version 3
36
+ tensors 39
37
+ metadata 26 / 26 (complete)
38
+ architecture llama
39
+ file type 2
40
+ quant version 2
41
+ ```
42
+
43
+ ## What it reads
44
+
45
+ | Format | Reported |
46
+ | --- | --- |
47
+ | **SafeTensors** | tensor inventory, dtypes, shapes, parameter count, stored payload bytes, metadata, header/file size agreement |
48
+ | **GGUF** | version, tensor count, full metadata key/value inventory, architecture, file type, quantization version |
49
+ | TFLite, ONNX, Core ML | format identification and SHA-256 only |
50
+
51
+ Format is decided from container evidence — FlatBuffer identifier, magic
52
+ bytes, header structure — never from the filename extension. ONNX and Core ML
53
+ are separated by their protobuf field layout rather than guessed.
54
+
55
+ ## Usage
56
+
57
+ ```console
58
+ deepbom inspect <file>
59
+ deepbom inspect <file> --json
60
+ deepbom --version
61
+ ```
62
+
63
+ As a library:
64
+
65
+ ```python
66
+ from deepbom import inspect
67
+
68
+ artifact = inspect("model.gguf")
69
+ print(artifact.format, artifact.sha256)
70
+ print(artifact.detail["architecture"])
71
+ print(artifact.to_dict())
72
+ ```
73
+
74
+ ## Scope
75
+
76
+ This package is pure Python with no dependencies. It reports **container-level
77
+ facts only**: what the header and directory structures determine. Tensor
78
+ payload values are never read, and nothing is inferred that the container does
79
+ not state.
80
+
81
+ Graph structure, quantization contracts, predicted delegate placement,
82
+ target-profile cost projections and CycloneDX ML-BOM export are **not** part of
83
+ this package. For TFLite graph analysis:
84
+
85
+ ```console
86
+ npx deepbom audit model.tflite
87
+ ```
88
+
89
+ ONNX, Core ML and runtime evidence are available in the browser version at
90
+ <https://deepbom.org>.
91
+
92
+ ## Privacy
93
+
94
+ No network access. Model bytes, filenames and results are never uploaded.
95
+
96
+ ## License
97
+
98
+ ISC — see [LICENSE](./LICENSE).
@@ -0,0 +1,41 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "deepbom"
7
+ version = "0.1.0"
8
+ description = "Deployment-artifact inspection for on-device neural network models"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { file = "LICENSE" }
12
+ authors = [{ name = "Jun-Hwan Kwon" }]
13
+ keywords = [
14
+ "gguf", "safetensors", "tflite", "onnx", "coreml",
15
+ "on-device", "edge-ai", "quantization", "ml-bom", "static-analysis",
16
+ ]
17
+ classifiers = [
18
+ "Development Status :: 3 - Alpha",
19
+ "Intended Audience :: Developers",
20
+ "Intended Audience :: Science/Research",
21
+ "License :: OSI Approved :: ISC License (ISCL)",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.9",
24
+ "Programming Language :: Python :: 3.10",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Programming Language :: Python :: 3.13",
28
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
29
+ "Topic :: Software Development :: Quality Assurance",
30
+ "Typing :: Typed",
31
+ ]
32
+ dependencies = []
33
+
34
+ [project.urls]
35
+ Homepage = "https://deepbom.org"
36
+
37
+ [project.scripts]
38
+ deepbom = "deepbom.__main__:main"
39
+
40
+ [tool.setuptools.packages.find]
41
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,12 @@
1
+ """Deployment-artifact inspection for on-device neural network models.
2
+
3
+ Identifies model artifact formats and reads the container-level contracts that
4
+ can be decoded without loading tensor payload values.
5
+
6
+ This package performs no network access. Model bytes stay on the machine.
7
+ """
8
+
9
+ from .inspect import Artifact, InspectionError, identify, inspect
10
+
11
+ __version__ = "0.1.0"
12
+ __all__ = ["Artifact", "InspectionError", "identify", "inspect", "__version__"]
@@ -0,0 +1,98 @@
1
+ """Command line entry point: ``deepbom`` / ``python -m deepbom``."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import sys
8
+
9
+ from . import __version__
10
+ from .inspect import Artifact, InspectionError, inspect
11
+
12
+
13
+ def _fmt_bytes(value: int) -> str:
14
+ size = float(value)
15
+ for unit in ("B", "KB", "MB", "GB"):
16
+ if size < 1024 or unit == "GB":
17
+ return f"{size:.0f} {unit}" if unit == "B" else f"{size:.1f} {unit}"
18
+ size /= 1024
19
+ return f"{size:.1f} GB"
20
+
21
+
22
+ def _render(artifact: Artifact) -> str:
23
+ out: list[str] = []
24
+ detail = artifact.detail
25
+ out.append(artifact.path)
26
+ out.append(f" sha256 {artifact.sha256}")
27
+ out.append(f" format {artifact.format} size {_fmt_bytes(artifact.size_bytes)}")
28
+ out.append(f" evidence {artifact.format_evidence}")
29
+ out.append("")
30
+
31
+ if artifact.format == "safetensors":
32
+ out.append("tensors")
33
+ out.append(f" count {detail['tensor_count']:,}")
34
+ out.append(f" parameters {detail['parameter_count']:,}")
35
+ out.append(f" stored payload {_fmt_bytes(detail['stored_payload_bytes'])}")
36
+ dtypes = ", ".join(f"{k} x{v}" for k, v in detail["dtype_counts"].items())
37
+ out.append(f" dtypes {dtypes}")
38
+ if detail.get("metadata"):
39
+ out.append(f" metadata keys {', '.join(sorted(detail['metadata'])[:6])}")
40
+ out.append("")
41
+ elif artifact.format == "gguf":
42
+ out.append("container")
43
+ out.append(f" gguf version {detail['gguf_version']}")
44
+ out.append(f" tensors {detail['tensor_count']:,}")
45
+ complete = "complete" if detail.get("metadata_complete") else "partial"
46
+ out.append(
47
+ f" metadata {detail['metadata_decoded_count']:,} / "
48
+ f"{detail['metadata_kv_count']:,} ({complete})"
49
+ )
50
+ for label, key in (("architecture", "architecture"), ("name", "name"),
51
+ ("file type", "file_type"), ("quant version", "quantization_version")):
52
+ if detail.get(key) is not None:
53
+ out.append(f" {label:<18} {detail[key]}")
54
+ out.append("")
55
+
56
+ if artifact.notes:
57
+ out.append("notes")
58
+ for note in artifact.notes:
59
+ out.append(f" - {note}")
60
+ out.append("")
61
+
62
+ out.append("Container-level facts only. No tensor payload values were read.")
63
+ return "\n".join(out)
64
+
65
+
66
+ def main(argv: list[str] | None = None) -> int:
67
+ parser = argparse.ArgumentParser(
68
+ prog="deepbom",
69
+ description="Deployment-artifact inspection for on-device neural network models.",
70
+ epilog="Graph, quantization and delegate analysis: `npx deepbom audit` or https://deepbom.org",
71
+ )
72
+ parser.add_argument("--version", action="version", version=__version__)
73
+ sub = parser.add_subparsers(dest="command")
74
+
75
+ inspect_cmd = sub.add_parser("inspect", help="Inspect an artifact container")
76
+ inspect_cmd.add_argument("path", help="Path to a model artifact")
77
+ inspect_cmd.add_argument("--json", action="store_true", help="Emit JSON")
78
+
79
+ args = parser.parse_args(argv)
80
+ if args.command != "inspect":
81
+ parser.print_help()
82
+ return 1
83
+
84
+ try:
85
+ artifact = inspect(args.path)
86
+ except InspectionError as error:
87
+ print(f"deepbom: {error}", file=sys.stderr)
88
+ return 2
89
+
90
+ if args.json:
91
+ print(json.dumps(artifact.to_dict(), indent=2, ensure_ascii=False))
92
+ else:
93
+ print(_render(artifact))
94
+ return 0
95
+
96
+
97
+ if __name__ == "__main__":
98
+ raise SystemExit(main())
@@ -0,0 +1,310 @@
1
+ """Format identification and container-level inspection.
2
+
3
+ Only facts the serialized container determines are reported. Nothing is
4
+ inferred from a filename extension, and tensor payload values are never read.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import hashlib
10
+ import json
11
+ import struct
12
+ from dataclasses import dataclass, field
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ # GGUF value type ids, ggml/gguf.md
17
+ _GGUF_TYPES = {
18
+ 0: ("uint8", "<B", 1), 1: ("int8", "<b", 1),
19
+ 2: ("uint16", "<H", 2), 3: ("int16", "<h", 2),
20
+ 4: ("uint32", "<I", 4), 5: ("int32", "<i", 4),
21
+ 6: ("float32", "<f", 4), 7: ("bool", "<?", 1),
22
+ 8: ("string", None, None), 9: ("array", None, None),
23
+ 10: ("uint64", "<Q", 8), 11: ("int64", "<q", 8),
24
+ 12: ("float64", "<d", 8),
25
+ }
26
+
27
+ _SAFETENSORS_DTYPE_BITS = {
28
+ "BOOL": 1, "U8": 8, "I8": 8, "F8_E4M3": 8, "F8_E5M2": 8,
29
+ "U16": 16, "I16": 16, "F16": 16, "BF16": 16,
30
+ "U32": 32, "I32": 32, "F32": 32,
31
+ "U64": 64, "I64": 64, "F64": 64,
32
+ }
33
+
34
+ _READ_LIMIT = 64 * 1024 * 1024 # header scan ceiling
35
+
36
+
37
+ class InspectionError(Exception):
38
+ """Raised when a file cannot be inspected as a supported artifact."""
39
+
40
+
41
+ @dataclass
42
+ class Artifact:
43
+ """Container-level facts about one artifact."""
44
+
45
+ path: str
46
+ size_bytes: int
47
+ sha256: str
48
+ format: str
49
+ format_evidence: str
50
+ detail: dict[str, Any] = field(default_factory=dict)
51
+ notes: list[str] = field(default_factory=list)
52
+
53
+ def to_dict(self) -> dict[str, Any]:
54
+ return {
55
+ "schema": "deepbom.artifact_inspection.v1",
56
+ "path": self.path,
57
+ "size_bytes": self.size_bytes,
58
+ "sha256": self.sha256,
59
+ "format": self.format,
60
+ "format_evidence": self.format_evidence,
61
+ "detail": self.detail,
62
+ "notes": self.notes,
63
+ }
64
+
65
+
66
+ def _sha256(path: Path) -> str:
67
+ digest = hashlib.sha256()
68
+ with path.open("rb") as handle:
69
+ for block in iter(lambda: handle.read(1 << 20), b""):
70
+ digest.update(block)
71
+ return digest.hexdigest()
72
+
73
+
74
+ def identify(head: bytes) -> tuple[str, str]:
75
+ """Return (format, evidence) from leading bytes alone."""
76
+ if len(head) >= 8 and head[4:8] == b"TFL3":
77
+ return "tflite", 'FlatBuffer file identifier "TFL3" at offset 4'
78
+ if head[:4] == b"GGUF":
79
+ return "gguf", 'magic "GGUF" at offset 0'
80
+ if head[:4] == b"PK\x03\x04":
81
+ return "zip-container", "ZIP local file header — Core ML .mlpackage or archived artifact"
82
+ if len(head) >= 8:
83
+ # SafeTensors: u64 little-endian header length, then a JSON object
84
+ (header_len,) = struct.unpack("<Q", head[:8])
85
+ if 2 <= header_len <= _READ_LIMIT and head[8:9] == b"{":
86
+ return "safetensors", "u64 header length followed by a JSON header"
87
+ protobuf = _classify_protobuf(head)
88
+ if protobuf is not None:
89
+ return protobuf
90
+ return "unknown", "no recognised container signature"
91
+
92
+
93
+ def _read_varint(buf: bytes, index: int) -> tuple[int, int]:
94
+ """Return (value, next_index) for a protobuf varint."""
95
+ value = 0
96
+ shift = 0
97
+ while index < len(buf):
98
+ byte = buf[index]
99
+ value |= (byte & 0x7F) << shift
100
+ index += 1
101
+ if not byte & 0x80:
102
+ return value, index
103
+ shift += 7
104
+ if shift > 63:
105
+ break
106
+ raise InspectionError("truncated varint")
107
+
108
+
109
+ def _classify_protobuf(head: bytes) -> tuple[str, str] | None:
110
+ """Separate ONNX ModelProto from Core ML Model by their second field.
111
+
112
+ Both start with field 1 as a varint. ONNX field 2 is `producer_name`, a
113
+ short printable string; Core ML field 2 is the `description` message.
114
+ """
115
+ if not head[:1] == b"\x08":
116
+ return None
117
+ try:
118
+ _, index = _read_varint(head, 1)
119
+ if index >= len(head) or head[index] != 0x12:
120
+ return None
121
+ length, after = _read_varint(head, index + 1)
122
+ length_bytes = after - (index + 1)
123
+ payload = head[after:after + min(length, 48)]
124
+ except InspectionError:
125
+ return None
126
+
127
+ printable = bool(payload) and all(32 <= byte < 127 for byte in payload)
128
+ if length_bytes == 1 and length <= 64 and printable:
129
+ return "onnx", "protobuf ir_version followed by a printable producer_name string"
130
+ return "coreml", "protobuf specificationVersion followed by a description message"
131
+
132
+
133
+ def _inspect_safetensors(path: Path, size: int) -> tuple[dict[str, Any], list[str]]:
134
+ with path.open("rb") as handle:
135
+ (header_len,) = struct.unpack("<Q", handle.read(8))
136
+ if header_len > _READ_LIMIT or 8 + header_len > size:
137
+ raise InspectionError("SafeTensors header length exceeds the file")
138
+ header = json.loads(handle.read(header_len).decode("utf-8"))
139
+
140
+ tensors: list[dict[str, Any]] = []
141
+ dtypes: dict[str, int] = {}
142
+ total_elements = 0
143
+ stored_bytes = 0
144
+ for name, entry in header.items():
145
+ if name == "__metadata__":
146
+ continue
147
+ shape = entry.get("shape", [])
148
+ dtype = entry.get("dtype", "UNKNOWN")
149
+ offsets = entry.get("data_offsets", [0, 0])
150
+ elements = 1
151
+ for dim in shape:
152
+ elements *= int(dim)
153
+ if not shape:
154
+ elements = 1
155
+ total_elements += elements
156
+ stored_bytes += int(offsets[1]) - int(offsets[0])
157
+ dtypes[dtype] = dtypes.get(dtype, 0) + 1
158
+ tensors.append({"name": name, "dtype": dtype, "shape": shape, "elements": elements})
159
+
160
+ detail: dict[str, Any] = {
161
+ "tensor_count": len(tensors),
162
+ "parameter_count": total_elements,
163
+ "stored_payload_bytes": stored_bytes,
164
+ "dtype_counts": dict(sorted(dtypes.items())),
165
+ "metadata": header.get("__metadata__", {}),
166
+ "tensors": tensors,
167
+ }
168
+
169
+ notes: list[str] = []
170
+ declared_end = 8 + header_len + stored_bytes
171
+ if declared_end != size:
172
+ notes.append(
173
+ f"declared payload ends at {declared_end} but the file is {size} bytes "
174
+ f"({size - declared_end:+d})"
175
+ )
176
+ for dtype, count in dtypes.items():
177
+ if dtype not in _SAFETENSORS_DTYPE_BITS:
178
+ notes.append(f"unrecognised dtype {dtype!r} on {count} tensor(s)")
179
+ return detail, notes
180
+
181
+
182
+ def _read_gguf_string(handle) -> str:
183
+ (length,) = struct.unpack("<Q", handle.read(8))
184
+ if length > 1 << 20:
185
+ raise InspectionError("GGUF string length is implausible")
186
+ return handle.read(length).decode("utf-8", errors="replace")
187
+
188
+
189
+ def _read_gguf_value(handle, type_id: int, depth: int = 0) -> Any:
190
+ if type_id not in _GGUF_TYPES:
191
+ raise InspectionError(f"unknown GGUF value type {type_id}")
192
+ name, fmt, width = _GGUF_TYPES[type_id]
193
+ if name == "string":
194
+ return _read_gguf_string(handle)
195
+ if name == "array":
196
+ if depth > 1:
197
+ raise InspectionError("nested GGUF arrays are not decoded")
198
+ (elem_type,) = struct.unpack("<I", handle.read(4))
199
+ (count,) = struct.unpack("<Q", handle.read(8))
200
+ if count > 1 << 26:
201
+ raise InspectionError("GGUF array length is implausible")
202
+ if elem_type not in _GGUF_TYPES:
203
+ raise InspectionError(f"unknown GGUF array element type {elem_type}")
204
+ elem_name, _, elem_width = _GGUF_TYPES[elem_type]
205
+ if count <= 64:
206
+ return [_read_gguf_value(handle, elem_type, depth + 1) for _ in range(count)]
207
+ # Large arrays (tokenizer vocabularies) are summarised, but their bytes
208
+ # must still be consumed or every following key/value misaligns.
209
+ if elem_width is not None:
210
+ handle.seek(count * elem_width, 1)
211
+ elif elem_name == "string":
212
+ for _ in range(count):
213
+ (length,) = struct.unpack("<Q", handle.read(8))
214
+ if length > 1 << 20:
215
+ raise InspectionError("GGUF string length is implausible")
216
+ handle.seek(length, 1)
217
+ else:
218
+ raise InspectionError(f"cannot skip a large array of {elem_name}")
219
+ return {"array_type": elem_name, "length": count, "elided": True}
220
+ return struct.unpack(fmt, handle.read(width))[0]
221
+
222
+
223
+ def _inspect_gguf(path: Path, size: int) -> tuple[dict[str, Any], list[str]]:
224
+ notes: list[str] = []
225
+ with path.open("rb") as handle:
226
+ handle.read(4) # magic
227
+ version, tensor_count, kv_count = struct.unpack("<IQQ", handle.read(20))
228
+ if version not in (2, 3):
229
+ notes.append(f"GGUF version {version} is outside the versions this reader decodes (2, 3)")
230
+ metadata: dict[str, Any] = {}
231
+ truncated = False
232
+ if version in (2, 3):
233
+ try:
234
+ for _ in range(kv_count):
235
+ key = _read_gguf_string(handle)
236
+ (type_id,) = struct.unpack("<I", handle.read(4))
237
+ metadata[key] = _read_gguf_value(handle, type_id)
238
+ except (InspectionError, struct.error, UnicodeDecodeError) as error:
239
+ truncated = True
240
+ notes.append(f"metadata decoding stopped early: {error}")
241
+
242
+ detail: dict[str, Any] = {
243
+ "gguf_version": version,
244
+ "tensor_count": tensor_count,
245
+ "metadata_kv_count": kv_count,
246
+ "metadata_decoded_count": len(metadata),
247
+ "architecture": metadata.get("general.architecture"),
248
+ "name": metadata.get("general.name"),
249
+ "file_type": metadata.get("general.file_type"),
250
+ "quantization_version": metadata.get("general.quantization_version"),
251
+ "metadata": metadata,
252
+ }
253
+ if truncated:
254
+ detail["metadata_complete"] = False
255
+ else:
256
+ detail["metadata_complete"] = len(metadata) == kv_count
257
+ return detail, notes
258
+
259
+
260
+ def _inspect_tflite(path: Path, size: int) -> tuple[dict[str, Any], list[str]]:
261
+ return (
262
+ {"file_identifier": "TFL3"},
263
+ [
264
+ "Graph, quantization and delegate analysis for TFLite is not implemented in this "
265
+ "pure-Python package. Use `npx deepbom audit` or https://deepbom.org",
266
+ ],
267
+ )
268
+
269
+
270
+ def inspect(path: str | Path) -> Artifact:
271
+ """Inspect one artifact and return container-level facts."""
272
+ target = Path(path)
273
+ if not target.is_file():
274
+ raise InspectionError(f"not a file: {target}")
275
+ size = target.stat().st_size
276
+ if size == 0:
277
+ raise InspectionError(f"empty file: {target}")
278
+
279
+ with target.open("rb") as handle:
280
+ head = handle.read(1024)
281
+ fmt, evidence = identify(head)
282
+
283
+ detail: dict[str, Any] = {}
284
+ notes: list[str] = []
285
+ try:
286
+ if fmt == "safetensors":
287
+ detail, notes = _inspect_safetensors(target, size)
288
+ elif fmt == "gguf":
289
+ detail, notes = _inspect_gguf(target, size)
290
+ elif fmt == "tflite":
291
+ detail, notes = _inspect_tflite(target, size)
292
+ elif fmt in ("onnx", "coreml", "zip-container"):
293
+ notes = [
294
+ f"{fmt} container inspection is not implemented in this pure-Python package. "
295
+ "Use https://deepbom.org",
296
+ ]
297
+ else:
298
+ notes = ["Unrecognised container. No format-specific facts are reported."]
299
+ except (struct.error, UnicodeDecodeError, json.JSONDecodeError) as error:
300
+ raise InspectionError(f"{fmt} header could not be decoded: {error}") from error
301
+
302
+ return Artifact(
303
+ path=str(target),
304
+ size_bytes=size,
305
+ sha256=_sha256(target),
306
+ format=fmt,
307
+ format_evidence=evidence,
308
+ detail=detail,
309
+ notes=notes,
310
+ )
@@ -0,0 +1,139 @@
1
+ Metadata-Version: 2.4
2
+ Name: deepbom
3
+ Version: 0.1.0
4
+ Summary: Deployment-artifact inspection for on-device neural network models
5
+ Author: Jun-Hwan Kwon
6
+ License: Copyright (C) 2026 Jun-Hwan Kwon. All rights reserved.
7
+
8
+ This repository and its generated software artifacts are currently provided as
9
+ private research software. No license is granted to copy, modify, distribute,
10
+ sublicense, reverse engineer, or create derivative works from the source code,
11
+ WebAssembly modules, generated JavaScript bindings, or packaged executables,
12
+ except where a separate file or component carries an explicit license.
13
+
14
+ Access to the hosted service does not grant a software or implementation
15
+ license.
16
+
17
+ Future public releases may license selected contracts, conformance fixtures,
18
+ validation data, or automation clients separately. A license applies only to
19
+ the files and versions that explicitly carry it. Third-party dependencies and
20
+ model artifacts remain subject to their respective licenses.
21
+
22
+ Project-URL: Homepage, https://deepbom.org
23
+ Keywords: gguf,safetensors,tflite,onnx,coreml,on-device,edge-ai,quantization,ml-bom,static-analysis
24
+ Classifier: Development Status :: 3 - Alpha
25
+ Classifier: Intended Audience :: Developers
26
+ Classifier: Intended Audience :: Science/Research
27
+ Classifier: License :: OSI Approved :: ISC License (ISCL)
28
+ Classifier: Programming Language :: Python :: 3
29
+ Classifier: Programming Language :: Python :: 3.9
30
+ Classifier: Programming Language :: Python :: 3.10
31
+ Classifier: Programming Language :: Python :: 3.11
32
+ Classifier: Programming Language :: Python :: 3.12
33
+ Classifier: Programming Language :: Python :: 3.13
34
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
35
+ Classifier: Topic :: Software Development :: Quality Assurance
36
+ Classifier: Typing :: Typed
37
+ Requires-Python: >=3.9
38
+ Description-Content-Type: text/markdown
39
+ License-File: LICENSE
40
+ Dynamic: license-file
41
+
42
+ # deepbom
43
+
44
+ Deployment-artifact inspection for on-device neural network models.
45
+
46
+ Identifies model artifact formats from their container signature and reads the
47
+ contracts that can be decoded without loading tensor payload values.
48
+
49
+ ```console
50
+ $ pip install deepbom
51
+ $ deepbom inspect model.safetensors
52
+
53
+ model.safetensors
54
+ sha256 a35fd03f52c12f4e78a246bec1927e9a169377fbb8905dc13165d285010e7a44
55
+ format safetensors size 2.6 MB
56
+ evidence u64 header length followed by a JSON header
57
+
58
+ tensors
59
+ count 38
60
+ parameters 1,377,408
61
+ stored payload 2.6 MB
62
+ dtypes F16 x38
63
+
64
+ Container-level facts only. No tensor payload values were read.
65
+ ```
66
+
67
+ ```console
68
+ $ deepbom inspect model.gguf
69
+
70
+ model.gguf
71
+ sha256 cb95a6e10f28b76a1dd71c15560dec5a5eee8943f591ef45d11c129786b22cff
72
+ format gguf size 509.0 KB
73
+ evidence magic "GGUF" at offset 0
74
+
75
+ container
76
+ gguf version 3
77
+ tensors 39
78
+ metadata 26 / 26 (complete)
79
+ architecture llama
80
+ file type 2
81
+ quant version 2
82
+ ```
83
+
84
+ ## What it reads
85
+
86
+ | Format | Reported |
87
+ | --- | --- |
88
+ | **SafeTensors** | tensor inventory, dtypes, shapes, parameter count, stored payload bytes, metadata, header/file size agreement |
89
+ | **GGUF** | version, tensor count, full metadata key/value inventory, architecture, file type, quantization version |
90
+ | TFLite, ONNX, Core ML | format identification and SHA-256 only |
91
+
92
+ Format is decided from container evidence — FlatBuffer identifier, magic
93
+ bytes, header structure — never from the filename extension. ONNX and Core ML
94
+ are separated by their protobuf field layout rather than guessed.
95
+
96
+ ## Usage
97
+
98
+ ```console
99
+ deepbom inspect <file>
100
+ deepbom inspect <file> --json
101
+ deepbom --version
102
+ ```
103
+
104
+ As a library:
105
+
106
+ ```python
107
+ from deepbom import inspect
108
+
109
+ artifact = inspect("model.gguf")
110
+ print(artifact.format, artifact.sha256)
111
+ print(artifact.detail["architecture"])
112
+ print(artifact.to_dict())
113
+ ```
114
+
115
+ ## Scope
116
+
117
+ This package is pure Python with no dependencies. It reports **container-level
118
+ facts only**: what the header and directory structures determine. Tensor
119
+ payload values are never read, and nothing is inferred that the container does
120
+ not state.
121
+
122
+ Graph structure, quantization contracts, predicted delegate placement,
123
+ target-profile cost projections and CycloneDX ML-BOM export are **not** part of
124
+ this package. For TFLite graph analysis:
125
+
126
+ ```console
127
+ npx deepbom audit model.tflite
128
+ ```
129
+
130
+ ONNX, Core ML and runtime evidence are available in the browser version at
131
+ <https://deepbom.org>.
132
+
133
+ ## Privacy
134
+
135
+ No network access. Model bytes, filenames and results are never uploaded.
136
+
137
+ ## License
138
+
139
+ ISC — see [LICENSE](./LICENSE).
@@ -0,0 +1,11 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/deepbom/__init__.py
5
+ src/deepbom/__main__.py
6
+ src/deepbom/inspect.py
7
+ src/deepbom.egg-info/PKG-INFO
8
+ src/deepbom.egg-info/SOURCES.txt
9
+ src/deepbom.egg-info/dependency_links.txt
10
+ src/deepbom.egg-info/entry_points.txt
11
+ src/deepbom.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ deepbom = deepbom.__main__:main
@@ -0,0 +1 @@
1
+ deepbom