jarvis-ai-assistant 0.1.58__py3-none-any.whl → 0.1.60__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 CHANGED
@@ -1,3 +1,3 @@
1
1
  """Jarvis AI Assistant"""
2
2
 
3
- __version__ = "0.1.58"
3
+ __version__ = "0.1.60"
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
- while True:
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
-
@@ -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/ai8.py CHANGED
@@ -72,7 +72,7 @@ class AI8Model(BasePlatform):
72
72
 
73
73
  def set_model_name(self, model_name: str):
74
74
  """设置模型名称"""
75
- PrettyOutput.print(f"设置模型: {model_name}", OutputType.USER)
75
+
76
76
  self.model_name = model_name
77
77
 
78
78
  def create_conversation(self) -> bool:
jarvis/models/base.py CHANGED
@@ -42,9 +42,5 @@ class BasePlatform(ABC):
42
42
  raise NotImplementedError("set_system_message is not implemented")
43
43
 
44
44
  def set_suppress_output(self, suppress: bool):
45
- """设置是否屏蔽输出
46
-
47
- Args:
48
- suppress: 是否屏蔽输出
49
- """
45
+ """设置是否屏蔽输出"""
50
46
  self.suppress_output = suppress
jarvis/models/kimi.py CHANGED
@@ -360,6 +360,8 @@ class KimiModel(BasePlatform):
360
360
  PrettyOutput.print(f" 原文: {text}", OutputType.PROGRESS)
361
361
 
362
362
  PrettyOutput.print("", OutputType.PROGRESS)
363
+
364
+ PrettyOutput.print(full_response, OutputType.RESULT)
363
365
 
364
366
  return full_response
365
367
 
jarvis/models/openai.py CHANGED
@@ -44,7 +44,7 @@ class OpenAIModel(BasePlatform):
44
44
 
45
45
  def set_model_name(self, model_name: str):
46
46
  """设置模型名称"""
47
- PrettyOutput.print(f"设置模型: {model_name}", OutputType.USER)
47
+
48
48
  self.model_name = model_name
49
49
 
50
50
  def set_system_message(self, message: str):
jarvis/models/oyi.py CHANGED
@@ -45,7 +45,7 @@ class OyiModel(BasePlatform):
45
45
 
46
46
  def set_model_name(self, model_name: str):
47
47
  """设置模型名称"""
48
- PrettyOutput.print(f"设置模型: {model_name}", OutputType.USER)
48
+
49
49
  self.model_name = model_name
50
50
 
51
51
 
jarvis/models/registry.py CHANGED
@@ -29,7 +29,8 @@ class PlatformRegistry:
29
29
  # 创建 __init__.py 使其成为 Python 包
30
30
  with open(os.path.join(user_platform_dir, "__init__.py"), "w") as f:
31
31
  pass
32
- PrettyOutput.print(f"已创建平台目录: {user_platform_dir}", OutputType.INFO)
32
+
33
+ pass
33
34
  except Exception as e:
34
35
  PrettyOutput.print(f"创建平台目录失败: {str(e)}", OutputType.ERROR)
35
36
  return ""
@@ -81,7 +82,7 @@ class PlatformRegistry:
81
82
  directory: 平台目录路径
82
83
 
83
84
  Returns:
84
- Dict[str, Type[BaseModel]]: 平台名称到平台类的映射
85
+ Dict[str, Type[BasePlatform]]: 平台名称到平台类的映射
85
86
  """
86
87
  platforms = {}
87
88
 
@@ -112,7 +113,7 @@ class PlatformRegistry:
112
113
 
113
114
  # 遍历模块中的所有类
114
115
  for name, obj in inspect.getmembers(module):
115
- # 检查是否是BaseModel的子类,但不是BaseModel本身
116
+ # 检查是否是BasePlatform的子类,但不是BasePlatform本身
116
117
  if (inspect.isclass(obj) and
117
118
  issubclass(obj, BasePlatform) and
118
119
  obj != BasePlatform and
@@ -120,8 +121,8 @@ class PlatformRegistry:
120
121
  # 检查平台实现
121
122
  if not PlatformRegistry.check_platform_implementation(obj):
122
123
  continue
124
+
123
125
  platforms[obj.platform_name] = obj
124
- PrettyOutput.print(f"从 {directory} 加载平台: {obj.platform_name}", OutputType.INFO)
125
126
  break
126
127
  except Exception as e:
127
128
  PrettyOutput.print(f"加载平台 {module_name} 失败: {str(e)}", OutputType.ERROR)
@@ -159,6 +160,7 @@ class PlatformRegistry:
159
160
  raise Exception(f"Failed to create platform: {PlatformRegistry.global_platform_name}")
160
161
  return platform
161
162
 
163
+
162
164
  def register_platform(self, name: str, platform_class: Type[BasePlatform]):
163
165
  """注册平台类
