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 +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