jarvis-ai-assistant 0.1.58__py3-none-any.whl → 0.1.59__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.
Potentially problematic release.
This version of jarvis-ai-assistant might be problematic. Click here for more details.
- jarvis/__init__.py +1 -1
- jarvis/agent.py +98 -50
- jarvis/jarvis_coder/main.py +127 -2
- jarvis/models/base.py +1 -5
- jarvis/models/kimi.py +2 -0
- jarvis/models/registry.py +3 -3
- jarvis/tools/coder.py +66 -0
- jarvis/tools/search.py +33 -1
- {jarvis_ai_assistant-0.1.58.dist-info → jarvis_ai_assistant-0.1.59.dist-info}/METADATA +56 -36
- {jarvis_ai_assistant-0.1.58.dist-info → jarvis_ai_assistant-0.1.59.dist-info}/RECORD +14 -14
- jarvis/tools/bing_search.py +0 -38
- {jarvis_ai_assistant-0.1.58.dist-info → jarvis_ai_assistant-0.1.59.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.58.dist-info → jarvis_ai_assistant-0.1.59.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.58.dist-info → jarvis_ai_assistant-0.1.59.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.58.dist-info → jarvis_ai_assistant-0.1.59.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
jarvis/agent.py
CHANGED
|
@@ -25,6 +25,7 @@ class Agent:
|
|
|
25
25
|
self.name = name
|
|
26
26
|
self.is_sub_agent = is_sub_agent
|
|
27
27
|
self.prompt = ""
|
|
28
|
+
self.conversation_turns = 0 # 添加对话轮次计数器
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
@staticmethod
|
|
@@ -99,6 +100,75 @@ class Agent:
|
|
|
99
100
|
PrettyOutput.print(f"从 {user_jarvis_methodology} 加载经验总结: {', '.join(data.keys())}", OutputType.INFO)
|
|
100
101
|
return ret
|
|
101
102
|
|
|
103
|
+
def _summarize_and_clear_history(self) -> None:
|
|
104
|
+
"""总结当前对话历史并清空历史记录,只保留系统消息和总结
|
|
105
|
+
|
|
106
|
+
这个方法会:
|
|
107
|
+
1. 请求模型总结当前对话的关键信息
|
|
108
|
+
2. 清空对话历史
|
|
109
|
+
3. 保留系统消息
|
|
110
|
+
4. 添加总结作为新的上下文
|
|
111
|
+
5. 重置对话轮数
|
|
112
|
+
"""
|
|
113
|
+
# 创建一个新的模型实例来做总结,避免影响主对话
|
|
114
|
+
|
|
115
|
+
PrettyOutput.print("总结对话历史,准备生成总结,开始新的对话...", OutputType.PLANNING)
|
|
116
|
+
|
|
117
|
+
prompt = """请总结之前对话中的关键信息,包括:
|
|
118
|
+
1. 当前任务目标
|
|
119
|
+
2. 已经确认的关键信息
|
|
120
|
+
3. 已经尝试过的方案
|
|
121
|
+
4. 当前进展
|
|
122
|
+
5. 待解决的问题
|
|
123
|
+
|
|
124
|
+
请用简洁的要点形式描述,突出重要信息。不要包含对话细节。
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
summary = self.model.chat(prompt)
|
|
129
|
+
|
|
130
|
+
# 清空当前对话历史,但保留系统消息
|
|
131
|
+
self.model.delete_chat()
|
|
132
|
+
|
|
133
|
+
# 添加总结作为新的上下文
|
|
134
|
+
self.prompt = f"""以下是之前对话的关键信息总结:
|
|
135
|
+
|
|
136
|
+
{summary}
|
|
137
|
+
|
|
138
|
+
请基于以上信息继续完成任务。
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
# 重置对话轮数
|
|
142
|
+
self.conversation_turns = 0
|
|
143
|
+
|
|
144
|
+
except Exception as e:
|
|
145
|
+
PrettyOutput.print(f"总结对话历史失败: {str(e)}", OutputType.ERROR)
|
|
146
|
+
|
|
147
|
+
def _complete_task(self) -> str:
|
|
148
|
+
"""完成任务并生成总结
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
str: 任务总结或完成状态
|
|
152
|
+
"""
|
|
153
|
+
PrettyOutput.section("任务完成", OutputType.SUCCESS)
|
|
154
|
+
|
|
155
|
+
if not self.is_sub_agent:
|
|
156
|
+
return "Task completed"
|
|
157
|
+
|
|
158
|
+
# 生成任务总结
|
|
159
|
+
summary_prompt = f"""请对以上任务执行情况生成一个简洁的总结报告,包括:
|
|
160
|
+
|
|
161
|
+
1. 任务目标: 任务重述
|
|
162
|
+
2. 执行结果: 成功/失败
|
|
163
|
+
3. 关键信息: 提取执行过程中的重要信息
|
|
164
|
+
4. 重要发现: 任何值得注意的发现
|
|
165
|
+
5. 后续建议: 如果有的话
|
|
166
|
+
|
|
167
|
+
请用简洁的要点形式描述,突出重要信息。
|
|
168
|
+
"""
|
|
169
|
+
self.prompt = summary_prompt
|
|
170
|
+
return self._call_model(self.prompt)
|
|
171
|
+
|
|
102
172
|
def run(self, user_input: str, file_list: Optional[List[str]] = None, keep_history: bool = False) -> str:
|
|
103
173
|
"""处理用户输入并返回响应,返回任务总结报告
|
|
104
174
|
|
|
@@ -112,6 +182,7 @@ class Agent:
|
|
|
112
182
|
"""
|
|
113
183
|
try:
|
|
114
184
|
self.clear_history()
|
|
185
|
+
self.conversation_turns = 0 # 重置对话轮次
|
|
115
186
|
|
|
116
187
|
if file_list:
|
|
117
188
|
self.model.upload_files(file_list)
|
|
@@ -146,8 +217,8 @@ class Agent:
|
|
|
146
217
|
6. 制定行动计划:根据目前可以使用的工具制定行动计划
|
|
147
218
|
7. 执行行动计划:每步执行一个步骤,最多使用一个工具(工具执行完成后,等待工具结果再执行下一步)
|
|
148
219
|
8. 监控与调整:如果执行结果与预期不符,则反思并调整行动计划,迭代之前的步骤
|
|
149
|
-
9.
|
|
150
|
-
|
|
220
|
+
9. 经验总结:如果当前任务具有通用性且执行过程中遇到了值得记录的经验,使用经验总结工具记录经验总结,以提升后期处理类似问题的能力
|
|
221
|
+
10. 任务结束:如果任务已经完成,使用任务结束指令结束任务
|
|
151
222
|
-------------------------------------------------------------
|
|
152
223
|
|
|
153
224
|
经验总结模板:
|
|
@@ -184,6 +255,12 @@ arguments:
|
|
|
184
255
|
|
|
185
256
|
-------------------------------------------------------------
|
|
186
257
|
|
|
258
|
+
特殊指令:
|
|
259
|
+
1. !<<SUMMARIZE>>! - 当你发现对话历史过长可能导致token超限时,可以使用此指令总结当前对话要点并清空历史。使用方法:直接回复"!<<SUMMARIZE>>!"即可。
|
|
260
|
+
2. !<<FINISHED>>! - 当你确认任务已经完成时,使用此指令结束任务。使用方法:在回复中包含"!<<FINISHED>>!"即可。
|
|
261
|
+
|
|
262
|
+
-------------------------------------------------------------
|
|
263
|
+
|
|
187
264
|
{methodology_prompt}
|
|
188
265
|
|
|
189
266
|
-------------------------------------------------------------
|
|
@@ -196,8 +273,20 @@ arguments:
|
|
|
196
273
|
# 显示思考状态
|
|
197
274
|
PrettyOutput.print("分析任务...", OutputType.PROGRESS)
|
|
198
275
|
|
|
276
|
+
# 增加对话轮次
|
|
277
|
+
self.conversation_turns += 1
|
|
278
|
+
|
|
279
|
+
# 如果对话超过10轮,在提示中添加提醒
|
|
280
|
+
if self.conversation_turns > 10:
|
|
281
|
+
self.prompt = f"{self.prompt}\n(提示:当前对话已超过10轮,建议使用 !<<SUMMARIZE>>! 指令总结对话历史,避免token超限)"
|
|
282
|
+
|
|
199
283
|
current_response = self._call_model(self.prompt)
|
|
200
284
|
|
|
285
|
+
# 检查是否需要总结对话历史
|
|
286
|
+
if "!<<SUMMARIZE>>!" in current_response:
|
|
287
|
+
self._summarize_and_clear_history()
|
|
288
|
+
continue
|
|
289
|
+
|
|
201
290
|
try:
|
|
202
291
|
result = Agent.extract_tool_calls(current_response)
|
|
203
292
|
except Exception as e:
|
|
@@ -206,7 +295,6 @@ arguments:
|
|
|
206
295
|
continue
|
|
207
296
|
|
|
208
297
|
if len(result) > 0:
|
|
209
|
-
|
|
210
298
|
PrettyOutput.print("执行工具调用...", OutputType.PROGRESS)
|
|
211
299
|
tool_result = self.tool_registry.handle_tool_calls(result)
|
|
212
300
|
PrettyOutput.print(tool_result, OutputType.RESULT)
|
|
@@ -214,6 +302,10 @@ arguments:
|
|
|
214
302
|
self.prompt = tool_result
|
|
215
303
|
continue
|
|
216
304
|
|
|
305
|
+
# 检查是否完成任务
|
|
306
|
+
if "!<<FINISHED>>!" in current_response:
|
|
307
|
+
return self._complete_task()
|
|
308
|
+
|
|
217
309
|
# 获取用户输入
|
|
218
310
|
user_input = get_multiline_input(f"{self.name}: 您可以继续输入,或输入空行结束当前任务")
|
|
219
311
|
if user_input == "__interrupt__":
|
|
@@ -225,34 +317,7 @@ arguments:
|
|
|
225
317
|
continue
|
|
226
318
|
|
|
227
319
|
if not user_input:
|
|
228
|
-
|
|
229
|
-
choice = prompt("是否需要手动为此任务生成经验总结以提升Jarvis对类似任务的处理能力?(y/n), 回车跳过: ")
|
|
230
|
-
if choice == "y":
|
|
231
|
-
self._make_methodology()
|
|
232
|
-
break
|
|
233
|
-
elif choice == "n" or choice == "":
|
|
234
|
-
break
|
|
235
|
-
else:
|
|
236
|
-
PrettyOutput.print("请输入y或n", OutputType.ERROR)
|
|
237
|
-
continue
|
|
238
|
-
PrettyOutput.section("任务完成", OutputType.SUCCESS)
|
|
239
|
-
if self.is_sub_agent:
|
|
240
|
-
# 生成任务总结
|
|
241
|
-
summary_prompt = f"""请对以上任务执行情况生成一个简洁的总结报告,包括:
|
|
242
|
-
|
|
243
|
-
1. 任务目标: 任务重述
|
|
244
|
-
2. 执行结果: 成功/失败
|
|
245
|
-
3. 关键信息: 提取执行过程中的重要信息
|
|
246
|
-
4. 重要发现: 任何值得注意的发现
|
|
247
|
-
5. 后续建议: 如果有的话
|
|
248
|
-
|
|
249
|
-
请用简洁的要点形式描述,突出重要信息。
|
|
250
|
-
"""
|
|
251
|
-
self.prompt = summary_prompt
|
|
252
|
-
summary = self._call_model(self.prompt)
|
|
253
|
-
return summary
|
|
254
|
-
else:
|
|
255
|
-
return "Task completed"
|
|
320
|
+
return self._complete_task()
|
|
256
321
|
|
|
257
322
|
except Exception as e:
|
|
258
323
|
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
@@ -274,23 +339,6 @@ arguments:
|
|
|
274
339
|
"""清除对话历史,只保留系统提示"""
|
|
275
340
|
self.prompt = ""
|
|
276
341
|
self.model.reset()
|
|
342
|
+
self.conversation_turns = 0 # 重置对话轮次
|
|
343
|
+
|
|
277
344
|
|
|
278
|
-
def _make_methodology(self):
|
|
279
|
-
"""生成经验总结"""
|
|
280
|
-
current_response = self._call_model("""请根据之前的对话内容,判断是否有必要更新、添加、删除现有经验总结,如果有,使用methodology工具进行管理。
|
|
281
|
-
经验总结模板:
|
|
282
|
-
1. 问题重述
|
|
283
|
-
2. 最优解决方案
|
|
284
|
-
3. 最优方案执行步骤(失败的行动不需要体现)
|
|
285
|
-
""")
|
|
286
|
-
|
|
287
|
-
try:
|
|
288
|
-
result = Agent.extract_tool_calls(current_response)
|
|
289
|
-
except Exception as e:
|
|
290
|
-
PrettyOutput.print(f"工具调用错误: {str(e)}", OutputType.ERROR)
|
|
291
|
-
return
|
|
292
|
-
if len(result) > 0:
|
|
293
|
-
PrettyOutput.print("执行工具调用...", OutputType.PROGRESS)
|
|
294
|
-
tool_result = self.tool_registry.handle_tool_calls(result)
|
|
295
|
-
PrettyOutput.print(tool_result, OutputType.RESULT)
|
|
296
|
-
|
jarvis/jarvis_coder/main.py
CHANGED
|
@@ -11,6 +11,11 @@ import yaml
|
|
|
11
11
|
from jarvis.models.base import BasePlatform
|
|
12
12
|
from jarvis.utils import OutputType, PrettyOutput, get_multiline_input, load_env_from_file
|
|
13
13
|
from jarvis.models.registry import PlatformRegistry
|
|
14
|
+
from prompt_toolkit import PromptSession
|
|
15
|
+
from prompt_toolkit.completion import WordCompleter, Completer, Completion
|
|
16
|
+
from prompt_toolkit.formatted_text import FormattedText
|
|
17
|
+
from prompt_toolkit.styles import Style
|
|
18
|
+
import fnmatch
|
|
14
19
|
|
|
15
20
|
# 全局锁对象
|
|
16
21
|
index_lock = threading.Lock()
|
|
@@ -493,6 +498,7 @@ file2.py: 7
|
|
|
493
498
|
注意事项:
|
|
494
499
|
1、仅输出补丁内容,不要输出任何其他内容,每个补丁必须用<PATCH_START>和<PATCH_END>标记
|
|
495
500
|
2、如果在大段代码中有零星修改,生成多个补丁
|
|
501
|
+
3、要替换的内容,一定要与文件内容完全一致,不要有任何多余或者缺失的内容
|
|
496
502
|
"""
|
|
497
503
|
|
|
498
504
|
success, response = self._call_model_with_retry(self.main_model, prompt)
|
|
@@ -758,8 +764,8 @@ def main():
|
|
|
758
764
|
# 循环处理需求
|
|
759
765
|
while True:
|
|
760
766
|
try:
|
|
761
|
-
#
|
|
762
|
-
feature = get_multiline_input("请输入开发需求 (输入空行退出):")
|
|
767
|
+
# 获取需求,传入项目根目录
|
|
768
|
+
feature = get_multiline_input("请输入开发需求 (输入空行退出):", tool.root_dir)
|
|
763
769
|
|
|
764
770
|
if not feature or feature == "__interrupt__":
|
|
765
771
|
break
|
|
@@ -787,3 +793,122 @@ def main():
|
|
|
787
793
|
|
|
788
794
|
if __name__ == "__main__":
|
|
789
795
|
exit(main())
|
|
796
|
+
|
|
797
|
+
class FilePathCompleter(Completer):
|
|
798
|
+
"""文件路径自动完成器"""
|
|
799
|
+
|
|
800
|
+
def __init__(self, root_dir: str):
|
|
801
|
+
self.root_dir = root_dir
|
|
802
|
+
self._file_list = None
|
|
803
|
+
|
|
804
|
+
def _get_files(self) -> List[str]:
|
|
805
|
+
"""获取git管理的文件列表"""
|
|
806
|
+
if self._file_list is None:
|
|
807
|
+
try:
|
|
808
|
+
# 切换到项目根目录
|
|
809
|
+
old_cwd = os.getcwd()
|
|
810
|
+
os.chdir(self.root_dir)
|
|
811
|
+
|
|
812
|
+
# 获取git管理的文件列表
|
|
813
|
+
self._file_list = os.popen("git ls-files").read().splitlines()
|
|
814
|
+
|
|
815
|
+
# 恢复工作目录
|
|
816
|
+
os.chdir(old_cwd)
|
|
817
|
+
except Exception as e:
|
|
818
|
+
PrettyOutput.print(f"获取文件列表失败: {str(e)}", OutputType.WARNING)
|
|
819
|
+
self._file_list = []
|
|
820
|
+
return self._file_list
|
|
821
|
+
|
|
822
|
+
def get_completions(self, document, complete_event):
|
|
823
|
+
"""获取补全建议"""
|
|
824
|
+
text_before_cursor = document.text_before_cursor
|
|
825
|
+
|
|
826
|
+
# 检查是否刚输入了@
|
|
827
|
+
if text_before_cursor.endswith('@'):
|
|
828
|
+
# 显示所有文件
|
|
829
|
+
for path in self._get_files():
|
|
830
|
+
yield Completion(path, start_position=0)
|
|
831
|
+
return
|
|
832
|
+
|
|
833
|
+
# 检查之前是否有@,并获取@后的搜索词
|
|
834
|
+
at_pos = text_before_cursor.rfind('@')
|
|
835
|
+
if at_pos == -1:
|
|
836
|
+
return
|
|
837
|
+
|
|
838
|
+
search = text_before_cursor[at_pos + 1:].lower().strip()
|
|
839
|
+
|
|
840
|
+
# 提供匹配的文件建议
|
|
841
|
+
for path in self._get_files():
|
|
842
|
+
path_lower = path.lower()
|
|
843
|
+
if (search in path_lower or # 直接包含
|
|
844
|
+
search in os.path.basename(path_lower) or # 文件名包含
|
|
845
|
+
any(fnmatch.fnmatch(path_lower, f'*{s}*') for s in search.split())): # 通配符匹配
|
|
846
|
+
# 计算正确的start_position
|
|
847
|
+
yield Completion(path, start_position=-(len(search)))
|
|
848
|
+
|
|
849
|
+
class SmartCompleter(Completer):
|
|
850
|
+
"""智能自动完成器,组合词语和文件路径补全"""
|
|
851
|
+
|
|
852
|
+
def __init__(self, word_completer: WordCompleter, file_completer: FilePathCompleter):
|
|
853
|
+
self.word_completer = word_completer
|
|
854
|
+
self.file_completer = file_completer
|
|
855
|
+
|
|
856
|
+
def get_completions(self, document, complete_event):
|
|
857
|
+
"""获取补全建议"""
|
|
858
|
+
# 如果当前行以@结尾,使用文件补全
|
|
859
|
+
if document.text_before_cursor.strip().endswith('@'):
|
|
860
|
+
yield from self.file_completer.get_completions(document, complete_event)
|
|
861
|
+
else:
|
|
862
|
+
# 否则使用词语补全
|
|
863
|
+
yield from self.word_completer.get_completions(document, complete_event)
|
|
864
|
+
|
|
865
|
+
def get_multiline_input(prompt_text: str, root_dir: str = None) -> str:
|
|
866
|
+
"""获取多行输入,支持文件路径自动完成功能
|
|
867
|
+
|
|
868
|
+
Args:
|
|
869
|
+
prompt_text: 提示文本
|
|
870
|
+
root_dir: 项目根目录,用于文件补全
|
|
871
|
+
|
|
872
|
+
Returns:
|
|
873
|
+
str: 用户输入的文本
|
|
874
|
+
"""
|
|
875
|
+
# 创建文件补全器
|
|
876
|
+
file_completer = FilePathCompleter(root_dir or os.getcwd())
|
|
877
|
+
|
|
878
|
+
# 创建提示样式
|
|
879
|
+
style = Style.from_dict({
|
|
880
|
+
'prompt': 'ansicyan bold',
|
|
881
|
+
'input': 'ansiwhite',
|
|
882
|
+
})
|
|
883
|
+
|
|
884
|
+
# 创建会话
|
|
885
|
+
session = PromptSession(
|
|
886
|
+
completer=file_completer,
|
|
887
|
+
style=style,
|
|
888
|
+
multiline=False,
|
|
889
|
+
enable_history_search=True,
|
|
890
|
+
complete_while_typing=True
|
|
891
|
+
)
|
|
892
|
+
|
|
893
|
+
# 显示初始提示文本
|
|
894
|
+
print(f"\n{prompt_text}")
|
|
895
|
+
|
|
896
|
+
# 创建提示符
|
|
897
|
+
prompt = FormattedText([
|
|
898
|
+
('class:prompt', ">>> ")
|
|
899
|
+
])
|
|
900
|
+
|
|
901
|
+
# 获取输入
|
|
902
|
+
lines = []
|
|
903
|
+
try:
|
|
904
|
+
while True:
|
|
905
|
+
line = session.prompt(prompt).strip()
|
|
906
|
+
if not line: # 空行表示输入结束
|
|
907
|
+
break
|
|
908
|
+
lines.append(line)
|
|
909
|
+
except KeyboardInterrupt:
|
|
910
|
+
return "__interrupt__"
|
|
911
|
+
except EOFError:
|
|
912
|
+
pass
|
|
913
|
+
|
|
914
|
+
return "\n".join(lines)
|
jarvis/models/base.py
CHANGED
jarvis/models/kimi.py
CHANGED
jarvis/models/registry.py
CHANGED
|
@@ -81,7 +81,7 @@ class PlatformRegistry:
|
|
|
81
81
|
directory: 平台目录路径
|
|
82
82
|
|
|
83
83
|
Returns:
|
|
84
|
-
Dict[str, Type[
|
|
84
|
+
Dict[str, Type[BasePlatform]]: 平台名称到平台类的映射
|
|
85
85
|
"""
|
|
86
86
|
platforms = {}
|
|
87
87
|
|
|
@@ -112,7 +112,7 @@ class PlatformRegistry:
|
|
|
112
112
|
|
|
113
113
|
# 遍历模块中的所有类
|
|
114
114
|
for name, obj in inspect.getmembers(module):
|
|
115
|
-
# 检查是否是
|
|
115
|
+
# 检查是否是BasePlatform的子类,但不是BasePlatform本身
|
|
116
116
|
if (inspect.isclass(obj) and
|
|
117
117
|
issubclass(obj, BasePlatform) and
|
|
118
118
|
obj != BasePlatform and
|
|
@@ -176,7 +176,7 @@ class PlatformRegistry:
|
|
|
176
176
|
name: 平台名称
|
|
177
177
|
|
|
178
178
|
Returns:
|
|
179
|
-
|
|
179
|
+
BasePlatform: 平台实例
|
|
180
180
|
"""
|
|
181
181
|
if name not in self.platforms:
|
|
182
182
|
PrettyOutput.print(f"未找到平台: {name}", OutputType.ERROR)
|
jarvis/tools/coder.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Dict, Any, Optional
|
|
3
|
+
from jarvis.jarvis_coder.main import JarvisCoder
|
|
4
|
+
from jarvis.utils import PrettyOutput, OutputType
|
|
5
|
+
|
|
6
|
+
class CoderTool:
|
|
7
|
+
"""代码修改工具"""
|
|
8
|
+
|
|
9
|
+
name = "coder"
|
|
10
|
+
description = "用于自动修改和生成代码的工具"
|
|
11
|
+
parameters = {
|
|
12
|
+
"feature": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "要实现的功能描述",
|
|
15
|
+
"required": True
|
|
16
|
+
},
|
|
17
|
+
"dir": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"description": "项目根目录",
|
|
20
|
+
"required": False
|
|
21
|
+
},
|
|
22
|
+
"language": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"description": "编程语言",
|
|
25
|
+
"required": False
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _init_coder(self, dir: Optional[str] = None, language: Optional[str] = "python") -> None:
|
|
31
|
+
"""初始化JarvisCoder实例"""
|
|
32
|
+
if not self._coder:
|
|
33
|
+
import os
|
|
34
|
+
work_dir = dir or os.getcwd()
|
|
35
|
+
self._coder = JarvisCoder(work_dir, language)
|
|
36
|
+
|
|
37
|
+
def execute(self, **kwargs) -> Dict[str, Any]:
|
|
38
|
+
"""执行代码修改
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
feature: 要实现的功能描述
|
|
42
|
+
dir: 可选,项目根目录
|
|
43
|
+
language: 可选,编程语言
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Dict[str, Any]: 执行结果
|
|
47
|
+
"""
|
|
48
|
+
feature = kwargs.get("feature")
|
|
49
|
+
dir = kwargs.get("dir")
|
|
50
|
+
language = kwargs.get("language", "python")
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
self.current_dir = os.getcwd()
|
|
54
|
+
self._init_coder(dir, language)
|
|
55
|
+
result = self._coder.execute(feature)
|
|
56
|
+
return result
|
|
57
|
+
except Exception as e:
|
|
58
|
+
PrettyOutput.print(f"代码修改失败: {str(e)}", OutputType.ERROR)
|
|
59
|
+
return {
|
|
60
|
+
"success": False,
|
|
61
|
+
"stdout": "",
|
|
62
|
+
"stderr": f"执行失败: {str(e)}",
|
|
63
|
+
"error": e
|
|
64
|
+
}
|
|
65
|
+
finally:
|
|
66
|
+
os.chdir(self.current_dir)
|
jarvis/tools/search.py
CHANGED
|
@@ -2,7 +2,39 @@ from typing import Dict, Any, List
|
|
|
2
2
|
from jarvis.models.registry import PlatformRegistry
|
|
3
3
|
from jarvis.utils import PrettyOutput, OutputType
|
|
4
4
|
from jarvis.tools.webpage import WebpageTool
|
|
5
|
-
from
|
|
5
|
+
from playwright.sync_api import sync_playwright
|
|
6
|
+
from urllib.parse import quote
|
|
7
|
+
|
|
8
|
+
def bing_search(query):
|
|
9
|
+
try:
|
|
10
|
+
with sync_playwright() as p:
|
|
11
|
+
browser = p.chromium.launch()
|
|
12
|
+
page = browser.new_page()
|
|
13
|
+
page.goto(
|
|
14
|
+
f"https://www.bing.com/search?form=QBRE&q={quote(query)}&cc=US"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
page.wait_for_selector("#b_results", timeout=10000)
|
|
18
|
+
|
|
19
|
+
summaries = page.evaluate("""() => {
|
|
20
|
+
const liElements = Array.from(
|
|
21
|
+
document.querySelectorAll("#b_results > .b_algo")
|
|
22
|
+
);
|
|
23
|
+
return liElements.map((li) => {
|
|
24
|
+
const abstractElement = li.querySelector(".b_caption > p");
|
|
25
|
+
const linkElement = li.querySelector("a");
|
|
26
|
+
const href = linkElement.getAttribute("href");
|
|
27
|
+
const title = linkElement.textContent;
|
|
28
|
+
const abstract = abstractElement ? abstractElement.textContent : "";
|
|
29
|
+
return { href, title, abstract };
|
|
30
|
+
});
|
|
31
|
+
}""")
|
|
32
|
+
|
|
33
|
+
browser.close()
|
|
34
|
+
print(summaries)
|
|
35
|
+
return summaries
|
|
36
|
+
except Exception as error:
|
|
37
|
+
print("An error occurred:", error)
|
|
6
38
|
|
|
7
39
|
class SearchTool:
|
|
8
40
|
name = "search"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: jarvis-ai-assistant
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.59
|
|
4
4
|
Summary: Jarvis: An AI assistant that uses tools to interact with the system
|
|
5
5
|
Home-page: https://github.com/skyfireitdiy/Jarvis
|
|
6
6
|
Author: skyfire
|
|
@@ -66,6 +66,8 @@ Dynamic: requires-python
|
|
|
66
66
|
|
|
67
67
|
*Your intelligent assistant for development and system interaction*
|
|
68
68
|
|
|
69
|
+
English | [简体中文](README_zh.md)
|
|
70
|
+
|
|
69
71
|
[Features](#features) •
|
|
70
72
|
[Usage](#usage) •
|
|
71
73
|
[Configuration](#configuration) •
|
|
@@ -112,19 +114,23 @@ pip install jarvis-ai-assistant
|
|
|
112
114
|
|
|
113
115
|
## 🔧 Configuration
|
|
114
116
|
|
|
115
|
-
|
|
117
|
+
Jarvis supports configuration through environment variables that can be set in the `~/.jarvis_env` file:
|
|
116
118
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
| Environment Variable | Description | Default Value | Required |
|
|
120
|
+
|---------|------|--------|------|
|
|
121
|
+
| JARVIS_PLATFORM | AI platform to use, supports kimi/openai/ai8 etc | kimi | Yes |
|
|
122
|
+
| JARVIS_MODEL | Model name to use | - | No |
|
|
123
|
+
| JARVIS_CODEGEN_PLATFORM | AI platform for code generation | Same as JARVIS_PLATFORM | No |
|
|
124
|
+
| JARVIS_CODEGEN_MODEL | Model name for code generation | Same as JARVIS_MODEL | No |
|
|
125
|
+
| OPENAI_API_KEY | API key for OpenAI platform | - | Required for OpenAI |
|
|
126
|
+
| OPENAI_API_BASE | Base URL for OpenAI API | https://api.deepseek.com | No |
|
|
127
|
+
| OPENAI_MODEL_NAME | Model name for OpenAI | deepseek-chat | No |
|
|
128
|
+
| AI8_API_KEY | API key for AI8 platform | - | Required for AI8 |
|
|
129
|
+
| AI8_MODEL | Model name for AI8 platform | deepseek-chat | No |
|
|
130
|
+
| KIMI_API_KEY | API key for Kimi platform | - | Required for Kimi |
|
|
131
|
+
| OYI_API_KEY | API key for OYI platform | - | Required for OYI |
|
|
132
|
+
| OYI_MODEL | Model name for OYI platform | deepseek-chat | No |
|
|
121
133
|
|
|
122
|
-
### For OpenAI:
|
|
123
|
-
```bash
|
|
124
|
-
OPENAI_API_KEY=your_api_key_here
|
|
125
|
-
OPENAI_API_BASE=your_api_base # Optional, defaults to https://api.deepseek.com
|
|
126
|
-
OPENAI_MODEL_NAME=your_model_name # Optional, defaults to deepseek-chat
|
|
127
|
-
```
|
|
128
134
|
|
|
129
135
|
## 🎯 Usage
|
|
130
136
|
|
|
@@ -241,55 +247,57 @@ Create a new Python file in `~/.jarvis_models/`:
|
|
|
241
247
|
|
|
242
248
|
```python
|
|
243
249
|
from typing import Dict, List
|
|
244
|
-
from jarvis.models.base import
|
|
250
|
+
from jarvis.models.base import BasePlatform
|
|
245
251
|
from jarvis.utils import PrettyOutput, OutputType
|
|
246
252
|
|
|
247
|
-
class
|
|
253
|
+
class CustomPlatform(BasePlatform):
|
|
248
254
|
"""Custom model implementation"""
|
|
249
255
|
|
|
250
|
-
|
|
256
|
+
platform_name = "custom" # Platform identifier
|
|
251
257
|
|
|
252
258
|
def __init__(self):
|
|
253
259
|
"""Initialize model"""
|
|
254
|
-
#
|
|
260
|
+
# add initialization code
|
|
261
|
+
super().__init__()
|
|
255
262
|
self.messages = []
|
|
256
263
|
self.system_message = ""
|
|
257
|
-
|
|
258
|
-
def
|
|
259
|
-
"""Set
|
|
260
|
-
self.
|
|
261
|
-
|
|
264
|
+
|
|
265
|
+
def set_model_name(self, model_name: str):
|
|
266
|
+
"""Set model name"""
|
|
267
|
+
self.model_name = model_name
|
|
268
|
+
|
|
262
269
|
def chat(self, message: str) -> str:
|
|
263
|
-
"""
|
|
270
|
+
"""Chat with model
|
|
264
271
|
|
|
265
272
|
Args:
|
|
266
|
-
message:
|
|
273
|
+
message: user input message
|
|
267
274
|
|
|
268
275
|
Returns:
|
|
269
|
-
str:
|
|
276
|
+
str: model response
|
|
270
277
|
"""
|
|
271
278
|
try:
|
|
272
|
-
#
|
|
273
|
-
PrettyOutput.print("
|
|
279
|
+
# implement chat logic
|
|
280
|
+
PrettyOutput.print("Sending request...", OutputType.PROGRESS)
|
|
274
281
|
|
|
275
|
-
#
|
|
282
|
+
# add message to history
|
|
276
283
|
self.messages.append({"role": "user", "content": message})
|
|
277
284
|
|
|
278
|
-
#
|
|
279
|
-
response = "
|
|
285
|
+
# get response from model
|
|
286
|
+
response = "model response"
|
|
280
287
|
|
|
281
|
-
#
|
|
288
|
+
# add response to history
|
|
282
289
|
self.messages.append({"role": "assistant", "content": response})
|
|
283
290
|
|
|
284
291
|
return response
|
|
285
292
|
|
|
286
293
|
except Exception as e:
|
|
287
|
-
PrettyOutput.print(f"
|
|
294
|
+
PrettyOutput.print(f"Chat failed: {str(e)}", OutputType.ERROR)
|
|
288
295
|
raise Exception(f"Chat failed: {str(e)}")
|
|
289
|
-
|
|
290
|
-
def
|
|
291
|
-
"""
|
|
292
|
-
|
|
296
|
+
|
|
297
|
+
def upload_files(self, file_list: List[str]) -> List[Dict]:
|
|
298
|
+
"""Upload files"""
|
|
299
|
+
# implement file upload logic
|
|
300
|
+
return []
|
|
293
301
|
|
|
294
302
|
def reset(self):
|
|
295
303
|
"""Reset model state"""
|
|
@@ -297,10 +305,22 @@ class CustomModel(BaseModel):
|
|
|
297
305
|
if self.system_message:
|
|
298
306
|
self.messages.append({"role": "system", "content": self.system_message})
|
|
299
307
|
|
|
308
|
+
def name(self) -> str:
|
|
309
|
+
"""Return model name"""
|
|
310
|
+
return self.model_name
|
|
311
|
+
|
|
300
312
|
def delete_chat(self) -> bool:
|
|
301
313
|
"""Delete current chat session"""
|
|
302
314
|
self.reset()
|
|
303
|
-
return True
|
|
315
|
+
return True
|
|
316
|
+
|
|
317
|
+
def set_system_message(self, message: str):
|
|
318
|
+
"""Set system message"""
|
|
319
|
+
self.system_message = message
|
|
320
|
+
|
|
321
|
+
def set_suppress_output(self, suppress: bool):
|
|
322
|
+
"""Set whether to suppress output"""
|
|
323
|
+
self.suppress_output = suppress
|
|
304
324
|
```
|
|
305
325
|
|
|
306
326
|
### Development Guidelines
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
jarvis/__init__.py,sha256=
|
|
2
|
-
jarvis/agent.py,sha256=
|
|
1
|
+
jarvis/__init__.py,sha256=b8yvcF8JU8Ok7r75ZnTd8pf8eQvluvd5CjMfxkewBAc,50
|
|
2
|
+
jarvis/agent.py,sha256=vSba-jPjCCxYqI2uje6OZK_m-VwmNTOnJSheZy1C5PA,13914
|
|
3
3
|
jarvis/main.py,sha256=gXXtnrkkvGwEswJL6qiYjVrg3bpzye-GJeAe0Nf2B9o,6509
|
|
4
4
|
jarvis/utils.py,sha256=JlkuC9RtspXH2VWDmj9nR0vnb8ie1gIsKc4vC7WRco8,7321
|
|
5
|
-
jarvis/jarvis_coder/main.py,sha256=
|
|
5
|
+
jarvis/jarvis_coder/main.py,sha256=TosDDiaYSjDpzKPNKcygxZ3XXWbcnvBmIIMn3UMBc60,35102
|
|
6
6
|
jarvis/models/__init__.py,sha256=mrOt67nselz_H1gX9wdAO4y2DY5WPXzABqJbr5Des8k,63
|
|
7
7
|
jarvis/models/ai8.py,sha256=aSiUazBrj4Fsit3tQX7jj8nM8MDBhHFnFrbQC6CvcGw,12745
|
|
8
|
-
jarvis/models/base.py,sha256=
|
|
9
|
-
jarvis/models/kimi.py,sha256=
|
|
8
|
+
jarvis/models/base.py,sha256=ShV1H8Unee4RMaiFO4idROQA0Hc6wu4dyeRPX5fcszk,1433
|
|
9
|
+
jarvis/models/kimi.py,sha256=1iTB0Z_WOmCML3Ufsge6jmeKOYvccr7I5lS3JUXymU4,17611
|
|
10
10
|
jarvis/models/openai.py,sha256=FcZVcCK41jP4SB_q4kZ6bga2tYbL-s6zjpS4KU30-tw,4477
|
|
11
11
|
jarvis/models/oyi.py,sha256=vVs1ru5K8WpofjwoYTxUQVvXL4Yc7ZH6nZhKYpEZcdk,15288
|
|
12
|
-
jarvis/models/registry.py,sha256=
|
|
12
|
+
jarvis/models/registry.py,sha256=G4Woj4-GnLiKEPAZObyJN47mOQ2r0bo4xmaQdWZwHrs,7963
|
|
13
13
|
jarvis/tools/__init__.py,sha256=Kj1bKj34lwRDKMKHLOrLyQElf2lHbqA2tDgP359eaDo,71
|
|
14
14
|
jarvis/tools/base.py,sha256=EGRGbdfbLXDLwtyoWdvp9rlxNX7bzc20t0Vc2VkwIEY,652
|
|
15
|
-
jarvis/tools/
|
|
15
|
+
jarvis/tools/coder.py,sha256=FBAk9A8P6WCVpexcTLYw-bqH3z6CkYa9c1EWooLzv5s,1992
|
|
16
16
|
jarvis/tools/file_ops.py,sha256=h8g0eT9UvlJf4kt0DLXvdSsjcPj7x19lxWdDApeDfpg,3842
|
|
17
17
|
jarvis/tools/generator.py,sha256=vVP3eN5cCDpRXf_fn0skETkPXAW1XZFWx9pt2_ahK48,5999
|
|
18
18
|
jarvis/tools/methodology.py,sha256=G3cOaHTMujGZBhDLhQEqyCV2NISizO3MXRuho1KfI6Y,5223
|
|
19
19
|
jarvis/tools/registry.py,sha256=mlOAmUq3yzRz-7yvwrrCwbe5Lmw8eh1v8-_Fa5sezwI,7209
|
|
20
|
-
jarvis/tools/search.py,sha256=
|
|
20
|
+
jarvis/tools/search.py,sha256=1EqOVvLhg2Csh-i03-XeCrusbyfmH69FZ8khwZt8Tow,6131
|
|
21
21
|
jarvis/tools/shell.py,sha256=UPKshPyOaUwTngresUw-ot1jHjQIb4wCY5nkJqa38lU,2520
|
|
22
22
|
jarvis/tools/sub_agent.py,sha256=rEtAmSVY2ZjFOZEKr5m5wpACOQIiM9Zr_3dT92FhXYU,2621
|
|
23
23
|
jarvis/tools/webpage.py,sha256=d3w3Jcjcu1ESciezTkz3n3Zf-rp_l91PrVoDEZnckOo,2391
|
|
24
|
-
jarvis_ai_assistant-0.1.
|
|
25
|
-
jarvis_ai_assistant-0.1.
|
|
26
|
-
jarvis_ai_assistant-0.1.
|
|
27
|
-
jarvis_ai_assistant-0.1.
|
|
28
|
-
jarvis_ai_assistant-0.1.
|
|
29
|
-
jarvis_ai_assistant-0.1.
|
|
24
|
+
jarvis_ai_assistant-0.1.59.dist-info/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
|
|
25
|
+
jarvis_ai_assistant-0.1.59.dist-info/METADATA,sha256=--vje3-tzKtlivC7SlLfAPjA9MqIhkEcbFoKYXz7WFU,11213
|
|
26
|
+
jarvis_ai_assistant-0.1.59.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
27
|
+
jarvis_ai_assistant-0.1.59.dist-info/entry_points.txt,sha256=ieRI4ilnGNx1R6LlzT2P510mJ27lhLesVZToezDjSd8,89
|
|
28
|
+
jarvis_ai_assistant-0.1.59.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
|
|
29
|
+
jarvis_ai_assistant-0.1.59.dist-info/RECORD,,
|
jarvis/tools/bing_search.py
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
from playwright.sync_api import sync_playwright
|
|
2
|
-
from urllib.parse import quote
|
|
3
|
-
|
|
4
|
-
def bing_search(query):
|
|
5
|
-
try:
|
|
6
|
-
with sync_playwright() as p:
|
|
7
|
-
browser = p.chromium.launch()
|
|
8
|
-
page = browser.new_page()
|
|
9
|
-
page.goto(
|
|
10
|
-
f"https://www.bing.com/search?form=QBRE&q={quote(query)}&cc=US"
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
page.wait_for_selector("#b_results", timeout=10000)
|
|
14
|
-
|
|
15
|
-
summaries = page.evaluate("""() => {
|
|
16
|
-
const liElements = Array.from(
|
|
17
|
-
document.querySelectorAll("#b_results > .b_algo")
|
|
18
|
-
);
|
|
19
|
-
return liElements.map((li) => {
|
|
20
|
-
const abstractElement = li.querySelector(".b_caption > p");
|
|
21
|
-
const linkElement = li.querySelector("a");
|
|
22
|
-
const href = linkElement.getAttribute("href");
|
|
23
|
-
const title = linkElement.textContent;
|
|
24
|
-
const abstract = abstractElement ? abstractElement.textContent : "";
|
|
25
|
-
return { href, title, abstract };
|
|
26
|
-
});
|
|
27
|
-
}""")
|
|
28
|
-
|
|
29
|
-
browser.close()
|
|
30
|
-
print(summaries)
|
|
31
|
-
return summaries
|
|
32
|
-
except Exception as error:
|
|
33
|
-
print("An error occurred:", error)
|
|
34
|
-
|
|
35
|
-
if __name__ == "__main__":
|
|
36
|
-
# results = bing_search("北京到西雅图的距离")
|
|
37
|
-
results = bing_search("北京到西雅图的距离")
|
|
38
|
-
print(results)
|
|
File without changes
|
|
File without changes
|
{jarvis_ai_assistant-0.1.58.dist-info → jarvis_ai_assistant-0.1.59.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|