jarvis-ai-assistant 0.1.207__py3-none-any.whl → 0.1.209__py3-none-any.whl
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/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +63 -103
- jarvis/jarvis_agent/edit_file_handler.py +43 -47
- jarvis/jarvis_agent/jarvis.py +33 -39
- jarvis/jarvis_code_agent/code_agent.py +74 -30
- jarvis/jarvis_code_agent/lint.py +6 -6
- jarvis/jarvis_code_analysis/code_review.py +164 -175
- jarvis/jarvis_data/config_schema.json +0 -25
- jarvis/jarvis_git_utils/git_commiter.py +148 -153
- jarvis/jarvis_methodology/main.py +70 -81
- jarvis/jarvis_platform/base.py +21 -17
- jarvis/jarvis_platform/kimi.py +59 -64
- jarvis/jarvis_platform/tongyi.py +118 -131
- jarvis/jarvis_platform/yuanbao.py +117 -122
- jarvis/jarvis_platform_manager/main.py +102 -502
- jarvis/jarvis_platform_manager/service.py +432 -0
- jarvis/jarvis_smart_shell/main.py +99 -33
- jarvis/jarvis_tools/ask_user.py +0 -1
- jarvis/jarvis_tools/edit_file.py +64 -55
- jarvis/jarvis_tools/file_analyzer.py +17 -28
- jarvis/jarvis_tools/read_code.py +80 -81
- jarvis/jarvis_utils/builtin_replace_map.py +1 -36
- jarvis/jarvis_utils/config.py +13 -48
- jarvis/jarvis_utils/embedding.py +6 -51
- jarvis/jarvis_utils/git_utils.py +93 -43
- jarvis/jarvis_utils/http.py +104 -0
- jarvis/jarvis_utils/methodology.py +12 -17
- jarvis/jarvis_utils/utils.py +186 -63
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/METADATA +4 -19
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/RECORD +34 -40
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/entry_points.txt +1 -1
- jarvis/jarvis_data/huggingface.tar.gz +0 -0
- jarvis/jarvis_dev/main.py +0 -1247
- jarvis/jarvis_tools/chdir.py +0 -72
- jarvis/jarvis_tools/code_plan.py +0 -218
- jarvis/jarvis_tools/create_code_agent.py +0 -95
- jarvis/jarvis_tools/create_sub_agent.py +0 -82
- jarvis/jarvis_tools/file_operation.py +0 -238
- jarvis/jarvis_utils/jarvis_history.py +0 -98
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
jarvis/jarvis_agent/__init__.py
CHANGED
@@ -7,27 +7,30 @@ import platform
|
|
7
7
|
from typing import Any, Callable, Dict, List, Optional, Protocol, Tuple, Union
|
8
8
|
|
9
9
|
# 第三方库导入
|
10
|
-
from yaspin import yaspin # type: ignore
|
11
10
|
|
12
11
|
# 本地库导入
|
13
12
|
# jarvis_agent 相关
|
14
13
|
# jarvis_platform 相关
|
15
14
|
from jarvis.jarvis_platform.base import BasePlatform
|
16
15
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
16
|
+
|
17
17
|
# jarvis_utils 相关
|
18
|
-
from jarvis.jarvis_utils.config import (
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
from jarvis.jarvis_utils.config import (
|
19
|
+
get_max_token_count,
|
20
|
+
is_execute_tool_confirm,
|
21
|
+
is_use_analysis,
|
22
|
+
is_use_methodology,
|
23
|
+
)
|
23
24
|
from jarvis.jarvis_utils.embedding import get_context_token_count
|
24
|
-
from jarvis.jarvis_utils.globals import (
|
25
|
-
|
26
|
-
|
25
|
+
from jarvis.jarvis_utils.globals import (
|
26
|
+
delete_agent,
|
27
|
+
get_interrupt,
|
28
|
+
make_agent_name,
|
29
|
+
set_agent,
|
30
|
+
set_interrupt,
|
31
|
+
)
|
27
32
|
from jarvis.jarvis_utils.input import get_multiline_input
|
28
|
-
from jarvis.jarvis_utils.
|
29
|
-
from jarvis.jarvis_utils.methodology import (load_methodology,
|
30
|
-
upload_methodology)
|
33
|
+
from jarvis.jarvis_utils.methodology import load_methodology, upload_methodology
|
31
34
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
32
35
|
from jarvis.jarvis_utils.tag import ct, ot
|
33
36
|
from jarvis.jarvis_utils.utils import user_confirm
|
@@ -139,7 +142,7 @@ class Agent:
|
|
139
142
|
self.prompt = ""
|
140
143
|
|
141
144
|
def __del__(self):
|
142
|
-
|
145
|
+
# 只有在记录启动时才停止记录
|
143
146
|
delete_agent(self.name)
|
144
147
|
|
145
148
|
def __init__(
|
@@ -150,7 +153,7 @@ class Agent:
|
|
150
153
|
platform: Union[Optional[BasePlatform], Optional[str]] = None,
|
151
154
|
model_name: Optional[str] = None,
|
152
155
|
summary_prompt: Optional[str] = None,
|
153
|
-
auto_complete:
|
156
|
+
auto_complete: bool = False,
|
154
157
|
output_handler: List[OutputHandlerProtocol] = [],
|
155
158
|
use_tools: List[str] = [],
|
156
159
|
input_handler: Optional[List[Callable[[str, Any], Tuple[str, bool]]]] = None,
|
@@ -160,7 +163,6 @@ class Agent:
|
|
160
163
|
use_methodology: Optional[bool] = None,
|
161
164
|
use_analysis: Optional[bool] = None,
|
162
165
|
files: List[str] = [],
|
163
|
-
history_count: Optional[int] = None,
|
164
166
|
):
|
165
167
|
self.files = files
|
166
168
|
"""初始化Jarvis Agent实例
|
@@ -235,17 +237,8 @@ class Agent:
|
|
235
237
|
# Load configuration from environment variables
|
236
238
|
self.addon_prompt = ""
|
237
239
|
|
238
|
-
self.tool_call_count = 0
|
239
|
-
self.max_tool_call_count = get_max_tool_call_count()
|
240
240
|
self.after_tool_call_cb: Optional[Callable[[Agent], None]] = None
|
241
241
|
|
242
|
-
self.history = JarvisHistory()
|
243
|
-
self.history_dir = str(Path(get_data_dir())/"history")
|
244
|
-
self.history.start_record(self.history_dir)
|
245
|
-
|
246
|
-
self.history_count = history_count if history_count is not None else get_history_count()
|
247
|
-
|
248
|
-
|
249
242
|
self.execute_tool_confirm = (
|
250
243
|
execute_tool_confirm
|
251
244
|
if execute_tool_confirm is not None
|
@@ -274,9 +267,7 @@ class Agent:
|
|
274
267
|
)
|
275
268
|
|
276
269
|
self.max_token_count = get_max_token_count()
|
277
|
-
self.auto_complete =
|
278
|
-
auto_complete if auto_complete is not None else is_auto_complete()
|
279
|
-
)
|
270
|
+
self.auto_complete = auto_complete
|
280
271
|
welcome_message = f"{name} 初始化完成 - 使用 {self.model.name()} 模型"
|
281
272
|
|
282
273
|
PrettyOutput.print(welcome_message, OutputType.SYSTEM)
|
@@ -407,10 +398,10 @@ class Agent:
|
|
407
398
|
if self.conversation_length > self.max_token_count:
|
408
399
|
message = self._summarize_and_clear_history() + "\n\n" + message
|
409
400
|
self.conversation_length += get_context_token_count(message)
|
410
|
-
|
401
|
+
|
411
402
|
response = self.model.chat_until_success(message) # type: ignore
|
412
403
|
self.conversation_length += get_context_token_count(response)
|
413
|
-
|
404
|
+
|
414
405
|
return response
|
415
406
|
|
416
407
|
def generate_summary(self) -> str:
|
@@ -422,8 +413,8 @@ class Agent:
|
|
422
413
|
注意:
|
423
414
|
仅生成摘要,不修改对话状态
|
424
415
|
"""
|
425
|
-
|
426
|
-
|
416
|
+
print("📄 正在总结对话历史...")
|
417
|
+
summary_prompt = """
|
427
418
|
<summary_request>
|
428
419
|
<objective>
|
429
420
|
请对当前对话历史进行简明扼要的总结,提取关键信息和重要决策点。这个总结将作为上下文继续任务,因此需要保留对后续对话至关重要的内容。
|
@@ -447,14 +438,11 @@ class Agent:
|
|
447
438
|
"""
|
448
439
|
|
449
440
|
try:
|
450
|
-
|
451
|
-
|
452
|
-
spinner.text = "总结对话历史完成"
|
453
|
-
spinner.ok("✅")
|
441
|
+
summary = self.model.chat_until_success(self.prompt + "\n" + summary_prompt) # type: ignore
|
442
|
+
print("✅ 总结对话历史完成")
|
454
443
|
return summary
|
455
444
|
except Exception as e:
|
456
|
-
|
457
|
-
spinner.fail("❌")
|
445
|
+
print("❌ 总结对话历史失败")
|
458
446
|
return ""
|
459
447
|
|
460
448
|
def _summarize_and_clear_history(self) -> str:
|
@@ -482,9 +470,9 @@ class Agent:
|
|
482
470
|
summary = self.generate_summary()
|
483
471
|
else:
|
484
472
|
import tempfile
|
473
|
+
|
485
474
|
tmp_file = tempfile.NamedTemporaryFile(delete=False)
|
486
475
|
tmp_file_name = tmp_file.name
|
487
|
-
self.history.save_history(tmp_file_name)
|
488
476
|
self.clear_history() # type: ignore
|
489
477
|
|
490
478
|
if need_summary:
|
@@ -540,32 +528,16 @@ class Agent:
|
|
540
528
|
)
|
541
529
|
if len(tool_list) == 0:
|
542
530
|
return False, ""
|
543
|
-
|
544
|
-
self.max_tool_call_count > 0
|
545
|
-
and self.tool_call_count >= self.max_tool_call_count
|
546
|
-
):
|
547
|
-
if user_confirm(f"工具调用次数超过限制,是否继续执行?", True):
|
548
|
-
self.reset_tool_call_count()
|
549
|
-
else:
|
550
|
-
return False, ""
|
551
|
-
if self.execute_tool_confirm:
|
552
|
-
self.reset_tool_call_count()
|
531
|
+
|
553
532
|
if not self.execute_tool_confirm or user_confirm(
|
554
533
|
f"需要执行{tool_list[0].name()}确认执行?", True
|
555
534
|
):
|
556
|
-
|
557
|
-
|
558
|
-
)
|
559
|
-
with spinner.hidden():
|
560
|
-
result = tool_list[0].handle(response, self)
|
561
|
-
spinner.text = f"{tool_list[0].name()}执行完成"
|
562
|
-
spinner.ok("✅")
|
563
|
-
self.tool_call_count += 1
|
564
|
-
return result
|
565
|
-
return False, ""
|
535
|
+
print(f"🔧 正在执行{tool_list[0].name()}...")
|
536
|
+
result = tool_list[0].handle(response, self)
|
537
|
+
print(f"✅ {tool_list[0].name()}执行完成")
|
566
538
|
|
567
|
-
|
568
|
-
|
539
|
+
return result
|
540
|
+
return False, ""
|
569
541
|
|
570
542
|
def _complete_task(self) -> str:
|
571
543
|
"""完成任务并生成总结(如果需要)
|
@@ -581,21 +553,19 @@ class Agent:
|
|
581
553
|
if self.use_analysis:
|
582
554
|
self._analysis_task()
|
583
555
|
if self.need_summary:
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
spinner.ok("✅")
|
590
|
-
return ret
|
556
|
+
print("📄 正在生成总结...")
|
557
|
+
self.prompt = self.summary_prompt
|
558
|
+
ret = self.model.chat_until_success(self.prompt) # type: ignore
|
559
|
+
print("✅ 总结生成完成")
|
560
|
+
return ret
|
591
561
|
|
592
562
|
return "任务完成"
|
593
563
|
|
594
564
|
def _analysis_task(self):
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
565
|
+
print("🔍 正在分析任务...")
|
566
|
+
try:
|
567
|
+
# 让模型判断是否需要生成方法论
|
568
|
+
analysis_prompt = f"""<task_analysis>
|
599
569
|
<request>
|
600
570
|
当前任务已结束,请分析该任务的解决方案:
|
601
571
|
1. 首先检查现有工具或方法论是否已经可以完成该任务,如果可以,直接说明即可,无需生成新内容
|
@@ -741,17 +711,12 @@ arguments:
|
|
741
711
|
</output_requirements>
|
742
712
|
</task_analysis>"""
|
743
713
|
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
spinner.text = "分析完成"
|
751
|
-
spinner.ok("✅")
|
752
|
-
except Exception as e:
|
753
|
-
spinner.text = "分析失败"
|
754
|
-
spinner.fail("❌")
|
714
|
+
self.prompt = analysis_prompt
|
715
|
+
response = self.model.chat_until_success(self.prompt) # type: ignore
|
716
|
+
self._call_tools(response)
|
717
|
+
print("✅ 分析完成")
|
718
|
+
except Exception as e:
|
719
|
+
print("❌ 分析失败")
|
755
720
|
|
756
721
|
def make_default_addon_prompt(self, need_complete: bool) -> str:
|
757
722
|
"""生成附加提示。
|
@@ -808,9 +773,8 @@ arguments:
|
|
808
773
|
set_agent(self.name, self)
|
809
774
|
|
810
775
|
while True:
|
811
|
-
history_md = ""
|
812
776
|
if self.first:
|
813
|
-
|
777
|
+
self._first_run()
|
814
778
|
try:
|
815
779
|
current_response = self._call_model(self.prompt, True)
|
816
780
|
self.prompt = ""
|
@@ -822,8 +786,13 @@ arguments:
|
|
822
786
|
)
|
823
787
|
if user_input:
|
824
788
|
# 如果有工具调用且用户确认继续,则将干预信息和工具执行结果拼接为prompt
|
825
|
-
if any(
|
826
|
-
|
789
|
+
if any(
|
790
|
+
handler.can_handle(current_response)
|
791
|
+
for handler in self.output_handler
|
792
|
+
):
|
793
|
+
if user_confirm(
|
794
|
+
"检测到有工具调用,是否继续处理工具调用?", True
|
795
|
+
):
|
827
796
|
self.prompt = f"{user_input}\n\n{current_response}"
|
828
797
|
continue
|
829
798
|
self.prompt += f"{user_input}"
|
@@ -843,8 +812,6 @@ arguments:
|
|
843
812
|
if self.auto_complete and ot("!!!COMPLETE!!!") in current_response:
|
844
813
|
return self._complete_task()
|
845
814
|
|
846
|
-
self.reset_tool_call_count()
|
847
|
-
|
848
815
|
# 获取用户输入
|
849
816
|
user_input = self.multiline_inputer(
|
850
817
|
f"{self.name}: 请输入,或输入空行来结束当前任务:"
|
@@ -858,8 +825,6 @@ arguments:
|
|
858
825
|
return self._complete_task()
|
859
826
|
|
860
827
|
except Exception as e:
|
861
|
-
if history_md:
|
862
|
-
os.remove(history_md)
|
863
828
|
PrettyOutput.print(f"任务失败: {str(e)}", OutputType.ERROR)
|
864
829
|
return f"Task failed: {str(e)}"
|
865
830
|
|
@@ -868,21 +833,15 @@ arguments:
|
|
868
833
|
return f"Task failed: {str(e)}"
|
869
834
|
|
870
835
|
def _first_run(self):
|
871
|
-
|
872
|
-
if self.history_count > 0 and self.model and self.model.support_upload_files():
|
873
|
-
import tempfile
|
874
|
-
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
875
|
-
history_md = str(Path(tempfile.gettempdir())/f"{self.name}_history_{timestamp}.md")
|
876
|
-
self.history.export_history_to_markdown(self.history_dir, history_md, max_files=self.history_count)
|
877
|
-
self.files.append(history_md)
|
878
|
-
|
879
|
-
# 如果有上传文件,先上传文件
|
836
|
+
# 如果有上传文件,先上传文件
|
880
837
|
if self.model and self.model.support_upload_files():
|
881
838
|
if self.use_methodology:
|
882
839
|
if not upload_methodology(self.model, other_files=self.files):
|
883
840
|
if self.files:
|
884
|
-
PrettyOutput.print(
|
885
|
-
|
841
|
+
PrettyOutput.print(
|
842
|
+
"文件上传失败,将忽略文件列表", OutputType.WARNING
|
843
|
+
)
|
844
|
+
# 上传失败则回退到本地加载
|
886
845
|
msg = self.prompt
|
887
846
|
for handler in self.input_handler:
|
888
847
|
msg, _ = handler(msg, self)
|
@@ -894,7 +853,9 @@ arguments:
|
|
894
853
|
self.prompt = f"{self.prompt}\n\n上传的文件包含历史对话信息,可以从中获取一些经验信息。"
|
895
854
|
elif self.files:
|
896
855
|
if not self.model.upload_files(self.files):
|
897
|
-
PrettyOutput.print(
|
856
|
+
PrettyOutput.print(
|
857
|
+
"文件上传失败,将忽略文件列表", OutputType.WARNING
|
858
|
+
)
|
898
859
|
else:
|
899
860
|
self.prompt = f"{self.prompt}\n\n上传的文件包含历史对话信息,可以从中获取一些经验信息。"
|
900
861
|
else:
|
@@ -907,7 +868,6 @@ arguments:
|
|
907
868
|
self.prompt = f"{self.prompt}\n\n以下是历史类似问题的执行经验,可参考:\n{load_methodology(msg, self.get_tool_registry())}"
|
908
869
|
|
909
870
|
self.first = False
|
910
|
-
return history_md
|
911
871
|
|
912
872
|
def clear_history(self):
|
913
873
|
"""清空对话历史但保留系统提示
|
@@ -2,9 +2,6 @@ import os
|
|
2
2
|
import re
|
3
3
|
from typing import Any, Dict, List, Tuple
|
4
4
|
|
5
|
-
from yaspin import yaspin # type: ignore
|
6
|
-
from yaspin.core import Yaspin # type: ignore
|
7
|
-
|
8
5
|
from jarvis.jarvis_agent.output_handler import OutputHandler
|
9
6
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
10
7
|
from jarvis.jarvis_utils.git_utils import revert_file
|
@@ -71,19 +68,19 @@ class EditFileHandler(OutputHandler):
|
|
71
68
|
{"SEARCH": diff["SEARCH"], "REPLACE": diff["REPLACE"]} for diff in diffs
|
72
69
|
]
|
73
70
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
71
|
+
print(f"📝 正在处理文件 {file_path}...")
|
72
|
+
# 首先尝试fast_edit模式
|
73
|
+
success, result = self._fast_edit(file_path, file_patches)
|
74
|
+
if not success:
|
75
|
+
# 如果fast_edit失败,尝试slow_edit模式
|
76
|
+
success, result = EditFileHandler._slow_edit(
|
77
|
+
file_path, file_patches, agent
|
78
|
+
)
|
82
79
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
80
|
+
if success:
|
81
|
+
results.append(f"✅ 文件 {file_path} 修改成功")
|
82
|
+
else:
|
83
|
+
results.append(f"❌ 文件 {file_path} 修改失败: {result}")
|
87
84
|
|
88
85
|
summary = "\n".join(results)
|
89
86
|
return False, summary
|
@@ -153,10 +150,11 @@ class EditFileHandler(OutputHandler):
|
|
153
150
|
file_path = match.group(1) or match.group(2) or match.group(3)
|
154
151
|
diffs = []
|
155
152
|
for diff_match in self.diff_pattern.finditer(match.group(0)):
|
153
|
+
# 完全保留原始格式(包括所有空白和换行)
|
156
154
|
diffs.append(
|
157
155
|
{
|
158
|
-
"SEARCH": diff_match.group(1)
|
159
|
-
"REPLACE": diff_match.group(2)
|
156
|
+
"SEARCH": diff_match.group(1), # 原始SEARCH内容
|
157
|
+
"REPLACE": diff_match.group(2), # 原始REPLACE内容
|
160
158
|
}
|
161
159
|
)
|
162
160
|
if diffs:
|
@@ -167,9 +165,7 @@ class EditFileHandler(OutputHandler):
|
|
167
165
|
return patches
|
168
166
|
|
169
167
|
@staticmethod
|
170
|
-
def _fast_edit(
|
171
|
-
file_path: str, patches: List[Dict[str, str]], spinner: Yaspin
|
172
|
-
) -> Tuple[bool, str]:
|
168
|
+
def _fast_edit(file_path: str, patches: List[Dict[str, str]]) -> Tuple[bool, str]:
|
173
169
|
"""快速应用补丁到文件
|
174
170
|
|
175
171
|
该方法直接尝试将补丁应用到目标文件,适用于简单、明确的修改场景。
|
@@ -182,7 +178,6 @@ class EditFileHandler(OutputHandler):
|
|
182
178
|
Args:
|
183
179
|
file_path: 要修改的文件路径,支持绝对路径和相对路径
|
184
180
|
patches: 补丁列表,每个补丁包含search(搜索文本)和replace(替换文本)
|
185
|
-
spinner: 进度显示对象,用于显示处理状态和结果
|
186
181
|
|
187
182
|
Returns:
|
188
183
|
Tuple[bool, str]:
|
@@ -207,17 +202,22 @@ class EditFileHandler(OutputHandler):
|
|
207
202
|
replace_text = patch["REPLACE"]
|
208
203
|
patch_count += 1
|
209
204
|
|
210
|
-
|
211
|
-
|
205
|
+
# 精确匹配搜索文本(保留原始换行和空格)
|
206
|
+
exact_search = search_text
|
207
|
+
|
208
|
+
if exact_search in modified_content:
|
209
|
+
if modified_content.count(exact_search) > 1:
|
212
210
|
PrettyOutput.print(
|
213
|
-
f"搜索文本在文件中存在多处匹配:\n{
|
211
|
+
f"搜索文本在文件中存在多处匹配:\n{exact_search}",
|
214
212
|
output_type=OutputType.WARNING,
|
215
213
|
)
|
216
|
-
return False, f"搜索文本在文件中存在多处匹配:\n{
|
214
|
+
return False, f"搜索文本在文件中存在多处匹配:\n{exact_search}"
|
215
|
+
|
216
|
+
# 直接执行替换(保留所有原始格式)
|
217
217
|
modified_content = modified_content.replace(
|
218
|
-
|
218
|
+
exact_search, replace_text
|
219
219
|
)
|
220
|
-
|
220
|
+
print(f"✅ 补丁 #{patch_count} 应用成功")
|
221
221
|
else:
|
222
222
|
# 尝试增加缩进重试
|
223
223
|
found = False
|
@@ -243,7 +243,7 @@ class EditFileHandler(OutputHandler):
|
|
243
243
|
modified_content = modified_content.replace(
|
244
244
|
indented_search, indented_replace
|
245
245
|
)
|
246
|
-
|
246
|
+
print(
|
247
247
|
f"✅ 补丁 #{patch_count} 应用成功 (自动增加 {space_count} 个空格缩进)"
|
248
248
|
)
|
249
249
|
found = True
|
@@ -260,19 +260,17 @@ class EditFileHandler(OutputHandler):
|
|
260
260
|
with open(file_path, "w", encoding="utf-8") as f:
|
261
261
|
f.write(modified_content)
|
262
262
|
|
263
|
-
|
264
|
-
spinner.ok("✅")
|
263
|
+
print(f"✅ 文件 {file_path} 修改完成,应用了 {patch_count} 个补丁")
|
265
264
|
return True, modified_content
|
266
265
|
|
267
266
|
except Exception as e:
|
268
|
-
|
269
|
-
spinner.fail("❌")
|
267
|
+
print(f"❌ 文件修改失败: {str(e)}")
|
270
268
|
revert_file(file_path)
|
271
269
|
return False, f"文件修改失败: {str(e)}"
|
272
270
|
|
273
271
|
@staticmethod
|
274
272
|
def _slow_edit(
|
275
|
-
file_path: str, patches: List[Dict[str, str]],
|
273
|
+
file_path: str, patches: List[Dict[str, str]], agent: Any
|
276
274
|
) -> Tuple[bool, str]:
|
277
275
|
"""使用AI模型生成补丁并应用到文件
|
278
276
|
|
@@ -287,7 +285,6 @@ class EditFileHandler(OutputHandler):
|
|
287
285
|
Args:
|
288
286
|
file_path: 要修改的文件路径,支持绝对路径和相对路径
|
289
287
|
patches: 补丁列表,每个补丁包含search(搜索文本)和replace(替换文本)
|
290
|
-
spinner: 进度显示对象,用于显示处理状态和结果
|
291
288
|
agent: 执行处理的agent实例,用于访问AI模型平台
|
292
289
|
|
293
290
|
Returns:
|
@@ -308,15 +305,14 @@ class EditFileHandler(OutputHandler):
|
|
308
305
|
upload_success = False
|
309
306
|
|
310
307
|
# 如果是大文件,尝试上传到模型平台
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
upload_success = True
|
308
|
+
if (
|
309
|
+
is_large_context
|
310
|
+
and model.support_upload_files()
|
311
|
+
and model.upload_files([file_path])
|
312
|
+
):
|
313
|
+
upload_success = True
|
318
314
|
|
319
|
-
model.set_suppress_output(
|
315
|
+
model.set_suppress_output(False)
|
320
316
|
|
321
317
|
# 构建补丁内容
|
322
318
|
patch_content = []
|
@@ -387,8 +383,9 @@ class EditFileHandler(OutputHandler):
|
|
387
383
|
# 检查是否被中断
|
388
384
|
if get_interrupt():
|
389
385
|
set_interrupt(False)
|
390
|
-
|
391
|
-
|
386
|
+
user_input = agent.multiline_inputer(
|
387
|
+
"补丁应用被中断,请输入补充信息:"
|
388
|
+
)
|
392
389
|
if not user_input.strip():
|
393
390
|
return False, "用户中断了补丁应用"
|
394
391
|
return False, f"用户中断了补丁应用并提供了补充信息: {user_input}"
|
@@ -422,7 +419,7 @@ class EditFileHandler(OutputHandler):
|
|
422
419
|
if generated_patches:
|
423
420
|
# 尝试应用生成的补丁
|
424
421
|
success, result = EditFileHandler._fast_edit(
|
425
|
-
file_path, generated_patches
|
422
|
+
file_path, generated_patches
|
426
423
|
)
|
427
424
|
if success:
|
428
425
|
return True, result
|
@@ -430,7 +427,6 @@ class EditFileHandler(OutputHandler):
|
|
430
427
|
return False, "AI模型无法生成有效的补丁"
|
431
428
|
|
432
429
|
except Exception as e:
|
433
|
-
|
434
|
-
spinner.fail("❌")
|
430
|
+
print(f"❌ 文件修改失败: {str(e)}")
|
435
431
|
revert_file(file_path)
|
436
432
|
return False, f"文件修改失败: {str(e)}"
|
jarvis/jarvis_agent/jarvis.py
CHANGED
@@ -4,13 +4,17 @@ import os
|
|
4
4
|
import sys
|
5
5
|
from typing import Dict
|
6
6
|
|
7
|
-
import yaml
|
8
|
-
from prompt_toolkit import prompt
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
import yaml # type: ignore
|
8
|
+
from prompt_toolkit import prompt # type: ignore
|
9
|
+
|
10
|
+
from jarvis.jarvis_agent import (
|
11
|
+
Agent,
|
12
|
+
OutputType,
|
13
|
+
PrettyOutput,
|
14
|
+
get_multiline_input,
|
15
|
+
origin_agent_system_prompt,
|
16
|
+
user_confirm,
|
17
|
+
)
|
14
18
|
from jarvis.jarvis_agent.builtin_input_handler import builtin_input_handler
|
15
19
|
from jarvis.jarvis_agent.shell_input_handler import shell_input_handler
|
16
20
|
from jarvis.jarvis_tools.registry import ToolRegistry
|
@@ -26,43 +30,33 @@ def _load_tasks() -> Dict[str, str]:
|
|
26
30
|
data_dir = get_data_dir()
|
27
31
|
pre_command_path = os.path.join(data_dir, "pre-command")
|
28
32
|
if os.path.exists(pre_command_path):
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
spinner.text = f"预定义任务加载完成{pre_command_path}"
|
41
|
-
spinner.ok("✅")
|
42
|
-
except (yaml.YAMLError, OSError):
|
43
|
-
spinner.text = f"预定义任务加载失败{pre_command_path}"
|
44
|
-
spinner.fail("❌")
|
33
|
+
print(f"🔍 从{pre_command_path}加载预定义任务...")
|
34
|
+
try:
|
35
|
+
with open(pre_command_path, "r", encoding="utf-8", errors="ignore") as f:
|
36
|
+
user_tasks = yaml.safe_load(f)
|
37
|
+
if isinstance(user_tasks, dict):
|
38
|
+
for name, desc in user_tasks.items():
|
39
|
+
if desc:
|
40
|
+
tasks[str(name)] = str(desc)
|
41
|
+
print(f"✅ 预定义任务加载完成 {pre_command_path}")
|
42
|
+
except (yaml.YAMLError, OSError):
|
43
|
+
print(f"❌ 预定义任务加载失败 {pre_command_path}")
|
45
44
|
|
46
45
|
# Check .jarvis/pre-command in current directory
|
47
46
|
pre_command_path = ".jarvis/pre-command"
|
48
47
|
if os.path.exists(pre_command_path):
|
49
48
|
abs_path = os.path.abspath(pre_command_path)
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
spinner.text = f"预定义任务加载完成{pre_command_path}"
|
62
|
-
spinner.ok("✅")
|
63
|
-
except (yaml.YAMLError, OSError):
|
64
|
-
spinner.text = f"预定义任务加载失败{pre_command_path}"
|
65
|
-
spinner.fail("❌")
|
49
|
+
print(f"🔍 从{abs_path}加载预定义任务...")
|
50
|
+
try:
|
51
|
+
with open(pre_command_path, "r", encoding="utf-8", errors="ignore") as f:
|
52
|
+
local_tasks = yaml.safe_load(f)
|
53
|
+
if isinstance(local_tasks, dict):
|
54
|
+
for name, desc in local_tasks.items():
|
55
|
+
if desc:
|
56
|
+
tasks[str(name)] = str(desc)
|
57
|
+
print(f"✅ 预定义任务加载完成 {pre_command_path}")
|
58
|
+
except (yaml.YAMLError, OSError):
|
59
|
+
print(f"❌ 预定义任务加载失败 {pre_command_path}")
|
66
60
|
|
67
61
|
return tasks
|
68
62
|
|