jarvis-ai-assistant 0.1.130__py3-none-any.whl → 0.1.132__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 +71 -38
- jarvis/jarvis_agent/builtin_input_handler.py +73 -0
- jarvis/{jarvis_code_agent → jarvis_agent}/file_input_handler.py +1 -1
- jarvis/jarvis_agent/main.py +1 -1
- jarvis/{jarvis_code_agent → jarvis_agent}/patch.py +77 -55
- jarvis/{jarvis_code_agent → jarvis_agent}/shell_input_handler.py +1 -2
- jarvis/jarvis_code_agent/code_agent.py +93 -88
- jarvis/jarvis_dev/main.py +335 -626
- jarvis/jarvis_git_squash/main.py +11 -32
- jarvis/jarvis_lsp/base.py +2 -26
- jarvis/jarvis_lsp/cpp.py +2 -14
- jarvis/jarvis_lsp/go.py +0 -13
- jarvis/jarvis_lsp/python.py +1 -30
- jarvis/jarvis_lsp/registry.py +10 -14
- jarvis/jarvis_lsp/rust.py +0 -12
- jarvis/jarvis_multi_agent/__init__.py +20 -29
- 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 +1 -1
- jarvis/jarvis_platform/yuanbao.py +264 -0
- jarvis/jarvis_platform_manager/main.py +3 -3
- jarvis/jarvis_rag/file_processors.py +138 -0
- jarvis/jarvis_rag/main.py +1305 -425
- jarvis/jarvis_tools/ask_codebase.py +227 -41
- jarvis/jarvis_tools/code_review.py +229 -166
- jarvis/jarvis_tools/create_code_agent.py +76 -72
- jarvis/jarvis_tools/create_sub_agent.py +32 -15
- jarvis/jarvis_tools/execute_python_script.py +58 -0
- jarvis/jarvis_tools/execute_shell.py +15 -28
- jarvis/jarvis_tools/execute_shell_script.py +2 -2
- jarvis/jarvis_tools/file_analyzer.py +271 -0
- jarvis/jarvis_tools/file_operation.py +3 -3
- jarvis/jarvis_tools/find_caller.py +213 -0
- jarvis/jarvis_tools/find_symbol.py +211 -0
- jarvis/jarvis_tools/function_analyzer.py +248 -0
- jarvis/jarvis_tools/git_commiter.py +89 -70
- jarvis/jarvis_tools/lsp_find_definition.py +83 -67
- jarvis/jarvis_tools/lsp_find_references.py +62 -46
- jarvis/jarvis_tools/lsp_get_diagnostics.py +90 -74
- jarvis/jarvis_tools/methodology.py +89 -48
- jarvis/jarvis_tools/project_analyzer.py +220 -0
- 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 +73 -30
- jarvis/jarvis_tools/tool_generator.py +7 -9
- jarvis/jarvis_utils/__init__.py +1 -0
- jarvis/jarvis_utils/config.py +67 -3
- jarvis/jarvis_utils/embedding.py +344 -45
- jarvis/jarvis_utils/git_utils.py +18 -2
- jarvis/jarvis_utils/input.py +7 -4
- jarvis/jarvis_utils/methodology.py +379 -7
- jarvis/jarvis_utils/output.py +5 -3
- jarvis/jarvis_utils/utils.py +62 -10
- {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/METADATA +3 -4
- jarvis_ai_assistant-0.1.132.dist-info/RECORD +82 -0
- {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/entry_points.txt +2 -0
- jarvis/jarvis_c2rust/c2rust.yaml +0 -734
- jarvis/jarvis_code_agent/builtin_input_handler.py +0 -43
- jarvis/jarvis_codebase/__init__.py +0 -0
- jarvis/jarvis_codebase/main.py +0 -1011
- jarvis/jarvis_tools/lsp_get_document_symbols.py +0 -87
- jarvis/jarvis_tools/lsp_prepare_rename.py +0 -130
- jarvis_ai_assistant-0.1.130.dist-info/RECORD +0 -79
- {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/top_level.txt +0 -0
|
@@ -9,12 +9,168 @@
|
|
|
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
|
from jarvis.jarvis_utils.config import dont_use_local_model
|
|
22
|
+
|
|
23
|
+
# 全局缓存,避免重复计算嵌入向量
|
|
24
|
+
_methodology_embeddings_cache = {}
|
|
25
|
+
_methodology_index_cache: Optional[Tuple[faiss.IndexIDMap, List[Dict[str, str]], str]] = None
|
|
26
|
+
|
|
27
|
+
def _get_cache_directory() -> str:
|
|
28
|
+
"""
|
|
29
|
+
获取缓存目录路径,如果不存在则创建
|
|
30
|
+
|
|
31
|
+
返回:
|
|
32
|
+
str: 缓存目录的路径
|
|
33
|
+
"""
|
|
34
|
+
cache_dir = os.path.expanduser("~/.jarvis/cache")
|
|
35
|
+
if not os.path.exists(cache_dir):
|
|
36
|
+
try:
|
|
37
|
+
os.makedirs(cache_dir, exist_ok=True)
|
|
38
|
+
except Exception as e:
|
|
39
|
+
PrettyOutput.print(f"创建缓存目录失败: {str(e)}", OutputType.ERROR)
|
|
40
|
+
return cache_dir
|
|
41
|
+
|
|
42
|
+
def _get_embeddings_cache_path() -> str:
|
|
43
|
+
"""
|
|
44
|
+
获取嵌入向量缓存文件的路径
|
|
45
|
+
|
|
46
|
+
返回:
|
|
47
|
+
str: 嵌入向量缓存文件的路径
|
|
48
|
+
"""
|
|
49
|
+
return os.path.join(_get_cache_directory(), "methodology_embeddings.pkl")
|
|
50
|
+
|
|
51
|
+
def _get_index_cache_path() -> str:
|
|
52
|
+
"""
|
|
53
|
+
获取索引缓存文件的路径
|
|
54
|
+
|
|
55
|
+
返回:
|
|
56
|
+
str: 索引缓存文件的路径
|
|
57
|
+
"""
|
|
58
|
+
return os.path.join(_get_cache_directory(), "methodology_index.faiss")
|
|
59
|
+
|
|
60
|
+
def _get_index_metadata_path() -> str:
|
|
61
|
+
"""
|
|
62
|
+
获取索引元数据文件的路径
|
|
63
|
+
|
|
64
|
+
返回:
|
|
65
|
+
str: 索引元数据文件的路径
|
|
66
|
+
"""
|
|
67
|
+
return os.path.join(_get_cache_directory(), "methodology_index_metadata.pkl")
|
|
68
|
+
|
|
69
|
+
def _load_embeddings_cache() -> Dict[int, np.ndarray]:
|
|
70
|
+
"""
|
|
71
|
+
从文件系统加载嵌入向量缓存
|
|
72
|
+
|
|
73
|
+
返回:
|
|
74
|
+
Dict[int, np.ndarray]: 嵌入向量缓存字典
|
|
75
|
+
"""
|
|
76
|
+
cache_path = _get_embeddings_cache_path()
|
|
77
|
+
if not os.path.exists(cache_path):
|
|
78
|
+
return {}
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
with open(cache_path, "rb") as f:
|
|
82
|
+
embeddings_cache = pickle.load(f)
|
|
83
|
+
return embeddings_cache
|
|
84
|
+
except Exception as e:
|
|
85
|
+
return {}
|
|
86
|
+
|
|
87
|
+
def _save_embeddings_cache(cache: Dict[int, np.ndarray]) -> bool:
|
|
88
|
+
"""
|
|
89
|
+
将嵌入向量缓存保存到文件系统
|
|
90
|
+
|
|
91
|
+
参数:
|
|
92
|
+
cache: 要保存的嵌入向量缓存字典
|
|
93
|
+
|
|
94
|
+
返回:
|
|
95
|
+
bool: 保存是否成功
|
|
96
|
+
"""
|
|
97
|
+
if not cache:
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
cache_path = _get_embeddings_cache_path()
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
with open(cache_path, "wb") as f:
|
|
104
|
+
pickle.dump(cache, f)
|
|
105
|
+
return True
|
|
106
|
+
except Exception as e:
|
|
107
|
+
PrettyOutput.print(f"保存嵌入向量缓存失败: {str(e)}", OutputType.WARNING)
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
def _load_index_cache() -> Optional[Tuple[faiss.IndexIDMap, List[Dict[str, str]], str]]:
|
|
111
|
+
"""
|
|
112
|
+
从文件系统加载索引缓存
|
|
113
|
+
|
|
114
|
+
返回:
|
|
115
|
+
Optional[Tuple[faiss.IndexIDMap, List[Dict[str, str]], str]]: 索引缓存元组
|
|
116
|
+
"""
|
|
117
|
+
index_path = _get_index_cache_path()
|
|
118
|
+
metadata_path = _get_index_metadata_path()
|
|
119
|
+
|
|
120
|
+
if not os.path.exists(index_path) or not os.path.exists(metadata_path):
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
# 加载索引
|
|
125
|
+
index = faiss.read_index(index_path)
|
|
126
|
+
|
|
127
|
+
# 加载元数据
|
|
128
|
+
with open(metadata_path, "rb") as f:
|
|
129
|
+
metadata = pickle.load(f)
|
|
130
|
+
|
|
131
|
+
methodology_data = metadata.get("methodology_data", [])
|
|
132
|
+
methodology_hash = metadata.get("methodology_hash", "")
|
|
133
|
+
|
|
134
|
+
if isinstance(index, faiss.IndexIDMap) and methodology_data and methodology_hash:
|
|
135
|
+
return index, methodology_data, methodology_hash
|
|
136
|
+
except Exception as e:
|
|
137
|
+
PrettyOutput.print(f"加载索引缓存失败: {str(e)}", OutputType.WARNING)
|
|
138
|
+
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
def _save_index_cache(index: faiss.IndexIDMap, methodology_data: List[Dict[str, str]], methodology_hash: str) -> bool:
|
|
142
|
+
"""
|
|
143
|
+
将索引缓存保存到文件系统
|
|
144
|
+
|
|
145
|
+
参数:
|
|
146
|
+
index: FAISS索引
|
|
147
|
+
methodology_data: 方法论数据列表
|
|
148
|
+
methodology_hash: 方法论文件哈希值
|
|
149
|
+
|
|
150
|
+
返回:
|
|
151
|
+
bool: 保存是否成功
|
|
152
|
+
"""
|
|
153
|
+
index_path = _get_index_cache_path()
|
|
154
|
+
metadata_path = _get_index_metadata_path()
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
# 保存索引
|
|
158
|
+
faiss.write_index(index, index_path)
|
|
159
|
+
|
|
160
|
+
# 保存元数据
|
|
161
|
+
metadata = {
|
|
162
|
+
"methodology_data": methodology_data,
|
|
163
|
+
"methodology_hash": methodology_hash
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
with open(metadata_path, "wb") as f:
|
|
167
|
+
pickle.dump(metadata, f)
|
|
168
|
+
|
|
169
|
+
return True
|
|
170
|
+
except Exception as e:
|
|
171
|
+
PrettyOutput.print(f"保存索引缓存失败: {str(e)}", OutputType.WARNING)
|
|
172
|
+
return False
|
|
173
|
+
|
|
18
174
|
def _create_methodology_embedding(embedding_model: Any, methodology_text: str) -> np.ndarray:
|
|
19
175
|
"""
|
|
20
176
|
为方法论文本创建嵌入向量。
|
|
@@ -27,6 +183,11 @@ def _create_methodology_embedding(embedding_model: Any, methodology_text: str) -
|
|
|
27
183
|
np.ndarray: 嵌入向量
|
|
28
184
|
"""
|
|
29
185
|
try:
|
|
186
|
+
# 检查缓存中是否已有此文本的嵌入向量
|
|
187
|
+
cache_key = hash(methodology_text)
|
|
188
|
+
if cache_key in _methodology_embeddings_cache:
|
|
189
|
+
return _methodology_embeddings_cache[cache_key]
|
|
190
|
+
|
|
30
191
|
# 截断长文本
|
|
31
192
|
max_length = 512
|
|
32
193
|
text = ' '.join(methodology_text.split()[:max_length])
|
|
@@ -36,10 +197,16 @@ def _create_methodology_embedding(embedding_model: Any, methodology_text: str) -
|
|
|
36
197
|
convert_to_tensor=True,
|
|
37
198
|
normalize_embeddings=True)
|
|
38
199
|
vector = np.array(embedding.cpu().numpy(), dtype=np.float32)
|
|
39
|
-
|
|
200
|
+
result = vector[0] # 返回第一个向量,因为我们只编码了一个文本
|
|
201
|
+
|
|
202
|
+
# 缓存嵌入向量以便后续使用
|
|
203
|
+
_methodology_embeddings_cache[cache_key] = result
|
|
204
|
+
|
|
205
|
+
return result
|
|
40
206
|
except Exception as e:
|
|
41
207
|
PrettyOutput.print(f"创建方法论嵌入向量失败: {str(e)}", OutputType.ERROR)
|
|
42
208
|
return np.zeros(1536, dtype=np.float32)
|
|
209
|
+
|
|
43
210
|
def make_methodology_prompt(data: Dict[str, str]) -> str:
|
|
44
211
|
"""
|
|
45
212
|
从方法论数据生成格式化提示
|
|
@@ -55,6 +222,124 @@ def make_methodology_prompt(data: Dict[str, str]) -> str:
|
|
|
55
222
|
ret += f"问题: {key}\n方法论: {value}\n"
|
|
56
223
|
return ret
|
|
57
224
|
|
|
225
|
+
def _get_methodology_directory() -> str:
|
|
226
|
+
"""
|
|
227
|
+
获取方法论目录路径,如果不存在则创建
|
|
228
|
+
|
|
229
|
+
返回:
|
|
230
|
+
str: 方法论目录的路径
|
|
231
|
+
"""
|
|
232
|
+
methodology_dir = os.path.expanduser("~/.jarvis/methodologies")
|
|
233
|
+
if not os.path.exists(methodology_dir):
|
|
234
|
+
try:
|
|
235
|
+
os.makedirs(methodology_dir, exist_ok=True)
|
|
236
|
+
except Exception as e:
|
|
237
|
+
PrettyOutput.print(f"创建方法论目录失败: {str(e)}", OutputType.ERROR)
|
|
238
|
+
return methodology_dir
|
|
239
|
+
|
|
240
|
+
def _get_methodology_files_hash() -> str:
|
|
241
|
+
"""
|
|
242
|
+
计算所有方法论文件的组合哈希值,用于检测文件变化
|
|
243
|
+
|
|
244
|
+
返回:
|
|
245
|
+
str: 所有方法论文件的组合哈希值
|
|
246
|
+
"""
|
|
247
|
+
methodology_dir = _get_methodology_directory()
|
|
248
|
+
if not os.path.exists(methodology_dir):
|
|
249
|
+
return ""
|
|
250
|
+
|
|
251
|
+
# 获取所有方法论文件的路径和修改时间
|
|
252
|
+
files_data = []
|
|
253
|
+
for filepath in glob.glob(os.path.join(methodology_dir, "*.json")):
|
|
254
|
+
mtime = os.path.getmtime(filepath)
|
|
255
|
+
files_data.append((filepath, mtime))
|
|
256
|
+
|
|
257
|
+
# 按路径排序,保证哈希值的一致性
|
|
258
|
+
files_data.sort(key=lambda x: x[0])
|
|
259
|
+
|
|
260
|
+
# 计算组合哈希值
|
|
261
|
+
if not files_data:
|
|
262
|
+
return ""
|
|
263
|
+
|
|
264
|
+
hasher = hashlib.md5()
|
|
265
|
+
for filepath, mtime in files_data:
|
|
266
|
+
hasher.update(f"{filepath}:{mtime}".encode("utf-8"))
|
|
267
|
+
|
|
268
|
+
return hasher.hexdigest()
|
|
269
|
+
|
|
270
|
+
def _load_all_methodologies() -> Dict[str, str]:
|
|
271
|
+
"""
|
|
272
|
+
加载所有方法论文件
|
|
273
|
+
|
|
274
|
+
返回:
|
|
275
|
+
Dict[str, str]: 方法论字典,键为问题类型,值为方法论内容
|
|
276
|
+
"""
|
|
277
|
+
methodology_dir = _get_methodology_directory()
|
|
278
|
+
all_methodologies = {}
|
|
279
|
+
|
|
280
|
+
if not os.path.exists(methodology_dir):
|
|
281
|
+
return all_methodologies
|
|
282
|
+
|
|
283
|
+
for filepath in glob.glob(os.path.join(methodology_dir, "*.json")):
|
|
284
|
+
try:
|
|
285
|
+
with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
|
|
286
|
+
methodology = json.load(f)
|
|
287
|
+
problem_type = methodology.get("problem_type", "")
|
|
288
|
+
content = methodology.get("content", "")
|
|
289
|
+
if problem_type and content:
|
|
290
|
+
all_methodologies[problem_type] = content
|
|
291
|
+
except Exception as e:
|
|
292
|
+
filename = os.path.basename(filepath)
|
|
293
|
+
PrettyOutput.print(f"加载方法论文件 {filename} 失败: {str(e)}", OutputType.WARNING)
|
|
294
|
+
|
|
295
|
+
return all_methodologies
|
|
296
|
+
|
|
297
|
+
def _migrate_from_old_format():
|
|
298
|
+
"""
|
|
299
|
+
从旧的单文件格式迁移到新的多文件格式
|
|
300
|
+
"""
|
|
301
|
+
old_format_file = os.path.expanduser("~/.jarvis/methodology")
|
|
302
|
+
if not os.path.exists(old_format_file):
|
|
303
|
+
return
|
|
304
|
+
|
|
305
|
+
try:
|
|
306
|
+
# 加载旧格式文件
|
|
307
|
+
with open(old_format_file, "r", encoding="utf-8", errors="ignore") as f:
|
|
308
|
+
old_data = yaml.safe_load(f) or {}
|
|
309
|
+
|
|
310
|
+
if not old_data:
|
|
311
|
+
return
|
|
312
|
+
|
|
313
|
+
# 创建新目录
|
|
314
|
+
methodology_dir = _get_methodology_directory()
|
|
315
|
+
|
|
316
|
+
# 迁移每个方法论
|
|
317
|
+
migrated_count = 0
|
|
318
|
+
for problem_type, content in old_data.items():
|
|
319
|
+
# 为每个方法论创建文件名(使用问题类型的MD5哈希作为文件名)
|
|
320
|
+
safe_filename = hashlib.md5(problem_type.encode('utf-8')).hexdigest()
|
|
321
|
+
file_path = os.path.join(methodology_dir, f"{safe_filename}.json")
|
|
322
|
+
|
|
323
|
+
# 保存为新格式
|
|
324
|
+
with open(file_path, "w", encoding="utf-8", errors="ignore") as f:
|
|
325
|
+
json.dump({
|
|
326
|
+
"problem_type": problem_type,
|
|
327
|
+
"content": content
|
|
328
|
+
}, f, ensure_ascii=False, indent=2)
|
|
329
|
+
|
|
330
|
+
migrated_count += 1
|
|
331
|
+
|
|
332
|
+
if migrated_count > 0:
|
|
333
|
+
# 备份旧文件
|
|
334
|
+
backup_path = old_format_file + ".bak"
|
|
335
|
+
try:
|
|
336
|
+
os.rename(old_format_file, backup_path)
|
|
337
|
+
PrettyOutput.print(f"已成功迁移 {migrated_count} 个方法论,旧文件已备份为 {backup_path}", OutputType.INFO)
|
|
338
|
+
except Exception as e:
|
|
339
|
+
PrettyOutput.print(f"备份旧文件失败,但已完成迁移: {str(e)}", OutputType.WARNING)
|
|
340
|
+
except Exception as e:
|
|
341
|
+
PrettyOutput.print(f"迁移方法论失败: {str(e)}", OutputType.ERROR)
|
|
342
|
+
|
|
58
343
|
def load_methodology(user_input: str) -> str:
|
|
59
344
|
"""
|
|
60
345
|
加载方法论并构建向量索引以进行相似性搜索。
|
|
@@ -66,19 +351,91 @@ def load_methodology(user_input: str) -> str:
|
|
|
66
351
|
str: 相关的方法论提示,如果未找到方法论则返回空字符串
|
|
67
352
|
"""
|
|
68
353
|
from yaspin import yaspin
|
|
69
|
-
|
|
70
|
-
|
|
354
|
+
|
|
355
|
+
# 加载嵌入向量缓存
|
|
356
|
+
global _methodology_embeddings_cache
|
|
357
|
+
if not _methodology_embeddings_cache:
|
|
358
|
+
with yaspin(text="加载嵌入向量缓存...", color="yellow") as spinner:
|
|
359
|
+
_methodology_embeddings_cache = _load_embeddings_cache()
|
|
360
|
+
spinner.text = f"加载嵌入向量缓存完成 ({len(_methodology_embeddings_cache)} 个向量)"
|
|
361
|
+
spinner.ok("✅")
|
|
362
|
+
|
|
363
|
+
# 检查是否需要从旧格式迁移
|
|
364
|
+
with yaspin(text="检查方法论格式...", color="yellow") as spinner:
|
|
365
|
+
_migrate_from_old_format()
|
|
366
|
+
spinner.text = "检查方法论格式完成"
|
|
367
|
+
spinner.ok("✅")
|
|
368
|
+
|
|
369
|
+
# 获取方法论目录
|
|
370
|
+
methodology_dir = _get_methodology_directory()
|
|
371
|
+
if not os.path.exists(methodology_dir) or not glob.glob(os.path.join(methodology_dir, "*.json")):
|
|
71
372
|
return ""
|
|
72
373
|
|
|
73
374
|
try:
|
|
375
|
+
# 获取文件的修改时间戳组合哈希,用于检测文件是否被修改
|
|
376
|
+
methodology_hash = _get_methodology_files_hash()
|
|
377
|
+
|
|
74
378
|
with yaspin(text="加载方法论文件...", color="yellow") as spinner:
|
|
75
|
-
|
|
76
|
-
data = yaml.safe_load(f)
|
|
379
|
+
data = _load_all_methodologies()
|
|
77
380
|
if dont_use_local_model():
|
|
78
381
|
spinner.text = "加载方法论文件完成"
|
|
79
382
|
spinner.ok("✅")
|
|
80
383
|
return make_methodology_prompt(data)
|
|
81
384
|
|
|
385
|
+
# 检查缓存的索引是否可用且方法论文件未被修改
|
|
386
|
+
global _methodology_index_cache
|
|
387
|
+
if _methodology_index_cache is None:
|
|
388
|
+
# 尝试从文件系统加载索引缓存
|
|
389
|
+
with yaspin(text="加载索引缓存...", color="yellow") as spinner:
|
|
390
|
+
_methodology_index_cache = _load_index_cache()
|
|
391
|
+
if _methodology_index_cache:
|
|
392
|
+
spinner.text = "加载索引缓存完成"
|
|
393
|
+
spinner.ok("✅")
|
|
394
|
+
else:
|
|
395
|
+
spinner.text = "没有可用的索引缓存"
|
|
396
|
+
spinner.fail("❌")
|
|
397
|
+
|
|
398
|
+
if _methodology_index_cache is not None:
|
|
399
|
+
cached_index, cached_data, cache_hash = _methodology_index_cache
|
|
400
|
+
if cache_hash == methodology_hash:
|
|
401
|
+
# 直接使用缓存的索引和数据
|
|
402
|
+
with yaspin(text="使用缓存的方法论索引...", color="yellow") as spinner:
|
|
403
|
+
methodology_index = cached_index
|
|
404
|
+
methodology_data = cached_data
|
|
405
|
+
spinner.text = "使用缓存的方法论索引完成"
|
|
406
|
+
spinner.ok("✅")
|
|
407
|
+
|
|
408
|
+
with yaspin(text="加载嵌入模型...", color="yellow") as spinner:
|
|
409
|
+
embedding_model = load_embedding_model()
|
|
410
|
+
spinner.text = "加载嵌入模型完成"
|
|
411
|
+
spinner.ok("✅")
|
|
412
|
+
|
|
413
|
+
with yaspin(text="执行搜索...", color="yellow") as spinner:
|
|
414
|
+
# 使用缓存构造输入文本的嵌入
|
|
415
|
+
query_embedding = _create_methodology_embedding(embedding_model, user_input)
|
|
416
|
+
k = min(3, len(methodology_data))
|
|
417
|
+
distances, indices = methodology_index.search(
|
|
418
|
+
query_embedding.reshape(1, -1), k
|
|
419
|
+
) # type: ignore
|
|
420
|
+
spinner.text = "执行搜索完成"
|
|
421
|
+
spinner.ok("✅")
|
|
422
|
+
|
|
423
|
+
with yaspin(text="处理搜索结果...", color="yellow") as spinner:
|
|
424
|
+
relevant_methodologies = {}
|
|
425
|
+
for dist, idx in zip(distances[0], indices[0]):
|
|
426
|
+
if idx >= 0:
|
|
427
|
+
similarity = 1.0 / (1.0 + float(dist))
|
|
428
|
+
methodology = methodology_data[idx]
|
|
429
|
+
if similarity >= 0.5:
|
|
430
|
+
relevant_methodologies[methodology["key"]] = methodology["value"]
|
|
431
|
+
spinner.text = "处理搜索结果完成"
|
|
432
|
+
spinner.ok("✅")
|
|
433
|
+
|
|
434
|
+
if relevant_methodologies:
|
|
435
|
+
return make_methodology_prompt(relevant_methodologies)
|
|
436
|
+
return make_methodology_prompt(data)
|
|
437
|
+
|
|
438
|
+
# 如果缓存无效,从头构建索引
|
|
82
439
|
with yaspin(text="初始化数据结构...", color="yellow") as spinner:
|
|
83
440
|
methodology_data: List[Dict[str, str]] = []
|
|
84
441
|
vectors: List[np.ndarray] = []
|
|
@@ -115,6 +472,12 @@ def load_methodology(user_input: str) -> str:
|
|
|
115
472
|
hnsw_index.hnsw.efSearch = 16
|
|
116
473
|
methodology_index = faiss.IndexIDMap(hnsw_index)
|
|
117
474
|
methodology_index.add_with_ids(vectors_array, np.array(ids)) # type: ignore
|
|
475
|
+
# 缓存构建好的索引和数据以及时间戳哈希
|
|
476
|
+
_methodology_index_cache = (methodology_index, methodology_data, methodology_hash)
|
|
477
|
+
|
|
478
|
+
# 将索引和嵌入向量缓存保存到文件系统
|
|
479
|
+
_save_index_cache(methodology_index, methodology_data, methodology_hash)
|
|
480
|
+
|
|
118
481
|
spinner.text = "构建索引完成"
|
|
119
482
|
spinner.ok("✅")
|
|
120
483
|
|
|
@@ -137,10 +500,19 @@ def load_methodology(user_input: str) -> str:
|
|
|
137
500
|
relevant_methodologies[methodology["key"]] = methodology["value"]
|
|
138
501
|
spinner.text = "处理搜索结果完成"
|
|
139
502
|
spinner.ok("✅")
|
|
140
|
-
|
|
503
|
+
|
|
504
|
+
# 保存嵌入向量缓存到文件系统
|
|
505
|
+
with yaspin(text="保存嵌入向量缓存...", color="yellow") as spinner:
|
|
506
|
+
if _save_embeddings_cache(_methodology_embeddings_cache):
|
|
507
|
+
spinner.text = f"保存嵌入向量缓存完成 ({len(_methodology_embeddings_cache)} 个向量)"
|
|
508
|
+
spinner.ok("✅")
|
|
509
|
+
else:
|
|
510
|
+
spinner.text = "保存嵌入向量缓存失败"
|
|
511
|
+
spinner.fail("❌")
|
|
141
512
|
|
|
142
513
|
if relevant_methodologies:
|
|
143
514
|
return make_methodology_prompt(relevant_methodologies)
|
|
144
515
|
return make_methodology_prompt(data)
|
|
145
516
|
except Exception as e:
|
|
517
|
+
PrettyOutput.print(f"加载方法论失败: {str(e)}", OutputType.ERROR)
|
|
146
518
|
return ""
|
jarvis/jarvis_utils/output.py
CHANGED
|
@@ -186,7 +186,7 @@ class PrettyOutput:
|
|
|
186
186
|
)
|
|
187
187
|
console.print()
|
|
188
188
|
console.print(panel)
|
|
189
|
-
if traceback
|
|
189
|
+
if traceback:
|
|
190
190
|
console.print_exception()
|
|
191
191
|
@staticmethod
|
|
192
192
|
def section(title: str, output_type: OutputType = OutputType.INFO):
|
|
@@ -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
|
@@ -9,10 +9,12 @@ 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"
|
|
@@ -22,7 +24,7 @@ def init_env():
|
|
|
22
24
|
jarvis_dir.mkdir(parents=True)
|
|
23
25
|
if env_file.exists():
|
|
24
26
|
try:
|
|
25
|
-
with open(env_file, "r", encoding="utf-8") as f:
|
|
27
|
+
with open(env_file, "r", encoding="utf-8", errors="ignore") as f:
|
|
26
28
|
for line in f:
|
|
27
29
|
line = line.strip()
|
|
28
30
|
if line and not line.startswith(("#", ";")):
|
|
@@ -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()
|
|
@@ -83,14 +94,20 @@ def get_file_line_count(filename: str) -> int:
|
|
|
83
94
|
int: Number of lines in the file, 0 if file cannot be read
|
|
84
95
|
"""
|
|
85
96
|
try:
|
|
86
|
-
return len(open(filename, "r", encoding="utf-8").readlines())
|
|
97
|
+
return len(open(filename, "r", encoding="utf-8", errors="ignore").readlines())
|
|
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,14 +149,24 @@ 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
|
|
137
166
|
|
|
138
167
|
for file_path in files:
|
|
139
168
|
try:
|
|
140
|
-
with open(file_path, 'r', encoding='utf-8') as f:
|
|
169
|
+
with open(file_path, 'r', encoding='utf-8', errors="ignore") as f:
|
|
141
170
|
content = f.read()
|
|
142
171
|
total_tokens += get_context_token_count(content)
|
|
143
172
|
|
|
@@ -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.132
|
|
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,6 +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: python-pptx>=1.0.0
|
|
68
|
+
Requires-Dist: pandas>=2.0.0
|
|
67
69
|
Provides-Extra: dev
|
|
68
70
|
Requires-Dist: pytest; extra == "dev"
|
|
69
71
|
Requires-Dist: black; extra == "dev"
|
|
@@ -179,9 +181,6 @@ jarvis-git-squash --help
|
|
|
179
181
|
| create_sub_agent | 创建子代理 |
|
|
180
182
|
| lsp_find_definition | 查找符号定义 |
|
|
181
183
|
| lsp_find_references | 查找符号引用 |
|
|
182
|
-
| lsp_get_diagnostics | 获取代码诊断信息 |
|
|
183
|
-
| lsp_get_document_symbols | 获取文档符号 |
|
|
184
|
-
| lsp_prepare_rename | 准备符号重命名 |
|
|
185
184
|
| lsp_validate_edit | 验证代码编辑 |
|
|
186
185
|
| rag | 文档检索和问答 |
|
|
187
186
|
| select_code_files | 选择代码文件 |
|