jarvis-ai-assistant 0.1.199__tar.gz → 0.1.201__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {jarvis_ai_assistant-0.1.199/src/jarvis_ai_assistant.egg-info → jarvis_ai_assistant-0.1.201}/PKG-INFO +1 -1
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/pyproject.toml +1 -1
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/setup.py +1 -1
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/__init__.py +1 -1
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_agent/__init__.py +5 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_agent/builtin_input_handler.py +1 -0
- jarvis_ai_assistant-0.1.201/src/jarvis/jarvis_agent/edit_file_handler.py +415 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_agent/code_agent.py +4 -3
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_platform/base.py +3 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_platform/yuanbao.py +5 -5
- jarvis_ai_assistant-0.1.201/src/jarvis/jarvis_tools/edit_file.py +271 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/registry.py +11 -11
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_utils/builtin_replace_map.py +0 -88
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_utils/utils.py +7 -1
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201/src/jarvis_ai_assistant.egg-info}/PKG-INFO +1 -1
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis_ai_assistant.egg-info/SOURCES.txt +1 -0
- jarvis_ai_assistant-0.1.199/src/jarvis/jarvis_tools/edit_file.py +0 -489
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/MANIFEST.in +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/README.md +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/setup.cfg +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_agent/jarvis.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_agent/main.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_agent/output_handler.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_agent/shell_input_handler.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_agent/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_agent/lint.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/c_cpp.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/csharp.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/data_format.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/devops.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/docs.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/go.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/infrastructure.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/java.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/javascript.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/kotlin.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/loader.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/php.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/python.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/ruby.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/rust.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/shell.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/sql.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/swift.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/checklists/web.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_code_analysis/code_review.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_data/config_schema.json +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_data/huggingface.tar.gz +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_dev/main.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_git_details/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_git_details/main.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_git_squash/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_git_squash/main.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_git_utils/git_commiter.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_mcp/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_mcp/sse_mcp_client.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_mcp/stdio_mcp_client.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_mcp/streamable_mcp_client.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_methodology/main.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_multi_agent/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_multi_agent/main.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_platform/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_platform/human.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_platform/kimi.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_platform/openai.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_platform/registry.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_platform/tongyi.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_platform_manager/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_platform_manager/main.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_smart_shell/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_smart_shell/main.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/ask_user.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/base.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/chdir.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/cli/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/cli/main.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/code_plan.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/create_code_agent.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/create_sub_agent.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/execute_script.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/file_analyzer.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/file_operation.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/generate_new_tool.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/methodology.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/read_code.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/read_webpage.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/rewrite_file.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/search_web.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_tools/virtual_tty.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_utils/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_utils/config.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_utils/embedding.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_utils/file_processors.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_utils/git_utils.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_utils/globals.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_utils/input.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_utils/jarvis_history.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_utils/methodology.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_utils/output.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_utils/tag.py +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis_ai_assistant.egg-info/dependency_links.txt +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis_ai_assistant.egg-info/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis_ai_assistant.egg-info/requires.txt +0 -0
- {jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis_ai_assistant.egg-info/top_level.txt +0 -0
@@ -8,7 +8,7 @@ default = true
|
|
8
8
|
|
9
9
|
[project]
|
10
10
|
name = "jarvis-ai-assistant"
|
11
|
-
version = "0.1.
|
11
|
+
version = "0.1.201"
|
12
12
|
description = "Jarvis: An AI assistant that uses tools to interact with the system"
|
13
13
|
readme = "README.md"
|
14
14
|
authors = [{ name = "skyfire", email = "skyfireitdiy@hotmail.com" }]
|
@@ -3,7 +3,7 @@ from setuptools import setup, find_packages
|
|
3
3
|
|
4
4
|
setup(
|
5
5
|
name="jarvis-ai-assistant",
|
6
|
-
version="0.1.
|
6
|
+
version="0.1.201",
|
7
7
|
author="skyfire",
|
8
8
|
author_email="skyfireitdiy@hotmail.com",
|
9
9
|
description="An AI assistant that uses various tools to interact with the system",
|
{jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_agent/__init__.py
RENAMED
@@ -821,6 +821,11 @@ arguments:
|
|
821
821
|
f"模型交互期间被中断,请输入用户干预信息:"
|
822
822
|
)
|
823
823
|
if user_input:
|
824
|
+
# 如果有工具调用且用户确认继续,则将干预信息和工具执行结果拼接为prompt
|
825
|
+
if any(handler.can_handle(current_response) for handler in self.output_handler):
|
826
|
+
if user_confirm("检测到有工具调用,是否继续处理工具调用?", True):
|
827
|
+
self.prompt = f"{user_input}\n\n{current_response}"
|
828
|
+
continue
|
824
829
|
self.prompt += f"{user_input}"
|
825
830
|
continue
|
826
831
|
|
@@ -45,6 +45,7 @@ def builtin_input_handler(user_input: str, agent_: Any) -> Tuple[str, bool]:
|
|
45
45
|
tool_registry_ if tool_registry_ else ToolRegistry()
|
46
46
|
)
|
47
47
|
agent.set_addon_prompt(tool_registry.prompt())
|
48
|
+
return "", False
|
48
49
|
elif tag == "ReloadConfig":
|
49
50
|
from jarvis.jarvis_utils.utils import load_config
|
50
51
|
|
@@ -0,0 +1,415 @@
|
|
1
|
+
import os
|
2
|
+
import re
|
3
|
+
from abc import ABC, abstractmethod
|
4
|
+
from typing import Any, Dict, List, Tuple
|
5
|
+
|
6
|
+
from yaspin import yaspin
|
7
|
+
from yaspin.core import Yaspin
|
8
|
+
|
9
|
+
from jarvis.jarvis_agent.output_handler import OutputHandler
|
10
|
+
from jarvis.jarvis_platform.registry import PlatformRegistry
|
11
|
+
from jarvis.jarvis_tools.file_operation import FileOperationTool
|
12
|
+
from jarvis.jarvis_utils.git_utils import revert_file
|
13
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
14
|
+
from jarvis.jarvis_utils.tag import ct, ot
|
15
|
+
from jarvis.jarvis_utils.utils import is_context_overflow
|
16
|
+
|
17
|
+
|
18
|
+
class EditFileHandler(OutputHandler):
|
19
|
+
def __init__(self):
|
20
|
+
self.patch_pattern = re.compile(
|
21
|
+
ot("PATCH file=([^>]+)") + r"\s*"
|
22
|
+
r"(?:"
|
23
|
+
+ ot("DIFF")
|
24
|
+
+ r"\s*"
|
25
|
+
+ ot("SEARCH")
|
26
|
+
+ r"\s*(.*?)\s*"
|
27
|
+
+ ct("SEARCH")
|
28
|
+
+ r"\s*"
|
29
|
+
+ ot("REPLACE")
|
30
|
+
+ r"\s*(.*?)\s*"
|
31
|
+
+ ct("REPLACE")
|
32
|
+
+ r"\s*"
|
33
|
+
+ ct("DIFF")
|
34
|
+
+ r"\s*)+"
|
35
|
+
+ ct("PATCH"),
|
36
|
+
re.DOTALL,
|
37
|
+
)
|
38
|
+
self.diff_pattern = re.compile(
|
39
|
+
ot("DIFF")
|
40
|
+
+ r"\s*"
|
41
|
+
+ ot("SEARCH")
|
42
|
+
+ r"\s*(.*?)\s*"
|
43
|
+
+ ct("SEARCH")
|
44
|
+
+ r"\s*"
|
45
|
+
+ ot("REPLACE")
|
46
|
+
+ r"\s*(.*?)\s*"
|
47
|
+
+ ct("REPLACE")
|
48
|
+
+ r"\s*"
|
49
|
+
+ ct("DIFF"),
|
50
|
+
re.DOTALL,
|
51
|
+
)
|
52
|
+
|
53
|
+
def handle(self, response: str, agent: Any) -> Tuple[bool, str]:
|
54
|
+
"""处理文件编辑响应
|
55
|
+
|
56
|
+
Args:
|
57
|
+
response: 包含文件编辑指令的响应字符串
|
58
|
+
agent: 执行处理的agent实例
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
Tuple[bool, str]: 返回处理结果元组,第一个元素表示是否处理成功,第二个元素为处理结果汇总字符串
|
62
|
+
"""
|
63
|
+
patches = self._parse_patches(response)
|
64
|
+
if not patches:
|
65
|
+
return False, "未找到有效的文件编辑指令"
|
66
|
+
|
67
|
+
results = []
|
68
|
+
overall_success = True
|
69
|
+
|
70
|
+
for file_path, diffs in patches.items():
|
71
|
+
file_path = os.path.abspath(file_path)
|
72
|
+
file_patches = [
|
73
|
+
{"search": diff["search"], "replace": diff["replace"]} for diff in diffs
|
74
|
+
]
|
75
|
+
|
76
|
+
with yaspin(text=f"正在处理文件 {file_path}...", color="cyan") as spinner:
|
77
|
+
# 首先尝试fast_edit模式
|
78
|
+
success, result = self._fast_edit(file_path, file_patches, spinner)
|
79
|
+
if not success:
|
80
|
+
# 如果fast_edit失败,尝试slow_edit模式
|
81
|
+
success, result = EditFileHandler._slow_edit(
|
82
|
+
file_path, file_patches, spinner, agent
|
83
|
+
)
|
84
|
+
|
85
|
+
if success:
|
86
|
+
results.append(f"✅ 文件 {file_path} 修改成功")
|
87
|
+
else:
|
88
|
+
results.append(f"❌ 文件 {file_path} 修改失败: {result}")
|
89
|
+
|
90
|
+
summary = "\n".join(results)
|
91
|
+
return False, summary
|
92
|
+
|
93
|
+
def can_handle(self, response: str) -> bool:
|
94
|
+
"""判断是否能处理给定的响应
|
95
|
+
|
96
|
+
Args:
|
97
|
+
response: 需要判断的响应字符串
|
98
|
+
|
99
|
+
Returns:
|
100
|
+
bool: 返回是否能处理该响应
|
101
|
+
"""
|
102
|
+
return bool(self.patch_pattern.search(response))
|
103
|
+
|
104
|
+
def prompt(self) -> str:
|
105
|
+
"""获取处理器的提示信息
|
106
|
+
|
107
|
+
Returns:
|
108
|
+
str: 返回处理器的提示字符串
|
109
|
+
"""
|
110
|
+
return f"""文件编辑指令格式:
|
111
|
+
{ot("PATCH file=文件路径")}
|
112
|
+
{ot("DIFF")}
|
113
|
+
{ot("SEARCH")}
|
114
|
+
原始代码
|
115
|
+
{ct("SEARCH")}
|
116
|
+
{ot("REPLACE")}
|
117
|
+
新代码
|
118
|
+
{ct("REPLACE")}
|
119
|
+
{ct("DIFF")}
|
120
|
+
{ct("PATCH")}
|
121
|
+
|
122
|
+
每个PATCH块可以包含多个DIFF块,每个DIFF块包含一组搜索和替换内容。
|
123
|
+
搜索文本必须能在文件中唯一匹配,否则编辑将失败。"""
|
124
|
+
|
125
|
+
def name(self) -> str:
|
126
|
+
"""获取处理器的名称
|
127
|
+
|
128
|
+
Returns:
|
129
|
+
str: 返回处理器的名称字符串
|
130
|
+
"""
|
131
|
+
return "PATCH"
|
132
|
+
|
133
|
+
def _parse_patches(self, response: str) -> Dict[str, List[Dict[str, str]]]:
|
134
|
+
"""解析响应中的补丁信息
|
135
|
+
|
136
|
+
该方法使用正则表达式从响应文本中提取文件编辑指令(PATCH块),
|
137
|
+
每个PATCH块可以包含多个DIFF块,每个DIFF块包含一组搜索和替换内容。
|
138
|
+
解析后会返回一个字典,键是文件路径,值是该文件对应的补丁列表。
|
139
|
+
|
140
|
+
Args:
|
141
|
+
response: 包含补丁信息的响应字符串,格式应符合PATCH指令规范
|
142
|
+
|
143
|
+
Returns:
|
144
|
+
Dict[str, List[Dict[str, str]]]:
|
145
|
+
返回解析后的补丁信息字典,结构为:
|
146
|
+
{
|
147
|
+
"文件路径1": [
|
148
|
+
{"search": "搜索文本1", "replace": "替换文本1"},
|
149
|
+
{"search": "搜索文本2", "replace": "替换文本2"}
|
150
|
+
],
|
151
|
+
"文件路径2": [...]
|
152
|
+
}
|
153
|
+
"""
|
154
|
+
patches = {}
|
155
|
+
for match in self.patch_pattern.finditer(response):
|
156
|
+
file_path = match.group(1)
|
157
|
+
diffs = []
|
158
|
+
for diff_match in self.diff_pattern.finditer(match.group(0)):
|
159
|
+
diffs.append(
|
160
|
+
{
|
161
|
+
"search": diff_match.group(1).strip(),
|
162
|
+
"replace": diff_match.group(2).strip(),
|
163
|
+
}
|
164
|
+
)
|
165
|
+
if diffs:
|
166
|
+
patches[file_path] = diffs
|
167
|
+
return patches
|
168
|
+
|
169
|
+
@staticmethod
|
170
|
+
def _fast_edit(
|
171
|
+
file_path: str, patches: List[Dict[str, str]], spinner: Yaspin
|
172
|
+
) -> Tuple[bool, str]:
|
173
|
+
"""快速应用补丁到文件
|
174
|
+
|
175
|
+
该方法直接尝试将补丁应用到目标文件,适用于简单、明确的修改场景。
|
176
|
+
特点:
|
177
|
+
1. 直接进行字符串替换,效率高
|
178
|
+
2. 会自动处理缩进问题,尝试匹配不同缩进级别的代码
|
179
|
+
3. 确保搜索文本在文件中唯一匹配
|
180
|
+
4. 如果失败会自动回滚修改
|
181
|
+
|
182
|
+
Args:
|
183
|
+
file_path: 要修改的文件路径,支持绝对路径和相对路径
|
184
|
+
patches: 补丁列表,每个补丁包含search(搜索文本)和replace(替换文本)
|
185
|
+
spinner: 进度显示对象,用于显示处理状态和结果
|
186
|
+
|
187
|
+
Returns:
|
188
|
+
Tuple[bool, str]:
|
189
|
+
返回处理结果元组,第一个元素表示是否成功(True/False),
|
190
|
+
第二个元素为结果信息,成功时为修改后的文件内容,失败时为错误信息
|
191
|
+
"""
|
192
|
+
try:
|
193
|
+
# 确保目录存在
|
194
|
+
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
195
|
+
|
196
|
+
# 读取原始文件内容
|
197
|
+
file_content = ""
|
198
|
+
if os.path.exists(file_path):
|
199
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
200
|
+
file_content = f.read()
|
201
|
+
|
202
|
+
# 应用所有补丁
|
203
|
+
modified_content = file_content
|
204
|
+
patch_count = 0
|
205
|
+
for patch in patches:
|
206
|
+
search_text = patch["search"]
|
207
|
+
replace_text = patch["replace"]
|
208
|
+
patch_count += 1
|
209
|
+
|
210
|
+
if search_text in modified_content:
|
211
|
+
if modified_content.count(search_text) > 1:
|
212
|
+
return False, f"搜索文本在文件中存在多处匹配:\n{search_text}"
|
213
|
+
modified_content = modified_content.replace(
|
214
|
+
search_text, replace_text
|
215
|
+
)
|
216
|
+
spinner.write(f"✅ 补丁 #{patch_count} 应用成功")
|
217
|
+
else:
|
218
|
+
# 尝试增加缩进重试
|
219
|
+
found = False
|
220
|
+
for space_count in range(1, 17):
|
221
|
+
indented_search = "\n".join(
|
222
|
+
" " * space_count + line if line.strip() else line
|
223
|
+
for line in search_text.split("\n")
|
224
|
+
)
|
225
|
+
indented_replace = "\n".join(
|
226
|
+
" " * space_count + line if line.strip() else line
|
227
|
+
for line in replace_text.split("\n")
|
228
|
+
)
|
229
|
+
if indented_search in modified_content:
|
230
|
+
if modified_content.count(indented_search) > 1:
|
231
|
+
return (
|
232
|
+
False,
|
233
|
+
f"搜索文本在文件中存在多处匹配:\n{indented_search}",
|
234
|
+
)
|
235
|
+
modified_content = modified_content.replace(
|
236
|
+
indented_search, indented_replace
|
237
|
+
)
|
238
|
+
spinner.write(
|
239
|
+
f"✅ 补丁 #{patch_count} 应用成功 (自动增加 {space_count} 个空格缩进)"
|
240
|
+
)
|
241
|
+
found = True
|
242
|
+
break
|
243
|
+
|
244
|
+
if not found:
|
245
|
+
return False, f"搜索文本在文件中不存在:\n{search_text}"
|
246
|
+
|
247
|
+
# 写入修改后的内容
|
248
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
249
|
+
f.write(modified_content)
|
250
|
+
|
251
|
+
spinner.text = f"文件 {file_path} 修改完成,应用了 {patch_count} 个补丁"
|
252
|
+
spinner.ok("✅")
|
253
|
+
return True, modified_content
|
254
|
+
|
255
|
+
except Exception as e:
|
256
|
+
spinner.text = f"文件修改失败: {str(e)}"
|
257
|
+
spinner.fail("❌")
|
258
|
+
revert_file(file_path)
|
259
|
+
return False, f"文件修改失败: {str(e)}"
|
260
|
+
|
261
|
+
@staticmethod
|
262
|
+
def _slow_edit(
|
263
|
+
file_path: str, patches: List[Dict[str, str]], spinner: Yaspin, agent: Any
|
264
|
+
) -> Tuple[bool, str]:
|
265
|
+
"""使用AI模型生成补丁并应用到文件
|
266
|
+
|
267
|
+
当_fast_edit方法失败时调用此方法,使用AI模型生成更精确的补丁。
|
268
|
+
特点:
|
269
|
+
1. 适用于复杂修改场景或需要上下文理解的修改
|
270
|
+
2. 会自动处理大文件上传问题
|
271
|
+
3. 会尝试最多3次生成有效的补丁
|
272
|
+
4. 生成的补丁会再次通过_fast_edit方法应用
|
273
|
+
5. 如果失败会自动回滚修改
|
274
|
+
|
275
|
+
Args:
|
276
|
+
file_path: 要修改的文件路径,支持绝对路径和相对路径
|
277
|
+
patches: 补丁列表,每个补丁包含search(搜索文本)和replace(替换文本)
|
278
|
+
spinner: 进度显示对象,用于显示处理状态和结果
|
279
|
+
agent: 执行处理的agent实例,用于访问AI模型平台
|
280
|
+
|
281
|
+
Returns:
|
282
|
+
Tuple[bool, str]:
|
283
|
+
返回处理结果元组,第一个元素表示是否成功(True/False),
|
284
|
+
第二个元素为结果信息,成功时为修改后的文件内容,失败时为错误信息
|
285
|
+
"""
|
286
|
+
try:
|
287
|
+
model = PlatformRegistry().get_normal_platform()
|
288
|
+
|
289
|
+
# 读取原始文件内容
|
290
|
+
file_content = ""
|
291
|
+
if os.path.exists(file_path):
|
292
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
293
|
+
file_content = f.read()
|
294
|
+
|
295
|
+
is_large_context = is_context_overflow(file_content)
|
296
|
+
upload_success = False
|
297
|
+
|
298
|
+
# 如果是大文件,尝试上传到模型平台
|
299
|
+
with spinner.hidden():
|
300
|
+
if (
|
301
|
+
is_large_context
|
302
|
+
and model.support_upload_files()
|
303
|
+
and model.upload_files([file_path])
|
304
|
+
):
|
305
|
+
upload_success = True
|
306
|
+
|
307
|
+
model.set_suppress_output(True)
|
308
|
+
|
309
|
+
# 构建补丁内容
|
310
|
+
patch_content = []
|
311
|
+
for patch in patches:
|
312
|
+
patch_content.append(
|
313
|
+
{
|
314
|
+
"reason": "根据用户指令修改代码",
|
315
|
+
"search": patch["search"],
|
316
|
+
"replace": patch["replace"],
|
317
|
+
}
|
318
|
+
)
|
319
|
+
|
320
|
+
# 构建提示词
|
321
|
+
main_prompt = f"""
|
322
|
+
# 代码补丁生成专家指南
|
323
|
+
|
324
|
+
## 任务描述
|
325
|
+
你是一位精确的代码补丁生成专家,需要根据补丁描述生成精确的代码差异。
|
326
|
+
|
327
|
+
### 补丁内容
|
328
|
+
```
|
329
|
+
{str(patch_content)}
|
330
|
+
```
|
331
|
+
|
332
|
+
## 补丁生成要求
|
333
|
+
1. **精确性**:严格按照补丁的意图修改代码
|
334
|
+
2. **格式一致性**:严格保持原始代码的格式风格,如果补丁中缩进或者空行与原代码不一致,则需要修正补丁中的缩进或者空行
|
335
|
+
3. **最小化修改**:只修改必要的代码部分,保持其他部分不变
|
336
|
+
4. **上下文完整性**:提供足够的上下文,确保补丁能准确应用
|
337
|
+
|
338
|
+
## 输出格式规范
|
339
|
+
- 使用{ot("DIFF")}块包围每个需要修改的代码段
|
340
|
+
- 每个{ot("DIFF")}块必须包含SEARCH部分和REPLACE部分
|
341
|
+
- SEARCH部分是需要查找的原始代码
|
342
|
+
- REPLACE部分是替换后的新代码
|
343
|
+
- 确保SEARCH部分能在原文件中**唯一匹配**
|
344
|
+
- 如果修改较大,可以使用多个{ot("DIFF")}块
|
345
|
+
|
346
|
+
## 输出模板
|
347
|
+
{ot("DIFF")}
|
348
|
+
{">" * 5} SEARCH
|
349
|
+
[需要查找的原始代码,包含足够上下文,避免出现可匹配多处的情况]
|
350
|
+
{'='*5}
|
351
|
+
[替换后的新代码]
|
352
|
+
{"<" * 5} REPLACE
|
353
|
+
{ct("DIFF")}
|
354
|
+
|
355
|
+
{ot("DIFF")}
|
356
|
+
{">" * 5} SEARCH
|
357
|
+
[另一处需要查找的原始代码,包含足够上下文,避免出现可匹配多处的情况]
|
358
|
+
{'='*5}
|
359
|
+
[另一处替换后的新代码]
|
360
|
+
{"<" * 5} REPLACE
|
361
|
+
{ct("DIFF")}
|
362
|
+
"""
|
363
|
+
|
364
|
+
# 尝试最多3次生成补丁
|
365
|
+
for _ in range(3):
|
366
|
+
if is_large_context:
|
367
|
+
if upload_success:
|
368
|
+
response = model.chat_until_success(main_prompt)
|
369
|
+
else:
|
370
|
+
file_prompt = f"""
|
371
|
+
# 原始代码
|
372
|
+
{file_content}
|
373
|
+
"""
|
374
|
+
response = model.chat_until_success(main_prompt + file_prompt)
|
375
|
+
else:
|
376
|
+
file_prompt = f"""
|
377
|
+
# 原始代码
|
378
|
+
{file_content}
|
379
|
+
"""
|
380
|
+
response = model.chat_until_success(main_prompt + file_prompt)
|
381
|
+
|
382
|
+
# 解析生成的补丁
|
383
|
+
diff_blocks = re.finditer(
|
384
|
+
ot("DIFF")
|
385
|
+
+ r"\s*"
|
386
|
+
+ r">{4,} SEARCH\n?(.*?)\n?={4,}\n?(.*?)\s*<{4,} REPLACE\n?"
|
387
|
+
+ ct("DIFF"),
|
388
|
+
response,
|
389
|
+
re.DOTALL,
|
390
|
+
)
|
391
|
+
|
392
|
+
generated_patches = []
|
393
|
+
for match in diff_blocks:
|
394
|
+
generated_patches.append(
|
395
|
+
{
|
396
|
+
"search": match.group(1).strip(),
|
397
|
+
"replace": match.group(2).strip(),
|
398
|
+
}
|
399
|
+
)
|
400
|
+
|
401
|
+
if generated_patches:
|
402
|
+
# 尝试应用生成的补丁
|
403
|
+
success, result = EditFileHandler._fast_edit(
|
404
|
+
file_path, generated_patches, spinner
|
405
|
+
)
|
406
|
+
if success:
|
407
|
+
return True, result
|
408
|
+
|
409
|
+
return False, "AI模型无法生成有效的补丁"
|
410
|
+
|
411
|
+
except Exception as e:
|
412
|
+
spinner.text = f"文件修改失败: {str(e)}"
|
413
|
+
spinner.fail("❌")
|
414
|
+
revert_file(file_path)
|
415
|
+
return False, f"文件修改失败: {str(e)}"
|
@@ -14,6 +14,7 @@ from yaspin import yaspin # type: ignore
|
|
14
14
|
|
15
15
|
from jarvis.jarvis_agent import Agent
|
16
16
|
from jarvis.jarvis_agent.builtin_input_handler import builtin_input_handler
|
17
|
+
from jarvis.jarvis_agent.edit_file_handler import EditFileHandler
|
17
18
|
from jarvis.jarvis_agent.shell_input_handler import shell_input_handler
|
18
19
|
# 忽略yaspin的类型检查
|
19
20
|
from jarvis.jarvis_code_agent.lint import get_lint_tools
|
@@ -54,7 +55,7 @@ class CodeAgent:
|
|
54
55
|
"read_code",
|
55
56
|
"methodology",
|
56
57
|
"chdir",
|
57
|
-
"edit_file",
|
58
|
+
# "edit_file",
|
58
59
|
"rewrite_file",
|
59
60
|
]
|
60
61
|
)
|
@@ -94,7 +95,7 @@ class CodeAgent:
|
|
94
95
|
- 仅在命令行工具不足时使用专用工具
|
95
96
|
|
96
97
|
## 文件编辑工具使用规范
|
97
|
-
- 对于部分文件内容修改,使用
|
98
|
+
- 对于部分文件内容修改,使用PATCH
|
98
99
|
- 对于需要重写整个文件内容,使用rewrite_file工具
|
99
100
|
- 对于简单的修改,可以使用execute_script工具执行shell命令完成
|
100
101
|
</code_engineer_guide>
|
@@ -112,7 +113,7 @@ class CodeAgent:
|
|
112
113
|
system_prompt=code_system_prompt,
|
113
114
|
name="CodeAgent",
|
114
115
|
auto_complete=False,
|
115
|
-
output_handler=[tool_registry],
|
116
|
+
output_handler=[tool_registry, EditFileHandler()],
|
116
117
|
platform=platform_instance,
|
117
118
|
input_handler=[shell_input_handler, builtin_input_handler],
|
118
119
|
need_summary=need_summary,
|
{jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_platform/base.py
RENAMED
@@ -149,6 +149,9 @@ class BasePlatform(ABC):
|
|
149
149
|
response = re.sub(
|
150
150
|
ot("think") + r".*?" + ct("think"), "", response, flags=re.DOTALL
|
151
151
|
)
|
152
|
+
response = re.sub(
|
153
|
+
ot("thinking") + r".*?" + ct("thinking"), "", response, flags=re.DOTALL
|
154
|
+
)
|
152
155
|
return response
|
153
156
|
|
154
157
|
def chat_until_success(self, message: str) -> str:
|
{jarvis_ai_assistant-0.1.199 → jarvis_ai_assistant-0.1.201}/src/jarvis/jarvis_platform/yuanbao.py
RENAMED
@@ -98,7 +98,7 @@ class YuanbaoPlatform(BasePlatform):
|
|
98
98
|
|
99
99
|
try:
|
100
100
|
response = while_success(
|
101
|
-
lambda: requests.post(url, headers=headers, data=payload), sleep_time=5
|
101
|
+
lambda: requests.post(url, headers=headers, data=payload, timeout=600), sleep_time=5
|
102
102
|
)
|
103
103
|
response_json = response.json()
|
104
104
|
|
@@ -264,7 +264,7 @@ class YuanbaoPlatform(BasePlatform):
|
|
264
264
|
|
265
265
|
try:
|
266
266
|
response = while_success(
|
267
|
-
lambda: requests.post(url, headers=headers, json=payload), sleep_time=5
|
267
|
+
lambda: requests.post(url, headers=headers, json=payload, timeout=600), sleep_time=5
|
268
268
|
)
|
269
269
|
|
270
270
|
if response.status_code != 200:
|
@@ -477,12 +477,11 @@ class YuanbaoPlatform(BasePlatform):
|
|
477
477
|
if self.first_chat and self.system_message:
|
478
478
|
payload["prompt"] = f"{self.system_message}\n\n{message}"
|
479
479
|
payload["displayPrompt"] = payload["prompt"]
|
480
|
-
self.first_chat = False
|
481
480
|
|
482
481
|
try:
|
483
482
|
# 发送消息请求,获取流式响应
|
484
483
|
response = while_success(
|
485
|
-
lambda: requests.post(url, headers=headers, json=payload, stream=True),
|
484
|
+
lambda: requests.post(url, headers=headers, json=payload, stream=True, timeout=600),
|
486
485
|
sleep_time=5,
|
487
486
|
)
|
488
487
|
|
@@ -532,6 +531,7 @@ class YuanbaoPlatform(BasePlatform):
|
|
532
531
|
# 检测结束标志
|
533
532
|
elif line_str == "data: [DONE]":
|
534
533
|
return None
|
534
|
+
self.first_chat = False
|
535
535
|
return None
|
536
536
|
|
537
537
|
except Exception as e:
|
@@ -556,7 +556,7 @@ class YuanbaoPlatform(BasePlatform):
|
|
556
556
|
|
557
557
|
try:
|
558
558
|
response = while_success(
|
559
|
-
lambda: requests.post(url, headers=headers, json=payload), sleep_time=5
|
559
|
+
lambda: requests.post(url, headers=headers, json=payload, timeout=600), sleep_time=5
|
560
560
|
)
|
561
561
|
|
562
562
|
if response.status_code == 200:
|