Add status bar and compact summary widgets with context window resolu… (#152)

* Add status bar and compact summary widgets with context window resolution

- Implemented a shared status bar for CLI and TUI frontends, including helpers for managing session metrics and context windows.
- Created a `CompactSummaryWidget` for displaying manual summaries in a collapsible format.
- Introduced a `CompactingWidget` to indicate ongoing compacting processes.
- Added a base class `TimedStatusWidget` for widgets that require a timer.
- Developed context window resolution helpers to retrieve context window sizes from various model attributes.
- Enhanced tests for context window resolution and status bar functionalities, ensuring accurate behavior across different scenarios.
- Updated existing tests to cover new features and maintain code quality.

* refactor(Channel): simplify lambda function in _send_with_retry method

* feat: enhance context editing logic and improve error handling in StreamState

* refactor(Channel): streamline lambda function in _send_with_retry method

* feat: rename auto-approve option to auto-mode for unattended execution; update checkpoint queries to filter by agent name; improve compatibility validation logic

* feat: rename auto-approve option to auto-mode; update related logic and tests for improved unattended execution

* fix: correct formatting of console message for MCP server configuration status

* feat: add check for None summary_message in _apply_summarization_event to prevent errors

* feat: enhance _load_checkpoint_messages to validate message format and apply summarization event
This commit is contained in:
Xi Zhang
2026-04-12 18:47:37 +02:00
committed by GitHub
parent ff15f515cc
commit 65db3a4fcd
55 changed files with 3353 additions and 578 deletions
+81
View File
@@ -25,6 +25,11 @@ class TestSummarizationEmitter:
ev = StreamEventEmitter.summarization("")
assert ev.data["content"] == ""
def test_start_event(self):
ev = StreamEventEmitter.summarization_start()
assert ev.type == "summarization_start"
assert ev.data["type"] == "summarization_start"
# ---------------------------------------------------------------------------
# StreamState
@@ -37,12 +42,20 @@ class TestSummarizationState:
def test_initial_state(self):
state = StreamState()
assert state.summarization_text == ""
assert state.is_summarizing is False
def test_handle_summarization_start(self):
state = StreamState()
etype = state.handle_event({"type": "summarization_start"})
assert etype == "summarization_start"
assert state.is_summarizing is True
def test_handle_summarization(self):
state = StreamState()
etype = state.handle_event({"type": "summarization", "content": "summary"})
assert etype == "summarization"
assert state.summarization_text == "summary"
assert state.is_summarizing is True
def test_accumulates_chunks(self):
"""Summarization chunks are accumulated (streaming)."""
@@ -57,6 +70,7 @@ class TestSummarizationState:
args = state.get_display_args()
assert "summarization_text" in args
assert args["summarization_text"] == "ctx"
assert args["is_summarizing"] is True
def test_does_not_affect_thinking(self):
state = StreamState()
@@ -72,6 +86,13 @@ class TestSummarizationState:
assert state.response_text == "hello"
assert state.summarization_text == "sum"
def test_text_ends_summarizing_state(self):
state = StreamState()
state.handle_event({"type": "summarization_start"})
state.handle_event({"type": "summarization", "content": "sum"})
state.handle_event({"type": "text", "content": "hello"})
assert state.is_summarizing is False
# ---------------------------------------------------------------------------
# Rich CLI display
@@ -110,6 +131,28 @@ class TestSummarizationRichDisplay:
rendered = _render_group(group)
assert "Context Summarized" in rendered
def test_panel_rendered_while_summarizing(self):
from EvoScientist.stream.display import create_streaming_display
group = create_streaming_display(
summarization_text="The conversation was about ML.",
is_summarizing=True,
response_text="ok",
)
rendered = _render_group(group)
assert "Context Summarizing..." in rendered
def test_panel_rendered_start_placeholder(self):
from EvoScientist.stream.display import create_streaming_display
group = create_streaming_display(
summarization_text="",
is_summarizing=True,
response_text="ok",
)
rendered = _render_group(group)
assert "Context Summarizing..." in rendered
def test_long_text_truncated(self):
from EvoScientist.stream.display import create_streaming_display
@@ -136,6 +179,8 @@ class TestSummarizationWidget:
w = SummarizationWidget()
assert w._collapsed is True
assert w._content == ""
assert w._elapsed == 0.0
assert w._timer_handle is None
def test_set_content(self):
from EvoScientist.cli.widgets.summarization_widget import SummarizationWidget
@@ -170,11 +215,22 @@ class TestSummarizationWidget:
def test_finalize(self):
from EvoScientist.cli.widgets.summarization_widget import SummarizationWidget
class _Timer:
def __init__(self) -> None:
self.stopped = False
def stop(self) -> None:
self.stopped = True
w = SummarizationWidget()
w.append_text("some summary")
timer = _Timer()
w._timer_handle = timer
w.finalize()
assert w._is_active is False
assert w._collapsed is True
assert timer.stopped is True
assert w._timer_handle is None
def test_toggle_collapsed(self):
from EvoScientist.cli.widgets.summarization_widget import SummarizationWidget
@@ -187,6 +243,31 @@ class TestSummarizationWidget:
assert w._collapsed is True
class TestCompactSummaryWidget:
"""CompactSummaryWidget (manual /compact result panel)."""
def test_init_collapsed(self):
from EvoScientist.cli.widgets.compact_summary_widget import CompactSummaryWidget
w = CompactSummaryWidget("summary content")
assert w._collapsed is True
assert w._content == "summary content"
def test_char_count_label(self):
from EvoScientist.cli.widgets.compact_summary_widget import CompactSummaryWidget
w = CompactSummaryWidget("hello")
assert w._char_count_label() == "5 chars"
def test_toggle_collapsed(self):
from EvoScientist.cli.widgets.compact_summary_widget import CompactSummaryWidget
w = CompactSummaryWidget("hello world")
assert w._collapsed is True
w._collapsed = not w._collapsed
assert w._collapsed is False
# ---------------------------------------------------------------------------
# _extract_summarization_text helper
# ---------------------------------------------------------------------------