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 +1 -1
- jarvis/agent.py +32 -47
- jarvis/main.py +35 -51
- jarvis/models/__init__.py +1 -1
- jarvis/models/ai8.py +58 -88
- jarvis/models/base.py +6 -6
- jarvis/models/kimi.py +80 -171
- jarvis/models/openai.py +23 -43
- jarvis/models/oyi.py +91 -113
- jarvis/models/registry.py +44 -63
- jarvis/tools/__init__.py +1 -1
- jarvis/tools/base.py +2 -2
- jarvis/tools/file_ops.py +15 -19
- jarvis/tools/generator.py +12 -15
- jarvis/tools/methodology.py +20 -20
- jarvis/tools/registry.py +30 -44
- jarvis/tools/shell.py +11 -12
- jarvis/tools/sub_agent.py +2 -1
- jarvis/utils.py +27 -47
- {jarvis_ai_assistant-0.1.46.dist-info → jarvis_ai_assistant-0.1.48.dist-info}/METADATA +1 -1
- jarvis_ai_assistant-0.1.48.dist-info/RECORD +25 -0
- jarvis_ai_assistant-0.1.46.dist-info/RECORD +0 -25
- {jarvis_ai_assistant-0.1.46.dist-info → jarvis_ai_assistant-0.1.48.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.46.dist-info → jarvis_ai_assistant-0.1.48.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.46.dist-info → jarvis_ai_assistant-0.1.48.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.46.dist-info → jarvis_ai_assistant-0.1.48.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
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
|
-
|
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
|
-
|
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
|
-
|
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