fix(versioning): 修复浅备份文件修改归属错误及列表/详情不一致
This commit is contained in:
parent
765196ea3c
commit
6a2cb820bc
@ -636,9 +636,12 @@ class MainTerminalToolsExecutionMixin:
|
|||||||
def _track_shallow_versioning(self, file_path: Any) -> None:
|
def _track_shallow_versioning(self, file_path: Any) -> None:
|
||||||
"""Track a file edit for shallow versioning (conversation-scoped undo)."""
|
"""Track a file edit for shallow versioning (conversation-scoped undo)."""
|
||||||
conversation_id = getattr(self.context_manager, "current_conversation_id", None)
|
conversation_id = getattr(self.context_manager, "current_conversation_id", None)
|
||||||
|
# 优先使用当前用户消息的 message_id,让文件修改归属到正确的输入节点。
|
||||||
|
message_id = getattr(self.context_manager, "current_shallow_message_id", None) or conversation_id
|
||||||
from utils.perf_log import perf_log
|
from utils.perf_log import perf_log
|
||||||
perf_log("_track_shallow_versioning called", extra={
|
perf_log("_track_shallow_versioning called", extra={
|
||||||
"conversation_id": conversation_id,
|
"conversation_id": conversation_id,
|
||||||
|
"message_id": message_id,
|
||||||
"project_path": str(self.project_path),
|
"project_path": str(self.project_path),
|
||||||
"data_dir": str(self.data_dir),
|
"data_dir": str(self.data_dir),
|
||||||
"file_path": str(file_path),
|
"file_path": str(file_path),
|
||||||
@ -652,9 +655,10 @@ class MainTerminalToolsExecutionMixin:
|
|||||||
data_dir=self.data_dir,
|
data_dir=self.data_dir,
|
||||||
conversation_id=conversation_id,
|
conversation_id=conversation_id,
|
||||||
)
|
)
|
||||||
manager.track_edit(file_path, conversation_id)
|
manager.track_edit(file_path, message_id)
|
||||||
perf_log("_track_shallow_versioning done", extra={
|
perf_log("_track_shallow_versioning done", extra={
|
||||||
"conversation_id": conversation_id,
|
"conversation_id": conversation_id,
|
||||||
|
"message_id": message_id,
|
||||||
"file_path": str(file_path),
|
"file_path": str(file_path),
|
||||||
"tracked_files_count": len(getattr(manager, "_tracked_files", set())),
|
"tracked_files_count": len(getattr(manager, "_tracked_files", set())),
|
||||||
})
|
})
|
||||||
|
|||||||
@ -189,6 +189,14 @@ class ShallowVersioningManager:
|
|||||||
# ----------------------------
|
# ----------------------------
|
||||||
# public API
|
# public API
|
||||||
# ----------------------------
|
# ----------------------------
|
||||||
|
def _find_latest_snapshot_by_message_id(self, message_id: str) -> Optional[ShallowSnapshot]:
|
||||||
|
"""Return the most recent snapshot whose message_id matches."""
|
||||||
|
target = str(message_id or "")
|
||||||
|
for snapshot in reversed(self._snapshots):
|
||||||
|
if str(snapshot.message_id or "") == target:
|
||||||
|
return snapshot
|
||||||
|
return None
|
||||||
|
|
||||||
def track_edit(self, file_path: Path | str, message_id: str) -> None:
|
def track_edit(self, file_path: Path | str, message_id: str) -> None:
|
||||||
"""Track a file before it is edited; backup its current content if not already tracked."""
|
"""Track a file before it is edited; backup its current content if not already tracked."""
|
||||||
tracking_path = self._normalize_path(file_path)
|
tracking_path = self._normalize_path(file_path)
|
||||||
@ -206,12 +214,13 @@ class ShallowVersioningManager:
|
|||||||
backup = self._create_backup(resolved, 1)
|
backup = self._create_backup(resolved, 1)
|
||||||
self._tracked_files.add(tracking_path)
|
self._tracked_files.add(tracking_path)
|
||||||
|
|
||||||
# Add to the most recent snapshot if one exists, otherwise create a pre-edit snapshot.
|
# 归属到当前 message_id 对应的最新 snapshot;不存在则新建。
|
||||||
|
# 避免把本次输入的修改追加到上一个输入的 snapshot。
|
||||||
now = datetime.now().isoformat()
|
now = datetime.now().isoformat()
|
||||||
if self._snapshots:
|
target_snapshot = self._find_latest_snapshot_by_message_id(message_id)
|
||||||
latest = self._snapshots[-1]
|
if target_snapshot is not None:
|
||||||
latest.tracked_file_backups[tracking_path] = backup
|
target_snapshot.tracked_file_backups[tracking_path] = backup
|
||||||
self._persist_snapshot(latest)
|
self._persist_snapshot(target_snapshot)
|
||||||
else:
|
else:
|
||||||
snapshot = ShallowSnapshot(
|
snapshot = ShallowSnapshot(
|
||||||
message_id=message_id,
|
message_id=message_id,
|
||||||
@ -256,8 +265,17 @@ class ShallowVersioningManager:
|
|||||||
tracked_file_backups=tracked_file_backups,
|
tracked_file_backups=tracked_file_backups,
|
||||||
timestamp=now,
|
timestamp=now,
|
||||||
)
|
)
|
||||||
self._snapshots.append(snapshot)
|
# 如果当前 message_id 已存在 snapshot(由 track_edit 预创建),则替换它,
|
||||||
self._snapshot_sequence += 1
|
# 避免同一个输入节点留下多个 snapshot 导致 diff 只计算最后两次之间的变化。
|
||||||
|
existing_index = -1
|
||||||
|
for idx, s in enumerate(self._snapshots):
|
||||||
|
if str(s.message_id or "") == str(message_id or ""):
|
||||||
|
existing_index = idx
|
||||||
|
if existing_index >= 0:
|
||||||
|
self._snapshots[existing_index] = snapshot
|
||||||
|
else:
|
||||||
|
self._snapshots.append(snapshot)
|
||||||
|
self._snapshot_sequence += 1
|
||||||
self._persist_snapshot(snapshot)
|
self._persist_snapshot(snapshot)
|
||||||
|
|
||||||
# Evict old snapshots while keeping the most recent MAX_SNAPSHOTS.
|
# Evict old snapshots while keeping the most recent MAX_SNAPSHOTS.
|
||||||
|
|||||||
@ -431,6 +431,14 @@ def _record_hidden_versioning_checkpoint_after_run(
|
|||||||
debug_log(f"[Versioning] 记录快照失败: {exc}")
|
debug_log(f"[Versioning] 记录快照失败: {exc}")
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
debug_log(f"[Versioning] 记录快照异常: {exc}")
|
debug_log(f"[Versioning] 记录快照异常: {exc}")
|
||||||
|
finally:
|
||||||
|
# 当前输入处理结束,清除浅备份消息 ID,避免后续无消息上下文时的错误归属。
|
||||||
|
try:
|
||||||
|
ctx = getattr(web_terminal, "context_manager", None)
|
||||||
|
if ctx is not None and hasattr(ctx, "current_shallow_message_id"):
|
||||||
|
ctx.current_shallow_message_id = None
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _persist_and_echo_preceding_notice(
|
def _persist_and_echo_preceding_notice(
|
||||||
@ -1010,6 +1018,13 @@ async def handle_task_with_sender(
|
|||||||
videos=videos,
|
videos=videos,
|
||||||
metadata=user_message_metadata
|
metadata=user_message_metadata
|
||||||
)
|
)
|
||||||
|
# 为浅备份 track_edit 提供当前用户消息 ID,避免文件修改被归到上一个输入。
|
||||||
|
try:
|
||||||
|
web_terminal.context_manager.current_shallow_message_id = (
|
||||||
|
saved_user_message.get("message_id") if isinstance(saved_user_message, dict) else None
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
web_terminal.context_manager.current_shallow_message_id = None
|
||||||
try:
|
try:
|
||||||
user_message_index = len(getattr(web_terminal.context_manager, "conversation_history", []) or []) - 1
|
user_message_index = len(getattr(web_terminal.context_manager, "conversation_history", []) or []) - 1
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@ -1074,14 +1074,16 @@ def get_conversation_versioning_checkpoint_detail(conversation_id, seq: int, ter
|
|||||||
shallow_message_id = snapshot_data.get("shallow_message_id")
|
shallow_message_id = snapshot_data.get("shallow_message_id")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
files = row.get("files") or []
|
debug_log(f"[Versioning][Detail] resolved shallow_message_id={shallow_message_id}")
|
||||||
debug_log(f"[Versioning][Detail] resolved shallow_message_id={shallow_message_id} files={files}")
|
if shallow_message_id:
|
||||||
if shallow_message_id and isinstance(files, list):
|
|
||||||
shallow_manager = ShallowVersioningManager(
|
shallow_manager = ShallowVersioningManager(
|
||||||
project_path=workspace.project_path,
|
project_path=workspace.project_path,
|
||||||
data_dir=workspace.data_dir,
|
data_dir=workspace.data_dir,
|
||||||
conversation_id=normalized_id,
|
conversation_id=normalized_id,
|
||||||
)
|
)
|
||||||
|
# 与列表接口保持一致:从 shallow manager 重新计算文件变更,
|
||||||
|
# 而不是只给 inputs.jsonl 中原有的 files 补 patch_lines。
|
||||||
|
files = shallow_manager.get_file_diff_stats(str(shallow_message_id)) or []
|
||||||
stats = shallow_manager.get_diff_stats(str(shallow_message_id)) or {}
|
stats = shallow_manager.get_diff_stats(str(shallow_message_id)) or {}
|
||||||
normalized_files: List[Dict[str, Any]] = []
|
normalized_files: List[Dict[str, Any]] = []
|
||||||
for file_item in files:
|
for file_item in files:
|
||||||
@ -1091,7 +1093,7 @@ def get_conversation_versioning_checkpoint_detail(conversation_id, seq: int, ter
|
|||||||
path = str(item.get("path") or "")
|
path = str(item.get("path") or "")
|
||||||
if path:
|
if path:
|
||||||
patch = shallow_manager.get_file_patch_lines(path, str(shallow_message_id))
|
patch = shallow_manager.get_file_patch_lines(path, str(shallow_message_id))
|
||||||
debug_log(f"[Versioning][Detail] path={path} patch={patch}")
|
debug_log(f"[Versioning][Detail] path={path} patch_lines={len(patch.get('lines') or [])}")
|
||||||
item["patch_lines"] = patch.get("lines") or []
|
item["patch_lines"] = patch.get("lines") or []
|
||||||
item["patch_truncated"] = bool(patch.get("truncated"))
|
item["patch_truncated"] = bool(patch.get("truncated"))
|
||||||
normalized_files.append(item)
|
normalized_files.append(item)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user