jarvis-ai-assistant 0.1.46__py3-none-any.whl → 0.1.48__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
jarvis/tools/__init__.py CHANGED
@@ -2,4 +2,4 @@ from .registry import ToolRegistry
2
2
 
3
3
  __all__ = [
4
4
  'ToolRegistry',
5
- ]
5
+ ]
jarvis/tools/base.py CHANGED
@@ -2,9 +2,9 @@ from typing import Dict, Any, Callable
2
2
  import json
3
3
 
4
4
 
5
+
5
6
  class Tool:
6
- def __init__(self, name: str, description: str,
7
- parameters: Dict, func: Callable):
7
+ def __init__(self, name: str, description: str, parameters: Dict, func: Callable):
8
8
  self.name = name
9
9
  self.description = description
10
10
  self.parameters = parameters
jarvis/tools/file_ops.py CHANGED
@@ -34,19 +34,18 @@ class FileOperationTool:
34
34
  "required": ["operation", "filepath"]
35
35
  }
36
36
 
37
+
37
38
  def execute(self, args: Dict) -> Dict[str, Any]:
38
39
  """执行文件操作"""
39
40
  try:
40
41
  operation = args["operation"]
41
42
  filepath = args["filepath"]
42
43
  encoding = args.get("encoding", "utf-8")
43
-
44
+
44
45
  # 记录操作和完整路径
45
46
  abs_path = os.path.abspath(filepath)
46
- PrettyOutput.print(
47
- f"文件操作: {operation} - {abs_path}",
48
- OutputType.INFO)
49
-
47
+ PrettyOutput.print(f"文件操作: {operation} - {abs_path}", OutputType.INFO)
48
+
50
49
  if operation == "exists":
51
50
  exists = os.path.exists(filepath)
52
51
  return {
@@ -54,21 +53,21 @@ class FileOperationTool:
54
53
  "stdout": str(exists),
55
54
  "stderr": ""
56
55
  }
57
-
56
+
58
57
  elif operation == "read":
59
58
  if not os.path.exists(filepath):
60
59
  return {
61
60
  "success": False,
62
61
  "error": f"文件不存在: {filepath}"
63
62
  }
64
-
63
+
65
64
  # 检查文件大小
66
65
  if os.path.getsize(filepath) > 10 * 1024 * 1024: # 10MB
67
66
  return {
68
67
  "success": False,
69
68
  "error": "文件过大 (>10MB)"
70
69
  }
71
-
70
+
72
71
  with open(filepath, 'r', encoding=encoding) as f:
73
72
  content = f.read()
74
73
  return {
@@ -76,39 +75,36 @@ class FileOperationTool:
76
75
  "stdout": content,
77
76
  "stderr": ""
78
77
  }
79
-
78
+
80
79
  elif operation in ["write", "append"]:
81
80
  if not args.get("content"):
82
81
  return {
83
82
  "success": False,
84
83
  "error": "写入/追加操作需要提供content参数"
85
84
  }
86
-
85
+
87
86
  # 创建目录(如果不存在)
88
- os.makedirs(
89
- os.path.dirname(
90
- os.path.abspath(filepath)),
91
- exist_ok=True)
92
-
87
+ os.makedirs(os.path.dirname(os.path.abspath(filepath)), exist_ok=True)
88
+
93
89
  mode = 'a' if operation == "append" else 'w'
94
90
  with open(filepath, mode, encoding=encoding) as f:
95
91
  f.write(args["content"])
96
-
92
+
97
93
  return {
98
94
  "success": True,
99
95
  "stdout": f"成功{operation}内容到 {filepath}",
100
96
  "stderr": ""
101
97
  }
102
-
98
+
103
99
  else:
104
100
  return {
105
101
  "success": False,
106
102
  "error": f"未知操作: {operation}"
107
103
  }
108
-
104
+
109
105
  except Exception as e:
110
106
  PrettyOutput.print(str(e), OutputType.ERROR)
111
107
  return {
112
108
  "success": False,
113
109
  "error": f"文件操作失败: {str(e)}"
114
- }
110
+ }
jarvis/tools/generator.py CHANGED
@@ -5,7 +5,6 @@ from jarvis.models.registry import PlatformRegistry
5
5
  from jarvis.tools.registry import ToolRegistry
