fix(chat): bấm Dừng thì báo rõ là đã dừng, không dội lỗi Python
Một lượt chạy đã 159 giây, bấm Dừng, rồi hoặc không thấy gì đổi, hoặc nhận một dòng đỏ "'NoneType' object has no attribute 'read'". Cả hai đều không trả lời được câu người dùng đang hỏi: nó dừng chưa? Ba chỗ nói sai: - stop() chỉ đặt cờ huỷ rồi thôi. Huỷ thật mất vài giây (đang chờ gateway, đang chạy dở một tool), mà trong khoảng đó chỉ báo vẫn đếm "Đang chạy · 160s". Nay đổi nhãn sang "Đang dừng" ngay lúc bấm, và _sync_indicators giữ nhãn đó khi chuyển tab. - Huỷ giữa stream đóng socket, urllib3 ném AttributeError chứ không phải requests.RequestException, nên hai vòng đọc stream của provider bắt hụt và lỗi chui lên tận giao diện. Nay bắt rộng rồi lọc lại: chỉ nuốt khi thật sự đang huỷ, lỗi khác vẫn ném tiếp nguyên vẹn. - Lượt chạy kết thúc sau khi huỷ vẫn đặt dấu XANH "Đã hoàn thành" (hoặc bong bóng lỗi đỏ nếu kết thúc bằng ngoại lệ). Nay đặt "⏹ Đã dừng theo yêu cầu", và thanh trạng thái nói cùng một chuyện. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -99,6 +99,9 @@ class ChatPanel(ChatLiveTurnsMixin, ChatPanelLayoutMixin, ChatEventStreamMixin,
|
||||
# can run concurrently. Each value is a turn-context dict — see _start_turn.
|
||||
self.worker: AgentWorker | None = None
|
||||
self._active: Dict[AgentWorker, Dict[str, Any]] = {}
|
||||
# Người dùng đã bấm Dừng cho lượt đang chạy chưa. Cờ này chỉ đổi thứ
|
||||
# MÀN HÌNH nói, không đổi việc huỷ: huỷ vẫn là cờ trên worker.
|
||||
self._stop_requested = False
|
||||
self._turn_seq: int = 0
|
||||
# session_id -> its live messages list, for every conversation that still has
|
||||
# a turn running. Lets you start a new chat / reopen an old one WHILE work
|
||||
@@ -337,7 +340,10 @@ class ChatPanel(ChatLiveTurnsMixin, ChatPanelLayoutMixin, ChatEventStreamMixin,
|
||||
Switching chats, or hitting History → Refresh, shows whether THIS chat is
|
||||
still processing (a background turn) or idle."""
|
||||
if self._view_busy():
|
||||
self.thinking.start("chat.running") # this conversation is still working
|
||||
# Đã bấm Dừng mà lượt chưa kết thúc: giữ nhãn "đang dừng", đừng kéo
|
||||
# ngược về "đang chạy" — người dùng vừa bấm xong mà thấy chữ cũ thì
|
||||
# đọc ra là nút không ăn.
|
||||
self.thinking.start("chat.stopping" if self._stop_requested else "chat.running")
|
||||
else:
|
||||
self.thinking.stop()
|
||||
self.composer.set_running(bool(self._active)) # Stop shows while anything runs
|
||||
|
||||
@@ -217,6 +217,7 @@ class ChatTurnRunnerMixin:
|
||||
if self._view_busy() or len(self._active) >= self._max_parallel():
|
||||
self.composer.set_busy(True)
|
||||
self.status_message.emit(tr("chatpanel.working", name=tr(f"app.tab.{self.kind}")))
|
||||
self._stop_requested = False # lượt mới: xoá dấu vết lần Dừng trước
|
||||
self.thinking.start("chat.running")
|
||||
worker.start()
|
||||
|
||||
@@ -259,7 +260,7 @@ class ChatTurnRunnerMixin:
|
||||
self._show_usage(ctx) # per-turn + conversation token/cost
|
||||
except Exception: # noqa: BLE001 — usage display must never break a turn
|
||||
pass
|
||||
done = self.chat_view.add_success(tr("chat.done_marker")) # green done marker in the chat box
|
||||
done = self._dau_ket_thuc()
|
||||
folder = self.workspace_dir()
|
||||
if folder:
|
||||
done.add_folder_link(str(folder), tr("chat.open_output_folder"))
|
||||
@@ -273,12 +274,32 @@ class ChatTurnRunnerMixin:
|
||||
self._maybe_notify_teams(result)
|
||||
self._drain_queue()
|
||||
|
||||
def _dau_ket_thuc(self):
|
||||
"""Dấu kết thúc đặt vào khung chat khi một lượt vừa xong.
|
||||
|
||||
Dừng theo yêu cầu KHÔNG phải là hoàn thành: dấu xanh "Đã hoàn thành" ở
|
||||
đó nói ngược hẳn với thứ người dùng vừa làm, và là lý do người dùng báo
|
||||
"bấm Dừng mà không biết nó đã dừng hay chưa".
|
||||
|
||||
Tách khỏi ``_on_finished`` để nhánh này kiểm được bằng test mà không
|
||||
phải dựng cả một ``ChatPanel``.
|
||||
"""
|
||||
if self._stop_requested:
|
||||
return self.chat_view.add_status(tr("chat.stopped_marker"))
|
||||
return self.chat_view.add_success(tr("chat.done_marker"))
|
||||
|
||||
def _on_failed(self, ctx: Dict[str, Any], err: str) -> None:
|
||||
"""Lượt chạy lỗi: huỷ thư mục kết quả tạm và hiện lỗi (nếu hội thoại còn đang mở)."""
|
||||
live = self._turn_is_live(ctx)
|
||||
self._end_turn(ctx)
|
||||
self._cleanup_turn(ctx, False) # discard this turn's output sandbox
|
||||
if live:
|
||||
if live and self._stop_requested:
|
||||
# Người dùng vừa bấm Dừng: mọi lỗi phát sinh trong lúc huỷ là hệ quả
|
||||
# của chính việc huỷ (đóng socket giữa stream, tool bị cắt ngang).
|
||||
# Dội một traceback đỏ vào mặt họ là trả lời sai câu hỏi "nó dừng
|
||||
# chưa?" — thứ họ cần là một dòng nói rõ là đã dừng.
|
||||
self._dau_ket_thuc()
|
||||
elif live:
|
||||
self.chat_view.add_error(err)
|
||||
self.graph_event.emit(self.session_name, {"type": "error", "content": err})
|
||||
from ...providers.base import is_model_not_found_error
|
||||
@@ -291,7 +312,10 @@ class ChatTurnRunnerMixin:
|
||||
self.composer.set_text(ctx["display_text"])
|
||||
else:
|
||||
self._persist_session(ctx)
|
||||
self.status_message.emit(tr("chatpanel.failed", name=tr(f"app.tab.{self.kind}")))
|
||||
# Thanh trạng thái phải nói cùng một chuyện với khung chat: dừng theo
|
||||
# yêu cầu thì không phải "lỗi".
|
||||
key = "chatpanel.stopped" if self._stop_requested else "chatpanel.failed"
|
||||
self.status_message.emit(tr(key, name=tr(f"app.tab.{self.kind}")))
|
||||
self.turn_finished.emit({"error": err})
|
||||
self._drain_queue()
|
||||
|
||||
@@ -316,6 +340,11 @@ class ChatTurnRunnerMixin:
|
||||
"""Dừng mọi lượt đang chạy của hội thoại này và xoá sạch hàng đợi."""
|
||||
if not self._active:
|
||||
return
|
||||
# Báo NGAY trên màn: huỷ thật có thể mất vài giây (đang chờ gateway trả
|
||||
# lời, đang chạy dở một tool), mà trong lúc đó chỉ báo vẫn đếm "Đang
|
||||
# chạy · 160s" — người dùng đọc ra là nút Dừng không ăn.
|
||||
self._stop_requested = True
|
||||
self.thinking.set_label("chat.stopping")
|
||||
for w in list(self._active):
|
||||
if w.isRunning():
|
||||
w.request_stop()
|
||||
|
||||
Reference in New Issue
Block a user