jarvis-ai-assistant 0.1.45__py3-none-any.whl → 0.1.46__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.
jarvis/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Jarvis AI Assistant"""
2
2
 
3
- __version__ = "0.1.45"
3
+ __version__ = "0.1.46"
jarvis/agent.py CHANGED
@@ -10,10 +10,11 @@ import os
10
10
  from datetime import datetime
11
11
  from prompt_toolkit import prompt
12
12
 
13
+
13
14
  class Agent:
14
15
  def __init__(self, name: str = "Jarvis", is_sub_agent: bool = False):
15
16
  """Initialize Agent with a model, optional tool registry and name
16
-
17
+
17
18
  Args:
18
19
  model: 语言模型实例
19
20
  tool_registry: 工具注册表实例
@@ -26,7 +27,6 @@ class Agent:
26
27
  self.is_sub_agent = is_sub_agent
27
28
  self.prompt = ""
28
29
 
29
-
30
30
  @staticmethod
31
31
  def extract_tool_calls(content: str) -> List[Dict]:
32
32
  """从内容中提取工具调用,如果检测到多个工具调用则抛出异常,并返回工具调用之前的内容和工具调用"""
@@ -34,7 +34,7 @@ class Agent:
34
34
  lines = content.split('\n')
35
35
  tool_call_lines = []
36
36
  in_tool_call = False
37
-
37
+
38
38
  # 逐行处理
39
39
  for line in lines:
40
40
  if '<START_TOOL_CALL>' in line:
@@ -46,7 +46,7 @@ class Agent:
46
46
  # 直接解析YAML
47
47
  tool_call_text = '\n'.join(tool_call_lines)
48
48
  tool_call_data = yaml.safe_load(tool_call_text)
49
-
49
+
50
50
  # 验证必要的字段
51
51
  if "name" in tool_call_data and "arguments" in tool_call_data:
52
52
  # 返回工具调用之前的内容和工具调用
@@ -58,17 +58,21 @@ class Agent:
58
58
  PrettyOutput.print("工具调用缺少必要字段", OutputType.ERROR)
59
59
  raise Exception("工具调用缺少必要字段")
60
60
  except yaml.YAMLError as e:
61
- PrettyOutput.print(f"YAML解析错误: {str(e)}", OutputType.ERROR)
61
+ PrettyOutput.print(
62
+ f"YAML解析错误: {
63
+ str(e)}", OutputType.ERROR)
62
64
  raise Exception(f"YAML解析错误: {str(e)}")
63
65
  except Exception as e:
64
- PrettyOutput.print(f"处理工具调用时发生错误: {str(e)}", OutputType.ERROR)
66
+ PrettyOutput.print(
67
+ f"处理工具调用时发生错误: {
68
+ str(e)}", OutputType.ERROR)
65
69
  raise Exception(f"处理工具调用时发生错误: {str(e)}")
66
70
  in_tool_call = False
67
71
  continue
68
-
72
+
69
73
  if in_tool_call:
70
74
  tool_call_lines.append(line)
71
-
75
+
72
76
  return []
73
77
 
74
78
  def _call_model(self, message: str) -> str:
@@ -79,14 +83,15 @@ class Agent:
79
83
  if ret:
80
84
  return ret
81
85
  else:
82
- PrettyOutput.print(f"调用模型失败,重试中... 等待 {sleep_time}s", OutputType.INFO)
86
+ PrettyOutput.print(
87
+ f"调用模型失败,重试中... 等待 {sleep_time}s",
88
+ OutputType.INFO)
83
89
  time.sleep(sleep_time)
84
90
  sleep_time *= 2
85
91
  if sleep_time > 30:
86
92
  sleep_time = 30
87
93
  continue
88
94
 
89
-
90
95
  def _load_methodology(self) -> str:
91
96
  """加载经验总结"""
92
97
  user_jarvis_methodology = os.path.expanduser("~/.jarvis_methodology")
@@ -96,23 +101,28 @@ class Agent:
96
101
  data = yaml.safe_load(f)
97
102
  for k, v in data.items():
98
103
  ret += f"问题类型: \n{k}\n经验总结: \n{v}\n\n"
99
- PrettyOutput.print(f"从 {user_jarvis_methodology} 加载经验总结: {', '.join(data.keys())}", OutputType.INFO)
104
+ PrettyOutput.print(
105
+ f"从 {user_jarvis_methodology} 加载经验总结: {
106
+ ', '.join(
107
+ data.keys())}",
108
+ OutputType.INFO)
100
109
  return ret
101
110
 
