refactor: vá 4 hồi quy, tách 4 file chạm trần LOC, docstring lên 100%
Hồi quy đã vá
-------------
F-12 Kéo–thả hoặc dán tệp vào ô chat ném NameError. R08 tách `_Input` sang
`chat_input_box.py` nhưng để `_paths_from_mime()` ở lại
`composer_widget.py`, nên hai hàm sự kiện Qt gọi một cái tên không tồn
tại. Bốn hàm dùng chung chuyển sang `composer_mime.py` — module thứ ba
là chỗ duy nhất không lặp lại được lỗi này. Đo lại: cả thả lẫn dán đều
gắn 1 tệp, khớp bản trước refactor.
F-01 Đổi provider thì bộ chọn model AI-Edit không làm gì. Hook cũ kiểm
`folder.ai_model_combo`, thuộc tính R08-T12 đã dời sang
`ai_panel.resolver`. Làm mới vô điều kiện, đúng như tab cũ: lần lấy đầu
tiên hỏng thì đổi provider chính là lúc phải thử lại.
F-07 Hàng chọn kỳ của Dashboard bị đẩy xuống dưới các thẻ số liệu. Hàng này
lọc CẢ BA thẻ con chứ không riêng biểu đồ, nên để nó nằm dưới là bắt
người dùng đọc con số trước khi thấy con số đó tính cho kỳ nào. Kèm
theo: `TokenUsageCardWidget` bị bỏ sót `setContentsMargins(0,0,0,0)`
mà hai thẻ con còn lại đã có, đẩy cả hàng thẻ lệch 9px.
`check_layout_geometry` nay khớp TỪNG BYTE với bản trước refactor.
F-11 Hai lớp khai trùng tên phương thức; Python giữ bản sau nên bản đầu là
mã chết. `co4e_tab.py::showEvent` bản đầu gọi `_narrow_guard.attach()`
và không bao giờ chạy.
Tách file (F-09)
----------------
Bốn file chạm trần 400 dòng, mỗi lần cắt ra một trách nhiệm thật:
graph_renderer.py -> graph_scene_builder.py + graph_export.py
co4e_workflow_service.py -> co4e_run_history.py
json_config_repository.py -> config_sections.py
agents_admin_tab.py -> shared/agent_kind_visuals.py
File cuối còn xoá 3 bản sao của hàm đã có trong `shared/formatters.py`,
giống hệt đến từng dòng — nay định dạng thời gian và avatar không lệch nhau
giữa các bảng Giám sát nữa.
Docstring
---------
41,6% -> 100% (3.478/3.478 định nghĩa production), kể cả module dormant và
phương thức dunder. Toàn bộ phần bổ sung viết bằng tiếng Việt; comment tiếng
Anh có sẵn giữ nguyên — dịch ngược là một đợt riêng.
Seam chưa nối dây (F-05)
------------------------
9 seam mang nhãn `SEAM · dựng <ngày>` kèm hai câu: được nối khi nào, và để
dormant thì hỏng gì. Ngày lấy từ lịch sử git, không phải hạn tự đặt. Gate O
đọc nhãn đó và nhắc khi quá 30 ngày.
859 test xanh · 4/4 cổng CASAN · 19/24 checker khớp từng byte bản cũ.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,7 +19,15 @@ _FOLDER_LBL_MAX_CHARS = 42 # keep the composer's bottom row from crowding out A
|
||||
|
||||
|
||||
class CoworkTab(ChatPanel):
|
||||
"""Màn Cowork: khung chat chính, gắn với một project và một thư mục kết quả.
|
||||
|
||||
Khác :class:`ChatPanel` gốc ở chỗ kết quả được gom theo TỪNG hội thoại và
|
||||
chỉ hiện ra sau khi lượt chạy thành công — xem :meth:`register_output`.
|
||||
"""
|
||||
def __init__(self, ctx: AppContext):
|
||||
"""Màn Cowork — một ``ChatPanel`` kèm thanh công cụ riêng hiện provider, model và
|
||||
thư mục kết quả đang dùng.
|
||||
"""
|
||||
super().__init__(ctx, "cowork", "Cowork", placeholder_key="composer.placeholder_cowork")
|
||||
|
||||
self._title_lbl = QLabel()
|
||||
@@ -95,6 +103,7 @@ class CoworkTab(ChatPanel):
|
||||
lbl.setText(getattr(self, "title", "") or tr("cowork.title"))
|
||||
|
||||
def _retranslate(self) -> None:
|
||||
"""Áp lại chữ theo ngôn ngữ đang chọn cho tiêu đề và các nút trên thanh công cụ."""
|
||||
self.refresh_title()
|
||||
self.skills_btn.setText(tr("cowork.skills_btn"))
|
||||
self.skills_btn.setToolTip(tr("cowork.skills_tooltip"))
|
||||
@@ -105,6 +114,7 @@ class CoworkTab(ChatPanel):
|
||||
|
||||
# ---- output folder --------------------------------------------------
|
||||
def _pick_output_folder(self) -> None:
|
||||
"""Chọn thư mục kết quả cho hội thoại này."""
|
||||
start = str(self._session_output_dir())
|
||||
chosen = QFileDialog.getExistingDirectory(self, tr("cowork.pick_folder_title"), start)
|
||||
if not chosen:
|
||||
@@ -126,6 +136,11 @@ class CoworkTab(ChatPanel):
|
||||
self._refresh_outputs_from_disk()
|
||||
|
||||
def _apply_output_folder_label(self) -> None:
|
||||
"""Cập nhật nhãn cạnh nút chọn thư mục.
|
||||
|
||||
Chỉ hiện TÊN thư mục; đường dẫn đầy đủ để trong tooltip — bày cả đường dẫn
|
||||
ra ngoài chỉ làm rối một giá trị mà người dùng vừa tự chọn.
|
||||
"""
|
||||
full = str(self._session_output_dir())
|
||||
# Only the folder NAME is shown beside the button — the full path
|
||||
# (still available on hover) reads as noisy clutter for a value the
|
||||
@@ -150,6 +165,7 @@ class CoworkTab(ChatPanel):
|
||||
|
||||
# ---- project (workspace) --------------------------------------------
|
||||
def _project(self):
|
||||
"""Project đang gắn với hội thoại này; ``None`` nếu không tìm thấy."""
|
||||
from ..core.projects import load_project
|
||||
|
||||
return load_project(self.project_id)
|
||||
@@ -187,6 +203,7 @@ class CoworkTab(ChatPanel):
|
||||
".sh", ".bat", ".ps1", ".rb", ".pl"}
|
||||
|
||||
def assistant_title(self) -> str:
|
||||
"""Nhãn hiện trên bong bóng trả lời của màn Cowork."""
|
||||
return tr("cowork.assistant_title")
|
||||
|
||||
def _session_output_dir(self):
|
||||
@@ -222,6 +239,7 @@ class CoworkTab(ChatPanel):
|
||||
return self.ctx.config.cowork_output_dir() / self.session_id
|
||||
|
||||
def workspace_dir(self):
|
||||
"""Thư mục agent được phép đọc/ghi trong hội thoại này."""
|
||||
return self._session_output_dir()
|
||||
|
||||
def _turn_output_dir(self, turn_id: str):
|
||||
@@ -234,6 +252,11 @@ class CoworkTab(ChatPanel):
|
||||
return self._session_output_dir() / ".turns" / turn_id
|
||||
|
||||
def _is_intermediate_output(self, path: str) -> bool:
|
||||
"""Tệp này có phải file trung gian (do bước dựng sinh ra) không.
|
||||
|
||||
File trung gian không hiện lên ô "Tệp đầu ra" — người dùng chỉ quan tâm
|
||||
sản phẩm cuối.
|
||||
"""
|
||||
from pathlib import Path
|
||||
return Path(path).suffix.lower() in self._INTERMEDIATE_EXTS
|
||||
|
||||
@@ -253,6 +276,12 @@ class CoworkTab(ChatPanel):
|
||||
# file it made earlier in this same conversation — e.g. "sửa lại tiêu đề
|
||||
# trong file báo cáo vừa tạo" — since it can read_file/edit_file/write_file
|
||||
# it directly by the exact name listed here.
|
||||
"""Ghi chú đính kèm vào prompt: danh sách tệp hội thoại này đã tạo.
|
||||
|
||||
Nhờ đó agent (và người dùng, không phải tải lên lại) tham chiếu và sửa được
|
||||
tệp nó vừa tạo ở lượt trước — ví dụ "sửa lại tiêu đề trong file báo cáo vừa
|
||||
tạo" — vì nó đọc/ghi được tệp đó theo đúng tên liệt kê ở đây.
|
||||
"""
|
||||
names = self._existing_output_names()
|
||||
if not names:
|
||||
return ""
|
||||
@@ -308,9 +337,13 @@ class CoworkTab(ChatPanel):
|
||||
def register_output(self, path: str) -> None:
|
||||
# Suppress live/intermediate updates during a run — the Output box is
|
||||
# rebuilt from the surviving files once the turn succeeds (see below).
|
||||
"""Không làm gì: ô "Tệp đầu ra" của Cowork được dựng lại từ các tệp còn sống
|
||||
sau khi lượt chạy THÀNH CÔNG, chứ không cập nhật từng tệp giữa chừng.
|
||||
"""
|
||||
return
|
||||
|
||||
def on_file_written(self, path: str) -> None:
|
||||
"""Không làm gì: Cowork không cập nhật danh sách tệp theo thời gian thực."""
|
||||
return # no live file updates in Cowork
|
||||
|
||||
def _refresh_outputs_from_disk(self) -> None:
|
||||
@@ -329,17 +362,22 @@ class CoworkTab(ChatPanel):
|
||||
|
||||
def new_session(self) -> None:
|
||||
# The new thread stays in the CURRENT project (Claude-style).
|
||||
"""Mở hội thoại mới trong ĐÚNG project đang chọn, rồi trỏ lại thư mục kết quả."""
|
||||
super().new_session()
|
||||
# Refresh the project/folder labels + watch the new session's folder.
|
||||
self._apply_output_folder_label()
|
||||
|
||||
def load_conversation(self, conv) -> None:
|
||||
"""Mở lại một hội thoại cũ: khôi phục project của nó, dựng lại danh sách tệp
|
||||
đầu ra từ đĩa và trỏ lại nhãn thư mục.
|
||||
"""
|
||||
super().load_conversation(conv) # restores this conversation's project_id
|
||||
self._refresh_outputs_from_disk() # show this session's deliverables from disk
|
||||
# Refresh the project/folder labels + watch this conversation's folder.
|
||||
self._apply_output_folder_label()
|
||||
|
||||
def refresh_header(self) -> None:
|
||||
"""Cập nhật dòng "provider · model" trên thanh công cụ Cowork."""
|
||||
cfg = self.ctx.config
|
||||
label = PROVIDER_LABELS.get(cfg.active_provider, cfg.active_provider)
|
||||
self.model_lbl.setText(f"{label} · {cfg.model_label()}")
|
||||
@@ -377,6 +415,11 @@ class CoworkTab(ChatPanel):
|
||||
model = self._routed_model or self._model or ""
|
||||
|
||||
def job(worker: AgentWorker):
|
||||
"""Chạy nền một lượt Cowork qua ``ConversationApplicationService`` (R04-T04).
|
||||
|
||||
Sự kiện của service được dịch ngược về khuôn dict cũ để giao diện hiện tại
|
||||
không phải sửa.
|
||||
"""
|
||||
from ..application.conversations.core_runtime_adapter import (
|
||||
build_cowork_conversation_service,
|
||||
legacy_event_sink,
|
||||
|
||||
Reference in New Issue
Block a user