6
6
  from jarvis.utils import OutputType, PrettyOutput
7
7
 
8
-
9
8
  class ToolGeneratorTool:
10
9
  name = "generate_tool"
11
10
  description = "生成新的工具代码并自动注册到Jarvis,自动扩充Jarvis的能力"
@@ -37,15 +36,13 @@ class ToolGeneratorTool:
37
36
  """
38
37
  # 设置工具目录
39
38
  self.tools_dir = Path.home() / '.jarvis_tools'
40
-
39
+
41
40
  # 确保工具目录存在
42
41
  self.tools_dir.mkdir(parents=True, exist_ok=True)
43
42
 
44
- def _generate_tool_code(self, tool_name: str, class_name: str,
45
- description: str, parameters: Dict) -> str:
43
+ def _generate_tool_code(self, tool_name: str, class_name: str, description: str, parameters: Dict) -> str:
46
44
  """使用大模型生成工具代码"""
47
- platform_name = os.getenv(
48
- "JARVIS_CODEGEN_PLATFORM") or PlatformRegistry.get_global_platform_name()
45
+ platform_name = os.getenv("JARVIS_CODEGEN_PLATFORM") or PlatformRegistry.get_global_platform_name()
49
46
  model = PlatformRegistry.create_platform(platform_name)
50
47
  model_name = os.getenv("JARVIS_CODEGEN_MODEL")
51
48
  if model_name:
@@ -84,16 +81,16 @@ class ExampleTool:
84
81
  # 验证参数示例
85
82
  if "param1" not in args:
86
83
  return {{"success": False, "error": "缺少必需参数: param1"}}
87
-
84
+
88
85
  # 记录操作示例
89
86
  PrettyOutput.print(f"处理参数: {{args['param1']}}", OutputType.INFO)
90
87
 
91
88
  # 使用大模型示例
92
89
  response = self.model.chat("prompt")
93
-
90
+
94
91
  # 实现具体功能
95
92
  result = "处理结果"
96
-
93
+
97
94
  return {{
98
95
  "success": True,
99
96
  "stdout": result,
@@ -114,11 +111,11 @@ class ExampleTool:
114
111
  # 提取代码块
115
112
  code_start = response.find("```python")
116
113
  code_end = response.find("```", code_start + 9)
117
-
114
+
118
115
  if code_start == -1 or code_end == -1:
119
116
  # 如果没有找到代码块标记,假设整个响应都是代码
120
117
  return response
121
-
118
+
122
119
  # 提取代码块内容(去掉```python和```标记)
123
120
  code = response[code_start + 9:code_end].strip()
124
121
  return code
@@ -165,10 +162,10 @@ class ExampleTool:
165
162
  return {
166
163
  "success": True,
167
164
  "stdout": f"工具已生成并注册到Jarvis\n"
168
- f"工具目录: {self.tools_dir}\n"
169
- f"工具名称: {tool_name}\n"
170
- f"工具描述: {description}\n"
171
- f"工具参数: {parameters}",
165
+ f"工具目录: {self.tools_dir}\n"
166
+ f"工具名称: {tool_name}\n"
167
+ f"工具描述: {description}\n"
168
+ f"工具参数: {parameters}",
172
169
  "stderr": ""
173
170
  }
174
171
 
@@ -6,7 +6,7 @@ from jarvis.utils import OutputType, PrettyOutput
6
6
 
7
7
  class MethodologyTool:
8
8
  """经验管理工具"""
9
-
9
+
10
10
  name = "methodology"
11
11
  description = "管理问题处理经验总结,支持添加、更新、删除操作"
12
12
  parameters = {
@@ -29,12 +29,12 @@ class MethodologyTool:
29
29
  },
30
30
  "required": ["operation", "problem_type"]
31
31
  }
32
-
32
+
33
33
  def __init__(self):
34
34
  """初始化经验管理工具"""
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
  """确保经验总结文件存在"""
40
40
  if not os.path.exists(self.methodology_file):
@@ -43,7 +43,7 @@ class MethodologyTool:
43
43
  yaml.safe_dump({}, f, allow_unicode=True)
44
44
  except Exception as e:
45
45
  PrettyOutput.print(f"创建经验总结文件失败: {str(e)}", OutputType.ERROR)
46
-
46
+
47
47
  def _load_methodologies(self) -> Dict:
48
48
  """加载所有经验总结"""
49
49
  try:
@@ -52,7 +52,7 @@ class MethodologyTool:
52
52
  except Exception as e:
53
53
  PrettyOutput.print(f"加载经验总结失败: {str(e)}", OutputType.ERROR)
54
54
  return {}
55
-
55
+
56
56
  def _save_methodologies(self, methodologies: Dict):
57
57
  """保存所有经验总结"""
58
58
  try:
@@ -60,31 +60,31 @@ class MethodologyTool:
60
60
  yaml.safe_dump(methodologies, f, allow_unicode=True)
61
61
  except Exception as e:
62
62
  PrettyOutput.print(f"保存经验总结失败: {str(e)}", OutputType.ERROR)
63
-
63
+
64
64
  def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
65
65
  """执行经验总结管理操作
