jarvis-ai-assistant 0.1.213__tar.gz → 0.1.214__tar.gz

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.
Files changed (103) hide show
  1. {jarvis_ai_assistant-0.1.213/src/jarvis_ai_assistant.egg-info → jarvis_ai_assistant-0.1.214}/PKG-INFO +1 -1
  2. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/pyproject.toml +1 -1
  3. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/setup.py +1 -1
  4. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/__init__.py +1 -1
  5. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_agent/__init__.py +43 -20
  6. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_agent/builtin_input_handler.py +12 -1
  7. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_agent/edit_file_handler.py +44 -32
  8. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_agent/code_agent.py +10 -4
  9. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_platform/ai8.py +51 -5
  10. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_platform/base.py +33 -7
  11. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_platform/human.py +43 -1
  12. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_platform/kimi.py +50 -6
  13. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_platform/openai.py +38 -1
  14. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_platform/oyi.py +47 -3
  15. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_platform/tongyi.py +53 -7
  16. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_platform/yuanbao.py +47 -4
  17. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_platform_manager/main.py +65 -19
  18. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_platform_manager/service.py +66 -100
  19. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_utils/input.py +1 -0
  20. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214/src/jarvis_ai_assistant.egg-info}/PKG-INFO +1 -1
  21. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/LICENSE +0 -0
  22. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/MANIFEST.in +0 -0
  23. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/README.md +0 -0
  24. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/setup.cfg +0 -0
  25. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_agent/jarvis.py +0 -0
  26. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_agent/main.py +0 -0
  27. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_agent/output_handler.py +0 -0
  28. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_agent/shell_input_handler.py +0 -0
  29. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_agent/__init__.py +0 -0
  30. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_agent/lint.py +0 -0
  31. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/__init__.py +0 -0
  32. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/c_cpp.py +0 -0
  33. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/csharp.py +0 -0
  34. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/data_format.py +0 -0
  35. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/devops.py +0 -0
  36. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/docs.py +0 -0
  37. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/go.py +0 -0
  38. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/infrastructure.py +0 -0
  39. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/java.py +0 -0
  40. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/javascript.py +0 -0
  41. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/kotlin.py +0 -0
  42. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/loader.py +0 -0
  43. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/php.py +0 -0
  44. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/python.py +0 -0
  45. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/ruby.py +0 -0
  46. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/rust.py +0 -0
  47. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/shell.py +0 -0
  48. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/sql.py +0 -0
  49. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/swift.py +0 -0
  50. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/checklists/web.py +0 -0
  51. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_code_analysis/code_review.py +0 -0
  52. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_data/config_schema.json +0 -0
  53. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_data/tiktoken/9b5ad71b2ce5302211f9c61530b329a4922fc6a4 +0 -0
  54. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_git_details/__init__.py +0 -0
  55. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_git_details/main.py +0 -0
  56. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_git_squash/__init__.py +0 -0
  57. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_git_squash/main.py +0 -0
  58. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_git_utils/git_commiter.py +0 -0
  59. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_mcp/__init__.py +0 -0
  60. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_mcp/sse_mcp_client.py +0 -0
  61. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_mcp/stdio_mcp_client.py +0 -0
  62. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_mcp/streamable_mcp_client.py +0 -0
  63. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_methodology/main.py +0 -0
  64. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_multi_agent/__init__.py +0 -0
  65. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_multi_agent/main.py +0 -0
  66. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_platform/__init__.py +0 -0
  67. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_platform/registry.py +0 -0
  68. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_platform_manager/__init__.py +0 -0
  69. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_smart_shell/__init__.py +0 -0
  70. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_smart_shell/main.py +0 -0
  71. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_tools/__init__.py +0 -0
  72. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_tools/ask_user.py +0 -0
  73. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_tools/base.py +0 -0
  74. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_tools/cli/__init__.py +0 -0
  75. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_tools/cli/main.py +0 -0
  76. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_tools/edit_file.py +0 -0
  77. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_tools/execute_script.py +0 -0
  78. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_tools/file_analyzer.py +0 -0
  79. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_tools/generate_new_tool.py +0 -0
  80. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_tools/methodology.py +0 -0
  81. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_tools/read_code.py +0 -0
  82. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_tools/read_webpage.py +0 -0
  83. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_tools/registry.py +0 -0
  84. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_tools/rewrite_file.py +0 -0
  85. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_tools/search_web.py +0 -0
  86. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_tools/virtual_tty.py +0 -0
  87. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_utils/__init__.py +0 -0
  88. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_utils/builtin_replace_map.py +0 -0
  89. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_utils/config.py +0 -0
  90. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_utils/embedding.py +0 -0
  91. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_utils/file_processors.py +0 -0
  92. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_utils/git_utils.py +0 -0
  93. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_utils/globals.py +0 -0
  94. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_utils/http.py +0 -0
  95. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_utils/methodology.py +0 -0
  96. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_utils/output.py +0 -0
  97. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_utils/tag.py +0 -0
  98. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis/jarvis_utils/utils.py +0 -0
  99. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis_ai_assistant.egg-info/SOURCES.txt +0 -0
  100. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis_ai_assistant.egg-info/dependency_links.txt +0 -0
  101. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis_ai_assistant.egg-info/entry_points.txt +0 -0
  102. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis_ai_assistant.egg-info/requires.txt +0 -0
  103. {jarvis_ai_assistant-0.1.213 → jarvis_ai_assistant-0.1.214}/src/jarvis_ai_assistant.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.1.213
