jarvis-ai-assistant 0.1.32__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. jarvis/__init__.py +3 -0
  2. jarvis/__pycache__/__init__.cpython-313.pyc +0 -0
  3. jarvis/__pycache__/agent.cpython-313.pyc +0 -0
  4. jarvis/__pycache__/main.cpython-313.pyc +0 -0
  5. jarvis/__pycache__/models.cpython-313.pyc +0 -0
  6. jarvis/__pycache__/tools.cpython-313.pyc +0 -0
  7. jarvis/__pycache__/utils.cpython-313.pyc +0 -0
  8. jarvis/__pycache__/zte_llm.cpython-313.pyc +0 -0
  9. jarvis/agent.py +289 -0
  10. jarvis/main.py +148 -0
  11. jarvis/models/__init__.py +3 -0
  12. jarvis/models/__pycache__/__init__.cpython-313.pyc +0 -0
  13. jarvis/models/__pycache__/base.cpython-313.pyc +0 -0
  14. jarvis/models/__pycache__/kimi.cpython-313.pyc +0 -0
  15. jarvis/models/__pycache__/openai.cpython-313.pyc +0 -0
  16. jarvis/models/__pycache__/oyi.cpython-313.pyc +0 -0
  17. jarvis/models/__pycache__/registry.cpython-313.pyc +0 -0
  18. jarvis/models/base.py +39 -0
  19. jarvis/models/kimi.py +389 -0
  20. jarvis/models/openai.py +96 -0
  21. jarvis/models/oyi.py +271 -0
  22. jarvis/models/registry.py +199 -0
  23. jarvis/tools/__init__.py +5 -0
  24. jarvis/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  25. jarvis/tools/__pycache__/base.cpython-313.pyc +0 -0
  26. jarvis/tools/__pycache__/bing_search.cpython-313.pyc +0 -0
  27. jarvis/tools/__pycache__/calculator.cpython-313.pyc +0 -0
  28. jarvis/tools/__pycache__/calculator_tool.cpython-313.pyc +0 -0
  29. jarvis/tools/__pycache__/file_ops.cpython-313.pyc +0 -0
  30. jarvis/tools/__pycache__/generator.cpython-313.pyc +0 -0
  31. jarvis/tools/__pycache__/methodology.cpython-313.pyc +0 -0
  32. jarvis/tools/__pycache__/python_script.cpython-313.pyc +0 -0
  33. jarvis/tools/__pycache__/rag.cpython-313.pyc +0 -0
  34. jarvis/tools/__pycache__/registry.cpython-313.pyc +0 -0
  35. jarvis/tools/__pycache__/search.cpython-313.pyc +0 -0
  36. jarvis/tools/__pycache__/shell.cpython-313.pyc +0 -0
  37. jarvis/tools/__pycache__/sub_agent.cpython-313.pyc +0 -0
  38. jarvis/tools/__pycache__/user_confirmation.cpython-313.pyc +0 -0
  39. jarvis/tools/__pycache__/user_input.cpython-313.pyc +0 -0
  40. jarvis/tools/__pycache__/user_interaction.cpython-313.pyc +0 -0
  41. jarvis/tools/__pycache__/webpage.cpython-313.pyc +0 -0
  42. jarvis/tools/base.py +23 -0
  43. jarvis/tools/file_ops.py +110 -0
  44. jarvis/tools/generator.py +172 -0
  45. jarvis/tools/methodology.py +145 -0
  46. jarvis/tools/registry.py +183 -0
  47. jarvis/tools/shell.py +78 -0
  48. jarvis/tools/sub_agent.py +82 -0
  49. jarvis/utils.py +202 -0
  50. jarvis_ai_assistant-0.1.32.dist-info/LICENSE +21 -0
  51. jarvis_ai_assistant-0.1.32.dist-info/METADATA +345 -0
  52. jarvis_ai_assistant-0.1.32.dist-info/RECORD +55 -0
  53. jarvis_ai_assistant-0.1.32.dist-info/WHEEL +5 -0
  54. jarvis_ai_assistant-0.1.32.dist-info/entry_points.txt +2 -0
  55. jarvis_ai_assistant-0.1.32.dist-info/top_level.txt +1 -0
