jarvis-ai-assistant 0.1.98__py3-none-any.whl → 0.1.99__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 +199 -157
- jarvis/jarvis_code_agent/__init__.py +0 -0
- jarvis/jarvis_code_agent/main.py +203 -0
- jarvis/jarvis_codebase/main.py +412 -284
- jarvis/jarvis_coder/file_select.py +209 -0
- jarvis/jarvis_coder/git_utils.py +64 -2
- jarvis/jarvis_coder/main.py +11 -389
- jarvis/jarvis_coder/patch_handler.py +84 -14
- jarvis/jarvis_coder/plan_generator.py +49 -7
- jarvis/jarvis_rag/main.py +9 -9
- jarvis/jarvis_smart_shell/main.py +5 -7
- jarvis/models/base.py +6 -1
- jarvis/models/ollama.py +2 -2
- jarvis/models/registry.py +3 -6
- jarvis/tools/ask_user.py +6 -6
- jarvis/tools/codebase_qa.py +5 -7
- jarvis/tools/create_code_sub_agent.py +55 -0
- jarvis/tools/{sub_agent.py → create_sub_agent.py} +4 -1
- jarvis/tools/execute_code_modification.py +72 -0
- jarvis/tools/{file_ops.py → file_operation.py} +13 -14
- jarvis/tools/find_related_files.py +86 -0
- jarvis/tools/methodology.py +25 -25
- jarvis/tools/rag.py +32 -32
- jarvis/tools/registry.py +72 -36
- jarvis/tools/search.py +1 -1
- jarvis/tools/select_code_files.py +64 -0
- jarvis/utils.py +153 -49
- {jarvis_ai_assistant-0.1.98.dist-info → jarvis_ai_assistant-0.1.99.dist-info}/METADATA +1 -1
- jarvis_ai_assistant-0.1.99.dist-info/RECORD +52 -0
- {jarvis_ai_assistant-0.1.98.dist-info → jarvis_ai_assistant-0.1.99.dist-info}/entry_points.txt +2 -1
- jarvis/main.py +0 -155
- jarvis/tools/coder.py +0 -69
- jarvis_ai_assistant-0.1.98.dist-info/RECORD +0 -47
- /jarvis/tools/{shell.py → execute_shell.py} +0 -0
- /jarvis/tools/{generator.py → generate_tool.py} +0 -0
- /jarvis/tools/{webpage.py → read_webpage.py} +0 -0
- {jarvis_ai_assistant-0.1.98.dist-info → jarvis_ai_assistant-0.1.99.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.98.dist-info → jarvis_ai_assistant-0.1.99.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.98.dist-info → jarvis_ai_assistant-0.1.99.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import re
|
|
2
2
|
from typing import Dict, List, Tuple
|
|
3
3
|
from jarvis.models.registry import PlatformRegistry
|
|
4
|
-
from jarvis.utils import PrettyOutput, OutputType, get_multiline_input
|
|
4
|
+
from jarvis.utils import PrettyOutput, OutputType, get_multiline_input, get_single_line_input, is_long_context
|
|
5
5
|
|
|
6
6
|
class PlanGenerator:
|
|
7
7
|
"""Modification plan generator"""
|
|
@@ -48,7 +48,42 @@ class PlanGenerator:
|
|
|
48
48
|
return prompt
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
@staticmethod
|
|
52
|
+
def get_key_code(files: List[str], feature: str)->List[Dict[str, List[str]]]:
|
|
53
|
+
"""Extract relevant key code snippets from files"""
|
|
54
|
+
ret = []
|
|
55
|
+
for file in files:
|
|
56
|
+
PrettyOutput.print(f"Analyzing file: {file}", OutputType.INFO)
|
|
57
|
+
model = PlatformRegistry.get_global_platform_registry().get_codegen_platform()
|
|
58
|
+
model.set_suppress_output(True)
|
|
59
|
+
file_path = file
|
|
60
|
+
content = open(file_path, "r", encoding="utf-8").read()
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
prompt = f"""You are a code analysis expert who can extract relevant snippets from code.
|
|
64
|
+
Please return in the following format:
|
|
65
|
+
<PART>
|
|
66
|
+
content
|
|
67
|
+
</PART>
|
|
68
|
+
|
|
69
|
+
Multiple snippets can be returned. If the file content is not relevant to the requirement, return empty.
|
|
70
|
+
|
|
71
|
+
Requirement: {feature}
|
|
72
|
+
File path: {file_path}
|
|
73
|
+
Code content:
|
|
74
|
+
{content}
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
# 调用大模型进行分析
|
|
78
|
+
response = model.chat_until_success(prompt)
|
|
79
|
+
|
|
80
|
+
parts = re.findall(r'<PART>\n(.*?)\n</PART>', response, re.DOTALL)
|
|
81
|
+
ret.append({"file_path": file, "parts": parts})
|
|
82
|
+
except Exception as e:
|
|
83
|
+
PrettyOutput.print(f"Failed to analyze file: {str(e)}", OutputType.ERROR)
|
|
84
|
+
return ret
|
|
85
|
+
|
|
86
|
+
def generate_plan(self, feature: str, related_files: List[str]) -> Dict[str,str]:
|
|
52
87
|
"""Generate modification plan
|
|
53
88
|
|
|
54
89
|
Args:
|
|
@@ -59,8 +94,15 @@ class PlanGenerator:
|
|
|
59
94
|
Tuple[str, Dict[str,str]]: Modification plan, return None if user cancels
|
|
60
95
|
"""
|
|
61
96
|
additional_info = ""
|
|
97
|
+
file_info = []
|
|
98
|
+
if is_long_context(related_files):
|
|
99
|
+
file_info = PlanGenerator.get_key_code(related_files, feature)
|
|
100
|
+
else:
|
|
101
|
+
for file in related_files:
|
|
102
|
+
file_info.append({"file_path": file, "parts": [open(file, "r", encoding="utf-8").read()]})
|
|
103
|
+
|
|
62
104
|
while True:
|
|
63
|
-
prompt = self._build_prompt(feature,
|
|
105
|
+
prompt = self._build_prompt(feature, file_info, additional_info)
|
|
64
106
|
# Build prompt
|
|
65
107
|
PrettyOutput.print("Start generating modification plan...", OutputType.PROGRESS)
|
|
66
108
|
|
|
@@ -71,17 +113,17 @@ class PlanGenerator:
|
|
|
71
113
|
PrettyOutput.print("Modification plan generation failed, please try again", OutputType.ERROR)
|
|
72
114
|
tmp = get_multiline_input("Please enter your additional information or suggestions (press Enter to cancel):")
|
|
73
115
|
if tmp == "__interrupt__" or prompt == "":
|
|
74
|
-
return
|
|
116
|
+
return {}
|
|
75
117
|
additional_info += tmp + "\n"
|
|
76
118
|
continue
|
|
77
|
-
user_input =
|
|
119
|
+
user_input = get_single_line_input("Do you agree with this modification plan? (y/n) [y]").strip().lower() or 'y'
|
|
78
120
|
if user_input == 'y' or user_input == '':
|
|
79
|
-
return
|
|
121
|
+
return structed_plan
|
|
80
122
|
elif user_input == 'n':
|
|
81
123
|
# Get user feedback
|
|
82
124
|
tmp = get_multiline_input("Please enter your additional information or suggestions (press Enter to cancel):")
|
|
83
125
|
if prompt == "__interrupt__" or prompt == "":
|
|
84
|
-
return
|
|
126
|
+
return {}
|
|
85
127
|
additional_info += tmp + "\n"
|
|
86
128
|
continue
|
|
87
129
|
|
jarvis/jarvis_rag/main.py
CHANGED
|
@@ -90,7 +90,7 @@ class TextFileProcessor(FileProcessor):
|
|
|
90
90
|
continue
|
|
91
91
|
|
|
92
92
|
if not detected_encoding:
|
|
93
|
-
raise UnicodeDecodeError(f"Failed to decode file with supported encodings: {file_path}")
|
|
93
|
+
raise UnicodeDecodeError(f"Failed to decode file with supported encodings: {file_path}") # type: ignore
|
|
94
94
|
|
|
95
95
|
# Use the detected encoding to read the file
|
|
96
96
|
with open(file_path, 'r', encoding=detected_encoding, errors='replace') as f:
|
|
@@ -114,7 +114,7 @@ class PDFProcessor(FileProcessor):
|
|
|
114
114
|
@staticmethod
|
|
115
115
|
def extract_text(file_path: str) -> str:
|
|
116
116
|
text_parts = []
|
|
117
|
-
with fitz.open(file_path) as doc:
|
|
117
|
+
with fitz.open(file_path) as doc: # type: ignore
|
|
118
118
|
for page in doc:
|
|
119
119
|
text_parts.append(page.get_text())
|
|
120
120
|
return "\n".join(text_parts)
|
|
@@ -248,7 +248,7 @@ class RAGTool:
|
|
|
248
248
|
|
|
249
249
|
# Create a flat index to store original vectors, for reconstruction
|
|
250
250
|
self.flat_index = faiss.IndexFlatIP(self.vector_dim)
|
|
251
|
-
self.flat_index.add(vectors)
|
|
251
|
+
self.flat_index.add(vectors) # type: ignore
|
|
252
252
|
|
|
253
253
|
# Create an IVF index for fast search
|
|
254
254
|
nlist = max(4, int(vectors.shape[0] / 1000)) # 每1000个向量一个聚类中心
|
|
@@ -256,8 +256,8 @@ class RAGTool:
|
|
|
256
256
|
self.index = faiss.IndexIVFFlat(quantizer, self.vector_dim, nlist, faiss.METRIC_INNER_PRODUCT)
|
|
257
257
|
|
|
258
258
|
# Train and add vectors
|
|
259
|
-
self.index.train(vectors)
|
|
260
|
-
self.index.add(vectors)
|
|
259
|
+
self.index.train(vectors) # type: ignore
|
|
260
|
+
self.index.add(vectors) # type: ignore
|
|
261
261
|
# Set the number of clusters to probe during search
|
|
262
262
|
self.index.nprobe = min(nlist, 10)
|
|
263
263
|
|
|
@@ -341,7 +341,7 @@ class RAGTool:
|
|
|
341
341
|
except Exception as e:
|
|
342
342
|
PrettyOutput.print(f"Failed to get vector representation: {str(e)}",
|
|
343
343
|
output_type=OutputType.ERROR)
|
|
344
|
-
return np.zeros((len(texts), self.vector_dim), dtype=np.float32)
|
|
344
|
+
return np.zeros((len(texts), self.vector_dim), dtype=np.float32) # type: ignore
|
|
345
345
|
|
|
346
346
|
def _process_document_batch(self, documents: List[Document]) -> List[np.ndarray]:
|
|
347
347
|
"""Process a batch of documents vectorization
|
|
@@ -361,7 +361,7 @@ Content: {doc.content}
|
|
|
361
361
|
"""
|
|
362
362
|
texts.append(combined_text)
|
|
363
363
|
|
|
364
|
-
return self._get_embedding_batch(texts)
|
|
364
|
+
return self._get_embedding_batch(texts) # type: ignore
|
|
365
365
|
|
|
366
366
|
def _process_file(self, file_path: str) -> List[Document]:
|
|
367
367
|
"""Process a single file"""
|
|
@@ -516,7 +516,7 @@ Content: {doc.content}
|
|
|
516
516
|
if d.metadata['file_path'] == doc.metadata['file_path']), None)
|
|
517
517
|
if doc_idx is not None:
|
|
518
518
|
# Reconstruct vectors from flat index
|
|
519
|
-
vector = np.zeros((1, self.vector_dim), dtype=np.float32)
|
|
519
|
+
vector = np.zeros((1, self.vector_dim), dtype=np.float32) # type: ignore
|
|
520
520
|
self.flat_index.reconstruct(doc_idx, vector.ravel())
|
|
521
521
|
unchanged_vectors.append(vector)
|
|
522
522
|
|
|
@@ -585,7 +585,7 @@ Content: {doc.content}
|
|
|
585
585
|
|
|
586
586
|
# Initial search more results for MMR
|
|
587
587
|
initial_k = min(top_k * 2, len(self.documents))
|
|
588
|
-
distances, indices = self.index.search(query_vector, initial_k)
|
|
588
|
+
distances, indices = self.index.search(query_vector, initial_k) # type: ignore
|
|
589
589
|
|
|
590
590
|
# Get valid results
|
|
591
591
|
valid_indices = indices[0][indices[0] != -1]
|
|
@@ -4,24 +4,23 @@ import os
|
|
|
4
4
|
import sys
|
|
5
5
|
import readline
|
|
6
6
|
from typing import Optional
|
|
7
|
-
from yaspin import yaspin
|
|
8
|
-
from yaspin.spinners import Spinners
|
|
7
|
+
from yaspin import yaspin # type: ignore
|
|
8
|
+
from yaspin.spinners import Spinners # type: ignore
|
|
9
9
|
|
|
10
10
|
from jarvis.models.registry import PlatformRegistry
|
|
11
|
-
from jarvis.utils import PrettyOutput, OutputType, load_env_from_file
|
|
11
|
+
from jarvis.utils import PrettyOutput, OutputType, get_single_line_input, load_env_from_file
|
|
12
12
|
|
|
13
13
|
def execute_command(command: str) -> None:
|
|
14
14
|
"""Show command and allow user to edit, then execute, Ctrl+C to cancel"""
|
|
15
15
|
try:
|
|
16
|
-
print("\nGenerated command (can be edited, press Enter to execute, Ctrl+C to cancel):")
|
|
17
16
|
# Pre-fill input line
|
|
18
17
|
readline.set_startup_hook(lambda: readline.insert_text(command))
|
|
19
18
|
try:
|
|
20
|
-
edited_command =
|
|
19
|
+
edited_command = get_single_line_input("Generated command (can be edited, press Enter to execute, Ctrl+C to cancel)")
|
|
21
20
|
if edited_command.strip(): # Ensure command is not empty
|
|
22
21
|
os.system(edited_command)
|
|
23
22
|
except KeyboardInterrupt:
|
|
24
|
-
print("
|
|
23
|
+
PrettyOutput.print("Execution cancelled", OutputType.INFO)
|
|
25
24
|
finally:
|
|
26
25
|
readline.set_startup_hook() # Clear pre-filled
|
|
27
26
|
except Exception as e:
|
|
@@ -38,7 +37,6 @@ def process_request(request: str) -> Optional[str]:
|
|
|
38
37
|
"""
|
|
39
38
|
try:
|
|
40
39
|
# Get language model instance
|
|
41
|
-
PlatformRegistry.suppress_output = True
|
|
42
40
|
model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
|
|
43
41
|
model.set_suppress_output(True)
|
|
44
42
|
|
jarvis/models/base.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
+
import re
|
|
2
3
|
from typing import Dict, List, Tuple
|
|
3
4
|
|
|
4
5
|
from jarvis.utils import OutputType, PrettyOutput, while_success, while_true
|
|
@@ -26,7 +27,11 @@ class BasePlatform(ABC):
|
|
|
26
27
|
raise NotImplementedError("chat is not implemented")
|
|
27
28
|
|
|
28
29
|
def chat_until_success(self, message: str) -> str:
|
|
29
|
-
|
|
30
|
+
def _chat():
|
|
31
|
+
response = self.chat(message)
|
|
32
|
+
response = re.sub(r'<think>(.*?)</think>', '', response, flags=re.DOTALL)
|
|
33
|
+
return response
|
|
34
|
+
return while_true(lambda: while_success(lambda: _chat(), 5), 5)
|
|
30
35
|
|
|
31
36
|
@abstractmethod
|
|
32
37
|
def upload_files(self, file_list: List[str]) -> List[Dict]:
|
jarvis/models/ollama.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import requests
|
|
2
2
|
from typing import List, Dict, Tuple
|
|
3
3
|
from jarvis.models.base import BasePlatform
|
|
4
|
-
from jarvis.utils import OutputType, PrettyOutput
|
|
4
|
+
from jarvis.utils import OutputType, PrettyOutput, get_single_line_input
|
|
5
5
|
import os
|
|
6
6
|
import json
|
|
7
7
|
|
|
@@ -136,7 +136,7 @@ if __name__ == "__main__":
|
|
|
136
136
|
ollama = OllamaPlatform()
|
|
137
137
|
while True:
|
|
138
138
|
try:
|
|
139
|
-
message =
|
|
139
|
+
message = get_single_line_input("\nInput question (Ctrl+C to exit)")
|
|
140
140
|
ollama.chat_until_success(message)
|
|
141
141
|
except KeyboardInterrupt:
|
|
142
142
|
print("\nGoodbye!")
|
jarvis/models/registry.py
CHANGED
|
@@ -3,8 +3,8 @@ import inspect
|
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
5
5
|
from typing import Dict, Type, Optional, List
|
|
6
|
-
from .base import BasePlatform
|
|
7
|
-
from
|
|
6
|
+
from jarvis.models.base import BasePlatform
|
|
7
|
+
from jarvis.utils import PrettyOutput, OutputType
|
|
8
8
|
|
|
9
9
|
REQUIRED_METHODS = [
|
|
10
10
|
('chat', ['message']), # 方法名和参数列表
|
|
@@ -23,7 +23,6 @@ class PlatformRegistry:
|
|
|
23
23
|
|
|
24
24
|
global_platform_name = "kimi"
|
|
25
25
|
global_platform_registry = None
|
|
26
|
-
suppress_output = False
|
|
27
26
|
|
|
28
27
|
@staticmethod
|
|
29
28
|
def get_platform_dir() -> str:
|
|
@@ -117,7 +116,7 @@ class PlatformRegistry:
|
|
|
117
116
|
module = importlib.import_module(module_name)
|
|
118
117
|
|
|
119
118
|
# 遍历模块中的所有类
|
|
120
|
-
for
|
|
119
|
+
for _, obj in inspect.getmembers(module):
|
|
121
120
|
# 检查是否是BasePlatform的子类,但不是BasePlatform本身
|
|
122
121
|
if (inspect.isclass(obj) and
|
|
123
122
|
issubclass(obj, BasePlatform) and
|
|
@@ -126,8 +125,6 @@ class PlatformRegistry:
|
|
|
126
125
|
# 检查平台实现
|
|
127
126
|
if not PlatformRegistry.check_platform_implementation(obj):
|
|
128
127
|
continue
|
|
129
|
-
if not PlatformRegistry.suppress_output:
|
|
130
|
-
PrettyOutput.print(f"Load platform from {os.path.join(directory, filename)}: {obj.platform_name}", OutputType.SUCCESS) # type: ignore
|
|
131
128
|
platforms[obj.platform_name] = obj # type: ignore
|
|
132
129
|
break
|
|
133
130
|
except Exception as e:
|
jarvis/tools/ask_user.py
CHANGED
|
@@ -18,22 +18,22 @@ class AskUserTool:
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
21
|
-
"""
|
|
21
|
+
"""Execute the operation of asking the user
|
|
22
22
|
|
|
23
23
|
Args:
|
|
24
|
-
args:
|
|
24
|
+
args: A dictionary containing the question
|
|
25
25
|
|
|
26
26
|
Returns:
|
|
27
|
-
Dict:
|
|
27
|
+
Dict: A dictionary containing the user's response
|
|
28
28
|
"""
|
|
29
29
|
try:
|
|
30
30
|
question = args["question"]
|
|
31
31
|
|
|
32
|
-
#
|
|
33
|
-
PrettyOutput.print("\
|
|
32
|
+
# Display the question
|
|
33
|
+
PrettyOutput.print("\nQuestion:", OutputType.SYSTEM)
|
|
34
34
|
PrettyOutput.print(question, OutputType.SYSTEM)
|
|
35
35
|
|
|
36
|
-
#
|
|
36
|
+
# Get user input
|
|
37
37
|
user_response = get_multiline_input("Please enter your answer (input empty line to end)")
|
|
38
38
|
|
|
39
39
|
if user_response == "__interrupt__":
|
jarvis/tools/codebase_qa.py
CHANGED
|
@@ -4,7 +4,7 @@ from jarvis.jarvis_codebase.main import CodeBase
|
|
|
4
4
|
from jarvis.utils import find_git_root, PrettyOutput, OutputType
|
|
5
5
|
|
|
6
6
|
class CodebaseQATool:
|
|
7
|
-
"""
|
|
7
|
+
"""Codebase QA Tool"""
|
|
8
8
|
|
|
9
9
|
name = "codebase_qa"
|
|
10
10
|
description = "Answer questions about the codebase, can query and understand code functionality, structure, and implementation details"
|
|
@@ -29,7 +29,7 @@ class CodebaseQATool:
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
def execute(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
32
|
-
"""
|
|
32
|
+
"""Execute codebase QA"""
|
|
33
33
|
try:
|
|
34
34
|
dir = params.get("dir")
|
|
35
35
|
question = params["question"]
|
|
@@ -42,7 +42,7 @@ class CodebaseQATool:
|
|
|
42
42
|
return {
|
|
43
43
|
"success": False,
|
|
44
44
|
"stdout": "",
|
|
45
|
-
"stderr": "
|
|
45
|
+
"stderr": "Error: Current directory is not in a Git repository",
|
|
46
46
|
"error": "NotInGitRepository"
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -62,13 +62,11 @@ class CodebaseQATool:
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
except Exception as e:
|
|
65
|
-
PrettyOutput.print(f"
|
|
65
|
+
PrettyOutput.print(f"Codebase QA error: {str(e)}", output_type=OutputType.ERROR)
|
|
66
66
|
return {
|
|
67
67
|
"success": False,
|
|
68
68
|
"stdout": "",
|
|
69
|
-
"stderr": f"
|
|
69
|
+
"stderr": f"Error executing codebase QA: {str(e)}",
|
|
70
70
|
"error": str(type(e).__name__)
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
def register():
|
|
74
|
-
return CodebaseQATool()
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from typing import Dict, Any
|
|
2
|
+
|
|
3
|
+
from jarvis.agent import Agent
|
|
4
|
+
from jarvis.utils import OutputType, PrettyOutput
|
|
5
|
+
from jarvis.jarvis_code_agent.main import system_prompt
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CodeSubAgentTool:
|
|
9
|
+
name = "create_code_sub_agent"
|
|
10
|
+
description = "Create a sub-agent to handle specific code development subtasks"
|
|
11
|
+
parameters = {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"properties": {
|
|
14
|
+
"name": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "The name of the sub-agent"
|
|
17
|
+
},
|
|
18
|
+
"subtask": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "The specific code development subtask to complete"
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
"required": ["subtask", "name"]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
27
|
+
"""Execute code development subtask"""
|
|
28
|
+
try:
|
|
29
|
+
subtask = args["subtask"]
|
|
30
|
+
name = args["name"]
|
|
31
|
+
|
|
32
|
+
PrettyOutput.print(f"Creating code sub-agent {name} for subtask: {subtask}", OutputType.INFO)
|
|
33
|
+
|
|
34
|
+
# Create sub-agent
|
|
35
|
+
sub_agent = Agent(
|
|
36
|
+
system_prompt=system_prompt,
|
|
37
|
+
name=name,
|
|
38
|
+
is_sub_agent=True
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Execute subtask
|
|
42
|
+
result = sub_agent.run(subtask)
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
"success": True,
|
|
46
|
+
"stdout": f"Code Development Subtask Results:\n\n{result}",
|
|
47
|
+
"stderr": ""
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
except Exception as e:
|
|
51
|
+
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
52
|
+
return {
|
|
53
|
+
"success": False,
|
|
54
|
+
"error": f"Failed to execute code development subtask: {str(e)}"
|
|
55
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Dict, Any
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
from jarvis.agent import Agent
|
|
4
|
+
from jarvis.agent import Agent, origin_agent_system_prompt
|
|
5
5
|
from jarvis.utils import OutputType, PrettyOutput
|
|
6
6
|
|
|
7
7
|
|
|
@@ -58,8 +58,11 @@ class SubAgentTool:
|
|
|
58
58
|
if goal:
|
|
59
59
|
task_description += f"\n\nCompletion goal:\n{goal}"
|
|
60
60
|
|
|
61
|
+
|
|
62
|
+
|
|
61
63
|
# Create sub-agent
|
|
62
64
|
sub_agent = Agent(
|
|
65
|
+
system_prompt=origin_agent_system_prompt,
|
|
63
66
|
name=agent_name,
|
|
64
67
|
is_sub_agent=True
|
|
65
68
|
)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from typing import Dict, Any
|
|
2
|
+
|
|
3
|
+
from jarvis.agent import Agent
|
|
4
|
+
from jarvis.utils import OutputType, PrettyOutput
|
|
5
|
+
from jarvis.jarvis_coder.patch_handler import PatchHandler
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CodeModifyTool:
|
|
9
|
+
name = "execute_code_modification"
|
|
10
|
+
description = "Execute code modifications according to the provided plan"
|
|
11
|
+
parameters = {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"properties": {
|
|
14
|
+
"task": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "The code modification task description"
|
|
17
|
+
},
|
|
18
|
+
"structured_plan": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"description": "Dictionary mapping file paths to their modification plans. Example: {'path/to/file.py': 'Add function foo() to handle...'}",
|
|
21
|
+
"additionalProperties": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"description": "Modification plan for a specific file"
|
|
24
|
+
},
|
|
25
|
+
"examples": [{
|
|
26
|
+
"src/file1.py": "Add error handling to process_data()",
|
|
27
|
+
"src/file2.py": "Update API endpoint URL in get_data()"
|
|
28
|
+
}]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"required": ["task", "raw_plan", "structured_plan"]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
35
|
+
"""Execute code modifications using PatchHandler"""
|
|
36
|
+
try:
|
|
37
|
+
task = args["task"]
|
|
38
|
+
structured_plan = args["structured_plan"]
|
|
39
|
+
|
|
40
|
+
PrettyOutput.print("Executing code modifications...", OutputType.INFO)
|
|
41
|
+
|
|
42
|
+
# Create patch handler instance
|
|
43
|
+
patch_handler = PatchHandler()
|
|
44
|
+
|
|
45
|
+
# Apply patches and handle the process
|
|
46
|
+
success = patch_handler.handle_patch_application(
|
|
47
|
+
feature=task,
|
|
48
|
+
structed_plan=structured_plan
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if not success:
|
|
52
|
+
return {
|
|
53
|
+
"success": False,
|
|
54
|
+
"error": "Code modification was cancelled or failed",
|
|
55
|
+
"stdout": "Changes have been rolled back",
|
|
56
|
+
"stderr": ""
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
"success": True,
|
|
61
|
+
"stdout": "Code modifications have been successfully applied and committed",
|
|
62
|
+
"stderr": ""
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
except Exception as e:
|
|
66
|
+
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
67
|
+
return {
|
|
68
|
+
"success": False,
|
|
69
|
+
"error": f"Failed to execute code modifications: {str(e)}",
|
|
70
|
+
"stdout": "",
|
|
71
|
+
"stderr": str(e)
|
|
72
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
from typing import Dict, Any
|
|
1
|
+
from typing import Dict, Any
|
|
2
2
|
import os
|
|
3
|
-
from enum import Enum
|
|
4
3
|
|
|
5
4
|
from jarvis.utils import OutputType, PrettyOutput
|
|
6
5
|
|
|
@@ -36,15 +35,15 @@ class FileOperationTool:
|
|
|
36
35
|
|
|
37
36
|
|
|
38
37
|
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
39
|
-
"""
|
|
38
|
+
"""Execute file operations"""
|
|
40
39
|
try:
|
|
41
40
|
operation = args["operation"]
|
|
42
41
|
filepath = args["filepath"]
|
|
43
42
|
encoding = args.get("encoding", "utf-8")
|
|
44
43
|
|
|
45
|
-
#
|
|
44
|
+
# Record the operation and the full path
|
|
46
45
|
abs_path = os.path.abspath(filepath)
|
|
47
|
-
PrettyOutput.print(f"
|
|
46
|
+
PrettyOutput.print(f"File operation: {operation} - {abs_path}", OutputType.INFO)
|
|
48
47
|
|
|
49
48
|
if operation == "exists":
|
|
50
49
|
exists = os.path.exists(filepath)
|
|
@@ -61,15 +60,15 @@ class FileOperationTool:
|
|
|
61
60
|
"error": f"文件不存在: {filepath}"
|
|
62
61
|
}
|
|
63
62
|
|
|
64
|
-
#
|
|
63
|
+
# Check file size
|
|
65
64
|
if os.path.getsize(filepath) > 10 * 1024 * 1024: # 10MB
|
|
66
65
|
return {
|
|
67
66
|
"success": False,
|
|
68
|
-
"error": "
|
|
67
|
+
"error": "File too large (>10MB)"
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
content = open(filepath, 'r', encoding=encoding).read()
|
|
71
|
+
PrettyOutput.print(content, OutputType.INFO)
|
|
73
72
|
return {
|
|
74
73
|
"success": True,
|
|
75
74
|
"stdout": content,
|
|
@@ -80,10 +79,10 @@ class FileOperationTool:
|
|
|
80
79
|
if not args.get("content"):
|
|
81
80
|
return {
|
|
82
81
|
"success": False,
|
|
83
|
-
"error": "
|
|
82
|
+
"error": "Write/append operation requires providing the content parameter"
|
|
84
83
|
}
|
|
85
84
|
|
|
86
|
-
#
|
|
85
|
+
# Create directory (if it doesn't exist)
|
|
87
86
|
os.makedirs(os.path.dirname(os.path.abspath(filepath)), exist_ok=True)
|
|
88
87
|
|
|
89
88
|
mode = 'a' if operation == "append" else 'w'
|
|
@@ -92,19 +91,19 @@ class FileOperationTool:
|
|
|
92
91
|
|
|
93
92
|
return {
|
|
94
93
|
"success": True,
|
|
95
|
-
"stdout": f"
|
|
94
|
+
"stdout": f"Successfully {operation} content to {filepath}",
|
|
96
95
|
"stderr": ""
|
|
97
96
|
}
|
|
98
97
|
|
|
99
98
|
else:
|
|
100
99
|
return {
|
|
101
100
|
"success": False,
|
|
102
|
-
"error": f"
|
|
101
|
+
"error": f"Unknown operation: {operation}"
|
|
103
102
|
}
|
|
104
103
|
|
|
105
104
|
except Exception as e:
|
|
106
105
|
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
107
106
|
return {
|
|
108
107
|
"success": False,
|
|
109
|
-
"error": f"
|
|
108
|
+
"error": f"File operation failed: {str(e)}"
|
|
110
109
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Any, Dict
|
|
3
|
+
from jarvis.jarvis_codebase.main import CodeBase
|
|
4
|
+
from jarvis.utils import find_git_root, PrettyOutput, OutputType
|
|
5
|
+
|
|
6
|
+
class CodebaseSearchTool:
|
|
7
|
+
"""Codebase Search Tool"""
|
|
8
|
+
|
|
9
|
+
name = "find_related_files"
|
|
10
|
+
description = "Find code files related to the given requirement by searching the codebase using NLP-based semantic search. IMPORTANT: Requires complete sentence descriptions - keywords or phrases alone will not yield comprehensive results."
|
|
11
|
+
parameters = {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"properties": {
|
|
14
|
+
"dir": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "Project root directory"
|
|
17
|
+
},
|
|
18
|
+
"query": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": """Requirement description to find related code files. Must be a complete sentence that clearly describes what you're looking for.
|
|
21
|
+
|
|
22
|
+
Good examples:
|
|
23
|
+
- 'Need to modify the user authentication process to add password validation'
|
|
24
|
+
- 'Want to update the database connection configuration to support multiple databases'
|
|
25
|
+
- 'Looking for the code that handles file upload functionality in the system'
|
|
26
|
+
|
|
27
|
+
Bad examples (will not work well):
|
|
28
|
+
- 'user auth'
|
|
29
|
+
- 'database config'
|
|
30
|
+
- 'upload'""",
|
|
31
|
+
"examples": [
|
|
32
|
+
"Need to modify the error handling in the authentication process",
|
|
33
|
+
"Want to update how the system processes user uploads",
|
|
34
|
+
"Looking for the code that manages database connections"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
"top_k": {
|
|
38
|
+
"type": "integer",
|
|
39
|
+
"description": "Number of most relevant files to return",
|
|
40
|
+
"default": 5
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"required": ["query"]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
def execute(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
47
|
+
"""Execute codebase search"""
|
|
48
|
+
try:
|
|
49
|
+
dir = params.get("dir")
|
|
50
|
+
query = params["query"]
|
|
51
|
+
top_k = params.get("top_k", 5)
|
|
52
|
+
|
|
53
|
+
# Find the root directory of the codebase
|
|
54
|
+
current_dir = os.getcwd()
|
|
55
|
+
root_dir = find_git_root(dir or current_dir)
|
|
56
|
+
if not root_dir:
|
|
57
|
+
return {
|
|
58
|
+
"success": False,
|
|
59
|
+
"stdout": "",
|
|
60
|
+
"stderr": "Error: Current directory is not in a Git repository",
|
|
61
|
+
"error": "NotInGitRepository"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
os.chdir(root_dir)
|
|
65
|
+
codebase = CodeBase(root_dir)
|
|
66
|
+
# Generate index
|
|
67
|
+
|
|
68
|
+
codebase.generate_codebase()
|
|
69
|
+
# Execute search
|
|
70
|
+
response = codebase.search_similar(query, top_k)
|
|
71
|
+
os.chdir(current_dir)
|
|
72
|
+
return {
|
|
73
|
+
"success": True,
|
|
74
|
+
"stdout": str(response),
|
|
75
|
+
"stderr": "",
|
|
76
|
+
"error": None
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
except Exception as e:
|
|
80
|
+
PrettyOutput.print(f"Codebase QA error: {str(e)}", output_type=OutputType.ERROR)
|
|
81
|
+
return {
|
|
82
|
+
"success": False,
|
|
83
|
+
"stdout": "",
|
|
84
|
+
"stderr": f"Error executing codebase QA: {str(e)}",
|
|
85
|
+
"error": str(type(e).__name__)
|
|
86
|
+
}
|