66
-
66
+
67
67
  Args:
68
68
  args: 包含操作参数的字典
69
69
  - operation: 操作类型 (delete/update/add)
70
70
  - problem_type: 问题类型
71
71
  - content: 经验总结内容 (update/add 时必需)
72
-
72
+
73
73
  Returns:
74
74
  Dict[str, Any]: 包含执行结果的字典
75
75
  """
76
76
  operation = args.get("operation")
77
77
  problem_type = args.get("problem_type")
78
78
  content = args.get("content")
79
-
79
+
80
80
  if not operation or not problem_type:
81
81
  return {
82
82
  "success": False,
83
83
  "error": "缺少必要参数: operation 和 problem_type"
84
84
  }
85
-
85
+
86
86
  methodologies = self._load_methodologies()
87
-
87
+
88
88
  try:
89
89
  if operation == "delete":
90
90
  if problem_type in methodologies:
@@ -99,43 +99,43 @@ class MethodologyTool:
99
99
  "success": False,
100
100
  "error": f"未找到问题类型 '{problem_type}' 的经验总结"
101
101
  }
102
-
102
+
103
103
  elif operation in ["update", "add"]:
104
104
  if not content:
105
105
  return {
106
106
  "success": False,
107
107
  "error": "需要提供经验总结内容"
108
108
  }
109
-
109
+
110
110
  methodologies[problem_type] = content
111
111
  self._save_methodologies(methodologies)
112
-
112
+
113
113
  action = "更新" if problem_type in methodologies else "添加"
114
114
  return {
115
115
  "success": True,
116
116
  "stdout": f"已{action}问题类型 '{problem_type}' 的经验总结"
117
117
  }
118
-
118
+
119
119
  else:
120
120
  return {
121
121
  "success": False,
122
122
  "error": f"不支持的操作类型: {operation}"
123
123
  }
124
-
124
+
125
125
  except Exception as e:
126
126
  return {
127
127
  "success": False,
128
128
  "error": f"执行失败: {str(e)}"
129
129
  }
130
-
130
+
131
131
  def get_methodology(self, problem_type: str) -> Optional[str]:
132
132
  """获取指定问题类型的经验总结
133
-
133
+
134
134
  Args:
135
135
  problem_type: 问题类型
136
-
136
+
137
137
  Returns:
138
138
  Optional[str]: 经验总结内容,如果不存在则返回 None
139
139
  """
140
140
  methodologies = self._load_methodologies()
141
- return methodologies.get(problem_type)
141
+ return methodologies.get(problem_type)
jarvis/tools/registry.py CHANGED
@@ -11,8 +11,7 @@ from jarvis.utils import OutputType, PrettyOutput
11
11
 
12
12
 
13
13
  class ToolRegistry:
14
- global_tool_registry = None # type: ignore
15
-
14
+ global_tool_registry = None # type: ignore
16
15
  def __init__(self):
17
16
  """初始化工具注册器
