jarvis-ai-assistant 0.1.32__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.
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")