From ed028dc00b7560b75558663327013869fbc8afa7 Mon Sep 17 00:00:00 2001 From: worldmozara Date: Tue, 14 Jul 2026 22:11:30 +0800 Subject: [PATCH] core/cli: update display and state for streaming, error, cancel events --- src/plyngent/cli/display.py | 10 ++++++++-- src/plyngent/cli/state.py | 5 +++-- tests/test_agent/test_loop.py | 8 ++++---- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/plyngent/cli/display.py b/src/plyngent/cli/display.py index d0aaaab..64a3c48 100644 --- a/src/plyngent/cli/display.py +++ b/src/plyngent/cli/display.py @@ -5,6 +5,8 @@ from typing import TYPE_CHECKING import click from plyngent.agent import ( + CancelledEvent, + ErrorEvent, MaxRoundsEvent, TextDeltaEvent, ToolCallEvent, @@ -20,7 +22,7 @@ if TYPE_CHECKING: _TOOL_RESULT_PREVIEW = 200 -async def render_events(events: AsyncIterator[AgentEvent]) -> None: +async def render_events(events: AsyncIterator[AgentEvent]) -> None: # noqa: C901, PLR0912 """Print agent events to the terminal.""" printed_text = False async for event in events: @@ -47,6 +49,10 @@ async def render_events(events: AsyncIterator[AgentEvent]) -> None: else content[:_TOOL_RESULT_PREVIEW] + "…" ) click.secho(f"[tool result] {preview}", fg="magenta") + elif isinstance(event, ErrorEvent): + click.secho(f"\n[error] {event.message}", fg="bright_red") + elif isinstance(event, CancelledEvent): + click.secho("\n[cancelled]", fg="yellow") elif isinstance(event, MaxRoundsEvent): if event.continued: click.secho( @@ -60,5 +66,5 @@ async def render_events(events: AsyncIterator[AgentEvent]) -> None: _ = event if printed_text: click.echo() - # Blank line before the next readline prompt so log and input are not jammed. click.echo() + diff --git a/src/plyngent/cli/state.py b/src/plyngent/cli/state.py index a47e752..4660edb 100644 --- a/src/plyngent/cli/state.py +++ b/src/plyngent/cli/state.py @@ -34,7 +34,7 @@ class ReplState: def __post_init__(self) -> None: # DeepSeek client uses a compatible but distinct param type; treat as ChatClient. - self.client = cast("ChatClient", create_client(self.provider)) + self.client = cast("ChatClient", cast("object", create_client(self.provider))) self.agent = self._make_agent() def _tool_registry(self) -> ToolRegistry | None: @@ -53,12 +53,13 @@ class ReplState: session_id=self.session_id, max_rounds=self.max_rounds, on_limit=prompt_continue_limit, + stream=True, ) def rebuild_client(self) -> None: """Recreate client and agent after provider/model/tools change.""" messages = list(self.agent.messages) - self.client = cast("ChatClient", create_client(self.provider)) + self.client = cast("ChatClient", cast("object", create_client(self.provider))) self.agent = self._make_agent() self.agent.messages = messages diff --git a/tests/test_agent/test_loop.py b/tests/test_agent/test_loop.py index 991cd7f..b89f8ff 100644 --- a/tests/test_agent/test_loop.py +++ b/tests/test_agent/test_loop.py @@ -96,10 +96,10 @@ async def test_run_chat_loop_text_only() -> None: ] ) messages: list[AnyChatMessage] = [UserChatMessage(content="hi")] - events = [e async for e in run_chat_loop(client, messages, model="m")] - assert isinstance(events[0], AssistantMessageEvent) - assert isinstance(events[1], TextDeltaEvent) - assert events[1].content == "hello" + events = [e async for e in run_chat_loop(client, messages, model="m", stream=False)] + assert isinstance(events[0], TextDeltaEvent) + assert events[0].content == "hello" + assert isinstance(events[1], AssistantMessageEvent) assert len(messages) == 2 # noqa: PLR2004 assert len(client.calls) == 1