zenthrix 0.1.2__tar.gz → 0.2.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.
- {zenthrix-0.1.2 → zenthrix-0.2.0}/CHANGELOG.md +30 -1
- {zenthrix-0.1.2 → zenthrix-0.2.0}/PKG-INFO +29 -4
- {zenthrix-0.1.2 → zenthrix-0.2.0}/README.md +27 -3
- {zenthrix-0.1.2 → zenthrix-0.2.0}/pyproject.toml +4 -2
- {zenthrix-0.1.2 → zenthrix-0.2.0}/python/zenthrix/__init__.py +4 -1
- {zenthrix-0.1.2 → zenthrix-0.2.0}/python/zenthrix/adapters/__init__.py +7 -1
- {zenthrix-0.1.2 → zenthrix-0.2.0}/python/zenthrix/adapters/gguf_loader.py +2 -2
- {zenthrix-0.1.2 → zenthrix-0.2.0}/python/zenthrix/adapters/onnx_loader.py +2 -1
- zenthrix-0.2.0/python/zenthrix/adapters/safetensors_loader.py +16 -0
- zenthrix-0.2.0/python/zenthrix/cli.py +227 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/python/zenthrix/engine.py +15 -5
- zenthrix-0.2.0/python/zenthrix/metadata.py +342 -0
- zenthrix-0.2.0/python/zenthrix/runtime.py +96 -0
- zenthrix-0.2.0/python/zenthrix/settings.py +103 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/python/zenthrix/validation.py +3 -1
- zenthrix-0.2.0/tests/test_cli.py +129 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/tests/test_engine.py +23 -0
- zenthrix-0.2.0/tests/test_metadata.py +185 -0
- zenthrix-0.2.0/tests/test_runtime.py +92 -0
- zenthrix-0.2.0/tests/test_settings.py +99 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/tests/test_validation.py +26 -2
- {zenthrix-0.1.2 → zenthrix-0.2.0}/uv.lock +1 -1
- zenthrix-0.1.2/python/zenthrix/cli.py +0 -84
- zenthrix-0.1.2/tests/test_cli.py +0 -77
- {zenthrix-0.1.2 → zenthrix-0.2.0}/.github/CODEOWNERS +0 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/.github/SECURITY.md +0 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/.github/SUPPORT.md +0 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/.github/dependabot.yml +0 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/.github/pull_request_template.md +0 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/.github/workflows/ci.yml +0 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/.github/workflows/release.yml +0 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/.gitignore +0 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/CONTRIBUTING.md +0 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/LICENSE +0 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/python/zenthrix/adapters/pytorch_loader.py +0 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/python/zenthrix/config.py +0 -0
- {zenthrix-0.1.2 → zenthrix-0.2.0}/python/zenthrix/exceptions.py +0 -0
|
@@ -5,7 +5,34 @@ All notable changes to Zenthrix are documented here.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project follows [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [0.2.0] - 2026-09-23
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **SafeTensors format support**: Added `.safetensors` model input format adapter alongside ONNX, GGUF, and PyTorch.
|
|
13
|
+
- **Model metadata inspection**: Extract tensor names, shapes, dtypes, parameter counts, and estimated memory footprint directly from SafeTensors, GGUF, and ONNX source model files without requiring the native engine or loading full weights into RAM.
|
|
14
|
+
- **Configuration file support (`zenthrix.toml`)**: Added project-level defaults for compilation (`target`, `quantization`, `output`) and inference (`max_tokens`, `temperature`) resolved hierarchically with CLI override precedence.
|
|
15
|
+
- **Runtime plugin discovery**: Dynamic discovery of third-party or native runtime adapters registered through the `zenthrix.runtimes` entry point group.
|
|
16
|
+
- **`zenthrix plugins` CLI command**: Inspect and list all discovered runtime plugins in the current Python environment.
|
|
17
|
+
- Exported `ModelMetadata` and `TensorInfo` in the public API.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Bumped the package and public API version to `0.2.0`.
|
|
22
|
+
- Added conditional dependency on `tomli` for Python < 3.11.
|
|
23
|
+
|
|
24
|
+
## [0.1.3] - 2026-09-18
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Added an injectable runtime adapter boundary so provisioned native runtimes
|
|
29
|
+
can implement inference without changing the public `Engine` API.
|
|
30
|
+
- Added opt-in JSON output for CLI inference results and expected errors.
|
|
31
|
+
- `zenthrix inspect` now returns a non-zero error when the native engine is
|
|
32
|
+
unavailable instead of reporting a successful but incomplete inspection.
|
|
33
|
+
- ONNX and GGUF adapter path errors now use the public
|
|
34
|
+
`InputValidationError` type consistently.
|
|
35
|
+
- Bumped the package and public API version to `0.1.3`.
|
|
9
36
|
|
|
10
37
|
## [0.1.2] - 2026-09-18
|
|
11
38
|
|
|
@@ -54,3 +81,5 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
|
|
54
81
|
[0.1.0]: https://github.com/withbrian-technologies/zenthrix/releases/tag/v0.1.0
|
|
55
82
|
[0.1.1]: https://github.com/withbrian-technologies/zenthrix/releases/tag/v0.1.1
|
|
56
83
|
[0.1.2]: https://github.com/withbrian-technologies/zenthrix/releases/tag/v0.1.2
|
|
84
|
+
[0.1.3]: https://github.com/withbrian-technologies/zenthrix/releases/tag/v0.1.3
|
|
85
|
+
[0.2.0]: https://github.com/withbrian-technologies/zenthrix/releases/tag/v0.2.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: zenthrix
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Hardware-adaptive edge neural graph compiler frontend
|
|
5
5
|
Author: WithBrian Technologies
|
|
6
6
|
License: Apache License
|
|
@@ -210,6 +210,7 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
210
210
|
Classifier: Programming Language :: Python :: 3
|
|
211
211
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
212
212
|
Requires-Python: >=3.10
|
|
213
|
+
Requires-Dist: tomli>=2.0; python_version < '3.11'
|
|
213
214
|
Description-Content-Type: text/markdown
|
|
214
215
|
|
|
215
216
|
# Zenthrix
|
|
@@ -217,6 +218,7 @@ Description-Content-Type: text/markdown
|
|
|
217
218
|
**Hardware-Adaptive Edge Neural Graph Compiler**
|
|
218
219
|
|
|
219
220
|
[](https://pypi.org/project/zenthrix/)
|
|
221
|
+
[](https://github.com/withbrian-technologies/zenthrix/releases/latest)
|
|
220
222
|
[](LICENSE)
|
|
221
223
|
[](.github/workflows/ci.yml)
|
|
222
224
|
|
|
@@ -238,7 +240,7 @@ Zenthrix is an edge-native model compiler frontend for compiling open-weight neu
|
|
|
238
240
|
|
|
239
241
|
## Key Features
|
|
240
242
|
|
|
241
|
-
- **Direct Ingestion** — Native loaders for ONNX, PyTorch Export (AOTInductor), and GGUF architectures, with no intermediate format conversion required.
|
|
243
|
+
- **Direct Ingestion** — Native loaders for SafeTensors, ONNX, PyTorch Export (AOTInductor), and GGUF architectures, with no intermediate format conversion required.
|
|
242
244
|
- **Unified Memory Tiling** — Schedules compute passes against unified memory architectures, reducing peak active RAM allocation by up to 40%.
|
|
243
245
|
- **Zero-Copy Runtime** — Emits standalone, relocatable `.zx` binaries that execute locally without a heavy Python runtime dependency.
|
|
244
246
|
- **Privacy-First Compilation** — Models compile entirely on-device; weights and computational graphs never leave the local environment.
|
|
@@ -290,9 +292,16 @@ zenthrix compile \
|
|
|
290
292
|
--output ./llama-3.2-1b.zx
|
|
291
293
|
```
|
|
292
294
|
|
|
293
|
-
### 2. Inspect Graph Optimizations
|
|
295
|
+
### 2. Inspect Models & Graph Optimizations
|
|
294
296
|
|
|
295
|
-
|
|
297
|
+
Inspect uncompiled source models (`.safetensors`, `.gguf`, `.onnx`) directly to view parameter counts, tensor shapes, and estimated memory footprint without requiring the native engine:
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
zenthrix inspect ./model.safetensors
|
|
301
|
+
zenthrix inspect ./model.gguf --json
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
For compiled `.zx` binaries, inspect projected memory profiles and operator fusions:
|
|
296
305
|
|
|
297
306
|
```bash
|
|
298
307
|
zenthrix inspect ./llama-3.2-1b.zx --memory-profile
|
|
@@ -329,6 +338,22 @@ print(f"Time to First Token (TTFT): {output.ttft_ms} ms")
|
|
|
329
338
|
print(f"Throughput: {output.tokens_per_second} tokens/sec")
|
|
330
339
|
```
|
|
331
340
|
|
|
341
|
+
### Automation and Runtime Availability
|
|
342
|
+
|
|
343
|
+
The public package does not include the proprietary native compiler/runtime.
|
|
344
|
+
`compile`, `inspect`, and `run` therefore return a non-zero status until a
|
|
345
|
+
compatible runtime is provisioned; they never create placeholder artifacts or
|
|
346
|
+
claim successful inference.
|
|
347
|
+
|
|
348
|
+
For automation, add `--json` to `run`, `inspect`, or `compile`. Expected
|
|
349
|
+
failures are emitted as a JSON object with `error` and `message` fields:
|
|
350
|
+
|
|
351
|
+
```json
|
|
352
|
+
{"error": "EngineUnavailableError", "message": "..."}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Successful inference uses `text`, `ttft_ms`, and `tokens_per_second` fields.
|
|
356
|
+
|
|
332
357
|
## Supported Target Architectures
|
|
333
358
|
|
|
334
359
|
| Silicon Target | Optimization Backend | Compute Units |
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
**Hardware-Adaptive Edge Neural Graph Compiler**
|
|
4
4
|
|
|
5
5
|
[](https://pypi.org/project/zenthrix/)
|
|
6
|
+
[](https://github.com/withbrian-technologies/zenthrix/releases/latest)
|
|
6
7
|
[](LICENSE)
|
|
7
8
|
[](.github/workflows/ci.yml)
|
|
8
9
|
|
|
@@ -24,7 +25,7 @@ Zenthrix is an edge-native model compiler frontend for compiling open-weight neu
|
|
|
24
25
|
|
|
25
26
|
## Key Features
|
|
26
27
|
|
|
27
|
-
- **Direct Ingestion** — Native loaders for ONNX, PyTorch Export (AOTInductor), and GGUF architectures, with no intermediate format conversion required.
|
|
28
|
+
- **Direct Ingestion** — Native loaders for SafeTensors, ONNX, PyTorch Export (AOTInductor), and GGUF architectures, with no intermediate format conversion required.
|
|
28
29
|
- **Unified Memory Tiling** — Schedules compute passes against unified memory architectures, reducing peak active RAM allocation by up to 40%.
|
|
29
30
|
- **Zero-Copy Runtime** — Emits standalone, relocatable `.zx` binaries that execute locally without a heavy Python runtime dependency.
|
|
30
31
|
- **Privacy-First Compilation** — Models compile entirely on-device; weights and computational graphs never leave the local environment.
|
|
@@ -76,9 +77,16 @@ zenthrix compile \
|
|
|
76
77
|
--output ./llama-3.2-1b.zx
|
|
77
78
|
```
|
|
78
79
|
|
|
79
|
-
### 2. Inspect Graph Optimizations
|
|
80
|
+
### 2. Inspect Models & Graph Optimizations
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
Inspect uncompiled source models (`.safetensors`, `.gguf`, `.onnx`) directly to view parameter counts, tensor shapes, and estimated memory footprint without requiring the native engine:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
zenthrix inspect ./model.safetensors
|
|
86
|
+
zenthrix inspect ./model.gguf --json
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
For compiled `.zx` binaries, inspect projected memory profiles and operator fusions:
|
|
82
90
|
|
|
83
91
|
```bash
|
|
84
92
|
zenthrix inspect ./llama-3.2-1b.zx --memory-profile
|
|
@@ -115,6 +123,22 @@ print(f"Time to First Token (TTFT): {output.ttft_ms} ms")
|
|
|
115
123
|
print(f"Throughput: {output.tokens_per_second} tokens/sec")
|
|
116
124
|
```
|
|
117
125
|
|
|
126
|
+
### Automation and Runtime Availability
|
|
127
|
+
|
|
128
|
+
The public package does not include the proprietary native compiler/runtime.
|
|
129
|
+
`compile`, `inspect`, and `run` therefore return a non-zero status until a
|
|
130
|
+
compatible runtime is provisioned; they never create placeholder artifacts or
|
|
131
|
+
claim successful inference.
|
|
132
|
+
|
|
133
|
+
For automation, add `--json` to `run`, `inspect`, or `compile`. Expected
|
|
134
|
+
failures are emitted as a JSON object with `error` and `message` fields:
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{"error": "EngineUnavailableError", "message": "..."}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Successful inference uses `text`, `ttft_ms`, and `tokens_per_second` fields.
|
|
141
|
+
|
|
118
142
|
## Supported Target Architectures
|
|
119
143
|
|
|
120
144
|
| Silicon Target | Optimization Backend | Compute Units |
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "zenthrix"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "Hardware-adaptive edge neural graph compiler frontend"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -16,7 +16,9 @@ classifiers = [
|
|
|
16
16
|
"Programming Language :: Python :: 3",
|
|
17
17
|
"Programming Language :: Python :: 3 :: Only",
|
|
18
18
|
]
|
|
19
|
-
dependencies = [
|
|
19
|
+
dependencies = [
|
|
20
|
+
"tomli>=2.0; python_version < '3.11'",
|
|
21
|
+
]
|
|
20
22
|
|
|
21
23
|
[dependency-groups]
|
|
22
24
|
dev = [
|
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
from .engine import Engine, InferenceResult
|
|
4
4
|
from .exceptions import EngineUnavailableError, InputValidationError, ZenthrixError
|
|
5
|
+
from .metadata import ModelMetadata, TensorInfo
|
|
5
6
|
|
|
6
|
-
__version__ = "0.
|
|
7
|
+
__version__ = "0.2.0"
|
|
7
8
|
|
|
8
9
|
__all__ = [
|
|
9
10
|
"Engine",
|
|
10
11
|
"EngineUnavailableError",
|
|
11
12
|
"InferenceResult",
|
|
12
13
|
"InputValidationError",
|
|
14
|
+
"ModelMetadata",
|
|
15
|
+
"TensorInfo",
|
|
13
16
|
"ZenthrixError",
|
|
14
17
|
"__version__",
|
|
15
18
|
]
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
from .gguf_loader import validate_gguf
|
|
4
4
|
from .onnx_loader import validate_onnx
|
|
5
5
|
from .pytorch_loader import validate_pytorch_export
|
|
6
|
+
from .safetensors_loader import validate_safetensors
|
|
6
7
|
|
|
7
|
-
__all__ = [
|
|
8
|
+
__all__ = [
|
|
9
|
+
"validate_gguf",
|
|
10
|
+
"validate_onnx",
|
|
11
|
+
"validate_pytorch_export",
|
|
12
|
+
"validate_safetensors",
|
|
13
|
+
]
|
|
8
14
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
+
from ..exceptions import InputValidationError
|
|
5
6
|
from ..validation import validate_model_path
|
|
6
7
|
|
|
7
8
|
|
|
@@ -9,6 +10,5 @@ def validate_gguf(path: str | Path) -> Path:
|
|
|
9
10
|
"""Validate a GGUF model path."""
|
|
10
11
|
model_path = validate_model_path(path)
|
|
11
12
|
if model_path.suffix.lower() != ".gguf":
|
|
12
|
-
raise
|
|
13
|
+
raise InputValidationError(f"Expected a .gguf file, got: {model_path}")
|
|
13
14
|
return model_path
|
|
14
|
-
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
+
from ..exceptions import InputValidationError
|
|
5
6
|
from ..validation import validate_model_path
|
|
6
7
|
|
|
7
8
|
|
|
@@ -9,5 +10,5 @@ def validate_onnx(path: str | Path) -> Path:
|
|
|
9
10
|
"""Validate an ONNX model path without importing optional ONNX tooling."""
|
|
10
11
|
model_path = validate_model_path(path)
|
|
11
12
|
if model_path.suffix.lower() != ".onnx":
|
|
12
|
-
raise
|
|
13
|
+
raise InputValidationError(f"Expected an .onnx file, got: {model_path}")
|
|
13
14
|
return model_path
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""SafeTensors input validation."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from ..exceptions import InputValidationError
|
|
6
|
+
from ..validation import validate_model_path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def validate_safetensors(path: str | Path) -> Path:
|
|
10
|
+
"""Validate a SafeTensors model path."""
|
|
11
|
+
model_path = validate_model_path(path)
|
|
12
|
+
if model_path.suffix.lower() != ".safetensors":
|
|
13
|
+
raise InputValidationError(
|
|
14
|
+
f"Expected a .safetensors file, got: {model_path}"
|
|
15
|
+
)
|
|
16
|
+
return model_path
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"""Command-line interface for the Zenthrix frontend."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from .metadata import ModelMetadata
|
|
12
|
+
|
|
13
|
+
from . import __version__
|
|
14
|
+
from .engine import Engine
|
|
15
|
+
from .exceptions import EngineUnavailableError, ZenthrixError
|
|
16
|
+
from .validation import (
|
|
17
|
+
validate_compile_options,
|
|
18
|
+
validate_compiled_model_path,
|
|
19
|
+
validate_model_input,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
24
|
+
"""Build the command-line parser."""
|
|
25
|
+
parser = argparse.ArgumentParser(prog="zenthrix")
|
|
26
|
+
parser.add_argument("--version", action="version", version=__version__)
|
|
27
|
+
commands = parser.add_subparsers(dest="command", required=True)
|
|
28
|
+
|
|
29
|
+
compile_parser = commands.add_parser("compile", help="Compile a model")
|
|
30
|
+
compile_parser.add_argument("--model", required=True)
|
|
31
|
+
compile_parser.add_argument("--format", required=True, dest="model_format")
|
|
32
|
+
compile_parser.add_argument("--target", default="auto")
|
|
33
|
+
compile_parser.add_argument("--quantization", default=None)
|
|
34
|
+
compile_parser.add_argument("--output", required=True)
|
|
35
|
+
compile_parser.add_argument("--json", action="store_true")
|
|
36
|
+
|
|
37
|
+
inspect_parser = commands.add_parser(
|
|
38
|
+
"inspect", help="Inspect a compiled or source model"
|
|
39
|
+
)
|
|
40
|
+
inspect_parser.add_argument("model")
|
|
41
|
+
inspect_parser.add_argument("--memory-profile", action="store_true")
|
|
42
|
+
inspect_parser.add_argument("--json", action="store_true")
|
|
43
|
+
|
|
44
|
+
run_parser = commands.add_parser("run", help="Run inference")
|
|
45
|
+
run_parser.add_argument("--model", required=True)
|
|
46
|
+
run_parser.add_argument("--prompt", required=True)
|
|
47
|
+
run_parser.add_argument("--max-tokens", type=int, default=None)
|
|
48
|
+
run_parser.add_argument("--temperature", type=float, default=None)
|
|
49
|
+
run_parser.add_argument("--json", action="store_true")
|
|
50
|
+
|
|
51
|
+
commands.add_parser("plugins", help="List installed runtime plugins")
|
|
52
|
+
|
|
53
|
+
return parser
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _compile(args: argparse.Namespace) -> int:
|
|
57
|
+
validate_model_input(args.model, args.model_format)
|
|
58
|
+
validate_compile_options(args.target, args.quantization, args.output)
|
|
59
|
+
raise ZenthrixError(
|
|
60
|
+
"The native compiler engine is not installed. "
|
|
61
|
+
"The public frontend cannot produce a .zx binary yet."
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _inspect(args: argparse.Namespace) -> int:
|
|
66
|
+
# Source model inspection — works without native engine
|
|
67
|
+
model_path = args.model
|
|
68
|
+
suffix = model_path.rsplit(".", 1)[-1].lower() if "." in model_path else ""
|
|
69
|
+
|
|
70
|
+
if suffix == "zx":
|
|
71
|
+
validate_compiled_model_path(model_path)
|
|
72
|
+
raise EngineUnavailableError(
|
|
73
|
+
"The Zenthrix native engine is not installed. "
|
|
74
|
+
"Install the platform runtime before inspecting compiled models."
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Inspect a source model (safetensors, gguf, onnx)
|
|
78
|
+
from .metadata import extract_metadata
|
|
79
|
+
|
|
80
|
+
meta = extract_metadata(model_path)
|
|
81
|
+
|
|
82
|
+
if args.json:
|
|
83
|
+
print(json.dumps(meta.to_dict()))
|
|
84
|
+
else:
|
|
85
|
+
_print_metadata(meta)
|
|
86
|
+
return 0
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _print_metadata(meta: ModelMetadata) -> None:
|
|
90
|
+
"""Pretty-print model metadata to stdout."""
|
|
91
|
+
|
|
92
|
+
lines = [
|
|
93
|
+
"",
|
|
94
|
+
f" Model: {meta.path}",
|
|
95
|
+
f" Format: {meta.format}",
|
|
96
|
+
f" File size: {_format_size(meta.file_size_bytes)}",
|
|
97
|
+
]
|
|
98
|
+
if meta.parameter_count:
|
|
99
|
+
lines.append(f" Parameters: {meta.parameter_count:,}")
|
|
100
|
+
if meta.estimated_memory_mb:
|
|
101
|
+
lines.append(f" Est. memory: {meta.estimated_memory_mb:.1f} MB")
|
|
102
|
+
if meta.architecture:
|
|
103
|
+
lines.append(f" Architecture: {meta.architecture}")
|
|
104
|
+
if meta.tensors:
|
|
105
|
+
lines.append(f" Tensors: {len(meta.tensors)}")
|
|
106
|
+
if meta.extra:
|
|
107
|
+
for key, value in sorted(meta.extra.items()):
|
|
108
|
+
lines.append(f" {key}: {value}")
|
|
109
|
+
lines.append("")
|
|
110
|
+
print("\n".join(lines))
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _format_size(size_bytes: int) -> str:
|
|
114
|
+
"""Format byte count as a human-readable string."""
|
|
115
|
+
if size_bytes >= 1_073_741_824:
|
|
116
|
+
return f"{size_bytes / 1_073_741_824:.1f} GB"
|
|
117
|
+
if size_bytes >= 1_048_576:
|
|
118
|
+
return f"{size_bytes / 1_048_576:.1f} MB"
|
|
119
|
+
if size_bytes >= 1024:
|
|
120
|
+
return f"{size_bytes / 1024:.1f} KB"
|
|
121
|
+
return f"{size_bytes} B"
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _run(args: argparse.Namespace) -> int:
|
|
125
|
+
result = Engine(args.model).generate(
|
|
126
|
+
args.prompt,
|
|
127
|
+
temperature=args.temperature if args.temperature is not None else 0.2,
|
|
128
|
+
max_tokens=args.max_tokens if args.max_tokens is not None else 128,
|
|
129
|
+
)
|
|
130
|
+
if args.json:
|
|
131
|
+
print(
|
|
132
|
+
json.dumps(
|
|
133
|
+
{
|
|
134
|
+
"text": result.text,
|
|
135
|
+
"ttft_ms": result.ttft_ms,
|
|
136
|
+
"tokens_per_second": result.tokens_per_second,
|
|
137
|
+
}
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
else:
|
|
141
|
+
print(result.text)
|
|
142
|
+
return 0
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _plugins(_args: argparse.Namespace) -> int:
|
|
146
|
+
from .runtime import discover_runtimes
|
|
147
|
+
|
|
148
|
+
runtimes = discover_runtimes()
|
|
149
|
+
if not runtimes:
|
|
150
|
+
print("No runtime plugins installed.")
|
|
151
|
+
print()
|
|
152
|
+
print("Install a compatible runtime package, or see:")
|
|
153
|
+
print(" https://github.com/withbrian-technologies/zenthrix#plugins")
|
|
154
|
+
return 0
|
|
155
|
+
|
|
156
|
+
print("Installed runtimes:")
|
|
157
|
+
for name, adapter_cls in runtimes:
|
|
158
|
+
module = f"{adapter_cls.__module__}:{adapter_cls.__qualname__}"
|
|
159
|
+
print(f" • {name} ({module})")
|
|
160
|
+
return 0
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def main(argv: list[str] | None = None) -> int:
|
|
164
|
+
"""Run the CLI and return a process exit code."""
|
|
165
|
+
args = build_parser().parse_args(argv)
|
|
166
|
+
|
|
167
|
+
# Merge config-file defaults for compile/run when available
|
|
168
|
+
_apply_config_defaults(args)
|
|
169
|
+
|
|
170
|
+
try:
|
|
171
|
+
if args.command == "compile":
|
|
172
|
+
return _compile(args)
|
|
173
|
+
if args.command == "inspect":
|
|
174
|
+
return _inspect(args)
|
|
175
|
+
if args.command == "run":
|
|
176
|
+
return _run(args)
|
|
177
|
+
if args.command == "plugins":
|
|
178
|
+
return _plugins(args)
|
|
179
|
+
except (ZenthrixError, ValueError) as error:
|
|
180
|
+
if getattr(args, "json", False):
|
|
181
|
+
print(
|
|
182
|
+
json.dumps(
|
|
183
|
+
{
|
|
184
|
+
"error": type(error).__name__,
|
|
185
|
+
"message": str(error),
|
|
186
|
+
}
|
|
187
|
+
)
|
|
188
|
+
)
|
|
189
|
+
else:
|
|
190
|
+
print(f"zenthrix: error: {error}", file=sys.stderr)
|
|
191
|
+
return 2
|
|
192
|
+
return 0
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _apply_config_defaults(args: argparse.Namespace) -> None:
|
|
196
|
+
"""Load zenthrix.toml and fill in missing CLI arguments."""
|
|
197
|
+
try:
|
|
198
|
+
from .settings import find_config, load_config
|
|
199
|
+
except ImportError:
|
|
200
|
+
return
|
|
201
|
+
|
|
202
|
+
config_path = find_config()
|
|
203
|
+
if config_path is None:
|
|
204
|
+
return
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
config = load_config(config_path)
|
|
208
|
+
except Exception:
|
|
209
|
+
return # silently skip broken config during CLI execution
|
|
210
|
+
|
|
211
|
+
command = getattr(args, "command", None)
|
|
212
|
+
section = config.get(command, {}) if command else {}
|
|
213
|
+
if not isinstance(section, dict):
|
|
214
|
+
return
|
|
215
|
+
|
|
216
|
+
for key, value in section.items():
|
|
217
|
+
# CLI arguments take precedence — only fill in None values
|
|
218
|
+
current = getattr(args, key, _SENTINEL)
|
|
219
|
+
if current is None:
|
|
220
|
+
setattr(args, key, value)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
_SENTINEL = object()
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
if __name__ == "__main__":
|
|
227
|
+
raise SystemExit(main())
|
|
@@ -4,7 +4,7 @@ from dataclasses import dataclass
|
|
|
4
4
|
from math import isfinite
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
-
from .
|
|
7
|
+
from .runtime import RuntimeAdapter, default_runtime
|
|
8
8
|
from .validation import validate_compiled_model_path
|
|
9
9
|
|
|
10
10
|
|
|
@@ -25,8 +25,11 @@ class Engine:
|
|
|
25
25
|
until that engine is provisioned.
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
|
-
def __init__(
|
|
28
|
+
def __init__(
|
|
29
|
+
self, model_path: str | Path, *, runtime: RuntimeAdapter | None = None
|
|
30
|
+
) -> None:
|
|
29
31
|
self.model_path = validate_compiled_model_path(model_path)
|
|
32
|
+
self._runtime = runtime if runtime is not None else default_runtime()
|
|
30
33
|
|
|
31
34
|
def generate(
|
|
32
35
|
self,
|
|
@@ -42,7 +45,14 @@ class Engine:
|
|
|
42
45
|
raise ValueError("temperature must be a finite non-negative number")
|
|
43
46
|
if max_tokens < 1:
|
|
44
47
|
raise ValueError("max_tokens must be at least 1")
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
result = self._runtime.generate(
|
|
49
|
+
self.model_path,
|
|
50
|
+
prompt,
|
|
51
|
+
temperature=temperature,
|
|
52
|
+
max_tokens=max_tokens,
|
|
53
|
+
)
|
|
54
|
+
return InferenceResult(
|
|
55
|
+
text=result.text,
|
|
56
|
+
ttft_ms=result.ttft_ms,
|
|
57
|
+
tokens_per_second=result.tokens_per_second,
|
|
48
58
|
)
|