WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit d302eb6

Browse files
authored
Enable error codes and TUI render in Context Panel (#445)
* Enable error codes and TUI render in Context Panel * Optimize error code for less repeat logic
1 parent 0dac716 commit d302eb6

File tree

9 files changed

+1445
-59
lines changed

9 files changed

+1445
-59
lines changed

deepfabric/error_codes.py

Lines changed: 581 additions & 0 deletions
Large diffs are not rendered by default.

deepfabric/generator.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
INTERRUPTED_DATASET_FILENAME,
2222
)
2323
from .dataset import Dataset
24+
from .error_codes import classify_error
2425
from .exceptions import DataSetGeneratorError
2526
from .llm import LLMClient
2627
from .metrics import trace
@@ -422,7 +423,7 @@ async def _generate(
422423
tasks.append(asyncio.create_task(_generate(prompt, start_sample_idx + idx, path_info)))
423424
results = await asyncio.gather(*tasks)
424425

425-
for success, payload in results:
426+
for idx, (success, payload) in enumerate(results):
426427
if success:
427428
samples.append(payload)
428429
else:
@@ -434,6 +435,18 @@ async def _generate(
434435
)
435436
self.failure_analysis[failure_type].append(error_msg)
436437

438+
# Classify and emit error to progress reporter
439+
classified = classify_error(
440+
error if isinstance(error, Exception) else str(error),
441+
provider=self.provider,
442+
context={"error_type": failure_type},
443+
)
444+
if self.progress_reporter:
445+
self.progress_reporter.emit_error(
446+
classified,
447+
sample_idx=start_sample_idx + idx,
448+
)
449+
437450
return samples, failed_responses
438451

439452
def analyze_failure(self, response_content: str, error: Exception | None = None) -> str:

deepfabric/progress.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
metrics, etc.) to react to progress events.
99
"""
1010

11-
from typing import Any, Protocol
11+
from typing import TYPE_CHECKING, Any, Protocol
12+
13+
if TYPE_CHECKING:
14+
from .error_codes import ClassifiedError
1215

1316

1417
class StreamObserver(Protocol):
@@ -50,6 +53,15 @@ def on_step_complete(self, step_name: str, metadata: dict[str, Any]) -> None:
5053
"""
5154
...
5255

56+
def on_error(self, error: "ClassifiedError", metadata: dict[str, Any]) -> None:
57+
"""Called when an error occurs during generation.
58+
59+
Args:
60+
error: ClassifiedError with error code and details
61+
metadata: Additional context (sample_idx, step, etc.)
62+
"""
63+
...
64+
5365

