jarvis-ai-assistant 0.1.195__py3-none-any.whl → 0.1.197__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
jarvis/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """Jarvis AI Assistant"""
3
3
 
4
- __version__ = "0.1.195"
4
+ __version__ = "0.1.197"
@@ -1,6 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  # 标准库导入
3
3
  import datetime
4
+ import os
4
5
  from pathlib import Path
5
6
  import platform
6
7
  from typing import Any, Callable, Dict, List, Optional, Protocol, Tuple, Union
@@ -18,7 +19,7 @@ from jarvis.jarvis_utils.config import (get_data_dir, get_max_token_count,
18
19
  get_max_tool_call_count,
19
20
  is_auto_complete,
20
21
  is_execute_tool_confirm,
21
- is_use_analysis, is_use_methodology)
22
+ is_use_analysis, get_history_count, is_use_methodology)
22
23
  from jarvis.jarvis_utils.embedding import get_context_token_count
23
24
  from jarvis.jarvis_utils.globals import (delete_agent, get_interrupt,
24
25
  make_agent_name, set_agent,
@@ -159,6 +160,7 @@ class Agent:
159
160
  use_methodology: Optional[bool] = None,
160
161
  use_analysis: Optional[bool] = None,
161
162
  files: List[str] = [],
163
+ history_count: Optional[int] = None,
162
164
  ):
163
165
  self.files = files
164
166
  """初始化Jarvis Agent实例
@@ -238,7 +240,11 @@ class Agent:
238
240
  self.after_tool_call_cb: Optional[Callable[[Agent], None]] = None
239
241
 
240
242
  self.history = JarvisHistory()
241
- self.history.start_record(str(Path(get_data_dir())/"history"))
243
+ self.history_dir = str(Path(get_data_dir())/"history")
244
+ self.history.start_record(self.history_dir)
245
+
246
+ self.history_count = history_count if history_count is not None else get_history_count()
247
+
242
248
 
243
249
  self.execute_tool_confirm = (
244
250
  execute_tool_confirm
@@ -467,13 +473,25 @@ class Agent:
467
473
  注意:
468
474
  当上下文长度超过最大值时使用
469
475
  """
470
- summary = self.generate_summary()
471
- self.clear_history() # type: ignore
476
+ need_summary = True
477
+ tmp_file_name = ""
478
+ try:
479
+ if self.model and self.model.support_upload_files():
480
+ need_summary = False
481
+ if need_summary:
482
+ summary = self.generate_summary()
483
+ else:
484
+ import tempfile
485
+ tmp_file = tempfile.NamedTemporaryFile(delete=False)
486
+ tmp_file_name = tmp_file.name
487
+ self.history.save_history(tmp_file_name)
488
+ self.clear_history() # type: ignore
472
489
 
473
- if not summary:
474
- return ""
490
+ if need_summary:
491
+ if not summary:
492
+ return ""
475
493
 
476
- return f"""
494
+ return f"""
477
495
  以下是之前对话的关键信息总结:
478
496
 
479
497
  <content>
@@ -481,7 +499,15 @@ class Agent:
481
499
  </content>
482
500
 
483
501
  请基于以上信息继续完成任务。请注意,这是之前对话的摘要,上下文长度已超过限制而被重置。请直接继续任务,无需重复已完成的步骤。如有需要,可以询问用户以获取更多信息。
484
- """
502
+ """
503
+ else:
504
+ if self.model and self.model.upload_files([tmp_file_name]):
505
+ return "上传的文件是历史对话信息,请基于历史对话信息继续完成任务。"
506
+ else:
507
+ return ""
508
+ finally:
509
+ if tmp_file_name:
510
+ os.remove(tmp_file_name)
485
511
 
486
512
  def _call_tools(self, response: str) -> Tuple[bool, Any]:
