wafer-core 0.1.37__py3-none-any.whl → 0.1.39__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 (32) hide show
  1. wafer_core/lib/trace_compare/fusion_analyzer.py +2 -0
  2. wafer_core/rollouts/_logging/__init__.py +5 -1
  3. wafer_core/rollouts/_logging/logging_config.py +95 -3
  4. wafer_core/rollouts/_logging/sample_handler.py +66 -0
  5. wafer_core/rollouts/_pytui/__init__.py +114 -0
  6. wafer_core/rollouts/_pytui/app.py +809 -0
  7. wafer_core/rollouts/_pytui/console.py +291 -0
  8. wafer_core/rollouts/_pytui/renderer.py +210 -0
  9. wafer_core/rollouts/_pytui/spinner.py +73 -0
  10. wafer_core/rollouts/_pytui/terminal.py +489 -0
  11. wafer_core/rollouts/_pytui/text.py +470 -0
  12. wafer_core/rollouts/_pytui/theme.py +241 -0
  13. wafer_core/rollouts/evaluation.py +142 -177
  14. wafer_core/rollouts/progress_app.py +395 -0
  15. wafer_core/rollouts/tui/DESIGN.md +251 -115
  16. wafer_core/rollouts/tui/monitor.py +64 -20
  17. wafer_core/tools/compile/__init__.py +30 -0
  18. wafer_core/tools/compile/compiler.py +314 -0
  19. wafer_core/tools/compile/modal_compile.py +359 -0
  20. wafer_core/tools/compile/tests/__init__.py +1 -0
  21. wafer_core/tools/compile/tests/test_compiler.py +675 -0
  22. wafer_core/tools/compile/tests/test_data/utils.cuh +10 -0
  23. wafer_core/tools/compile/tests/test_data/vector_add.cu +7 -0
  24. wafer_core/tools/compile/tests/test_data/with_header.cu +9 -0
  25. wafer_core/tools/compile/tests/test_modal_integration.py +326 -0
  26. wafer_core/tools/compile/types.py +117 -0
  27. {wafer_core-0.1.37.dist-info → wafer_core-0.1.39.dist-info}/METADATA +1 -1
  28. {wafer_core-0.1.37.dist-info → wafer_core-0.1.39.dist-info}/RECORD +29 -12
  29. wafer_core/rollouts/events.py +0 -240
  30. wafer_core/rollouts/progress_display.py +0 -476
  31. wafer_core/utils/event_streaming.py +0 -63
  32. {wafer_core-0.1.37.dist-info → wafer_core-0.1.39.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.stdout",
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
+ ]