metrik-cli 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.
@@ -0,0 +1,89 @@
1
+ # Metrik — .gitignore
2
+ #
3
+ # NOTE: the previous version of this file contained `docs/*`, `.gitignore`, and `.claude`,
4
+ # which excluded the entire documentation tree — i.e. every Phase 0/1 deliverable — from
5
+ # version control. Replaced deliberately. docs/ is the project's primary output right now.
6
+
7
+ # --- Build output that must never be committed ---
8
+ # ADR 016: diagram sources live in docs/diagrams/*.mmd; rendered images are build output.
9
+ docs/static/diagrams/
10
+ # ADR 015: generated reference docs are committed on purpose (drift detection), so they
11
+ # are NOT ignored here — see docs/reference/.
12
+
13
+ # --- Python ---
14
+ __pycache__/
15
+ *.py[cod]
16
+ *.so
17
+ .Python
18
+ build/
19
+ dist/
20
+ *.egg-info/
21
+ .eggs/
22
+ .venv/
23
+ venv/
24
+ env/
25
+
26
+ # Tooling caches
27
+ .pytest_cache/
28
+ .mypy_cache/
29
+ .ruff_cache/
30
+ .hypothesis/
31
+ .coverage
32
+ .coverage.*
33
+ htmlcov/
34
+ coverage.xml
35
+ .benchmarks/
36
+ .tox/
37
+ .nox/
38
+
39
+ # uv (ADR 013) — uv.lock IS committed; the cache is not.
40
+ .uv/
41
+
42
+ # --- Node / web (ADR 009) ---
43
+ node_modules/
44
+ .next/
45
+ out/
46
+ .turbo/
47
+ *.tsbuildinfo
48
+ .pnpm-store/
49
+
50
+ # --- Tauri (ADR 010) ---
51
+ apps/desktop/src-tauri/target/
52
+ apps/desktop/src-tauri/gen/
53
+
54
+ # --- Docs site (ADR 015) ---
55
+ docs/.docusaurus/
56
+ docs/build/
57
+
58
+ # --- Metrik runtime data ---
59
+ # The artifact store and blob store (ADR 006). Content-addressed, machine-generated,
60
+ # gigabytes. Never in git.
61
+ .metrik/
62
+ *.metrik-store/
63
+ # Local config may hold API keys (ADR 018).
64
+ metrik.local.toml
65
+ .env
66
+ .env.*
67
+ !.env.example
68
+
69
+ # Model weights and checkpoints — always too large, often license-encumbered (plan.md §5.7).
70
+ *.safetensors
71
+ *.gguf
72
+ *.onnx
73
+ *.pt
74
+ *.pth
75
+ *.bin
76
+ # ...except the tiny-random fixtures, which are committed on purpose (ADR 017).
77
+ !tests/fixtures/**
78
+
79
+ # --- Editors and OS ---
80
+ .vscode/
81
+ !.vscode/extensions.json
82
+ .idea/
83
+ *.swp
84
+ .DS_Store
85
+ Thumbs.db
86
+ desktop.ini
87
+
88
+ # --- Local agent/tooling settings ---
89
+ .claude/settings.local.json
@@ -0,0 +1,58 @@
1
+ Metadata-Version: 2.4
2
+ Name: metrik-cli
3
+ Version: 0.1.0
4
+ Summary: Metrik command-line interface.
5
+ Project-URL: Homepage, https://github.com/Asmodeus14/Metrik
6
+ Project-URL: Repository, https://github.com/Asmodeus14/Metrik
7
+ Project-URL: Issues, https://github.com/Asmodeus14/Metrik/issues
8
+ Author: The Metrik Authors
9
+ License-Expression: Apache-2.0
10
+ Classifier: Development Status :: 2 - Pre-Alpha
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
14
+ Requires-Python: >=3.11
15
+ Requires-Dist: metrik-bench==0.1.0
16
+ Requires-Dist: metrik-core==0.1.0
17
+ Requires-Dist: metrik-explorer==0.1.0
18
+ Requires-Dist: metrik-sdk==0.1.0
19
+ Requires-Dist: metrik-viz==0.1.0
20
+ Requires-Dist: rich>=13.7
21
+ Requires-Dist: typer>=0.12
22
+ Description-Content-Type: text/markdown
23
+
24
+ # metrik-cli
25
+
26
+ The `metrik` command-line interface.
27
+
28
+ ```console
29
+ $ metrik --version
30
+ $ metrik store path # which store am I using, and why
31
+ $ metrik store ls # what is in it
32
+ $ metrik store show <id> # one artifact: envelope + payload
33
+ $ metrik store lineage <id> # trace it back to what it was derived from
34
+ $ metrik store verify # rehash everything; non-zero exit if anything is wrong
35
+
36
+ $ metrik runs ls # what has been run here, newest first
37
+ $ metrik runs ls --failed # only the ones that did not succeed
38
+ $ metrik runs show <id> # what was asked, what happened, what came out
39
+
40
+ $ metrik plugins # which producers are installed and loadable
41
+ ```
42
+
43
+ `<id>` accepts an abbreviated digest (`b3:f8fae13`), the way `git` accepts a short commit.
44
+
45
+ A run is recorded **whether the work succeeded or raised**, so a command that produced no
46
+ artifacts still leaves a trace — with the failure kind, the message, and the remediation that
47
+ was printed at the time. A success can be re-derived from its artifacts; a failure cannot be
48
+ re-derived from anything, which is why it is the case most worth recording.
49
+
50
+ A run whose outputs have gaps in them is `partial`, not `succeeded`. That distinction survives
51
+ to the exit status and to `runs ls`, so a script cannot mistake a result with holes for a
52
+ complete one.
53
+
54
+ The CLI is designed dual-mode ([`plan.md`](../../plan.md) §3.1): direct and in-process against
55
+ the store today, `--remote` against a daemon from Phase 10. That split is why the CLI can ship
56
+ without the API existing.
57
+
58
+ See [ADR 012](../../docs/planning/adr/012-cli-framework.md) for the Typer + Rich decision.
@@ -0,0 +1,35 @@
1
+ # metrik-cli
2
+
3
+ The `metrik` command-line interface.
4
+
5
+ ```console
6
+ $ metrik --version
7
+ $ metrik store path # which store am I using, and why
8
+ $ metrik store ls # what is in it
9
+ $ metrik store show <id> # one artifact: envelope + payload
10
+ $ metrik store lineage <id> # trace it back to what it was derived from
11
+ $ metrik store verify # rehash everything; non-zero exit if anything is wrong
12
+
13
+ $ metrik runs ls # what has been run here, newest first
14
+ $ metrik runs ls --failed # only the ones that did not succeed
15
+ $ metrik runs show <id> # what was asked, what happened, what came out
16
+
17
+ $ metrik plugins # which producers are installed and loadable
18
+ ```
19
+
20
+ `<id>` accepts an abbreviated digest (`b3:f8fae13`), the way `git` accepts a short commit.
21
+
22
+ A run is recorded **whether the work succeeded or raised**, so a command that produced no
23
+ artifacts still leaves a trace — with the failure kind, the message, and the remediation that
24
+ was printed at the time. A success can be re-derived from its artifacts; a failure cannot be
25
+ re-derived from anything, which is why it is the case most worth recording.
26
+
27
+ A run whose outputs have gaps in them is `partial`, not `succeeded`. That distinction survives
28
+ to the exit status and to `runs ls`, so a script cannot mistake a result with holes for a
29
+ complete one.
30
+
31
+ The CLI is designed dual-mode ([`plan.md`](../../plan.md) §3.1): direct and in-process against
32
+ the store today, `--remote` against a daemon from Phase 10. That split is why the CLI can ship
33
+ without the API existing.
34
+
35
+ See [ADR 012](../../docs/planning/adr/012-cli-framework.md) for the Typer + Rich decision.
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "metrik-cli"
7
+ version = "0.1.0"
8
+ description = "Metrik command-line interface."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "Apache-2.0"
12
+ authors = [{ name = "The Metrik Authors" }]
13
+ classifiers = [
14
+ "Development Status :: 2 - Pre-Alpha",
15
+ "Environment :: Console",
16
+ "Intended Audience :: Science/Research",
17
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
18
+ ]
19
+
20
+ # Typer for type-hints-to-CLI, Rich for output quality (ADR 012). The CLI is a *surface*,
21
+ # so it may depend on metrik-core directly; the SDK-dogfooding contract in .importlinter
22
+ # constrains producers and consumers, not surfaces.
23
+ dependencies = [
24
+ "metrik-core==0.1.0",
25
+ "metrik-sdk==0.1.0",
26
+ "metrik-explorer==0.1.0",
27
+ "metrik-viz==0.1.0",
28
+ "metrik-bench==0.1.0",
29
+ "typer>=0.12",
30
+ "rich>=13.7",
31
+ ]
32
+
33
+ [project.scripts]
34
+ metrik = "metrik.cli.main:main"
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/Asmodeus14/Metrik"
38
+ Repository = "https://github.com/Asmodeus14/Metrik"
39
+ Issues = "https://github.com/Asmodeus14/Metrik/issues"
40
+
41
+ [tool.uv.sources]
42
+ metrik-core = { workspace = true }
43
+ metrik-sdk = { workspace = true }
44
+ metrik-explorer = { workspace = true }
45
+ metrik-viz = { workspace = true }
46
+ metrik-bench = { workspace = true }
47
+
48
+ [tool.hatch.build.targets.wheel]
49
+ packages = ["src/metrik"]
@@ -0,0 +1,36 @@
1
+ """The ``metrik`` command-line interface.
2
+
3
+ ADR 012: Typer for type-hints-to-CLI, Rich for output quality. ``plan.md`` §10 correction 1
4
+ moves the CLI early -- it grows with each module from Phase 2 rather than arriving at Phase 9,
5
+ so there is a usable surface (and therefore real feedback) while the design can still change
6
+ cheaply.
7
+
8
+ The CLI is designed dual-mode (``plan.md`` §3.1): direct and in-process against the store, or
9
+ ``--remote`` against a daemon. Only the direct mode exists today; the daemon arrives in
10
+ Phase 10. The split is worth keeping visible now because it is what lets the CLI ship without
11
+ the API existing.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+
17
+ def _distribution_version(name: str) -> str:
18
+ from importlib.metadata import PackageNotFoundError, version
19
+
20
+ try:
21
+ return version(name)
22
+ except PackageNotFoundError: # pragma: no cover - only in an uninstalled source tree
23
+ return "0+unknown"
24
+
25
+
26
+ #: Read from the installed distribution rather than hardcoded.
27
+ #:
28
+ #: Both existed for a while and drifted: a 0.1.0 build reported 0.0.1 from `metrik --version`,
29
+ #: found by installing the actual wheel rather than by any test. A version string is exactly
30
+ #: the kind of duplicate that stays wrong quietly, because nothing imports it in anger.
31
+ #:
32
+ #: The fallback covers a source checkout with nothing installed, where there is no metadata to
33
+ #: read and no released version to name.
34
+ __version__ = _distribution_version("metrik-cli")
35
+
36
+ __all__ = ["__version__"]
@@ -0,0 +1,7 @@
1
+ """Command groups.
2
+
3
+ One module per group. Each stays a thin shell over the library: parse, call, render. Anything
4
+ worth testing belongs below the CLI, where it can be tested without a subprocess.
5
+ """
6
+
7
+ from __future__ import annotations
@@ -0,0 +1,119 @@
1
+ """``metrik bench`` -- run a benchmark, or explain why it will not.
2
+
3
+ ADR 019. HumanEval and MBPP ask a model to write code and then execute it, so this command
4
+ establishes whether the machine can contain that **before** anything is generated. A refusal
5
+ after an hour of decoding is a refusal that arrives too late to be a safety feature.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Annotated
11
+
12
+ import typer
13
+ from rich.table import Table
14
+ from rich.text import Text
15
+
16
+ from metrik.bench import TASKS, detect_runtimes, require_runtime, resolve_task
17
+ from metrik.cli.console import out
18
+ from metrik.core.errors import UnsupportedError
19
+
20
+ __all__ = ["register"]
21
+
22
+ _STYLE = {"ready": "measured", "not_running": "warn", "not_installed": "muted"}
23
+
24
+
25
+ def register(app: typer.Typer) -> None:
26
+ @app.command("bench")
27
+ def bench(
28
+ model: Annotated[
29
+ str | None, typer.Argument(help="Model to evaluate. Omit with --check.")
30
+ ] = None,
31
+ task: Annotated[str | None, typer.Option("--task", help="Which benchmark to run.")] = None,
32
+ check: Annotated[
33
+ bool,
34
+ typer.Option("--check", help="Report what isolation is available and stop."),
35
+ ] = False,
36
+ ) -> None:
37
+ """Evaluate a model under container isolation -- or refuse, and say why.
38
+
39
+ There is no unsandboxed mode. A task that executes model-generated code runs in a
40
+ container or does not run (ADR 019), and no flag changes that.
41
+ """
42
+ if check:
43
+ _report_capability()
44
+ return
45
+
46
+ if model is None or task is None:
47
+ raise UnsupportedError(
48
+ "a model and a --task are required",
49
+ remediation="Run `metrik bench --check` to see the tasks and what can run here.",
50
+ )
51
+
52
+ spec = resolve_task(task)
53
+ if spec.executes_code:
54
+ # Before generation, deliberately. This raises when nothing can contain the run,
55
+ # and there is no branch that continues past it.
56
+ runtime = require_runtime(task_id=spec.id)
57
+ out.print(
58
+ f"[measured]{runtime.name} {runtime.version} ready[/measured] "
59
+ f"[muted]- {spec.id} will run under container isolation[/muted]"
60
+ )
61
+
62
+ raise UnsupportedError(
63
+ f"the {spec.id} runner is not implemented yet",
64
+ remediation=(
65
+ "Phase 5 ships the sandbox gate before the runner it gates. `metrik bench "
66
+ "--check` reports what isolation this machine has."
67
+ ),
68
+ )
69
+
70
+
71
+ def _report_capability() -> None:
72
+ """What was probed, what each said, and what that permits."""
73
+ runtimes = detect_runtimes()
74
+
75
+ table = Table(box=None, pad_edge=False)
76
+ table.add_column("runtime", no_wrap=True)
77
+ table.add_column("state", no_wrap=True)
78
+ table.add_column("detail", overflow="fold")
79
+ for runtime in runtimes:
80
+ table.add_row(
81
+ Text(runtime.name),
82
+ Text(runtime.availability.value, style=_STYLE.get(runtime.availability.value, "")),
83
+ Text(runtime.version or runtime.detail),
84
+ )
85
+ out.print(table)
86
+ out.print()
87
+
88
+ usable = next((r for r in runtimes if r.ready), None)
89
+ tasks = Table(box=None, pad_edge=False)
90
+ tasks.add_column("task", style="type", no_wrap=True)
91
+ tasks.add_column("executes code", no_wrap=True)
92
+ tasks.add_column("can run here", no_wrap=True)
93
+ tasks.add_column("what it is", overflow="fold")
94
+ for spec in sorted(TASKS.values(), key=lambda s: s.id):
95
+ # The distinction the whole command exists to make visible: a task that executes code
96
+ # needs a runtime, and one that does not never did. Requiring a container for MMLU
97
+ # would push people to disable the check they need for HumanEval.
98
+ runnable = usable is not None or not spec.executes_code
99
+ tasks.add_row(
100
+ Text(spec.id),
101
+ Text("yes" if spec.executes_code else "no", style="warn" if spec.executes_code else ""),
102
+ Text("yes" if runnable else "no", style="measured" if runnable else "err"),
103
+ Text(spec.summary),
104
+ )
105
+ out.print(tasks)
106
+ out.print()
107
+
108
+ if usable is not None:
109
+ out.print(f"[measured]container isolation available via {usable.name}[/measured]")
110
+ return
111
+
112
+ out.print("[warn]no container runtime is available[/warn]")
113
+ for runtime in runtimes:
114
+ if runtime.remediation:
115
+ out.print(f" [muted]{runtime.remediation}[/muted]")
116
+ out.print(
117
+ "[muted]Tasks that do not execute code still run. There is no unsandboxed mode "
118
+ "(ADR 019).[/muted]"
119
+ )