18
17
  """
@@ -31,13 +30,13 @@ class ToolRegistry:
31
30
  def _load_builtin_tools(self):
32
31
  """从内置tools目录加载工具"""
33
32
  tools_dir = Path(__file__).parent
34
-
33
+
35
34
  # 遍历目录下的所有.py文件
36
35
  for file_path in tools_dir.glob("*.py"):
37
36
  # 跳过基础文件和__init__.py
38
37
  if file_path.name in ["base.py", "__init__.py", "registry.py"]:
39
38
  continue
40
-
39
+
41
40
  self.register_tool_by_file(file_path)
42
41
 
43
42
  def _load_external_tools(self):
@@ -45,21 +44,21 @@ class ToolRegistry:
45
44
  external_tools_dir = Path.home() / '.jarvis_tools'
46
45
  if not external_tools_dir.exists():
47
46
  return
48
-
47
+
49
48
  # 遍历目录下的所有.py文件
50
49
  for file_path in external_tools_dir.glob("*.py"):
51
50
  # 跳过__init__.py
52
51
  if file_path.name == "__init__.py":
53
52
  continue
54
-
53
+
55
54
  self.register_tool_by_file(file_path)
56
55
 
57
56
  def register_tool_by_file(self, file_path: str):
58
57
  """从指定文件加载并注册工具
59
-
58
+
60
59
  Args:
61
60
  file_path: 工具文件的路径
62
-
61
+
63
62
  Returns:
64
63
  bool: 是否成功加载工具