5466
class ProgressReporter:
5567
"""Central progress reporter that notifies observers of generation events.
@@ -118,6 +130,17 @@ def emit_step_complete(self, step_name: str, **metadata) -> None:
118130
for observer in self._observers:
119131
observer.on_step_complete(step_name, metadata)
120132

133+
def emit_error(self, error: "ClassifiedError", **metadata) -> None:
134+
"""Emit an error event to all observers.
135+
136+
Args:
137+
error: ClassifiedError with error code and details
138+
**metadata: Additional context as keyword arguments
139+
"""
140+
for observer in self._observers:
141+
if hasattr(observer, "on_error"):
142+
observer.on_error(error, metadata)
143+
121144

122145
# Convenience context manager for tracking steps
123146
class ProgressStep:

deepfabric/tui.py

Lines changed: 98 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from collections import deque
66
from dataclasses import dataclass
77
from time import monotonic
8-
from typing import Any
8+
from typing import TYPE_CHECKING, Any
99

1010
from rich.console import Console, RenderableType
1111
from rich.layout import Layout
@@ -24,6 +24,67 @@
2424

2525
from .progress import StreamObserver
2626

27+
if TYPE_CHECKING:
28+
from .error_codes import ClassifiedError
29+
30+
31+
class TopicBuildingMixin:
32+
"""Mixin providing shared functionality for Tree and Graph building TUIs.
33+
34+
Provides common implementations for:
35+
- _refresh_left(): Update events panel in left column
36+
- on_error(): Handle error events from progress reporter
37+
- on_step_start()/on_step_complete(): No-op handlers for step events
38+
- update_status_panel(): Update status panel (requires _status_panel() in subclass)
39+
40+
Subclasses must have these attributes:
41+
- tui: DeepFabricTUI instance
42+
- live_layout: Layout | None
43+
- events_log: deque
44+
"""
45+
46+
tui: "DeepFabricTUI"
47+
live_layout: "Layout | None"
48+
events_log: "deque"
49+
50+
def _refresh_left(self) -> None:
51+
"""Update events panel in left column."""
52+
if self.live_layout is not None:
53+
try:
54+
self.live_layout["main"]["left"]["events"].update(
55+
self.tui.build_events_panel(list(self.events_log))
56+
)
57+
except Exception:
58+
return
59+
60+
def on_error(self, error: "ClassifiedError", metadata: dict[str, Any]) -> None: # noqa: ARG002
61+
"""Handle error events - log to events panel."""
62+
error_event = error.to_event()
63+
self.events_log.append(f"X {error_event}")
64+
self._refresh_left()
65+
66+
def on_step_start(self, step_name: str, metadata: dict[str, Any]) -> None: # noqa: ARG002
67+
"""Handle step start - topic building doesn't need specific handling."""
68+
pass
69+
70+
def on_step_complete(self, step_name: str, metadata: dict[str, Any]) -> None: # noqa: ARG002
71+
"""Handle step complete - topic building doesn't need specific handling."""
72+
pass
73+
74+
def update_status_panel(self) -> None:
75+
"""Update the status panel in the right column."""
76+
if self.live_layout is None:
77+
return
78+
try:
79+
self.live_layout["main"]["right"]["status"].update(self._status_panel())
80+
except Exception:
81+
return
82+
83+
def _status_panel(self) -> Panel:
84+
"""Create status panel - must be implemented by subclass."""
85+
raise NotImplementedError
86+
87+
2788
# Constants
2889
STREAM_BUFFER_DISPLAY_THRESHOLD = 1000 # Show ellipsis if accumulated text exceeds this
2990
STREAM_TEXT_MAX_LENGTH = 8000 # Max characters to display in streaming text
@@ -104,7 +165,19 @@ def build_events_panel(self, events: list[str], title: str = "Events") -> Panel:
104165
text = Text("Waiting...", style="dim")
105166
else:
106167
# Keep events short; show newest at bottom
107-
text = Text("\n".join(events[-EVENT_LOG_MAX_LINES:]))
168+
# Colorize based on prefix: X = red (error), checkmark = green (success)
169+
text = Text()
170+
for i, event in enumerate(events[-EVENT_LOG_MAX_LINES:]):
171+
if i > 0:
172+
text.append("\n")
173+
if event.startswith("X "):
174+
text.append("X ", style="bold red")
175+
text.append(event[2:])
176+
elif event.startswith("✓ ") or event.startswith("✔ "):
177+
text.append(event[0] + " ", style="bold green")
178+
text.append(event[2:])
179+
else:
180+
text.append(event)
108181
return Panel(text, title=title, border_style="dim", padding=(0, 1))
109182

110183
def create_footer(self, layout: Layout, title: str = "Run Status") -> Progress:
@@ -183,7 +256,7 @@ def info(self, message: str) -> None:
183256
self.console.print(f" {message}", style="blue")
184257

185258

186-
class TreeBuildingTUI(StreamObserver):
259+
class TreeBuildingTUI(TopicBuildingMixin, StreamObserver):
187260
"""TUI for tree building operations with simplified progress and streaming."""
188261

189262
def __init__(self, tui: DeepFabricTUI):
@@ -355,24 +428,6 @@ def _refresh_context(self) -> None:
355428
except Exception:
356429
return
357430

