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