jarvis-ai-assistant 0.1.102__py3-none-any.whl → 0.1.103__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.
Potentially problematic release.
This version of jarvis-ai-assistant might be problematic. Click here for more details.
- jarvis/__init__.py +1 -1
- jarvis/agent.py +138 -117
- jarvis/jarvis_code_agent/code_agent.py +234 -0
- jarvis/{jarvis_coder → jarvis_code_agent}/file_select.py +16 -17
- jarvis/jarvis_code_agent/patch.py +118 -0
- jarvis/jarvis_code_agent/relevant_files.py +66 -0
- jarvis/jarvis_codebase/main.py +878 -0
- jarvis/jarvis_platform/main.py +5 -3
- jarvis/jarvis_rag/main.py +818 -0
- jarvis/jarvis_smart_shell/main.py +2 -2
- jarvis/models/ai8.py +3 -1
- jarvis/models/kimi.py +36 -30
- jarvis/models/ollama.py +17 -11
- jarvis/models/openai.py +15 -12
- jarvis/models/oyi.py +24 -7
- jarvis/models/registry.py +1 -25
- jarvis/tools/__init__.py +0 -6
- jarvis/tools/ask_codebase.py +99 -0
- jarvis/tools/ask_user.py +1 -9
- jarvis/tools/chdir.py +1 -1
- jarvis/tools/code_review.py +163 -0
- jarvis/tools/create_code_sub_agent.py +19 -45
- jarvis/tools/create_code_test_agent.py +115 -0
- jarvis/tools/create_ctags_agent.py +176 -0
- jarvis/tools/create_sub_agent.py +2 -2
- jarvis/tools/execute_shell.py +2 -2
- jarvis/tools/file_operation.py +2 -2
- jarvis/tools/find_in_codebase.py +108 -0
- jarvis/tools/git_commiter.py +68 -0
- jarvis/tools/methodology.py +3 -3
- jarvis/tools/rag.py +141 -0
- jarvis/tools/read_code.py +147 -0
- jarvis/tools/read_webpage.py +1 -1
- jarvis/tools/registry.py +47 -31
- jarvis/tools/search.py +8 -6
- jarvis/tools/select_code_files.py +4 -4
- jarvis/utils.py +374 -84
- {jarvis_ai_assistant-0.1.102.dist-info → jarvis_ai_assistant-0.1.103.dist-info}/METADATA +52 -2
- jarvis_ai_assistant-0.1.103.dist-info/RECORD +51 -0
- jarvis_ai_assistant-0.1.103.dist-info/entry_points.txt +11 -0
- jarvis/jarvis_code_agent/main.py +0 -200
- jarvis/jarvis_coder/git_utils.py +0 -123
- jarvis/jarvis_coder/patch_handler.py +0 -340
- jarvis/jarvis_github/main.py +0 -232
- jarvis/tools/execute_code_modification.py +0 -70
- jarvis/tools/find_files.py +0 -119
- jarvis/tools/generate_tool.py +0 -174
- jarvis/tools/thinker.py +0 -151
- jarvis_ai_assistant-0.1.102.dist-info/RECORD +0 -46
- jarvis_ai_assistant-0.1.102.dist-info/entry_points.txt +0 -6
- /jarvis/{jarvis_coder → jarvis_codebase}/__init__.py +0 -0
- /jarvis/{jarvis_github → jarvis_rag}/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.102.dist-info → jarvis_ai_assistant-0.1.103.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.102.dist-info → jarvis_ai_assistant-0.1.103.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.102.dist-info → jarvis_ai_assistant-0.1.103.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
jarvis/agent.py
CHANGED
|
@@ -1,22 +1,38 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import time
|
|
3
|
-
from typing import Dict, List, Optional
|
|
3
|
+
from typing import Callable, Dict, List, Optional
|
|
4
4
|
|
|
5
5
|
from prompt_toolkit import prompt
|
|
6
6
|
import yaml
|
|
7
7
|
|
|
8
|
+
from jarvis.models.base import BasePlatform
|
|
8
9
|
from jarvis.models.registry import PlatformRegistry
|
|
9
|
-
from jarvis.tools import ToolRegistry
|
|
10
|
-
from jarvis.
|
|
11
|
-
from jarvis.utils import PrettyOutput, OutputType, get_single_line_input, load_methodology, add_agent, delete_current_agent, get_max_context_length, get_multiline_input, init_env
|
|
10
|
+
from jarvis.tools.registry import ToolRegistry, tool_call_help
|
|
11
|
+
from jarvis.utils import PrettyOutput, OutputType, is_auto_complete, load_methodology, add_agent, delete_current_agent, get_max_context_length, get_multiline_input, init_env
|
|
12
12
|
import os
|
|
13
13
|
|
|
14
14
|
class Agent:
|
|
15
15
|
|
|
16
16
|
def __del__(self):
|
|
17
17
|
delete_current_agent()
|
|
18
|
+
|
|
19
|
+
def set_summary_prompt(self, summary_prompt: str):
|
|
20
|
+
self.summary_prompt = summary_prompt
|
|
21
|
+
|
|
22
|
+
def set_output_filter(self, output_filter: List[Callable]):
|
|
23
|
+
self.output_filter = output_filter
|
|
18
24
|
|
|
19
|
-
def __init__(self,
|
|
25
|
+
def __init__(self,
|
|
26
|
+
system_prompt: str,
|
|
27
|
+
name: str = "Jarvis",
|
|
28
|
+
is_sub_agent: bool = False,
|
|
29
|
+
tool_registry: Optional[ToolRegistry] = None,
|
|
30
|
+
platform: Optional[BasePlatform] = None,
|
|
31
|
+
summary_prompt: Optional[str] = None,
|
|
32
|
+
auto_complete: bool = False,
|
|
33
|
+
record_methodology: bool = True,
|
|
34
|
+
output_filter: Optional[List[Callable]] = None,
|
|
35
|
+
need_summary: bool = True):
|
|
20
36
|
"""Initialize Agent with a model, optional tool registry and name
|
|
21
37
|
|
|
22
38
|
Args:
|
|
@@ -24,25 +40,70 @@ class Agent:
|
|
|
24
40
|
name: Agent name, default is "Jarvis"
|
|
25
41
|
is_sub_agent: Whether it is a sub-agent, default is False
|
|
26
42
|
tool_registry: Tool registry instance
|
|
43
|
+
platform: Optional platform instance, default uses normal platform
|
|
27
44
|
"""
|
|
28
45
|
add_agent(name)
|
|
29
46
|
PrettyOutput.print(f"Welcome to Jarvis, your AI assistant, Initiating...", OutputType.SYSTEM)
|
|
30
|
-
|
|
31
|
-
|
|
47
|
+
if platform is not None:
|
|
48
|
+
self.model = platform
|
|
49
|
+
else:
|
|
50
|
+
self.model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
|
|
51
|
+
self.tool_registry = tool_registry if tool_registry else ToolRegistry()
|
|
52
|
+
self.record_methodology = record_methodology
|
|
32
53
|
self.name = name
|
|
33
54
|
self.is_sub_agent = is_sub_agent
|
|
34
55
|
self.prompt = ""
|
|
35
56
|
self.conversation_length = 0 # Use length counter instead
|
|
36
57
|
self.system_prompt = system_prompt
|
|
58
|
+
self.need_summary = need_summary
|
|
37
59
|
# Load configuration from environment variables
|
|
60
|
+
self.output_filter = output_filter if output_filter else []
|
|
61
|
+
|
|
62
|
+
self.summary_prompt = summary_prompt if summary_prompt else f"""Please generate a concise summary report of the task execution, including:
|
|
63
|
+
|
|
64
|
+
1. Task Objective: Task restatement
|
|
65
|
+
2. Execution Result: Success/Failure
|
|
66
|
+
3. Key Information: Important information extracted during execution
|
|
67
|
+
4. Important Findings: Any noteworthy discoveries
|
|
68
|
+
5. Follow-up Suggestions: If any
|
|
69
|
+
|
|
70
|
+
Please describe in concise bullet points, highlighting important information.
|
|
71
|
+
"""
|
|
38
72
|
|
|
39
73
|
self.max_context_length = get_max_context_length()
|
|
74
|
+
|
|
75
|
+
self.auto_complete = auto_complete
|
|
76
|
+
|
|
77
|
+
|
|
40
78
|
|
|
41
79
|
|
|
42
80
|
# Initialize methodology related attributes
|
|
43
81
|
self.methodology_data = []
|
|
44
82
|
|
|
45
83
|
PrettyOutput.section(f"Jarvis initialized - With {self.model.name()}", OutputType.SYSTEM)
|
|
84
|
+
tools = self.tool_registry.get_all_tools()
|
|
85
|
+
if tools:
|
|
86
|
+
PrettyOutput.section(f"Available tools: {', '.join([tool['name'] for tool in tools])}", OutputType.SYSTEM)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# Load methodology
|
|
90
|
+
|
|
91
|
+
tools_prompt = self.tool_registry.load_tools()
|
|
92
|
+
complete_prompt = """"""
|
|
93
|
+
if self.auto_complete:
|
|
94
|
+
complete_prompt = """
|
|
95
|
+
When the task is completed, you should print the following message:
|
|
96
|
+
<!!!COMPLETE!!!>
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
self.model.set_system_message(f"""
|
|
100
|
+
{self.system_prompt}
|
|
101
|
+
|
|
102
|
+
{tools_prompt}
|
|
103
|
+
|
|
104
|
+
{complete_prompt}
|
|
105
|
+
""")
|
|
106
|
+
self.first = True
|
|
46
107
|
|
|
47
108
|
@staticmethod
|
|
48
109
|
def extract_tool_calls(content: str) -> List[Dict]:
|
|
@@ -52,14 +113,6 @@ class Agent:
|
|
|
52
113
|
tool_call_lines = []
|
|
53
114
|
in_tool_call = False
|
|
54
115
|
|
|
55
|
-
tool_call_help = """Tool Usage Format:
|
|
56
|
-
|
|
57
|
-
<TOOL_CALL>
|
|
58
|
-
name: tool_name
|
|
59
|
-
arguments:
|
|
60
|
-
param1: value1
|
|
61
|
-
param2: value2
|
|
62
|
-
</TOOL_CALL>"""
|
|
63
116
|
|
|
64
117
|
# Process line by line
|
|
65
118
|
for line in lines:
|
|
@@ -68,27 +121,19 @@ arguments:
|
|
|
68
121
|
continue
|
|
69
122
|
elif '</TOOL_CALL>' in line:
|
|
70
123
|
if in_tool_call and tool_call_lines:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
PrettyOutput.print("Tool call missing necessary fields", OutputType.ERROR)
|
|
85
|
-
raise Exception("Tool call missing necessary fields, " + tool_call_help)
|
|
86
|
-
except yaml.YAMLError as e:
|
|
87
|
-
PrettyOutput.print(f"YAML parsing error: {str(e)}", OutputType.ERROR)
|
|
88
|
-
raise Exception(f"YAML parsing error: {str(e)}")
|
|
89
|
-
except Exception as e:
|
|
90
|
-
PrettyOutput.print(f"Error processing tool call: {str(e)}", OutputType.ERROR)
|
|
91
|
-
raise Exception(f"Error processing tool call: {str(e)}")
|
|
124
|
+
# Parse YAML directly
|
|
125
|
+
tool_call_text = '\n'.join(tool_call_lines)
|
|
126
|
+
tool_call_data = yaml.safe_load(tool_call_text)
|
|
127
|
+
|
|
128
|
+
# Validate necessary fields
|
|
129
|
+
if "name" in tool_call_data and "arguments" in tool_call_data:
|
|
130
|
+
# Return content before tool call and tool call
|
|
131
|
+
return [{
|
|
132
|
+
"name": tool_call_data["name"],
|
|
133
|
+
"arguments": tool_call_data["arguments"]
|
|
134
|
+
}]
|
|
135
|
+
else:
|
|
136
|
+
raise Exception("Tool call missing necessary fields")
|
|
92
137
|
in_tool_call = False
|
|
93
138
|
continue
|
|
94
139
|
|
|
@@ -165,53 +210,39 @@ Please continue the task based on the above information.
|
|
|
165
210
|
"""
|
|
166
211
|
PrettyOutput.section("Task completed", OutputType.SUCCESS)
|
|
167
212
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if user_input in ['y', 'n', '']:
|
|
172
|
-
break
|
|
173
|
-
PrettyOutput.print("Invalid input, please enter y or n", OutputType.WARNING)
|
|
174
|
-
|
|
175
|
-
if user_input == 'y':
|
|
176
|
-
try:
|
|
177
|
-
# 让模型判断是否需要生成方法论
|
|
178
|
-
analysis_prompt = """The current task has ended, please analyze whether a methodology needs to be generated.
|
|
179
|
-
If you think a methodology should be generated, first determine whether to create a new methodology or update an existing one. If updating an existing methodology, use 'update', otherwise use 'add'.
|
|
180
|
-
If you think a methodology is not needed, please explain why.
|
|
181
|
-
The methodology should be applicable to general scenarios, do not include task-specific information such as code commit messages.
|
|
182
|
-
The methodology should include: problem restatement, optimal solution, notes (as needed), and nothing else.
|
|
183
|
-
Only output the methodology tool call instruction, or the explanation for not generating a methodology. Do not output anything else.
|
|
184
|
-
"""
|
|
185
|
-
self.prompt = analysis_prompt
|
|
186
|
-
response = self._call_model(self.prompt)
|
|
187
|
-
|
|
188
|
-
# 检查是否包含工具调用
|
|
213
|
+
if not self.is_sub_agent:
|
|
214
|
+
if self.record_methodology:
|
|
215
|
+
|
|
189
216
|
try:
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
217
|
+
# 让模型判断是否需要生成方法论
|
|
218
|
+
analysis_prompt = """The current task has ended, please analyze whether a methodology needs to be generated.
|
|
219
|
+
If you think a methodology should be generated, first determine whether to create a new methodology or update an existing one. If updating an existing methodology, use 'update', otherwise use 'add'.
|
|
220
|
+
If you think a methodology is not needed, please explain why.
|
|
221
|
+
The methodology should be applicable to general scenarios, do not include task-specific information such as code commit messages.
|
|
222
|
+
The methodology should include: problem restatement, optimal solution, notes (as needed), and nothing else.
|
|
223
|
+
Only output the methodology tool call instruction, or the explanation for not generating a methodology. Do not output anything else.
|
|
224
|
+
"""
|
|
225
|
+
self.prompt = analysis_prompt
|
|
226
|
+
response = self._call_model(self.prompt)
|
|
227
|
+
|
|
228
|
+
# 检查是否包含工具调用
|
|
229
|
+
try:
|
|
230
|
+
tool_calls = Agent.extract_tool_calls(response)
|
|
231
|
+
if tool_calls:
|
|
232
|
+
self.tool_registry.handle_tool_calls(tool_calls)
|
|
233
|
+
except Exception as e:
|
|
234
|
+
PrettyOutput.print(f"Failed to handle methodology generation: {str(e)}", OutputType.ERROR)
|
|
235
|
+
|
|
193
236
|
except Exception as e:
|
|
194
|
-
PrettyOutput.print(f"
|
|
195
|
-
|
|
196
|
-
except Exception as e:
|
|
197
|
-
PrettyOutput.print(f"Error generating methodology: {str(e)}", OutputType.ERROR)
|
|
198
|
-
|
|
199
|
-
if not self.is_sub_agent:
|
|
237
|
+
PrettyOutput.print(f"Error generating methodology: {str(e)}", OutputType.ERROR)
|
|
238
|
+
|
|
200
239
|
return "Task completed"
|
|
201
240
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
3. Key Information: Important information extracted during execution
|
|
208
|
-
4. Important Findings: Any noteworthy discoveries
|
|
209
|
-
5. Follow-up Suggestions: If any
|
|
210
|
-
|
|
211
|
-
Please describe in concise bullet points, highlighting important information.
|
|
212
|
-
"""
|
|
213
|
-
self.prompt = summary_prompt
|
|
214
|
-
return self._call_model(self.prompt)
|
|
241
|
+
if self.need_summary:
|
|
242
|
+
self.prompt = self.summary_prompt
|
|
243
|
+
return self._call_model(self.prompt)
|
|
244
|
+
|
|
245
|
+
return "Task completed"
|
|
215
246
|
|
|
216
247
|
|
|
217
248
|
def run(self, user_input: str, file_list: Optional[List[str]] = None) -> str:
|
|
@@ -224,28 +255,22 @@ Please describe in concise bullet points, highlighting important information.
|
|
|
224
255
|
Returns:
|
|
225
256
|
str: Task summary report
|
|
226
257
|
"""
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
|
|
227
261
|
try:
|
|
228
262
|
PrettyOutput.section("Preparing environment", OutputType.PLANNING)
|
|
229
263
|
if file_list:
|
|
230
264
|
self.model.upload_files(file_list)
|
|
231
265
|
|
|
232
|
-
# Load methodology
|
|
233
|
-
methodology_prompt = load_methodology(user_input)
|
|
234
|
-
tools_prompt = load_tools()
|
|
235
|
-
|
|
236
266
|
# 显示任务开始
|
|
237
267
|
PrettyOutput.section(f"Starting new task: {self.name}", OutputType.PLANNING)
|
|
238
268
|
|
|
239
|
-
self.
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
{tools_prompt}
|
|
245
|
-
|
|
246
|
-
{methodology_prompt}
|
|
247
|
-
""")
|
|
248
|
-
self.prompt = f"{user_input}"
|
|
269
|
+
if self.first:
|
|
270
|
+
self.prompt = f"{user_input}\n\n{load_methodology(user_input)}"
|
|
271
|
+
self.first = False
|
|
272
|
+
else:
|
|
273
|
+
self.prompt = f"{user_input}"
|
|
249
274
|
|
|
250
275
|
while True:
|
|
251
276
|
try:
|
|
@@ -261,25 +286,32 @@ Please describe in concise bullet points, highlighting important information.
|
|
|
261
286
|
continue
|
|
262
287
|
else:
|
|
263
288
|
current_response = self._call_model(self.prompt)
|
|
264
|
-
self.
|
|
289
|
+
self.prompt = ""
|
|
290
|
+
self.conversation_length += len(current_response)
|
|
291
|
+
|
|
292
|
+
for filter in self.output_filter:
|
|
293
|
+
self.prompt += filter(current_response)
|
|
294
|
+
|
|
265
295
|
try:
|
|
266
296
|
result = Agent.extract_tool_calls(current_response)
|
|
267
297
|
except Exception as e:
|
|
268
298
|
PrettyOutput.print(f"Tool call error: {str(e)}", OutputType.ERROR)
|
|
269
|
-
self.prompt
|
|
299
|
+
self.prompt += f"Tool call error: {str(e)}"
|
|
270
300
|
continue
|
|
271
301
|
|
|
272
302
|
if len(result) > 0:
|
|
273
303
|
PrettyOutput.print("Executing tool call...", OutputType.PROGRESS)
|
|
274
304
|
tool_result = self.tool_registry.handle_tool_calls(result)
|
|
275
|
-
self.prompt
|
|
305
|
+
self.prompt += tool_result
|
|
306
|
+
|
|
307
|
+
if self.prompt:
|
|
276
308
|
continue
|
|
309
|
+
|
|
310
|
+
if self.auto_complete and "<!!!COMPLETE!!!>" in current_response:
|
|
311
|
+
return self._complete_task()
|
|
277
312
|
|
|
278
313
|
# 获取用户输入
|
|
279
314
|
user_input = get_multiline_input(f"{self.name}: You can continue to input, or enter an empty line to end the current task")
|
|
280
|
-
if user_input == "__interrupt__":
|
|
281
|
-
PrettyOutput.print("Task cancelled by user", OutputType.WARNING)
|
|
282
|
-
return "Task cancelled by user"
|
|
283
315
|
|
|
284
316
|
if user_input:
|
|
285
317
|
self.prompt = user_input
|
|
@@ -362,10 +394,11 @@ def select_task(tasks: dict) -> str:
|
|
|
362
394
|
# Convert tasks to list for ordered display
|
|
363
395
|
task_names = list(tasks.keys())
|
|
364
396
|
|
|
365
|
-
|
|
397
|
+
task_list = ["Available tasks:"]
|
|
366
398
|
for i, name in enumerate(task_names, 1):
|
|
367
|
-
|
|
368
|
-
|
|
399
|
+
task_list.append(f"[{i}] {name}")
|
|
400
|
+
task_list.append("[0] Skip predefined tasks")
|
|
401
|
+
PrettyOutput.print("\n".join(task_list), OutputType.INFO)
|
|
369
402
|
|
|
370
403
|
|
|
371
404
|
while True:
|
|
@@ -406,25 +439,13 @@ When users need to execute tasks, you will strictly follow these steps to handle
|
|
|
406
439
|
7. Execute Action Plan: Execute one step at a time, **use at most one tool** (wait for tool execution results before proceeding)
|
|
407
440
|
8. Monitor and Adjust: If execution results don't match expectations, reflect and adjust the action plan, iterate previous steps
|
|
408
441
|
9. Methodology: If the current task has general applicability and valuable experience is gained, use methodology tools to record it for future similar problems
|
|
409
|
-
10.
|
|
442
|
+
10. Auto check the task goal completion status: If the task goal is completed, use the task completion command to end the task
|
|
443
|
+
11. Task Completion: End the task using task completion command when finished
|
|
410
444
|
|
|
411
445
|
Methodology Template:
|
|
412
446
|
1. Problem Restatement
|
|
413
447
|
2. Optimal Solution
|
|
414
448
|
3. Optimal Solution Steps (exclude failed actions)
|
|
415
|
-
|
|
416
|
-
Strict Rules:
|
|
417
|
-
- Execute only one tool at a time
|
|
418
|
-
- Tool execution must strictly follow the tool usage format
|
|
419
|
-
- Wait for user to provide execution results
|
|
420
|
-
- Don't assume or imagine results
|
|
421
|
-
- Don't create fake dialogues
|
|
422
|
-
- If current information is insufficient, you may ask the user
|
|
423
|
-
- Not all problem-solving steps are mandatory, skip as appropriate
|
|
424
|
-
- Request user guidance when multiple iterations show no progress
|
|
425
|
-
- If yaml string contains colons, wrap the entire string in quotes to avoid yaml parsing errors
|
|
426
|
-
- Use | syntax for multi-line strings in yaml
|
|
427
|
-
- If you can start executing the task, please start directly without asking the user if you can begin.
|
|
428
449
|
|
|
429
450
|
-------------------------------------------------------------"""
|
|
430
451
|
|
|
@@ -453,7 +474,7 @@ def main():
|
|
|
453
474
|
while True:
|
|
454
475
|
try:
|
|
455
476
|
user_input = get_multiline_input("Please enter your task (input empty line to exit):")
|
|
456
|
-
if not user_input
|
|
477
|
+
if not user_input:
|
|
457
478
|
break
|
|
458
479
|
agent.run(user_input, args.files)
|
|
459
480
|
except Exception as e:
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
from enum import auto
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
import yaml
|
|
7
|
+
from jarvis.agent import Agent
|
|
8
|
+
from jarvis.jarvis_code_agent.patch import apply_patch
|
|
9
|
+
from jarvis.jarvis_code_agent.file_select import select_files
|
|
10
|
+
from jarvis.jarvis_code_agent.relevant_files import find_relevant_files
|
|
11
|
+
from jarvis.models.registry import PlatformRegistry
|
|
12
|
+
from jarvis.tools.git_commiter import GitCommitTool
|
|
13
|
+
from jarvis.tools.registry import ToolRegistry
|
|
14
|
+
from jarvis.utils import OutputType, PrettyOutput, get_file_line_count, get_multiline_input, get_single_line_input, has_uncommitted_changes, init_env, find_git_root, is_disable_codebase, make_choice_input, user_confirm
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CodeAgent:
|
|
21
|
+
def __init__(self):
|
|
22
|
+
self.root_dir = os.getcwd()
|
|
23
|
+
tool_registry = ToolRegistry()
|
|
24
|
+
tool_registry.use_tools(["read_code", "execute_shell", "search", "ask_user", "ask_codebase"])
|
|
25
|
+
code_system_prompt = """
|
|
26
|
+
You are a code agent, you are responsible for modifying the code.
|
|
27
|
+
|
|
28
|
+
You should read the code and analyze the code, and then provide a plan for the code modification.
|
|
29
|
+
|
|
30
|
+
## Workflow Steps
|
|
31
|
+
|
|
32
|
+
1. ANALYSIS
|
|
33
|
+
- Understand the requirement thoroughly
|
|
34
|
+
- Identify which files need to be modified
|
|
35
|
+
- Review the current implementation
|
|
36
|
+
- Consider potential impacts
|
|
37
|
+
|
|
38
|
+
2. PLANNING
|
|
39
|
+
- Break down the changes into logical steps
|
|
40
|
+
- Consider dependencies between changes
|
|
41
|
+
- Plan the implementation sequence
|
|
42
|
+
- Think about potential risks
|
|
43
|
+
|
|
44
|
+
3. IMPLEMENTATION
|
|
45
|
+
For each file that needs changes:
|
|
46
|
+
a. Read and understand the current code
|
|
47
|
+
b. Plan the specific modifications
|
|
48
|
+
c. Write the patch in the required format
|
|
49
|
+
d. Review the patch for correctness
|
|
50
|
+
|
|
51
|
+
## File Reading Guidelines
|
|
52
|
+
|
|
53
|
+
1. For Large Files (>200 lines):
|
|
54
|
+
- Do NOT read the entire file at once using 'read_code'
|
|
55
|
+
- First use 'execute_shell' with grep/find to locate relevant sections
|
|
56
|
+
- Then use 'read_code' with specific line ranges to read only necessary portions
|
|
57
|
+
- Example:
|
|
58
|
+
* Use: execute_shell("grep -n 'function_name' path/to/file")
|
|
59
|
+
* Then: read_code("path/to/file", start_line=found_line-10, end_line=found_line+20)
|
|
60
|
+
|
|
61
|
+
2. For Small Files:
|
|
62
|
+
- Can read entire file using 'read_code' directly
|
|
63
|
+
|
|
64
|
+
## Patch Format and Guidelines
|
|
65
|
+
|
|
66
|
+
1. Basic Format:
|
|
67
|
+
<PATCH>
|
|
68
|
+
> /path/to/file start_line,end_line
|
|
69
|
+
new_content_line1
|
|
70
|
+
new_content_line2
|
|
71
|
+
</PATCH>
|
|
72
|
+
|
|
73
|
+
2. Rules:
|
|
74
|
+
- Each <PATCH> block MUST contain exactly ONE patch for ONE location
|
|
75
|
+
- Multiple changes to different locations require separate <PATCH> blocks
|
|
76
|
+
- Line Numbers Behavior:
|
|
77
|
+
* start_line (first number): This line WILL be replaced
|
|
78
|
+
* end_line (second number): This line will NOT be replaced
|
|
79
|
+
* The patch replaces content from start_line (inclusive) to end_line (exclusive)
|
|
80
|
+
- Use absolute paths relative to the project root
|
|
81
|
+
- Maintain consistent indentation
|
|
82
|
+
- Include enough context for precise location
|
|
83
|
+
|
|
84
|
+
3. Multiple Changes Example:
|
|
85
|
+
Before:
|
|
86
|
+
```
|
|
87
|
+
Line 0: first line
|
|
88
|
+
Line 1: second line
|
|
89
|
+
Line 2: third line
|
|
90
|
+
Line 3: fourth line
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
For multiple changes, use separate patches:
|
|
94
|
+
```
|
|
95
|
+
<PATCH>
|
|
96
|
+
> /path/to/file 0,1
|
|
97
|
+
new first line
|
|
98
|
+
</PATCH>
|
|
99
|
+
|
|
100
|
+
<PATCH>
|
|
101
|
+
> /path/to/file 2,3
|
|
102
|
+
new third line
|
|
103
|
+
</PATCH>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
After:
|
|
107
|
+
```
|
|
108
|
+
new first line
|
|
109
|
+
Line 1: second line
|
|
110
|
+
new third line
|
|
111
|
+
Line 3: fourth line
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Note: In this example:
|
|
115
|
+
- Each change is in its own <PATCH> block
|
|
116
|
+
- Changes are applied sequentially
|
|
117
|
+
- Line numbers are based on the original file
|
|
118
|
+
|
|
119
|
+
## Implementation Guidelines
|
|
120
|
+
|
|
121
|
+
1. Code Quality:
|
|
122
|
+
- Keep changes minimal and focused
|
|
123
|
+
- Maintain consistent style
|
|
124
|
+
- Add clear comments for complex logic
|
|
125
|
+
- Follow project patterns
|
|
126
|
+
- Ensure proper error handling
|
|
127
|
+
|
|
128
|
+
2. Tools Available:
|
|
129
|
+
- Use 'read_code/ask_codebase' to examine file contents
|
|
130
|
+
- Use 'execute_shell' for grep/find/ctags operations
|
|
131
|
+
- Use 'search' to search on web
|
|
132
|
+
- Use 'ask_user' when clarification is needed
|
|
133
|
+
|
|
134
|
+
Please proceed with the analysis and implementation following this workflow.
|
|
135
|
+
Start by examining the files and planning your changes.
|
|
136
|
+
Then provide the necessary patches in the specified format.
|
|
137
|
+
"""
|
|
138
|
+
self.agent = Agent(system_prompt=code_system_prompt,
|
|
139
|
+
name="CodeAgent",
|
|
140
|
+
auto_complete=False,
|
|
141
|
+
is_sub_agent=False,
|
|
142
|
+
tool_registry=tool_registry,
|
|
143
|
+
platform=PlatformRegistry().get_codegen_platform(),
|
|
144
|
+
record_methodology=False,
|
|
145
|
+
output_filter=[apply_patch],
|
|
146
|
+
need_summary=False)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _init_env(self):
|
|
151
|
+
curr_dir = os.getcwd()
|
|
152
|
+
git_dir = find_git_root(curr_dir)
|
|
153
|
+
self.root_dir = git_dir
|
|
154
|
+
if has_uncommitted_changes():
|
|
155
|
+
git_commiter = GitCommitTool()
|
|
156
|
+
git_commiter.execute({})
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def make_files_prompt(self, files: List[str]) -> str:
|
|
160
|
+
"""Make the files prompt.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
files: The files to be modified
|
|
164
|
+
|
|
165
|
+
"""
|
|
166
|
+
return "\n".join(
|
|
167
|
+
f"- {file} ({get_file_line_count(file)} lines)"
|
|
168
|
+
for file in files
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
def run(self, user_input: str) :
|
|
172
|
+
"""Run the code agent with the given user input.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
user_input: The user's requirement/request
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
str: Output describing the execution result
|
|
179
|
+
"""
|
|
180
|
+
try:
|
|
181
|
+
self._init_env()
|
|
182
|
+
files = find_relevant_files(user_input, self.root_dir)
|
|
183
|
+
self.agent.run(self._build_first_edit_prompt(user_input, self.make_files_prompt(files)))
|
|
184
|
+
|
|
185
|
+
except Exception as e:
|
|
186
|
+
return f"Error during execution: {str(e)}"
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _build_first_edit_prompt(self, user_input: str, files_prompt: str) -> str:
|
|
191
|
+
"""Build the initial prompt for the agent.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
user_input: The user's requirement
|
|
195
|
+
files_prompt: The formatted list of relevant files
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
str: The formatted prompt
|
|
199
|
+
"""
|
|
200
|
+
return f"""# Code Modification Task
|
|
201
|
+
|
|
202
|
+
## User Requirement
|
|
203
|
+
{user_input}
|
|
204
|
+
|
|
205
|
+
## Available Files
|
|
206
|
+
{files_prompt}
|
|
207
|
+
"""
|
|
208
|
+
def main():
|
|
209
|
+
"""Jarvis main entry point"""
|
|
210
|
+
# Add argument parser
|
|
211
|
+
init_env()
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
try:
|
|
215
|
+
# Interactive mode
|
|
216
|
+
while True:
|
|
217
|
+
try:
|
|
218
|
+
user_input = get_multiline_input("Please enter your requirement (input empty line to exit):")
|
|
219
|
+
if not user_input:
|
|
220
|
+
break
|
|
221
|
+
agent = CodeAgent()
|
|
222
|
+
agent.run(user_input)
|
|
223
|
+
|
|
224
|
+
except Exception as e:
|
|
225
|
+
PrettyOutput.print(f"Error: {str(e)}", OutputType.ERROR)
|
|
226
|
+
|
|
227
|
+
except Exception as e:
|
|
228
|
+
PrettyOutput.print(f"Initialization error: {str(e)}", OutputType.ERROR)
|
|
229
|
+
return 1
|
|
230
|
+
|
|
231
|
+
return 0
|
|
232
|
+
|
|
233
|
+
if __name__ == "__main__":
|
|
234
|
+
exit(main())
|