65
64
  """
@@ -68,32 +67,31 @@ class ToolRegistry:
68
67
  if not file_path.exists() or not file_path.is_file():
69
68
  PrettyOutput.print(f"文件不存在: {file_path}", OutputType.ERROR)
70
69
  return False
71
-
70
+
72
71
  # 动态导入模块
73
72
  module_name = file_path.stem
74
- spec = importlib.util.spec_from_file_location(
75
- module_name, file_path)
73
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
76
74
  if not spec or not spec.loader:
77
75
  PrettyOutput.print(f"无法加载模块: {file_path}", OutputType.ERROR)
78
76
  return False
79
-
77
+
80
78
  module = importlib.util.module_from_spec(spec)
81
79
  sys.modules[module_name] = module # 添加到 sys.modules 以支持相对导入
82
80
  spec.loader.exec_module(module)
83
-
81
+
84
82
  # 查找模块中的工具类
85
83
  tool_found = False
86
84
  for item_name in dir(module):
87
85
  item = getattr(module, item_name)
88
86
  # 检查是否是类,并且有必要的属性
89
- if (isinstance(item, type) and
90
- hasattr(item, 'name') and
91
- hasattr(item, 'description') and
92
- hasattr(item, 'parameters')):
93
-
87
+ if (isinstance(item, type) and
88
+ hasattr(item, 'name') and
89
+ hasattr(item, 'description') and
90
+ hasattr(item, 'parameters')):
91
+
94
92
  # 实例化工具类,传入模型和输出处理器
95
93
  tool_instance = item()
96
-
94
+
97
95
  # 注册工具
98
96
  self.register_tool(
99
97
  name=tool_instance.name,
@@ -101,31 +99,20 @@ class ToolRegistry:
101
99
  parameters=tool_instance.parameters,
102
100
  func=tool_instance.execute
103
101
  )
104
- PrettyOutput.print(
105
- f"从 {file_path} 加载工具: {
106
- tool_instance.name}: {
107
- tool_instance.description}",
108
- OutputType.INFO)
102
+ PrettyOutput.print(f"从 {file_path} 加载工具: {tool_instance.name}: {tool_instance.description}", OutputType.INFO)
109
103
  tool_found = True
110
-
104
+
111
105
  if not tool_found:
112
- PrettyOutput.print(
113
- f"文件中未找到有效的工具类: {file_path}",
114
- OutputType.WARNING)
106
+ PrettyOutput.print(f"文件中未找到有效的工具类: {file_path}", OutputType.WARNING)
115
107
  return False
116
-
108
+
117
109
  return True
118
-
110
+
119
111
  except Exception as e:
120
- PrettyOutput.print(
121
- f"加载工具失败 {
122
- file_path.name}: {
123
- str(e)}",
124
- OutputType.ERROR)
112
+ PrettyOutput.print(f"加载工具失败 {file_path.name}: {str(e)}", OutputType.ERROR)
125
113
  return False
126
114
 
127
- def register_tool(self, name: str, description: str,
128
- parameters: Dict, func: Callable):
115
+ def register_tool(self, name: str, description: str, parameters: Dict, func: Callable):
129
116
  """注册新工具"""
130
117
  self.tools[name] = Tool(name, description, parameters, func)
131
118
 
@@ -149,12 +136,12 @@ class ToolRegistry:
149
136
  try:
150
137
  if not tool_calls:
151
138
  return ""
152
-
139
+
153
140
  # 只处理第一个工具调用
154
141
  tool_call = tool_calls[0]
155
142
  name = tool_call["name"]
156
143
  args = tool_call["arguments"]
157
-
144
+
158
145
  if isinstance(args, str):
159
146
  try:
160
147
  args = json.loads(args)
@@ -166,14 +153,13 @@ class ToolRegistry:
166
153
  PrettyOutput.section(f"执行工具: {name}", OutputType.TOOL)
167
154
  if isinstance(args, dict):
168
155
  for key, value in args.items():
169
- PrettyOutput.print(
170
- f"参数: {key} = {value}", OutputType.DEBUG)
156
+ PrettyOutput.print(f"参数: {key} = {value}", OutputType.DEBUG)
171
157
  else:
172
158
  PrettyOutput.print(f"参数: {args}", OutputType.DEBUG)
173
-
159
+
174
160
  # 执行工具调用
175
161
  result = self.execute_tool(name, args)
176
-
162
+
177
163
  # 处理结果
178
164
  if result["success"]:
179
165
  stdout = result["stdout"]
@@ -190,7 +176,7 @@ class ToolRegistry:
190
176
  error_msg = result["error"]
191
177
  output = f"执行失败: {error_msg}"
192
178
  PrettyOutput.section("执行失败", OutputType.ERROR)
193
-
179
+
194
180
  return output
195
181
  except Exception as e:
196
182
  PrettyOutput.print(f"执行工具失败: {str(e)}", OutputType.ERROR)
jarvis/tools/shell.py CHANGED
@@ -21,6 +21,7 @@ class ShellTool:
21
21
  "required": ["command"]
22
22
  }
23
23
 
24
+
24
25
  def _escape_command(self, cmd: str) -> str:
25
26
  """转义命令中的特殊字符"""
26
27
  return cmd.replace("'", "'\"'\"'")
@@ -29,23 +30,21 @@ class ShellTool:
29
30
  """执行shell命令"""
30
31
  try:
31
32
  command = args["command"]
32
-
33
+
33
34
  # 生成临时文件名
34
- output_file = os.path.join(
35
- tempfile.gettempdir(), f"jarvis_shell_{
36
- os.getpid()}.log")
37
-
35
+ output_file = os.path.join(tempfile.gettempdir(), f"jarvis_shell_{os.getpid()}.log")
36
+
38
37
  # 转义命令中的特殊字符
39
38
  escaped_command = self._escape_command(command)
40
-
39
+
41
40
  # 修改命令以使用script
42
41
  tee_command = f"script -q -c '{escaped_command}' {output_file}"
43
-
42
+
44
43
  PrettyOutput.print(f"执行命令: {command}", OutputType.INFO)
45
-
44
+
46
45
  # 执行命令
47
46
  return_code = os.system(tee_command)
48
-
47
+
49
48
  # 读取输出文件
50
49
  try:
51
50
  with open(output_file, 'r', encoding='utf-8', errors='replace') as f:
@@ -60,14 +59,14 @@ class ShellTool:
60
59
  finally:
61
60
  # 清理临时文件
62
61
  Path(output_file).unlink(missing_ok=True)
63
-
62
+
64
63
  return {
65
64
  "success": return_code == 0,
66
65
  "stdout": output,
67
66
  "stderr": "",
68
67
  "return_code": return_code
69
68
  }
70
-
69
+
71
70
  except Exception as e:
72
71
  # 确保清理临时文件
73
72
  if 'output_file' in locals():
@@ -76,4 +75,4 @@ class ShellTool:
76
75
  return {
77
76
  "success": False,
78
77
  "error": str(e)
79
- }
78
+ }
jarvis/tools/sub_agent.py CHANGED
@@ -39,6 +39,7 @@ class SubAgentTool:
39
39
  "required": ["agent_name", "task", "context", "goal"]
40
40
  }
41
41
 
42
+
42
43
  def execute(self, args: Dict) -> Dict[str, Any]:
43
44
  """创建并运行子代理"""
44
45
  try:
@@ -78,4 +79,4 @@ class SubAgentTool:
78
79
  return {
79
80
  "success": False,
80
81
  "error": f"子代理执行失败: {str(e)}"
81
- }
82
+ }