487
513
  """调用工具执行响应
@@ -782,31 +808,9 @@ arguments:
782
808
  set_agent(self.name, self)
783
809
 
784
810
  while True:
811
+ history_md = ""
785
812
  if self.first:
786
- # 如果有上传文件,先上传文件
787
- if self.model and self.model.support_upload_files():
788
- if self.use_methodology:
789
- if not upload_methodology(self.model, other_files=self.files):
790
- if self.files:
791
- PrettyOutput.print("文件上传失败,将忽略文件列表", OutputType.WARNING)
792
- # 上传失败则回退到本地加载
793
- msg = self.prompt
794
- for handler in self.input_handler:
795
- msg, _ = handler(msg, self)
796
- self.prompt = f"{self.prompt}\n\n以下是历史类似问题的执行经验,可参考:\n{load_methodology(msg, self.get_tool_registry())}"
797
- elif self.files:
798
- if not self.model.upload_files(self.files):
799
- PrettyOutput.print("文件上传失败,将忽略文件列表", OutputType.WARNING)
800
- else:
801
- if self.files:
802
- PrettyOutput.print("不支持上传文件,将忽略文件列表", OutputType.WARNING)
803
- if self.use_methodology:
804
- msg = self.prompt
805
- for handler in self.input_handler:
806
- msg, _ = handler(msg, self)
807
- self.prompt = f"{self.prompt}\n\n以下是历史类似问题的执行经验,可参考:\n{load_methodology(msg, self.get_tool_registry())}"
808
-
809
- self.first = False
813
+ history_md = self._first_run()
810
814
  try:
811
815
  current_response = self._call_model(self.prompt, True)
812
816
  self.prompt = ""
@@ -849,6 +853,8 @@ arguments:
849
853
  return self._complete_task()
850
854
 
851
855
  except Exception as e:
856
+ if history_md:
857
+ os.remove(history_md)
852
858
  PrettyOutput.print(f"任务失败: {str(e)}", OutputType.ERROR)
853
859
  return f"Task failed: {str(e)}"
854
860
 
@@ -856,6 +862,41 @@ arguments:
856
862
  PrettyOutput.print(f"任务失败: {str(e)}", OutputType.ERROR)
857
863
  return f"Task failed: {str(e)}"
858
864
 
865
+ def _first_run(self):
866
+ history_md = ""
867
+ if self.history_count > 0 and self.model and self.model.support_upload_files():
868
+ import tempfile
869
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
870
+ history_md = str(Path(tempfile.gettempdir())/f"{self.name}_history_{timestamp}.md")
871
+ self.history.export_history_to_markdown(tempfile.gettempdir(), history_md, max_files=self.history_count)
872
+ self.files.append(history_md)
873
+
874
+ # 如果有上传文件,先上传文件
875
+ if self.model and self.model.support_upload_files():
876
+ if self.use_methodology:
877
+ if not upload_methodology(self.model, other_files=self.files):
878
+ if self.files:
879
+ PrettyOutput.print("文件上传失败,将忽略文件列表", OutputType.WARNING)
880
+ # 上传失败则回退到本地加载
881
+ msg = self.prompt
882
+ for handler in self.input_handler:
883
+ msg, _ = handler(msg, self)
884
+ self.prompt = f"{self.prompt}\n\n以下是历史类似问题的执行经验,可参考:\n{load_methodology(msg, self.get_tool_registry())}"
885
+ elif self.files:
886
+ if not self.model.upload_files(self.files):
887
+ PrettyOutput.print("文件上传失败,将忽略文件列表", OutputType.WARNING)
888
+ else:
889
+ if self.files:
890
+ PrettyOutput.print("不支持上传文件,将忽略文件列表", OutputType.WARNING)
891
+ if self.use_methodology:
892
+ msg = self.prompt
893
+ for handler in self.input_handler:
894
+ msg, _ = handler(msg, self)
895
+ self.prompt = f"{self.prompt}\n\n以下是历史类似问题的执行经验,可参考:\n{load_methodology(msg, self.get_tool_registry())}"
896
+
897
+ self.first = False
898
+ return history_md
899
+
859
900
  def clear_history(self):
860
901
  """清空对话历史但保留系统提示
861
902
 
@@ -201,6 +201,11 @@
201
201
  "description": "是否打印提示",
202
202
  "default": false
203
203
  },
