mjolnir-cli 0.2.0__py3-none-any.whl

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.
Files changed (80) hide show
  1. mjolnir_cli/__init__.py +8 -0
  2. mjolnir_cli/__main__.py +4 -0
  3. mjolnir_cli/accuracy.py +333 -0
  4. mjolnir_cli/autoresearch.py +539 -0
  5. mjolnir_cli/benchy.py +475 -0
  6. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4/config.yaml +78 -0
  7. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_DFLASH2/config.yaml +103 -0
  8. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_DSPARK/config.yaml +102 -0
  9. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_FA2/config.yaml +84 -0
  10. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_FA4hd256/config.yaml +104 -0
  11. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR/config.yaml +86 -0
  12. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3/config.yaml +93 -0
  13. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_DFLASH2/config.yaml +123 -0
  14. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_DSPARK/config.yaml +124 -0
  15. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_LMH8/.env +2 -0
  16. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_LMH8/config.yaml +98 -0
  17. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_LMH8_VT32K/.env +4 -0
  18. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_LMH8_VT32K/config.yaml +98 -0
  19. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_LMH8_VT64K/.env +4 -0
  20. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_LMH8_VT64K/config.yaml +102 -0
  21. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_VT32K/.env +3 -0
  22. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_VT32K/config.yaml +98 -0
  23. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/draft_vocab.json +1 -0
  24. mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/kv_scales.json +35197 -0
  25. mjolnir_cli/bundled/configs/Qwen/Qwen3.8_chat_template.jinja +456 -0
  26. mjolnir_cli/bundled/docker/litellm/docker-compose.yaml +113 -0
  27. mjolnir_cli/bundled/docker/litellm/litellm.env.example +51 -0
  28. mjolnir_cli/bundled/docker/litellm/litellm_config.template.example.yaml +192 -0
  29. mjolnir_cli/bundled/docker/perses/README.md +89 -0
  30. mjolnir_cli/bundled/docker/perses/dashboards/global-datasource.yaml +17 -0
  31. mjolnir_cli/bundled/docker/perses/dashboards/performance_statistics.yaml +771 -0
  32. mjolnir_cli/bundled/docker/perses/dashboards/project.yaml +13 -0
  33. mjolnir_cli/bundled/docker/perses/dashboards/query_statistics.yaml +399 -0
  34. mjolnir_cli/bundled/docker/perses/docker-compose.yaml +51 -0
  35. mjolnir_cli/bundled/docker/perses/perses.yml +16 -0
  36. mjolnir_cli/bundled/docker/perses/prometheus.template.yaml +20 -0
  37. mjolnir_cli/bundled/scripts/hw/setup-thor.sh +1287 -0
  38. mjolnir_cli/cachectl.py +146 -0
  39. mjolnir_cli/charts.py +1687 -0
  40. mjolnir_cli/cli.py +1480 -0
  41. mjolnir_cli/clidoc.py +170 -0
  42. mjolnir_cli/config.py +346 -0
  43. mjolnir_cli/dockerctl.py +533 -0
  44. mjolnir_cli/drivers/fa2_driver.py +89 -0
  45. mjolnir_cli/drivers/fi_driver.py +134 -0
  46. mjolnir_cli/e2e.py +302 -0
  47. mjolnir_cli/fonts/InterDisplay-Bold.ttf +0 -0
  48. mjolnir_cli/fonts/InterDisplay-Medium.ttf +0 -0
  49. mjolnir_cli/fonts/InterDisplay-Regular.ttf +0 -0
  50. mjolnir_cli/fonts/InterDisplay-SemiBold.ttf +0 -0
  51. mjolnir_cli/fonts/OFL.txt +92 -0
  52. mjolnir_cli/gate.py +233 -0
  53. mjolnir_cli/history.py +159 -0
  54. mjolnir_cli/interactive.py +383 -0
  55. mjolnir_cli/legs.py +109 -0
  56. mjolnir_cli/litellm.py +254 -0
  57. mjolnir_cli/perses.py +214 -0
  58. mjolnir_cli/picker.py +70 -0
  59. mjolnir_cli/readme.py +1020 -0
  60. mjolnir_cli/stack.py +340 -0
  61. mjolnir_cli/tasks.py +122 -0
  62. mjolnir_cli/theme.py +281 -0
  63. mjolnir_cli/thor.py +108 -0
  64. mjolnir_cli/verify/__init__.py +2 -0
  65. mjolnir_cli/verify/cli.py +816 -0
  66. mjolnir_cli/verify/docker.py +334 -0
  67. mjolnir_cli/verify/kvcache.py +180 -0
  68. mjolnir_cli/verify/legs.py +134 -0
  69. mjolnir_cli/verify/ncu.py +141 -0
  70. mjolnir_cli/verify/profile.py +312 -0
  71. mjolnir_cli/verify/report.py +177 -0
  72. mjolnir_cli/verify/results.py +157 -0
  73. mjolnir_cli/verify/spec.py +279 -0
  74. mjolnir_cli/verify/stages.py +817 -0
  75. mjolnir_cli-0.2.0.dist-info/METADATA +87 -0
  76. mjolnir_cli-0.2.0.dist-info/RECORD +80 -0
  77. mjolnir_cli-0.2.0.dist-info/WHEEL +4 -0
  78. mjolnir_cli-0.2.0.dist-info/entry_points.txt +2 -0
  79. mjolnir_cli-0.2.0.dist-info/licenses/LICENSE +202 -0
  80. mjolnir_cli-0.2.0.dist-info/licenses/NOTICE +64 -0