3
+ Version: 0.1.214
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "jarvis-ai-assistant"
7
- version = "0.1.213"
7
+ version = "0.1.214"
8
8
  description = "Jarvis: An AI assistant that uses tools to interact with the system"
9
9
  readme = "README.md"
10
10
  authors = [{ name = "skyfire", email = "skyfireitdiy@hotmail.com" }]
@@ -3,7 +3,7 @@ from setuptools import setup, find_packages # type: ignore
3
3
 
4
4
  setup(
5
5
  name="jarvis-ai-assistant",
6
- version="0.1.213",
6
+ version="0.1.214",
7
7
  author="skyfire",
8
8
  author_email="skyfireitdiy@hotmail.com",
9
9
  description="An AI assistant that uses various tools to interact with the system",
@@ -1,4 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """Jarvis AI Assistant"""
3
3
 
4
- __version__ = "0.1.213"
4
+ __version__ = "0.1.214"
@@ -119,13 +119,17 @@ origin_agent_system_prompt = f"""
119
119
 
120
120
 
121
121
  class OutputHandlerProtocol(Protocol):
122
- def name(self) -> str: ...
122
+ def name(self) -> str:
123
+ ...
123
124
 
124
- def can_handle(self, response: str) -> bool: ...
125
+ def can_handle(self, response: str) -> bool:
126
+ ...
125
127
 
126
- def prompt(self) -> str: ...
128
+ def prompt(self) -> str:
129
+ ...
127
130
 
128
- def handle(self, response: str, agent: Any) -> Tuple[bool, Any]: ...
131
+ def handle(self, response: str, agent: Any) -> Tuple[bool, Any]:
132
+ ...
129
133
 
130
134
 
131
135
  class Agent:
@@ -191,9 +195,7 @@ class Agent:
191
195
  if isinstance(platform, str):
192
196
  self.model = PlatformRegistry().create_platform(platform)
193
197
  if self.model is None:
194
- PrettyOutput.print(
195
- f"平台 {platform} 不存在,将使用普通模型", OutputType.WARNING
196
- )
198
+ PrettyOutput.print(f"平台 {platform} 不存在,将使用普通模型", OutputType.WARNING)
197
199
  self.model = PlatformRegistry().get_normal_platform()
198
200
  else:
199
201
  self.model = platform
@@ -356,6 +358,33 @@ class Agent:
356
358
  """
357
359
  self.after_tool_call_cb = cb
358
360
 
361
+ def save_session(self) -> bool:
362
+ """保存当前会话状态到文件"""
363
+ if not self.model:
364
+ PrettyOutput.print("没有可用的模型实例来保存会话。", OutputType.ERROR)
365
+ return False
366
+ session_dir = os.path.join(os.getcwd(), ".jarvis")
367
+ os.makedirs(session_dir, exist_ok=True)
368
+ session_file = os.path.join(session_dir, "saved_session.json")
369
+ return self.model.save(session_file)
370
+
371
+ def restore_session(self) -> bool:
372
+ """从文件恢复会话状态"""
373
+ if not self.model:
374
+ return False # No model, cannot restore
375
+ session_file = os.path.join(os.getcwd(), ".jarvis", "saved_session.json")
376
+ if not os.path.exists(session_file):
377
+ return False
378
+
379
+ if self.model.restore(session_file):
380
+ try:
381
+ os.remove(session_file)
382
+ PrettyOutput.print("会话已恢复,并已删除会话文件。", OutputType.SUCCESS)
383
+ except OSError as e:
384
+ PrettyOutput.print(f"删除会话文件失败: {e}", OutputType.ERROR)
385
+ return True
386
+ return False
387
+
359
388
  def get_tool_registry(self) -> Optional[Any]:
360
389
  """获取工具注册表实例"""
361
390
  from jarvis.jarvis_tools.registry import ToolRegistry
@@ -781,18 +810,14 @@ arguments:
781
810
 
782
811
  if get_interrupt():
783
812
  set_interrupt(False)
784
- user_input = self.multiline_inputer(
785
- f"模型交互期间被中断,请输入用户干预信息:"
786
- )
813
+ user_input = self.multiline_inputer(f"模型交互期间被中断,请输入用户干预信息:")
787
814
  if user_input:
788
815
  # 如果有工具调用且用户确认继续,则将干预信息和工具执行结果拼接为prompt
789
816
  if any(
790
817
  handler.can_handle(current_response)
791
818
  for handler in self.output_handler
792
819
  ):
793
- if user_confirm(
794
- "检测到有工具调用,是否继续处理工具调用?", True
795
- ):
820
+ if user_confirm("检测到有工具调用,是否继续处理工具调用?", True):
796
821
  self.prompt = f"{user_input}\n\n{current_response}"
797
822
  continue
798
823
  self.prompt += f"{user_input}"
@@ -838,9 +863,7 @@ arguments:
838
863
  if self.use_methodology:
839
864
  if not upload_methodology(self.model, other_files=self.files):
840
865
  if self.files:
841
- PrettyOutput.print(
842
- "文件上传失败,将忽略文件列表", OutputType.WARNING
843
- )
866
+ PrettyOutput.print("文件上传失败,将忽略文件列表", OutputType.WARNING)
844
867
  # 上传失败则回退到本地加载
845
868
  msg = self.prompt
846
869
  for handler in self.input_handler:
@@ -848,14 +871,14 @@ arguments:
848
871
  self.prompt = f"{self.prompt}\n\n以下是历史类似问题的执行经验,可参考:\n{load_methodology(msg, self.get_tool_registry())}"
849
872
  else:
850
873
  if self.files:
851
- self.prompt = f"{self.prompt}\n\n上传的文件包含历史对话信息和方法论文件,可以从中获取一些经验信息。"
874
+ self.prompt = (
875
+ f"{self.prompt}\n\n上传的文件包含历史对话信息和方法论文件,可以从中获取一些经验信息。"
876
+ )
852
877
  else:
853
878
  self.prompt = f"{self.prompt}\n\n上传的文件包含历史对话信息,可以从中获取一些经验信息。"
854
879
  elif self.files:
855
880
  if not self.model.upload_files(self.files):
856
- PrettyOutput.print(
857
- "文件上传失败,将忽略文件列表", OutputType.WARNING
858
- )
881
+ PrettyOutput.print("文件上传失败,将忽略文件列表", OutputType.WARNING)
859
882
  else:
860
883
  self.prompt = f"{self.prompt}\n\n上传的文件包含历史对话信息,可以从中获取一些经验信息。"
861
884
  else:
@@ -1,5 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import re
3
+ import sys
3
4
  from typing import Any, Tuple
4
5
 
5
6
  from jarvis.jarvis_utils.config import get_replace_map
@@ -29,7 +30,6 @@ def builtin_input_handler(user_input: str, agent_: Any) -> Tuple[str, bool]:
29
30
  replace_map = get_replace_map()
30
31
  # 处理每个标记
31
32
  for tag in special_tags:
32
-
33
33
  # 优先处理特殊标记
34
34
  if tag == "Summary":
35
35
  agent._summarize_and_clear_history()
@@ -51,6 +51,17 @@ def builtin_input_handler(user_input: str, agent_: Any) -> Tuple[str, bool]:
51
51
 
52
52
  load_config()
53
53
  return "", True
54
+ elif tag == "SaveSession":
55
+ if agent.save_session():
56
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
57
+
58
+ PrettyOutput.print("会话已成功保存。正在退出...", OutputType.SUCCESS)
59
+ sys.exit(0)
60
+ else:
61
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
62
+
63
+ PrettyOutput.print("保存会话失败。", OutputType.ERROR)
64
+ return "", True
54
65
 
55
66
  processed_tag = set()
56
67
  add_on_prompt = ""
@@ -206,48 +206,60 @@ class EditFileHandler(OutputHandler):
206
206
  exact_search = search_text
207
207
 
208
208
  if exact_search in modified_content:
209
- if modified_content.count(exact_search) > 1:
210
- PrettyOutput.print(
211
- f"搜索文本在文件中存在多处匹配:\n{exact_search}",
212
- output_type=OutputType.WARNING,
213
- )
214
- return False, f"搜索文本在文件中存在多处匹配:\n{exact_search}"
215
-
216
209
  # 直接执行替换(保留所有原始格式)
217
210
  modified_content = modified_content.replace(
218
211
  exact_search, replace_text
219
212
  )
220
213
  print(f"✅ 补丁 #{patch_count} 应用成功")
221
214
  else:
222
- # 尝试增加缩进重试
223
215
  found = False
224
- for space_count in range(1, 17):
225
- indented_search = "\n".join(
226
- " " * space_count + line if line.strip() else line
227
- for line in search_text.split("\n")
228
- )
229
- indented_replace = "\n".join(
230
- " " * space_count + line if line.strip() else line
231
- for line in replace_text.split("\n")
232
- )
233
- if indented_search in modified_content:
234
- if modified_content.count(indented_search) > 1:
235
- PrettyOutput.print(
236
- f"搜索文本在文件中存在多处匹配:\n{indented_search}",
237
- output_type=OutputType.WARNING,
238
- )
239
- return (
240
- False,
241
- f"搜索文本在文件中存在多处匹配:\n{indented_search}",
242
- )
216
+ # 如果匹配不到,并且search与replace块的首尾都是换行,尝试去掉第一个和最后一个换行
217
+ if (
218
+ search_text.startswith("\n")
219
+ and search_text.endswith("\n")
220
+ and replace_text.startswith("\n")
221
+ and replace_text.endswith("\n")
222
+ ):
223
+ stripped_search = search_text[1:-1]
224
+ stripped_replace = replace_text[1:-1]
225
+ if stripped_search in modified_content:
243
226
  modified_content = modified_content.replace(
244
- indented_search, indented_replace
245
- )
246
- print(
247
- f"✅ 补丁 #{patch_count} 应用成功 (自动增加 {space_count} 个空格缩进)"
227
+ stripped_search, stripped_replace
248
228
  )
229
+ print(f"✅ 补丁 #{patch_count} 应用成功 (自动去除首尾换行)")
249
230
  found = True
250
- break
231
+
232
+ if not found:
233
+ # 尝试增加缩进重试
234
+ current_search = search_text
235
+ current_replace = replace_text
236
+ if (
237
+ current_search.startswith("\n")
238
+ and current_search.endswith("\n")
239
+ and current_replace.startswith("\n")
240
+ and current_replace.endswith("\n")
241
+ ):
242
+ current_search = current_search[1:-1]
243
+ current_replace = current_replace[1:-1]
244
+
245
+ for space_count in range(1, 17):
246
+ indented_search = "\n".join(
247
+ " " * space_count + line if line.strip() else line
248
+ for line in current_search.split("\n")
249
+ )
250
+ indented_replace = "\n".join(
251
+ " " * space_count + line if line.strip() else line
252
+ for line in current_replace.split("\n")
253
+ )
254
+ if indented_search in modified_content:
255
+ modified_content = modified_content.replace(
256
+ indented_search, indented_replace
257
+ )
258
+ print(
259
+ f"✅ 补丁 #{patch_count} 应用成功 (自动增加 {space_count} 个空格缩进)"
260
+ )
261
+ found = True
262
+ break
251
263
 
252
264
  if not found:
253
265
  PrettyOutput.print(
@@ -131,7 +131,7 @@ class CodeAgent:
131
131
  input_handler=[shell_input_handler, builtin_input_handler],
132
132
  need_summary=need_summary,
133
133
  use_methodology=False, # 禁用方法论
134
- use_analysis=False # 禁用分析
134
+ use_analysis=False, # 禁用分析
135
135
  )
136
136
 
137
137
  self.agent.set_after_tool_call_cb(self.after_tool_call_cb)
@@ -359,9 +359,7 @@ class CodeAgent:
359
359
 
360
360
  # 添加提交信息到final_ret
361
361
  if commits:
362
- final_ret += (
363
- f"\n\n代码已修改完成\n补丁内容:\n```diff\n{diff}\n```\n"
364
- )
362
+ final_ret += f"\n\n代码已修改完成\n补丁内容:\n```diff\n{diff}\n```\n"
365
363
  # 修改后的提示逻辑
366
364
  lint_tools_info = "\n".join(
367
365
  f" - {file}: 使用 {'、'.join(get_lint_tools(file))}"
@@ -380,6 +378,7 @@ class CodeAgent:
380
378
  {file_list}
381
379
  {tool_info}
382
380
  如果本次修改引入了警告和错误,请根据警告和错误信息修复代码
381
+ 注意:如果要进行静态检查,需要在所有的修改都完成之后进行集中检查
383
382
  """
384
383
  agent.set_addon_prompt(addon_prompt)
385
384
  else:
@@ -428,6 +427,13 @@ def main() -> None:
428
427
 
429
428
  try:
430
429
  agent = CodeAgent(platform=args.platform, model=args.model, need_summary=False)
430
+
431
+ # 尝试恢复会话
432
+ if agent.agent.restore_session():
433
+ PrettyOutput.print(
434
+ "已从 .jarvis/saved_session.json 恢复会话。", OutputType.SUCCESS
435
+ )
436
+
431
437
  if args.requirement:
432
438
  agent.run(args.requirement)
433
439
  else:
@@ -1,5 +1,6 @@
1
1
  import os
2
- from typing import Generator, List, Tuple
2
+ from typing import Any, Dict, Generator, List, Tuple
3
+
3
4
  from jarvis.jarvis_platform.base import BasePlatform
4
5
  import json
5
6
 
@@ -57,7 +58,6 @@ class AI8Model(BasePlatform):
57
58
  def create_conversation(self) -> bool:
58
59
  """Create a new conversation"""
59
60
  try:
60
-
61
61
  # 1. 创建会话
62
62
  response = while_success(
63
63
  lambda: http.post(
@@ -117,13 +117,12 @@ class AI8Model(BasePlatform):
117
117
  def chat(self, message: str) -> Generator[str, None, None]:
118
118
  """Execute conversation"""
119
119
  try:
120
-
121
120
  # 确保有会话ID
122
121
  if not self.conversation:
123
122
  if not self.create_conversation():
124
123
  raise Exception("Failed to create conversation")
125
124
 
126
- payload = {
125
+ payload: Dict[str, Any] = {
127
126
  "text": message,
128
127
  "sessionId": self.conversation["id"] if self.conversation else None,
129
128
  "files": [],
@@ -184,7 +183,7 @@ class AI8Model(BasePlatform):
184
183
 
185
184
  data = response.json()
186
185
  if data["code"] == 0:
187
- self.conversation = None
186
+ self.conversation = {}
188
187
  return True
189
188
  else:
190
189
  error_msg = f"删除会话失败: {data.get('msg', '未知错误')}"
@@ -195,6 +194,53 @@ class AI8Model(BasePlatform):
195
194
  PrettyOutput.print(f"删除会话失败: {str(e)}", OutputType.ERROR)
196
195
  return False
197
196
 
197
+ def save(self, file_path: str) -> bool:
198
+ """Save chat session to a file."""
199
+ if not self.conversation:
200
+ PrettyOutput.print("没有活动的会话可供保存", OutputType.WARNING)
201
+ return False
202
+
203
+ state = {
204
+ "conversation": self.conversation,
205
+ "model_name": self.model_name,
206
+ "system_prompt": self.system_prompt,
207
+ }
208
+
209
+ try:
210
+ with open(file_path, "w", encoding="utf-8") as f:
211
+ json.dump(state, f, ensure_ascii=False, indent=4)
212
+ self._saved = True
213
+ PrettyOutput.print(f"会话已成功保存到 {file_path}", OutputType.SUCCESS)
214
+ return True
215
+ except Exception as e:
216
+ PrettyOutput.print(f"保存会话失败: {str(e)}", OutputType.ERROR)
217
+ return False
218
+
219
+ def restore(self, file_path: str) -> bool:
220
+ """Restore chat session from a file."""
221
+ try:
222
+ with open(file_path, "r", encoding="utf-8") as f:
223
+ state = json.load(f)
224
+
225
+ self.conversation = state["conversation"]
226
+ self.model_name = state["model_name"]
227
+ self.system_prompt = state.get("system_prompt", "")
228
+
229
+ # A restored session should not be deleted on exit, as it's persistent.
230
+ self._saved = True
231
+
232
+ PrettyOutput.print(f"从 {file_path} 成功恢复会话", OutputType.SUCCESS)
233
+ return True
234
+ except FileNotFoundError:
235
+ PrettyOutput.print(f"会话文件未找到: {file_path}", OutputType.ERROR)
236
+ return False
237
+ except KeyError as e:
238
+ PrettyOutput.print(f"恢复失败: 会话文件格式不正确,缺少键 {e}", OutputType.ERROR)
239
+ return False
240
+ except Exception as e:
241
+ PrettyOutput.print(f"恢复会话失败: {str(e)}", OutputType.ERROR)
242
+ return False
243
+
198
244
  def get_available_models(self) -> List[str]:
199
245
  """Get available model list
200
246
 
@@ -27,10 +27,12 @@ class BasePlatform(ABC):
27
27
  """Initialize model"""
28
28
  self.suppress_output = True # 添加输出控制标志
29
29
  self.web = False # 添加web属性,默认false
30
+ self._saved = False
30
31
 
31
32
  def __del__(self):
32
33
  """Destroy model"""
33
- self.delete_chat()
34
+ if not self._saved:
35
+ self.delete_chat()
34
36
 
35
37
  @abstractmethod
36
38
  def set_model_name(self, model_name: str):
@@ -93,14 +95,10 @@ class BasePlatform(ABC):
93
95
  ):
94
96
  response += trunk
95
97
 
96
- print(
97
- f"📤 提交第{submit_count}部分完成,当前进度:{length}/{len(message)}"
98
- )
98
+ print(f"📤 提交第{submit_count}部分完成,当前进度:{length}/{len(message)}")
99
99
  print("✅ 提交完成")
100
100
  response += "\n" + while_true(
101
- lambda: while_success(
102
- lambda: self._chat("内容已经全部提供完毕,请根据内容继续"), 5
103
- ),
101
+ lambda: while_success(lambda: self._chat("内容已经全部提供完毕,请根据内容继续"), 5),
104
102
  5,
105
103
  )
106
104
  else:
@@ -183,6 +181,34 @@ class BasePlatform(ABC):
183
181
  """Delete chat"""
184
182
  raise NotImplementedError("delete_chat is not implemented")
185
183
 
184
+ @abstractmethod
185
+ def save(self, file_path: str) -> bool:
186
+ """Save chat session to a file.
187
+
188
+ Note:
189
+ Implementations of this method should set `self._saved = True` upon successful saving
190
+ to prevent the session from being deleted on object destruction.
191
+
192
+ Args:
193
+ file_path: The path to save the session file.
194
+
195
+ Returns:
196
+ True if saving is successful, False otherwise.
197
+ """
198
+ raise NotImplementedError("save is not implemented")
199
+
200
+ @abstractmethod
201
+ def restore(self, file_path: str) -> bool:
202
+ """Restore chat session from a file.
203
+
204
+ Args:
205
+ file_path: The path to restore the session file from.
206
+
207
+ Returns:
208
+ True if restoring is successful, False otherwise.
209
+ """
210
+ raise NotImplementedError("restore is not implemented")
211
+
186
212
  @abstractmethod
187
213
  def set_system_prompt(self, message: str):
188
214
  """Set system message"""
@@ -4,6 +4,7 @@
4
4
 
5
5
  # 提供与真实人类交互的模拟接口
6
6
 
7
+ import json
7
8
  import random
8
9
  import string
9
10
  from typing import Generator, List, Tuple
@@ -59,8 +60,9 @@ class HumanPlatform(BasePlatform):
59
60
 
60
61
  # 将prompt复制到剪贴板
61
62
  import subprocess
63
+
62
64
  try:
63
- subprocess.run(['xsel', '-ib'], input=prompt.encode('utf-8'), check=True)
65
+ subprocess.run(["xsel", "-ib"], input=prompt.encode("utf-8"), check=True)
64
66
  PrettyOutput.print("提示已复制到剪贴板", OutputType.INFO)
65
67
  except subprocess.CalledProcessError as e:
66
68
  PrettyOutput.print(f"无法复制到剪贴板: {e}", OutputType.WARNING)
@@ -80,6 +82,46 @@ class HumanPlatform(BasePlatform):
80
82
  self.first_message = True
81
83
  return True
82
84
 
85
+ def save(self, file_path: str) -> bool:
86
+ """Save chat session to a file."""
87
+ state = {
88
+ "conversation_id": self.conversation_id,
89
+ "model_name": self.model_name,
90
+ "system_message": self.system_message,
91
+ "first_message": self.first_message,
92
+ }
93
+
94
+ try:
95
+ with open(file_path, "w", encoding="utf-8") as f:
96
+ json.dump(state, f, ensure_ascii=False, indent=4)
97
+ self._saved = True
98
+ PrettyOutput.print(f"会话已成功保存到 {file_path}", OutputType.SUCCESS)
99
+ return True
100
+ except Exception as e:
101
+ PrettyOutput.print(f"保存会话失败: {str(e)}", OutputType.ERROR)
102
+ return False
103
+
104
+ def restore(self, file_path: str) -> bool:
105
+ """Restore chat session from a file."""
106
+ try:
107
+ with open(file_path, "r", encoding="utf-8") as f:
108
+ state = json.load(f)
109
+
110
+ self.conversation_id = state.get("conversation_id", "")
111
+ self.model_name = state.get("model_name", "human")
112
+ self.system_message = state.get("system_message", "")
113
+ self.first_message = state.get("first_message", True)
114
+ self._saved = True
115
+
116
+ PrettyOutput.print(f"从 {file_path} 成功恢复会话", OutputType.SUCCESS)
117
+ return True
118
+ except FileNotFoundError:
119
+ PrettyOutput.print(f"会话文件未找到: {file_path}", OutputType.ERROR)
120
+ return False
121
+ except Exception as e:
122
+ PrettyOutput.print(f"恢复会话失败: {str(e)}", OutputType.ERROR)
123
+ return False
124
+
83
125
  def name(self) -> str:
84
126
  """平台名称"""
85
127
  return self.model_name
@@ -70,9 +70,7 @@ class KimiModel(BasePlatform):
70
70
  sleep_time=5,
71
71
  )
