allegro-buddy 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.
Files changed (63) hide show
  1. allegro_buddy-0.1.0/.github/assets/free.svg +95 -0
  2. allegro_buddy-0.1.0/.github/assets/queue.svg +85 -0
  3. allegro_buddy-0.1.0/.github/assets/why.svg +76 -0
  4. allegro_buddy-0.1.0/.github/workflows/ci.yml +52 -0
  5. allegro_buddy-0.1.0/.github/workflows/pypi.yml +44 -0
  6. allegro_buddy-0.1.0/LICENSE +21 -0
  7. allegro_buddy-0.1.0/PKG-INFO +90 -0
  8. allegro_buddy-0.1.0/README.md +76 -0
  9. allegro_buddy-0.1.0/pyproject.toml +60 -0
  10. allegro_buddy-0.1.0/src/allegro_buddy/__init__.py +5 -0
  11. allegro_buddy-0.1.0/src/allegro_buddy/__main__.py +4 -0
  12. allegro_buddy-0.1.0/src/allegro_buddy/cli.py +567 -0
  13. allegro_buddy-0.1.0/src/allegro_buddy/clusters/allegro.ini +19 -0
  14. allegro_buddy-0.1.0/src/allegro_buddy/clusters/bhlrn4.ini +20 -0
  15. allegro_buddy-0.1.0/src/allegro_buddy/core.py +1556 -0
  16. allegro_buddy-0.1.0/src/allegro_buddy/fit.py +862 -0
  17. allegro_buddy-0.1.0/src/allegro_buddy/handlers/__init__.py +1 -0
  18. allegro_buddy-0.1.0/src/allegro_buddy/handlers/config.py +86 -0
  19. allegro_buddy-0.1.0/src/allegro_buddy/handlers/dev.py +386 -0
  20. allegro_buddy-0.1.0/src/allegro_buddy/handlers/job.py +68 -0
  21. allegro_buddy-0.1.0/src/allegro_buddy/handlers/resources.py +777 -0
  22. allegro_buddy-0.1.0/src/allegro_buddy/handlers/why.py +523 -0
  23. allegro_buddy-0.1.0/src/allegro_buddy/settings.py +238 -0
  24. allegro_buddy-0.1.0/tests/conftest.py +125 -0
  25. allegro_buddy-0.1.0/tests/demo_cluster.py +393 -0
  26. allegro_buddy-0.1.0/tests/slurm_records.py +142 -0
  27. allegro_buddy-0.1.0/tests/snapshots/allegro-config-show.txt +23 -0
  28. allegro_buddy-0.1.0/tests/snapshots/allegro-dev-start.txt +2 -0
  29. allegro_buddy-0.1.0/tests/snapshots/allegro-dev-status.txt +6 -0
  30. allegro_buddy-0.1.0/tests/snapshots/allegro-dev-stop.txt +1 -0
  31. allegro_buddy-0.1.0/tests/snapshots/allegro-error-unknown-job.txt +1 -0
  32. allegro_buddy-0.1.0/tests/snapshots/allegro-free-a5000.txt +8 -0
  33. allegro_buddy-0.1.0/tests/snapshots/allegro-free-cpu.txt +8 -0
  34. allegro_buddy-0.1.0/tests/snapshots/allegro-free-nodes.txt +9 -0
  35. allegro_buddy-0.1.0/tests/snapshots/allegro-free.txt +13 -0
  36. allegro_buddy-0.1.0/tests/snapshots/allegro-help.txt +25 -0
  37. allegro_buddy-0.1.0/tests/snapshots/allegro-info.txt +20 -0
  38. allegro_buddy-0.1.0/tests/snapshots/allegro-job-history.txt +8 -0
  39. allegro_buddy-0.1.0/tests/snapshots/allegro-job-output.txt +7 -0
  40. allegro_buddy-0.1.0/tests/snapshots/allegro-node-cpu.txt +5 -0
  41. allegro_buddy-0.1.0/tests/snapshots/allegro-node-shared.txt +6 -0
  42. allegro_buddy-0.1.0/tests/snapshots/allegro-node.txt +6 -0
  43. allegro_buddy-0.1.0/tests/snapshots/allegro-queue-all.txt +13 -0
  44. allegro_buddy-0.1.0/tests/snapshots/allegro-queue.txt +8 -0
  45. allegro_buddy-0.1.0/tests/snapshots/allegro-why-fix-confirm.txt +5 -0
  46. allegro_buddy-0.1.0/tests/snapshots/allegro-why-fix.txt +4 -0
  47. allegro_buddy-0.1.0/tests/snapshots/allegro-why-job.txt +8 -0
  48. allegro_buddy-0.1.0/tests/snapshots/allegro-why-nodes.txt +15 -0
  49. allegro_buddy-0.1.0/tests/snapshots/allegro-why.txt +10 -0
  50. allegro_buddy-0.1.0/tests/test_cli.py +770 -0
  51. allegro_buddy-0.1.0/tests/test_cluster_output.py +435 -0
  52. allegro_buddy-0.1.0/tests/test_demo_output.py +100 -0
  53. allegro_buddy-0.1.0/tests/test_dev.py +139 -0
  54. allegro_buddy-0.1.0/tests/test_fit.py +763 -0
  55. allegro_buddy-0.1.0/tests/test_gpu.py +443 -0
  56. allegro_buddy-0.1.0/tests/test_gpu_counts.py +223 -0
  57. allegro_buddy-0.1.0/tests/test_integration.py +420 -0
  58. allegro_buddy-0.1.0/tests/test_parsing.py +705 -0
  59. allegro_buddy-0.1.0/tests/test_queue.py +246 -0
  60. allegro_buddy-0.1.0/tests/test_slurm.py +496 -0
  61. allegro_buddy-0.1.0/tests/test_stats.py +296 -0
  62. allegro_buddy-0.1.0/tests/test_why.py +439 -0
  63. allegro_buddy-0.1.0/uv.lock +595 -0
