zeroquantz 0.1.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.
- zeroquantz/__init__.py +14 -0
- zeroquantz/__main__.py +8 -0
- zeroquantz/agent/__init__.py +16 -0
- zeroquantz/agent/dispatcher.py +520 -0
- zeroquantz/agent/intents.py +46 -0
- zeroquantz/agent/parser.py +255 -0
- zeroquantz/benchmark/__init__.py +7 -0
- zeroquantz/benchmark/latency.py +66 -0
- zeroquantz/benchmark/memory.py +41 -0
- zeroquantz/benchmark/quality.py +38 -0
- zeroquantz/benchmark/runner.py +151 -0
- zeroquantz/cli/__init__.py +7 -0
- zeroquantz/cli/app.py +98 -0
- zeroquantz/cli/commands.py +459 -0
- zeroquantz/cli/interactive.py +56 -0
- zeroquantz/core/__init__.py +7 -0
- zeroquantz/core/artifacts.py +179 -0
- zeroquantz/core/context.py +127 -0
- zeroquantz/core/events.py +30 -0
- zeroquantz/core/exceptions.py +105 -0
- zeroquantz/core/session.py +202 -0
- zeroquantz/core/subenv.py +202 -0
- zeroquantz/deploy/__init__.py +25 -0
- zeroquantz/deploy/assets.py +161 -0
- zeroquantz/deploy/launcher.py +80 -0
- zeroquantz/deploy/runtime_env.py +66 -0
- zeroquantz/deploy/targets.py +154 -0
- zeroquantz/export/__init__.py +8 -0
- zeroquantz/export/exporter.py +68 -0
- zeroquantz/export/report.py +203 -0
- zeroquantz/hardware/__init__.py +15 -0
- zeroquantz/hardware/capabilities.py +152 -0
- zeroquantz/hardware/detector.py +200 -0
- zeroquantz/hardware/gpu.py +31 -0
- zeroquantz/models/__init__.py +8 -0
- zeroquantz/models/architecture.py +168 -0
- zeroquantz/models/downloader.py +161 -0
- zeroquantz/models/hf_auth.py +105 -0
- zeroquantz/models/inspector.py +249 -0
- zeroquantz/models/metadata.py +108 -0
- zeroquantz/models/search.py +71 -0
- zeroquantz/optimization/__init__.py +22 -0
- zeroquantz/optimization/candidate.py +272 -0
- zeroquantz/optimization/constraints.py +70 -0
- zeroquantz/optimization/fit.py +203 -0
- zeroquantz/optimization/pareto.py +66 -0
- zeroquantz/optimization/planner.py +297 -0
- zeroquantz/optimization/recommender.py +149 -0
- zeroquantz/profiling/__init__.py +18 -0
- zeroquantz/profiling/calibration.py +74 -0
- zeroquantz/profiling/sensitivity.py +234 -0
- zeroquantz/quantization/__init__.py +17 -0
- zeroquantz/quantization/backends/__init__.py +8 -0
- zeroquantz/quantization/backends/bitsandbytes.py +210 -0
- zeroquantz/quantization/backends/torchao.py +198 -0
- zeroquantz/quantization/base.py +136 -0
- zeroquantz/quantization/catalog.py +321 -0
- zeroquantz/quantization/config.py +106 -0
- zeroquantz/quantization/gguf_pipeline.py +210 -0
- zeroquantz/quantization/isolated.py +248 -0
- zeroquantz/quantization/memory.py +133 -0
- zeroquantz/quantization/native.py +91 -0
- zeroquantz/quantization/registry.py +101 -0
- zeroquantz/render.py +341 -0
- zeroquantz/runtimes/__init__.py +18 -0
- zeroquantz/runtimes/base.py +64 -0
- zeroquantz/runtimes/compatibility.py +91 -0
- zeroquantz/runtimes/registry.py +70 -0
- zeroquantz/runtimes/transformers.py +53 -0
- zeroquantz/runtimes/vllm.py +83 -0
- zeroquantz/tui/__init__.py +13 -0
- zeroquantz/tui/app.py +77 -0
- zeroquantz/tui/banner.py +47 -0
- zeroquantz/tui/screens/__init__.py +25 -0
- zeroquantz/tui/screens/confirm.py +41 -0
- zeroquantz/tui/screens/execute.py +194 -0
- zeroquantz/tui/screens/model_select.py +206 -0
- zeroquantz/tui/screens/plan.py +177 -0
- zeroquantz/tui/screens/quantize_select.py +272 -0
- zeroquantz/tui/screens/settings.py +219 -0
- zeroquantz/tui/screens/token.py +94 -0
- zeroquantz/tui/screens/welcome.py +128 -0
- zeroquantz/tui/screens/workspace.py +175 -0
- zeroquantz/tui/styles/app.tcss +424 -0
- zeroquantz/tui/widgets/__init__.py +9 -0
- zeroquantz/tui/widgets/chip.py +36 -0
- zeroquantz/tui/widgets/sidebar.py +107 -0
- zeroquantz/tui/widgets/status_bar.py +43 -0
- zeroquantz/utils/__init__.py +8 -0
- zeroquantz/utils/config.py +46 -0
- zeroquantz/utils/env.py +78 -0
- zeroquantz/utils/logging.py +73 -0
- zeroquantz/utils/metrics.py +98 -0
- zeroquantz/utils/paths.py +57 -0
- zeroquantz/utils/units.py +134 -0
- zeroquantz/verification/__init__.py +17 -0
- zeroquantz/verification/logits.py +55 -0
- zeroquantz/verification/report.py +186 -0
- zeroquantz/verification/weights.py +44 -0
- zeroquantz/version.py +8 -0
- zeroquantz-0.1.0.dist-info/METADATA +72 -0
- zeroquantz-0.1.0.dist-info/RECORD +105 -0
- zeroquantz-0.1.0.dist-info/WHEEL +4 -0
- zeroquantz-0.1.0.dist-info/entry_points.txt +2 -0
- zeroquantz-0.1.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/* ZeroQuantz TUI theme — OpenCode-style dark, cyan/green accents. */
|
|
2
|
+
|
|
3
|
+
Screen {
|
|
4
|
+
background: #0b0e14;
|
|
5
|
+
color: #c9d1d9;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/* ---- welcome screen ---- */
|
|
9
|
+
|
|
10
|
+
#welcome {
|
|
11
|
+
align: center middle;
|
|
12
|
+
height: 1fr;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.banner {
|
|
16
|
+
color: #c9d1d9;
|
|
17
|
+
text-style: bold;
|
|
18
|
+
content-align: center middle;
|
|
19
|
+
width: auto;
|
|
20
|
+
margin-bottom: 1;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.tagline {
|
|
24
|
+
color: #6e7681;
|
|
25
|
+
content-align: center middle;
|
|
26
|
+
width: auto;
|
|
27
|
+
margin-bottom: 1;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.status-line {
|
|
31
|
+
content-align: center middle;
|
|
32
|
+
width: auto;
|
|
33
|
+
margin-bottom: 1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.prompt-line {
|
|
37
|
+
color: #58a6ff;
|
|
38
|
+
text-style: bold;
|
|
39
|
+
content-align: center middle;
|
|
40
|
+
width: auto;
|
|
41
|
+
margin-bottom: 1;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#menu {
|
|
45
|
+
width: auto;
|
|
46
|
+
max-width: 72;
|
|
47
|
+
height: auto;
|
|
48
|
+
background: transparent;
|
|
49
|
+
border: none;
|
|
50
|
+
padding: 0 2;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
OptionList {
|
|
54
|
+
background: transparent;
|
|
55
|
+
border: none;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
OptionList > .option-list--option-highlighted {
|
|
59
|
+
background: #172033;
|
|
60
|
+
text-style: bold;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
OptionList:focus > .option-list--option-highlighted {
|
|
64
|
+
background: #1f2b40;
|
|
65
|
+
text-style: bold;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* ---- workspace screen ---- */
|
|
69
|
+
|
|
70
|
+
#topbar {
|
|
71
|
+
height: 1;
|
|
72
|
+
background: #11151c;
|
|
73
|
+
color: #8b949e;
|
|
74
|
+
padding: 0 1;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
#body {
|
|
78
|
+
height: 1fr;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
#conversation {
|
|
82
|
+
width: 1fr;
|
|
83
|
+
height: 1fr;
|
|
84
|
+
background: #0d1117;
|
|
85
|
+
padding: 0 1;
|
|
86
|
+
scrollbar-size-vertical: 1;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
#sidebar {
|
|
90
|
+
width: 38;
|
|
91
|
+
height: 1fr;
|
|
92
|
+
background: #0d1117;
|
|
93
|
+
border-left: solid #21262d;
|
|
94
|
+
padding: 0 1;
|
|
95
|
+
display: block;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
#sidebar.hidden {
|
|
99
|
+
display: none;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
#progress {
|
|
103
|
+
height: auto;
|
|
104
|
+
color: #d29922;
|
|
105
|
+
padding: 0 1;
|
|
106
|
+
display: none;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
#progress.active {
|
|
110
|
+
display: block;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
#prompt {
|
|
114
|
+
dock: bottom;
|
|
115
|
+
border: tall #21262d;
|
|
116
|
+
background: #0d1117;
|
|
117
|
+
color: #c9d1d9;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
#prompt:focus {
|
|
121
|
+
border: tall #58a6ff;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* ---- wizard pages ---- */
|
|
125
|
+
|
|
126
|
+
.page-title {
|
|
127
|
+
color: #58a6ff;
|
|
128
|
+
text-style: bold;
|
|
129
|
+
padding: 1 2 0 2;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.page-subtitle {
|
|
133
|
+
color: #6e7681;
|
|
134
|
+
padding: 0 2 1 2;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.page-hint {
|
|
138
|
+
color: #6e7681;
|
|
139
|
+
padding: 1 2;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
#model-page {
|
|
143
|
+
padding: 1 2;
|
|
144
|
+
height: 1fr;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
#model-input {
|
|
148
|
+
border: tall #21262d;
|
|
149
|
+
background: #0d1117;
|
|
150
|
+
margin: 0 0 1 0;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
#model-input:focus {
|
|
154
|
+
border: tall #58a6ff;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
#model-suggestions {
|
|
158
|
+
height: 1fr;
|
|
159
|
+
background: transparent;
|
|
160
|
+
border: none;
|
|
161
|
+
margin-bottom: 1;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
#model-summary {
|
|
165
|
+
height: auto;
|
|
166
|
+
max-height: 12;
|
|
167
|
+
padding: 1 0;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
#model-actions {
|
|
171
|
+
height: auto;
|
|
172
|
+
align: left middle;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
#download-page {
|
|
176
|
+
padding: 1 2;
|
|
177
|
+
height: 1fr;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
#dl-model {
|
|
181
|
+
height: auto;
|
|
182
|
+
margin-bottom: 1;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
#dl-progress {
|
|
186
|
+
width: 100%;
|
|
187
|
+
margin: 1 0;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
#quant-page {
|
|
191
|
+
padding: 1 2;
|
|
192
|
+
height: 1fr;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
#quant-model {
|
|
196
|
+
padding: 0 0 1 0;
|
|
197
|
+
height: auto;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.ctl-row {
|
|
201
|
+
height: 1;
|
|
202
|
+
width: 1fr;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.ctl-label {
|
|
206
|
+
color: #8b949e;
|
|
207
|
+
width: auto;
|
|
208
|
+
padding: 0 1 0 2;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.ctl-value {
|
|
212
|
+
color: #3fb950;
|
|
213
|
+
text-style: bold;
|
|
214
|
+
width: auto;
|
|
215
|
+
padding: 0 1;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.chip {
|
|
219
|
+
width: auto;
|
|
220
|
+
height: 1;
|
|
221
|
+
padding: 0 1;
|
|
222
|
+
margin: 0 1 0 0;
|
|
223
|
+
color: #c9d1d9;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.chip:hover {
|
|
227
|
+
background: #161b22;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.chip:focus {
|
|
231
|
+
background: #161b22;
|
|
232
|
+
text-style: bold;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.chip.-selected {
|
|
236
|
+
background: #1f2b40;
|
|
237
|
+
color: #58a6ff;
|
|
238
|
+
text-style: bold;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
#quant-table {
|
|
242
|
+
height: 1fr;
|
|
243
|
+
margin-top: 1;
|
|
244
|
+
background: #0d1117;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/* ---- plan + execute ---- */
|
|
248
|
+
|
|
249
|
+
#plan-page,
|
|
250
|
+
#exec-page {
|
|
251
|
+
padding: 1 2;
|
|
252
|
+
height: 1fr;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
#deploy-chips {
|
|
256
|
+
height: 1;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
#plan-scroll {
|
|
260
|
+
height: 1fr;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
#plan-body {
|
|
264
|
+
height: auto;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
#plan-status {
|
|
268
|
+
height: auto;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
#exec-stages {
|
|
272
|
+
height: auto;
|
|
273
|
+
padding: 0 0 1 0;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
#exec-progress {
|
|
277
|
+
width: 100%;
|
|
278
|
+
margin: 0 0 1 0;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
#exec-log {
|
|
282
|
+
height: 1fr;
|
|
283
|
+
min-height: 6;
|
|
284
|
+
background: #0d1117;
|
|
285
|
+
border: round #21262d;
|
|
286
|
+
padding: 0 1;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
DataTable > .datatable--cursor {
|
|
290
|
+
background: #1f2b40;
|
|
291
|
+
text-style: bold;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
DataTable > .datatable--header {
|
|
295
|
+
color: #58a6ff;
|
|
296
|
+
text-style: bold;
|
|
297
|
+
background: #11151c;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
#summary-page {
|
|
301
|
+
padding: 1 2;
|
|
302
|
+
height: 1fr;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
#summary-scroll {
|
|
306
|
+
height: 1fr;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
#summary-body {
|
|
310
|
+
height: auto;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
#summary-log {
|
|
314
|
+
height: 9;
|
|
315
|
+
min-height: 3;
|
|
316
|
+
background: #0d1117;
|
|
317
|
+
border: round #21262d;
|
|
318
|
+
padding: 0 1;
|
|
319
|
+
margin-top: 1;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/* ---- settings ---- */
|
|
323
|
+
|
|
324
|
+
#settings-page {
|
|
325
|
+
padding: 1 2;
|
|
326
|
+
height: 1fr;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
#settings-tabs {
|
|
330
|
+
height: 1fr;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
#dl-table,
|
|
334
|
+
#q-table {
|
|
335
|
+
height: 1fr;
|
|
336
|
+
background: #0d1117;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
#tok-status {
|
|
340
|
+
height: auto;
|
|
341
|
+
padding: 1 0;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/* ---- adjust page ---- */
|
|
345
|
+
|
|
346
|
+
#adjust-page {
|
|
347
|
+
padding: 1 2;
|
|
348
|
+
height: 1fr;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
#adjust-body {
|
|
352
|
+
height: auto;
|
|
353
|
+
padding: 1 0;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/* ---- confirm modal ---- */
|
|
357
|
+
|
|
358
|
+
ConfirmScreen {
|
|
359
|
+
align: center middle;
|
|
360
|
+
background: #000000 60%;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
#confirm-dialog {
|
|
364
|
+
width: 74;
|
|
365
|
+
max-width: 90%;
|
|
366
|
+
height: auto;
|
|
367
|
+
background: #0d1117;
|
|
368
|
+
border: round #f85149;
|
|
369
|
+
padding: 1 2;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
#confirm-message {
|
|
373
|
+
height: auto;
|
|
374
|
+
padding-bottom: 1;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/* ---- token modal ---- */
|
|
378
|
+
|
|
379
|
+
TokenScreen {
|
|
380
|
+
align: center middle;
|
|
381
|
+
background: #000000 60%;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
#token-dialog {
|
|
385
|
+
width: 78;
|
|
386
|
+
max-width: 90%;
|
|
387
|
+
height: auto;
|
|
388
|
+
background: #0d1117;
|
|
389
|
+
border: round #58a6ff;
|
|
390
|
+
padding: 1 2;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.dialog-title {
|
|
394
|
+
color: #58a6ff;
|
|
395
|
+
text-style: bold;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.dialog-sub {
|
|
399
|
+
color: #6e7681;
|
|
400
|
+
padding: 0 0 1 0;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
#token-status {
|
|
404
|
+
height: auto;
|
|
405
|
+
padding: 0 0 1 0;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
#token-input {
|
|
409
|
+
border: tall #21262d;
|
|
410
|
+
background: #0b0e14;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
#token-input:focus {
|
|
414
|
+
border: tall #58a6ff;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
#token-actions {
|
|
418
|
+
height: auto;
|
|
419
|
+
padding-top: 1;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
#token-actions Button {
|
|
423
|
+
margin: 0 2 0 0;
|
|
424
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""ZeroQuantz TUI widgets."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from zeroquantz.tui.widgets.chip import Chip
|
|
6
|
+
from zeroquantz.tui.widgets.sidebar import Sidebar
|
|
7
|
+
from zeroquantz.tui.widgets.status_bar import StatusBar
|
|
8
|
+
|
|
9
|
+
__all__ = ["Chip", "Sidebar", "StatusBar"]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""A small, clickable (and keyboard-focusable) chip for inline segmented controls."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from textual.binding import Binding
|
|
6
|
+
from textual.message import Message
|
|
7
|
+
from textual.widgets import Static
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Chip(Static):
|
|
11
|
+
"""A selectable inline control. Click it, or focus it and press Enter/Space."""
|
|
12
|
+
|
|
13
|
+
can_focus = True
|
|
14
|
+
BINDINGS = [Binding("enter,space", "select", "Select", show=False)]
|
|
15
|
+
|
|
16
|
+
class Selected(Message):
|
|
17
|
+
def __init__(self, group: str, value: str) -> None:
|
|
18
|
+
self.group = group
|
|
19
|
+
self.value = value
|
|
20
|
+
super().__init__()
|
|
21
|
+
|
|
22
|
+
def __init__(self, label: str, *, group: str, value: str, selected: bool = False) -> None:
|
|
23
|
+
super().__init__(label)
|
|
24
|
+
self.chip_group = group
|
|
25
|
+
self.chip_value = value
|
|
26
|
+
self.add_class("chip")
|
|
27
|
+
self.set_class(selected, "-selected")
|
|
28
|
+
|
|
29
|
+
def set_selected(self, selected: bool) -> None:
|
|
30
|
+
self.set_class(selected, "-selected")
|
|
31
|
+
|
|
32
|
+
def on_click(self) -> None:
|
|
33
|
+
self.post_message(self.Selected(self.chip_group, self.chip_value))
|
|
34
|
+
|
|
35
|
+
def action_select(self) -> None:
|
|
36
|
+
self.post_message(self.Selected(self.chip_group, self.chip_value))
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""Collapsible side panel: Model / Hardware / Goal / Plan / Session at a glance."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from rich.console import Group
|
|
8
|
+
from rich.rule import Rule
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
from rich.text import Text
|
|
11
|
+
from textual.widgets import Static
|
|
12
|
+
|
|
13
|
+
from zeroquantz.utils import units
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from zeroquantz.core.context import AppContext
|
|
17
|
+
|
|
18
|
+
_HEAD = "bold cyan"
|
|
19
|
+
_MUTED = "grey62"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _section(title: str, rows: list[tuple[str, str]]) -> Group:
|
|
23
|
+
grid = Table.grid(padding=(0, 1))
|
|
24
|
+
grid.add_column(style=_MUTED, no_wrap=True)
|
|
25
|
+
grid.add_column(style="white")
|
|
26
|
+
for k, v in rows:
|
|
27
|
+
grid.add_row(k, v)
|
|
28
|
+
return Group(Text(title, style=_HEAD), grid, Text(""))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Sidebar(Static):
|
|
32
|
+
"""Reactive summary of the current session state."""
|
|
33
|
+
|
|
34
|
+
def update_context(self, ctx: AppContext) -> None:
|
|
35
|
+
sections: list = []
|
|
36
|
+
|
|
37
|
+
# Model
|
|
38
|
+
mp = ctx.session.model_profile
|
|
39
|
+
if mp is not None:
|
|
40
|
+
sections.append(
|
|
41
|
+
_section(
|
|
42
|
+
"Model",
|
|
43
|
+
[
|
|
44
|
+
("id", mp.model_id),
|
|
45
|
+
("arch", mp.architecture or "?"),
|
|
46
|
+
("params", units.humanize_params(mp.parameter_count)),
|
|
47
|
+
("dtype", mp.dtype),
|
|
48
|
+
("size", f"{mp.estimated_weight_size_gb:.1f} GB"),
|
|
49
|
+
],
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
else:
|
|
53
|
+
sections.append(_section("Model", [("", "none loaded")]))
|
|
54
|
+
|
|
55
|
+
# Goal
|
|
56
|
+
goal = ctx.session.goal
|
|
57
|
+
goal_rows = [("objective", str(goal.objective))]
|
|
58
|
+
if goal.max_vram_gb:
|
|
59
|
+
goal_rows.append(("max VRAM", f"{goal.max_vram_gb:g} GB"))
|
|
60
|
+
if goal.runtime:
|
|
61
|
+
goal_rows.append(("runtime", goal.runtime))
|
|
62
|
+
if goal.max_quality_loss:
|
|
63
|
+
goal_rows.append(("max loss", f"{goal.max_quality_loss*100:g}%"))
|
|
64
|
+
sections.append(_section("Goal", goal_rows))
|
|
65
|
+
|
|
66
|
+
# Hardware
|
|
67
|
+
hw = ctx.hardware
|
|
68
|
+
sections.append(
|
|
69
|
+
_section(
|
|
70
|
+
"Hardware",
|
|
71
|
+
[
|
|
72
|
+
("gpu", hw.gpu_name),
|
|
73
|
+
("vram", f"{hw.total_vram_gb:g} GB" if hw.total_vram_gb else "n/a"),
|
|
74
|
+
("sm", hw.sm or "-"),
|
|
75
|
+
("cuda", hw.cuda_version or ("yes" if hw.cuda_available else "no")),
|
|
76
|
+
],
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Plan
|
|
81
|
+
plan = ctx.session.plan
|
|
82
|
+
if plan is not None:
|
|
83
|
+
sections.append(
|
|
84
|
+
_section(
|
|
85
|
+
"Active plan",
|
|
86
|
+
[
|
|
87
|
+
("scheme", f"{plan.low_precision}/{plan.high_precision}"),
|
|
88
|
+
("size", f"{plan.estimated_size_gb:.1f} GB"),
|
|
89
|
+
("vram", f"{plan.estimated_vram_gb:.1f} GB"),
|
|
90
|
+
("fits", "yes" if plan.fits_budget else "no"),
|
|
91
|
+
],
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Session
|
|
96
|
+
sections.append(
|
|
97
|
+
_section(
|
|
98
|
+
"Session",
|
|
99
|
+
[
|
|
100
|
+
("name", ctx.session.name),
|
|
101
|
+
("ops", str(len(ctx.session.operation_log))),
|
|
102
|
+
("selected", ctx.session.selected_method or "-"),
|
|
103
|
+
],
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
self.update(Group(Rule(style="grey30"), *sections))
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Top status bar: model + goal on the left, GPU + VRAM on the right."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
from rich.text import Text
|
|
9
|
+
from textual.widgets import Static
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from zeroquantz.core.context import AppContext
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class StatusBar(Static):
|
|
16
|
+
"""A one-line context header, updated as the session changes."""
|
|
17
|
+
|
|
18
|
+
def update_context(self, ctx: AppContext) -> None:
|
|
19
|
+
session = ctx.session
|
|
20
|
+
model = session.model_id or "no model"
|
|
21
|
+
goal = session.goal
|
|
22
|
+
goal_bits = [str(goal.objective)]
|
|
23
|
+
if goal.max_vram_gb:
|
|
24
|
+
goal_bits.append(f"≤{goal.max_vram_gb:g}GB")
|
|
25
|
+
if goal.runtime:
|
|
26
|
+
goal_bits.append(goal.runtime)
|
|
27
|
+
left = Text.assemble(
|
|
28
|
+
("Model ", "grey62"),
|
|
29
|
+
(model, "bold #58a6ff"),
|
|
30
|
+
(" Goal ", "grey62"),
|
|
31
|
+
(" · ".join(goal_bits), "white"),
|
|
32
|
+
)
|
|
33
|
+
vram = f"{ctx.hardware.total_vram_gb:g} GB" if ctx.hardware.total_vram_gb else "n/a"
|
|
34
|
+
right = Text.assemble(
|
|
35
|
+
(ctx.hardware.gpu_name, "white"),
|
|
36
|
+
(" VRAM ", "grey62"),
|
|
37
|
+
(vram, "bold #3fb950" if ctx.hardware.cuda_available else "bold #d29922"),
|
|
38
|
+
)
|
|
39
|
+
grid = Table.grid(expand=True)
|
|
40
|
+
grid.add_column(justify="left", ratio=1)
|
|
41
|
+
grid.add_column(justify="right")
|
|
42
|
+
grid.add_row(left, right)
|
|
43
|
+
self.update(grid)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Load ``~/.zeroquantz/config.toml`` into a typed :class:`AppConfig`.
|
|
2
|
+
|
|
3
|
+
Precedence (low -> high) is defaults < config file < session < CLI flags. This
|
|
4
|
+
module only handles the file layer; callers apply the higher-precedence layers.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import tomllib
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel
|
|
14
|
+
|
|
15
|
+
from zeroquantz.core.exceptions import ConfigError
|
|
16
|
+
from zeroquantz.utils.paths import paths
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class BenchmarkConfig(BaseModel):
|
|
20
|
+
warmup_runs: int = 3
|
|
21
|
+
runs: int = 10
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class AppConfig(BaseModel):
|
|
25
|
+
default_runtime: str | None = None
|
|
26
|
+
objective: str = "balanced"
|
|
27
|
+
cache_dir: str | None = None
|
|
28
|
+
benchmark: BenchmarkConfig = BenchmarkConfig()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def load_config(path: str | Path | None = None) -> AppConfig:
|
|
32
|
+
config_path = Path(path) if path is not None else paths().config_file
|
|
33
|
+
if not config_path.exists():
|
|
34
|
+
return AppConfig()
|
|
35
|
+
try:
|
|
36
|
+
data: dict[str, Any] = tomllib.loads(config_path.read_text(encoding="utf-8"))
|
|
37
|
+
except (OSError, tomllib.TOMLDecodeError) as exc:
|
|
38
|
+
raise ConfigError(
|
|
39
|
+
f"Could not read config at {config_path}.", detail=str(exc)
|
|
40
|
+
) from exc
|
|
41
|
+
try:
|
|
42
|
+
return AppConfig.model_validate(data)
|
|
43
|
+
except ValueError as exc:
|
|
44
|
+
raise ConfigError(
|
|
45
|
+
f"Invalid configuration in {config_path}.", detail=str(exc)
|
|
46
|
+
) from exc
|