jarvis-ai-assistant 0.1.220__py3-none-any.whl → 0.1.222__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.
Files changed (32) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +110 -395
  3. jarvis/jarvis_agent/edit_file_handler.py +32 -185
  4. jarvis/jarvis_agent/jarvis.py +14 -9
  5. jarvis/jarvis_agent/main.py +13 -6
  6. jarvis/jarvis_agent/prompt_builder.py +57 -0
  7. jarvis/jarvis_agent/prompts.py +188 -0
  8. jarvis/jarvis_agent/protocols.py +30 -0
  9. jarvis/jarvis_agent/session_manager.py +84 -0
  10. jarvis/jarvis_agent/tool_executor.py +49 -0
  11. jarvis/jarvis_code_agent/code_agent.py +14 -23
  12. jarvis/jarvis_code_analysis/code_review.py +1 -1
  13. jarvis/jarvis_data/config_schema.json +13 -18
  14. jarvis/jarvis_git_details/main.py +1 -1
  15. jarvis/jarvis_platform/kimi.py +4 -2
  16. jarvis/jarvis_rag/__init__.py +2 -2
  17. jarvis/jarvis_rag/cache.py +28 -30
  18. jarvis/jarvis_rag/cli.py +141 -52
  19. jarvis/jarvis_rag/embedding_manager.py +32 -46
  20. jarvis/jarvis_rag/llm_interface.py +32 -34
  21. jarvis/jarvis_rag/query_rewriter.py +11 -12
  22. jarvis/jarvis_rag/rag_pipeline.py +40 -43
  23. jarvis/jarvis_rag/reranker.py +18 -18
  24. jarvis/jarvis_rag/retriever.py +29 -29
  25. jarvis/jarvis_tools/edit_file.py +11 -36
  26. jarvis/jarvis_utils/config.py +20 -25
  27. {jarvis_ai_assistant-0.1.220.dist-info → jarvis_ai_assistant-0.1.222.dist-info}/METADATA +25 -20
  28. {jarvis_ai_assistant-0.1.220.dist-info → jarvis_ai_assistant-0.1.222.dist-info}/RECORD +32 -27
  29. {jarvis_ai_assistant-0.1.220.dist-info → jarvis_ai_assistant-0.1.222.dist-info}/entry_points.txt +9 -0
  30. {jarvis_ai_assistant-0.1.220.dist-info → jarvis_ai_assistant-0.1.222.dist-info}/WHEEL +0 -0
  31. {jarvis_ai_assistant-0.1.220.dist-info → jarvis_ai_assistant-0.1.222.dist-info}/licenses/LICENSE +0 -0
  32. {jarvis_ai_assistant-0.1.220.dist-info → jarvis_ai_assistant-0.1.222.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,84 @@
1
+ # -*- coding: utf-8 -*-
2
+ import os
3
+ from typing import Any, Dict, Optional, TYPE_CHECKING
4
+
5
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
6
+
7
+ if TYPE_CHECKING:
8
+ from jarvis.jarvis_platform.base import BasePlatform
9
+
10
+
11
+ class SessionManager:
12
+ """
13
+ Manages the session state of an agent, including conversation history,
14
+ user data, and persistence.
15
+ """
16
+
17
+ def __init__(self, model: "BasePlatform", agent_name: str):
18
+ self.model = model
19
+ self.agent_name = agent_name
20
+ self.prompt: str = ""
21
+ self.conversation_length: int = 0
22
+ self.user_data: Dict[str, Any] = {}
23
+ self.addon_prompt: str = ""
24
+
25
+ def set_user_data(self, key: str, value: Any):
26
+ """Sets a value in the user data dictionary."""
27
+ self.user_data[key] = value
28
+
29
+ def get_user_data(self, key: str) -> Optional[Any]:
30
+ """Gets a value from the user data dictionary."""
31
+ return self.user_data.get(key)
32
+
33
+ def set_addon_prompt(self, addon_prompt: str):
34
+ """Sets the addon prompt for the next model call."""
35
+ self.addon_prompt = addon_prompt
36
+
37
+ def clear(self):
38
+ """
39
+ Clears the current conversation history, prompt, and length counter.
40
+ """
41
+ self.model.reset()
42
+ self.conversation_length = 0
43
+ self.prompt = ""
44
+
45
+ def save_session(self) -> bool:
46
+ """Saves the current session state to a file."""
47
+ session_dir = os.path.join(os.getcwd(), ".jarvis")
48
+ os.makedirs(session_dir, exist_ok=True)
49
+ platform_name = self.model.platform_name()
50
+ model_name = self.model.name().replace("/", "_").replace("\\", "_")
51
+ session_file = os.path.join(
52
+ session_dir,
53
+ f"saved_session_{self.agent_name}_{platform_name}_{model_name}.json",
54
+ )
55
+ return self.model.save(session_file)
56
+
57
+ def restore_session(self) -> bool:
58
+ """Restores the session state from a file."""
59
+ platform_name = self.model.platform_name()
60
+ model_name = self.model.name().replace("/", "_").replace("\\", "_")
61
+ session_file = os.path.join(
62
+ os.getcwd(),
63
+ ".jarvis",
64
+ f"saved_session_{self.agent_name}_{platform_name}_{model_name}.json",
65
+ )
66
+ if not os.path.exists(session_file):
67
+ return False
68
+
69
+ if self.model.restore(session_file):
70
+ try:
71
+ os.remove(session_file)
72
+ PrettyOutput.print("会话已恢复,并已删除会话文件。", OutputType.SUCCESS)
73
+ except OSError as e:
74
+ PrettyOutput.print(f"删除会话文件失败: {e}", OutputType.ERROR)
75
+ return True
76
+ return False
77
+
78
+ def clear_history(self):
79
+ """
80
+ Clears conversation history but keeps the system prompt by resetting the model state.
81
+ """
82
+ self.prompt = ""
83
+ self.model.reset()
84
+ self.conversation_length = 0
@@ -0,0 +1,49 @@
1
+ # -*- coding: utf-8 -*-
2
+ from typing import Any, Tuple, TYPE_CHECKING
3
+
4
+ from jarvis.jarvis_utils.input import user_confirm
5
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
6
+
7
+ if TYPE_CHECKING:
8
+ from jarvis.jarvis_agent import Agent
9
+
10
+
11
+ def execute_tool_call(response: str, agent: "Agent") -> Tuple[bool, Any]:
12
+ """
13
+ Parses the model's response, identifies the appropriate tool, and executes it.
14
+
15
+ Args:
16
+ response: The response string from the model, potentially containing a tool call.
17
+ agent: The agent instance, providing context like output handlers and settings.
18
+
19
+ Returns:
20
+ A tuple containing:
21
+ - A boolean indicating if the tool's result should be returned to the user.
22
+ - The result of the tool execution or an error message.
23
+ """
24
+ tool_list = []
25
+ for handler in agent.output_handler:
26
+ if handler.can_handle(response):
27
+ tool_list.append(handler)
28
+
29
+ if len(tool_list) > 1:
30
+ error_message = (
31
+ f"操作失败:检测到多个操作。一次只能执行一个操作。"
32
+ f"尝试执行的操作:{', '.join([handler.name() for handler in tool_list])}"
33
+ )
34
+ PrettyOutput.print(error_message, OutputType.WARNING)
35
+ return False, error_message
36
+
37
+ if not tool_list:
38
+ return False, ""
39
+
40
+ tool_to_execute = tool_list[0]
41
+ if not agent.execute_tool_confirm or user_confirm(
42
+ f"需要执行{tool_to_execute.name()}确认执行?", True
43
+ ):
44
+ print(f"🔧 正在执行{tool_to_execute.name()}...")
45
+ result = tool_to_execute.handle(response, agent)
46
+ print(f"✅ {tool_to_execute.name()}执行完成")
47
+ return result
48
+
49
+ return False, ""
@@ -18,7 +18,7 @@ from jarvis.jarvis_code_agent.lint import get_lint_tools
18
18
  from jarvis.jarvis_git_utils.git_commiter import GitCommitTool
19
19
  from jarvis.jarvis_platform.registry import PlatformRegistry
20
20
  from jarvis.jarvis_tools.registry import ToolRegistry
21
- from jarvis.jarvis_utils.config import is_confirm_before_apply_patch
21
+ from jarvis.jarvis_utils.config import is_confirm_before_apply_patch, is_enable_static_analysis
22
22
  from jarvis.jarvis_utils.git_utils import (
23
23
  confirm_add_new_files,
24
24
  find_git_root_and_cd,
@@ -43,8 +43,7 @@ class CodeAgent:
43
43
 
44
44
  def __init__(
45
45
  self,
46
- platform: Optional[str] = None,
47
- model: Optional[str] = None,
46
+ llm_type: str = "normal",
48
47
  need_summary: bool = True,
49
48
  ):
50
49
  self.root_dir = os.getcwd()
@@ -113,21 +112,12 @@ class CodeAgent:
113
112
  10. 我不订阅闲 AI
114
113
  </say_to_llm>
115
114
  """
116
- # 处理platform参数
117
- platform_instance = (
118
- PlatformRegistry().create_platform(platform) # type: ignore
119
- if platform
120
- else PlatformRegistry().get_normal_platform()
121
- ) # type: ignore
122
- if model:
123
- platform_instance.set_model_name(model) # type: ignore
124
-
125
115
  self.agent = Agent(
126
116
  system_prompt=code_system_prompt,
127
117
  name="CodeAgent",
128
118
  auto_complete=False,
129
119
  output_handler=[tool_registry, EditFileHandler()],
130
- platform=platform_instance,
120
+ llm_type=llm_type,
131
121
  input_handler=[shell_input_handler, builtin_input_handler],
132
122
  need_summary=need_summary,
133
123
  use_methodology=False, # 禁用方法论
@@ -374,7 +364,7 @@ class CodeAgent:
374
364
  if lint_tools_info
375
365
  else ""
376
366
  )
377
- if lint_tools_info:
367
+ if lint_tools_info and is_enable_static_analysis():
378
368
  addon_prompt = f"""
379
369
  请对以下修改的文件进行静态扫描:
380
370
  {file_list}
@@ -392,19 +382,19 @@ class CodeAgent:
392
382
  return
393
383
  # 用户确认最终结果
394
384
  if commited:
395
- agent.prompt += final_ret
385
+ agent.session.prompt += final_ret
396
386
  return
397
387
  PrettyOutput.print(final_ret, OutputType.USER, lang="markdown")
398
388
  if not is_confirm_before_apply_patch() or user_confirm(
399
389
  "是否使用此回复?", default=True
400
390
  ):
401
- agent.prompt += final_ret
391
+ agent.session.prompt += final_ret
402
392
  return
403
- agent.prompt += final_ret
393
+ agent.session.prompt += final_ret
404
394
  custom_reply = get_multiline_input("请输入自定义回复")
405
395
  if custom_reply.strip(): # 如果自定义回复为空,返回空字符串
406
396
  agent.set_addon_prompt(custom_reply)
407
- agent.prompt += final_ret
397
+ agent.session.prompt += final_ret
408
398
 
409
399
 
410
400
  def main() -> None:
@@ -413,10 +403,11 @@ def main() -> None:
413
403
 
414
404
  parser = argparse.ArgumentParser(description="Jarvis Code Agent")
415
405
  parser.add_argument(
416
- "-p", "--platform", type=str, help="Target platform name", default=None
417
- )
418
- parser.add_argument(
419
- "-m", "--model", type=str, help="Model name to use", default=None
406
+ "--llm_type",
407
+ type=str,
408
+ default="normal",
409
+ choices=["normal", "thinking"],
410
+ help="LLM type to use",
420
411
  )
421
412
  parser.add_argument(
422
413
  "-r", "--requirement", type=str, help="Requirement to process", default=None
@@ -434,7 +425,7 @@ def main() -> None:
434
425
  PrettyOutput.print(f"当前目录: {git_dir}", OutputType.INFO)
435
426
 
436
427
  try:
437
- agent = CodeAgent(platform=args.platform, model=args.model, need_summary=False)
428
+ agent = CodeAgent(llm_type=args.llm_type, need_summary=False)
438
429
 
439
430
  # 尝试恢复会话
440
431
  if args.restore_session:
@@ -638,7 +638,7 @@ class CodeReviewTool:
638
638
  [在此处插入完整MARKDOWN格式的审查报告]
639
639
  {ct("REPORT")}""",
640
640
  output_handler=[tool_registry],
641
- platform=PlatformRegistry().get_thinking_platform(),
641
+ llm_type="thinking",
642
642
  auto_complete=False,
643
643
  )
644
644
 
@@ -181,34 +181,29 @@
181
181
  "description": "是否打印提示",
182
182
  "default": false
183
183
  },
184
+ "JARVIS_ENABLE_STATIC_ANALYSIS": {
185
+ "type": "boolean",
186
+ "description": "是否启用静态代码分析",
187
+ "default": true
188
+ },
184
189
  "JARVIS_RAG": {
185
190
  "type": "object",
186
191
  "description": "RAG框架的配置",
187
192
  "properties": {
188
- "embedding_mode": {
189
- "type": "string",
190
- "enum": [
191
- "performance",
192
- "accuracy"
193
- ],
194
- "default": "performance",
195
- "description": "嵌入模型的模式, 'performance'表示性能优先, 'accuracy'表示准确度优先"
196
- },
197
- "embedding_cache_path": {
193
+ "embedding_model": {
198
194
  "type": "string",
199
- "default": ".jarvis/rag/embeddings",
200
- "description": "嵌入向量缓存的路径, 相对于当前工作目录"
195
+ "default": "BAAI/bge-base-zh-v1.5",
196
+ "description": "用于RAG的嵌入模型的名称, 默认为 'BAAI/bge-base-zh-v1.5'"
201
197
  },
202
- "vector_db_path": {
198
+ "rerank_model": {
203
199
  "type": "string",
204
- "default": ".jarvis/rag/vectordb",
205
- "description": "向量数据库的持久化存储路径, 相对于当前工作目录"
200
+ "default": "BAAI/bge-reranker-base",
201
+ "description": "用于RAG的rerank模型的名称, 默认为 'BAAI/bge-reranker-base'"
206
202
  }
207
203
  },
208
204
  "default": {
209
- "embedding_mode": "performance",
210
- "embedding_cache_path": ".jarvis/rag/embeddings",
211
- "vector_db_path": ".jarvis/rag/vectordb"
205
+ "embedding_model": "BAAI/bge-base-zh-v1.5",
206
+ "rerank_model": "BAAI/bge-reranker-base"
212
207
  }
213
208
  },
214
209
  "JARVIS_REPLACE_MAP": {
@@ -199,7 +199,7 @@ class GitCommitAnalyzer:
199
199
  [检查代码是否符合行业最佳实践和项目规范]
200
200
  {ct("REPORT")}""",
201
201
  output_handler=[tool_registry],
202
- platform=PlatformRegistry().get_normal_platform(),
202
+ llm_type="normal",
203
203
  auto_complete=True,
204
204
  )
205
205
 
@@ -26,6 +26,7 @@ class KimiModel(BasePlatform):
26
26
  return [
27
27
  ("kimi", "基于网页的 Kimi,免费接口"),
28
28
  ("k1", "基于网页的 Kimi,深度思考模型"),
29
+ ("k2", "基于网页的 Kimi,深度思考模型 K2"),
29
30
  ]
30
31
 
31
32
  def __init__(self):
@@ -278,8 +279,9 @@ class KimiModel(BasePlatform):
278
279
  "use_search": True if self.web else False,
279
280
  "extend": {"sidebar": True},
280
281
  "kimiplus_id": "kimi",
281
- "use_research": False,
282
- "use_math": False,
282
+ "use_deep_research": False,
283
+ "use_semantic_memory": True,
284
+ "history": [],
283
285
  "refs": refs,
284
286
  "refs_file": refs_file,
285
287
  "model": self.model_name,
@@ -1,7 +1,7 @@
1
1
  """
2
- Jarvis RAG Framework
2
+ Jarvis RAG 框架
3
3
 
4
- A flexible RAG pipeline with pluggable remote LLMs and local, cache-enabled embedding models.
4
+ 一个灵活的RAG管道,具有可插拔的远程LLM和本地带缓存的嵌入模型。
5
5
  """
6
6
 
7
7
  from .rag_pipeline import JarvisRAGPipeline
@@ -6,74 +6,72 @@ from diskcache import Cache
6
6
 
7
7
  class EmbeddingCache:
8
8
  """
9
- A disk-based cache for storing and retrieving text embeddings.
9
+ 一个用于存储和检索文本嵌入的基于磁盘的缓存。
10
10
 
11
- This class uses diskcache to create a persistent, local cache. It generates
12
- a key for each text content based on its SHA256 hash, making lookups
13
- deterministic and efficient.
11
+ 该类使用diskcache创建一个持久化的本地缓存。它根据每个文本内容的
12
+ SHA256哈希值为其生成一个键,使得查找过程具有确定性和高效性。
14
13
  """
15
14
 
16
15
  def __init__(self, cache_dir: str, salt: str = ""):
17
16
  """
18
- Initializes the EmbeddingCache.
17
+ 初始化EmbeddingCache
19
18
 
20
- Args:
21
- cache_dir (str): The directory where the cache will be stored.
22
- salt (str): A salt to be added to the hash. This is crucial for
23
- ensuring that embeddings generated by different models
24
- do not collide. For example, use the model name as a salt.
19
+ 参数:
20
+ cache_dir (str): 缓存将要存储的目录。
21
+ salt (str): 添加到哈希中的盐值。这对于确保由不同模型生成的
22
+ 嵌入不会发生冲突至关重要。例如,可以使用模型名称作为盐值。
25
23
  """
26
24
  self.cache = Cache(cache_dir)
27
25
  self.salt = salt
28
26
 
29
27
  def _get_key(self, text: str) -> str:
30
- """Generates a unique cache key for a given text and salt."""
28
+ """为一个给定的文本和盐值生成一个唯一的缓存键。"""
31
29
  hash_object = hashlib.sha256((self.salt + text).encode("utf-8"))
32
30
  return hash_object.hexdigest()
33
31
 
34
32
  def get(self, text: str) -> Optional[Any]:
35
33
  """
36
- Retrieves an embedding from the cache.
34
+ 从缓存中检索一个嵌入。
37
35
 
38
- Args:
39
- text (str): The text to look up.
36
+ 参数:
37
+ text (str): 要查找的文本。
40
38
 
41
- Returns:
42
- The cached embedding, or None if it's not in the cache.
39
+ 返回:
40
+ 缓存的嵌入,如果不在缓存中则返回None
43
41
  """
44
42
  key = self._get_key(text)
45
43
  return self.cache.get(key)
46
44
 
47
45
  def set(self, text: str, embedding: Any) -> None:
48
46
  """
49
- Stores an embedding in the cache.
47
+ 在缓存中存储一个嵌入。
50
48
 
51
- Args:
52
- text (str): The text corresponding to the embedding.
53
- embedding (Any): The embedding vector to store.
49
+ 参数:
50
+ text (str): 与嵌入相对应的文本。
51
+ embedding (Any): 要存储的嵌入向量。
54
52
  """
55
53
  key = self._get_key(text)
56
54
  self.cache.set(key, embedding)
57
55
 
58
56
  def get_batch(self, texts: List[str]) -> List[Optional[Any]]:
59
57
  """
60
- Retrieves a batch of embeddings from the cache.
58
+ 从缓存中检索一批嵌入。
61
59
 
62
- Args:
63
- texts (List[str]): A list of texts to look up.
60
+ 参数:
61
+ texts (List[str]): 要查找的文本列表。
64
62
 
65
- Returns:
66
- A list containing cached embeddings or None for cache misses.
63
+ 返回:
64
+ 一个列表,其中包含缓存的嵌入,对于缓存未命中的情况则为None
67
65
  """
68
66
  return [self.get(text) for text in texts]
69
67
 
70
68
  def set_batch(self, texts: List[str], embeddings: List[Any]) -> None:
71
69
  """
72
- Stores a batch of embeddings in the cache.
70
+ 在缓存中存储一批嵌入。
73
71
 
74
- Args:
75
- texts (List[str]): The list of texts.
76
- embeddings (List[Any]): The list of corresponding embeddings.
72
+ 参数:
73
+ texts (List[str]): 文本列表。
74
+ embeddings (List[Any]): 相应的嵌入列表。
77
75
  """
78
76
  if len(texts) != len(embeddings):
79
77
  raise ValueError("Length of texts and embeddings must be the same.")
@@ -83,5 +81,5 @@ class EmbeddingCache:
83
81
  self.set(text, embedding)
84
82
 
85
83
  def close(self):
86
- """Closes the cache connection."""
84
+ """关闭缓存连接。"""
87
85
  self.cache.close()