@@ -0,0 +1,146 @@
1
+ """Host page-cache management + the one-time, targeted NOPASSWD sudo grant
2
+ that lets the kernel benches clear it without a password prompt.
3
+
4
+ The kernel compile / verify / bench path clears the host page cache
5
+ (``sysctl -w vm.drop_caches=3``) right before the measurement so a warm file
6
+ cache can't skew the numbers. That call needs root, so
7
+ ``mjolnir grant-permissions`` installs a *targeted* NOPASSWD grant —
8
+ drop-caches only, **never full sudo** — as a single sudoers drop-in
9
+ (``/etc/sudoers.d/mjolnir-drop-caches``). After that one install (one sudo
10
+ password prompt) the cache clear runs passwordless on every run.
11
+
12
+ ``prepare_bench()`` is the "always-before" hook the compile/test/bench entry
13
+ points call: it clears when the grant is present and silently skips (with a
14
+ hint) when it isn't — it never prompts and never fails a run.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import getpass
20
+ import os
21
+ import re
22
+ import subprocess
23
+ import sys
24
+ import tempfile
25
+
26
+ # The command the grant covers. sudoers requires the absolute path; the
27
+ # property is the kernel page/dentry/inode cache clear.
28
+ SYSCTL = "/usr/sbin/sysctl"
29
+ DROP_CACHES = "vm.drop_caches=3"
30
+ DROP_CACHES_CMD = f"{SYSCTL} -w {DROP_CACHES}"
31
+
32
+ # The sudoers drop-in the grant installs (one file, one rule).
33
+ SUDOERS_DROPIN = "/etc/sudoers.d/mjolnir-drop-caches"
34
+
35
+ # The rule is recognized in a `sudo -l` listing by the sysctl command + the
36
+ # property name. `=` is escaped to `\=` in the listing, but `vm.drop_caches`
37
+ # (a literal dot) survives verbatim — and the literal dot keeps it distinct
38
+ # from an unrelated `tee /proc/sys/vm/drop_caches` grant (a slash, not a dot).
39
+ _GRANTED_RE = re.compile(r"sysctl.*vm\.drop_caches")
40
+
41
+
42
+ def _user() -> str:
43
+ """The invoking user (honors SUDO_USER when already running under sudo)."""
44
+ return os.environ.get("SUDO_USER") or getpass.getuser()
45
+
46
+
47
+ def _rule() -> str:
48
+ """The single sudoers rule: this user runs the cache clear passwordless —
49
+ nothing else (explicitly *not* full sudo)."""
50
+ return f"{_user()} ALL=(ALL) NOPASSWD: {DROP_CACHES_CMD}\n"
51
+
52
+
53
+ def drop_caches_granted() -> bool:
54
+ """True if the current user can run the cache clear passwordless.
55
+
56
+ Checked via the non-interactive ``sudo -n -l`` listing (read-only, no
57
+ password prompt). A missing/locked sudo → False (callers skip the clear
58
+ rather than fail)."""
59
+ try:
60
+ p = subprocess.run(["sudo", "-n", "-l"], capture_output=True, text=True, timeout=15)
61
+ except (OSError, subprocess.TimeoutExpired):
62
+ return False
63
+ return p.returncode == 0 and _GRANTED_RE.search(p.stdout) is not None
64
+
65
+
66
+ def drop_caches() -> int:
67
+ """Clear the page cache passwordless (``sudo -n sysctl -w
68
+ vm.drop_caches=3``). Returns the sysctl exit code (0 = cleared).
69
+
70
+ The command is passed as separate argv elements (NOT one joined string)
71
+ so ``sudo`` matches it against the ``/usr/sbin/sysctl -w vm.drop_caches=3``
72
+ NOPASSWD rule — a single token would not match and would fall through to
73
+ the password-requiring rule. ``-n`` makes a missing grant fail fast
74
+ instead of prompting — callers that may run without the grant check
75
+ :func:`drop_caches_granted` first or use :func:`prepare_bench`."""
76
+ return subprocess.call(["sudo", "-n", SYSCTL, "-w", DROP_CACHES])
77
+
78
+
79
+ def grant_drop_caches(assume_yes: bool = False) -> int:
80
+ """Install the targeted drop-caches NOPASSWD grant (idempotent; exit code).
81
+
82
+ Already granted → no-op (0). Otherwise the single rule is written to a
83
+ temp file, validated with ``visudo -cf`` (a bad rule can never reach
84
+ sudoers), then installed as a 0440 root-owned drop-in via one ``sudo
85
+ install`` — the single password prompt. Adds only the cache-clear
86
+ command, never full sudo."""
87
+ if drop_caches_granted():
88
+ print("[mjolnir] drop-caches grant already present — nothing to do", file=sys.stderr)
89
+ return 0
90
+ if not assume_yes:
91
+ try:
92
+ ans = (
93
+ input(
94
+ f"Install a NOPASSWD sudo grant for '{DROP_CACHES_CMD}' (cache clear only — not full sudo)? [y/N] "
95
+ )
96
+ .strip()
97
+ .lower()
98
+ )
99
+ except EOFError:
100
+ ans = ""
101
+ if ans not in ("y", "yes"):
102
+ print("[mjolnir] aborted — no change", file=sys.stderr)
103
+ return 1
104
+
105
+ rule = _rule()
106
+ fd, tmp = tempfile.mkstemp(prefix="mjolnir-sudoers-")
107
+ try:
108
+ with os.fdopen(fd, "w") as fh:
109
+ fh.write(rule)
110
+ chk = subprocess.run(["visudo", "-cf", tmp], capture_output=True, text=True)
111
+ if chk.returncode != 0:
112
+ print(f"[mjolnir] visudo rejected the rule:\n{chk.stdout}{chk.stderr}", file=sys.stderr)
113
+ return 1
114
+ # One privileged step: install as a 0440 root-owned drop-in.
115
+ rc = subprocess.call(["sudo", "install", "-m", "0440", "-o", "root", "-g", "root", tmp, SUDOERS_DROPIN])
116
+ if rc != 0:
117
+ print(f"[mjolnir] failed to install the drop-in (exit {rc})", file=sys.stderr)
118
+ return rc
119
+ print(f"[mjolnir] installed {SUDOERS_DROPIN}", file=sys.stderr)
120
+ return 0
121
+ finally:
122
+ try:
123
+ os.unlink(tmp)
124
+ except OSError:
125
+ pass
126
+
127
+
128
+ def prepare_bench() -> None:
129
+ """The "always-before" clean-cache hook. Called right before a kernel
130
+ compile/test/bench (``mjolnir verify``, ``image gates``) and around the server
131
+ (before ``serve up``, after ``serve down``) so a warm file cache can't skew
132
+ a measurement and freed cache is released after serving. Never raises and
133
+ never prompts — if the grant is missing it prints a hint and proceeds
134
+ (the work still happens, just on a warm cache).
135
+ """
136
+ if not drop_caches_granted():
137
+ print(
138
+ "[mjolnir] page-cache clear skipped (grant missing) — run 'mjolnir grant-permissions' to enable it",
139
+ file=sys.stderr,
140
+ )
141
+ return
142
+ rc = drop_caches()
143
+ if rc == 0:
144
+ print("[mjolnir] page cache cleared (vm.drop_caches=3)", file=sys.stderr)
145
+ else:
146
+ print(f"[mjolnir] page-cache clear failed (exit {rc}) — proceeding", file=sys.stderr)