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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +110 -395
- jarvis/jarvis_agent/edit_file_handler.py +32 -185
- jarvis/jarvis_agent/jarvis.py +14 -9
- jarvis/jarvis_agent/main.py +13 -6
- jarvis/jarvis_agent/prompt_builder.py +57 -0
- jarvis/jarvis_agent/prompts.py +188 -0
- jarvis/jarvis_agent/protocols.py +30 -0
- jarvis/jarvis_agent/session_manager.py +84 -0
- jarvis/jarvis_agent/tool_executor.py +49 -0
- jarvis/jarvis_code_agent/code_agent.py +14 -23
- jarvis/jarvis_code_analysis/code_review.py +1 -1
- jarvis/jarvis_data/config_schema.json +13 -18
- jarvis/jarvis_git_details/main.py +1 -1
- jarvis/jarvis_platform/kimi.py +4 -2
- jarvis/jarvis_rag/__init__.py +2 -2
- jarvis/jarvis_rag/cache.py +28 -30
- jarvis/jarvis_rag/cli.py +141 -52
- jarvis/jarvis_rag/embedding_manager.py +32 -46
- jarvis/jarvis_rag/llm_interface.py +32 -34
- jarvis/jarvis_rag/query_rewriter.py +11 -12
- jarvis/jarvis_rag/rag_pipeline.py +40 -43
- jarvis/jarvis_rag/reranker.py +18 -18
- jarvis/jarvis_rag/retriever.py +29 -29
- jarvis/jarvis_tools/edit_file.py +11 -36
- jarvis/jarvis_utils/config.py +20 -25
- {jarvis_ai_assistant-0.1.220.dist-info → jarvis_ai_assistant-0.1.222.dist-info}/METADATA +25 -20
- {jarvis_ai_assistant-0.1.220.dist-info → jarvis_ai_assistant-0.1.222.dist-info}/RECORD +32 -27
- {jarvis_ai_assistant-0.1.220.dist-info → jarvis_ai_assistant-0.1.222.dist-info}/entry_points.txt +9 -0
- {jarvis_ai_assistant-0.1.220.dist-info → jarvis_ai_assistant-0.1.222.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.220.dist-info → jarvis_ai_assistant-0.1.222.dist-info}/licenses/LICENSE +0 -0
- {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
|
-
|
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
|
-
|
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
|
-
"
|
417
|
-
|
418
|
-
|
419
|
-
"
|
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(
|
428
|
+
agent = CodeAgent(llm_type=args.llm_type, need_summary=False)
|
438
429
|
|
439
430
|
# 尝试恢复会话
|
440
431
|
if args.restore_session:
|
@@ -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
|
-
"
|
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": ".
|
200
|
-
"description": "
|
195
|
+
"default": "BAAI/bge-base-zh-v1.5",
|
196
|
+
"description": "用于RAG的嵌入模型的名称, 默认为 'BAAI/bge-base-zh-v1.5'"
|
201
197
|
},
|
202
|
-
"
|
198
|
+
"rerank_model": {
|
203
199
|
"type": "string",
|
204
|
-
"default": "
|
205
|
-
"description": "
|
200
|
+
"default": "BAAI/bge-reranker-base",
|
201
|
+
"description": "用于RAG的rerank模型的名称, 默认为 'BAAI/bge-reranker-base'"
|
206
202
|
}
|
207
203
|
},
|
208
204
|
"default": {
|
209
|
-
"
|
210
|
-
"
|
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": {
|
jarvis/jarvis_platform/kimi.py
CHANGED
@@ -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
|
-
"
|
282
|
-
"
|
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,
|
jarvis/jarvis_rag/__init__.py
CHANGED
jarvis/jarvis_rag/cache.py
CHANGED
@@ -6,74 +6,72 @@ from diskcache import Cache
|
|
6
6
|
|
7
7
|
class EmbeddingCache:
|
8
8
|
"""
|
9
|
-
|
9
|
+
一个用于存储和检索文本嵌入的基于磁盘的缓存。
|
10
10
|
|
11
|
-
|
12
|
-
|
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
|
-
|
17
|
+
初始化EmbeddingCache。
|
19
18
|
|
20
|
-
|
21
|
-
cache_dir (str):
|
22
|
-
salt (str):
|
23
|
-
|
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
|
-
"""
|
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
|
-
|
34
|
+
从缓存中检索一个嵌入。
|
37
35
|
|
38
|
-
|
39
|
-
text (str):
|
36
|
+
参数:
|
37
|
+
text (str): 要查找的文本。
|
40
38
|
|
41
|
-
|
42
|
-
|
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
|
-
|
47
|
+
在缓存中存储一个嵌入。
|
50
48
|
|
51
|
-
|
52
|
-
text (str):
|
53
|
-
embedding (Any):
|
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
|
-
|
58
|
+
从缓存中检索一批嵌入。
|
61
59
|
|
62
|
-
|
63
|
-
texts (List[str]):
|
60
|
+
参数:
|
61
|
+
texts (List[str]): 要查找的文本列表。
|
64
62
|
|
65
|
-
|
66
|
-
|
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
|
-
|
70
|
+
在缓存中存储一批嵌入。
|
73
71
|
|
74
|
-
|
75
|
-
texts (List[str]):
|
76
|
-
embeddings (List[Any]):
|
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
|
-
"""
|
84
|
+
"""关闭缓存连接。"""
|
87
85
|
self.cache.close()
|