mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 07:27:15 +08:00
feat(agent): 新增 apply_patch 多文件补丁编辑工具
参考 Codex apply_patch 设计,支持单次调用对多个文本文件执行新增、 更新和删除:补丁以 *** Begin Patch / *** End Patch 包裹,Update 段 用 @@ 分隔 hunk,上下文行须与当前内容精确一致;先整体校验全部文件 操作,通过后才逐个原子写盘,并以校验期 sha256 防止并发修改。 - 工具描述与系统提示词明确 apply_patch / edit_file / write_file 分工: 跨多文件或增删文件用 apply_patch,单文件单处替换用 edit_file - 注册进工具工厂与 ALWAYS_INCLUDE 列表,加入策略 inventory 与 MCP 隐藏列表,流式回调归类为 file_write - 同步 create-moviepilot-plugin / publish-moviepilot-plugin / create-moviepilot-skill 技能的 allowed-tools 与版本 - 新增 tests/test_agent_apply_patch.py 覆盖解析、多文件应用、 整体拒绝、权限边界与版本冲突场景
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import asyncio
|
||||
import re
|
||||
import threading
|
||||
from typing import Any, Optional, Tuple
|
||||
|
||||
@@ -20,6 +21,19 @@ class _StreamChain(ChainBase):
|
||||
pass
|
||||
|
||||
|
||||
_PATCH_FILE_HEADER_PATTERN = re.compile(
|
||||
r"\*\*\* (?:Add|Update|Delete) File:\s*(\S+)"
|
||||
)
|
||||
|
||||
|
||||
def _extract_first_patch_path(patch: Optional[str]) -> Optional[str]:
|
||||
"""从补丁文本中提取首个文件路径,作为流式消息展示目标。"""
|
||||
if not patch:
|
||||
return None
|
||||
match = _PATCH_FILE_HEADER_PATTERN.search(patch)
|
||||
return match.group(1) if match else None
|
||||
|
||||
|
||||
class StreamingHandler:
|
||||
"""
|
||||
流式Token缓冲管理器
|
||||
@@ -337,6 +351,8 @@ class StreamingHandler:
|
||||
return "file_read", tool_kwargs.get("file_path")
|
||||
if tool_name in {"write_file", "edit_file"}:
|
||||
return "file_write", tool_kwargs.get("file_path")
|
||||
if tool_name == "apply_patch":
|
||||
return "file_write", _extract_first_patch_path(tool_kwargs.get("patch"))
|
||||
if tool_name in {"list_directory", "query_directory_settings"}:
|
||||
return "directory", tool_kwargs.get("path")
|
||||
if tool_name == "browse_webpage":
|
||||
|
||||
Reference in New Issue
Block a user