jarvis-ai-assistant 0.1.134__py3-none-any.whl → 0.1.138__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.

Files changed (78) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +201 -79
  3. jarvis/jarvis_agent/builtin_input_handler.py +16 -6
  4. jarvis/jarvis_agent/file_input_handler.py +9 -9
  5. jarvis/jarvis_agent/jarvis.py +10 -10
  6. jarvis/jarvis_agent/main.py +12 -11
  7. jarvis/jarvis_agent/output_handler.py +3 -3
  8. jarvis/jarvis_agent/patch.py +86 -62
  9. jarvis/jarvis_agent/shell_input_handler.py +5 -3
  10. jarvis/jarvis_code_agent/code_agent.py +134 -99
  11. jarvis/jarvis_code_agent/file_select.py +24 -24
  12. jarvis/jarvis_dev/main.py +45 -51
  13. jarvis/jarvis_git_details/__init__.py +0 -0
  14. jarvis/jarvis_git_details/main.py +179 -0
  15. jarvis/jarvis_git_squash/main.py +7 -7
  16. jarvis/jarvis_lsp/base.py +11 -11
  17. jarvis/jarvis_lsp/cpp.py +14 -14
  18. jarvis/jarvis_lsp/go.py +13 -13
  19. jarvis/jarvis_lsp/python.py +8 -8
  20. jarvis/jarvis_lsp/registry.py +21 -21
  21. jarvis/jarvis_lsp/rust.py +15 -15
  22. jarvis/jarvis_methodology/main.py +101 -0
  23. jarvis/jarvis_multi_agent/__init__.py +11 -11
  24. jarvis/jarvis_multi_agent/main.py +6 -6
  25. jarvis/jarvis_platform/__init__.py +1 -1
  26. jarvis/jarvis_platform/ai8.py +67 -89
  27. jarvis/jarvis_platform/base.py +14 -13
  28. jarvis/jarvis_platform/kimi.py +25 -28
  29. jarvis/jarvis_platform/ollama.py +24 -26
  30. jarvis/jarvis_platform/openai.py +15 -19
  31. jarvis/jarvis_platform/oyi.py +48 -50
  32. jarvis/jarvis_platform/registry.py +27 -28
  33. jarvis/jarvis_platform/yuanbao.py +38 -42
  34. jarvis/jarvis_platform_manager/main.py +81 -81
  35. jarvis/jarvis_platform_manager/openai_test.py +21 -21
  36. jarvis/jarvis_rag/file_processors.py +18 -18
  37. jarvis/jarvis_rag/main.py +261 -277
  38. jarvis/jarvis_smart_shell/main.py +12 -12
  39. jarvis/jarvis_tools/ask_codebase.py +28 -28
  40. jarvis/jarvis_tools/ask_user.py +8 -8
  41. jarvis/jarvis_tools/base.py +4 -4
  42. jarvis/jarvis_tools/chdir.py +9 -9
  43. jarvis/jarvis_tools/code_review.py +19 -19
  44. jarvis/jarvis_tools/create_code_agent.py +15 -15
  45. jarvis/jarvis_tools/execute_python_script.py +3 -3
  46. jarvis/jarvis_tools/execute_shell.py +11 -11
  47. jarvis/jarvis_tools/execute_shell_script.py +3 -3
  48. jarvis/jarvis_tools/file_analyzer.py +29 -29
  49. jarvis/jarvis_tools/file_operation.py +22 -20
  50. jarvis/jarvis_tools/find_caller.py +25 -25
  51. jarvis/jarvis_tools/find_methodolopy.py +65 -0
  52. jarvis/jarvis_tools/find_symbol.py +24 -24
  53. jarvis/jarvis_tools/function_analyzer.py +27 -27
  54. jarvis/jarvis_tools/git_commiter.py +9 -9
  55. jarvis/jarvis_tools/lsp_get_diagnostics.py +19 -19
  56. jarvis/jarvis_tools/methodology.py +23 -62
  57. jarvis/jarvis_tools/project_analyzer.py +29 -33
  58. jarvis/jarvis_tools/rag.py +15 -15
  59. jarvis/jarvis_tools/read_code.py +24 -22
  60. jarvis/jarvis_tools/read_webpage.py +31 -31
  61. jarvis/jarvis_tools/registry.py +72 -52
  62. jarvis/jarvis_tools/tool_generator.py +18 -18
  63. jarvis/jarvis_utils/config.py +23 -23
  64. jarvis/jarvis_utils/embedding.py +83 -83
  65. jarvis/jarvis_utils/git_utils.py +20 -20
  66. jarvis/jarvis_utils/globals.py +18 -6
  67. jarvis/jarvis_utils/input.py +10 -9
  68. jarvis/jarvis_utils/methodology.py +140 -136
  69. jarvis/jarvis_utils/output.py +11 -11
  70. jarvis/jarvis_utils/utils.py +22 -70
  71. {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/METADATA +1 -1
  72. jarvis_ai_assistant-0.1.138.dist-info/RECORD +85 -0
  73. {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/entry_points.txt +2 -0
  74. jarvis/jarvis_tools/select_code_files.py +0 -62
  75. jarvis_ai_assistant-0.1.134.dist-info/RECORD +0 -82
  76. {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/LICENSE +0 -0
  77. {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/WHEEL +0 -0
  78. {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/top_level.txt +0 -0
@@ -24,38 +24,38 @@ class WebpageTool:
24
24
  """Read webpage content using Playwright to handle JavaScript-rendered pages"""
25
25
  try:
26
26
  url = args["url"].strip()
27
-
27
+
28
28
  with sync_playwright() as p:
29
29
  # Launch browser
30
30
  browser = p.chromium.launch(
31
31
  headless=True,
32
32
  args=['--disable-gpu', '--no-sandbox', '--disable-dev-shm-usage']
33
33
  )
34
-
34
+
35
35
  # Create a new page with appropriate settings
36
36
  page = browser.new_page(
37
37
  user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
38
38
  viewport={'width': 1920, 'height': 1080}
39
39
  )
40
-
40
+
41
41
  # Set timeout to avoid long waits
42
42
  page.set_default_timeout(30000) # 30 seconds
43
-
43
+
44
44
  try:
45
45
  # Navigate to URL and wait for page to load
46
46
  response = page.goto(url, wait_until="domcontentloaded")
47
-
47
+
48
48
  # Additional wait for network to be idle (with a timeout)
49
49
  try:
50
50
  page.wait_for_load_state("networkidle", timeout=10000)
51
51
  except PlaywrightTimeoutError:
52
52
  # Continue even if network doesn't become completely idle
53
53
  pass
54
-
54
+
55
55
  # Make sure we got a valid response
56
56
  if not response or response.status >= 400:
57
57
  raise Exception(f"Failed to load page: HTTP {response.status if response else 'No response'}")
58
-
58
+
59
59
  # Get page title safely
60
60
  title = "No title"
61
61
  try:
@@ -68,26 +68,26 @@ class WebpageTool:
68
68
  title = title_element.text_content() or "No title"
69
69
  except Exception:
70
70
  pass
71
-
71
+
72
72
  # Get the HTML content after JavaScript execution
73
73
  html_content = page.content()
74
-
74
+
75
75
  except Exception as e:
76
76
  raise Exception(f"Error navigating to page: {str(e)}")
77
77
  finally:
78
78
  # Always close browser
79
79
  browser.close()
80
-
80
+
81
81
  # Parse with BeautifulSoup and convert to markdown
82
82
  markdown_content = self._html_to_markdown(html_content, url)
83
-
83
+
84
84
  # Build output in markdown format
85
85
  output = [
86
86
  f"# {title}",
87
87
  f"Url: {url}",
88
88
  markdown_content
89
89
  ]
90
-
90
+
91
91
  return {
92
92
  "success": True,
93
93
  "stdout": "\n".join(output),
@@ -101,7 +101,7 @@ class WebpageTool:
101
101
  "stdout": "",
102
102
  "stderr": f"Failed to parse webpage: {str(e)}"
103
103
  }
104
-
104
+
105
105
  def _create_soup_element(self, content):
106
106
  """Safely create a BeautifulSoup element, ensuring it's treated as markup"""
107
107
  if isinstance(content, str):
@@ -112,15 +112,15 @@ class WebpageTool:
112
112
  # Return an empty list if the div is None
113
113
  return []
114
114
  return content
115
-
115
+
116
116
  def _html_to_markdown(self, html_content: str, base_url: str) -> str:
117
117
  """Convert HTML to Markdown format preserving the content structure"""
118
118
  soup = BeautifulSoup(html_content, 'html.parser')
119
-
119
+
120
120
  # Remove unwanted elements
121
121
  for element in soup(['script', 'style', 'meta', 'noscript', 'head']):
122
122
  element.decompose()
123
-
123
+
124
124
  # Process headings
125
125
  for level in range(1, 7):
126
126
  for heading in soup.find_all(f'h{level}'):
@@ -128,14 +128,14 @@ class WebpageTool:
128
128
  heading_md = "\n\n" + "#" * level + " " + text + "\n\n"
129
129
  new_element = self._create_soup_element(heading_md)
130
130
  heading.replace_with(*new_element)
131
-
131
+
132
132
  # Process paragraphs
133
133
  for p in soup.find_all('p'):
134
134
  text = p.get_text().strip()
135
135
  if text:
136
136
  new_element = self._create_soup_element("\n\n" + text + "\n\n")
137
137
  p.replace_with(*new_element)
138
-
138
+
139
139
  # Process unordered lists
140
140
  for ul in soup.find_all('ul'):
141
141
  items = []
@@ -143,7 +143,7 @@ class WebpageTool:
143
143
  items.append("* " + li.get_text().strip())
144
144
  new_element = self._create_soup_element("\n\n" + "\n".join(items) + "\n\n")
145
145
  ul.replace_with(*new_element)
146
-
146
+
147
147
  # Process ordered lists
148
148
  for ol in soup.find_all('ol'):
149
149
  items = []
@@ -151,7 +151,7 @@ class WebpageTool:
151
151
  items.append(str(i) + ". " + li.get_text().strip())
152
152
  new_element = self._create_soup_element("\n\n" + "\n".join(items) + "\n\n")
153
153
  ol.replace_with(*new_element)
154
-
154
+
155
155
  # Process links (first pass)
156
156
  for a in soup.find_all('a', href=True):
157
157
  try:
@@ -166,7 +166,7 @@ class WebpageTool:
166
166
  a.replace_with(*new_element)
167
167
  except (KeyError, AttributeError):
168
168
  continue
169
-
169
+
170
170
  # Process images
171
171
  for img in soup.find_all('img', src=True):
172
172
  try:
@@ -180,37 +180,37 @@ class WebpageTool:
180
180
  img.replace_with(*new_element)
181
181
  except (KeyError, AttributeError, UnboundLocalError):
182
182
  continue
183
-
183
+
184
184
  # Process code blocks
185
185
  for pre in soup.find_all('pre'):
186
186
  code = pre.get_text().strip()
187
187
  pre_md = "\n\n```\n" + code + "\n```\n\n"
188
188
  new_element = self._create_soup_element(pre_md)
189
189
  pre.replace_with(*new_element)
190
-
190
+
191
191
  # Process inline code
192
192
  for code in soup.find_all('code'):
193
193
  text = code.get_text().strip()
194
194
  code_md = "`" + text + "`"
195
195
  new_element = self._create_soup_element(code_md)
196
196
  code.replace_with(*new_element)
197
-
197
+
198
198
  # Process line breaks
199
199
  for br in soup.find_all('br'):
200
200
  new_element = self._create_soup_element('\n')
201
201
  br.replace_with(*new_element)
202
-
202
+
203
203
  # Get the full text
204
204
  markdown_text = soup.get_text()
205
-
205
+
206
206
  # Clean up extra whitespace and line breaks
207
207
  markdown_text = re.sub(r'\n{3,}', '\n\n', markdown_text)
208
208
  markdown_text = re.sub(r'\s{2,}', ' ', markdown_text)
209
-
209
+
210
210
  # Process links again (for any that might have been missed)
211
211
  link_pattern = r'\[([^\]]+)\]\(([^)]+)\)'
212
212
  all_links = re.findall(link_pattern, markdown_text)
213
-
213
+
214
214
  # Add a section with all links at the end
215
215
  if all_links:
216
216
  link_section = ["", "## Links", ""]
@@ -220,7 +220,7 @@ class WebpageTool:
220
220
  if link_entry not in seen_links:
221
221
  link_section.append(link_entry)
222
222
  seen_links.add(link_entry)
223
-
223
+
224
224
  markdown_text += "\n\n" + "\n".join(link_section)
225
-
226
- return markdown_text.strip()
225
+
226
+ return markdown_text.strip()
@@ -85,21 +85,40 @@ class ToolRegistry(OutputHandler):
85
85
  if self._extract_tool_calls(response):
86
86
  return True
87
87
  return False
88
-
88
+
89
89
  def prompt(self) -> str:
90
90
  """加载工具"""
91
91
  tools = self.get_all_tools()
92
92
  if tools:
93
93
  tools_prompt = "## 可用工具:\n"
94
94
  for tool in tools:
95
- tools_prompt += f"- 名称: {tool['name']}\n"
96
- tools_prompt += f" 描述: {tool['description']}\n"
97
- tools_prompt += f" 参数: {tool['parameters']}\n"
98
- tools_prompt += tool_call_help
95
+ try:
96
+ tools_prompt += f"- 名称: {tool['name']}\n"
97
+ tools_prompt += f" 描述: {tool['description']}\n"
98
+ tools_prompt += " 参数: |\n"
99
+
100
+ # 生成格式化的YAML参数
101
+ yaml_params = yaml.dump(
102
+ tool['parameters'],
103
+ allow_unicode=True,
104
+ indent=4,
105
+ sort_keys=False,
106
+ width=120 # 增加行宽限制
107
+ )
108
+
109
+ # 添加缩进并移除尾部空格
110
+ for line in yaml_params.split('\n'):
111
+ tools_prompt += f" {line.rstrip()}\n"
112
+
113
+ except yaml.YAMLError as e:
114
+ PrettyOutput.print(f"工具 {tool['name']} 参数序列化失败: {str(e)}", OutputType.ERROR)
115
+ continue
116
+
117
+ tools_prompt += tool_call_help.rstrip() # 移除帮助文本尾部空格
99
118
  return tools_prompt
100
119
  return ""
101
-
102
- def handle(self, response: str) -> Tuple[bool, Any]:
120
+
121
+ def handle(self, response: str, agent: Any) -> Tuple[bool, Any]:
103
122
  tool_calls = self._extract_tool_calls(response)
104
123
  if len(tool_calls) > 1:
105
124
  PrettyOutput.print(f"操作失败:检测到多个操作。一次只能执行一个操作。尝试执行的操作:{', '.join([tool_call['name'] for tool_call in tool_calls])}", OutputType.WARNING)
@@ -107,7 +126,7 @@ class ToolRegistry(OutputHandler):
107
126
  if len(tool_calls) == 0:
108
127
  return False, ""
109
128
  tool_call = tool_calls[0]
110
- return False, self.handle_tool_calls(tool_call)
129
+ return False, self.handle_tool_calls(tool_call, agent)
111
130
 
112
131
  def __init__(self):
113
132
  """初始化工具注册表"""
@@ -132,13 +151,13 @@ class ToolRegistry(OutputHandler):
132
151
  def _load_builtin_tools(self):
133
152
  """从内置工具目录加载工具"""
134
153
  tools_dir = Path(__file__).parent
135
-
154
+
136
155
  # 遍历目录中的所有.py文件
137
156
  for file_path in tools_dir.glob("*.py"):
138
157
  # 跳过base.py和__init__.py
139
158
  if file_path.name in ["base.py", "__init__.py", "registry.py"]:
140
159
  continue
141
-
160
+
142
161
  self.register_tool_by_file(str(file_path))
143
162
 
144
163
  def _load_external_tools(self):
@@ -146,21 +165,21 @@ class ToolRegistry(OutputHandler):
146
165
  external_tools_dir = Path.home() / '.jarvis/tools'
147
166
  if not external_tools_dir.exists():
148
167
  return
149
-
168
+
150
169
  # 遍历目录中的所有.py文件
151
170
  for file_path in external_tools_dir.glob("*.py"):
152
171
  # 跳过__init__.py
153
172
  if file_path.name == "__init__.py":
154
173
  continue
155
-
174
+
156
175
  self.register_tool_by_file(str(file_path))
157
176
 
158
177
  def register_tool_by_file(self, file_path: str):
159
178
  """从指定文件加载并注册工具
160
-
179
+
161
180
  参数:
162
181
  file_path: 工具文件的路径
163
-
182
+
164
183
  返回:
165
184
  bool: 工具是否加载成功
166
185
  """
@@ -169,35 +188,35 @@ class ToolRegistry(OutputHandler):
169
188
  if not p_file_path.exists() or not p_file_path.is_file():
170
189
  PrettyOutput.print(f"文件不存在: {p_file_path}", OutputType.ERROR)
171
190
  return False
172
-
191
+
173
192
  # 临时将父目录添加到sys.path
174
193
  parent_dir = str(p_file_path.parent)
175
194
  sys.path.insert(0, parent_dir)
176
-
195
+
177
196
  try:
178
197
  # 使用标准导入机制导入模块
179
198
  module_name = p_file_path.stem
180
199
  module = __import__(module_name)
181
-
200
+
182
201
  # 在模块中查找工具类
183
202
  tool_found = False
184
203
  for item_name in dir(module):
185
204
  item = getattr(module, item_name)
186
205
  # 检查是否是类并具有必要属性
187
- if (isinstance(item, type) and
188
- hasattr(item, 'name') and
189
- hasattr(item, 'description') and
206
+ if (isinstance(item, type) and
207
+ hasattr(item, 'name') and
208
+ hasattr(item, 'description') and
190
209
  hasattr(item, 'parameters') and
191
- hasattr(item, 'execute') and
210
+ hasattr(item, 'execute') and
192
211
  item.name == module_name):
193
212
 
194
213
  if hasattr(item, "check"):
195
214
  if not item.check():
196
215
  continue
197
-
216
+
198
217
  # 实例化工具类
199
218
  tool_instance = item()
200
-
219
+
201
220
  # 注册工具
202
221
  self.register_tool(
203
222
  name=tool_instance.name,
@@ -207,29 +226,29 @@ class ToolRegistry(OutputHandler):
207
226
  )
208
227
  tool_found = True
209
228
  break
210
-
229
+
211
230
  if not tool_found:
212
231
  return False
213
-
232
+
214
233
  return True
215
-
234
+
216
235
  finally:
217
236
  # 从sys.path中移除目录
218
237
  sys.path.remove(parent_dir)
219
-
238
+
220
239
  except Exception as e:
221
240
  PrettyOutput.print(f"从 {Path(file_path).name} 加载工具失败: {str(e)}", OutputType.ERROR)
222
241
  return False
223
242
  @staticmethod
224
243
  def _extract_tool_calls(content: str) -> List[Dict]:
225
244
  """从内容中提取工具调用。
226
-
245
+
227
246
  参数:
228
247
  content: 包含工具调用的内容
229
-
248
+
230
249
  返回:
231
250
  List[Dict]: 包含名称和参数的提取工具调用列表
232
-
251
+
233
252
  异常:
234
253
  Exception: 如果工具调用缺少必要字段
235
254
  """
@@ -264,12 +283,13 @@ class ToolRegistry(OutputHandler):
264
283
  return {"success": False, "stderr": f"工具 {name} 不存在,可用的工具有: {', '.join(self.tools.keys())}", "stdout": ""}
265
284
  return tool.execute(arguments)
266
285
 
267
- def handle_tool_calls(self, tool_call: Dict) -> str:
286
+ def handle_tool_calls(self, tool_call: Dict, agent: Any) -> str:
268
287
  """处理工具调用,只处理第一个工具"""
269
288
  try:
270
289
  # 只处理第一个工具调用
271
290
  name = tool_call["name"]
272
291
  args = tool_call["arguments"]
292
+ args["agent"] = agent
273
293
 
274
294
  tool_call_help = f"""
275
295
  # 🛠️ 工具使用系统
@@ -330,14 +350,14 @@ arguments:
330
350
  - 创建虚构对话
331
351
  - 在没有所需信息的情况下继续
332
352
  """
333
-
353
+
334
354
  if isinstance(args, str):
335
355
  try:
336
356
  args = json.loads(args)
337
357
  except json.JSONDecodeError:
338
358
  PrettyOutput.print(f"工具参数格式无效: {name} {tool_call_help}", OutputType.ERROR)
339
359
  return ""
340
-
360
+
341
361
  # Execute tool call
342
362
  result = self.execute_tool(name, args)
343
363
 
@@ -350,14 +370,14 @@ arguments:
350
370
  output_parts.append(f"错误:\n{stderr}")
351
371
  output = "\n\n".join(output_parts)
352
372
  output = "无输出和错误" if not output else output
353
-
373
+
354
374
  # Process the result
355
375
  if result["success"]:
356
376
  # If the output exceeds 4k characters, use a large model to summarize
357
377
  if get_context_token_count(output) > self.max_token_count:
358
378
  PrettyOutput.section("输出过长,正在总结...", OutputType.SYSTEM)
359
379
  try:
360
-
380
+
361
381
  model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
362
382
  model.set_suppress_output(False)
363
383
  # If the output exceeds the maximum context length, only take the last part
@@ -391,7 +411,7 @@ arguments:
391
411
  PrettyOutput.print(f"总结失败: {str(e)}", OutputType.ERROR)
392
412
  output = f"输出过长 ({len(output)} 字符),建议查看原始输出。\n前300字符预览:\n{output[:300]}..."
393
413
  return output
394
-
414
+
395
415
  except Exception as e:
396
416
  PrettyOutput.print(f"工具执行失败:{str(e)}", OutputType.ERROR)
397
417
  return f"工具调用失败: {str(e)}"
@@ -419,13 +439,13 @@ def main():
419
439
  call_parser.add_argument('--args-file', type=str, help='从文件加载工具参数 (JSON格式)')
420
440
 
421
441
  args = parser.parse_args()
422
-
442
+
423
443
  # 初始化工具注册表
424
444
  registry = ToolRegistry()
425
-
445
+
426
446
  if args.command == 'list':
427
447
  tools = registry.get_all_tools()
428
-
448
+
429
449
  if args.json:
430
450
  if args.detailed:
431
451
  print(json.dumps(tools, indent=2, ensure_ascii=False))
@@ -445,17 +465,17 @@ def main():
445
465
  req_mark = "*" if param_name in required else ""
446
466
  desc = param_info.get('description', '无描述')
447
467
  print(f" - {param_name}{req_mark}: {desc}")
448
-
468
+
449
469
  elif args.command == 'call':
450
470
  tool_name = args.tool_name
451
471
  tool = registry.get_tool(tool_name)
452
-
472
+
453
473
  if not tool:
454
474
  PrettyOutput.print(f"错误: 工具 '{tool_name}' 不存在", OutputType.ERROR)
455
475
  available_tools = ", ".join([t["name"] for t in registry.get_all_tools()])
456
476
  print(f"可用工具: {available_tools}")
457
477
  return 1
458
-
478
+
459
479
  # 获取参数
460
480
  tool_args = {}
461
481
  if args.args:
@@ -464,7 +484,7 @@ def main():
464
484
  except json.JSONDecodeError:
465
485
  PrettyOutput.print("错误: 参数必须是有效的JSON格式", OutputType.ERROR)
466
486
  return 1
467
-
487
+
468
488
  elif args.args_file:
469
489
  try:
470
490
  with open(args.args_file, 'r', encoding='utf-8') as f:
@@ -472,11 +492,11 @@ def main():
472
492
  except (json.JSONDecodeError, FileNotFoundError) as e:
473
493
  PrettyOutput.print(f"错误: 无法从文件加载参数: {str(e)}", OutputType.ERROR)
474
494
  return 1
475
-
495
+
476
496
  # 检查必需参数
477
497
  required_params = tool.parameters.get('required', [])
478
498
  missing_params = [p for p in required_params if p not in tool_args]
479
-
499
+
480
500
  if missing_params:
481
501
  PrettyOutput.print(f"错误: 缺少必需参数: {', '.join(missing_params)}", OutputType.ERROR)
482
502
  print("\n参数说明:")
@@ -486,30 +506,30 @@ def main():
486
506
  desc = param_info.get('description', '无描述')
487
507
  print(f" - {param_name}: {desc}")
488
508
  return 1
489
-
509
+
490
510
  # 执行工具
491
511
  with yaspin(text=f"正在执行工具 {tool_name}...").dots12:
492
512
  result = registry.execute_tool(tool_name, tool_args)
493
-
513
+
494
514
  # 显示结果
495
515
  if result["success"]:
496
516
  PrettyOutput.section(f"工具 {tool_name} 执行成功", OutputType.SUCCESS)
497
517
  else:
498
518
  PrettyOutput.section(f"工具 {tool_name} 执行失败", OutputType.ERROR)
499
-
519
+
500
520
  if result.get("stdout"):
501
521
  print("\n输出:")
502
522
  print(result["stdout"])
503
-
523
+
504
524
  if result.get("stderr"):
505
525
  PrettyOutput.print("\n错误:", OutputType.ERROR)
506
526
  print(result["stderr"])
507
-
527
+
508
528
  return 0 if result["success"] else 1
509
-
529
+
510
530
  else:
511
531
  parser.print_help()
512
-
532
+
513
533
  return 0
514
534
 
515
535
 
@@ -11,7 +11,7 @@ from jarvis.jarvis_utils.utils import ct, ot
11
11
 
12
12
  class ToolGenerator:
13
13
  """工具生成器类,用于自动创建与Jarvis系统集成的新工具"""
14
-
14
+
15
15
  name = "tool_generator"
16
16
  description = "使用LLM自动生成与系统集成的新工具"
17
17
  parameters = {
@@ -22,7 +22,7 @@ class ToolGenerator:
22
22
  "description": "新工具的名称"
23
23
  },
24
24
  "description": {
25
- "type": "string",
25
+ "type": "string",
26
26
  "description": "工具用途描述"
27
27
  },
28
28
  "input_spec": {
@@ -32,7 +32,7 @@ class ToolGenerator:
32
32
  },
33
33
  "required": ["tool_name", "description", "input_spec"]
34
34
  }
35
-
35
+
36
36
  def execute(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
37
37
  """
38
38
  执行工具生成过程
@@ -43,19 +43,19 @@ class ToolGenerator:
43
43
  """
44
44
  # 获取代码生成平台实例
45
45
  model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
46
-
46
+
47
47
  try:
48
48
  tool_name = arguments["tool_name"]
49
49
  description = arguments["description"]
50
50
  input_spec = arguments["input_spec"]
51
-
51
+
52
52
  # 使用LLM生成工具实现代码
53
53
  with yaspin(text="正在生成工具...", color="cyan") as spinner:
54
54
  prompt = self._create_prompt(tool_name, description, input_spec)
55
55
  llm_response = model.chat_until_success(prompt)
56
56
  spinner.text = "工具生成完成"
57
57
  spinner.ok("✅")
58
-
58
+
59
59
  # 从LLM响应中提取实现代码
60
60
  with yaspin(text="正在提取工具实现...", color="cyan") as spinner:
61
61
  implementation = self._extract_code(llm_response)
@@ -67,7 +67,7 @@ class ToolGenerator:
67
67
  }
68
68
  spinner.text = "工具实现提取完成"
69
69
  spinner.ok("✅")
70
-
70
+
71
71
  # 验证生成的工具代码是否符合返回值格式要求
72
72
  with yaspin(text="正在验证工具返回值格式...", color="cyan") as spinner:
73
73
  if not self._validate_return_value_format(implementation):
@@ -78,31 +78,31 @@ class ToolGenerator:
78
78
  }
79
79
  spinner.text = "工具返回值格式验证完成"
80
80
  spinner.ok("✅")
81
-
81
+
82
82
  # 保存生成的新工具
83
83
  with yaspin(text="正在保存工具...", color="cyan") as spinner:
84
84
  tools_dir = Path.home() / ".jarvis" / "tools"
85
85
  tools_dir.mkdir(parents=True, exist_ok=True)
86
86
  tool_file = tools_dir / f"{tool_name}.py"
87
-
87
+
88
88
  with open(tool_file, "w", errors="ignore") as f:
89
89
  f.write(implementation)
90
90
  spinner.text = "工具保存完成"
91
91
  spinner.ok("✅")
92
-
92
+
93
93
  return {
94
94
  "success": True,
95
95
  "stdout": f"工具成功生成于: {tool_file}",
96
96
  "stderr": ""
97
97
  }
98
-
98
+
99
99
  except Exception as e:
100
100
  return {
101
101
  "success": False,
102
102
  "stdout": "",
103
103
  "stderr": f"工具生成失败: {str(e)}"
104
104
  }
105
-
105
+
106
106
  def _create_prompt(self, tool_name: str, description: str, input_spec: str) -> str:
107
107
  """
108
108
  创建用于工具生成的LLM提示
@@ -134,10 +134,10 @@ class CustomTool:
134
134
 
135
135
  def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
136
136
  """执行工具功能
137
-
137
+
138
138
  Args:
139
139
  args: 传递给工具的参数
140
-
140
+
141
141
  Returns:
142
142
  {
143
143
  "success": bool,
@@ -148,7 +148,7 @@ class CustomTool:
148
148
  try:
149
149
  # 在此实现工具逻辑
150
150
  # 使用LLM
151
- # model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
151
+ # model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
152
152
  # result = model.chat_until_success(prompt)
153
153
 
154
154
  result = "工具执行结果"
@@ -189,7 +189,7 @@ class CustomTool:
189
189
  示例:
190
190
  {example_code}
191
191
  '''
192
-
192
+
193
193
  def _extract_code(self, response: str) -> str:
194
194
  """
195
195
  从LLM响应中提取Python代码
@@ -202,7 +202,7 @@ class CustomTool:
202
202
  if sm:
203
203
  return sm.group(1)
204
204
  return ""
205
-
205
+
206
206
  def _validate_return_value_format(self, code: str) -> bool:
207
207
  """
208
208
  验证execute方法的返回值格式是否正确
@@ -216,6 +216,6 @@ class CustomTool:
216
216
  if "def execute(self, args: Dict) -> Dict:" not in code and \
217
217
  "def execute(self, args: Dict) -> Dict[str, Any]:" not in code:
218
218
  return False
219
-
219
+
220
220
  # 检查返回值中是否包含所有必需字段
221
221
  return all(field in code for field in required_fields)