jarvis-ai-assistant 0.1.46__py3-none-any.whl → 0.1.48__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.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']