@@ -0,0 +1,95 @@
1
+ <svg class="rich-terminal" viewBox="0 0 994 269.6" xmlns="http://www.w3.org/2000/svg">
2
+ <!-- Generated with Rich https://www.textualize.io -->
3
+ <style>
4
+
5
+ @font-face {
6
+ font-family: "Fira Code";
7
+ src: local("FiraCode-Regular"),
8
+ url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
9
+ url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
10
+ font-style: normal;
11
+ font-weight: 400;
12
+ }
13
+ @font-face {
14
+ font-family: "Fira Code";
15
+ src: local("FiraCode-Bold"),
16
+ url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
17
+ url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
18
+ font-style: bold;
19
+ font-weight: 700;
20
+ }
21
+
22
+ .terminal-2014532663-matrix {
23
+ font-family: Fira Code, monospace;
24
+ font-size: 20px;
25
+ line-height: 24.4px;
26
+ font-variant-east-asian: full-width;
27
+ }
28
+
29
+ .terminal-2014532663-title {
30
+ font-size: 18px;
31
+ font-weight: bold;
32
+ font-family: arial;
33
+ }
34
+
35
+ .terminal-2014532663-r1 { fill: #c5c8c6;font-weight: bold }
36
+ .terminal-2014532663-r2 { fill: #c5c8c6 }
37
+ .terminal-2014532663-r3 { fill: #98a84b }
38
+ .terminal-2014532663-r4 { fill: #868887 }
39
+ .terminal-2014532663-r5 { fill: #d0b344 }
40
+ .terminal-2014532663-r6 { fill: #608ab1 }
41
+ </style>
42
+
43
+ <defs>
44
+ <clipPath id="terminal-2014532663-clip-terminal">
45
+ <rect x="0" y="0" width="975.0" height="218.6" />
46
+ </clipPath>
47
+ <clipPath id="terminal-2014532663-line-0">
48
+ <rect x="0" y="1.5" width="976" height="24.65"/>
49
+ </clipPath>
50
+ <clipPath id="terminal-2014532663-line-1">
51
+ <rect x="0" y="25.9" width="976" height="24.65"/>
52
+ </clipPath>
53
+ <clipPath id="terminal-2014532663-line-2">
54
+ <rect x="0" y="50.3" width="976" height="24.65"/>
55
+ </clipPath>
56
+ <clipPath id="terminal-2014532663-line-3">
57
+ <rect x="0" y="74.7" width="976" height="24.65"/>
58
+ </clipPath>
59
+ <clipPath id="terminal-2014532663-line-4">
60
+ <rect x="0" y="99.1" width="976" height="24.65"/>
61
+ </clipPath>
62
+ <clipPath id="terminal-2014532663-line-5">
63
+ <rect x="0" y="123.5" width="976" height="24.65"/>
64
+ </clipPath>
65
+ <clipPath id="terminal-2014532663-line-6">
66
+ <rect x="0" y="147.9" width="976" height="24.65"/>
67
+ </clipPath>
68
+ <clipPath id="terminal-2014532663-line-7">
69
+ <rect x="0" y="172.3" width="976" height="24.65"/>
70
+ </clipPath>
71
+ </defs>
72
+
73
+ <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="267.6" rx="8"/><text class="terminal-2014532663-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">ab&#160;free&#160;--nodes</text>
74
+ <g transform="translate(26,22)">
75
+ <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
76
+ <circle cx="22" cy="0" r="7" fill="#febc2e"/>
77
+ <circle cx="44" cy="0" r="7" fill="#28c840"/>
78
+ </g>
79
+
80
+ <g transform="translate(9, 41)" clip-path="url(#terminal-2014532663-clip-terminal)">
81
+
82
+ <g class="terminal-2014532663-matrix">
83
+ <text class="terminal-2014532663-r1" x="0" y="20" textLength="756.4" clip-path="url(#terminal-2014532663-line-0)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;GPU&#160;nodes&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text class="terminal-2014532663-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-2014532663-line-0)">
84
+ </text><text class="terminal-2014532663-r1" x="0" y="44.4" textLength="73.2" clip-path="url(#terminal-2014532663-line-1)">Node&#160;&#160;</text><text class="terminal-2014532663-r1" x="109.8" y="44.4" textLength="109.8" clip-path="url(#terminal-2014532663-line-1)">State&#160;&#160;&#160;&#160;</text><text class="terminal-2014532663-r1" x="256.2" y="44.4" textLength="61" clip-path="url(#terminal-2014532663-line-1)">A5000</text><text class="terminal-2014532663-r1" x="353.8" y="44.4" textLength="85.4" clip-path="url(#terminal-2014532663-line-1)">GTX1080</text><text class="terminal-2014532663-r1" x="475.8" y="44.4" textLength="109.8" clip-path="url(#terminal-2014532663-line-1)">Free&#160;CPUs</text><text class="terminal-2014532663-r1" x="622.2" y="44.4" textLength="134.2" clip-path="url(#terminal-2014532663-line-1)">Free&#160;memory</text><text class="terminal-2014532663-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-2014532663-line-1)">
85
+ </text><text class="terminal-2014532663-r2" x="0" y="68.8" textLength="756.4" clip-path="url(#terminal-2014532663-line-2)">──────────────────────────────────────────────────────────────</text><text class="terminal-2014532663-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-2014532663-line-2)">
86
+ </text><text class="terminal-2014532663-r3" x="0" y="93.2" textLength="73.2" clip-path="url(#terminal-2014532663-line-3)">gpu102</text><text class="terminal-2014532663-r3" x="109.8" y="93.2" textLength="109.8" clip-path="url(#terminal-2014532663-line-3)">IDLE&#160;&#160;&#160;&#160;&#160;</text><text class="terminal-2014532663-r3" x="280.6" y="93.2" textLength="12.2" clip-path="url(#terminal-2014532663-line-3)">4</text><text class="terminal-2014532663-r4" x="292.8" y="93.2" textLength="12.2" clip-path="url(#terminal-2014532663-line-3)">/</text><text class="terminal-2014532663-r4" x="305" y="93.2" textLength="12.2" clip-path="url(#terminal-2014532663-line-3)">4</text><text class="terminal-2014532663-r4" x="353.8" y="93.2" textLength="85.4" clip-path="url(#terminal-2014532663-line-3)">&#160;&#160;&#160;&#160;&#160;&#160;-</text><text class="terminal-2014532663-r3" x="524.6" y="93.2" textLength="24.4" clip-path="url(#terminal-2014532663-line-3)">16</text><text class="terminal-2014532663-r4" x="549" y="93.2" textLength="12.2" clip-path="url(#terminal-2014532663-line-3)">/</text><text class="terminal-2014532663-r4" x="561.2" y="93.2" textLength="24.4" clip-path="url(#terminal-2014532663-line-3)">16</text><text class="terminal-2014532663-r3" x="683.2" y="93.2" textLength="24.4" clip-path="url(#terminal-2014532663-line-3)">90</text><text class="terminal-2014532663-r4" x="707.6" y="93.2" textLength="12.2" clip-path="url(#terminal-2014532663-line-3)">/</text><text class="terminal-2014532663-r4" x="719.8" y="93.2" textLength="24.4" clip-path="url(#terminal-2014532663-line-3)">90</text><text class="terminal-2014532663-r4" x="744.2" y="93.2" textLength="12.2" clip-path="url(#terminal-2014532663-line-3)">G</text><text class="terminal-2014532663-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-2014532663-line-3)">
87
+ </text><text class="terminal-2014532663-r3" x="0" y="117.6" textLength="73.2" clip-path="url(#terminal-2014532663-line-4)">gpu101</text><text class="terminal-2014532663-r5" x="109.8" y="117.6" textLength="109.8" clip-path="url(#terminal-2014532663-line-4)">MIXED&#160;&#160;&#160;&#160;</text><text class="terminal-2014532663-r5" x="280.6" y="117.6" textLength="12.2" clip-path="url(#terminal-2014532663-line-4)">3</text><text class="terminal-2014532663-r4" x="292.8" y="117.6" textLength="12.2" clip-path="url(#terminal-2014532663-line-4)">/</text><text class="terminal-2014532663-r4" x="305" y="117.6" textLength="12.2" clip-path="url(#terminal-2014532663-line-4)">4</text><text class="terminal-2014532663-r4" x="353.8" y="117.6" textLength="85.4" clip-path="url(#terminal-2014532663-line-4)">&#160;&#160;&#160;&#160;&#160;&#160;-</text><text class="terminal-2014532663-r3" x="524.6" y="117.6" textLength="24.4" clip-path="url(#terminal-2014532663-line-4)">12</text><text class="terminal-2014532663-r4" x="549" y="117.6" textLength="12.2" clip-path="url(#terminal-2014532663-line-4)">/</text><text class="terminal-2014532663-r4" x="561.2" y="117.6" textLength="24.4" clip-path="url(#terminal-2014532663-line-4)">16</text><text class="terminal-2014532663-r3" x="683.2" y="117.6" textLength="24.4" clip-path="url(#terminal-2014532663-line-4)">90</text><text class="terminal-2014532663-r4" x="707.6" y="117.6" textLength="12.2" clip-path="url(#terminal-2014532663-line-4)">/</text><text class="terminal-2014532663-r4" x="719.8" y="117.6" textLength="24.4" clip-path="url(#terminal-2014532663-line-4)">90</text><text class="terminal-2014532663-r4" x="744.2" y="117.6" textLength="12.2" clip-path="url(#terminal-2014532663-line-4)">G</text><text class="terminal-2014532663-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-2014532663-line-4)">
88
+ </text><text class="terminal-2014532663-r3" x="0" y="142" textLength="73.2" clip-path="url(#terminal-2014532663-line-5)">gpu201</text><text class="terminal-2014532663-r6" x="109.8" y="142" textLength="109.8" clip-path="url(#terminal-2014532663-line-5)">ALLOCATED</text><text class="terminal-2014532663-r4" x="256.2" y="142" textLength="61" clip-path="url(#terminal-2014532663-line-5)">&#160;&#160;&#160;&#160;-</text><text class="terminal-2014532663-r4" x="402.6" y="142" textLength="12.2" clip-path="url(#terminal-2014532663-line-5)">0</text><text class="terminal-2014532663-r4" x="414.8" y="142" textLength="12.2" clip-path="url(#terminal-2014532663-line-5)">/</text><text class="terminal-2014532663-r4" x="427" y="142" textLength="12.2" clip-path="url(#terminal-2014532663-line-5)">4</text><text class="terminal-2014532663-r4" x="536.8" y="142" textLength="12.2" clip-path="url(#terminal-2014532663-line-5)">0</text><text class="terminal-2014532663-r4" x="549" y="142" textLength="12.2" clip-path="url(#terminal-2014532663-line-5)">/</text><text class="terminal-2014532663-r4" x="561.2" y="142" textLength="24.4" clip-path="url(#terminal-2014532663-line-5)">16</text><text class="terminal-2014532663-r3" x="683.2" y="142" textLength="24.4" clip-path="url(#terminal-2014532663-line-5)">90</text><text class="terminal-2014532663-r4" x="707.6" y="142" textLength="12.2" clip-path="url(#terminal-2014532663-line-5)">/</text><text class="terminal-2014532663-r4" x="719.8" y="142" textLength="24.4" clip-path="url(#terminal-2014532663-line-5)">90</text><text class="terminal-2014532663-r4" x="744.2" y="142" textLength="12.2" clip-path="url(#terminal-2014532663-line-5)">G</text><text class="terminal-2014532663-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-2014532663-line-5)">
89
+ </text><text class="terminal-2014532663-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-2014532663-line-6)">
90
+ </text><text class="terminal-2014532663-r1" x="0" y="190.8" textLength="73.2" clip-path="url(#terminal-2014532663-line-7)">total&#160;</text><text class="terminal-2014532663-r3" x="280.6" y="190.8" textLength="12.2" clip-path="url(#terminal-2014532663-line-7)">7</text><text class="terminal-2014532663-r4" x="292.8" y="190.8" textLength="12.2" clip-path="url(#terminal-2014532663-line-7)">/</text><text class="terminal-2014532663-r4" x="305" y="190.8" textLength="12.2" clip-path="url(#terminal-2014532663-line-7)">8</text><text class="terminal-2014532663-r4" x="402.6" y="190.8" textLength="12.2" clip-path="url(#terminal-2014532663-line-7)">0</text><text class="terminal-2014532663-r4" x="414.8" y="190.8" textLength="12.2" clip-path="url(#terminal-2014532663-line-7)">/</text><text class="terminal-2014532663-r4" x="427" y="190.8" textLength="12.2" clip-path="url(#terminal-2014532663-line-7)">4</text><text class="terminal-2014532663-r3" x="524.6" y="190.8" textLength="24.4" clip-path="url(#terminal-2014532663-line-7)">28</text><text class="terminal-2014532663-r4" x="549" y="190.8" textLength="12.2" clip-path="url(#terminal-2014532663-line-7)">/</text><text class="terminal-2014532663-r4" x="561.2" y="190.8" textLength="24.4" clip-path="url(#terminal-2014532663-line-7)">48</text><text class="terminal-2014532663-r3" x="658.8" y="190.8" textLength="36.6" clip-path="url(#terminal-2014532663-line-7)">270</text><text class="terminal-2014532663-r4" x="695.4" y="190.8" textLength="12.2" clip-path="url(#terminal-2014532663-line-7)">/</text><text class="terminal-2014532663-r4" x="707.6" y="190.8" textLength="36.6" clip-path="url(#terminal-2014532663-line-7)">270</text><text class="terminal-2014532663-r4" x="744.2" y="190.8" textLength="12.2" clip-path="url(#terminal-2014532663-line-7)">G</text><text class="terminal-2014532663-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-2014532663-line-7)">
91
+ </text><text class="terminal-2014532663-r4" x="0" y="215.2" textLength="122" clip-path="url(#terminal-2014532663-line-8)">free/total</text><text class="terminal-2014532663-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-2014532663-line-8)">
92
+ </text>
93
+ </g>
94
+ </g>
95
+ </svg>
@@ -0,0 +1,85 @@
1
+ <svg class="rich-terminal" viewBox="0 0 1092 196.39999999999998" xmlns="http://www.w3.org/2000/svg">
2
+ <!-- Generated with Rich https://www.textualize.io -->
3
+ <style>
4
+
5
+ @font-face {
6
+ font-family: "Fira Code";
7
+ src: local("FiraCode-Regular"),
8
+ url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
9
+ url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
10
+ font-style: normal;
11
+ font-weight: 400;
12
+ }
13
+ @font-face {
14
+ font-family: "Fira Code";
15
+ src: local("FiraCode-Bold"),
16
+ url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
17
+ url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
18
+ font-style: bold;
19
+ font-weight: 700;
20
+ }
21
+
22
+ .terminal-1079823974-matrix {
23
+ font-family: Fira Code, monospace;
24
+ font-size: 20px;
25
+ line-height: 24.4px;
26
+ font-variant-east-asian: full-width;
27
+ }
28
+
29
+ .terminal-1079823974-title {
30
+ font-size: 18px;
31
+ font-weight: bold;
32
+ font-family: arial;
33
+ }
34
+
35
+ .terminal-1079823974-r1 { fill: #c5c8c6;font-weight: bold }
36
+ .terminal-1079823974-r2 { fill: #c5c8c6 }
37
+ .terminal-1079823974-r3 { fill: #68a0b3 }
38
+ .terminal-1079823974-r4 { fill: #98a84b;font-weight: bold }
39
+ .terminal-1079823974-r5 { fill: #98a84b }
40
+ .terminal-1079823974-r6 { fill: #d0b344;font-weight: bold }
41
+ .terminal-1079823974-r7 { fill: #d0b344 }
42
+ .terminal-1079823974-r8 { fill: #868887 }
43
+ </style>
44
+
45
+ <defs>
46
+ <clipPath id="terminal-1079823974-clip-terminal">
47
+ <rect x="0" y="0" width="1072.6" height="145.39999999999998" />
48
+ </clipPath>
49
+ <clipPath id="terminal-1079823974-line-0">
50
+ <rect x="0" y="1.5" width="1073.6" height="24.65"/>
51
+ </clipPath>
52
+ <clipPath id="terminal-1079823974-line-1">
53
+ <rect x="0" y="25.9" width="1073.6" height="24.65"/>
54
+ </clipPath>
55
+ <clipPath id="terminal-1079823974-line-2">
56
+ <rect x="0" y="50.3" width="1073.6" height="24.65"/>
57
+ </clipPath>
58
+ <clipPath id="terminal-1079823974-line-3">
59
+ <rect x="0" y="74.7" width="1073.6" height="24.65"/>
60
+ </clipPath>
61
+ <clipPath id="terminal-1079823974-line-4">
62
+ <rect x="0" y="99.1" width="1073.6" height="24.65"/>
63
+ </clipPath>
64
+ </defs>
65
+
66
+ <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="1090" height="194.4" rx="8"/><text class="terminal-1079823974-title" fill="#c5c8c6" text-anchor="middle" x="545" y="27">ab&#160;queue</text>
67
+ <g transform="translate(26,22)">
68
+ <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
69
+ <circle cx="22" cy="0" r="7" fill="#febc2e"/>
70
+ <circle cx="44" cy="0" r="7" fill="#28c840"/>
71
+ </g>
72
+
73
+ <g transform="translate(9, 41)" clip-path="url(#terminal-1079823974-clip-terminal)">
74
+
75
+ <g class="terminal-1079823974-matrix">
76
+ <text class="terminal-1079823974-r1" x="0" y="20" textLength="1037" clip-path="url(#terminal-1079823974-line-0)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Queue&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text class="terminal-1079823974-r2" x="1073.6" y="20" textLength="12.2" clip-path="url(#terminal-1079823974-line-0)">
77
+ </text><text class="terminal-1079823974-r1" x="0" y="44.4" textLength="61" clip-path="url(#terminal-1079823974-line-1)">JobID</text><text class="terminal-1079823974-r1" x="97.6" y="44.4" textLength="61" clip-path="url(#terminal-1079823974-line-1)">Name&#160;</text><text class="terminal-1079823974-r1" x="195.2" y="44.4" textLength="85.4" clip-path="url(#terminal-1079823974-line-1)">State&#160;&#160;</text><text class="terminal-1079823974-r1" x="317.2" y="44.4" textLength="109.8" clip-path="url(#terminal-1079823974-line-1)">Partition</text><text class="terminal-1079823974-r1" x="463.6" y="44.4" textLength="109.8" clip-path="url(#terminal-1079823974-line-1)">Time&#160;left</text><text class="terminal-1079823974-r1" x="610" y="44.4" textLength="97.6" clip-path="url(#terminal-1079823974-line-1)">Start&#160;&#160;&#160;</text><text class="terminal-1079823974-r1" x="744.2" y="44.4" textLength="292.8" clip-path="url(#terminal-1079823974-line-1)">Node/Reason&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text class="terminal-1079823974-r2" x="1073.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1079823974-line-1)">
78
+ </text><text class="terminal-1079823974-r2" x="0" y="68.8" textLength="1037" clip-path="url(#terminal-1079823974-line-2)">─────────────────────────────────────────────────────────────────────────────────────</text><text class="terminal-1079823974-r2" x="1073.6" y="68.8" textLength="12.2" clip-path="url(#terminal-1079823974-line-2)">
79
+ </text><text class="terminal-1079823974-r3" x="0" y="93.2" textLength="61" clip-path="url(#terminal-1079823974-line-3)">64612</text><text class="terminal-1079823974-r2" x="97.6" y="93.2" textLength="61" clip-path="url(#terminal-1079823974-line-3)">train</text><text class="terminal-1079823974-r4" x="195.2" y="93.2" textLength="85.4" clip-path="url(#terminal-1079823974-line-3)">RUNNING</text><text class="terminal-1079823974-r4" x="317.2" y="93.2" textLength="109.8" clip-path="url(#terminal-1079823974-line-3)">gpu&#160;&#160;&#160;&#160;&#160;&#160;</text><text class="terminal-1079823974-r2" x="463.6" y="93.2" textLength="109.8" clip-path="url(#terminal-1079823974-line-3)">&#160;&#160;&#160;&#160;6h57m</text><text class="terminal-1079823974-r5" x="744.2" y="93.2" textLength="292.8" clip-path="url(#terminal-1079823974-line-3)">gpu081&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text class="terminal-1079823974-r2" x="1073.6" y="93.2" textLength="12.2" clip-path="url(#terminal-1079823974-line-3)">
80
+ </text><text class="terminal-1079823974-r3" x="0" y="117.6" textLength="61" clip-path="url(#terminal-1079823974-line-4)">64613</text><text class="terminal-1079823974-r2" x="97.6" y="117.6" textLength="61" clip-path="url(#terminal-1079823974-line-4)">eval&#160;</text><text class="terminal-1079823974-r6" x="195.2" y="117.6" textLength="85.4" clip-path="url(#terminal-1079823974-line-4)">PENDING</text><text class="terminal-1079823974-r4" x="317.2" y="117.6" textLength="109.8" clip-path="url(#terminal-1079823974-line-4)">gpu&#160;&#160;&#160;&#160;&#160;&#160;</text><text class="terminal-1079823974-r2" x="463.6" y="117.6" textLength="109.8" clip-path="url(#terminal-1079823974-line-4)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;8h</text><text class="terminal-1079823974-r2" x="610" y="117.6" textLength="97.6" clip-path="url(#terminal-1079823974-line-4)">in&#160;2h10m</text><text class="terminal-1079823974-r7" x="744.2" y="117.6" textLength="292.8" clip-path="url(#terminal-1079823974-line-4)">GPU-bound&#160;·&#160;max&#160;2/4&#160;free</text><text class="terminal-1079823974-r2" x="1073.6" y="117.6" textLength="12.2" clip-path="url(#terminal-1079823974-line-4)">
81
+ </text><text class="terminal-1079823974-r8" x="0" y="142" textLength="671" clip-path="url(#terminal-1079823974-line-5)">ⓘ&#160;`ab&#160;why`&#160;shows&#160;what&#160;is&#160;short&#160;and&#160;what&#160;would&#160;start&#160;now</text><text class="terminal-1079823974-r2" x="1073.6" y="142" textLength="12.2" clip-path="url(#terminal-1079823974-line-5)">
82
+ </text>
83
+ </g>
84
+ </g>
85
+ </svg>
@@ -0,0 +1,76 @@
1
+ <svg class="rich-terminal" viewBox="0 0 994 147.6" xmlns="http://www.w3.org/2000/svg">
2
+ <!-- Generated with Rich https://www.textualize.io -->
3
+ <style>
4
+
5
+ @font-face {
6
+ font-family: "Fira Code";
7
+ src: local("FiraCode-Regular"),
8
+ url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
9
+ url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
10
+ font-style: normal;
11
+ font-weight: 400;
12
+ }
13
+ @font-face {
14
+ font-family: "Fira Code";
15
+ src: local("FiraCode-Bold"),
16
+ url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
17
+ url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
18
+ font-style: bold;
19
+ font-weight: 700;
20
+ }
21
+
22
+ .terminal-3157129480-matrix {
23
+ font-family: Fira Code, monospace;
24
+ font-size: 20px;
25
+ line-height: 24.4px;
26
+ font-variant-east-asian: full-width;
27
+ }
28
+
29
+ .terminal-3157129480-title {
30
+ font-size: 18px;
31
+ font-weight: bold;
32
+ font-family: arial;
33
+ }
34
+
35
+ .terminal-3157129480-r1 { fill: #68a0b3 }
36
+ .terminal-3157129480-r2 { fill: #c5c8c6 }
37
+ .terminal-3157129480-r3 { fill: #608ab1;font-weight: bold }
38
+ .terminal-3157129480-r4 { fill: #868887 }
39
+ .terminal-3157129480-r5 { fill: #d0b344 }
40
+ .terminal-3157129480-r6 { fill: #98a84b;font-weight: bold }
41
+ .terminal-3157129480-r7 { fill: #68a0b3;font-weight: bold }
42
+ </style>
43
+
44
+ <defs>
45
+ <clipPath id="terminal-3157129480-clip-terminal">
46
+ <rect x="0" y="0" width="975.0" height="96.6" />
47
+ </clipPath>
48
+ <clipPath id="terminal-3157129480-line-0">
49
+ <rect x="0" y="1.5" width="976" height="24.65"/>
50
+ </clipPath>
51
+ <clipPath id="terminal-3157129480-line-1">
52
+ <rect x="0" y="25.9" width="976" height="24.65"/>
53
+ </clipPath>
54
+ <clipPath id="terminal-3157129480-line-2">
55
+ <rect x="0" y="50.3" width="976" height="24.65"/>
56
+ </clipPath>
57
+ </defs>
58
+
59
+ <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="145.6" rx="8"/><text class="terminal-3157129480-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">ab&#160;why&#160;64267</text>
60
+ <g transform="translate(26,22)">
61
+ <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
62
+ <circle cx="22" cy="0" r="7" fill="#febc2e"/>
63
+ <circle cx="44" cy="0" r="7" fill="#28c840"/>
64
+ </g>
65
+
66
+ <g transform="translate(9, 41)" clip-path="url(#terminal-3157129480-clip-terminal)">
67
+
68
+ <g class="terminal-3157129480-matrix">
69
+ <text class="terminal-3157129480-r1" x="0" y="20" textLength="170.8" clip-path="url(#terminal-3157129480-line-0)">64267_[50-142]</text><text class="terminal-3157129480-r3" x="195.2" y="20" textLength="36.6" clip-path="url(#terminal-3157129480-line-0)">big</text><text class="terminal-3157129480-r2" x="231.8" y="20" textLength="158.6" clip-path="url(#terminal-3157129480-line-0)">&#160;&#160;4&#160;CPU&#160;·&#160;56G</text><text class="terminal-3157129480-r4" x="390.4" y="20" textLength="146.4" clip-path="url(#terminal-3157129480-line-0)">&#160;&#160;(93&#160;tasks)</text><text class="terminal-3157129480-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-3157129480-line-0)">
70
+ </text><text class="terminal-3157129480-r2" x="0" y="44.4" textLength="170.8" clip-path="url(#terminal-3157129480-line-1)">&#160;&#160;waiting&#160;for&#160;</text><text class="terminal-3157129480-r5" x="170.8" y="44.4" textLength="73.2" clip-path="url(#terminal-3157129480-line-1)">memory</text><text class="terminal-3157129480-r2" x="244" y="44.4" textLength="134.2" clip-path="url(#terminal-3157129480-line-1)">&#160;to&#160;free&#160;up</text><text class="terminal-3157129480-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-3157129480-line-1)">
71
+ </text><text class="terminal-3157129480-r6" x="24.4" y="68.8" textLength="195.2" clip-path="url(#terminal-3157129480-line-2)">starts&#160;now&#160;with&#160;</text><text class="terminal-3157129480-r7" x="219.6" y="68.8" textLength="183" clip-path="url(#terminal-3157129480-line-2)">--mem&#160;56G&#160;→&#160;32G</text><text class="terminal-3157129480-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-3157129480-line-2)">
72
+ </text><text class="terminal-3157129480-r4" x="0" y="93.2" textLength="671" clip-path="url(#terminal-3157129480-line-3)">ⓘ&#160;`ab&#160;why&#160;64267&#160;--fix`&#160;changes&#160;the&#160;job&#160;so&#160;it&#160;starts&#160;now</text><text class="terminal-3157129480-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-3157129480-line-3)">
73
+ </text>
74
+ </g>
75
+ </g>
76
+ </svg>
@@ -0,0 +1,52 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - 'src/**'
8
+ - 'tests/**'
9
+ - 'pyproject.toml'
10
+ - 'uv.lock'
11
+ - 'scripts/**'
12
+ - '.gitattributes'
13
+ - '.github/workflows/ci.yml'
14
+ pull_request:
15
+ types: [opened, synchronize, reopened, ready_for_review]
16
+ paths:
17
+ - 'src/**'
18
+ - 'tests/**'
19
+ - 'pyproject.toml'
20
+ - 'uv.lock'
21
+ - 'scripts/**'
22
+ - '.gitattributes'
23
+ - '.github/workflows/ci.yml'
24
+
25
+ # A new push to the same branch/PR cancels the run still in progress.
26
+ concurrency:
27
+ group: ${{ github.workflow }}-${{ github.ref }}
28
+ cancel-in-progress: true
29
+
30
+ permissions:
31
+ contents: read
32
+
33
+ jobs:
34
+ test:
35
+ # Draft PRs get no CI; run the checks locally before marking a PR ready.
36
+ if: github.event.pull_request.draft != true
37
+ runs-on: ubuntu-latest
38
+ timeout-minutes: 10
39
+ strategy:
40
+ matrix:
41
+ # Oldest supported and newest only; versions in between add cost, not coverage.
42
+ python: ['3.10', '3.13']
43
+ steps:
44
+ - uses: actions/checkout@v4
45
+ - uses: astral-sh/setup-uv@v6
46
+ with:
47
+ python-version: ${{ matrix.python }}
48
+ enable-cache: true
49
+ - run: uv sync --locked
50
+ - if: matrix.python == '3.10'
51
+ run: uv run ruff check .
52
+ - run: uv run pytest -q
@@ -0,0 +1,44 @@
1
+ name: PyPI
2
+
3
+ # Runs only in the public repo, on the release that release.yml creates there, so
4
+ # the sdist holds only the public snapshot and the PyPI provenance names the public
5
+ # repo. Uses PyPI Trusted Publishing (no token): the PyPI project trusts
6
+ # Grutschus/allegro-buddy, workflow pypi.yml, environment pypi.
7
+ on:
8
+ release:
9
+ types: [published]
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ build:
16
+ if: github.repository == 'Grutschus/allegro-buddy'
17
+ runs-on: ubuntu-latest
18
+ timeout-minutes: 5
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - uses: astral-sh/setup-uv@v6
22
+ - run: uv build
23
+ - uses: actions/upload-artifact@v4
24
+ with:
25
+ name: dist
26
+ path: dist/
27
+
28
+ # Separate job so a required reviewer on the pypi environment can download and
29
+ # inspect the dist artifact before approving the upload.
30
+ publish:
31
+ needs: build
32
+ runs-on: ubuntu-latest
33
+ timeout-minutes: 5
34
+ environment:
35
+ name: pypi
36
+ url: https://pypi.org/project/allegro-buddy/
37
+ permissions:
38
+ id-token: write
39
+ steps:
40
+ - uses: actions/download-artifact@v4
41
+ with:
42
+ name: dist
43
+ path: dist/
44
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Grutschus
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,90 @@
1
+ Metadata-Version: 2.5
2
+ Name: allegro-buddy
3
+ Version: 0.1.0
4
+ Summary: A friendly Slurm helper, with presets for the Allegro cluster.
5
+ Project-URL: Source, https://github.com/Grutschus/allegro-buddy
6
+ Author: Grutschus
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Requires-Python: >=3.10
10
+ Requires-Dist: click>=8.1.7
11
+ Requires-Dist: rich-click>=1.9
12
+ Requires-Dist: rich>=13.0.0
13
+ Description-Content-Type: text/markdown
14
+
15
+ # allegro-buddy
16
+
17
+ A small Slurm companion for everyday cluster work: what is free, why your job is
18
+ waiting, and a dev node in one command. It reads what it needs from Slurm, so it
19
+ works beyond the Allegro cluster it was born on.
20
+
21
+ <img src="https://raw.githubusercontent.com/Grutschus/allegro-buddy/main/.github/assets/queue.svg" alt="ab queue: your jobs, start estimates and why pending jobs wait">
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ uv tool install allegro-buddy # or: pip install allegro-buddy
27
+ ab completion --install # optional: tab completion
28
+ ```
29
+
30
+ This installs two equal commands, `ab` and `allegro-buddy`. Upgrade with
31
+ `uv tool upgrade allegro-buddy`.
32
+
33
+ ## Commands
34
+
35
+ | Command | What it does |
36
+ | --- | --- |
37
+ | `ab info` | Partitions you can use, with free CPUs, memory and GPUs |
38
+ | `ab free [--nodes] [-G a5000]` | Free GPUs by type, per partition or per node |
39
+ | `ab q [--all] [-w]` | Your queue, with start estimates and what pending jobs lack |
40
+ | `ab why [JOBID] [--fix]` | Why a job waits, and the one change that starts it now |
41
+ | `ab node [NODE]` | Which jobs use a node's GPUs, CPUs and memory, and for how long |
42
+ | `ab dev start / login / status / stop` | A detached dev allocation; `ab l` logs in |
43
+ | `ab job history / output` | Your finished jobs and their output |
44
+ | `ab config show` | Every setting and where it came from |
45
+
46
+ Every command has `-h`.
47
+
48
+ ### Why is my job waiting?
49
+
50
+ <img src="https://raw.githubusercontent.com/Grutschus/allegro-buddy/main/.github/assets/why.svg" alt="ab why: what a pending job waits for and the change that starts it now">
51
+
52
+ `ab why JOBID --fix` applies the suggested change after asking. It only touches
53
+ your own pending jobs and never lowers the GPU count unless you pass `--fix gpus`.
54
+
55
+ ### What is free?
56
+
57
+ <img src="https://raw.githubusercontent.com/Grutschus/allegro-buddy/main/.github/assets/free.svg" alt="ab free --nodes: free GPUs, CPUs and memory per node">
58
+
59
+ ### Dev nodes
60
+
61
+ ```bash
62
+ ab dev start --gpu 1 --gpu-type a5000 # waits until running, then shows how to log in
63
+ ab l # ssh into it (--srun where SSH is blocked)
64
+ ab dev stop
65
+ ```
66
+
67
+ ## Configuration
68
+
69
+ Slurm tells allegro-buddy which partitions have GPUs, which GPU types exist and
70
+ which partition is the default. A config file only fills in the rest, such as
71
+ your own `ab dev start` defaults:
72
+
73
+ ```ini
74
+ # ~/.config/allegro-buddy/config.ini
75
+ [dev]
76
+ gpu = 1
77
+ gpu-type = a5000
78
+ cpus = 8
79
+ mem = 32G
80
+ time = 04:00:00
81
+ ```
82
+
83
+ Values come from, highest first: command-line flags, your user file, a site file
84
+ (`$AB_SITE_CONFIG` or `/etc/allegro-buddy/config.ini`), a shipped cluster preset
85
+ (`allegro`, `bhlrn4`), then Slurm. The preset follows Slurm's `ClusterName`;
86
+ `AB_CLUSTER=none` turns it off. `ab config show` lists every value and its source.
87
+
88
+ ## License
89
+
90
+ MIT, see [LICENSE](https://github.com/Grutschus/allegro-buddy/blob/main/LICENSE).
@@ -0,0 +1,76 @@
1
+ # allegro-buddy
2
+
3
+ A small Slurm companion for everyday cluster work: what is free, why your job is
4
+ waiting, and a dev node in one command. It reads what it needs from Slurm, so it
5
+ works beyond the Allegro cluster it was born on.
6
+
7
+ <img src="https://raw.githubusercontent.com/Grutschus/allegro-buddy/main/.github/assets/queue.svg" alt="ab queue: your jobs, start estimates and why pending jobs wait">
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ uv tool install allegro-buddy # or: pip install allegro-buddy
13
+ ab completion --install # optional: tab completion
14
+ ```
15
+
16
+ This installs two equal commands, `ab` and `allegro-buddy`. Upgrade with
17
+ `uv tool upgrade allegro-buddy`.
18
+
19
+ ## Commands
20
+
21
+ | Command | What it does |
22
+ | --- | --- |
23
+ | `ab info` | Partitions you can use, with free CPUs, memory and GPUs |
24
+ | `ab free [--nodes] [-G a5000]` | Free GPUs by type, per partition or per node |
25
+ | `ab q [--all] [-w]` | Your queue, with start estimates and what pending jobs lack |
26
+ | `ab why [JOBID] [--fix]` | Why a job waits, and the one change that starts it now |
27
+ | `ab node [NODE]` | Which jobs use a node's GPUs, CPUs and memory, and for how long |
28
+ | `ab dev start / login / status / stop` | A detached dev allocation; `ab l` logs in |
29
+ | `ab job history / output` | Your finished jobs and their output |
30
+ | `ab config show` | Every setting and where it came from |
31
+
32
+ Every command has `-h`.
33
+
34
+ ### Why is my job waiting?
35
+
36
+ <img src="https://raw.githubusercontent.com/Grutschus/allegro-buddy/main/.github/assets/why.svg" alt="ab why: what a pending job waits for and the change that starts it now">
37
+
38
+ `ab why JOBID --fix` applies the suggested change after asking. It only touches
39
+ your own pending jobs and never lowers the GPU count unless you pass `--fix gpus`.
40
+
41
+ ### What is free?
42
+
43
+ <img src="https://raw.githubusercontent.com/Grutschus/allegro-buddy/main/.github/assets/free.svg" alt="ab free --nodes: free GPUs, CPUs and memory per node">
44
+
45
+ ### Dev nodes
46
+
47
+ ```bash
48
+ ab dev start --gpu 1 --gpu-type a5000 # waits until running, then shows how to log in
49
+ ab l # ssh into it (--srun where SSH is blocked)
50
+ ab dev stop
51
+ ```
52
+
53
+ ## Configuration
54
+
55
+ Slurm tells allegro-buddy which partitions have GPUs, which GPU types exist and
56
+ which partition is the default. A config file only fills in the rest, such as
57
+ your own `ab dev start` defaults:
58
+
59
+ ```ini
60
+ # ~/.config/allegro-buddy/config.ini
61
+ [dev]
62
+ gpu = 1
63
+ gpu-type = a5000
64
+ cpus = 8
65
+ mem = 32G
66
+ time = 04:00:00
67
+ ```
68
+
69
+ Values come from, highest first: command-line flags, your user file, a site file
70
+ (`$AB_SITE_CONFIG` or `/etc/allegro-buddy/config.ini`), a shipped cluster preset
71
+ (`allegro`, `bhlrn4`), then Slurm. The preset follows Slurm's `ClusterName`;
72
+ `AB_CLUSTER=none` turns it off. `ab config show` lists every value and its source.
73
+
74
+ ## License
75
+
76
+ MIT, see [LICENSE](https://github.com/Grutschus/allegro-buddy/blob/main/LICENSE).
@@ -0,0 +1,60 @@
1
+ [project]
2
+ name = "allegro-buddy"
3
+ version = "0.1.0"
4
+ description = "A friendly Slurm helper, with presets for the Allegro cluster."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ dependencies = [
8
+ "click>=8.1.7",
9
+ "rich>=13.0.0",
10
+ "rich-click>=1.9",
11
+ ]
12
+ authors = [{ name = "Grutschus" }]
13
+ license = { text = "MIT" }
14
+
15
+ [project.urls]
16
+ Source = "https://github.com/Grutschus/allegro-buddy"
17
+
18
+ [project.scripts]
19
+ ab = "allegro_buddy.cli:main"
20
+ allegro-buddy = "allegro_buddy.cli:main"
21
+
22
+ [build-system]
23
+ requires = ["hatchling"]
24
+ build-backend = "hatchling.build"
25
+
26
+ [dependency-groups]
27
+ dev = ["pre-commit>=4.0.1", "ruff>=0.6.9", "ty>=0.0.34", "pytest>=8.0", "pytest-mock>=3.14", "pytest-cov>=5.0"]
28
+
29
+ [tool.pytest.ini_options]
30
+ testpaths = ["tests"]
31
+ addopts = "-v"
32
+ markers = [
33
+ "cluster: requires a running Slurm cluster (pass --cluster to enable)",
34
+ ]
35
+
36
+ [tool.hatch.build.targets.wheel]
37
+ packages = ["src/allegro_buddy"]
38
+
39
+ [tool.ruff]
40
+ line-length = 90
41
+ target-version = "py310"
42
+
43
+ [tool.ruff.format]
44
+ quote-style = "single"
45
+ line-ending = "lf"
46
+ indent-style = "space"
47
+
48
+ [tool.ruff.lint]
49
+ extend-select = ["B", "C4", "COM", "I", "RET", "RUF", "SIM", "TC", "UP"]
50
+ ignore = ["COM812"]
51
+
52
+ [tool.ty.environment]
53
+ python-version = "3.10"
54
+
55
+ [tool.ty.src]
56
+ # scripts/ is private tooling that imports tests via sys.path
57
+ exclude = ["scripts"]
58
+
59
+ [tool.uv]
60
+ default-groups = ["dev"]
@@ -0,0 +1,5 @@
1
+ """allegro-buddy package."""
2
+
3
+ __all__ = ['__version__']
4
+
5
+ __version__ = '0.1.0'