jarvis-ai-assistant 0.1.101__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 +140 -140
- 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 +32 -29
- jarvis/jarvis_platform/main.py +5 -3
- jarvis/jarvis_rag/main.py +11 -15
- jarvis/jarvis_smart_shell/main.py +2 -2
- jarvis/models/ai8.py +1 -0
- jarvis/models/kimi.py +36 -30
- jarvis/models/ollama.py +17 -11
- jarvis/models/openai.py +15 -12
- jarvis/models/oyi.py +22 -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 +6 -3
- jarvis/tools/read_code.py +147 -0
- jarvis/tools/read_webpage.py +1 -1
- jarvis/tools/registry.py +92 -68
- jarvis/tools/search.py +8 -6
- jarvis/tools/select_code_files.py +4 -4
- jarvis/utils.py +270 -95
- {jarvis_ai_assistant-0.1.101.dist-info → jarvis_ai_assistant-0.1.103.dist-info}/METADATA +9 -5
- jarvis_ai_assistant-0.1.103.dist-info/RECORD +51 -0
- {jarvis_ai_assistant-0.1.101.dist-info → jarvis_ai_assistant-0.1.103.dist-info}/entry_points.txt +4 -2
- jarvis/jarvis_code_agent/main.py +0 -202
- jarvis/jarvis_coder/__init__.py +0 -0
- jarvis/jarvis_coder/git_utils.py +0 -123
- jarvis/jarvis_coder/main.py +0 -241
- jarvis/jarvis_coder/patch_handler.py +0 -340
- jarvis/jarvis_coder/plan_generator.py +0 -145
- 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.101.dist-info/RECORD +0 -51
- {jarvis_ai_assistant-0.1.101.dist-info → jarvis_ai_assistant-0.1.103.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.101.dist-info → jarvis_ai_assistant-0.1.103.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.101.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, load_embedding_model, 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,45 +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
|
|
38
|
-
self.
|
|
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
|
+
"""
|
|
72
|
+
|
|
39
73
|
self.max_context_length = get_max_context_length()
|
|
74
|
+
|
|
75
|
+
self.auto_complete = auto_complete
|
|
76
|
+
|
|
77
|
+
|
|
40
78
|
|
|
41
|
-
# Initialize embedding model
|
|
42
|
-
try:
|
|
43
|
-
self.embedding_model = load_embedding_model()
|
|
44
|
-
|
|
45
|
-
# Warm up model and get correct dimension
|
|
46
|
-
test_text = "This is a test text to ensure the model is fully loaded."
|
|
47
|
-
test_embedding = self.embedding_model.encode(
|
|
48
|
-
test_text,
|
|
49
|
-
convert_to_tensor=True,
|
|
50
|
-
normalize_embeddings=True
|
|
51
|
-
)
|
|
52
|
-
self.embedding_dimension = len(test_embedding)
|
|
53
|
-
PrettyOutput.print("Successfully loaded embedding model", OutputType.SUCCESS)
|
|
54
|
-
|
|
55
|
-
# Initialize HNSW index (use correct dimension)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
except Exception as e:
|
|
59
|
-
PrettyOutput.print(f"Failed to load embedding model: {str(e)}", OutputType.ERROR)
|
|
60
|
-
raise
|
|
61
79
|
|
|
62
80
|
# Initialize methodology related attributes
|
|
63
81
|
self.methodology_data = []
|
|
64
82
|
|
|
65
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
|
|
66
107
|
|
|
67
108
|
@staticmethod
|
|
68
109
|
def extract_tool_calls(content: str) -> List[Dict]:
|
|
@@ -72,14 +113,6 @@ class Agent:
|
|
|
72
113
|
tool_call_lines = []
|
|
73
114
|
in_tool_call = False
|
|
74
115
|
|
|
75
|
-
tool_call_help = """Tool Usage Format:
|
|
76
|
-
|
|
77
|
-
<TOOL_CALL>
|
|
78
|
-
name: tool_name
|
|
79
|
-
arguments:
|
|
80
|
-
param1: value1
|
|
81
|
-
param2: value2
|
|
82
|
-
</TOOL_CALL>"""
|
|
83
116
|
|
|
84
117
|
# Process line by line
|
|
85
118
|
for line in lines:
|
|
@@ -88,27 +121,19 @@ arguments:
|
|
|
88
121
|
continue
|
|
89
122
|
elif '</TOOL_CALL>' in line:
|
|
90
123
|
if in_tool_call and tool_call_lines:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
PrettyOutput.print("Tool call missing necessary fields", OutputType.ERROR)
|
|
105
|
-
raise Exception("Tool call missing necessary fields, " + tool_call_help)
|
|
106
|
-
except yaml.YAMLError as e:
|
|
107
|
-
PrettyOutput.print(f"YAML parsing error: {str(e)}", OutputType.ERROR)
|
|
108
|
-
raise Exception(f"YAML parsing error: {str(e)}")
|
|
109
|
-
except Exception as e:
|
|
110
|
-
PrettyOutput.print(f"Error processing tool call: {str(e)}", OutputType.ERROR)
|
|
111
|
-
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")
|
|
112
137
|
in_tool_call = False
|
|
113
138
|
continue
|
|
114
139
|
|
|
@@ -185,53 +210,39 @@ Please continue the task based on the above information.
|
|
|
185
210
|
"""
|
|
186
211
|
PrettyOutput.section("Task completed", OutputType.SUCCESS)
|
|
187
212
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if user_input in ['y', 'n', '']:
|
|
192
|
-
break
|
|
193
|
-
PrettyOutput.print("Invalid input, please enter y or n", OutputType.WARNING)
|
|
194
|
-
|
|
195
|
-
if user_input == 'y':
|
|
196
|
-
try:
|
|
197
|
-
# 让模型判断是否需要生成方法论
|
|
198
|
-
analysis_prompt = """The current task has ended, please analyze whether a methodology needs to be generated.
|
|
199
|
-
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'.
|
|
200
|
-
If you think a methodology is not needed, please explain why.
|
|
201
|
-
The methodology should be applicable to general scenarios, do not include task-specific information such as code commit messages.
|
|
202
|
-
The methodology should include: problem restatement, optimal solution, notes (as needed), and nothing else.
|
|
203
|
-
Only output the methodology tool call instruction, or the explanation for not generating a methodology. Do not output anything else.
|
|
204
|
-
"""
|
|
205
|
-
self.prompt = analysis_prompt
|
|
206
|
-
response = self._call_model(self.prompt)
|
|
207
|
-
|
|
208
|
-
# 检查是否包含工具调用
|
|
213
|
+
if not self.is_sub_agent:
|
|
214
|
+
if self.record_methodology:
|
|
215
|
+
|
|
209
216
|
try:
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
+
|
|
213
236
|
except Exception as e:
|
|
214
|
-
PrettyOutput.print(f"
|
|
215
|
-
|
|
216
|
-
except Exception as e:
|
|
217
|
-
PrettyOutput.print(f"Error generating methodology: {str(e)}", OutputType.ERROR)
|
|
218
|
-
|
|
219
|
-
if not self.is_sub_agent:
|
|
237
|
+
PrettyOutput.print(f"Error generating methodology: {str(e)}", OutputType.ERROR)
|
|
238
|
+
|
|
220
239
|
return "Task completed"
|
|
221
240
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
3. Key Information: Important information extracted during execution
|
|
228
|
-
4. Important Findings: Any noteworthy discoveries
|
|
229
|
-
5. Follow-up Suggestions: If any
|
|
230
|
-
|
|
231
|
-
Please describe in concise bullet points, highlighting important information.
|
|
232
|
-
"""
|
|
233
|
-
self.prompt = summary_prompt
|
|
234
|
-
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"
|
|
235
246
|
|
|
236
247
|
|
|
237
248
|
def run(self, user_input: str, file_list: Optional[List[str]] = None) -> str:
|
|
@@ -244,28 +255,22 @@ Please describe in concise bullet points, highlighting important information.
|
|
|
244
255
|
Returns:
|
|
245
256
|
str: Task summary report
|
|
246
257
|
"""
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
|
|
247
261
|
try:
|
|
248
262
|
PrettyOutput.section("Preparing environment", OutputType.PLANNING)
|
|
249
263
|
if file_list:
|
|
250
264
|
self.model.upload_files(file_list)
|
|
251
265
|
|
|
252
|
-
# Load methodology
|
|
253
|
-
methodology_prompt = load_methodology(user_input)
|
|
254
|
-
tools_prompt = load_tools()
|
|
255
|
-
|
|
256
266
|
# 显示任务开始
|
|
257
267
|
PrettyOutput.section(f"Starting new task: {self.name}", OutputType.PLANNING)
|
|
258
268
|
|
|
259
|
-
self.
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
{tools_prompt}
|
|
265
|
-
|
|
266
|
-
{methodology_prompt}
|
|
267
|
-
""")
|
|
268
|
-
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}"
|
|
269
274
|
|
|
270
275
|
while True:
|
|
271
276
|
try:
|
|
@@ -281,25 +286,32 @@ Please describe in concise bullet points, highlighting important information.
|
|
|
281
286
|
continue
|
|
282
287
|
else:
|
|
283
288
|
current_response = self._call_model(self.prompt)
|
|
284
|
-
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
|
+
|
|
285
295
|
try:
|
|
286
296
|
result = Agent.extract_tool_calls(current_response)
|
|
287
297
|
except Exception as e:
|
|
288
298
|
PrettyOutput.print(f"Tool call error: {str(e)}", OutputType.ERROR)
|
|
289
|
-
self.prompt
|
|
299
|
+
self.prompt += f"Tool call error: {str(e)}"
|
|
290
300
|
continue
|
|
291
301
|
|
|
292
302
|
if len(result) > 0:
|
|
293
303
|
PrettyOutput.print("Executing tool call...", OutputType.PROGRESS)
|
|
294
304
|
tool_result = self.tool_registry.handle_tool_calls(result)
|
|
295
|
-
self.prompt
|
|
305
|
+
self.prompt += tool_result
|
|
306
|
+
|
|
307
|
+
if self.prompt:
|
|
296
308
|
continue
|
|
309
|
+
|
|
310
|
+
if self.auto_complete and "<!!!COMPLETE!!!>" in current_response:
|
|
311
|
+
return self._complete_task()
|
|
297
312
|
|
|
298
313
|
# 获取用户输入
|
|
299
314
|
user_input = get_multiline_input(f"{self.name}: You can continue to input, or enter an empty line to end the current task")
|
|
300
|
-
if user_input == "__interrupt__":
|
|
301
|
-
PrettyOutput.print("Task cancelled by user", OutputType.WARNING)
|
|
302
|
-
return "Task cancelled by user"
|
|
303
315
|
|
|
304
316
|
if user_input:
|
|
305
317
|
self.prompt = user_input
|
|
@@ -382,10 +394,11 @@ def select_task(tasks: dict) -> str:
|
|
|
382
394
|
# Convert tasks to list for ordered display
|
|
383
395
|
task_names = list(tasks.keys())
|
|
384
396
|
|
|
385
|
-
|
|
397
|
+
task_list = ["Available tasks:"]
|
|
386
398
|
for i, name in enumerate(task_names, 1):
|
|
387
|
-
|
|
388
|
-
|
|
399
|
+
task_list.append(f"[{i}] {name}")
|
|
400
|
+
task_list.append("[0] Skip predefined tasks")
|
|
401
|
+
PrettyOutput.print("\n".join(task_list), OutputType.INFO)
|
|
389
402
|
|
|
390
403
|
|
|
391
404
|
while True:
|
|
@@ -426,26 +439,13 @@ When users need to execute tasks, you will strictly follow these steps to handle
|
|
|
426
439
|
7. Execute Action Plan: Execute one step at a time, **use at most one tool** (wait for tool execution results before proceeding)
|
|
427
440
|
8. Monitor and Adjust: If execution results don't match expectations, reflect and adjust the action plan, iterate previous steps
|
|
428
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
|
|
429
|
-
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
|
|
430
444
|
|
|
431
445
|
Methodology Template:
|
|
432
446
|
1. Problem Restatement
|
|
433
447
|
2. Optimal Solution
|
|
434
448
|
3. Optimal Solution Steps (exclude failed actions)
|
|
435
|
-
|
|
436
|
-
Strict Rules:
|
|
437
|
-
- Execute only one tool at a time
|
|
438
|
-
- Tool execution must strictly follow the tool usage format
|
|
439
|
-
- Wait for user to provide execution results
|
|
440
|
-
- Don't assume or imagine results
|
|
441
|
-
- Don't create fake dialogues
|
|
442
|
-
- If current information is insufficient, you may ask the user
|
|
443
|
-
- Not all problem-solving steps are mandatory, skip as appropriate
|
|
444
|
-
- Ask user before executing tools that might damage system or user's codebase
|
|
445
|
-
- Request user guidance when multiple iterations show no progress
|
|
446
|
-
- If yaml string contains colons, wrap the entire string in quotes to avoid yaml parsing errors
|
|
447
|
-
- Use | syntax for multi-line strings in yaml
|
|
448
|
-
- If you can start executing the task, please start directly without asking the user if you can begin.
|
|
449
449
|
|
|
450
450
|
-------------------------------------------------------------"""
|
|
451
451
|
|
|
@@ -459,7 +459,7 @@ def main():
|
|
|
459
459
|
|
|
460
460
|
try:
|
|
461
461
|
# 获取全局模型实例
|
|
462
|
-
agent = Agent(system_prompt=origin_agent_system_prompt)
|
|
462
|
+
agent = Agent(system_prompt=origin_agent_system_prompt, tool_registry=ToolRegistry())
|
|
463
463
|
|
|
464
464
|
# 加载预定义任务
|
|
465
465
|
tasks = load_tasks()
|
|
@@ -474,7 +474,7 @@ def main():
|
|
|
474
474
|
while True:
|
|
475
475
|
try:
|
|
476
476
|
user_input = get_multiline_input("Please enter your task (input empty line to exit):")
|
|
477
|
-
if not user_input
|
|
477
|
+
if not user_input:
|
|
478
478
|
break
|
|
479
479
|
agent.run(user_input, args.files)
|
|
480
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())
|