72
72
  if response.status_code != 200:
73
- PrettyOutput.print(
74
- f"错误:创建会话失败:{response.json()}", OutputType.ERROR
75
- )
73
+ PrettyOutput.print(f"错误:创建会话失败:{response.json()}", OutputType.ERROR)
76
74
  return False
77
75
  self.chat_id = response.json()["id"]
78
76
  return True
@@ -158,7 +156,7 @@ class KimiModel(BasePlatform):
158
156
  )
159
157
 
160
158
  response_data = b""
161
-
159
+
162
160
  # 处理流式响应
163
161
  for chunk in response_stream:
164
162
  response_data += chunk
@@ -298,9 +296,9 @@ class KimiModel(BasePlatform):
298
296
  lambda: http.stream_post(url, headers=headers, json=payload),
299
297
  sleep_time=5,
300
298
  )
301
-
299
+
302
300
  response_data = b""
303
-
301
+
304
302
  # 处理流式响应
305
303
  for chunk in response_stream:
306
304
  response_data += chunk
@@ -367,6 +365,52 @@ class KimiModel(BasePlatform):
367
365
  PrettyOutput.print(f"删除会话时发生错误: {str(e)}", OutputType.ERROR)
368
366
  return False
369
367
 
368
+ def save(self, file_path: str) -> bool:
369
+ """Save chat session to a file."""
370
+ if not self.chat_id:
371
+ PrettyOutput.print("没有活动的会话可供保存", OutputType.WARNING)
372
+ return False
373
+
374
+ state = {
375
+ "chat_id": self.chat_id,
376
+ "model_name": self.model_name,
377
+ "system_message": self.system_message,
378
+ "first_chat": self.first_chat,
379
+ "uploaded_files": self.uploaded_files,
380
+ }
381
+
382
+ try:
383
+ with open(file_path, "w", encoding="utf-8") as f:
384
+ json.dump(state, f, ensure_ascii=False, indent=4)
385
+ self._saved = True
386
+ PrettyOutput.print(f"会话已成功保存到 {file_path}", OutputType.SUCCESS)
387
+ return True
388
+ except Exception as e:
389
+ PrettyOutput.print(f"保存会话失败: {str(e)}", OutputType.ERROR)
390
+ return False
391
+
392
+ def restore(self, file_path: str) -> bool:
393
+ """Restore chat session from a file."""
394
+ try:
395
+ with open(file_path, "r", encoding="utf-8") as f:
396
+ state = json.load(f)
397
+
398
+ self.chat_id = state.get("chat_id", "")
399
+ self.model_name = state.get("model_name", "kimi")
400
+ self.system_message = state.get("system_message", "")
401
+ self.first_chat = state.get("first_chat", True)
402
+ self.uploaded_files = state.get("uploaded_files", [])
403
+ self._saved = True
404
+
405
+ PrettyOutput.print(f"从 {file_path} 成功恢复会话", OutputType.SUCCESS)
406
+ return True
407
+ except FileNotFoundError:
408
+ PrettyOutput.print(f"会话文件未找到: {file_path}", OutputType.ERROR)
409
+ return False
410
+ except Exception as e:
411
+ PrettyOutput.print(f"恢复会话失败: {str(e)}", OutputType.ERROR)
412
+ return False
413
+
370
414
  def name(self) -> str:
371
415
  """Model name"""
372
416
  return self.model_name