jarvis-ai-assistant 0.1.126__tar.gz → 0.1.128__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.
Potentially problematic release.
This version of jarvis-ai-assistant might be problematic. Click here for more details.
- {jarvis_ai_assistant-0.1.126/src/jarvis_ai_assistant.egg-info → jarvis_ai_assistant-0.1.128}/PKG-INFO +1 -1
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/pyproject.toml +1 -1
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/setup.py +14 -1
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/__init__.py +1 -1
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_agent/__init__.py +105 -87
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_code_agent/code_agent.py +21 -10
- jarvis_ai_assistant-0.1.128/src/jarvis/jarvis_code_agent/patch.py +275 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_codebase/main.py +240 -213
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_dev/main.py +4 -3
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_platform/base.py +6 -5
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_platform_manager/main.py +1 -1
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_rag/main.py +250 -186
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_smart_shell/main.py +0 -1
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/ask_codebase.py +4 -3
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/chdir.py +22 -22
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/code_review.py +38 -33
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/execute_shell.py +0 -3
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/file_operation.py +56 -55
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/git_commiter.py +60 -50
- jarvis_ai_assistant-0.1.128/src/jarvis/jarvis_tools/read_webpage.py +112 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/registry.py +40 -53
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/search_web.py +61 -36
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/tool_generator.py +35 -21
- jarvis_ai_assistant-0.1.128/src/jarvis/jarvis_utils/methodology.py +146 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_utils/output.py +1 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128/src/jarvis_ai_assistant.egg-info}/PKG-INFO +1 -1
- jarvis_ai_assistant-0.1.126/src/jarvis/jarvis_code_agent/patch.py +0 -248
- jarvis_ai_assistant-0.1.126/src/jarvis/jarvis_tools/read_webpage.py +0 -92
- jarvis_ai_assistant-0.1.126/src/jarvis/jarvis_utils/methodology.py +0 -128
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/MANIFEST.in +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/README.md +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/setup.cfg +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_agent/output_handler.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_code_agent/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_code_agent/file_select.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_code_agent/shell_input_handler.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_codebase/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_git_squash/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_git_squash/main.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_lsp/base.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_lsp/cpp.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_lsp/go.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_lsp/python.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_lsp/registry.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_lsp/rust.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_multi_agent/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_platform/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_platform/ai8.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_platform/kimi.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_platform/ollama.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_platform/openai.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_platform/oyi.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_platform/registry.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_platform_manager/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_platform_manager/openai_test.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_rag/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_smart_shell/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/ask_user.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/base.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/create_code_agent.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/create_sub_agent.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/execute_shell_script.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/lsp_find_definition.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/lsp_find_references.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/lsp_get_diagnostics.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/lsp_get_document_symbols.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/lsp_prepare_rename.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/methodology.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/rag.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_tools/select_code_files.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_utils/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_utils/config.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_utils/embedding.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_utils/git_utils.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_utils/globals.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_utils/input.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_utils/utils.py +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis_ai_assistant.egg-info/SOURCES.txt +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis_ai_assistant.egg-info/dependency_links.txt +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis_ai_assistant.egg-info/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis_ai_assistant.egg-info/requires.txt +0 -0
- {jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis_ai_assistant.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "jarvis-ai-assistant"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.128"
|
|
8
8
|
description = "Jarvis: An AI assistant that uses tools to interact with the system"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "skyfire", email = "skyfireitdiy@hotmail.com" }]
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
from setuptools import setup, find_packages
|
|
2
|
+
from setuptools.command.install import install
|
|
3
|
+
import subprocess
|
|
4
|
+
|
|
5
|
+
# 自定义安装命令
|
|
6
|
+
class CustomInstallCommand(install):
|
|
7
|
+
def run(self):
|
|
8
|
+
# 先运行默认的安装逻辑
|
|
9
|
+
install.run(self)
|
|
10
|
+
# 安装完成后执行 playwright install
|
|
11
|
+
subprocess.check_call(["playwright", "install"])
|
|
2
12
|
|
|
3
13
|
setup(
|
|
4
14
|
name="jarvis-ai-assistant",
|
|
5
|
-
version="0.1.
|
|
15
|
+
version="0.1.128",
|
|
6
16
|
author="skyfire",
|
|
7
17
|
author_email="skyfireitdiy@hotmail.com",
|
|
8
18
|
description="An AI assistant that uses various tools to interact with the system",
|
|
@@ -67,4 +77,7 @@ setup(
|
|
|
67
77
|
"Programming Language :: Python :: 3.10",
|
|
68
78
|
"Programming Language :: Python :: 3.11",
|
|
69
79
|
],
|
|
80
|
+
cmdclass={
|
|
81
|
+
'install': CustomInstallCommand, # 注册自定义安装命令
|
|
82
|
+
},
|
|
70
83
|
)
|
{jarvis_ai_assistant-0.1.126 → jarvis_ai_assistant-0.1.128}/src/jarvis/jarvis_agent/__init__.py
RENAMED
|
@@ -3,6 +3,7 @@ from typing import Any, Callable, List, Optional, Tuple, Union
|
|
|
3
3
|
|
|
4
4
|
from prompt_toolkit import prompt
|
|
5
5
|
import yaml
|
|
6
|
+
from yaspin import yaspin
|
|
6
7
|
|
|
7
8
|
from jarvis.jarvis_agent.output_handler import OutputHandler
|
|
8
9
|
from jarvis.jarvis_platform.base import BasePlatform
|
|
@@ -66,9 +67,9 @@ class Agent:
|
|
|
66
67
|
if model_name is not None:
|
|
67
68
|
self.model.set_model_name(model_name)
|
|
68
69
|
|
|
70
|
+
self.model.set_suppress_output(False)
|
|
69
71
|
|
|
70
72
|
self.output_handler = output_handler
|
|
71
|
-
|
|
72
73
|
|
|
73
74
|
self.record_methodology = record_methodology if record_methodology is not None else is_record_methodology()
|
|
74
75
|
self.use_methodology = use_methodology if use_methodology is not None else is_use_methodology()
|
|
@@ -165,6 +166,7 @@ class Agent:
|
|
|
165
166
|
message, need_return = handler(message, self)
|
|
166
167
|
if need_return:
|
|
167
168
|
return message
|
|
169
|
+
PrettyOutput.section("模型输出", OutputType.SYSTEM)
|
|
168
170
|
return self.model.chat_until_success(message) # type: ignore
|
|
169
171
|
|
|
170
172
|
|
|
@@ -184,35 +186,38 @@ class Agent:
|
|
|
184
186
|
"""
|
|
185
187
|
# Create a new model instance to summarize, avoid affecting the main conversation
|
|
186
188
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
prompt = """请总结之前对话中的关键信息,包括:
|
|
190
|
-
1. 当前任务目标
|
|
191
|
-
2. 已确认的关键信息
|
|
192
|
-
3. 已尝试的解决方案
|
|
193
|
-
4. 当前进展
|
|
194
|
-
5. 待解决的问题
|
|
195
|
-
|
|
196
|
-
请用简洁的要点形式描述,突出重要信息。不要包含对话细节。
|
|
197
|
-
"""
|
|
198
|
-
|
|
199
|
-
try:
|
|
200
|
-
summary = self._call_model(self.prompt + "\n" + prompt)
|
|
189
|
+
with yaspin(text="正在总结对话历史...", color="cyan") as spinner:
|
|
201
190
|
|
|
202
|
-
|
|
203
|
-
|
|
191
|
+
prompt = """请总结之前对话中的关键信息,包括:
|
|
192
|
+
1. 当前任务目标
|
|
193
|
+
2. 已确认的关键信息
|
|
194
|
+
3. 已尝试的解决方案
|
|
195
|
+
4. 当前进展
|
|
196
|
+
5. 待解决的问题
|
|
197
|
+
|
|
198
|
+
请用简洁的要点形式描述,突出重要信息。不要包含对话细节。
|
|
199
|
+
"""
|
|
204
200
|
|
|
205
|
-
|
|
206
|
-
|
|
201
|
+
try:
|
|
202
|
+
with spinner.hidden():
|
|
203
|
+
summary = self._call_model(self.prompt + "\n" + prompt)
|
|
204
|
+
|
|
205
|
+
# 清空当前对话历史,但保留系统消息
|
|
206
|
+
self.conversation_length = 0 # Reset conversation length
|
|
207
|
+
|
|
208
|
+
# 添加总结作为新的上下文
|
|
209
|
+
self.prompt = f"""以下是之前对话的关键信息总结:
|
|
207
210
|
|
|
208
|
-
{summary}
|
|
211
|
+
{summary}
|
|
209
212
|
|
|
210
|
-
请基于以上信息继续完成任务。
|
|
211
|
-
"""
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
213
|
+
请基于以上信息继续完成任务。
|
|
214
|
+
"""
|
|
215
|
+
self.conversation_length = len(self.prompt) # 设置新的起始长度
|
|
216
|
+
spinner.text = "总结对话历史完成"
|
|
217
|
+
spinner.ok("✅")
|
|
218
|
+
except Exception as e:
|
|
219
|
+
spinner.text = "总结对话历史失败"
|
|
220
|
+
spinner.fail("❌")
|
|
216
221
|
|
|
217
222
|
def _call_tools(self, response: str) -> Tuple[bool, Any]:
|
|
218
223
|
tool_list = []
|
|
@@ -225,7 +230,12 @@ class Agent:
|
|
|
225
230
|
if len(tool_list) == 0:
|
|
226
231
|
return False, ""
|
|
227
232
|
if not self.execute_tool_confirm or user_confirm(f"需要执行{tool_list[0].name()}确认执行?", True):
|
|
228
|
-
|
|
233
|
+
with yaspin(text=f"正在执行{tool_list[0].name()}...", color="cyan") as spinner:
|
|
234
|
+
with spinner.hidden():
|
|
235
|
+
result = tool_list[0].handle(response)
|
|
236
|
+
spinner.text = f"{tool_list[0].name()}执行完成"
|
|
237
|
+
spinner.ok("✅")
|
|
238
|
+
return result
|
|
229
239
|
return False, ""
|
|
230
240
|
|
|
231
241
|
|
|
@@ -239,33 +249,40 @@ class Agent:
|
|
|
239
249
|
- For main agent: May generate methodology if enabled
|
|
240
250
|
- For sub-agent: May generate summary if enabled
|
|
241
251
|
"""
|
|
242
|
-
PrettyOutput.section("任务完成", OutputType.SUCCESS)
|
|
243
|
-
|
|
244
252
|
if not self.is_sub_agent:
|
|
245
253
|
if self.record_methodology:
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
254
|
+
with yaspin(text="正在生成方法论...", color="cyan") as spinner:
|
|
255
|
+
try:
|
|
256
|
+
|
|
257
|
+
# 让模型判断是否需要生成方法论
|
|
258
|
+
analysis_prompt = """当前任务已结束,请分析是否需要生成方法论。
|
|
259
|
+
如果你认为需要生成方法论,请先确定是创建新方法论还是更新现有方法论。如果是更新现有方法论,请使用'update',否则使用'add'。
|
|
260
|
+
如果你认为不需要方法论,请解释原因。
|
|
261
|
+
方法论应适用于通用场景,不要包含任务特定信息,如代码提交信息等。
|
|
262
|
+
方法论应包含:问题重述、最优解决方案、注意事项(如有),除此之外不要包含其他内容。
|
|
263
|
+
只输出方法论工具调用指令,或不生成方法论的解释。不要输出其他内容。
|
|
264
|
+
"""
|
|
265
|
+
self.prompt = analysis_prompt
|
|
266
|
+
with spinner.hidden():
|
|
267
|
+
response = self._call_model(self.prompt)
|
|
268
|
+
|
|
269
|
+
with spinner.hidden():
|
|
270
|
+
self._call_tools(response)
|
|
271
|
+
spinner.text = "方法论生成完成"
|
|
272
|
+
spinner.ok("✅")
|
|
273
|
+
except Exception as e:
|
|
274
|
+
spinner.text = "方法论生成失败"
|
|
275
|
+
spinner.fail("❌")
|
|
264
276
|
return "任务完成"
|
|
265
277
|
|
|
266
278
|
if self.need_summary:
|
|
267
|
-
|
|
268
|
-
|
|
279
|
+
with yaspin(text="正在生成总结...", color="cyan") as spinner:
|
|
280
|
+
self.prompt = self.summary_prompt
|
|
281
|
+
with spinner.hidden():
|
|
282
|
+
ret = self._call_model(self.prompt)
|
|
283
|
+
spinner.text = "总结生成完成"
|
|
284
|
+
spinner.ok("✅")
|
|
285
|
+
return ret
|
|
269
286
|
|
|
270
287
|
return "任务完成"
|
|
271
288
|
|
|
@@ -282,13 +299,12 @@ class Agent:
|
|
|
282
299
|
"""
|
|
283
300
|
try:
|
|
284
301
|
set_agent(self.name, self)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
302
|
+
with yaspin(text="准备环境...", color="cyan") as spinner:
|
|
303
|
+
if file_list:
|
|
304
|
+
self.model.upload_files(file_list) # type: ignore
|
|
305
|
+
spinner.text = "环境准备完成"
|
|
306
|
+
spinner.ok("✅")
|
|
307
|
+
|
|
292
308
|
self.prompt = f"{user_input}"
|
|
293
309
|
|
|
294
310
|
if self.first:
|
|
@@ -298,9 +314,6 @@ class Agent:
|
|
|
298
314
|
|
|
299
315
|
while True:
|
|
300
316
|
try:
|
|
301
|
-
# 显示思考状态
|
|
302
|
-
PrettyOutput.print("正在分析任务...", OutputType.PROGRESS)
|
|
303
|
-
|
|
304
317
|
# 累加对话长度
|
|
305
318
|
self.conversation_length += get_context_token_count(self.prompt)
|
|
306
319
|
|
|
@@ -360,41 +373,46 @@ class Agent:
|
|
|
360
373
|
def _load_tasks() -> dict:
|
|
361
374
|
"""Load tasks from .jarvis files in user home and current directory."""
|
|
362
375
|
tasks = {}
|
|
363
|
-
|
|
376
|
+
|
|
364
377
|
# Check .jarvis/pre-command in user directory
|
|
365
378
|
user_jarvis = os.path.expanduser("~/.jarvis/pre-command")
|
|
366
379
|
if os.path.exists(user_jarvis):
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
380
|
+
with yaspin(text=f"从{user_jarvis}加载预定义任务...", color="cyan") as spinner:
|
|
381
|
+
try:
|
|
382
|
+
with open(user_jarvis, "r", encoding="utf-8") as f:
|
|
383
|
+
user_tasks = yaml.safe_load(f)
|
|
384
|
+
|
|
385
|
+
if isinstance(user_tasks, dict):
|
|
386
|
+
# Validate and add user directory tasks
|
|
387
|
+
for name, desc in user_tasks.items():
|
|
388
|
+
if desc: # Ensure description is not empty
|
|
389
|
+
tasks[str(name)] = str(desc)
|
|
390
|
+
spinner.text = "预定义任务加载完成"
|
|
391
|
+
spinner.ok("✅")
|
|
392
|
+
except Exception as e:
|
|
393
|
+
spinner.text = "预定义任务加载失败"
|
|
394
|
+
spinner.fail("❌")
|
|
395
|
+
|
|
381
396
|
# Check .jarvis/pre-command in current directory
|
|
382
397
|
if os.path.exists(".jarvis/pre-command"):
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
398
|
+
with yaspin(text=f"从{os.path.abspath('.jarvis/pre-command')}加载预定义任务...", color="cyan") as spinner:
|
|
399
|
+
try:
|
|
400
|
+
with open(".jarvis/pre-command", "r", encoding="utf-8") as f:
|
|
401
|
+
local_tasks = yaml.safe_load(f)
|
|
402
|
+
|
|
403
|
+
if isinstance(local_tasks, dict):
|
|
404
|
+
# Validate and add current directory tasks, overwrite user directory tasks if there is a name conflict
|
|
405
|
+
for name, desc in local_tasks.items():
|
|
406
|
+
if desc: # Ensure description is not empty
|
|
407
|
+
tasks[str(name)] = str(desc)
|
|
408
|
+
spinner.text = "预定义任务加载完成"
|
|
409
|
+
spinner.ok("✅")
|
|
410
|
+
except Exception as e:
|
|
411
|
+
spinner.text = "预定义任务加载失败"
|
|
412
|
+
spinner.fail("❌")
|
|
396
413
|
|
|
397
414
|
return tasks
|
|
415
|
+
|
|
398
416
|
def _select_task(tasks: dict) -> str:
|
|
399
417
|
"""Let user select a task from the list or skip. Returns task description if selected."""
|
|
400
418
|
if not tasks:
|
|
@@ -3,6 +3,8 @@ import subprocess
|
|
|
3
3
|
import os
|
|
4
4
|
from typing import Any, Tuple
|
|
5
5
|
|
|
6
|
+
from yaspin import yaspin
|
|
7
|
+
|
|
6
8
|
from jarvis.jarvis_agent import Agent
|
|
7
9
|
from jarvis.jarvis_code_agent.shell_input_handler import shell_input_handler
|
|
8
10
|
from jarvis.jarvis_code_agent.patch import PatchOutputHandler
|
|
@@ -83,12 +85,15 @@ def file_input_handler(user_input: str, agent: Any) -> Tuple[str, bool]:
|
|
|
83
85
|
"start_line": 1, # 1-based
|
|
84
86
|
"end_line": -1
|
|
85
87
|
})
|
|
86
|
-
|
|
88
|
+
|
|
87
89
|
# Read and process files if any were found
|
|
88
90
|
if files:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
with yaspin(text="正在读取文件...", color="cyan") as spinner:
|
|
92
|
+
result = FileOperationTool().execute({"operation":"read","files": files})
|
|
93
|
+
if result["success"]:
|
|
94
|
+
spinner.text = "文件读取完成"
|
|
95
|
+
spinner.ok("✅")
|
|
96
|
+
return result["stdout"] + "\n" + prompt, False
|
|
92
97
|
|
|
93
98
|
return prompt, False
|
|
94
99
|
|
|
@@ -209,12 +214,17 @@ class CodeAgent:
|
|
|
209
214
|
|
|
210
215
|
|
|
211
216
|
def _init_env(self):
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
217
|
+
with yaspin(text="正在初始化环境...", color="cyan") as spinner:
|
|
218
|
+
curr_dir = os.getcwd()
|
|
219
|
+
git_dir = find_git_root(curr_dir)
|
|
220
|
+
self.root_dir = git_dir
|
|
221
|
+
if has_uncommitted_changes():
|
|
222
|
+
with spinner.hidden():
|
|
223
|
+
git_commiter = GitCommitTool()
|
|
224
|
+
git_commiter.execute({})
|
|
225
|
+
else:
|
|
226
|
+
spinner.text = "环境初始化完成"
|
|
227
|
+
spinner.ok("✅")
|
|
218
228
|
|
|
219
229
|
|
|
220
230
|
|
|
@@ -229,6 +239,7 @@ class CodeAgent:
|
|
|
229
239
|
"""
|
|
230
240
|
try:
|
|
231
241
|
self._init_env()
|
|
242
|
+
|
|
232
243
|
start_commit = get_latest_commit_hash()
|
|
233
244
|
|
|
234
245
|
try:
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from typing import Dict, Any, Tuple
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from yaspin import yaspin
|
|
6
|
+
|
|
7
|
+
from jarvis.jarvis_agent.output_handler import OutputHandler
|
|
8
|
+
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
9
|
+
from jarvis.jarvis_tools.git_commiter import GitCommitTool
|
|
10
|
+
from jarvis.jarvis_tools.execute_shell_script import ShellScriptTool
|
|
11
|
+
from jarvis.jarvis_tools.file_operation import FileOperationTool
|
|
12
|
+
from jarvis.jarvis_utils.config import is_confirm_before_apply_patch
|
|
13
|
+
from jarvis.jarvis_utils.git_utils import get_commits_between, get_latest_commit_hash
|
|
14
|
+
from jarvis.jarvis_utils.input import get_multiline_input
|
|
15
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
16
|
+
from jarvis.jarvis_utils.utils import user_confirm
|
|
17
|
+
|
|
18
|
+
class PatchOutputHandler(OutputHandler):
|
|
19
|
+
def name(self) -> str:
|
|
20
|
+
return "PATCH"
|
|
21
|
+
def handle(self, response: str) -> Tuple[bool, Any]:
|
|
22
|
+
return False, apply_patch(response)
|
|
23
|
+
|
|
24
|
+
def can_handle(self, response: str) -> bool:
|
|
25
|
+
if _parse_patch(response):
|
|
26
|
+
return True
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
def prompt(self) -> str:
|
|
30
|
+
return """
|
|
31
|
+
# 🛠️ 上下文代码补丁规范
|
|
32
|
+
使用<PATCH>块来指定代码更改:
|
|
33
|
+
--------------------------------
|
|
34
|
+
<PATCH>
|
|
35
|
+
File: [文件路径]
|
|
36
|
+
Reason: [修改原因]
|
|
37
|
+
[上下文代码片段]
|
|
38
|
+
</PATCH>
|
|
39
|
+
--------------------------------
|
|
40
|
+
规则:
|
|
41
|
+
1. 代码片段必须包含足够的上下文(前后各3行)
|
|
42
|
+
2. 我可以看到完整代码,所以只需显示修改的代码部分
|
|
43
|
+
3. 保留原始缩进和格式
|
|
44
|
+
4. 对于新文件,提供完整代码
|
|
45
|
+
5. 修改现有文件时,保留周围未更改的代码
|
|
46
|
+
示例:
|
|
47
|
+
<PATCH>
|
|
48
|
+
File: src/utils/math.py
|
|
49
|
+
Reason: 修复除零处理
|
|
50
|
+
def safe_divide(a, b):
|
|
51
|
+
# 添加参数验证
|
|
52
|
+
if b == 0:
|
|
53
|
+
raise ValueError("除数不能为零")
|
|
54
|
+
return a / b
|
|
55
|
+
# 现有代码 ...
|
|
56
|
+
def add(a, b):
|
|
57
|
+
return a + b
|
|
58
|
+
</PATCH>
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def _parse_patch(patch_str: str) -> Dict[str, str]:
|
|
62
|
+
"""解析新的上下文补丁格式"""
|
|
63
|
+
result = {}
|
|
64
|
+
patches = re.findall(r'<PATCH>\n?(.*?)\n?</PATCH>', patch_str, re.DOTALL)
|
|
65
|
+
if patches:
|
|
66
|
+
for patch in patches:
|
|
67
|
+
first_line = patch.splitlines()[0]
|
|
68
|
+
sm = re.match(r'^File:\s*(.+)$', first_line)
|
|
69
|
+
if not sm:
|
|
70
|
+
PrettyOutput.print("无效的补丁格式", OutputType.WARNING)
|
|
71
|
+
continue
|
|
72
|
+
filepath = sm.group(1).strip()
|
|
73
|
+
result[filepath] = patch
|
|
74
|
+
return result
|
|
75
|
+
|
|
76
|
+
def apply_patch(output_str: str) -> str:
|
|
77
|
+
"""Apply patches to files"""
|
|
78
|
+
with yaspin(text="正在应用补丁...", color="cyan") as spinner:
|
|
79
|
+
try:
|
|
80
|
+
patches = _parse_patch(output_str)
|
|
81
|
+
except Exception as e:
|
|
82
|
+
PrettyOutput.print(f"解析补丁失败: {str(e)}", OutputType.ERROR)
|
|
83
|
+
return ""
|
|
84
|
+
|
|
85
|
+
# 获取当前提交hash作为起始点
|
|
86
|
+
spinner.text= "开始获取当前提交hash..."
|
|
87
|
+
start_hash = get_latest_commit_hash()
|
|
88
|
+
spinner.write("✅ 当前提交hash获取完成")
|
|
89
|
+
|
|
90
|
+
# 按文件逐个处理
|
|
91
|
+
for filepath, patch_content in patches.items():
|
|
92
|
+
try:
|
|
93
|
+
spinner.text = f"正在处理文件: {filepath}"
|
|
94
|
+
with spinner.hidden():
|
|
95
|
+
handle_code_operation(filepath, patch_content)
|
|
96
|
+
spinner.write(f"✅ 文件 {filepath} 处理完成")
|
|
97
|
+
except Exception as e:
|
|
98
|
+
spinner.text = f"文件 {filepath} 处理失败: {str(e)}, 回滚文件"
|
|
99
|
+
revert_file(filepath) # 回滚单个文件
|
|
100
|
+
spinner.write(f"✅ 文件 {filepath} 回滚完成")
|
|
101
|
+
|
|
102
|
+
final_ret = ""
|
|
103
|
+
diff = get_diff()
|
|
104
|
+
if diff:
|
|
105
|
+
PrettyOutput.print(diff, OutputType.CODE, lang="diff")
|
|
106
|
+
with spinner.hidden():
|
|
107
|
+
commited = handle_commit_workflow()
|
|
108
|
+
if commited:
|
|
109
|
+
# 获取提交信息
|
|
110
|
+
end_hash = get_latest_commit_hash()
|
|
111
|
+
commits = get_commits_between(start_hash, end_hash)
|
|
112
|
+
|
|
113
|
+
# 添加提交信息到final_ret
|
|
114
|
+
if commits:
|
|
115
|
+
final_ret += "✅ 补丁已应用\n"
|
|
116
|
+
final_ret += "提交信息:\n"
|
|
117
|
+
for commit_hash, commit_message in commits:
|
|
118
|
+
final_ret += f"- {commit_hash[:7]}: {commit_message}\n"
|
|
119
|
+
|
|
120
|
+
final_ret += f"应用补丁:\n{diff}"
|
|
121
|
+
|
|
122
|
+
else:
|
|
123
|
+
final_ret += "✅ 补丁已应用(没有新的提交)"
|
|
124
|
+
else:
|
|
125
|
+
final_ret += "❌ 我不想提交代码\n"
|
|
126
|
+
final_ret += "补丁预览:\n"
|
|
127
|
+
final_ret += diff
|
|
128
|
+
else:
|
|
129
|
+
final_ret += "❌ 没有要提交的更改\n"
|
|
130
|
+
# 用户确认最终结果
|
|
131
|
+
PrettyOutput.print(final_ret, OutputType.USER)
|
|
132
|
+
if not is_confirm_before_apply_patch() or user_confirm("是否使用此回复?", default=True):
|
|
133
|
+
return final_ret
|
|
134
|
+
return get_multiline_input("请输入自定义回复")
|
|
135
|
+
def revert_file(filepath: str):
|
|
136
|
+
"""增强版git恢复,处理新文件"""
|
|
137
|
+
import subprocess
|
|
138
|
+
try:
|
|
139
|
+
# 检查文件是否在版本控制中
|
|
140
|
+
result = subprocess.run(
|
|
141
|
+
['git', 'ls-files', '--error-unmatch', filepath],
|
|
142
|
+
stderr=subprocess.PIPE
|
|
143
|
+
)
|
|
144
|
+
if result.returncode == 0:
|
|
145
|
+
subprocess.run(['git', 'checkout', 'HEAD', '--', filepath], check=True)
|
|
146
|
+
else:
|
|
147
|
+
if os.path.exists(filepath):
|
|
148
|
+
os.remove(filepath)
|
|
149
|
+
subprocess.run(['git', 'clean', '-f', '--', filepath], check=True)
|
|
150
|
+
except subprocess.CalledProcessError as e:
|
|
151
|
+
PrettyOutput.print(f"恢复文件失败: {str(e)}", OutputType.ERROR)
|
|
152
|
+
# 修改后的恢复函数
|
|
153
|
+
def revert_change():
|
|
154
|
+
import subprocess
|
|
155
|
+
subprocess.run(['git', 'reset', '--hard', 'HEAD'], check=True)
|
|
156
|
+
subprocess.run(['git', 'clean', '-fd'], check=True)
|
|
157
|
+
# 修改后的获取差异函数
|
|
158
|
+
def get_diff() -> str:
|
|
159
|
+
"""使用git获取暂存区差异"""
|
|
160
|
+
import subprocess
|
|
161
|
+
try:
|
|
162
|
+
subprocess.run(['git', 'add', '.'], check=True)
|
|
163
|
+
result = subprocess.run(
|
|
164
|
+
['git', 'diff', '--cached'],
|
|
165
|
+
capture_output=True,
|
|
166
|
+
text=True,
|
|
167
|
+
check=True
|
|
168
|
+
)
|
|
169
|
+
ret = result.stdout
|
|
170
|
+
subprocess.run(['git', "reset", "--soft", "HEAD"], check=True)
|
|
171
|
+
return ret
|
|
172
|
+
except subprocess.CalledProcessError as e:
|
|
173
|
+
return f"获取差异失败: {str(e)}"
|
|
174
|
+
def handle_commit_workflow()->bool:
|
|
175
|
+
"""Handle the git commit workflow and return the commit details.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
tuple[bool, str, str]: (continue_execution, commit_id, commit_message)
|
|
179
|
+
"""
|
|
180
|
+
if is_confirm_before_apply_patch() and not user_confirm("是否要提交代码?", default=True):
|
|
181
|
+
revert_change()
|
|
182
|
+
return False
|
|
183
|
+
git_commiter = GitCommitTool()
|
|
184
|
+
commit_result = git_commiter.execute({})
|
|
185
|
+
return commit_result["success"]
|
|
186
|
+
|
|
187
|
+
# New handler functions below ▼▼▼
|
|
188
|
+
def handle_code_operation(filepath: str, patch_content: str) -> bool:
|
|
189
|
+
"""处理基于上下文的代码片段"""
|
|
190
|
+
with yaspin(text=f"正在修改文件 {filepath}...", color="cyan") as spinner:
|
|
191
|
+
try:
|
|
192
|
+
if not os.path.exists(filepath):
|
|
193
|
+
# 新建文件
|
|
194
|
+
spinner.text = "文件不存在,正在创建文件..."
|
|
195
|
+
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
|
196
|
+
open(filepath, 'w', encoding='utf-8').close()
|
|
197
|
+
spinner.write("✅ 文件创建完成")
|
|
198
|
+
old_file_content = FileOperationTool().execute({"operation": "read", "files": [{"path": filepath}]})
|
|
199
|
+
if not old_file_content["success"]:
|
|
200
|
+
spinner.write("❌ 文件读取失败")
|
|
201
|
+
return False
|
|
202
|
+
|
|
203
|
+
prompt = f"""
|
|
204
|
+
你是一个代码审查员,请审查以下代码并将其与上下文合并。
|
|
205
|
+
原始代码:
|
|
206
|
+
{old_file_content["stdout"]}
|
|
207
|
+
补丁内容:
|
|
208
|
+
{patch_content}
|
|
209
|
+
"""
|
|
210
|
+
prompt += f"""
|
|
211
|
+
请将代码与上下文合并并返回完整的合并代码,每次最多输出300行代码。
|
|
212
|
+
|
|
213
|
+
要求:
|
|
214
|
+
1. 严格保留原始代码的格式、空行和缩进
|
|
215
|
+
2. 仅在<MERGED_CODE>块中包含实际代码内容,包括空行和缩进
|
|
216
|
+
3. 绝对不要使用markdown代码块(```)或反引号,除非修改的是markdown文件
|
|
217
|
+
4. 除了合并后的代码,不要输出任何其他文本
|
|
218
|
+
5. 所有代码输出完成后,输出<!!!FINISHED!!!>
|
|
219
|
+
|
|
220
|
+
输出格式:
|
|
221
|
+
<MERGED_CODE>
|
|
222
|
+
[merged_code]
|
|
223
|
+
</MERGED_CODE>
|
|
224
|
+
"""
|
|
225
|
+
PrettyOutput.section("代码生成", OutputType.SYSTEM)
|
|
226
|
+
model = PlatformRegistry().get_codegen_platform()
|
|
227
|
+
model.set_suppress_output(False)
|
|
228
|
+
count = 30
|
|
229
|
+
start_line = -1
|
|
230
|
+
end_line = -1
|
|
231
|
+
code = []
|
|
232
|
+
finished = False
|
|
233
|
+
with spinner.hidden():
|
|
234
|
+
while count>0:
|
|
235
|
+
count -= 1
|
|
236
|
+
response = model.chat_until_success(prompt).splitlines()
|
|
237
|
+
try:
|
|
238
|
+
start_line = response.index("<MERGED_CODE>") + 1
|
|
239
|
+
except:
|
|
240
|
+
return False
|
|
241
|
+
try:
|
|
242
|
+
end_line = response.index("</MERGED_CODE>")
|
|
243
|
+
except:
|
|
244
|
+
return False
|
|
245
|
+
code = response[start_line:end_line]
|
|
246
|
+
try:
|
|
247
|
+
response.index("<!!!FINISHED!!!>")
|
|
248
|
+
finished = True
|
|
249
|
+
break
|
|
250
|
+
except:
|
|
251
|
+
prompt += f"""继续输出接下来的300行代码
|
|
252
|
+
要求:
|
|
253
|
+
1. 严格保留原始代码的格式、空行和缩进
|
|
254
|
+
2. 仅在<MERGED_CODE>块中包含实际代码内容,包括空行和缩进
|
|
255
|
+
3. 绝对不要使用markdown代码块(```)或反引号,除非修改的是markdown文件
|
|
256
|
+
4. 除了合并后的代码,不要输出任何其他文本
|
|
257
|
+
5. 所有代码输出完成后,输出<!!!FINISHED!!!>
|
|
258
|
+
"""
|
|
259
|
+
pass
|
|
260
|
+
if not finished:
|
|
261
|
+
spinner.text = "生成代码失败"
|
|
262
|
+
spinner.fail("❌")
|
|
263
|
+
return False
|
|
264
|
+
# 写入合并后的代码
|
|
265
|
+
spinner.text = "写入合并后的代码..."
|
|
266
|
+
with open(filepath, 'w', encoding='utf-8') as f:
|
|
267
|
+
f.write("\n".join(code)+"\n")
|
|
268
|
+
spinner.write("✅ 合并后的代码写入完成")
|
|
269
|
+
spinner.text = "代码修改完成"
|
|
270
|
+
spinner.ok("✅")
|
|
271
|
+
return True
|
|
272
|
+
except Exception as e:
|
|
273
|
+
spinner.text = "代码修改失败"
|
|
274
|
+
spinner.fail("❌")
|
|
275
|
+
return False
|