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
jarvis/tools/methodology.py
CHANGED
|
@@ -5,7 +5,7 @@ from jarvis.utils import OutputType, PrettyOutput
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class MethodologyTool:
|
|
8
|
-
"""
|
|
8
|
+
"""Experience management tool"""
|
|
9
9
|
|
|
10
10
|
name = "methodology"
|
|
11
11
|
description = "Manage problem-solving methodologies, supporting add, update, and delete operations"
|
|
@@ -31,47 +31,47 @@ class MethodologyTool:
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
def __init__(self):
|
|
34
|
-
"""
|
|
34
|
+
"""Initialize the experience management tool"""
|
|
35
35
|
self.methodology_file = os.path.expanduser("~/.jarvis_methodology")
|
|
36
36
|
self._ensure_file_exists()
|
|
37
37
|
|
|
38
38
|
def _ensure_file_exists(self):
|
|
39
|
-
"""
|
|
39
|
+
"""Ensure the methodology file exists"""
|
|
40
40
|
if not os.path.exists(self.methodology_file):
|
|
41
41
|
try:
|
|
42
42
|
with open(self.methodology_file, 'w', encoding='utf-8') as f:
|
|
43
43
|
yaml.safe_dump({}, f, allow_unicode=True)
|
|
44
44
|
except Exception as e:
|
|
45
|
-
PrettyOutput.print(f"
|
|
45
|
+
PrettyOutput.print(f"Failed to create methodology file: {str(e)}", OutputType.ERROR)
|
|
46
46
|
|
|
47
47
|
def _load_methodologies(self) -> Dict:
|
|
48
|
-
"""
|
|
48
|
+
"""Load all methodologies"""
|
|
49
49
|
try:
|
|
50
50
|
with open(self.methodology_file, 'r', encoding='utf-8') as f:
|
|
51
51
|
return yaml.safe_load(f) or {}
|
|
52
52
|
except Exception as e:
|
|
53
|
-
PrettyOutput.print(f"
|
|
53
|
+
PrettyOutput.print(f"Failed to load methodologies: {str(e)}", OutputType.ERROR)
|
|
54
54
|
return {}
|
|
55
55
|
|
|
56
56
|
def _save_methodologies(self, methodologies: Dict):
|
|
57
|
-
"""
|
|
57
|
+
"""Save all methodologies"""
|
|
58
58
|
try:
|
|
59
59
|
with open(self.methodology_file, 'w', encoding='utf-8') as f:
|
|
60
60
|
yaml.safe_dump(methodologies, f, allow_unicode=True)
|
|
61
61
|
except Exception as e:
|
|
62
|
-
PrettyOutput.print(f"
|
|
62
|
+
PrettyOutput.print(f"Failed to save methodologies: {str(e)}", OutputType.ERROR)
|
|
63
63
|
|
|
64
64
|
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
65
|
-
"""
|
|
65
|
+
"""Execute the operation of managing methodologies
|
|
66
66
|
|
|
67
67
|
Args:
|
|
68
|
-
args:
|
|
69
|
-
- operation:
|
|
70
|
-
- problem_type:
|
|
71
|
-
- content:
|
|
68
|
+
args: A dictionary containing the operation parameters
|
|
69
|
+
- operation: The operation type (delete/update/add)
|
|
70
|
+
- problem_type: The problem type
|
|
71
|
+
- content: The methodology content (required for update/add)
|
|
72
72
|
|
|
73
73
|
Returns:
|
|
74
|
-
Dict[str, Any]:
|
|
74
|
+
Dict[str, Any]: A dictionary containing the execution result
|
|
75
75
|
"""
|
|
76
76
|
operation = args.get("operation")
|
|
77
77
|
problem_type = args.get("problem_type")
|
|
@@ -80,7 +80,7 @@ class MethodologyTool:
|
|
|
80
80
|
if not operation or not problem_type:
|
|
81
81
|
return {
|
|
82
82
|
"success": False,
|
|
83
|
-
"error": "
|
|
83
|
+
"error": "Missing required parameters: operation and problem_type"
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
methodologies = self._load_methodologies()
|
|
@@ -92,50 +92,50 @@ class MethodologyTool:
|
|
|
92
92
|
self._save_methodologies(methodologies)
|
|
93
93
|
return {
|
|
94
94
|
"success": True,
|
|
95
|
-
"stdout": f"
|
|
95
|
+
"stdout": f"Deleted methodology for problem type '{problem_type}'"
|
|
96
96
|
}
|
|
97
97
|
else:
|
|
98
98
|
return {
|
|
99
99
|
"success": False,
|
|
100
|
-
"error": f"
|
|
100
|
+
"error": f"Methodology for problem type '{problem_type}' not found"
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
elif operation in ["update", "add"]:
|
|
104
104
|
if not content:
|
|
105
105
|
return {
|
|
106
106
|
"success": False,
|
|
107
|
-
"error": "
|
|
107
|
+
"error": "Need to provide methodology content"
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
methodologies[problem_type] = content
|
|
111
111
|
self._save_methodologies(methodologies)
|
|
112
112
|
|
|
113
|
-
action = "
|
|
113
|
+
action = "Update" if problem_type in methodologies else "Add"
|
|
114
114
|
return {
|
|
115
115
|
"success": True,
|
|
116
|
-
"stdout": f"
|
|
116
|
+
"stdout": f"{action} methodology for problem type '{problem_type}'"
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
else:
|
|
120
120
|
return {
|
|
121
121
|
"success": False,
|
|
122
|
-
"error": f"
|
|
122
|
+
"error": f"Unsupported operation type: {operation}"
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
except Exception as e:
|
|
126
126
|
return {
|
|
127
127
|
"success": False,
|
|
128
|
-
"error": f"
|
|
128
|
+
"error": f"Execution failed: {str(e)}"
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
def get_methodology(self, problem_type: str) -> Optional[str]:
|
|
132
|
-
"""
|
|
132
|
+
"""Get the methodology for a specific problem type
|
|
133
133
|
|
|
134
134
|
Args:
|
|
135
|
-
problem_type:
|
|
135
|
+
problem_type: The problem type
|
|
136
136
|
|
|
137
137
|
Returns:
|
|
138
|
-
Optional[str]:
|
|
138
|
+
Optional[str]: The methodology content, or None if it does not exist
|
|
139
139
|
"""
|
|
140
140
|
methodologies = self._load_methodologies()
|
|
141
141
|
return methodologies.get(problem_type)
|
jarvis/tools/rag.py
CHANGED
|
@@ -27,71 +27,71 @@ class RAGTool:
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
def __init__(self):
|
|
30
|
-
"""
|
|
31
|
-
self.rag_instances = {} #
|
|
30
|
+
"""Initialize RAG tool"""
|
|
31
|
+
self.rag_instances = {} # Cache RAG instances for different directories
|
|
32
32
|
|
|
33
33
|
def _get_rag_instance(self, dir_path: str) -> RAGCore:
|
|
34
|
-
"""
|
|
34
|
+
"""Get or create RAG instance
|
|
35
35
|
|
|
36
36
|
Args:
|
|
37
|
-
dir_path:
|
|
37
|
+
dir_path: The absolute path of the document directory
|
|
38
38
|
|
|
39
39
|
Returns:
|
|
40
|
-
RAGCore: RAG
|
|
40
|
+
RAGCore: RAG instance
|
|
41
41
|
"""
|
|
42
42
|
if dir_path not in self.rag_instances:
|
|
43
43
|
self.rag_instances[dir_path] = RAGCore(dir_path)
|
|
44
44
|
return self.rag_instances[dir_path]
|
|
45
45
|
|
|
46
46
|
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
47
|
-
"""
|
|
47
|
+
"""Execute document question and answer
|
|
48
48
|
|
|
49
49
|
Args:
|
|
50
|
-
args:
|
|
51
|
-
- dir:
|
|
52
|
-
- question:
|
|
53
|
-
- rebuild_index:
|
|
50
|
+
args: A dictionary containing parameters
|
|
51
|
+
- dir: The document directory path
|
|
52
|
+
- question: The question to ask
|
|
53
|
+
- rebuild_index: Whether to rebuild the index
|
|
54
54
|
|
|
55
55
|
Returns:
|
|
56
|
-
Dict[str, Any]:
|
|
56
|
+
Dict[str, Any]: The execution result
|
|
57
57
|
"""
|
|
58
58
|
try:
|
|
59
|
-
#
|
|
60
|
-
dir_path = os.path.expanduser(args["dir"]) #
|
|
61
|
-
dir_path = os.path.abspath(dir_path) #
|
|
59
|
+
# Get parameters
|
|
60
|
+
dir_path = os.path.expanduser(args["dir"]) # Expand ~ paths
|
|
61
|
+
dir_path = os.path.abspath(dir_path) # Convert to absolute path
|
|
62
62
|
question = args["question"]
|
|
63
63
|
rebuild_index = args.get("rebuild_index", False)
|
|
64
64
|
|
|
65
|
-
#
|
|
65
|
+
# Check if the directory exists
|
|
66
66
|
if not os.path.exists(dir_path):
|
|
67
67
|
return {
|
|
68
68
|
"success": False,
|
|
69
|
-
"error": f"
|
|
69
|
+
"error": f"Directory does not exist: {dir_path}"
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
#
|
|
72
|
+
# Check if it is a directory
|
|
73
73
|
if not os.path.isdir(dir_path):
|
|
74
74
|
return {
|
|
75
75
|
"success": False,
|
|
76
|
-
"error": f"
|
|
76
|
+
"error": f"The path is not a directory: {dir_path}"
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
#
|
|
79
|
+
# Get RAG instance
|
|
80
80
|
rag = self._get_rag_instance(dir_path)
|
|
81
81
|
|
|
82
|
-
#
|
|
82
|
+
# If you need to rebuild the index or the index does not exist
|
|
83
83
|
if rebuild_index or not rag.is_index_built():
|
|
84
|
-
PrettyOutput.print("
|
|
84
|
+
PrettyOutput.print("Building document index...", OutputType.INFO)
|
|
85
85
|
rag.build_index(dir_path)
|
|
86
86
|
|
|
87
|
-
#
|
|
88
|
-
PrettyOutput.print(f"
|
|
87
|
+
# Execute question and answer
|
|
88
|
+
PrettyOutput.print(f"Question: {question}", OutputType.INFO)
|
|
89
89
|
response = rag.ask(question)
|
|
90
90
|
|
|
91
91
|
if response is None:
|
|
92
92
|
return {
|
|
93
93
|
"success": False,
|
|
94
|
-
"error": "
|
|
94
|
+
"error": "Failed to get answer, possibly no relevant documents found"
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
return {
|
|
@@ -101,20 +101,20 @@ class RAGTool:
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
except Exception as e:
|
|
104
|
-
PrettyOutput.print(f"
|
|
104
|
+
PrettyOutput.print(f"Document question and answer failed: {str(e)}", OutputType.ERROR)
|
|
105
105
|
return {
|
|
106
106
|
"success": False,
|
|
107
|
-
"error": f"
|
|
107
|
+
"error": f"Execution failed: {str(e)}"
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
def main():
|
|
111
|
-
"""
|
|
111
|
+
"""Run the tool directly from the command line"""
|
|
112
112
|
import argparse
|
|
113
113
|
|
|
114
|
-
parser = argparse.ArgumentParser(description='
|
|
115
|
-
parser.add_argument('--dir', required=True, help='
|
|
116
|
-
parser.add_argument('--question', required=True, help='
|
|
117
|
-
parser.add_argument('--rebuild', action='store_true', help='
|
|
114
|
+
parser = argparse.ArgumentParser(description='Document question and answer tool')
|
|
115
|
+
parser.add_argument('--dir', required=True, help='Document directory path')
|
|
116
|
+
parser.add_argument('--question', required=True, help='The question to ask')
|
|
117
|
+
parser.add_argument('--rebuild', action='store_true', help='Rebuild index')
|
|
118
118
|
args = parser.parse_args()
|
|
119
119
|
|
|
120
120
|
tool = RAGTool()
|
|
@@ -125,7 +125,7 @@ def main():
|
|
|
125
125
|
})
|
|
126
126
|
|
|
127
127
|
if result["success"]:
|
|
128
|
-
PrettyOutput.print("\
|
|
128
|
+
PrettyOutput.print("\nAnswer:", OutputType.INFO)
|
|
129
129
|
PrettyOutput.print(result["stdout"], OutputType.INFO)
|
|
130
130
|
else:
|
|
131
131
|
PrettyOutput.print(result["error"], OutputType.ERROR)
|
jarvis/tools/registry.py
CHANGED
|
@@ -10,67 +10,93 @@ from jarvis.tools.base import Tool
|
|
|
10
10
|
from jarvis.utils import OutputType, PrettyOutput, get_max_context_length
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
|
|
14
|
+
def load_tools() -> str:
|
|
15
|
+
"""Load tools"""
|
|
16
|
+
PrettyOutput.section("Available tools", OutputType.PLANNING)
|
|
17
|
+
tools = ToolRegistry.get_global_tool_registry().get_all_tools()
|
|
18
|
+
if tools:
|
|
19
|
+
tools_prompt = "Available tools:\n"
|
|
20
|
+
for tool in tools:
|
|
21
|
+
PrettyOutput.print(f"{tool['name']}: {tool['description']}", OutputType.INFO)
|
|
22
|
+
tools_prompt += f"- Name: {tool['name']}\n"
|
|
23
|
+
tools_prompt += f" Description: {tool['description']}\n"
|
|
24
|
+
tools_prompt += f" Parameters: {tool['parameters']}\n"
|
|
25
|
+
tools_prompt += f" Usage Format: <TOOL_CALL>\n"
|
|
26
|
+
tools_prompt += """
|
|
27
|
+
Tool Usage Format:
|
|
28
|
+
|
|
29
|
+
<TOOL_CALL>
|
|
30
|
+
name: tool_name
|
|
31
|
+
arguments:
|
|
32
|
+
param1: value1
|
|
33
|
+
param2: value2
|
|
34
|
+
</TOOL_CALL>
|
|
35
|
+
---------------------------------------------
|
|
36
|
+
"""
|
|
37
|
+
return tools_prompt
|
|
38
|
+
return ""
|
|
39
|
+
|
|
13
40
|
class ToolRegistry:
|
|
14
41
|
global_tool_registry = None # type: ignore
|
|
15
42
|
def __init__(self):
|
|
16
|
-
"""
|
|
17
|
-
"""
|
|
43
|
+
"""Initialize tool registry"""
|
|
18
44
|
self.tools: Dict[str, Tool] = {}
|
|
19
|
-
#
|
|
45
|
+
# Load built-in tools and external tools
|
|
20
46
|
self._load_builtin_tools()
|
|
21
47
|
self._load_external_tools()
|
|
22
|
-
#
|
|
48
|
+
# Ensure max_context_length is an integer
|
|
23
49
|
self.max_context_length = int(get_max_context_length() * 0.8)
|
|
24
50
|
|
|
25
51
|
@staticmethod
|
|
26
52
|
def get_global_tool_registry():
|
|
27
|
-
"""
|
|
53
|
+
"""Get the global tool registry"""
|
|
28
54
|
if ToolRegistry.global_tool_registry is None:
|
|
29
55
|
ToolRegistry.global_tool_registry = ToolRegistry()
|
|
30
56
|
return ToolRegistry.global_tool_registry
|
|
31
57
|
|
|
32
58
|
def _load_builtin_tools(self):
|
|
33
|
-
"""
|
|
59
|
+
"""Load tools from the built-in tools directory"""
|
|
34
60
|
tools_dir = Path(__file__).parent
|
|
35
61
|
|
|
36
|
-
#
|
|
62
|
+
# Iterate through all .py files in the directory
|
|
37
63
|
for file_path in tools_dir.glob("*.py"):
|
|
38
|
-
#
|
|
64
|
+
# Skip base.py and __init__.py
|
|
39
65
|
if file_path.name in ["base.py", "__init__.py", "registry.py"]:
|
|
40
66
|
continue
|
|
41
67
|
|
|
42
68
|
self.register_tool_by_file(str(file_path))
|
|
43
69
|
|
|
44
70
|
def _load_external_tools(self):
|
|
45
|
-
"""
|
|
71
|
+
"""Load external tools from ~/.jarvis_tools"""
|
|
46
72
|
external_tools_dir = Path.home() / '.jarvis_tools'
|
|
47
73
|
if not external_tools_dir.exists():
|
|
48
74
|
return
|
|
49
75
|
|
|
50
|
-
#
|
|
76
|
+
# Iterate through all .py files in the directory
|
|
51
77
|
for file_path in external_tools_dir.glob("*.py"):
|
|
52
|
-
#
|
|
78
|
+
# Skip __init__.py
|
|
53
79
|
if file_path.name == "__init__.py":
|
|
54
80
|
continue
|
|
55
81
|
|
|
56
82
|
self.register_tool_by_file(str(file_path))
|
|
57
83
|
|
|
58
84
|
def register_tool_by_file(self, file_path: str):
|
|
59
|
-
"""
|
|
85
|
+
"""Load and register tools from a specified file
|
|
60
86
|
|
|
61
87
|
Args:
|
|
62
|
-
file_path:
|
|
88
|
+
file_path: The path of the tool file
|
|
63
89
|
|
|
64
90
|
Returns:
|
|
65
|
-
bool:
|
|
91
|
+
bool: Whether the tool is loaded successfully
|
|
66
92
|
"""
|
|
67
93
|
try:
|
|
68
|
-
p_file_path = Path(file_path).resolve() #
|
|
94
|
+
p_file_path = Path(file_path).resolve() # Get the absolute path
|
|
69
95
|
if not p_file_path.exists() or not p_file_path.is_file():
|
|
70
96
|
PrettyOutput.print(f"File does not exist: {p_file_path}", OutputType.ERROR)
|
|
71
97
|
return False
|
|
72
98
|
|
|
73
|
-
#
|
|
99
|
+
# Dynamically import the module
|
|
74
100
|
module_name = p_file_path.stem
|
|
75
101
|
spec = importlib.util.spec_from_file_location(module_name, p_file_path) # type: ignore
|
|
76
102
|
if not spec or not spec.loader:
|
|
@@ -78,30 +104,29 @@ class ToolRegistry:
|
|
|
78
104
|
return False
|
|
79
105
|
|
|
80
106
|
module = importlib.util.module_from_spec(spec) # type: ignore
|
|
81
|
-
sys.modules[module_name] = module #
|
|
107
|
+
sys.modules[module_name] = module # Add to sys.modules to support relative imports
|
|
82
108
|
spec.loader.exec_module(module)
|
|
83
109
|
|
|
84
|
-
#
|
|
110
|
+
# Find the tool class in the module
|
|
85
111
|
tool_found = False
|
|
86
112
|
for item_name in dir(module):
|
|
87
113
|
item = getattr(module, item_name)
|
|
88
|
-
#
|
|
114
|
+
# Check if it is a class and has the necessary attributes
|
|
89
115
|
if (isinstance(item, type) and
|
|
90
116
|
hasattr(item, 'name') and
|
|
91
117
|
hasattr(item, 'description') and
|
|
92
118
|
hasattr(item, 'parameters')):
|
|
93
119
|
|
|
94
|
-
#
|
|
120
|
+
# Instantiate the tool class, passing in the model and output processor
|
|
95
121
|
tool_instance = item()
|
|
96
122
|
|
|
97
|
-
#
|
|
123
|
+
# Register the tool
|
|
98
124
|
self.register_tool(
|
|
99
125
|
name=tool_instance.name,
|
|
100
126
|
description=tool_instance.description,
|
|
101
127
|
parameters=tool_instance.parameters,
|
|
102
128
|
func=tool_instance.execute
|
|
103
129
|
)
|
|
104
|
-
PrettyOutput.print(f"Loaded tool from {p_file_path}: {tool_instance.name}: {tool_instance.description}", OutputType.SUCCESS)
|
|
105
130
|
tool_found = True
|
|
106
131
|
break
|
|
107
132
|
|
|
@@ -116,43 +141,54 @@ class ToolRegistry:
|
|
|
116
141
|
return False
|
|
117
142
|
|
|
118
143
|
def register_tool(self, name: str, description: str, parameters: Dict, func: Callable):
|
|
119
|
-
"""
|
|
144
|
+
"""Register a new tool"""
|
|
120
145
|
self.tools[name] = Tool(name, description, parameters, func)
|
|
121
146
|
|
|
122
147
|
def get_tool(self, name: str) -> Optional[Tool]:
|
|
123
|
-
"""
|
|
148
|
+
"""Get a tool"""
|
|
124
149
|
return self.tools.get(name)
|
|
125
150
|
|
|
126
151
|
def get_all_tools(self) -> List[Dict]:
|
|
127
|
-
"""
|
|
152
|
+
"""Get all tools in Ollama format definition"""
|
|
128
153
|
return [tool.to_dict() for tool in self.tools.values()]
|
|
129
154
|
|
|
130
155
|
def execute_tool(self, name: str, arguments: Dict) -> Dict[str, Any]:
|
|
131
|
-
"""
|
|
156
|
+
"""Execute a specified tool"""
|
|
132
157
|
tool = self.get_tool(name)
|
|
133
158
|
if tool is None:
|
|
134
|
-
return {"success": False, "error": f"Tool {name} does not exist"}
|
|
159
|
+
return {"success": False, "error": f"Tool {name} does not exist, available tools: {', '.join(self.tools.keys())}"}
|
|
135
160
|
return tool.execute(arguments)
|
|
136
161
|
|
|
137
162
|
def handle_tool_calls(self, tool_calls: List[Dict]) -> str:
|
|
138
|
-
"""
|
|
163
|
+
"""Handle tool calls, only process the first tool"""
|
|
139
164
|
try:
|
|
140
165
|
if not tool_calls:
|
|
141
166
|
return ""
|
|
142
167
|
|
|
143
|
-
#
|
|
168
|
+
# Only process the first tool call
|
|
144
169
|
tool_call = tool_calls[0]
|
|
145
170
|
name = tool_call["name"]
|
|
146
171
|
args = tool_call["arguments"]
|
|
172
|
+
|
|
173
|
+
tool_call_help = """
|
|
174
|
+
Tool Usage Format:
|
|
175
|
+
|
|
176
|
+
<TOOL_CALL>
|
|
177
|
+
name: tool_name
|
|
178
|
+
arguments:
|
|
179
|
+
param1: value1
|
|
180
|
+
param2: value2
|
|
181
|
+
</TOOL_CALL>
|
|
182
|
+
"""
|
|
147
183
|
|
|
148
184
|
if isinstance(args, str):
|
|
149
185
|
try:
|
|
150
186
|
args = json.loads(args)
|
|
151
187
|
except json.JSONDecodeError:
|
|
152
|
-
PrettyOutput.print(f"Invalid tool parameters format: {name}", OutputType.ERROR)
|
|
188
|
+
PrettyOutput.print(f"Invalid tool parameters format: {name} {tool_call_help}", OutputType.ERROR)
|
|
153
189
|
return ""
|
|
154
190
|
|
|
155
|
-
#
|
|
191
|
+
# Display tool call information
|
|
156
192
|
PrettyOutput.section(f"Executing tool: {name}", OutputType.TOOL)
|
|
157
193
|
if isinstance(args, dict):
|
|
158
194
|
for key, value in args.items():
|
|
@@ -160,10 +196,10 @@ class ToolRegistry:
|
|
|
160
196
|
else:
|
|
161
197
|
PrettyOutput.print(f"Parameter: {args}", OutputType.DEBUG)
|
|
162
198
|
|
|
163
|
-
#
|
|
199
|
+
# Execute tool call
|
|
164
200
|
result = self.execute_tool(name, args)
|
|
165
201
|
|
|
166
|
-
#
|
|
202
|
+
# Process the result
|
|
167
203
|
if result["success"]:
|
|
168
204
|
stdout = result["stdout"]
|
|
169
205
|
stderr = result.get("stderr", "")
|
|
@@ -173,16 +209,16 @@ class ToolRegistry:
|
|
|
173
209
|
if stderr:
|
|
174
210
|
output_parts.append(f"Error:\n{stderr}")
|
|
175
211
|
output = "\n\n".join(output_parts)
|
|
176
|
-
output = "
|
|
212
|
+
output = "Tool execution successful, no output and error" if not output else output
|
|
177
213
|
PrettyOutput.section("Execution successful", OutputType.SUCCESS)
|
|
178
214
|
|
|
179
|
-
#
|
|
215
|
+
# If the output exceeds 4k characters, use a large model to summarize
|
|
180
216
|
if len(output) > self.max_context_length:
|
|
181
217
|
try:
|
|
182
218
|
PrettyOutput.print("Output is too long, summarizing...", OutputType.PROGRESS)
|
|
183
219
|
model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
|
|
184
220
|
|
|
185
|
-
#
|
|
221
|
+
# If the output exceeds the maximum context length, only take the last part
|
|
186
222
|
max_len = self.max_context_length
|
|
187
223
|
if len(output) > max_len:
|
|
188
224
|
output_to_summarize = output[-max_len:]
|
jarvis/tools/search.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Dict, Any, List
|
|
2
2
|
from jarvis.models.registry import PlatformRegistry
|
|
3
3
|
from jarvis.utils import PrettyOutput, OutputType
|
|
4
|
-
from jarvis.tools.
|
|
4
|
+
from jarvis.tools.read_webpage import WebpageTool
|
|
5
5
|
from playwright.sync_api import sync_playwright
|
|
6
6
|
from urllib.parse import quote
|
|
7
7
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from typing import Dict, Any, List
|
|
2
|
+
|
|
3
|
+
from jarvis.utils import OutputType, PrettyOutput
|
|
4
|
+
from jarvis.jarvis_coder.file_select import select_files
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CodeFileSelecterTool:
|
|
8
|
+
name = "select_code_files"
|
|
9
|
+
description = "Select and manage code files for modification with interactive file selection"
|
|
10
|
+
parameters = {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"properties": {
|
|
13
|
+
"related_files": {
|
|
14
|
+
"type": "array",
|
|
15
|
+
"items": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
},
|
|
18
|
+
"description": "List of initially related files",
|
|
19
|
+
"default": []
|
|
20
|
+
},
|
|
21
|
+
"root_dir": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"description": "Root directory of the codebase",
|
|
24
|
+
"default": "."
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"required": ["related_files"]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
31
|
+
"""Execute interactive file selection"""
|
|
32
|
+
try:
|
|
33
|
+
related_files = args["related_files"]
|
|
34
|
+
root_dir = args.get("root_dir", ".")
|
|
35
|
+
|
|
36
|
+
PrettyOutput.print("Starting interactive file selection...", OutputType.INFO)
|
|
37
|
+
|
|
38
|
+
# Use file_select module to handle file selection
|
|
39
|
+
selected_files = select_files(
|
|
40
|
+
related_files=related_files,
|
|
41
|
+
root_dir=root_dir
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Format output for display
|
|
45
|
+
output = "Selected files:\n"
|
|
46
|
+
for file in selected_files:
|
|
47
|
+
output += f"- {file}\n"
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
"success": True,
|
|
51
|
+
"stdout": output,
|
|
52
|
+
"stderr": "",
|
|
53
|
+
"selected_files": selected_files # Return the selected files for other tools to use
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
except Exception as e:
|
|
57
|
+
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
58
|
+
return {
|
|
59
|
+
"success": False,
|
|
60
|
+
"error": f"Failed to select files: {str(e)}",
|
|
61
|
+
"stdout": "",
|
|
62
|
+
"stderr": str(e),
|
|
63
|
+
"selected_files": [] # Return empty list on error
|
|
64
|
+
}
|