jarvis-ai-assistant 0.1.131__py3-none-any.whl → 0.1.134__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 +1 -1
- jarvis/jarvis_agent/__init__.py +165 -285
- jarvis/jarvis_agent/jarvis.py +143 -0
- jarvis/jarvis_agent/main.py +0 -2
- jarvis/jarvis_agent/patch.py +70 -48
- jarvis/jarvis_agent/shell_input_handler.py +1 -1
- jarvis/jarvis_code_agent/code_agent.py +169 -117
- jarvis/jarvis_dev/main.py +327 -626
- jarvis/jarvis_git_squash/main.py +10 -31
- jarvis/jarvis_lsp/base.py +0 -42
- jarvis/jarvis_lsp/cpp.py +0 -15
- jarvis/jarvis_lsp/go.py +0 -15
- jarvis/jarvis_lsp/python.py +0 -19
- jarvis/jarvis_lsp/registry.py +0 -62
- jarvis/jarvis_lsp/rust.py +0 -15
- jarvis/jarvis_multi_agent/__init__.py +19 -69
- jarvis/jarvis_multi_agent/main.py +43 -0
- jarvis/jarvis_platform/ai8.py +7 -32
- jarvis/jarvis_platform/base.py +2 -7
- jarvis/jarvis_platform/kimi.py +3 -144
- jarvis/jarvis_platform/ollama.py +54 -68
- jarvis/jarvis_platform/openai.py +0 -4
- jarvis/jarvis_platform/oyi.py +0 -75
- jarvis/jarvis_platform/registry.py +2 -16
- jarvis/jarvis_platform/yuanbao.py +264 -0
- jarvis/jarvis_rag/file_processors.py +138 -0
- jarvis/jarvis_rag/main.py +1305 -425
- jarvis/jarvis_tools/ask_codebase.py +216 -43
- jarvis/jarvis_tools/code_review.py +158 -113
- jarvis/jarvis_tools/create_sub_agent.py +0 -1
- jarvis/jarvis_tools/execute_python_script.py +58 -0
- jarvis/jarvis_tools/execute_shell.py +13 -26
- jarvis/jarvis_tools/execute_shell_script.py +1 -1
- jarvis/jarvis_tools/file_analyzer.py +282 -0
- jarvis/jarvis_tools/file_operation.py +1 -1
- jarvis/jarvis_tools/find_caller.py +278 -0
- jarvis/jarvis_tools/find_symbol.py +295 -0
- jarvis/jarvis_tools/function_analyzer.py +331 -0
- jarvis/jarvis_tools/git_commiter.py +5 -5
- jarvis/jarvis_tools/methodology.py +88 -53
- jarvis/jarvis_tools/project_analyzer.py +308 -0
- jarvis/jarvis_tools/rag.py +0 -5
- jarvis/jarvis_tools/read_code.py +24 -3
- jarvis/jarvis_tools/read_webpage.py +195 -81
- jarvis/jarvis_tools/registry.py +132 -11
- jarvis/jarvis_tools/search_web.py +22 -307
- jarvis/jarvis_tools/tool_generator.py +8 -10
- jarvis/jarvis_utils/__init__.py +1 -0
- jarvis/jarvis_utils/config.py +80 -76
- jarvis/jarvis_utils/embedding.py +344 -45
- jarvis/jarvis_utils/git_utils.py +9 -1
- jarvis/jarvis_utils/input.py +7 -6
- jarvis/jarvis_utils/methodology.py +384 -15
- jarvis/jarvis_utils/output.py +5 -3
- jarvis/jarvis_utils/utils.py +60 -8
- {jarvis_ai_assistant-0.1.131.dist-info → jarvis_ai_assistant-0.1.134.dist-info}/METADATA +8 -16
- jarvis_ai_assistant-0.1.134.dist-info/RECORD +82 -0
- {jarvis_ai_assistant-0.1.131.dist-info → jarvis_ai_assistant-0.1.134.dist-info}/entry_points.txt +4 -3
- jarvis/jarvis_codebase/__init__.py +0 -0
- jarvis/jarvis_codebase/main.py +0 -1011
- jarvis/jarvis_tools/lsp_find_definition.py +0 -150
- jarvis/jarvis_tools/lsp_find_references.py +0 -127
- jarvis/jarvis_tools/treesitter_analyzer.py +0 -331
- jarvis/jarvis_treesitter/README.md +0 -104
- jarvis/jarvis_treesitter/__init__.py +0 -20
- jarvis/jarvis_treesitter/database.py +0 -258
- jarvis/jarvis_treesitter/example.py +0 -115
- jarvis/jarvis_treesitter/grammar_builder.py +0 -182
- jarvis/jarvis_treesitter/language.py +0 -117
- jarvis/jarvis_treesitter/symbol.py +0 -31
- jarvis/jarvis_treesitter/tools_usage.md +0 -121
- jarvis_ai_assistant-0.1.131.dist-info/RECORD +0 -85
- {jarvis_ai_assistant-0.1.131.dist-info → jarvis_ai_assistant-0.1.134.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.131.dist-info → jarvis_ai_assistant-0.1.134.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.131.dist-info → jarvis_ai_assistant-0.1.134.dist-info}/top_level.txt +0 -0
|
@@ -9,12 +9,167 @@
|
|
|
9
9
|
"""
|
|
10
10
|
import os
|
|
11
11
|
import yaml
|
|
12
|
+
import glob
|
|
13
|
+
import json
|
|
14
|
+
import hashlib
|
|
15
|
+
import pickle
|
|
12
16
|
import numpy as np
|
|
13
17
|
import faiss
|
|
14
|
-
from typing import Dict, Any, List
|
|
18
|
+
from typing import Dict, Any, List, Tuple, Optional
|
|
15
19
|
from jarvis.jarvis_utils.output import PrettyOutput, OutputType
|
|
16
20
|
from jarvis.jarvis_utils.embedding import load_embedding_model
|
|
17
|
-
|
|
21
|
+
|
|
22
|
+
# 全局缓存,避免重复计算嵌入向量
|
|
23
|
+
_methodology_embeddings_cache = {}
|
|
24
|
+
_methodology_index_cache: Optional[Tuple[faiss.IndexIDMap, List[Dict[str, str]], str]] = None
|
|
25
|
+
|
|
26
|
+
def _get_cache_directory() -> str:
|
|
27
|
+
"""
|
|
28
|
+
获取缓存目录路径,如果不存在则创建
|
|
29
|
+
|
|
30
|
+
返回:
|
|
31
|
+
str: 缓存目录的路径
|
|
32
|
+
"""
|
|
33
|
+
cache_dir = os.path.expanduser("~/.jarvis/cache")
|
|
34
|
+
if not os.path.exists(cache_dir):
|
|
35
|
+
try:
|
|
36
|
+
os.makedirs(cache_dir, exist_ok=True)
|
|
37
|
+
except Exception as e:
|
|
38
|
+
PrettyOutput.print(f"创建缓存目录失败: {str(e)}", OutputType.ERROR)
|
|
39
|
+
return cache_dir
|
|
40
|
+
|
|
41
|
+
def _get_embeddings_cache_path() -> str:
|
|
42
|
+
"""
|
|
43
|
+
获取嵌入向量缓存文件的路径
|
|
44
|
+
|
|
45
|
+
返回:
|
|
46
|
+
str: 嵌入向量缓存文件的路径
|
|
47
|
+
"""
|
|
48
|
+
return os.path.join(_get_cache_directory(), "methodology_embeddings.pkl")
|
|
49
|
+
|
|
50
|
+
def _get_index_cache_path() -> str:
|
|
51
|
+
"""
|
|
52
|
+
获取索引缓存文件的路径
|
|
53
|
+
|
|
54
|
+
返回:
|
|
55
|
+
str: 索引缓存文件的路径
|
|
56
|
+
"""
|
|
57
|
+
return os.path.join(_get_cache_directory(), "methodology_index.faiss")
|
|
58
|
+
|
|
59
|
+
def _get_index_metadata_path() -> str:
|
|
60
|
+
"""
|
|
61
|
+
获取索引元数据文件的路径
|
|
62
|
+
|
|
63
|
+
返回:
|
|
64
|
+
str: 索引元数据文件的路径
|
|
65
|
+
"""
|
|
66
|
+
return os.path.join(_get_cache_directory(), "methodology_index_metadata.pkl")
|
|
67
|
+
|
|
68
|
+
def _load_embeddings_cache() -> Dict[int, np.ndarray]:
|
|
69
|
+
"""
|
|
70
|
+
从文件系统加载嵌入向量缓存
|
|
71
|
+
|
|
72
|
+
返回:
|
|
73
|
+
Dict[int, np.ndarray]: 嵌入向量缓存字典
|
|
74
|
+
"""
|
|
75
|
+
cache_path = _get_embeddings_cache_path()
|
|
76
|
+
if not os.path.exists(cache_path):
|
|
77
|
+
return {}
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
with open(cache_path, "rb") as f:
|
|
81
|
+
embeddings_cache = pickle.load(f)
|
|
82
|
+
return embeddings_cache
|
|
83
|
+
except Exception as e:
|
|
84
|
+
return {}
|
|
85
|
+
|
|
86
|
+
def _save_embeddings_cache(cache: Dict[int, np.ndarray]) -> bool:
|
|
87
|
+
"""
|
|
88
|
+
将嵌入向量缓存保存到文件系统
|
|
89
|
+
|
|
90
|
+
参数:
|
|
91
|
+
cache: 要保存的嵌入向量缓存字典
|
|
92
|
+
|
|
93
|
+
返回:
|
|
94
|
+
bool: 保存是否成功
|
|
95
|
+
"""
|
|
96
|
+
if not cache:
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
cache_path = _get_embeddings_cache_path()
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
with open(cache_path, "wb") as f:
|
|
103
|
+
pickle.dump(cache, f)
|
|
104
|
+
return True
|
|
105
|
+
except Exception as e:
|
|
106
|
+
PrettyOutput.print(f"保存嵌入向量缓存失败: {str(e)}", OutputType.WARNING)
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
def _load_index_cache() -> Optional[Tuple[faiss.IndexIDMap, List[Dict[str, str]], str]]:
|
|
110
|
+
"""
|
|
111
|
+
从文件系统加载索引缓存
|
|
112
|
+
|
|
113
|
+
返回:
|
|
114
|
+
Optional[Tuple[faiss.IndexIDMap, List[Dict[str, str]], str]]: 索引缓存元组
|
|
115
|
+
"""
|
|
116
|
+
index_path = _get_index_cache_path()
|
|
117
|
+
metadata_path = _get_index_metadata_path()
|
|
118
|
+
|
|
119
|
+
if not os.path.exists(index_path) or not os.path.exists(metadata_path):
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
# 加载索引
|
|
124
|
+
index = faiss.read_index(index_path)
|
|
125
|
+
|
|
126
|
+
# 加载元数据
|
|
127
|
+
with open(metadata_path, "rb") as f:
|
|
128
|
+
metadata = pickle.load(f)
|
|
129
|
+
|
|
130
|
+
methodology_data = metadata.get("methodology_data", [])
|
|
131
|
+
methodology_hash = metadata.get("methodology_hash", "")
|
|
132
|
+
|
|
133
|
+
if isinstance(index, faiss.IndexIDMap) and methodology_data and methodology_hash:
|
|
134
|
+
return index, methodology_data, methodology_hash
|
|
135
|
+
except Exception as e:
|
|
136
|
+
PrettyOutput.print(f"加载索引缓存失败: {str(e)}", OutputType.WARNING)
|
|
137
|
+
|
|
138
|
+
return None
|
|
139
|
+
|
|
140
|
+
def _save_index_cache(index: faiss.IndexIDMap, methodology_data: List[Dict[str, str]], methodology_hash: str) -> bool:
|
|
141
|
+
"""
|
|
142
|
+
将索引缓存保存到文件系统
|
|
143
|
+
|
|
144
|
+
参数:
|
|
145
|
+
index: FAISS索引
|
|
146
|
+
methodology_data: 方法论数据列表
|
|
147
|
+
methodology_hash: 方法论文件哈希值
|
|
148
|
+
|
|
149
|
+
返回:
|
|
150
|
+
bool: 保存是否成功
|
|
151
|
+
"""
|
|
152
|
+
index_path = _get_index_cache_path()
|
|
153
|
+
metadata_path = _get_index_metadata_path()
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
# 保存索引
|
|
157
|
+
faiss.write_index(index, index_path)
|
|
158
|
+
|
|
159
|
+
# 保存元数据
|
|
160
|
+
metadata = {
|
|
161
|
+
"methodology_data": methodology_data,
|
|
162
|
+
"methodology_hash": methodology_hash
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
with open(metadata_path, "wb") as f:
|
|
166
|
+
pickle.dump(metadata, f)
|
|
167
|
+
|
|
168
|
+
return True
|
|
169
|
+
except Exception as e:
|
|
170
|
+
PrettyOutput.print(f"保存索引缓存失败: {str(e)}", OutputType.WARNING)
|
|
171
|
+
return False
|
|
172
|
+
|
|
18
173
|
def _create_methodology_embedding(embedding_model: Any, methodology_text: str) -> np.ndarray:
|
|
19
174
|
"""
|
|
20
175
|
为方法论文本创建嵌入向量。
|
|
@@ -27,6 +182,11 @@ def _create_methodology_embedding(embedding_model: Any, methodology_text: str) -
|
|
|
27
182
|
np.ndarray: 嵌入向量
|
|
28
183
|
"""
|
|
29
184
|
try:
|
|
185
|
+
# 检查缓存中是否已有此文本的嵌入向量
|
|
186
|
+
cache_key = hash(methodology_text)
|
|
187
|
+
if cache_key in _methodology_embeddings_cache:
|
|
188
|
+
return _methodology_embeddings_cache[cache_key]
|
|
189
|
+
|
|
30
190
|
# 截断长文本
|
|
31
191
|
max_length = 512
|
|
32
192
|
text = ' '.join(methodology_text.split()[:max_length])
|
|
@@ -36,10 +196,16 @@ def _create_methodology_embedding(embedding_model: Any, methodology_text: str) -
|
|
|
36
196
|
convert_to_tensor=True,
|
|
37
197
|
normalize_embeddings=True)
|
|
38
198
|
vector = np.array(embedding.cpu().numpy(), dtype=np.float32)
|
|
39
|
-
|
|
199
|
+
result = vector[0] # 返回第一个向量,因为我们只编码了一个文本
|
|
200
|
+
|
|
201
|
+
# 缓存嵌入向量以便后续使用
|
|
202
|
+
_methodology_embeddings_cache[cache_key] = result
|
|
203
|
+
|
|
204
|
+
return result
|
|
40
205
|
except Exception as e:
|
|
41
206
|
PrettyOutput.print(f"创建方法论嵌入向量失败: {str(e)}", OutputType.ERROR)
|
|
42
207
|
return np.zeros(1536, dtype=np.float32)
|
|
208
|
+
|
|
43
209
|
def make_methodology_prompt(data: Dict[str, str]) -> str:
|
|
44
210
|
"""
|
|
45
211
|
从方法论数据生成格式化提示
|
|
@@ -55,6 +221,124 @@ def make_methodology_prompt(data: Dict[str, str]) -> str:
|
|
|
55
221
|
ret += f"问题: {key}\n方法论: {value}\n"
|
|
56
222
|
return ret
|
|
57
223
|
|
|
224
|
+
def _get_methodology_directory() -> str:
|
|
225
|
+
"""
|
|
226
|
+
获取方法论目录路径,如果不存在则创建
|
|
227
|
+
|
|
228
|
+
返回:
|
|
229
|
+
str: 方法论目录的路径
|
|
230
|
+
"""
|
|
231
|
+
methodology_dir = os.path.expanduser("~/.jarvis/methodologies")
|
|
232
|
+
if not os.path.exists(methodology_dir):
|
|
233
|
+
try:
|
|
234
|
+
os.makedirs(methodology_dir, exist_ok=True)
|
|
235
|
+
except Exception as e:
|
|
236
|
+
PrettyOutput.print(f"创建方法论目录失败: {str(e)}", OutputType.ERROR)
|
|
237
|
+
return methodology_dir
|
|
238
|
+
|
|
239
|
+
def _get_methodology_files_hash() -> str:
|
|
240
|
+
"""
|
|
241
|
+
计算所有方法论文件的组合哈希值,用于检测文件变化
|
|
242
|
+
|
|
243
|
+
返回:
|
|
244
|
+
str: 所有方法论文件的组合哈希值
|
|
245
|
+
"""
|
|
246
|
+
methodology_dir = _get_methodology_directory()
|
|
247
|
+
if not os.path.exists(methodology_dir):
|
|
248
|
+
return ""
|
|
249
|
+
|
|
250
|
+
# 获取所有方法论文件的路径和修改时间
|
|
251
|
+
files_data = []
|
|
252
|
+
for filepath in glob.glob(os.path.join(methodology_dir, "*.json")):
|
|
253
|
+
mtime = os.path.getmtime(filepath)
|
|
254
|
+
files_data.append((filepath, mtime))
|
|
255
|
+
|
|
256
|
+
# 按路径排序,保证哈希值的一致性
|
|
257
|
+
files_data.sort(key=lambda x: x[0])
|
|
258
|
+
|
|
259
|
+
# 计算组合哈希值
|
|
260
|
+
if not files_data:
|
|
261
|
+
return ""
|
|
262
|
+
|
|
263
|
+
hasher = hashlib.md5()
|
|
264
|
+
for filepath, mtime in files_data:
|
|
265
|
+
hasher.update(f"{filepath}:{mtime}".encode("utf-8"))
|
|
266
|
+
|
|
267
|
+
return hasher.hexdigest()
|
|
268
|
+
|
|
269
|
+
def _load_all_methodologies() -> Dict[str, str]:
|
|
270
|
+
"""
|
|
271
|
+
加载所有方法论文件
|
|
272
|
+
|
|
273
|
+
返回:
|
|
274
|
+
Dict[str, str]: 方法论字典,键为问题类型,值为方法论内容
|
|
275
|
+
"""
|
|
276
|
+
methodology_dir = _get_methodology_directory()
|
|
277
|
+
all_methodologies = {}
|
|
278
|
+
|
|
279
|
+
if not os.path.exists(methodology_dir):
|
|
280
|
+
return all_methodologies
|
|
281
|
+
|
|
282
|
+
for filepath in glob.glob(os.path.join(methodology_dir, "*.json")):
|
|
283
|
+
try:
|
|
284
|
+
with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
|
|
285
|
+
methodology = json.load(f)
|
|
286
|
+
problem_type = methodology.get("problem_type", "")
|
|
287
|
+
content = methodology.get("content", "")
|
|
288
|
+
if problem_type and content:
|
|
289
|
+
all_methodologies[problem_type] = content
|
|
290
|
+
except Exception as e:
|
|
291
|
+
filename = os.path.basename(filepath)
|
|
292
|
+
PrettyOutput.print(f"加载方法论文件 {filename} 失败: {str(e)}", OutputType.WARNING)
|
|
293
|
+
|
|
294
|
+
return all_methodologies
|
|
295
|
+
|
|
296
|
+
def _migrate_from_old_format():
|
|
297
|
+
"""
|
|
298
|
+
从旧的单文件格式迁移到新的多文件格式
|
|
299
|
+
"""
|
|
300
|
+
old_format_file = os.path.expanduser("~/.jarvis/methodology")
|
|
301
|
+
if not os.path.exists(old_format_file):
|
|
302
|
+
return
|
|
303
|
+
|
|
304
|
+
try:
|
|
305
|
+
# 加载旧格式文件
|
|
306
|
+
with open(old_format_file, "r", encoding="utf-8", errors="ignore") as f:
|
|
307
|
+
old_data = yaml.safe_load(f) or {}
|
|
308
|
+
|
|
309
|
+
if not old_data:
|
|
310
|
+
return
|
|
311
|
+
|
|
312
|
+
# 创建新目录
|
|
313
|
+
methodology_dir = _get_methodology_directory()
|
|
314
|
+
|
|
315
|
+
# 迁移每个方法论
|
|
316
|
+
migrated_count = 0
|
|
317
|
+
for problem_type, content in old_data.items():
|
|
318
|
+
# 为每个方法论创建文件名(使用问题类型的MD5哈希作为文件名)
|
|
319
|
+
safe_filename = hashlib.md5(problem_type.encode('utf-8')).hexdigest()
|
|
320
|
+
file_path = os.path.join(methodology_dir, f"{safe_filename}.json")
|
|
321
|
+
|
|
322
|
+
# 保存为新格式
|
|
323
|
+
with open(file_path, "w", encoding="utf-8", errors="ignore") as f:
|
|
324
|
+
json.dump({
|
|
325
|
+
"problem_type": problem_type,
|
|
326
|
+
"content": content
|
|
327
|
+
}, f, ensure_ascii=False, indent=2)
|
|
328
|
+
|
|
329
|
+
migrated_count += 1
|
|
330
|
+
|
|
331
|
+
if migrated_count > 0:
|
|
332
|
+
# 备份旧文件
|
|
333
|
+
backup_path = old_format_file + ".bak"
|
|
334
|
+
try:
|
|
335
|
+
os.rename(old_format_file, backup_path)
|
|
336
|
+
PrettyOutput.print(f"已成功迁移 {migrated_count} 个方法论,旧文件已备份为 {backup_path}", OutputType.INFO)
|
|
337
|
+
except Exception as e:
|
|
338
|
+
PrettyOutput.print(f"备份旧文件失败,但已完成迁移: {str(e)}", OutputType.WARNING)
|
|
339
|
+
except Exception as e:
|
|
340
|
+
PrettyOutput.print(f"迁移方法论失败: {str(e)}", OutputType.ERROR)
|
|
341
|
+
|
|
58
342
|
def load_methodology(user_input: str) -> str:
|
|
59
343
|
"""
|
|
60
344
|
加载方法论并构建向量索引以进行相似性搜索。
|
|
@@ -66,19 +350,83 @@ def load_methodology(user_input: str) -> str:
|
|
|
66
350
|
str: 相关的方法论提示,如果未找到方法论则返回空字符串
|
|
67
351
|
"""
|
|
68
352
|
from yaspin import yaspin
|
|
69
|
-
|
|
70
|
-
|
|
353
|
+
|
|
354
|
+
# 加载嵌入向量缓存
|
|
355
|
+
global _methodology_embeddings_cache
|
|
356
|
+
if not _methodology_embeddings_cache:
|
|
357
|
+
with yaspin(text="加载嵌入向量缓存...", color="yellow") as spinner:
|
|
358
|
+
_methodology_embeddings_cache = _load_embeddings_cache()
|
|
359
|
+
spinner.text = f"加载嵌入向量缓存完成 ({len(_methodology_embeddings_cache)} 个向量)"
|
|
360
|
+
spinner.ok("✅")
|
|
361
|
+
|
|
362
|
+
# 检查是否需要从旧格式迁移
|
|
363
|
+
with yaspin(text="检查方法论格式...", color="yellow") as spinner:
|
|
364
|
+
_migrate_from_old_format()
|
|
365
|
+
spinner.text = "检查方法论格式完成"
|
|
366
|
+
spinner.ok("✅")
|
|
367
|
+
|
|
368
|
+
# 获取方法论目录
|
|
369
|
+
methodology_dir = _get_methodology_directory()
|
|
370
|
+
if not os.path.exists(methodology_dir) or not glob.glob(os.path.join(methodology_dir, "*.json")):
|
|
71
371
|
return ""
|
|
72
372
|
|
|
73
373
|
try:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
374
|
+
# 获取文件的修改时间戳组合哈希,用于检测文件是否被修改
|
|
375
|
+
methodology_hash = _get_methodology_files_hash()
|
|
376
|
+
|
|
377
|
+
# 检查缓存的索引是否可用且方法论文件未被修改
|
|
378
|
+
global _methodology_index_cache
|
|
379
|
+
if _methodology_index_cache is None:
|
|
380
|
+
# 尝试从文件系统加载索引缓存
|
|
381
|
+
with yaspin(text="加载索引缓存...", color="yellow") as spinner:
|
|
382
|
+
_methodology_index_cache = _load_index_cache()
|
|
383
|
+
if _methodology_index_cache:
|
|
384
|
+
spinner.text = "加载索引缓存完成"
|
|
385
|
+
spinner.ok("✅")
|
|
386
|
+
else:
|
|
387
|
+
spinner.text = "没有可用的索引缓存"
|
|
388
|
+
spinner.fail("❌")
|
|
81
389
|
|
|
390
|
+
if _methodology_index_cache is not None:
|
|
391
|
+
cached_index, cached_data, cache_hash = _methodology_index_cache
|
|
392
|
+
if cache_hash == methodology_hash:
|
|
393
|
+
# 直接使用缓存的索引和数据
|
|
394
|
+
with yaspin(text="使用缓存的方法论索引...", color="yellow") as spinner:
|
|
395
|
+
methodology_index = cached_index
|
|
396
|
+
methodology_data = cached_data
|
|
397
|
+
spinner.text = "使用缓存的方法论索引完成"
|
|
398
|
+
spinner.ok("✅")
|
|
399
|
+
|
|
400
|
+
with yaspin(text="加载嵌入模型...", color="yellow") as spinner:
|
|
401
|
+
embedding_model = load_embedding_model()
|
|
402
|
+
spinner.text = "加载嵌入模型完成"
|
|
403
|
+
spinner.ok("✅")
|
|
404
|
+
|
|
405
|
+
with yaspin(text="执行搜索...", color="yellow") as spinner:
|
|
406
|
+
# 使用缓存构造输入文本的嵌入
|
|
407
|
+
query_embedding = _create_methodology_embedding(embedding_model, user_input)
|
|
408
|
+
k = min(3, len(methodology_data))
|
|
409
|
+
distances, indices = methodology_index.search(
|
|
410
|
+
query_embedding.reshape(1, -1), k
|
|
411
|
+
) # type: ignore
|
|
412
|
+
spinner.text = "执行搜索完成"
|
|
413
|
+
spinner.ok("✅")
|
|
414
|
+
|
|
415
|
+
with yaspin(text="处理搜索结果...", color="yellow") as spinner:
|
|
416
|
+
relevant_methodologies = {}
|
|
417
|
+
for dist, idx in zip(distances[0], indices[0]):
|
|
418
|
+
if idx >= 0:
|
|
419
|
+
similarity = 1.0 / (1.0 + float(dist))
|
|
420
|
+
methodology = methodology_data[idx]
|
|
421
|
+
if similarity >= 0.5:
|
|
422
|
+
relevant_methodologies[methodology["key"]] = methodology["value"]
|
|
423
|
+
spinner.text = "处理搜索结果完成"
|
|
424
|
+
spinner.ok("✅")
|
|
425
|
+
|
|
426
|
+
if relevant_methodologies:
|
|
427
|
+
return make_methodology_prompt(relevant_methodologies)
|
|
428
|
+
|
|
429
|
+
# 如果缓存无效,从头构建索引
|
|
82
430
|
with yaspin(text="初始化数据结构...", color="yellow") as spinner:
|
|
83
431
|
methodology_data: List[Dict[str, str]] = []
|
|
84
432
|
vectors: List[np.ndarray] = []
|
|
@@ -96,6 +444,11 @@ def load_methodology(user_input: str) -> str:
|
|
|
96
444
|
embedding_dimension = len(test_embedding)
|
|
97
445
|
spinner.text = "创建测试嵌入完成"
|
|
98
446
|
spinner.ok("✅")
|
|
447
|
+
|
|
448
|
+
with yaspin(text="加载方法论文件...", color="yellow") as spinner:
|
|
449
|
+
data = _load_all_methodologies()
|
|
450
|
+
spinner.text = "加载方法论文件完成"
|
|
451
|
+
spinner.ok("✅")
|
|
99
452
|
|
|
100
453
|
with yaspin(text="处理方法论数据...", color="yellow") as spinner:
|
|
101
454
|
for i, (key, value) in enumerate(data.items()):
|
|
@@ -115,12 +468,18 @@ def load_methodology(user_input: str) -> str:
|
|
|
115
468
|
hnsw_index.hnsw.efSearch = 16
|
|
116
469
|
methodology_index = faiss.IndexIDMap(hnsw_index)
|
|
117
470
|
methodology_index.add_with_ids(vectors_array, np.array(ids)) # type: ignore
|
|
471
|
+
# 缓存构建好的索引和数据以及时间戳哈希
|
|
472
|
+
_methodology_index_cache = (methodology_index, methodology_data, methodology_hash)
|
|
473
|
+
|
|
474
|
+
# 将索引和嵌入向量缓存保存到文件系统
|
|
475
|
+
_save_index_cache(methodology_index, methodology_data, methodology_hash)
|
|
476
|
+
|
|
118
477
|
spinner.text = "构建索引完成"
|
|
119
478
|
spinner.ok("✅")
|
|
120
479
|
|
|
121
480
|
with yaspin(text="执行搜索...", color="yellow") as spinner:
|
|
122
481
|
query_embedding = _create_methodology_embedding(embedding_model, user_input)
|
|
123
|
-
k = min(
|
|
482
|
+
k = min(10, len(methodology_data))
|
|
124
483
|
distances, indices = methodology_index.search(
|
|
125
484
|
query_embedding.reshape(1, -1), k
|
|
126
485
|
) # type: ignore
|
|
@@ -137,10 +496,20 @@ def load_methodology(user_input: str) -> str:
|
|
|
137
496
|
relevant_methodologies[methodology["key"]] = methodology["value"]
|
|
138
497
|
spinner.text = "处理搜索结果完成"
|
|
139
498
|
spinner.ok("✅")
|
|
140
|
-
|
|
499
|
+
|
|
500
|
+
# 保存嵌入向量缓存到文件系统
|
|
501
|
+
with yaspin(text="保存嵌入向量缓存...", color="yellow") as spinner:
|
|
502
|
+
if _save_embeddings_cache(_methodology_embeddings_cache):
|
|
503
|
+
spinner.text = f"保存嵌入向量缓存完成 ({len(_methodology_embeddings_cache)} 个向量)"
|
|
504
|
+
spinner.ok("✅")
|
|
505
|
+
else:
|
|
506
|
+
spinner.text = "保存嵌入向量缓存失败"
|
|
507
|
+
spinner.fail("❌")
|
|
141
508
|
|
|
142
509
|
if relevant_methodologies:
|
|
143
510
|
return make_methodology_prompt(relevant_methodologies)
|
|
144
|
-
|
|
511
|
+
|
|
512
|
+
return make_methodology_prompt(data)
|
|
145
513
|
except Exception as e:
|
|
514
|
+
PrettyOutput.print(f"加载方法论失败: {str(e)}", OutputType.ERROR)
|
|
146
515
|
return ""
|
jarvis/jarvis_utils/output.py
CHANGED
|
@@ -17,7 +17,7 @@ from rich.syntax import Syntax
|
|
|
17
17
|
from rich.style import Style as RichStyle
|
|
18
18
|
from pygments.lexers import guess_lexer
|
|
19
19
|
from pygments.util import ClassNotFound
|
|
20
|
-
from .globals import console, get_agent_list
|
|
20
|
+
from jarvis.jarvis_utils.globals import console, get_agent_list
|
|
21
21
|
class OutputType(Enum):
|
|
22
22
|
"""
|
|
23
23
|
输出类型枚举,用于分类和样式化不同类型的消息。
|
|
@@ -205,14 +205,16 @@ class PrettyOutput:
|
|
|
205
205
|
console.print(panel)
|
|
206
206
|
console.print()
|
|
207
207
|
@staticmethod
|
|
208
|
-
def print_stream(text: str):
|
|
208
|
+
def print_stream(text: str, is_thinking: bool = False):
|
|
209
209
|
"""
|
|
210
210
|
打印流式输出,不带换行符。
|
|
211
211
|
|
|
212
212
|
参数:
|
|
213
213
|
text: 要打印的文本
|
|
214
214
|
"""
|
|
215
|
-
style = RichStyle(color="bright_cyan",
|
|
215
|
+
style = RichStyle(color="bright_cyan", bold=True, frame=True, meta={"icon": "🤖"})
|
|
216
|
+
if is_thinking:
|
|
217
|
+
style = RichStyle(color="grey58", italic=True, frame=True, meta={"icon": "🤖"})
|
|
216
218
|
console.print(text, style=style, end="")
|
|
217
219
|
@staticmethod
|
|
218
220
|
def print_stream_end():
|
jarvis/jarvis_utils/utils.py
CHANGED
|
@@ -2,17 +2,19 @@ import os
|
|
|
2
2
|
import time
|
|
3
3
|
import hashlib
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Dict
|
|
5
|
+
from typing import Dict
|
|
6
6
|
import psutil
|
|
7
7
|
from jarvis.jarvis_utils.config import get_max_token_count
|
|
8
8
|
from jarvis.jarvis_utils.embedding import get_context_token_count
|
|
9
9
|
from jarvis.jarvis_utils.input import get_single_line_input
|
|
10
10
|
from jarvis.jarvis_utils.output import PrettyOutput, OutputType
|
|
11
11
|
def init_env():
|
|
12
|
-
"""
|
|
12
|
+
"""初始化环境变量从~/.jarvis/env文件
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
功能:
|
|
15
|
+
1. 创建不存在的.jarvis目录
|
|
16
|
+
2. 加载环境变量到os.environ
|
|
17
|
+
3. 处理文件读取异常
|
|
16
18
|
"""
|
|
17
19
|
jarvis_dir = Path.home() / ".jarvis"
|
|
18
20
|
env_file = jarvis_dir / "env"
|
|
@@ -34,6 +36,15 @@ def init_env():
|
|
|
34
36
|
except Exception as e:
|
|
35
37
|
PrettyOutput.print(f"警告: 读取 {env_file} 失败: {e}", OutputType.WARNING)
|
|
36
38
|
def while_success(func, sleep_time: float = 0.1):
|
|
39
|
+
"""循环执行函数直到成功
|
|
40
|
+
|
|
41
|
+
参数:
|
|
42
|
+
func -- 要执行的函数
|
|
43
|
+
sleep_time -- 每次失败后的等待时间(秒)
|
|
44
|
+
|
|
45
|
+
返回:
|
|
46
|
+
函数执行结果
|
|
47
|
+
"""
|
|
37
48
|
while True:
|
|
38
49
|
try:
|
|
39
50
|
return func()
|
|
@@ -87,10 +98,16 @@ def get_file_line_count(filename: str) -> int:
|
|
|
87
98
|
except Exception as e:
|
|
88
99
|
return 0
|
|
89
100
|
def init_gpu_config() -> Dict:
|
|
90
|
-
"""
|
|
101
|
+
"""初始化GPU配置
|
|
91
102
|
|
|
92
|
-
|
|
93
|
-
|
|
103
|
+
功能:
|
|
104
|
+
1. 检测CUDA可用性
|
|
105
|
+
2. 计算设备内存和共享内存
|
|
106
|
+
3. 设置CUDA内存分配策略
|
|
107
|
+
4. 处理异常情况
|
|
108
|
+
|
|
109
|
+
返回:
|
|
110
|
+
包含GPU配置信息的字典
|
|
94
111
|
"""
|
|
95
112
|
config = {
|
|
96
113
|
"has_gpu": False,
|
|
@@ -98,6 +115,8 @@ def init_gpu_config() -> Dict:
|
|
|
98
115
|
"device_memory": 0,
|
|
99
116
|
"memory_fraction": 0.8 # 默认使用80%的可用内存
|
|
100
117
|
}
|
|
118
|
+
|
|
119
|
+
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True"
|
|
101
120
|
|
|
102
121
|
try:
|
|
103
122
|
import torch
|
|
@@ -130,7 +149,17 @@ def init_gpu_config() -> Dict:
|
|
|
130
149
|
|
|
131
150
|
|
|
132
151
|
def is_long_context(files: list) -> bool:
|
|
133
|
-
"""
|
|
152
|
+
"""检查文件列表是否属于长上下文
|
|
153
|
+
|
|
154
|
+
判断标准:
|
|
155
|
+
当总token数超过最大上下文长度的80%时视为长上下文
|
|
156
|
+
|
|
157
|
+
参数:
|
|
158
|
+
files -- 要检查的文件路径列表
|
|
159
|
+
|
|
160
|
+
返回:
|
|
161
|
+
布尔值表示是否属于长上下文
|
|
162
|
+
"""
|
|
134
163
|
max_token_count = get_max_token_count()
|
|
135
164
|
threshold = max_token_count * 0.8
|
|
136
165
|
total_tokens = 0
|
|
@@ -148,3 +177,26 @@ def is_long_context(files: list) -> bool:
|
|
|
148
177
|
continue
|
|
149
178
|
|
|
150
179
|
return total_tokens > threshold
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def ot(tag_name: str) -> str:
|
|
183
|
+
"""生成HTML标签开始标记
|
|
184
|
+
|
|
185
|
+
参数:
|
|
186
|
+
tag_name -- HTML标签名称
|
|
187
|
+
|
|
188
|
+
返回:
|
|
189
|
+
格式化的开始标签字符串
|
|
190
|
+
"""
|
|
191
|
+
return f"<{tag_name}>"
|
|
192
|
+
|
|
193
|
+
def ct(tag_name: str) -> str:
|
|
194
|
+
"""生成HTML标签结束标记
|
|
195
|
+
|
|
196
|
+
参数:
|
|
197
|
+
tag_name -- HTML标签名称
|
|
198
|
+
|
|
199
|
+
返回:
|
|
200
|
+
格式化的结束标签字符串
|
|
201
|
+
"""
|
|
202
|
+
return f"</{tag_name}>"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: jarvis-ai-assistant
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.134
|
|
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
|
|
@@ -64,7 +64,8 @@ Requires-Dist: jedi>=0.17.2
|
|
|
64
64
|
Requires-Dist: psutil>=7.0.0
|
|
65
65
|
Requires-Dist: fastapi>=0.115.4
|
|
66
66
|
Requires-Dist: uvicorn>=0.33.0
|
|
67
|
-
Requires-Dist:
|
|
67
|
+
Requires-Dist: python-pptx>=1.0.0
|
|
68
|
+
Requires-Dist: pandas>=2.0.0
|
|
68
69
|
Provides-Extra: dev
|
|
69
70
|
Requires-Dist: pytest; extra == "dev"
|
|
70
71
|
Requires-Dist: black; extra == "dev"
|
|
@@ -133,6 +134,8 @@ jarvis-code-review --help
|
|
|
133
134
|
jarvis-dev --help
|
|
134
135
|
# 使用git squash的功能
|
|
135
136
|
jarvis-git-squash --help
|
|
137
|
+
# 使用代码库查询功能
|
|
138
|
+
jarvis-ask-codebase --help
|
|
136
139
|
```
|
|
137
140
|
|
|
138
141
|
---
|
|
@@ -146,15 +149,10 @@ jarvis-git-squash --help
|
|
|
146
149
|
| 核心配置 | `JARVIS_AUTO_COMPLETE` | false | 是否启用自动补全功能 |
|
|
147
150
|
| 核心配置 | `JARVIS_EXECUTE_TOOL_CONFIRM` | false | 执行工具前是否需要确认 |
|
|
148
151
|
| 核心配置 | `JARVIS_CONFIRM_BEFORE_APPLY_PATCH` | true | 应用补丁前是否需要确认 |
|
|
149
|
-
| 模型配置 | `
|
|
150
|
-
| 模型配置 | `
|
|
151
|
-
| 模型配置 | `JARVIS_MODEL` | kimi | 默认模型 |
|
|
152
|
-
| 模型配置 | `JARVIS_CODEGEN_PLATFORM` | JARVIS_PLATFORM | 代码生成任务使用的平台 |
|
|
153
|
-
| 模型配置 | `JARVIS_CODEGEN_MODEL` | JARVIS_MODEL | 代码生成任务使用的模型 |
|
|
152
|
+
| 模型配置 | `JARVIS_PLATFORM` | yuanbao | 默认AI平台 |
|
|
153
|
+
| 模型配置 | `JARVIS_MODEL` | deep_seek_v3 | 默认模型 |
|
|
154
154
|
| 模型配置 | `JARVIS_THINKING_PLATFORM` | JARVIS_PLATFORM | 思考任务使用的平台 |
|
|
155
155
|
| 模型配置 | `JARVIS_THINKING_MODEL` | JARVIS_MODEL | 思考任务使用的模型 |
|
|
156
|
-
| 模型配置 | `JARVIS_CHEAP_PLATFORM` | JARVIS_PLATFORM | 低成本任务使用的平台 |
|
|
157
|
-
| 模型配置 | `JARVIS_CHEAP_MODEL` | JARVIS_MODEL | 低成本任务使用的模型 |
|
|
158
156
|
| 方法论配置 | `JARVIS_USE_METHODOLOGY` | true | 是否启用方法论系统 |
|
|
159
157
|
| 方法论配置 | `JARVIS_RECORD_METHODOLOGY` | true | 是否记录方法论 |
|
|
160
158
|
| 方法论配置 | `JARVIS_NEED_SUMMARY` | true | 是否自动生成摘要 |
|
|
@@ -168,7 +166,7 @@ jarvis-git-squash --help
|
|
|
168
166
|
| read_code | 支持行号和范围的代码文件读取 |
|
|
169
167
|
| execute_shell | 执行系统命令并捕获输出 |
|
|
170
168
|
| execute_shell_script | 执行shell脚本文件 |
|
|
171
|
-
| ask_codebase |
|
|
169
|
+
| ask_codebase | 智能代码库查询和分析,用于定位功能所在文件和理解单点实现,适合查询特定功能位置和实现原理 |
|
|
172
170
|
| ask_user | 交互式用户输入收集 |
|
|
173
171
|
| file_operation | 基础文件操作(读取/写入/存在性检查) |
|
|
174
172
|
| git_commiter | 自动化git提交处理 |
|
|
@@ -178,8 +176,6 @@ jarvis-git-squash --help
|
|
|
178
176
|
| chdir | 更改工作目录 |
|
|
179
177
|
| create_code_agent | 创建新的代码代理 |
|
|
180
178
|
| create_sub_agent | 创建子代理 |
|
|
181
|
-
| lsp_find_definition | 查找符号定义 |
|
|
182
|
-
| lsp_find_references | 查找符号引用 |
|
|
183
179
|
| lsp_validate_edit | 验证代码编辑 |
|
|
184
180
|
| rag | 文档检索和问答 |
|
|
185
181
|
| select_code_files | 选择代码文件 |
|
|
@@ -254,10 +250,6 @@ class CustomPlatform(BasePlatform):
|
|
|
254
250
|
# 执行对话
|
|
255
251
|
pass
|
|
256
252
|
|
|
257
|
-
def upload_files(self, file_list: List[str]) -> List[Dict]:
|
|
258
|
-
# 上传文件
|
|
259
|
-
pass
|
|
260
|
-
|
|
261
253
|
def reset(self):
|
|
262
254
|
# 重置平台
|
|
263
255
|
pass
|