204
+ "JARVIS_USE_HISTORY_COUNT": {
205
+ "type": "number",
206
+ "description": "使用的历史记录数量",
207
+ "default": 0
208
+ },
204
209
  "JARVIS_REPLACE_MAP": {
205
210
  "type": "object",
206
211
  "description": "自定义替换映射表配置",
@@ -265,6 +265,16 @@ def is_print_prompt() -> bool:
265
265
  return GLOBAL_CONFIG_DATA.get("JARVIS_PRINT_PROMPT", False) == True
266
266
 
267
267
 
268
+ def get_history_count() -> int:
269
+ """
270
+ 获取是否启用历史记录功能。
271
+
272
+ 返回:
273
+ bool: 如果启用历史记录则返回True,默认为False
274
+ """
275
+ return GLOBAL_CONFIG_DATA.get("JARVIS_USE_HISTORY_COUNT", 0)
276
+
277
+
268
278
  def get_mcp_config() -> List[Dict[str, Any]]:
269
279
  """
270
280
  获取MCP配置列表。
@@ -1,6 +1,7 @@
1
+ import glob
1
2
  import os
2
3
  from datetime import datetime
3
- from typing import Dict, List, Optional
4
+ from typing import Dict, List, Optional, Union
4
5
 
5
6
  import yaml
6
7
 
@@ -22,22 +23,76 @@ class JarvisHistory:
22
23
  raise RuntimeError("Recording not started. Call start_record first.")
23
24
  self.records.append({"role": role, "message": msg})
24
25
 
25
- def stop_record(self) -> None:
26
+ def save_history(self, filename: str) -> None:
26
27
  """Save recorded messages to YAML file"""
27
- if not self.current_file:
28
- raise RuntimeError("No recording session to stop.")
29
28
 
30
29
  # Skip saving if records is empty
31
30
  if not self.records:
32
- self.current_file = None
33
- self.records = []
34
31
  return
35
32
 
36
33
  # Ensure directory exists
37
- os.makedirs(os.path.dirname(self.current_file), exist_ok=True)
34
+ os.makedirs(os.path.dirname(filename), exist_ok=True)
38
35
 
39
- with open(self.current_file, "w") as f:
36
+ with open(filename, "w") as f:
40
37
  yaml.safe_dump({"conversation": self.records}, f, allow_unicode=True)
41
38
 
39
+ def stop_record(self) -> None:
40
+ """Stop recording session and save messages"""
41
+ if not self.current_file:
42
+ raise RuntimeError("No recording session to stop.")
43
+
44
+ self.save_history(self.current_file)
42
45
  self.current_file = None
43
46
  self.records = []
47
+
48
+ @staticmethod
49
+ def export_history_to_markdown(
50
+ input_dir: str, output_file: str, max_files: Optional[int] = None
51
+ ) -> None:
52
+ """
53
+ Export all history files in the directory to a single markdown file
54
+
55
+ Args:
56
+ input_dir: Directory containing history YAML files
57
+ output_file: Path to output markdown file
58
+ max_files: Maximum number of history files to export (None for all)
59
+ """
60
+ # Find all history files in the directory
61
+ history_files = glob.glob(os.path.join(input_dir, "history_*.yaml"))
62
+
63
+ if not history_files:
64
+ raise FileNotFoundError(f"No history files found in {input_dir}")
65
+
66
+ # Sort files by modification time (newest first) and limit to max_files
67
+ history_files.sort(key=os.path.getmtime, reverse=True)
68
+ if max_files is not None:
69
+ history_files = history_files[:max_files]
70
+
71
+ # Ensure output directory exists
72
+ os.makedirs(os.path.dirname(output_file), exist_ok=True)
73
+
74
+ with open(output_file, "w", encoding="utf-8") as md_file:
75
+ md_file.write("# Jarvis Conversation History\n\n")
76
+
77
+ for history_file in sorted(history_files):
78
+ # Read YAML file
79
+ with open(history_file, "r", encoding="utf-8") as f:
80
+ data = yaml.safe_load(f)
81
+
82
+ if not data or "conversation" not in data:
83
+ continue
84
+
85
+ # Write file header with timestamp from filename
86
+ timestamp = os.path.basename(history_file)[
87
+ 8:-5
88
+ ] # Extract timestamp from "history_YYYYMMDD_HHMMSS.yaml"
89
+ md_file.write(
90
+ f"## Conversation at {timestamp[:4]}-{timestamp[4:6]}-{timestamp[6:8]} "
91
+ f"{timestamp[9:11]}:{timestamp[11:13]}:{timestamp[13:15]}\n\n"
92
+ )
93
+
94
+ # Write conversation messages
95
+ for msg in data["conversation"]:
96
+ md_file.write(f"**{msg['role']}**: {msg['message']}\n\n")
97
+
98
+ md_file.write("\n---\n\n")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.1.195
3
+ Version: 0.1.197
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
@@ -538,6 +538,7 @@ OPENAI_API_BASE: https://api.openai.com/v1
538
538
  | `JARVIS_PRINT_PROMPT` | false | 是否打印提示 |
539
539
  | `JARVIS_USE_METHODOLOGY` | true | 是否启用方法论功能 |
540
540
  | `JARVIS_USE_ANALYSIS` | true | 是否启用任务分析功能 |
541
+ | `JARVIS_USE_HISTORY_COUNT` | 0 | 使用N次历史记录作为上下文记忆 |
541
542
  | `JARVIS_DATA_PATH` | ~/.jarvis | Jarvis数据存储目录路径 |
542
543
 
543
544
  ## 🛠️ 工具说明 <a id="tools"></a>
@@ -1,5 +1,5 @@
1
- jarvis/__init__.py,sha256=BtvIF-tEcm-J883lHUbiv2FXU6ggOOfOjPVy85nlDJM,75
2
- jarvis/jarvis_agent/__init__.py,sha256=mBtXEkj60R_dFl99r3dWfO3_D3OYhdBnCT-xjabOG5c,31841
1
+ jarvis/__init__.py,sha256=yN1aJh6gZYIZoCWOibxj7NWyO48_EH7fOLOVXD_Dl4w,75
2
+ jarvis/jarvis_agent/__init__.py,sha256=qcer73IfiAd_IaHznBXAg-nzHZdkoCYhRloDFyJRcTM,33346
3
3
  jarvis/jarvis_agent/builtin_input_handler.py,sha256=4CCEtVLRBIpkhDUKd54VcX1JFCIWCvDqDh4B1H-eSn0,2187
4
4
  jarvis/jarvis_agent/jarvis.py,sha256=GH2zi8eXNpW8twiY3LKDEZgGmFC5geB0jlkwFrm7hOQ,6279
5
5
  jarvis/jarvis_agent/main.py,sha256=c6bQe-8LXvW2-NBn9Rn_yPYdrwnkJ8KQaSFY2cPvkxw,2775
@@ -29,7 +29,7 @@ jarvis/jarvis_code_analysis/checklists/shell.py,sha256=aRFYhQQvTgbYd-uY5pc8UHIUA
29
29
  jarvis/jarvis_code_analysis/checklists/sql.py,sha256=vR0T6qC7b4dURjJVAd7kSVxyvZEQXPG1Jqc2sNTGp5c,2355
30
30
  jarvis/jarvis_code_analysis/checklists/swift.py,sha256=TPx4I6Gupvs6tSerRKmTSKEPQpOLEbH2Y7LXg1uBgxc,2566
31
31
  jarvis/jarvis_code_analysis/checklists/web.py,sha256=25gGD7pDadZQybNFvALYxWvK0VRjGQb1NVJQElwjyk0,3943
32
- jarvis/jarvis_data/config_schema.json,sha256=u_I8KawgwUltW-tVw3t37rLv4TqdBynZaNGhOFH2Zf8,6397
32
+ jarvis/jarvis_data/config_schema.json,sha256=yMXCNy8-CRL44r9GKBClvX6LBX20cpm7QOW9TSffAbs,6533
33
33
  jarvis/jarvis_data/huggingface.tar.gz,sha256=dWKnc_tvyx-I_ZkXo91O0b38KxDmLW1ZbmJ3E6fCl_k,1120205
34
34
  jarvis/jarvis_dev/main.py,sha256=zzVDrPQlPJFnHxNjChBAYA8YwIaQYmPxG-bHjIxdL3s,40940
35
35
  jarvis/jarvis_git_details/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -79,20 +79,20 @@ jarvis/jarvis_tools/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
79
79
  jarvis/jarvis_tools/cli/main.py,sha256=Mg6TQDxMdzB1Ua1UrZ2EE-uQWsbaeojWaEGHJp2HimA,6375
80
80
  jarvis/jarvis_utils/__init__.py,sha256=67h0ldisGlh3oK4DAeNEL2Bl_VsI3tSmfclasyVlueM,850
81
81
  jarvis/jarvis_utils/builtin_replace_map.py,sha256=s7C5wKhoKkv-O4ltMcDzNpv5oGPC1EbXgiohPHAqksw,4892
82
- jarvis/jarvis_utils/config.py,sha256=e-nrbpAbGtFKkRuqzG021OgDZCvlYjMdlmPUqt2_zGo,7121
82
+ jarvis/jarvis_utils/config.py,sha256=OomZRIeRHiBntKXdqYP1ArI8aqRFqtMlLkd9-VSd5dA,7364
83
83
  jarvis/jarvis_utils/embedding.py,sha256=suqKOgH4cq2HYj4xvNpqDPN9pyc3hTCl934xYonF6qk,3922
84
84
  jarvis/jarvis_utils/file_processors.py,sha256=XiM248SHS7lLgQDCbORVFWqinbVDUawYxWDOsLXDxP8,3043
85
85
  jarvis/jarvis_utils/git_utils.py,sha256=F1558EbJ3dRl12DMwmT24XjkC8XqddBjhpWxLgyI0aI,15688
86
86
  jarvis/jarvis_utils/globals.py,sha256=9NTMfCVd0jvtloOv14-KE6clhcVStFmyN9jWxLmQ5so,3369
87
87
  jarvis/jarvis_utils/input.py,sha256=WOs9hYSiZE3ao5K-UJmC7KyZByYnC1opHGJTUZm7DVo,7884
88
- jarvis/jarvis_utils/jarvis_history.py,sha256=VG8THVUuuxwbGdFhYeM9mVlGzRCCqYZeW0PpSz_vXFU,1456
88
+ jarvis/jarvis_utils/jarvis_history.py,sha256=Td6cmze9Oc5-Ewz0l9RKYeSg_-fbEu9ZhyEueHEMcLY,3664
89
89
  jarvis/jarvis_utils/methodology.py,sha256=MhPrMxMqElyAn54BDfpQdUqrRr7IbSlrLvAI39LCgTM,8487
90
90
  jarvis/jarvis_utils/output.py,sha256=PRCgudPOB8gMEP3u-g0FGD2c6tBgJhLXUMqNPglfjV8,10813
91
91
  jarvis/jarvis_utils/tag.py,sha256=f211opbbbTcSyzCDwuIK_oCnKhXPNK-RknYyGzY1yD0,431
92
92
  jarvis/jarvis_utils/utils.py,sha256=55kIbFXPFEd6770mdy2fZDh96XH0rIFJw2w3rYhE2Cc,11895
93
- jarvis_ai_assistant-0.1.195.dist-info/licenses/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
94
- jarvis_ai_assistant-0.1.195.dist-info/METADATA,sha256=Zz3_o4jZtHVoB_SRn9aguPc4WGj-jmTwzF69ZqgtXa8,20134
95
- jarvis_ai_assistant-0.1.195.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
96
- jarvis_ai_assistant-0.1.195.dist-info/entry_points.txt,sha256=Gy3DOP1PYLMK0GCj4rrP_9lkOyBQ39EK_lKGUSwn41E,869
97
- jarvis_ai_assistant-0.1.195.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
98
- jarvis_ai_assistant-0.1.195.dist-info/RECORD,,
93
+ jarvis_ai_assistant-0.1.197.dist-info/licenses/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
94
+ jarvis_ai_assistant-0.1.197.dist-info/METADATA,sha256=WXBVY2pO7UC4ZppWf6rfgtm61tfqUA3hA0WigWBRs_g,20215
95
+ jarvis_ai_assistant-0.1.197.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
96
+ jarvis_ai_assistant-0.1.197.dist-info/entry_points.txt,sha256=Gy3DOP1PYLMK0GCj4rrP_9lkOyBQ39EK_lKGUSwn41E,869
97
+ jarvis_ai_assistant-0.1.197.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
98
+ jarvis_ai_assistant-0.1.197.dist-info/RECORD,,