164
166
 
@@ -167,7 +169,6 @@ class PlatformRegistry:
167
169
  model_class: 平台类
168
170
  """
169
171
  self.platforms[name] = platform_class
170
- PrettyOutput.print(f"已注册平台: {name}", OutputType.INFO)
171
172
 
172
173
  def create_platform(self, name: str) -> Optional[BasePlatform]:
173
174
  """创建平台实例
@@ -176,15 +177,15 @@ class PlatformRegistry:
176
177
  name: 平台名称
177
178
 
178
179
  Returns:
179
- BaseModel: 平台实例
180
+ BasePlatform: 平台实例
180
181
  """
181
182
  if name not in self.platforms:
182
183
  PrettyOutput.print(f"未找到平台: {name}", OutputType.ERROR)
183
184
  return None
184
185
 
185
186
  try:
187
+
186
188
  platform = self.platforms[name]()
187
- PrettyOutput.print(f"已创建平台实例: {name}", OutputType.INFO)
188
189
  return platform
189
190
  except Exception as e:
190
191
  PrettyOutput.print(f"创建平台失败: {str(e)}", OutputType.ERROR)
jarvis/tools/coder.py ADDED
@@ -0,0 +1,69 @@
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
+ def __init__(self):
30
+ self._coder = None
31
+
32
+
33
+ def _init_coder(self, dir: Optional[str] = None, language: Optional[str] = "python") -> None:
34
+ """初始化JarvisCoder实例"""
35
+ if not self._coder:
36
+ import os
37
+ work_dir = dir or os.getcwd()
38
+ self._coder = JarvisCoder(work_dir, language)
39
+
40
+ def execute(self, args: Dict) -> Dict[str, Any]:
41
+ """执行代码修改
42
+
43
+ Args:
44
+ feature: 要实现的功能描述
45
+ dir: 可选,项目根目录
46
+ language: 可选,编程语言
47
+
48
+ Returns:
49
+ Dict[str, Any]: 执行结果
50
+ """
51
+ feature = args.get("feature")
52
+ dir = args.get("dir")
53
+ language = args.get("language", "python")
54
+
55
+ try:
56
+ self.current_dir = os.getcwd()
57
+ self._init_coder(dir, language)
58
+ result = self._coder.execute(feature)
59
+ return result
60
+ except Exception as e:
61
+ PrettyOutput.print(f"代码修改失败: {str(e)}", OutputType.ERROR)
62
+ return {
63
+ "success": False,
64
+ "stdout": "",
65
+ "stderr": f"执行失败: {str(e)}",
66
+ "error": e
67
+ }
68
+ finally:
69
+ 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 jarvis.tools.bing_search import bing_search
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.58
3
+ Version: 0.1.60
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
- Create a `.jarvis_env` file in your home directory with your API keys:
117
+ Jarvis supports configuration through environment variables that can be set in the `~/.jarvis_env` file:
116
118
 
117
- ### For Kimi:
118
- ```bash
119
- KIMI_API_KEY=your_kimi_api_key_here
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 BaseModel
250
+ from jarvis.models.base import BasePlatform
245
251
  from jarvis.utils import PrettyOutput, OutputType
246
252
 
247
- class CustomModel(BaseModel):
253
+ class CustomPlatform(BasePlatform):
248
254
  """Custom model implementation"""
249
255
 
250
- model_name = "custom" # Model identifier
256
+ platform_name = "custom" # Platform identifier
251
257
 
252
258
  def __init__(self):
253
259
  """Initialize model"""
254
- # Add your initialization code here
260
+ # add initialization code
261
+ super().__init__()
255
262
  self.messages = []
256
263
  self.system_message = ""
257
-
258
- def set_system_message(self, message: str):
259
- """Set system message"""
260
- self.system_message = message
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
- """Execute chat with the model
270
+ """Chat with model
264
271
 
265
272
  Args:
266
- message: User input message
273
+ message: user input message
267
274
 
268
275
  Returns:
269
- str: Model response
276
+ str: model response
270
277
  """
271
278
  try:
272
- # Implement chat logic here
273
- PrettyOutput.print("发送请求...", OutputType.PROGRESS)
279
+ # implement chat logic
280
+ PrettyOutput.print("Sending request...", OutputType.PROGRESS)
274
281
 
275
- # Add message to history
282
+ # add message to history
276
283
  self.messages.append({"role": "user", "content": message})
277
284
 
278
- # Get response from your model
279
- response = "Model response"
285
+ # get response from model
286
+ response = "model response"
280
287
 