102
- def run(self, user_input: str, file_list: Optional[List[str]] = None, keep_history: bool = False) -> str:
111
+ def run(self, user_input: str,
112
+ file_list: Optional[List[str]] = None, keep_history: bool = False) -> str:
103
113
  """处理用户输入并返回响应,返回任务总结报告
104
-
114
+
105
115
  Args:
106
116
  user_input: 用户输入的任务描述
107
117
  file_list: 可选的文件列表,默认为None
108
118
  keep_history: 是否保留对话历史,默认为False
109
-
119
+
110
120
  Returns:
111
121
  str: 任务总结报告
112
122
  """
113
123
  try:
114
124
  self.clear_history()
115
-
125
+
116
126
  if file_list:
117
127
  self.model.upload_files(file_list)
118
128
 
@@ -125,7 +135,7 @@ class Agent:
125
135
  {methodology}
126
136
 
127
137
  """
128
-
138
+
129
139
  # 显示任务开始
130
140
  PrettyOutput.section(f"开始新任务: {self.name}", OutputType.PLANNING)
131
141
 
@@ -136,7 +146,7 @@ class Agent:
136
146
  tools_prompt += f" 参数: {tool['parameters']}\n"
137
147
 
138
148
  self.model.set_system_message(f"""你是 {self.name},一个问题处理能力强大的 AI 助手。
139
-
149
+
140
150
  你会严格按照以下步骤处理问题:
141
151
  1. 问题重述:确认理解问题
142
152
  2. 根因分析(如果是问题分析类需要,其他不需要)
@@ -148,7 +158,7 @@ class Agent:
148
158
  8. 监控与调整:如果执行结果与预期不符,则反思并调整行动计划,迭代之前的步骤
149
159
  9. 更新经验总结(如有必要):任务完成后总结执行过程中的经验教训,生成同类问题的通用经验总结,使用经验总结工具进行更新或者添加
150
160
 
151
- -------------------------------------------------------------
161
+ -------------------------------------------------------------
152
162
 
153
163
  经验总结模板:
154
164
  1. 问题重述
@@ -193,27 +203,30 @@ arguments:
193
203
  try:
194
204
  # 显示思考状态
195
205
  PrettyOutput.print("分析任务...", OutputType.PROGRESS)
196
-
206
+
197
207
  current_response = self._call_model(self.prompt)
198
-
208
+
199
209
  try:
200
210
  result = Agent.extract_tool_calls(current_response)
201
211
  except Exception as e:
202
- PrettyOutput.print(f"工具调用错误: {str(e)}", OutputType.ERROR)
212
+ PrettyOutput.print(
213
+ f"工具调用错误: {str(e)}", OutputType.ERROR)
203
214
  self.prompt = f"工具调用错误: {str(e)}"
204
215
  continue
205
-
216
+
206
217
  if len(result) > 0:
207
218
 
208
219
  PrettyOutput.print("执行工具调用...", OutputType.PROGRESS)
209
- tool_result = self.tool_registry.handle_tool_calls(result)
220
+ tool_result = self.tool_registry.handle_tool_calls(
221
+ result)
210
222
  PrettyOutput.print(tool_result, OutputType.RESULT)
211
223
 
212
224
  self.prompt = tool_result
213
225
  continue
214
-
226
+
215
227
  # 获取用户输入
216
- user_input = get_multiline_input(f"{self.name}: 您可以继续输入,或输入空行结束当前任务")
228
+ user_input = get_multiline_input(
229
+ f"{self.name}: 您可以继续输入,或输入空行结束当前任务")
217
230
  if user_input == "__interrupt__":
218
231
  PrettyOutput.print("任务已取消", OutputType.WARNING)
219
232
  return "Task cancelled by user"
@@ -221,10 +234,11 @@ arguments:
221
234
  if user_input:
222
235
  self.prompt = user_input
223
236
  continue
224
-
237
+
225
238
  if not user_input:
226
239
  while True:
227
- choice = prompt("是否需要手动为此任务生成经验总结以提升Jarvis对类似任务的处理能力?(y/n), 回车跳过: ")
240
+ choice = prompt(
241
+ "是否需要手动为此任务生成经验总结以提升Jarvis对类似任务的处理能力?(y/n), 回车跳过: ")
228
242
  if choice == "y":
229
243
  self._make_methodology()
230
244
  break
@@ -259,18 +273,20 @@ arguments:
259
273
  except Exception as e:
260
274
  PrettyOutput.print(str(e), OutputType.ERROR)
261
275
  return f"Task failed: {str(e)}"
262
-
276
+
263
277
  finally:
264
278
  # 只在不保留历史时删除会话
265
279
  if not keep_history:
266
280
  try:
267
281
  self.model.delete_chat()
268
282
  except Exception as e:
269
- PrettyOutput.print(f"清理会话时发生错误: {str(e)}", OutputType.ERROR)
283
+ PrettyOutput.print(
284
+ f"清理会话时发生错误: {
285
+ str(e)}", OutputType.ERROR)
270
286
 
