jarvis-ai-assistant 0.1.93__py3-none-any.whl → 0.1.97__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 -144
- jarvis/jarvis_codebase/main.py +87 -54
- jarvis/jarvis_coder/git_utils.py +4 -7
- jarvis/jarvis_coder/main.py +17 -22
- jarvis/jarvis_coder/patch_handler.py +141 -442
- jarvis/jarvis_coder/plan_generator.py +64 -36
- jarvis/jarvis_platform/main.py +1 -1
- jarvis/jarvis_rag/main.py +1 -1
- jarvis/jarvis_smart_shell/main.py +15 -15
- jarvis/main.py +24 -24
- jarvis/models/ai8.py +22 -22
- jarvis/models/base.py +17 -13
- jarvis/models/kimi.py +31 -31
- jarvis/models/ollama.py +28 -28
- jarvis/models/openai.py +22 -24
- jarvis/models/oyi.py +25 -25
- jarvis/models/registry.py +33 -34
- jarvis/tools/ask_user.py +5 -5
- jarvis/tools/base.py +2 -2
- jarvis/tools/chdir.py +9 -9
- jarvis/tools/codebase_qa.py +4 -4
- jarvis/tools/coder.py +4 -4
- jarvis/tools/file_ops.py +1 -1
- jarvis/tools/generator.py +23 -23
- jarvis/tools/methodology.py +4 -4
- jarvis/tools/rag.py +4 -4
- jarvis/tools/registry.py +38 -38
- jarvis/tools/search.py +42 -42
- jarvis/tools/shell.py +13 -13
- jarvis/tools/sub_agent.py +16 -16
- jarvis/tools/thinker.py +41 -41
- jarvis/tools/webpage.py +17 -17
- jarvis/utils.py +59 -60
- {jarvis_ai_assistant-0.1.93.dist-info → jarvis_ai_assistant-0.1.97.dist-info}/METADATA +1 -1
- jarvis_ai_assistant-0.1.97.dist-info/RECORD +47 -0
- jarvis_ai_assistant-0.1.93.dist-info/RECORD +0 -47
- {jarvis_ai_assistant-0.1.93.dist-info → jarvis_ai_assistant-0.1.97.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.93.dist-info → jarvis_ai_assistant-0.1.97.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.93.dist-info → jarvis_ai_assistant-0.1.97.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.93.dist-info → jarvis_ai_assistant-0.1.97.dist-info}/top_level.txt +0 -0
jarvis/tools/registry.py
CHANGED
|
@@ -39,7 +39,7 @@ class ToolRegistry:
|
|
|
39
39
|
if file_path.name in ["base.py", "__init__.py", "registry.py"]:
|
|
40
40
|
continue
|
|
41
41
|
|
|
42
|
-
self.register_tool_by_file(file_path)
|
|
42
|
+
self.register_tool_by_file(str(file_path))
|
|
43
43
|
|
|
44
44
|
def _load_external_tools(self):
|
|
45
45
|
"""从~/.jarvis_tools加载外部工具"""
|
|
@@ -53,7 +53,7 @@ class ToolRegistry:
|
|
|
53
53
|
if file_path.name == "__init__.py":
|
|
54
54
|
continue
|
|
55
55
|
|
|
56
|
-
self.register_tool_by_file(file_path)
|
|
56
|
+
self.register_tool_by_file(str(file_path))
|
|
57
57
|
|
|
58
58
|
def register_tool_by_file(self, file_path: str):
|
|
59
59
|
"""从指定文件加载并注册工具
|
|
@@ -65,19 +65,19 @@ class ToolRegistry:
|
|
|
65
65
|
bool: 是否成功加载工具
|
|
66
66
|
"""
|
|
67
67
|
try:
|
|
68
|
-
|
|
69
|
-
if not
|
|
70
|
-
PrettyOutput.print(f"
|
|
68
|
+
p_file_path = Path(file_path).resolve() # 获取绝对路径
|
|
69
|
+
if not p_file_path.exists() or not p_file_path.is_file():
|
|
70
|
+
PrettyOutput.print(f"File does not exist: {p_file_path}", OutputType.ERROR)
|
|
71
71
|
return False
|
|
72
72
|
|
|
73
73
|
# 动态导入模块
|
|
74
|
-
module_name =
|
|
75
|
-
spec = importlib.util.spec_from_file_location(module_name,
|
|
74
|
+
module_name = p_file_path.stem
|
|
75
|
+
spec = importlib.util.spec_from_file_location(module_name, p_file_path) # type: ignore
|
|
76
76
|
if not spec or not spec.loader:
|
|
77
|
-
PrettyOutput.print(f"
|
|
77
|
+
PrettyOutput.print(f"Failed to load module: {p_file_path}", OutputType.ERROR)
|
|
78
78
|
return False
|
|
79
79
|
|
|
80
|
-
module = importlib.util.module_from_spec(spec)
|
|
80
|
+
module = importlib.util.module_from_spec(spec) # type: ignore
|
|
81
81
|
sys.modules[module_name] = module # 添加到 sys.modules 以支持相对导入
|
|
82
82
|
spec.loader.exec_module(module)
|
|
83
83
|
|
|
@@ -101,18 +101,18 @@ class ToolRegistry:
|
|
|
101
101
|
parameters=tool_instance.parameters,
|
|
102
102
|
func=tool_instance.execute
|
|
103
103
|
)
|
|
104
|
-
PrettyOutput.print(f"
|
|
104
|
+
PrettyOutput.print(f"Loaded tool from {p_file_path}: {tool_instance.name}: {tool_instance.description}", OutputType.SUCCESS)
|
|
105
105
|
tool_found = True
|
|
106
106
|
break
|
|
107
107
|
|
|
108
108
|
if not tool_found:
|
|
109
|
-
PrettyOutput.print(f"
|
|
109
|
+
PrettyOutput.print(f"No valid tool class found in the file: {p_file_path}", OutputType.WARNING)
|
|
110
110
|
return False
|
|
111
111
|
|
|
112
112
|
return True
|
|
113
113
|
|
|
114
114
|
except Exception as e:
|
|
115
|
-
PrettyOutput.print(f"
|
|
115
|
+
PrettyOutput.print(f"Failed to load tool from {p_file_path.name}: {str(e)}", OutputType.ERROR)
|
|
116
116
|
return False
|
|
117
117
|
|
|
118
118
|
def register_tool(self, name: str, description: str, parameters: Dict, func: Callable):
|
|
@@ -149,16 +149,16 @@ class ToolRegistry:
|
|
|
149
149
|
try:
|
|
150
150
|
args = json.loads(args)
|
|
151
151
|
except json.JSONDecodeError:
|
|
152
|
-
PrettyOutput.print(f"
|
|
152
|
+
PrettyOutput.print(f"Invalid tool parameters format: {name}", OutputType.ERROR)
|
|
153
153
|
return ""
|
|
154
154
|
|
|
155
155
|
# 显示工具调用信息
|
|
156
|
-
PrettyOutput.section(f"
|
|
156
|
+
PrettyOutput.section(f"Executing tool: {name}", OutputType.TOOL)
|
|
157
157
|
if isinstance(args, dict):
|
|
158
158
|
for key, value in args.items():
|
|
159
|
-
PrettyOutput.print(f"
|
|
159
|
+
PrettyOutput.print(f"Parameter: {key} = {value}", OutputType.DEBUG)
|
|
160
160
|
else:
|
|
161
|
-
PrettyOutput.print(f"
|
|
161
|
+
PrettyOutput.print(f"Parameter: {args}", OutputType.DEBUG)
|
|
162
162
|
|
|
163
163
|
# 执行工具调用
|
|
164
164
|
result = self.execute_tool(name, args)
|
|
@@ -169,58 +169,58 @@ class ToolRegistry:
|
|
|
169
169
|
stderr = result.get("stderr", "")
|
|
170
170
|
output_parts = []
|
|
171
171
|
if stdout:
|
|
172
|
-
output_parts.append(f"
|
|
172
|
+
output_parts.append(f"Output:\n{stdout}")
|
|
173
173
|
if stderr:
|
|
174
|
-
output_parts.append(f"
|
|
174
|
+
output_parts.append(f"Error:\n{stderr}")
|
|
175
175
|
output = "\n\n".join(output_parts)
|
|
176
|
-
output = "
|
|
177
|
-
PrettyOutput.section("
|
|
176
|
+
output = "No output and error" if not output else output
|
|
177
|
+
PrettyOutput.section("Execution successful", OutputType.SUCCESS)
|
|
178
178
|
|
|
179
179
|
# 如果输出超过4k字符,使用大模型总结
|
|
180
180
|
if len(output) > self.max_context_length:
|
|
181
181
|
try:
|
|
182
|
-
PrettyOutput.print("
|
|
182
|
+
PrettyOutput.print("Output is too long, summarizing...", OutputType.PROGRESS)
|
|
183
183
|
model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
|
|
184
184
|
|
|
185
185
|
# 如果输出超过最大上下文长度,只取最后部分
|
|
186
186
|
max_len = self.max_context_length
|
|
187
187
|
if len(output) > max_len:
|
|
188
188
|
output_to_summarize = output[-max_len:]
|
|
189
|
-
truncation_notice = f"\n(
|
|
189
|
+
truncation_notice = f"\n(Note: Due to the length of the output, only the last {max_len} characters are summarized)"
|
|
190
190
|
else:
|
|
191
191
|
output_to_summarize = output
|
|
192
192
|
truncation_notice = ""
|
|
193
193
|
|
|
194
|
-
prompt = f"""
|
|
195
|
-
1.
|
|
196
|
-
2.
|
|
197
|
-
3.
|
|
198
|
-
4.
|
|
194
|
+
prompt = f"""Please summarize the execution result of the following tool, extracting key information and important results. Note:
|
|
195
|
+
1. Keep all important numerical values, paths, error information, etc.
|
|
196
|
+
2. Maintain the accuracy of the results
|
|
197
|
+
3. Describe the main content in concise language
|
|
198
|
+
4. If there is error information, ensure it is included in the summary
|
|
199
199
|
|
|
200
|
-
|
|
201
|
-
|
|
200
|
+
Tool name: {name}
|
|
201
|
+
Execution result:
|
|
202
202
|
{output_to_summarize}
|
|
203
203
|
|
|
204
|
-
|
|
204
|
+
Please provide a summary:"""
|
|
205
205
|
|
|
206
|
-
summary = model.
|
|
207
|
-
output = f"""---
|
|
206
|
+
summary = model.chat_until_success(prompt)
|
|
207
|
+
output = f"""--- Original output is too long, here is the summary ---{truncation_notice}
|
|
208
208
|
|
|
209
209
|
{summary}
|
|
210
210
|
|
|
211
|
-
---
|
|
211
|
+
--- Summary ends ---"""
|
|
212
212
|
|
|
213
213
|
except Exception as e:
|
|
214
|
-
PrettyOutput.print(f"
|
|
215
|
-
output = f"
|
|
214
|
+
PrettyOutput.print(f"Summary failed: {str(e)}", OutputType.ERROR)
|
|
215
|
+
output = f"Output is too long ({len(output)} characters), it is recommended to view the original output.\nPreview of the first 300 characters:\n{output[:300]}..."
|
|
216
216
|
|
|
217
217
|
else:
|
|
218
218
|
error_msg = result["error"]
|
|
219
|
-
output = f"
|
|
220
|
-
PrettyOutput.section("
|
|
219
|
+
output = f"Execution failed: {error_msg}"
|
|
220
|
+
PrettyOutput.section("Execution failed", OutputType.ERROR)
|
|
221
221
|
|
|
222
222
|
return output
|
|
223
223
|
|
|
224
224
|
except Exception as e:
|
|
225
|
-
PrettyOutput.print(f"
|
|
225
|
+
PrettyOutput.print(f"Tool execution failed: {str(e)}", OutputType.ERROR)
|
|
226
226
|
return f"Tool call failed: {str(e)}"
|
jarvis/tools/search.py
CHANGED
|
@@ -58,26 +58,26 @@ def bing_search(query):
|
|
|
58
58
|
return summaries
|
|
59
59
|
|
|
60
60
|
except Exception as error:
|
|
61
|
-
PrettyOutput.print(f"
|
|
61
|
+
PrettyOutput.print(f"Search error: {str(error)}", OutputType.ERROR)
|
|
62
62
|
return None
|
|
63
63
|
|
|
64
64
|
class SearchTool:
|
|
65
65
|
name = "search"
|
|
66
|
-
description = "
|
|
66
|
+
description = "Use Bing search engine to search for information, and extract key information based on the question"
|
|
67
67
|
parameters = {
|
|
68
68
|
"type": "object",
|
|
69
69
|
"properties": {
|
|
70
70
|
"query": {
|
|
71
71
|
"type": "string",
|
|
72
|
-
"description": "
|
|
72
|
+
"description": "Search keywords"
|
|
73
73
|
},
|
|
74
74
|
"question": {
|
|
75
75
|
"type": "string",
|
|
76
|
-
"description": "
|
|
76
|
+
"description": "Specific question to answer, used to extract relevant information from search results"
|
|
77
77
|
},
|
|
78
78
|
"max_results": {
|
|
79
79
|
"type": "integer",
|
|
80
|
-
"description": "
|
|
80
|
+
"description": "Maximum number of search results",
|
|
81
81
|
"default": 3
|
|
82
82
|
}
|
|
83
83
|
},
|
|
@@ -85,12 +85,12 @@ class SearchTool:
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
def __init__(self):
|
|
88
|
-
"""
|
|
88
|
+
"""Initialize the search tool, need to pass in the language model for information extraction"""
|
|
89
89
|
self.model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
|
|
90
90
|
self.webpage_tool = WebpageTool()
|
|
91
91
|
|
|
92
92
|
def _search(self, query: str, max_results: int) -> List[Dict]:
|
|
93
|
-
"""
|
|
93
|
+
"""Execute search request"""
|
|
94
94
|
try:
|
|
95
95
|
results = bing_search(query)
|
|
96
96
|
if not results:
|
|
@@ -106,76 +106,76 @@ class SearchTool:
|
|
|
106
106
|
})
|
|
107
107
|
return formatted_results
|
|
108
108
|
except Exception as e:
|
|
109
|
-
PrettyOutput.print(f"
|
|
109
|
+
PrettyOutput.print(f"Search request failed: {str(e)}", OutputType.ERROR)
|
|
110
110
|
return []
|
|
111
111
|
|
|
112
112
|
def _extract_info(self, contents: List[str], question: str) -> str:
|
|
113
|
-
"""
|
|
114
|
-
prompt = f"""
|
|
113
|
+
"""Use language model to extract key information from web content"""
|
|
114
|
+
prompt = f"""Please answer the question based on the following search results: {question}
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
Search results content:
|
|
117
117
|
{'-' * 40}
|
|
118
118
|
{''.join(contents)}
|
|
119
119
|
{'-' * 40}
|
|
120
120
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
1.
|
|
124
|
-
2.
|
|
125
|
-
3.
|
|
126
|
-
4.
|
|
121
|
+
Please provide a concise and accurate answer, focusing on information directly related to the question. If there is no relevant information in the search results, please clearly state that.
|
|
122
|
+
When answering, pay attention to:
|
|
123
|
+
1. Maintain objectivity, providing information based solely on search results
|
|
124
|
+
2. If there are conflicts between different sources, point out the differences
|
|
125
|
+
3. Appropriately cite information sources
|
|
126
|
+
4. If the information is incomplete or uncertain, please explain"""
|
|
127
127
|
|
|
128
128
|
try:
|
|
129
|
-
response = self.model.
|
|
129
|
+
response = self.model.chat_until_success(prompt)
|
|
130
130
|
return response
|
|
131
131
|
except Exception as e:
|
|
132
|
-
return f"
|
|
132
|
+
return f"Information extraction failed: {str(e)}"
|
|
133
133
|
|
|
134
134
|
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
135
|
-
"""
|
|
135
|
+
"""Execute search and extract information"""
|
|
136
136
|
try:
|
|
137
137
|
query = args["query"]
|
|
138
138
|
question = args["question"]
|
|
139
139
|
max_results = args.get("max_results", 3)
|
|
140
140
|
|
|
141
|
-
#
|
|
142
|
-
PrettyOutput.print(f"
|
|
143
|
-
PrettyOutput.print(f"
|
|
141
|
+
# Print search information
|
|
142
|
+
PrettyOutput.print(f"Search query: {query}", OutputType.INFO)
|
|
143
|
+
PrettyOutput.print(f"Related question: {question}", OutputType.INFO)
|
|
144
144
|
|
|
145
145
|
# 获取搜索结果
|
|
146
146
|
results = self._search(query, max_results)
|
|
147
147
|
if not results:
|
|
148
148
|
return {
|
|
149
149
|
"success": False,
|
|
150
|
-
"error": "
|
|
150
|
+
"error": "No search results found"
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
# 收集网页内容
|
|
154
154
|
contents = []
|
|
155
155
|
for i, result in enumerate(results, 1):
|
|
156
156
|
try:
|
|
157
|
-
PrettyOutput.print(f"
|
|
157
|
+
PrettyOutput.print(f"Reading result {i}/{len(results)}... {result['title']} - {result['href']}", OutputType.PROGRESS)
|
|
158
158
|
webpage_result = self.webpage_tool.execute({"url": result["href"]})
|
|
159
159
|
if webpage_result["success"]:
|
|
160
|
-
contents.append(f"\
|
|
160
|
+
contents.append(f"\nSource {i}: {result['href']}\n")
|
|
161
161
|
contents.append(webpage_result["stdout"])
|
|
162
162
|
except Exception as e:
|
|
163
|
-
PrettyOutput.print(f"
|
|
163
|
+
PrettyOutput.print(f"Failed to read result {i}: {str(e)}", OutputType.WARNING)
|
|
164
164
|
continue
|
|
165
165
|
|
|
166
166
|
if not contents:
|
|
167
167
|
return {
|
|
168
168
|
"success": False,
|
|
169
|
-
"error": "
|
|
169
|
+
"error": "No valid search results found"
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
#
|
|
173
|
-
PrettyOutput.print("
|
|
172
|
+
# Extract information
|
|
173
|
+
PrettyOutput.print("Analyzing search results...", OutputType.PROGRESS)
|
|
174
174
|
analysis = self._extract_info(contents, question)
|
|
175
175
|
|
|
176
176
|
return {
|
|
177
177
|
"success": True,
|
|
178
|
-
"stdout": f"
|
|
178
|
+
"stdout": f"Search analysis results:\n\n{analysis}",
|
|
179
179
|
"stderr": ""
|
|
180
180
|
}
|
|
181
181
|
|
|
@@ -190,22 +190,22 @@ def main():
|
|
|
190
190
|
import argparse
|
|
191
191
|
import sys
|
|
192
192
|
|
|
193
|
-
parser = argparse.ArgumentParser(description='Bing
|
|
194
|
-
parser.add_argument('query', help='
|
|
195
|
-
parser.add_argument('--max', type=int, default=5, help='
|
|
196
|
-
parser.add_argument('--url-only', action='store_true', help='
|
|
193
|
+
parser = argparse.ArgumentParser(description='Bing search tool')
|
|
194
|
+
parser.add_argument('query', help='Search keywords')
|
|
195
|
+
parser.add_argument('--max', type=int, default=5, help='Maximum number of results (default 5)')
|
|
196
|
+
parser.add_argument('--url-only', action='store_true', help='Only display URL')
|
|
197
197
|
args = parser.parse_args()
|
|
198
198
|
|
|
199
199
|
try:
|
|
200
|
-
PrettyOutput.print(f"
|
|
200
|
+
PrettyOutput.print(f"Searching: {args.query}", OutputType.INFO)
|
|
201
201
|
|
|
202
202
|
results = bing_search(args.query)
|
|
203
203
|
|
|
204
204
|
if not results:
|
|
205
|
-
PrettyOutput.print("
|
|
205
|
+
PrettyOutput.print("No search results found", OutputType.WARNING)
|
|
206
206
|
sys.exit(1)
|
|
207
207
|
|
|
208
|
-
PrettyOutput.print(f"\
|
|
208
|
+
PrettyOutput.print(f"\nFound {len(results)} results:", OutputType.INFO)
|
|
209
209
|
|
|
210
210
|
for i, result in enumerate(results[:args.max], 1):
|
|
211
211
|
PrettyOutput.print(f"\n{'-'*50}", OutputType.INFO)
|
|
@@ -213,15 +213,15 @@ def main():
|
|
|
213
213
|
PrettyOutput.print(f"{i}. {result['href']}", OutputType.INFO)
|
|
214
214
|
else:
|
|
215
215
|
PrettyOutput.print(f"{i}. {result['title']}", OutputType.INFO)
|
|
216
|
-
PrettyOutput.print(f"
|
|
216
|
+
PrettyOutput.print(f"Link: {result['href']}", OutputType.INFO)
|
|
217
217
|
if result['abstract']:
|
|
218
|
-
PrettyOutput.print(f"
|
|
218
|
+
PrettyOutput.print(f"Abstract: {result['abstract']}", OutputType.INFO)
|
|
219
219
|
|
|
220
220
|
except KeyboardInterrupt:
|
|
221
|
-
PrettyOutput.print("\
|
|
221
|
+
PrettyOutput.print("\nSearch cancelled", OutputType.WARNING)
|
|
222
222
|
sys.exit(1)
|
|
223
223
|
except Exception as e:
|
|
224
|
-
PrettyOutput.print(f"
|
|
224
|
+
PrettyOutput.print(f"Execution error: {str(e)}", OutputType.ERROR)
|
|
225
225
|
sys.exit(1)
|
|
226
226
|
|
|
227
227
|
if __name__ == "__main__":
|
jarvis/tools/shell.py
CHANGED
|
@@ -8,7 +8,7 @@ from jarvis.utils import OutputType, PrettyOutput
|
|
|
8
8
|
|
|
9
9
|
class ShellTool:
|
|
10
10
|
name = "execute_shell"
|
|
11
|
-
description = """
|
|
11
|
+
description = """Execute shell command and return result"""
|
|
12
12
|
|
|
13
13
|
parameters = {
|
|
14
14
|
"type": "object",
|
|
@@ -23,41 +23,41 @@ class ShellTool:
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
def _escape_command(self, cmd: str) -> str:
|
|
26
|
-
"""
|
|
26
|
+
"""Escape special characters in command"""
|
|
27
27
|
return cmd.replace("'", "'\"'\"'")
|
|
28
28
|
|
|
29
29
|
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
30
|
-
"""
|
|
30
|
+
"""Execute shell command"""
|
|
31
31
|
try:
|
|
32
32
|
command = args["command"]
|
|
33
33
|
|
|
34
|
-
#
|
|
34
|
+
# Generate temporary file name
|
|
35
35
|
output_file = os.path.join(tempfile.gettempdir(), f"jarvis_shell_{os.getpid()}.log")
|
|
36
36
|
|
|
37
|
-
#
|
|
37
|
+
# Escape special characters in command
|
|
38
38
|
escaped_command = self._escape_command(command)
|
|
39
39
|
|
|
40
|
-
#
|
|
40
|
+
# Modify command to use script
|
|
41
41
|
tee_command = f"script -q -c '{escaped_command}' {output_file}"
|
|
42
42
|
|
|
43
|
-
PrettyOutput.print(f"
|
|
43
|
+
PrettyOutput.print(f"Execute command: {command}", OutputType.INFO)
|
|
44
44
|
|
|
45
|
-
#
|
|
45
|
+
# Execute command
|
|
46
46
|
return_code = os.system(tee_command)
|
|
47
47
|
|
|
48
|
-
#
|
|
48
|
+
# Read output file
|
|
49
49
|
try:
|
|
50
50
|
with open(output_file, 'r', encoding='utf-8', errors='replace') as f:
|
|
51
51
|
output = f.read()
|
|
52
|
-
#
|
|
52
|
+
# Remove header and footer added by script
|
|
53
53
|
if output:
|
|
54
54
|
lines = output.splitlines()
|
|
55
55
|
if len(lines) > 2:
|
|
56
56
|
output = "\n".join(lines[1:-1])
|
|
57
57
|
except Exception as e:
|
|
58
|
-
output = f"
|
|
58
|
+
output = f"Failed to read output file: {str(e)}"
|
|
59
59
|
finally:
|
|
60
|
-
#
|
|
60
|
+
# Clean up temporary file
|
|
61
61
|
Path(output_file).unlink(missing_ok=True)
|
|
62
62
|
|
|
63
63
|
return {
|
|
@@ -68,7 +68,7 @@ class ShellTool:
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
except Exception as e:
|
|
71
|
-
#
|
|
71
|
+
# Ensure temporary file is cleaned up
|
|
72
72
|
if 'output_file' in locals():
|
|
73
73
|
Path(output_file).unlink(missing_ok=True)
|
|
74
74
|
PrettyOutput.print(str(e), OutputType.ERROR)
|
jarvis/tools/sub_agent.py
CHANGED
|
@@ -7,32 +7,32 @@ from jarvis.utils import OutputType, PrettyOutput
|
|
|
7
7
|
|
|
8
8
|
class SubAgentTool:
|
|
9
9
|
name = "create_sub_agent"
|
|
10
|
-
description = "
|
|
10
|
+
description = "Create a sub-agent to handle specific tasks, the sub-agent will generate a task summary report"
|
|
11
11
|
parameters = {
|
|
12
12
|
"type": "object",
|
|
13
13
|
"properties": {
|
|
14
14
|
"agent_name": {
|
|
15
15
|
"type": "string",
|
|
16
|
-
"description": "
|
|
16
|
+
"description": "Sub-agent name"
|
|
17
17
|
},
|
|
18
18
|
"task": {
|
|
19
19
|
"type": "string",
|
|
20
|
-
"description": "
|
|
20
|
+
"description": "Specific task to complete"
|
|
21
21
|
},
|
|
22
22
|
"context": {
|
|
23
23
|
"type": "string",
|
|
24
|
-
"description": "
|
|
24
|
+
"description": "Context information related to the task",
|
|
25
25
|
"default": ""
|
|
26
26
|
},
|
|
27
27
|
"goal": {
|
|
28
28
|
"type": "string",
|
|
29
|
-
"description": "
|
|
29
|
+
"description": "Completion goal of the task",
|
|
30
30
|
"default": ""
|
|
31
31
|
},
|
|
32
32
|
"files": {
|
|
33
33
|
"type": "array",
|
|
34
34
|
"items": {"type": "string"},
|
|
35
|
-
"description": "
|
|
35
|
+
"description": "Related file path list, used for file question answering and processing",
|
|
36
36
|
"default": []
|
|
37
37
|
}
|
|
38
38
|
},
|
|
@@ -41,7 +41,7 @@ class SubAgentTool:
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
44
|
-
"""
|
|
44
|
+
"""Create and run sub-agent"""
|
|
45
45
|
try:
|
|
46
46
|
agent_name = args["agent_name"]
|
|
47
47
|
task = args["task"]
|
|
@@ -49,28 +49,28 @@ class SubAgentTool:
|
|
|
49
49
|
goal = args.get("goal", "")
|
|
50
50
|
files = args.get("files", [])
|
|
51
51
|
|
|
52
|
-
PrettyOutput.print(f"
|
|
52
|
+
PrettyOutput.print(f"Create sub-agent: {agent_name}", OutputType.INFO)
|
|
53
53
|
|
|
54
|
-
#
|
|
54
|
+
# Build task description
|
|
55
55
|
task_description = task
|
|
56
56
|
if context:
|
|
57
|
-
task_description = f"
|
|
57
|
+
task_description = f"Context information:\n{context}\n\nTask:\n{task}"
|
|
58
58
|
if goal:
|
|
59
|
-
task_description += f"\n\n
|
|
59
|
+
task_description += f"\n\nCompletion goal:\n{goal}"
|
|
60
60
|
|
|
61
|
-
#
|
|
61
|
+
# Create sub-agent
|
|
62
62
|
sub_agent = Agent(
|
|
63
63
|
name=agent_name,
|
|
64
64
|
is_sub_agent=True
|
|
65
65
|
)
|
|
66
66
|
|
|
67
|
-
#
|
|
68
|
-
PrettyOutput.print("
|
|
67
|
+
# Run sub-agent, pass file list
|
|
68
|
+
PrettyOutput.print("Sub-agent starts executing task...", OutputType.INFO)
|
|
69
69
|
result = sub_agent.run(task_description, file_list=files)
|
|
70
70
|
|
|
71
71
|
return {
|
|
72
72
|
"success": True,
|
|
73
|
-
"stdout": f"
|
|
73
|
+
"stdout": f"Sub-agent task completed\n\n{result}",
|
|
74
74
|
"stderr": ""
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -78,5 +78,5 @@ class SubAgentTool:
|
|
|
78
78
|
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
79
79
|
return {
|
|
80
80
|
"success": False,
|
|
81
|
-
"error": f"
|
|
81
|
+
"error": f"Sub-agent execution failed: {str(e)}"
|
|
82
82
|
}
|