wafer-core 0.1.38__py3-none-any.whl → 0.1.40__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.
- wafer_core/lib/trace_compare/fusion_analyzer.py +2 -0
- wafer_core/rollouts/_logging/__init__.py +5 -1
- wafer_core/rollouts/_logging/logging_config.py +95 -3
- wafer_core/rollouts/_logging/sample_handler.py +66 -0
- wafer_core/rollouts/_pytui/__init__.py +114 -0
- wafer_core/rollouts/_pytui/app.py +809 -0
- wafer_core/rollouts/_pytui/console.py +291 -0
- wafer_core/rollouts/_pytui/renderer.py +210 -0
- wafer_core/rollouts/_pytui/spinner.py +73 -0
- wafer_core/rollouts/_pytui/terminal.py +489 -0
- wafer_core/rollouts/_pytui/text.py +470 -0
- wafer_core/rollouts/_pytui/theme.py +241 -0
- wafer_core/rollouts/evaluation.py +142 -177
- wafer_core/rollouts/progress_app.py +395 -0
- wafer_core/rollouts/tui/DESIGN.md +251 -115
- wafer_core/rollouts/tui/monitor.py +64 -20
- wafer_core/tools/compile/__init__.py +30 -0
- wafer_core/tools/compile/benchmark.py +636 -0
- wafer_core/tools/compile/compiler.py +301 -0
- wafer_core/tools/compile/modal_compile.py +369 -0
- wafer_core/tools/compile/tests/__init__.py +1 -0
- wafer_core/tools/compile/tests/test_compiler.py +675 -0
- wafer_core/tools/compile/tests/test_data/utils.cuh +10 -0
- wafer_core/tools/compile/tests/test_data/vector_add.cu +7 -0
- wafer_core/tools/compile/tests/test_data/with_header.cu +9 -0
- wafer_core/tools/compile/tests/test_modal_integration.py +326 -0
- wafer_core/tools/compile/types.py +117 -0
- {wafer_core-0.1.38.dist-info → wafer_core-0.1.40.dist-info}/METADATA +1 -1
- {wafer_core-0.1.38.dist-info → wafer_core-0.1.40.dist-info}/RECORD +30 -12
- wafer_core/rollouts/events.py +0 -240
- wafer_core/rollouts/progress_display.py +0 -476
- wafer_core/utils/event_streaming.py +0 -63
- {wafer_core-0.1.38.dist-info → wafer_core-0.1.40.dist-info}/WHEEL +0 -0
|
@@ -737,6 +737,8 @@ def analyze_fusion_differences(
|
|
|
737
737
|
"metadata": {
|
|
738
738
|
"trace1_gpu": trace1_gpu,
|
|
739
739
|
"trace2_gpu": trace2_gpu,
|
|
740
|
+
"trace1_platform": trace1_platform,
|
|
741
|
+
"trace2_platform": trace2_platform,
|
|
740
742
|
"trace1_total_kernels": len(trace1_kernels),
|
|
741
743
|
"trace2_total_kernels": len(trace2_kernels),
|
|
742
744
|
"trace1_correlation_groups": len(trace1_large),
|
|
@@ -9,11 +9,15 @@ Provides standardized logging configuration with:
|
|
|
9
9
|
|
|
10
10
|
from .._logging.color_formatter import ColorFormatter, Colors
|
|
11
11
|
from .._logging.json_formatter import JSONFormatter
|
|
12
|
-
from .._logging.logging_config import setup_logging
|
|
12
|
+
from .._logging.logging_config import EvalLoggingContext, setup_eval_logging, setup_logging
|
|
13
|
+
from .._logging.sample_handler import SampleRoutingHandler
|
|
13
14
|
|
|
14
15
|
__all__ = [
|
|
15
16
|
"setup_logging",
|
|
17
|
+
"setup_eval_logging",
|
|
18
|
+
"EvalLoggingContext",
|
|
16
19
|
"ColorFormatter",
|
|
17
20
|
"Colors",
|
|
18
21
|
"JSONFormatter",
|
|
22
|
+
"SampleRoutingHandler",
|
|
19
23
|
]
|
|
@@ -3,10 +3,17 @@
|
|
|
3
3
|
Tiger Style: Explicit configuration, bounded resources, fail-fast.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
import atexit
|
|
7
|
+
import logging
|
|
6
8
|
import logging.config
|
|
9
|
+
import logging.handlers
|
|
7
10
|
import os
|
|
11
|
+
from pathlib import Path
|
|
8
12
|
from typing import Any
|
|
9
13
|
|
|
14
|
+
from .json_formatter import JSONFormatter
|
|
15
|
+
from .sample_handler import SampleRoutingHandler
|
|
16
|
+
|
|
10
17
|
|
|
11
18
|
def setup_logging(
|
|
12
19
|
level: str | None = None,
|
|
@@ -120,7 +127,7 @@ def setup_logging(
|
|
|
120
127
|
"class": "logging.StreamHandler",
|
|
121
128
|
"level": console_level,
|
|
122
129
|
"formatter": console_formatter,
|
|
123
|
-
"stream": "ext://sys.
|
|
130
|
+
"stream": "ext://sys.stderr",
|
|
124
131
|
}
|
|
125
132
|
}
|
|
126
133
|
|
|
@@ -179,6 +186,91 @@ def setup_logging(
|
|
|
179
186
|
if queue_handler is not None and hasattr(queue_handler, "listener"):
|
|
180
187
|
queue_handler.listener.start()
|
|
181
188
|
# Register cleanup on exit (mCoding pattern)
|
|
182
|
-
import atexit
|
|
183
|
-
|
|
184
189
|
atexit.register(queue_handler.listener.stop)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class EvalLoggingContext:
|
|
193
|
+
"""Manages eval-specific logging handlers for the duration of an eval run.
|
|
194
|
+
|
|
195
|
+
Returned by setup_eval_logging(). Call teardown() when the eval is done
|
|
196
|
+
to remove handlers and close file handles.
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
def __init__(
|
|
200
|
+
self,
|
|
201
|
+
logger: logging.Logger,
|
|
202
|
+
queue_handler: logging.handlers.QueueHandler,
|
|
203
|
+
listener: logging.handlers.QueueListener,
|
|
204
|
+
sample_handler: SampleRoutingHandler,
|
|
205
|
+
) -> None:
|
|
206
|
+
self.logger = logger
|
|
207
|
+
self.queue_handler = queue_handler
|
|
208
|
+
self.listener = listener
|
|
209
|
+
self.sample_handler = sample_handler
|
|
210
|
+
|
|
211
|
+
def teardown(self) -> None:
|
|
212
|
+
"""Stop listener, remove handler, close files."""
|
|
213
|
+
self.listener.stop()
|
|
214
|
+
self.logger.removeHandler(self.queue_handler)
|
|
215
|
+
self.sample_handler.close()
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def setup_eval_logging(
|
|
219
|
+
output_dir: Path,
|
|
220
|
+
logger_name: str = "wafer.eval.events",
|
|
221
|
+
max_log_bytes: int = 100_000_000,
|
|
222
|
+
backup_count: int = 5,
|
|
223
|
+
) -> EvalLoggingContext:
|
|
224
|
+
"""Configure eval-specific logging: overview JSONL + per-sample JSONL.
|
|
225
|
+
|
|
226
|
+
Adds handlers to the named logger (not root) for the duration of an
|
|
227
|
+
eval run. Returns an EvalLoggingContext — call .teardown() when done.
|
|
228
|
+
|
|
229
|
+
Handlers:
|
|
230
|
+
1. RotatingFileHandler -> {output_dir}/events.jsonl (INFO+ overview)
|
|
231
|
+
2. SampleRoutingHandler -> {output_dir}/samples/{sample_id}.jsonl (all levels)
|
|
232
|
+
|
|
233
|
+
Both are wrapped in a QueueHandler for non-blocking writes.
|
|
234
|
+
"""
|
|
235
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
236
|
+
|
|
237
|
+
formatter = JSONFormatter()
|
|
238
|
+
|
|
239
|
+
# 1. Overview: events.jsonl (INFO+ only)
|
|
240
|
+
events_handler = logging.handlers.RotatingFileHandler(
|
|
241
|
+
str(output_dir / "events.jsonl"),
|
|
242
|
+
mode="a",
|
|
243
|
+
maxBytes=max_log_bytes,
|
|
244
|
+
backupCount=backup_count,
|
|
245
|
+
)
|
|
246
|
+
events_handler.setFormatter(formatter)
|
|
247
|
+
events_handler.setLevel(logging.INFO)
|
|
248
|
+
|
|
249
|
+
# 2. Per-sample: samples/{sample_id}.jsonl (all levels including DEBUG)
|
|
250
|
+
sample_handler = SampleRoutingHandler(output_dir)
|
|
251
|
+
sample_handler.setFormatter(formatter)
|
|
252
|
+
sample_handler.setLevel(logging.DEBUG)
|
|
253
|
+
|
|
254
|
+
# Wrap both in QueueHandler for non-blocking writes.
|
|
255
|
+
# QueueHandler puts records onto a queue; QueueListener consumes
|
|
256
|
+
# from that queue and dispatches to the real handlers in a background thread.
|
|
257
|
+
import queue as queue_mod
|
|
258
|
+
|
|
259
|
+
q: queue_mod.Queue[logging.LogRecord] = queue_mod.Queue()
|
|
260
|
+
queue_handler = logging.handlers.QueueHandler(q)
|
|
261
|
+
listener = logging.handlers.QueueListener(
|
|
262
|
+
q, events_handler, sample_handler, respect_handler_level=True
|
|
263
|
+
)
|
|
264
|
+
listener.start()
|
|
265
|
+
|
|
266
|
+
logger = logging.getLogger(logger_name)
|
|
267
|
+
logger.addHandler(queue_handler)
|
|
268
|
+
logger.setLevel(logging.DEBUG) # Let handlers decide what to filter
|
|
269
|
+
logger.propagate = False # Events go to files only, not root
|
|
270
|
+
|
|
271
|
+
return EvalLoggingContext(
|
|
272
|
+
logger=logger,
|
|
273
|
+
queue_handler=queue_handler,
|
|
274
|
+
listener=listener,
|
|
275
|
+
sample_handler=sample_handler,
|
|
276
|
+
)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Per-sample JSONL routing handler for eval logging.
|
|
2
|
+
|
|
3
|
+
Routes log records to per-sample files based on extra["sample_id"].
|
|
4
|
+
Eagerly closes files on sample_end to bound open FDs to max_concurrent.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import TextIO
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SampleRoutingHandler(logging.Handler):
|
|
13
|
+
"""Routes log records to per-sample JSONL files.
|
|
14
|
+
|
|
15
|
+
Each record with a sample_id in its extra dict gets written to
|
|
16
|
+
{output_dir}/samples/{sample_id}.jsonl. Records without sample_id
|
|
17
|
+
are silently dropped.
|
|
18
|
+
|
|
19
|
+
File handles are opened lazily on first write and closed eagerly
|
|
20
|
+
when a sample_end message is seen. Peak open FDs = max concurrent
|
|
21
|
+
samples, not total samples.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, output_dir: Path) -> None:
|
|
25
|
+
super().__init__()
|
|
26
|
+
self.samples_dir = output_dir / "samples"
|
|
27
|
+
self.samples_dir.mkdir(parents=True, exist_ok=True)
|
|
28
|
+
self._files: dict[str, TextIO] = {}
|
|
29
|
+
|
|
30
|
+
def emit(self, record: logging.LogRecord) -> None:
|
|
31
|
+
sample_id = getattr(record, "sample_id", None)
|
|
32
|
+
if sample_id is None:
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
f = self._get_or_open(sample_id)
|
|
37
|
+
f.write(self.format(record) + "\n")
|
|
38
|
+
f.flush()
|
|
39
|
+
|
|
40
|
+
# Eagerly close on sample_end — QueueHandler serializes
|
|
41
|
+
# writes so this is safe.
|
|
42
|
+
if record.getMessage() == "sample_end":
|
|
43
|
+
self._close_sample(sample_id)
|
|
44
|
+
except Exception:
|
|
45
|
+
self.handleError(record)
|
|
46
|
+
|
|
47
|
+
def _get_or_open(self, sample_id: str) -> TextIO:
|
|
48
|
+
f = self._files.get(sample_id)
|
|
49
|
+
if f is not None:
|
|
50
|
+
return f
|
|
51
|
+
path = self.samples_dir / f"{sample_id}.jsonl"
|
|
52
|
+
f = open(path, "a")
|
|
53
|
+
self._files[sample_id] = f
|
|
54
|
+
return f
|
|
55
|
+
|
|
56
|
+
def _close_sample(self, sample_id: str) -> None:
|
|
57
|
+
f = self._files.pop(sample_id, None)
|
|
58
|
+
if f is not None:
|
|
59
|
+
f.close()
|
|
60
|
+
|
|
61
|
+
def close(self) -> None:
|
|
62
|
+
"""Close all open file handles on shutdown."""
|
|
63
|
+
for f in self._files.values():
|
|
64
|
+
f.close()
|
|
65
|
+
self._files.clear()
|
|
66
|
+
super().close()
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""pytui - Elm-style Python TUI framework.
|
|
2
|
+
|
|
3
|
+
Zero deps. Differential rendering. Pure functions.
|
|
4
|
+
|
|
5
|
+
Quick start:
|
|
6
|
+
from dataclasses import dataclass, replace
|
|
7
|
+
from pytui import App, Cmd, KeyPress
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class Model:
|
|
11
|
+
count: int = 0
|
|
12
|
+
|
|
13
|
+
def update(model, msg):
|
|
14
|
+
match msg:
|
|
15
|
+
case KeyPress(key="q"): return model, Cmd.quit()
|
|
16
|
+
case KeyPress(key="j"): return replace(model, count=model.count + 1), Cmd.none()
|
|
17
|
+
return model, Cmd.none()
|
|
18
|
+
|
|
19
|
+
def view(model, width, height):
|
|
20
|
+
return [f"Count: {model.count}", "", "j: up q: quit"]
|
|
21
|
+
|
|
22
|
+
App(init=(Model(), Cmd.none()), update=update, view=view).run()
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
# Elm core
|
|
26
|
+
from .app import App, Cmd, FocusEvent, KeyPress, MouseEvent, PasteEvent, Resize, Sub
|
|
27
|
+
|
|
28
|
+
# Console (unified output with spinner/logging coordination)
|
|
29
|
+
from .console import Console, ConsoleHandler, SpinnerHandle
|
|
30
|
+
|
|
31
|
+
# Renderer
|
|
32
|
+
from .renderer import RenderState, diff_render
|
|
33
|
+
|
|
34
|
+
# Spinner
|
|
35
|
+
from .spinner import Spinner
|
|
36
|
+
|
|
37
|
+
# Terminal
|
|
38
|
+
from .terminal import Terminal, TerminalProtocol
|
|
39
|
+
|
|
40
|
+
# Text utilities
|
|
41
|
+
from .text import (
|
|
42
|
+
AnsiCode,
|
|
43
|
+
AnsiCodeTracker,
|
|
44
|
+
apply_background_to_line,
|
|
45
|
+
extract_ansi_code,
|
|
46
|
+
slice_ansi,
|
|
47
|
+
strip_terminal_control_sequences,
|
|
48
|
+
truncate_to_width,
|
|
49
|
+
visible_width,
|
|
50
|
+
wrap_text_with_ansi,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Theme
|
|
54
|
+
from .theme import (
|
|
55
|
+
DARK_THEME,
|
|
56
|
+
MINIMAL_THEME,
|
|
57
|
+
RESET,
|
|
58
|
+
ROUNDED_THEME,
|
|
59
|
+
SOFT_DARK_THEME,
|
|
60
|
+
MinimalTheme,
|
|
61
|
+
RoundedTheme,
|
|
62
|
+
Theme,
|
|
63
|
+
ToolDisplayMode,
|
|
64
|
+
hex_to_bg,
|
|
65
|
+
hex_to_fg,
|
|
66
|
+
hex_to_rgb,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
__all__ = [
|
|
70
|
+
# Elm core
|
|
71
|
+
"App",
|
|
72
|
+
"Cmd",
|
|
73
|
+
"Sub",
|
|
74
|
+
"KeyPress",
|
|
75
|
+
"MouseEvent",
|
|
76
|
+
"PasteEvent",
|
|
77
|
+
"FocusEvent",
|
|
78
|
+
"Resize",
|
|
79
|
+
# Terminal
|
|
80
|
+
"Terminal",
|
|
81
|
+
"TerminalProtocol",
|
|
82
|
+
# Renderer
|
|
83
|
+
"RenderState",
|
|
84
|
+
"diff_render",
|
|
85
|
+
# Text
|
|
86
|
+
"visible_width",
|
|
87
|
+
"truncate_to_width",
|
|
88
|
+
"slice_ansi",
|
|
89
|
+
"wrap_text_with_ansi",
|
|
90
|
+
"apply_background_to_line",
|
|
91
|
+
"extract_ansi_code",
|
|
92
|
+
"strip_terminal_control_sequences",
|
|
93
|
+
"AnsiCode",
|
|
94
|
+
"AnsiCodeTracker",
|
|
95
|
+
# Spinner
|
|
96
|
+
"Spinner",
|
|
97
|
+
# Console
|
|
98
|
+
"Console",
|
|
99
|
+
"ConsoleHandler",
|
|
100
|
+
"SpinnerHandle",
|
|
101
|
+
# Theme
|
|
102
|
+
"Theme",
|
|
103
|
+
"MinimalTheme",
|
|
104
|
+
"RoundedTheme",
|
|
105
|
+
"DARK_THEME",
|
|
106
|
+
"SOFT_DARK_THEME",
|
|
107
|
+
"MINIMAL_THEME",
|
|
108
|
+
"ROUNDED_THEME",
|
|
109
|
+
"RESET",
|
|
110
|
+
"ToolDisplayMode",
|
|
111
|
+
"hex_to_fg",
|
|
112
|
+
"hex_to_bg",
|
|
113
|
+
"hex_to_rgb",
|
|
114
|
+
]
|