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.
- metrik_cli-0.1.0/.gitignore +89 -0
- metrik_cli-0.1.0/PKG-INFO +58 -0
- metrik_cli-0.1.0/README.md +35 -0
- metrik_cli-0.1.0/pyproject.toml +49 -0
- metrik_cli-0.1.0/src/metrik/cli/__init__.py +36 -0
- metrik_cli-0.1.0/src/metrik/cli/commands/__init__.py +7 -0
- metrik_cli-0.1.0/src/metrik/cli/commands/bench.py +119 -0
- metrik_cli-0.1.0/src/metrik/cli/commands/explore.py +370 -0
- metrik_cli-0.1.0/src/metrik/cli/commands/plugins.py +167 -0
- metrik_cli-0.1.0/src/metrik/cli/commands/runs.py +254 -0
- metrik_cli-0.1.0/src/metrik/cli/commands/store.py +262 -0
- metrik_cli-0.1.0/src/metrik/cli/commands/viz.py +102 -0
- metrik_cli-0.1.0/src/metrik/cli/config.py +93 -0
- metrik_cli-0.1.0/src/metrik/cli/console.py +65 -0
- metrik_cli-0.1.0/src/metrik/cli/main.py +98 -0
- metrik_cli-0.1.0/src/metrik/cli/py.typed +0 -0
- metrik_cli-0.1.0/tests/test_run_commands.py +164 -0
- metrik_cli-0.1.0/tests/test_store_commands.py +393 -0
|
@@ -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,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
|
+
)
|