271
287
  def clear_history(self):
272
288
  """清除对话历史,只保留系统提示"""
273
- self.prompt = ""
289
+ self.prompt = ""
274
290
  self.model.reset()
275
291
 
276
292
  def _make_methodology(self):
@@ -281,7 +297,7 @@ arguments:
281
297
  2. 最优解决方案
282
298
  3. 最优方案执行步骤(失败的行动不需要体现)
283
299
  """)
284
-
300
+
285
301
  try:
286
302
  result = Agent.extract_tool_calls(current_response)
287
303
  except Exception as e:
@@ -291,4 +307,3 @@ arguments:
291
307
  PrettyOutput.print("执行工具调用...", OutputType.PROGRESS)
292
308
  tool_result = self.tool_registry.handle_tool_calls(result)
293
309
  PrettyOutput.print(tool_result, OutputType.RESULT)
294
-
jarvis/main.py CHANGED
@@ -1,6 +1,9 @@
1
1
  #!/usr/bin/env python3
2
2
  """Command line interface for Jarvis."""
3
3
 
4
+ from jarvis.utils import PrettyOutput, OutputType, get_multiline_input, load_env_from_file
5
+ from jarvis.tools import ToolRegistry
6
+ from jarvis.agent import Agent
4
7
  import argparse
5
8
  import yaml
6
9
  import os
@@ -13,73 +16,76 @@ from jarvis.models.registry import PlatformRegistry
13
16
  # 添加父目录到Python路径以支持导入
14
17
  sys.path.insert(0, str(Path(__file__).parent.parent))
15
18
 
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
19
 
21
20
  def load_tasks() -> dict:
22
21
  """Load tasks from .jarvis files in user home and current directory."""
23
22
  tasks = {}
24
-
23
+
25
24
  # 检查用户目录下的 .jarvis
26
25
  user_jarvis = os.path.expanduser("~/.jarvis")
27
26
  if os.path.exists(user_jarvis):
28
27
  try:
29
28
  with open(user_jarvis, "r", encoding="utf-8") as f:
30
29
  user_tasks = yaml.safe_load(f)
31
-
30
+
32
31
  if isinstance(user_tasks, dict):
33
32
  # 验证并添加用户目录的任务
34
33
  for name, desc in user_tasks.items():
35
34
  if desc: # 确保描述不为空
36
35
  tasks[str(name)] = str(desc)
37
36
  else:
38
- PrettyOutput.print("Warning: ~/.jarvis file should contain a dictionary of task_name: task_description", OutputType.ERROR)
37
+ PrettyOutput.print(
38
+ "Warning: ~/.jarvis file should contain a dictionary of task_name: task_description",
39
+ OutputType.ERROR)
39
40
  except Exception as e:
40
- PrettyOutput.print(f"Error loading ~/.jarvis file: {str(e)}", OutputType.ERROR)
41
-
41
+ PrettyOutput.print(
42
+ f"Error loading ~/.jarvis file: {str(e)}", OutputType.ERROR)
43
+
42
44
  # 检查当前目录下的 .jarvis
43
45
  if os.path.exists(".jarvis"):
44
46
  try:
45
47
  with open(".jarvis", "r", encoding="utf-8") as f:
46
48
  local_tasks = yaml.safe_load(f)
47
-
49
+
48
50
  if isinstance(local_tasks, dict):
49
51
  # 验证并添加当前目录的任务,如果有重名则覆盖用户目录的任务
50
52
  for name, desc in local_tasks.items():
51
53
  if desc: # 确保描述不为空
52
54
  tasks[str(name)] = str(desc)
53
55
  else:
54
- PrettyOutput.print("Warning: .jarvis file should contain a dictionary of task_name: task_description", OutputType.ERROR)
56
+ PrettyOutput.print(
57
+ "Warning: .jarvis file should contain a dictionary of task_name: task_description",
58
+ OutputType.ERROR)
55
59
  except Exception as e:
56
- PrettyOutput.print(f"Error loading .jarvis file: {str(e)}", OutputType.ERROR)
57
-
60
+ PrettyOutput.print(
61
+ f"Error loading .jarvis file: {
62
+ str(e)}", OutputType.ERROR)
63
+
58
64
  return tasks
59
65
 
66
+
60
67
  def select_task(tasks: dict) -> str:
61
68
  """Let user select a task from the list or skip. Returns task description if selected."""
62
69
  if not tasks:
63
70
  return ""
64
-
71
+
65
72
  # Convert tasks to list for ordered display
66
73
  task_names = list(tasks.keys())
67
-
74
+
68
75
  PrettyOutput.print("\nAvailable tasks:", OutputType.INFO)
69
76
  for i, name in enumerate(task_names, 1):
70
77
  PrettyOutput.print(f"[{i}] {name}", OutputType.INFO)
71
78
  PrettyOutput.print("[0] 跳过预定义任务", OutputType.INFO)
72
-
73
-
79
+
74
80
  while True:
75
81
  try:
76
82
  choice = prompt(
77
83
  "\n请选择一个任务编号(0跳过): ",
78
84
  ).strip()
79
-
85
+
80
86
  if not choice:
81
87
  return ""
82
-
88
+
83
89
  choice = int(choice)
84
90
  if choice == 0:
85
91
  return ""
@@ -87,8 +93,10 @@ def select_task(tasks: dict) -> str:
87
93
  selected_name = task_names[choice - 1]
88
94
  return tasks[selected_name] # Return the task description
89
95
  else:
90
- PrettyOutput.print("Invalid choice. Please select a number from the list.", OutputType.ERROR)
91
-
96
+ PrettyOutput.print(
97
+ "Invalid choice. Please select a number from the list.",
98
+ OutputType.ERROR)
99
+
92
100
  except KeyboardInterrupt:
93
101
  return "" # Return empty on Ctrl+C
94
102
  except EOFError:
@@ -97,12 +105,16 @@ def select_task(tasks: dict) -> str:
97
105
  PrettyOutput.print(f"选择失败: {str(e)}", OutputType.ERROR)
98
106
  continue
99
107
 
108
+
100
109
  def main():
101
110
  """Jarvis 的主入口点"""
102
111
  # 添加参数解析器
103
112
  parser = argparse.ArgumentParser(description='Jarvis AI 助手')
104
113
  parser.add_argument('-f', '--files', nargs='*', help='要处理的文件列表')
105
- parser.add_argument('--keep-history', action='store_true', help='保持聊天历史(不删除会话)')
114
+ parser.add_argument(
115
+ '--keep-history',
116
+ action='store_true',
117
+ help='保持聊天历史(不删除会话)')
106
118
  parser.add_argument('-p', '--platform', default='', help='选择AI平台')
107
119
  args = parser.parse_args()
108
120
 
@@ -111,36 +123,45 @@ def main():
111
123
  platform = args.platform if args.platform else os.getenv('JARVIS_PLATFORM')
112
124
 
113
125
  if not platform:
114
- PrettyOutput.print("未指定AI平台,请使用 -p 参数或者设置 JARVIS_PLATFORM 环境变量", OutputType.ERROR)
126
+ PrettyOutput.print(
127
+ "未指定AI平台,请使用 -p 参数或者设置 JARVIS_PLATFORM 环境变量",
128
+ OutputType.ERROR)
115
129
  return 1
116
130
 
117
131
  PlatformRegistry.get_global_platform_registry().set_global_platform_name(platform)
118
-
132
+
119
133
  try:
120
134
  # 获取全局模型实例
121
135
  agent = Agent()
122
136
 
123
137
  # 欢迎信息
124
- PrettyOutput.print(f"Jarvis 已初始化 - With {platform} 平台,模型: {agent.model.name()}", OutputType.SYSTEM)
138
+ PrettyOutput.print(
139
+ f"Jarvis 已初始化 - With {platform} 平台,模型: {agent.model.name()}", OutputType.SYSTEM)
125
140
  if args.keep_history:
126
141
  PrettyOutput.print("已启用历史保留模式", OutputType.INFO)
127
-
142
+
128
143
  # 加载预定义任务
129
144
  tasks = load_tasks()
130
145
  if tasks:
131
146
  selected_task = select_task(tasks)
132
147
  if selected_task:
133
148
  PrettyOutput.print(f"\n执行任务: {selected_task}", OutputType.INFO)
134
- agent.run(selected_task, args.files, keep_history=args.keep_history)
149
+ agent.run(
150
+ selected_task,
151
+ args.files,
152
+ keep_history=args.keep_history)
135
153
  return 0
136
-
154
+
137
155
  # 如果没有选择预定义任务,进入交互模式
138
156
  while True:
139
157
  try:
140
158
  user_input = get_multiline_input("请输入您的任务(输入空行退出):")
141
159
  if not user_input or user_input == "__interrupt__":
142
160
  break
143
- agent.run(user_input, args.files, keep_history=args.keep_history)
161
+ agent.run(
162
+ user_input,
163
+ args.files,
164
+ keep_history=args.keep_history)
144
165
  except Exception as e:
145
166
  PrettyOutput.print(f"错误: {str(e)}", OutputType.ERROR)
146
167
 
@@ -150,5 +171,6 @@ def main():
150
171
 
151
172
  return 0
152
173
 
174
+
153
175
  if __name__ == "__main__":
154
- exit(main())
176
+ exit(main())
jarvis/models/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from .base import BasePlatform
2
2
 
3
- __all__ = ['BasePlatform']
3
+ __all__ = ['BasePlatform']