358-
def _refresh_left(self) -> None:
359-
if self.live_layout is not None:
360-
try:
361-
# Update events panel in left column
362-
self.live_layout["main"]["left"]["events"].update(
363-
self.tui.build_events_panel(list(self.events_log))
364-
)
365-
except Exception:
366-
return
367-
368-
def on_step_start(self, step_name: str, metadata: dict[str, Any]) -> None:
369-
"""Handle step start - tree building doesn't need specific handling."""
370-
pass
371-
372-
def on_step_complete(self, step_name: str, metadata: dict[str, Any]) -> None:
373-
"""Handle step complete - tree building doesn't need specific handling."""
374-
pass
375-
376431
def finish_building(self, total_paths: int, failed_generations: int) -> None:
377432
"""Finish the tree building process."""
378433
if self.live_display:
@@ -400,16 +455,8 @@ def _status_panel(self) -> Panel:
400455
table.add_row("Failed:", str(self.failed_attempts))
401456
return Panel(table, title="Status", border_style="dim", padding=(0, 1))
402457

403-
def update_status_panel(self) -> None:
404-
if self.live_layout is None:
405-
return
406-
try:
407-
self.live_layout["main"]["right"]["status"].update(self._status_panel())
408-
except Exception:
409-
return
410458

411-
412-
class GraphBuildingTUI(StreamObserver):
459+
class GraphBuildingTUI(TopicBuildingMixin, StreamObserver):
413460
"""TUI for graph building operations with simplified progress and streaming."""
414461

415462
def __init__(self, tui: DeepFabricTUI):
@@ -576,23 +623,6 @@ def _refresh_context(self) -> None:
576623
except Exception:
577624
return
578625

579-
def _refresh_left(self) -> None:
580-
if self.live_layout is not None:
581-
try:
582-
self.live_layout["main"]["left"]["events"].update(
583-
self.tui.build_events_panel(list(self.events_log))
584-
)
585-
except Exception:
586-
return
587-
588-
def on_step_start(self, step_name: str, metadata: dict[str, Any]) -> None:
589-
"""Handle step start - graph building doesn't need specific handling."""
590-
pass
591-
592-
def on_step_complete(self, step_name: str, metadata: dict[str, Any]) -> None:
593-
"""Handle step complete - graph building doesn't need specific handling."""
594-
pass
595-
596626
def finish_building(self, failed_generations: int) -> None:
597627
"""Finish the graph building process."""
598628
if self.live_display:
@@ -629,14 +659,6 @@ def _status_panel(self) -> Panel:
629659
table.add_row("Failed:", str(self.failed_attempts))
630660
return Panel(table, title="Status", border_style="dim", padding=(0, 1))
631661

632-
def update_status_panel(self) -> None:
633-
if self.live_layout is None:
634-
return
635-
try:
636-
self.live_layout["main"]["right"]["status"].update(self._status_panel())
637-
except Exception:
638-
return
639-
640662

641663
class DatasetGenerationTUI(StreamObserver):
642664
"""Enhanced TUI for dataset generation with rich integration and streaming display."""
@@ -945,6 +967,27 @@ def log_event(self, message: str) -> None:
945967
self.tui.build_events_panel(list(self.events_log))
946968
)
947969

970+
def on_error(self, error: "ClassifiedError", metadata: dict[str, Any]) -> None:
971+
"""Handle error events from the progress reporter.
972+
973+
Displays concise error information in the Events panel using
974+
standardized DeepFabric error codes.
975+
976+
Args:
977+
error: ClassifiedError with error code and details
978+
metadata: Additional context (sample_idx, etc.)
979+
"""
980+
# Format concise error message for Events panel
981+
error_event = error.to_event()
982+
983+
# Add sample context if available
984+
sample_idx = metadata.get("sample_idx")
985+
if sample_idx is not None:
986+
error_event = f"[{sample_idx}] {error_event}"
987+
988+
# Log to events panel with error indicator
989+
self.log_event(f"X {error_event}")
990+
948991

949992
# Global TUI instances
950993
_tui_instance = None

0 commit comments

Comments
 (0)