281
- # Add response to history
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"对话失败: {str(e)}", OutputType.ERROR)
294
+ PrettyOutput.print(f"Chat failed: {str(e)}", OutputType.ERROR)
288
295
  raise Exception(f"Chat failed: {str(e)}")
289
-
290
- def name(self) -> str:
291
- """Return model name"""
292
- return self.model_name
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
@@ -0,0 +1,29 @@
1
+ jarvis/__init__.py,sha256=cCtC3WSQC0fShm7fHAZRV89NzbaWtqD8EDoDEoGVJVk,50
2
+ jarvis/agent.py,sha256=vSba-jPjCCxYqI2uje6OZK_m-VwmNTOnJSheZy1C5PA,13914
3
+ jarvis/main.py,sha256=gXXtnrkkvGwEswJL6qiYjVrg3bpzye-GJeAe0Nf2B9o,6509
4
+ jarvis/utils.py,sha256=JlkuC9RtspXH2VWDmj9nR0vnb8ie1gIsKc4vC7WRco8,7321
5
+ jarvis/jarvis_coder/main.py,sha256=TosDDiaYSjDpzKPNKcygxZ3XXWbcnvBmIIMn3UMBc60,35102
6
+ jarvis/models/__init__.py,sha256=mrOt67nselz_H1gX9wdAO4y2DY5WPXzABqJbr5Des8k,63
7
+ jarvis/models/ai8.py,sha256=AMBZRS9hKW1Ts_YoHMMelhT0CRMeMzHtVH31z0FpP-Q,12671
8
+ jarvis/models/base.py,sha256=ShV1H8Unee4RMaiFO4idROQA0Hc6wu4dyeRPX5fcszk,1433
9
+ jarvis/models/kimi.py,sha256=1iTB0Z_WOmCML3Ufsge6jmeKOYvccr7I5lS3JUXymU4,17611
10
+ jarvis/models/openai.py,sha256=7oOxrL6EM7iaqJgZsaFvVmyzY0ars4BP3EKAUlX1RPw,4403
11
+ jarvis/models/oyi.py,sha256=mW-uhUZbts2L_oI8WueZUTEmrLY1CiBHn4UV2HP7ZCE,15214
12
+ jarvis/models/registry.py,sha256=hJyaROiOF_TkbtIXsjOD8-ArOvAvtxviawyqBFfLV6s,7617
13
+ jarvis/tools/__init__.py,sha256=Kj1bKj34lwRDKMKHLOrLyQElf2lHbqA2tDgP359eaDo,71
14
+ jarvis/tools/base.py,sha256=EGRGbdfbLXDLwtyoWdvp9rlxNX7bzc20t0Vc2VkwIEY,652
15
+ jarvis/tools/coder.py,sha256=ZJfPInKms4Hj3-eQlBwamVsvZ-2nlZ-4jsqJ-tJc6mg,2040
16
+ jarvis/tools/file_ops.py,sha256=h8g0eT9UvlJf4kt0DLXvdSsjcPj7x19lxWdDApeDfpg,3842
17
+ jarvis/tools/generator.py,sha256=vVP3eN5cCDpRXf_fn0skETkPXAW1XZFWx9pt2_ahK48,5999
18
+ jarvis/tools/methodology.py,sha256=G3cOaHTMujGZBhDLhQEqyCV2NISizO3MXRuho1KfI6Y,5223
19
+ jarvis/tools/registry.py,sha256=mlOAmUq3yzRz-7yvwrrCwbe5Lmw8eh1v8-_Fa5sezwI,7209
20
+ jarvis/tools/search.py,sha256=1EqOVvLhg2Csh-i03-XeCrusbyfmH69FZ8khwZt8Tow,6131
21
+ jarvis/tools/shell.py,sha256=UPKshPyOaUwTngresUw-ot1jHjQIb4wCY5nkJqa38lU,2520
22
+ jarvis/tools/sub_agent.py,sha256=rEtAmSVY2ZjFOZEKr5m5wpACOQIiM9Zr_3dT92FhXYU,2621
23
+ jarvis/tools/webpage.py,sha256=d3w3Jcjcu1ESciezTkz3n3Zf-rp_l91PrVoDEZnckOo,2391
24
+ jarvis_ai_assistant-0.1.60.dist-info/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
25
+ jarvis_ai_assistant-0.1.60.dist-info/METADATA,sha256=7RcBcdhyJDGa7UhfVwNHG8BvuJnw5PhI3nbLWS1PGog,11213
26
+ jarvis_ai_assistant-0.1.60.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
27
+ jarvis_ai_assistant-0.1.60.dist-info/entry_points.txt,sha256=ieRI4ilnGNx1R6LlzT2P510mJ27lhLesVZToezDjSd8,89
28
+ jarvis_ai_assistant-0.1.60.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
29
+ jarvis_ai_assistant-0.1.60.dist-info/RECORD,,
@@ -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)
@@ -1,29 +0,0 @@
1
- jarvis/__init__.py,sha256=rc0PX9hG--lb8NGfVN3-VdS1SnQTkK_G_FvWr1ZO-LM,50
2
- jarvis/agent.py,sha256=s8VebNef8BbKM6D9IDWWN-XenEXEL9KoYnDLGKyZY58,12347
3
- jarvis/main.py,sha256=gXXtnrkkvGwEswJL6qiYjVrg3bpzye-GJeAe0Nf2B9o,6509
4
- jarvis/utils.py,sha256=JlkuC9RtspXH2VWDmj9nR0vnb8ie1gIsKc4vC7WRco8,7321
5
- jarvis/jarvis_coder/main.py,sha256=7Zc50dpPSM6hq4ASg1-yGZ83iS1NxuvQn70D9SPNbqY,30751
6
- jarvis/models/__init__.py,sha256=mrOt67nselz_H1gX9wdAO4y2DY5WPXzABqJbr5Des8k,63
7
- jarvis/models/ai8.py,sha256=aSiUazBrj4Fsit3tQX7jj8nM8MDBhHFnFrbQC6CvcGw,12745
8
- jarvis/models/base.py,sha256=39g0GcXrNu16RV3HzazIqleynCaQDhm0XzF0r0uMDro,1506
9
- jarvis/models/kimi.py,sha256=j4VtRip0WhTgI4PGL_YXL9Z-II-xHe6hvJW3vOY-ShA,17545
10
- jarvis/models/openai.py,sha256=FcZVcCK41jP4SB_q4kZ6bga2tYbL-s6zjpS4KU30-tw,4477
11
- jarvis/models/oyi.py,sha256=vVs1ru5K8WpofjwoYTxUQVvXL4Yc7ZH6nZhKYpEZcdk,15288
12
- jarvis/models/registry.py,sha256=iVBjN9ImEvGHcz8WR-z8pPMJQZI907o_nccVOFANhak,7951
13
- jarvis/tools/__init__.py,sha256=Kj1bKj34lwRDKMKHLOrLyQElf2lHbqA2tDgP359eaDo,71
14
- jarvis/tools/base.py,sha256=EGRGbdfbLXDLwtyoWdvp9rlxNX7bzc20t0Vc2VkwIEY,652
15
- jarvis/tools/bing_search.py,sha256=SvYeXM83eP9qREDcWguh0_r0m0npbcwXIyzEwwfreKU,1444
16
- jarvis/tools/file_ops.py,sha256=h8g0eT9UvlJf4kt0DLXvdSsjcPj7x19lxWdDApeDfpg,3842
17
- jarvis/tools/generator.py,sha256=vVP3eN5cCDpRXf_fn0skETkPXAW1XZFWx9pt2_ahK48,5999
18
- jarvis/tools/methodology.py,sha256=G3cOaHTMujGZBhDLhQEqyCV2NISizO3MXRuho1KfI6Y,5223
19
- jarvis/tools/registry.py,sha256=mlOAmUq3yzRz-7yvwrrCwbe5Lmw8eh1v8-_Fa5sezwI,7209
20
- jarvis/tools/search.py,sha256=o7EJqEtKKDqlENkXhxDuDA73djzeEH9j4BB0n61egso,4899
21
- jarvis/tools/shell.py,sha256=UPKshPyOaUwTngresUw-ot1jHjQIb4wCY5nkJqa38lU,2520
22
- jarvis/tools/sub_agent.py,sha256=rEtAmSVY2ZjFOZEKr5m5wpACOQIiM9Zr_3dT92FhXYU,2621
23
- jarvis/tools/webpage.py,sha256=d3w3Jcjcu1ESciezTkz3n3Zf-rp_l91PrVoDEZnckOo,2391
24
- jarvis_ai_assistant-0.1.58.dist-info/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
25
- jarvis_ai_assistant-0.1.58.dist-info/METADATA,sha256=upXDcw_I_71ZInBR5zWI90zGsm5YDBoFG64btCGews0,10049
26
- jarvis_ai_assistant-0.1.58.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
27
- jarvis_ai_assistant-0.1.58.dist-info/entry_points.txt,sha256=ieRI4ilnGNx1R6LlzT2P510mJ27lhLesVZToezDjSd8,89
28
- jarvis_ai_assistant-0.1.58.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
29
- jarvis_ai_assistant-0.1.58.dist-info/RECORD,,