jarvis/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """Jarvis AI Assistant"""
2
+
3
+ __version__ = "0.1.32"
Binary file
Binary file
Binary file
Binary file
jarvis/agent.py ADDED
@@ -0,0 +1,289 @@
1
+ import time
2
+ from typing import Dict, List, Optional
3
+
4
+ import yaml
5
+
6
+ from .models.registry import ModelRegistry
7
+ from .tools import ToolRegistry
8
+ from .utils import PrettyOutput, OutputType, get_multiline_input, while_success
9
+ import os
10
+ from datetime import datetime
11
+ from prompt_toolkit import prompt
12
+
13
+ class Agent:
14
+ def __init__(self, name: str = "Jarvis", is_sub_agent: bool = False):
15
+ """Initialize Agent with a model, optional tool registry and name
16
+
17
+ Args:
18
+ model: 语言模型实例
19
+ tool_registry: 工具注册表实例
20
+ name: Agent名称,默认为"Jarvis"
21
+ is_sub_agent: 是否为子Agent,默认为False
22
+ """
23
+ self.model = ModelRegistry.get_global_model()
24
+ self.tool_registry = ToolRegistry.get_global_tool_registry()
25
+ self.name = name
26
+ self.is_sub_agent = is_sub_agent
27
+ self.prompt = ""
28
+
29
+
30
+ @staticmethod
31
+ def extract_tool_calls(content: str) -> List[Dict]:
32
+ """从内容中提取工具调用,如果检测到多个工具调用则抛出异常,并返回工具调用之前的内容和工具调用"""
33
+ # 分割内容为行
34
+ lines = content.split('\n')
35
+ tool_call_lines = []
36
+ in_tool_call = False
37
+
38
+ # 逐行处理
39
+ for line in lines:
40
+ if '<START_TOOL_CALL>' in line:
41
+ in_tool_call = True
42
+ continue
43
+ elif '<END_TOOL_CALL>' in line:
44
+ if in_tool_call and tool_call_lines:
45
+ try:
46
+ # 直接解析YAML
47
+ tool_call_text = '\n'.join(tool_call_lines)
48
+ tool_call_data = yaml.safe_load(tool_call_text)
49
+
50
+ # 验证必要的字段
51
+ if "name" in tool_call_data and "arguments" in tool_call_data:
52
+ # 返回工具调用之前的内容和工具调用
53
+ return [{
54
+ "name": tool_call_data["name"],
55
+ "arguments": tool_call_data["arguments"]
56
+ }]
57
+ else:
58
+ PrettyOutput.print("工具调用缺少必要字段", OutputType.ERROR)
59
+ raise Exception("工具调用缺少必要字段")
60
+ except yaml.YAMLError as e:
61
+ PrettyOutput.print(f"YAML解析错误: {str(e)}", OutputType.ERROR)
62
+ raise Exception(f"YAML解析错误: {str(e)}")
63
+ except Exception as e:
64
+ PrettyOutput.print(f"处理工具调用时发生错误: {str(e)}", OutputType.ERROR)
65
+ raise Exception(f"处理工具调用时发生错误: {str(e)}")
66
+ in_tool_call = False
67
+ continue
68
+
69
+ if in_tool_call:
70
+ tool_call_lines.append(line)
71
+
72
+ return []
73
+
74
+ def _call_model(self, message: str) -> str:
75
+ """调用模型获取响应"""
76
+ sleep_time = 5
77
+ while True:
78
+ ret = while_success(lambda: self.model.chat(message), sleep_time=5)
79
+ if ret:
80
+ return ret
81
+ else:
82
+ PrettyOutput.print(f"调用模型失败,重试中... 等待 {sleep_time}s", OutputType.INFO)
83
+ time.sleep(sleep_time)
84
+ sleep_time *= 2
85
+ if sleep_time > 30:
86
+ sleep_time = 30
87
+ continue
88
+
89
+
90
+ def _load_methodology(self) -> str:
91
+ """加载方法论"""
92
+ user_jarvis_methodology = os.path.expanduser("~/.jarvis_methodology")
93
+ ret = ""
94
+ if os.path.exists(user_jarvis_methodology):
95
+ with open(user_jarvis_methodology, "r", encoding="utf-8") as f:
96
+ data = yaml.safe_load(f)
97
+ for k, v in data.items():
98
+ ret += f"问题类型: \n{k}\n方法论: \n{v}\n\n"
99
+ return ret
100
+
101
+ def run(self, user_input: str, file_list: Optional[List[str]] = None, keep_history: bool = False) -> str:
102
+ """处理用户输入并返回响应,返回任务总结报告
103
+
104
+ Args:
105
+ user_input: 用户输入的任务描述
106
+ file_list: 可选的文件列表,默认为None
107
+ keep_history: 是否保留对话历史,默认为False
108
+
109
+ Returns:
110
+ str: 任务总结报告
111
+ """
112
+ try:
113
+ self.clear_history()
114
+
115
+ if file_list:
116
+ self.model.upload_files(file_list)
117
+
118
+ # 加载方法论
119
+ methodology = self._load_methodology()
120
+
121
+ methodology_prompt = ""
122
+ if methodology:
123
+ methodology_prompt = f"""这是以往处理问题的标准方法论,如果当前任务与此类似,可参考:
124
+ {methodology}
125
+
126
+ """
127
+
128
+ # 显示任务开始
129
+ PrettyOutput.section(f"开始新任务: {self.name}", OutputType.PLANNING)
130
+
131
+ tools_prompt = "可用工具:\n"
132
+ for tool in self.tool_registry.get_all_tools():
133
+ tools_prompt += f"- 名称: {tool['name']}\n"
134
+ tools_prompt += f" 描述: {tool['description']}\n"
135
+ tools_prompt += f" 参数: {tool['parameters']}\n"
136
+
137
+ self.model.set_system_message(f"""你是 {self.name},一个问题处理能力强大的 AI 助手。
138
+
139
+ 你会严格按照以下步骤处理问题:
140
+ 1. 问题重述:确认理解问题
141
+ 2. 根因分析(如果是问题分析类需要,其他不需要)
142
+ 3. 设定目标:需要可达成,可检验的一个或多个目标
143
+ 4. 生成解决方案:生成一个或者多个具备可操作性的解决方案
144
+ 5. 评估解决方案:从众多解决方案中选择一种最优的方案
145
+ 6. 制定行动计划:根据目前可以使用的工具制定行动计划
146
+ 7. 执行行动计划:每步执行一个步骤,最多使用一个工具(工具执行完成后,等待工具结果再执行下一步)
147
+ 8. 监控与调整:如果执行结果与预期不符,则反思并调整行动计划,迭代之前的步骤
148
+ 9. 更新方法论(如有必要):任务完成后总结执行过程中的经验教训,生成同类问题的通用方法论,使用方法论工具进行更新或者添加
149
+
150
+ -------------------------------------------------------------
151
+
152
+ 方法论模板:
153
+ 1. 问题重述
154
+ 2. 最优解决方案
155
+ 3. 最优方案执行步骤(失败的行动不需要体现)
156
+
157
+ -------------------------------------------------------------
158
+
159
+ {tools_prompt}
160
+
161
+ -------------------------------------------------------------
162
+
163
+ 工具使用格式:
164
+
165
+ <START_TOOL_CALL>
166
+ name: tool_name
167
+ arguments:
168
+ param1: value1
169
+ param2: value2
170
+ <END_TOOL_CALL>
171
+
172
+ -------------------------------------------------------------
173
+
174
+ 严格规则:
175
+ 1. 每次只能执行一个工具
176
+ 2. 等待用户提供执行结果
177
+ 3. 不要假设或想象结果
178
+ 4. 不要创建虚假对话
179
+ 5. 如果现有信息不足以解决问题,则可以询问用户
180
+ 6. 处理问题的每个步骤不是必须有的,可按情况省略
181
+
182
+ -------------------------------------------------------------
183
+
184
+ {methodology_prompt}
185
+
186
+ -------------------------------------------------------------
187
+
188
+ """)
189
+ self.prompt = f"用户任务: {user_input}"
190
+
191
+ while True:
192
+ try:
193
+ # 显示思考状态
194
+ PrettyOutput.print("分析任务...", OutputType.PROGRESS)
195
+
196
+ current_response = self._call_model(self.prompt)
197
+
198
+ try:
199
+ result = Agent.extract_tool_calls(current_response)
200
+ except Exception as e:
201
+ PrettyOutput.print(f"工具调用错误: {str(e)}", OutputType.ERROR)
202
+ self.prompt = f"工具调用错误: {str(e)}"
203
+ continue
204
+
205
+ if len(result) > 0:
206
+
207
+ PrettyOutput.print("执行工具调用...", OutputType.PROGRESS)
208
+ tool_result = self.tool_registry.handle_tool_calls(result)
209
+ PrettyOutput.print(tool_result, OutputType.RESULT)
210
+
211
+ self.prompt = tool_result
212
+ continue
213
+
214
+ # 获取用户输入
215
+ user_input = get_multiline_input(f"{self.name}: 您可以继续输入,或输入空行结束当前任务")
216
+ if user_input == "__interrupt__":
217
+ PrettyOutput.print("任务已取消", OutputType.WARNING)
218
+ return "Task cancelled by user"
219
+
220
+ if user_input:
221
+ self.prompt = user_input
222
+ continue
223
+
224
+ if not user_input:
225
+ while True:
226
+ choice = prompt("是否需要为此任务生成方法论以提升Jarvis对类似任务的处理能力?(y/n), 回车跳过: ")
227
+ if choice == "y":
228
+ self._make_methodology()
229
+ break
230
+ elif choice == "n" or choice == "":
231
+ break
232
+ else:
233
+ PrettyOutput.print("请输入y或n", OutputType.ERROR)
234
+ continue
235
+ PrettyOutput.section("任务完成", OutputType.SUCCESS)
236
+ if self.is_sub_agent:
237
+ # 生成任务总结
238
+ summary_prompt = f"""请对以上任务执行情况生成一个简洁的总结报告,包括:
239
+
240
+ 1. 任务目标: 任务重述
241
+ 2. 执行结果: 成功/失败
242
+ 3. 关键信息: 提取执行过程中的重要信息
243
+ 4. 重要发现: 任何值得注意的发现
244
+ 5. 后续建议: 如果有的话
245
+
246
+ 请用简洁的要点形式描述,突出重要信息。
247
+ """
248
+ self.prompt = summary_prompt
249
+ summary = self._call_model(self.prompt)
250
+ return summary
251
+ else:
252
+ return "Task completed"
253
+
254
+ except Exception as e:
255
+ PrettyOutput.print(str(e), OutputType.ERROR)
256
+ return f"Task failed: {str(e)}"
257
+
258
+ except Exception as e:
259
+ PrettyOutput.print(str(e), OutputType.ERROR)
260
+ return f"Task failed: {str(e)}"
261
+
262
+ finally:
263
+ # 只在不保留历史时删除会话
264
+ if not keep_history:
265
+ try:
266
+ self.model.delete_chat()
267
+ except Exception as e:
268
+ PrettyOutput.print(f"清理会话时发生错误: {str(e)}", OutputType.ERROR)
269
+
270
+ def clear_history(self):
271
+ """清除对话历史,只保留系统提示"""
272
+ self.prompt = ""
273
+ self.model.reset()
274
+
275
+ def _make_methodology(self):
276
+ """生成方法论"""
277
+ current_response = self._call_model("""请根据之前的对话内容,判断是否有必要更新、添加、删除现有方法论,如果有,使用methodology工具进行管理。
278
+ 方法论模板:
279
+ 1. 问题重述
280
+ 2. 最优解决方案
281
+ 3. 最优方案执行步骤(失败的行动不需要体现)
282
+ """)
283
+
284
+ try:
285
+ result = Agent.extract_tool_calls(current_response)
286
+ except Exception as e:
287
+ PrettyOutput.print(f"工具调用错误: {str(e)}", OutputType.ERROR)
288
+ return
289
+
jarvis/main.py ADDED
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env python3
2
+ """Command line interface for Jarvis."""
3
+
4
+ import argparse
5
+ import yaml
6
+ import os
7
+ import sys
8
+ from pathlib import Path
9
+ from prompt_toolkit import prompt
10
+
11
+ from jarvis.models.registry import ModelRegistry
12
+
13
+ # 添加父目录到Python路径以支持导入
14
+ sys.path.insert(0, str(Path(__file__).parent.parent))
15
+
16
+ from jarvis.agent import Agent
17
+ from jarvis.tools import ToolRegistry
18
+ from jarvis.utils import PrettyOutput, OutputType, get_multiline_input, load_env_from_file
19
+
20
+
21
+ def load_tasks() -> dict:
22
+ """Load tasks from .jarvis files in user home and current directory."""
23
+ tasks = {}
24
+
25
+ # 检查用户目录下的 .jarvis
26
+ user_jarvis = os.path.expanduser("~/.jarvis")
27
+ if os.path.exists(user_jarvis):
28
+ try:
29
+ with open(user_jarvis, "r", encoding="utf-8") as f:
30
+ user_tasks = yaml.safe_load(f)
31
+
32
+ if isinstance(user_tasks, dict):
33
+ # 验证并添加用户目录的任务
34
+ for name, desc in user_tasks.items():
35
+ if desc: # 确保描述不为空
36
+ tasks[str(name)] = str(desc)
37
+ else:
38
+ PrettyOutput.print("Warning: ~/.jarvis file should contain a dictionary of task_name: task_description", OutputType.ERROR)
39
+ except Exception as e:
40
+ PrettyOutput.print(f"Error loading ~/.jarvis file: {str(e)}", OutputType.ERROR)
41
+
42
+ # 检查当前目录下的 .jarvis
43
+ if os.path.exists(".jarvis"):
44
+ try:
45
+ with open(".jarvis", "r", encoding="utf-8") as f:
46
+ local_tasks = yaml.safe_load(f)
47
+
48
+ if isinstance(local_tasks, dict):
49
+ # 验证并添加当前目录的任务,如果有重名则覆盖用户目录的任务
50
+ for name, desc in local_tasks.items():
51
+ if desc: # 确保描述不为空
52
+ tasks[str(name)] = str(desc)
53
+ else:
54
+ PrettyOutput.print("Warning: .jarvis file should contain a dictionary of task_name: task_description", OutputType.ERROR)
55
+ except Exception as e:
56
+ PrettyOutput.print(f"Error loading .jarvis file: {str(e)}", OutputType.ERROR)
57
+
58
+ return tasks
59
+
60
+ def select_task(tasks: dict) -> str:
61
+ """Let user select a task from the list or skip. Returns task description if selected."""
62
+ if not tasks:
63
+ return ""
64
+
65
+ # Convert tasks to list for ordered display
66
+ task_names = list(tasks.keys())
67
+
68
+ PrettyOutput.print("\nAvailable tasks:", OutputType.INFO)
69
+ for i, name in enumerate(task_names, 1):
70
+ PrettyOutput.print(f"[{i}] {name}", OutputType.INFO)
71
+ PrettyOutput.print("[0] 跳过预定义任务", OutputType.INFO)
72
+
73
+
74
+ while True:
75
+ try:
76
+ choice = prompt(
77
+ "\n请选择一个任务编号(0跳过): ",
78
+ ).strip()
79
+
80
+ if not choice:
81
+ return ""
82
+
83
+ choice = int(choice)
84
+ if choice == 0:
85
+ return ""
86
+ elif 1 <= choice <= len(task_names):
87
+ selected_name = task_names[choice - 1]
88
+ return tasks[selected_name] # Return the task description
89
+ else:
90
+ PrettyOutput.print("Invalid choice. Please select a number from the list.", OutputType.ERROR)
91
+
92
+ except KeyboardInterrupt:
93
+ return "" # Return empty on Ctrl+C
94
+ except EOFError:
95
+ return "" # Return empty on Ctrl+D
96
+ except Exception as e:
97
+ PrettyOutput.print(f"选择失败: {str(e)}", OutputType.ERROR)
98
+ continue
99
+
100
+ def main():
101
+ """Main entry point for Jarvis."""
102
+ # Add argument parser
103
+ parser = argparse.ArgumentParser(description='Jarvis AI Assistant')
104
+ parser.add_argument('-f', '--files', nargs='*', help='List of files to process')
105
+ parser.add_argument('--keep-history', action='store_true', help='Keep chat history (do not delete chat session)')
106
+ parser.add_argument('-m', '--model', default='kimi', help='选择模型')
107
+ args = parser.parse_args()
108
+
109
+ load_env_from_file()
110
+
111
+ ModelRegistry.get_model_registry().set_global_model(args.model)
112
+
113
+ try:
114
+ # 获取全局模型实例
115
+ agent = Agent()
116
+
117
+ # 欢迎信息
118
+ PrettyOutput.print(f"Jarvis 已初始化 - With {args.model}", OutputType.SYSTEM)
119
+ if args.keep_history:
120
+ PrettyOutput.print("已启用历史保留模式", OutputType.INFO)
121
+
122
+ # 加载预定义任务
123
+ tasks = load_tasks()
124
+ if tasks:
125
+ selected_task = select_task(tasks)
126
+ if selected_task:
127
+ PrettyOutput.print(f"\n执行任务: {selected_task}", OutputType.INFO)
128
+ agent.run(selected_task, args.files, keep_history=args.keep_history)
129
+ return 0
130
+
131
+ # 如果没有选择预定义任务,进入交互模式
132
+ while True:
133
+ try:
134
+ user_input = get_multiline_input("请输入您的任务(输入空行退出):")
135
+ if not user_input or user_input == "__interrupt__":
136
+ break
137
+ agent.run(user_input, args.files, keep_history=args.keep_history)
138
+ except Exception as e:
139
+ PrettyOutput.print(f"错误: {str(e)}", OutputType.ERROR)
140
+
141
+ except Exception as e:
142
+ PrettyOutput.print(f"初始化错误: {str(e)}", OutputType.ERROR)
143
+ return 1
144
+
145
+ return 0
146
+
147
+ if __name__ == "__main__":
148
+ exit(main())
@@ -0,0 +1,3 @@
1
+ from .base import BaseModel
2
+
3
+ __all__ = ['BaseModel']
jarvis/models/base.py ADDED
@@ -0,0 +1,39 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Dict, List
3
+ from ..utils import OutputType, PrettyOutput
4
+
5
+
6
+ class BaseModel(ABC):
7
+ """大语言模型基类"""
8
+
9
+ def __init__(self):
10
+ """初始化模型"""
11
+ pass
12
+
13
+
14
+ @abstractmethod
15
+ def chat(self, message: str) -> str:
16
+ """执行对话"""
17
+ raise NotImplementedError("chat is not implemented")
18
+
19
+ def upload_files(self, file_list: List[str]) -> List[Dict]:
20
+ """上传文件"""
21
+ raise NotImplementedError("upload_files is not implemented")
22
+
23
+ def reset(self):
24
+ """重置模型"""
25
+ raise NotImplementedError("reset is not implemented")
26
+
27
+ @abstractmethod
28
+ def name(self) -> str:
29
+ """模型名称"""
30
+ raise NotImplementedError("name is not implemented")
31
+
32
+ @abstractmethod
33
+ def delete_chat(self)->bool:
34
+ """删除对话"""
35
+ raise NotImplementedError("delete_chat is not implemented")
36
+
37
+ def set_system_message(self, message: str):
38
+ """设置系统消息"""
39
+ raise NotImplementedError("set_system_message is not implemented")