jarvis-ai-assistant 0.1.110__py3-none-any.whl → 0.1.112__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/agent.py +51 -39
- jarvis/jarvis_code_agent/code_agent.py +89 -53
- jarvis/jarvis_code_agent/file_select.py +20 -20
- jarvis/jarvis_code_agent/patch.py +20 -11
- jarvis/jarvis_code_agent/relevant_files.py +68 -16
- jarvis/jarvis_codebase/main.py +82 -88
- jarvis/jarvis_lsp/cpp.py +1 -1
- jarvis/jarvis_lsp/go.py +1 -1
- jarvis/jarvis_lsp/python.py +0 -2
- jarvis/jarvis_lsp/registry.py +13 -13
- jarvis/jarvis_lsp/rust.py +1 -1
- jarvis/jarvis_platform/ai8.py +14 -14
- jarvis/jarvis_platform/base.py +1 -1
- jarvis/jarvis_platform/kimi.py +17 -17
- jarvis/jarvis_platform/ollama.py +14 -14
- jarvis/jarvis_platform/openai.py +8 -8
- jarvis/jarvis_platform/oyi.py +19 -19
- jarvis/jarvis_platform/registry.py +6 -6
- jarvis/jarvis_platform_manager/main.py +17 -17
- jarvis/jarvis_rag/main.py +25 -25
- jarvis/jarvis_smart_shell/main.py +6 -6
- jarvis/jarvis_tools/ask_codebase.py +4 -4
- jarvis/jarvis_tools/ask_user.py +2 -2
- jarvis/jarvis_tools/create_code_agent.py +8 -8
- jarvis/jarvis_tools/create_sub_agent.py +2 -2
- jarvis/jarvis_tools/execute_shell.py +2 -2
- jarvis/jarvis_tools/file_operation.py +1 -1
- jarvis/jarvis_tools/git_commiter.py +4 -6
- jarvis/jarvis_tools/methodology.py +3 -3
- jarvis/jarvis_tools/rag.py +3 -3
- jarvis/jarvis_tools/read_code.py +4 -3
- jarvis/jarvis_tools/read_webpage.py +19 -6
- jarvis/jarvis_tools/registry.py +11 -11
- jarvis/jarvis_tools/search.py +88 -27
- jarvis/jarvis_tools/select_code_files.py +1 -1
- jarvis/jarvis_tools/tool_generator.py +182 -0
- jarvis/utils/date_utils.py +19 -0
- jarvis/utils.py +31 -25
- jarvis_ai_assistant-0.1.112.dist-info/METADATA +460 -0
- jarvis_ai_assistant-0.1.112.dist-info/RECORD +64 -0
- jarvis_ai_assistant-0.1.110.dist-info/METADATA +0 -462
- jarvis_ai_assistant-0.1.110.dist-info/RECORD +0 -62
- {jarvis_ai_assistant-0.1.110.dist-info → jarvis_ai_assistant-0.1.112.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.110.dist-info → jarvis_ai_assistant-0.1.112.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.110.dist-info → jarvis_ai_assistant-0.1.112.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.110.dist-info → jarvis_ai_assistant-0.1.112.dist-info}/top_level.txt +0 -0
jarvis/jarvis_codebase/main.py
CHANGED
|
@@ -37,9 +37,9 @@ class CodeBase:
|
|
|
37
37
|
self.embedding_model.encode([test_text],
|
|
38
38
|
convert_to_tensor=True,
|
|
39
39
|
normalize_embeddings=True)
|
|
40
|
-
PrettyOutput.print("
|
|
40
|
+
PrettyOutput.print("模型加载成功", output_type=OutputType.SUCCESS)
|
|
41
41
|
except Exception as e:
|
|
42
|
-
PrettyOutput.print(f"
|
|
42
|
+
PrettyOutput.print(f"加载模型失败: {str(e)}", output_type=OutputType.ERROR)
|
|
43
43
|
raise
|
|
44
44
|
|
|
45
45
|
self.vector_dim = self.embedding_model.get_sentence_embedding_dimension()
|
|
@@ -71,7 +71,7 @@ class CodeBase:
|
|
|
71
71
|
if self.thread_count > 1:
|
|
72
72
|
model.set_suppress_output(True)
|
|
73
73
|
else:
|
|
74
|
-
PrettyOutput.print(f"
|
|
74
|
+
PrettyOutput.print(f"为 {file_path} 生成描述 ...", output_type=OutputType.PROGRESS)
|
|
75
75
|
prompt = f"""Please analyze the following code file and generate a detailed description. The description should include:
|
|
76
76
|
1. Overall file functionality description
|
|
77
77
|
2. description for each global variable, function, type definition, class, method, and other code elements
|
|
@@ -129,7 +129,7 @@ Code content:
|
|
|
129
129
|
self.file_paths.append(file_path)
|
|
130
130
|
vectors.append(cache_data["vector"])
|
|
131
131
|
except Exception as e:
|
|
132
|
-
PrettyOutput.print(f"
|
|
132
|
+
PrettyOutput.print(f"加载缓存文件 {cache_file} 失败: {str(e)}",
|
|
133
133
|
output_type=OutputType.WARNING)
|
|
134
134
|
continue
|
|
135
135
|
|
|
@@ -142,14 +142,14 @@ Code content:
|
|
|
142
142
|
self.index = faiss.IndexIDMap(hnsw_index)
|
|
143
143
|
self.index.add_with_ids(vectors_array, np.array(range(len(vectors)))) # type: ignore
|
|
144
144
|
|
|
145
|
-
PrettyOutput.print(f"
|
|
145
|
+
PrettyOutput.print(f"加载 {len(self.vector_cache)} 个向量缓存并重建索引",
|
|
146
146
|
output_type=OutputType.INFO)
|
|
147
147
|
else:
|
|
148
148
|
self.index = None
|
|
149
|
-
PrettyOutput.print("
|
|
149
|
+
PrettyOutput.print("没有找到有效的缓存文件", output_type=OutputType.WARNING)
|
|
150
150
|
|
|
151
151
|
except Exception as e:
|
|
152
|
-
PrettyOutput.print(f"
|
|
152
|
+
PrettyOutput.print(f"加载缓存目录失败: {str(e)}",
|
|
153
153
|
output_type=OutputType.WARNING)
|
|
154
154
|
self.vector_cache = {}
|
|
155
155
|
self.file_paths = []
|
|
@@ -161,7 +161,7 @@ Code content:
|
|
|
161
161
|
with open(file_path, "rb") as f:
|
|
162
162
|
file_md5 = hashlib.md5(f.read()).hexdigest()
|
|
163
163
|
except Exception as e:
|
|
164
|
-
PrettyOutput.print(f"
|
|
164
|
+
PrettyOutput.print(f"计算 {file_path} 的MD5失败: {str(e)}",
|
|
165
165
|
output_type=OutputType.ERROR)
|
|
166
166
|
file_md5 = ""
|
|
167
167
|
|
|
@@ -182,7 +182,7 @@ Code content:
|
|
|
182
182
|
with lzma.open(cache_path, 'wb') as f:
|
|
183
183
|
pickle.dump(cache_data, f, protocol=pickle.HIGHEST_PROTOCOL)
|
|
184
184
|
except Exception as e:
|
|
185
|
-
PrettyOutput.print(f"
|
|
185
|
+
PrettyOutput.print(f"保存 {file_path} 的缓存失败: {str(e)}",
|
|
186
186
|
output_type=OutputType.ERROR)
|
|
187
187
|
|
|
188
188
|
def get_cached_vector(self, file_path: str, description: str) -> Optional[np.ndarray]:
|
|
@@ -195,7 +195,7 @@ Code content:
|
|
|
195
195
|
with open(file_path, "rb") as f:
|
|
196
196
|
current_md5 = hashlib.md5(f.read()).hexdigest()
|
|
197
197
|
except Exception as e:
|
|
198
|
-
PrettyOutput.print(f"
|
|
198
|
+
PrettyOutput.print(f"计算 {file_path} 的MD5失败: {str(e)}",
|
|
199
199
|
output_type=OutputType.ERROR)
|
|
200
200
|
return None
|
|
201
201
|
|
|
@@ -232,7 +232,7 @@ Content: {content}
|
|
|
232
232
|
self.cache_vector(file_path, vector, description)
|
|
233
233
|
return vector
|
|
234
234
|
except Exception as e:
|
|
235
|
-
PrettyOutput.print(f"
|
|
235
|
+
PrettyOutput.print(f"向量化 {file_path} 失败: {str(e)}",
|
|
236
236
|
output_type=OutputType.ERROR)
|
|
237
237
|
return np.zeros(self.vector_dim, dtype=np.float32) # type: ignore
|
|
238
238
|
|
|
@@ -257,7 +257,7 @@ Content: {content}
|
|
|
257
257
|
return bool(files_to_delete)
|
|
258
258
|
|
|
259
259
|
except Exception as e:
|
|
260
|
-
PrettyOutput.print(f"
|
|
260
|
+
PrettyOutput.print(f"清理缓存失败: {str(e)}",
|
|
261
261
|
output_type=OutputType.ERROR)
|
|
262
262
|
return False
|
|
263
263
|
|
|
@@ -293,7 +293,7 @@ Content: {content}
|
|
|
293
293
|
return file_path
|
|
294
294
|
|
|
295
295
|
except Exception as e:
|
|
296
|
-
PrettyOutput.print(f"
|
|
296
|
+
PrettyOutput.print(f"处理 {file_path} 失败: {str(e)}",
|
|
297
297
|
output_type=OutputType.ERROR)
|
|
298
298
|
return None
|
|
299
299
|
|
|
@@ -318,13 +318,13 @@ Content: {content}
|
|
|
318
318
|
|
|
319
319
|
for i, (file_path, data) in enumerate(self.vector_cache.items()):
|
|
320
320
|
if "vector" not in data:
|
|
321
|
-
PrettyOutput.print(f"
|
|
321
|
+
PrettyOutput.print(f"无效的缓存数据 {file_path}: 缺少向量",
|
|
322
322
|
output_type=OutputType.WARNING)
|
|
323
323
|
continue
|
|
324
324
|
|
|
325
325
|
vector = data["vector"]
|
|
326
326
|
if not isinstance(vector, np.ndarray):
|
|
327
|
-
PrettyOutput.print(f"
|
|
327
|
+
PrettyOutput.print(f"无效的向量类型 {file_path}: {type(vector)}",
|
|
328
328
|
output_type=OutputType.WARNING)
|
|
329
329
|
continue
|
|
330
330
|
|
|
@@ -335,26 +335,26 @@ Content: {content}
|
|
|
335
335
|
if vectors:
|
|
336
336
|
vectors = np.vstack(vectors)
|
|
337
337
|
if len(vectors) != len(ids):
|
|
338
|
-
PrettyOutput.print(f"
|
|
338
|
+
PrettyOutput.print(f"向量数量不匹配: {len(vectors)} 个向量 vs {len(ids)} 个ID",
|
|
339
339
|
output_type=OutputType.ERROR)
|
|
340
340
|
self.index = None
|
|
341
341
|
return
|
|
342
342
|
|
|
343
343
|
try:
|
|
344
344
|
self.index.add_with_ids(vectors, np.array(ids)) # type: ignore
|
|
345
|
-
PrettyOutput.print(f"
|
|
345
|
+
PrettyOutput.print(f"成功构建包含 {len(vectors)} 个向量的索引",
|
|
346
346
|
output_type=OutputType.SUCCESS)
|
|
347
347
|
except Exception as e:
|
|
348
|
-
PrettyOutput.print(f"
|
|
348
|
+
PrettyOutput.print(f"添加向量到索引失败: {str(e)}",
|
|
349
349
|
output_type=OutputType.ERROR)
|
|
350
350
|
self.index = None
|
|
351
351
|
else:
|
|
352
|
-
PrettyOutput.print("
|
|
352
|
+
PrettyOutput.print("没有找到有效的向量, 索引未构建",
|
|
353
353
|
output_type=OutputType.WARNING)
|
|
354
354
|
self.index = None
|
|
355
355
|
|
|
356
356
|
except Exception as e:
|
|
357
|
-
PrettyOutput.print(f"
|
|
357
|
+
PrettyOutput.print(f"构建索引失败: {str(e)}",
|
|
358
358
|
output_type=OutputType.ERROR)
|
|
359
359
|
self.index = None
|
|
360
360
|
|
|
@@ -379,20 +379,20 @@ Content: {content}
|
|
|
379
379
|
try:
|
|
380
380
|
os.remove(cache_path)
|
|
381
381
|
except Exception as e:
|
|
382
|
-
PrettyOutput.print(f"
|
|
382
|
+
PrettyOutput.print(f"删除缓存文件 {cached_file} 失败: {str(e)}",
|
|
383
383
|
output_type=OutputType.WARNING)
|
|
384
384
|
|
|
385
385
|
if files_to_delete:
|
|
386
386
|
for file_path in files_to_delete:
|
|
387
387
|
del self.vector_cache[file_path]
|
|
388
|
-
PrettyOutput.print(f"
|
|
388
|
+
PrettyOutput.print(f"清理了 {len(files_to_delete)} 个不存在的文件的缓存",
|
|
389
389
|
output_type=OutputType.INFO)
|
|
390
390
|
|
|
391
391
|
# Update the git file list
|
|
392
392
|
self.git_file_list = self.get_git_file_list()
|
|
393
393
|
|
|
394
394
|
# Check file changes
|
|
395
|
-
PrettyOutput.print("
|
|
395
|
+
PrettyOutput.print("检查文件变化...", output_type=OutputType.INFO)
|
|
396
396
|
changes_detected = False
|
|
397
397
|
new_files = []
|
|
398
398
|
modified_files = []
|
|
@@ -424,36 +424,36 @@ Content: {content}
|
|
|
424
424
|
modified_files.append(file_path)
|
|
425
425
|
changes_detected = True
|
|
426
426
|
except Exception as e:
|
|
427
|
-
PrettyOutput.print(f"
|
|
427
|
+
PrettyOutput.print(f"检查 {file_path} 失败: {str(e)}",
|
|
428
428
|
output_type=OutputType.ERROR)
|
|
429
429
|
progress.advance(task)
|
|
430
430
|
|
|
431
431
|
# If changes are detected, display changes and ask the user
|
|
432
432
|
if changes_detected:
|
|
433
|
-
output_lines = ["
|
|
433
|
+
output_lines = ["检测到以下变化:"]
|
|
434
434
|
if new_files:
|
|
435
|
-
output_lines.append("
|
|
435
|
+
output_lines.append("新文件:")
|
|
436
436
|
output_lines.extend(f" {f}" for f in new_files)
|
|
437
437
|
if modified_files:
|
|
438
|
-
output_lines.append("
|
|
438
|
+
output_lines.append("修改的文件:")
|
|
439
439
|
output_lines.extend(f" {f}" for f in modified_files)
|
|
440
440
|
if deleted_files:
|
|
441
|
-
output_lines.append("
|
|
441
|
+
output_lines.append("删除的文件:")
|
|
442
442
|
output_lines.extend(f" {f}" for f in deleted_files)
|
|
443
443
|
|
|
444
444
|
PrettyOutput.print("\n".join(output_lines), output_type=OutputType.WARNING)
|
|
445
445
|
|
|
446
446
|
# If force is True, continue directly
|
|
447
447
|
if not force:
|
|
448
|
-
if not user_confirm("
|
|
449
|
-
PrettyOutput.print("
|
|
448
|
+
if not user_confirm("重建索引?", False):
|
|
449
|
+
PrettyOutput.print("取消重建索引", output_type=OutputType.INFO)
|
|
450
450
|
return
|
|
451
451
|
|
|
452
452
|
# Clean deleted files
|
|
453
453
|
for file_path in files_to_delete:
|
|
454
454
|
del self.vector_cache[file_path]
|
|
455
455
|
if files_to_delete:
|
|
456
|
-
PrettyOutput.print(f"
|
|
456
|
+
PrettyOutput.print(f"清理了 {len(files_to_delete)} 个文件的缓存",
|
|
457
457
|
output_type=OutputType.INFO)
|
|
458
458
|
|
|
459
459
|
# Process new and modified files
|
|
@@ -482,19 +482,19 @@ Content: {content}
|
|
|
482
482
|
pbar.update(1)
|
|
483
483
|
|
|
484
484
|
if processed_files:
|
|
485
|
-
PrettyOutput.print("
|
|
485
|
+
PrettyOutput.print("重建向量数据库...", output_type=OutputType.INFO)
|
|
486
486
|
self.gen_vector_db_from_cache()
|
|
487
|
-
PrettyOutput.print(f"
|
|
487
|
+
PrettyOutput.print(f"成功生成了 {len(processed_files)} 个文件的索引",
|
|
488
488
|
output_type=OutputType.SUCCESS)
|
|
489
489
|
else:
|
|
490
|
-
PrettyOutput.print("
|
|
490
|
+
PrettyOutput.print("没有检测到文件变化, 不需要重建索引", output_type=OutputType.INFO)
|
|
491
491
|
|
|
492
492
|
except Exception as e:
|
|
493
493
|
# Try to save the cache when an exception occurs
|
|
494
494
|
try:
|
|
495
495
|
self._load_all_cache()
|
|
496
496
|
except Exception as save_error:
|
|
497
|
-
PrettyOutput.print(f"
|
|
497
|
+
PrettyOutput.print(f"保存缓存失败: {str(save_error)}",
|
|
498
498
|
output_type=OutputType.ERROR)
|
|
499
499
|
raise e # Re-raise the original exception
|
|
500
500
|
|
|
@@ -524,7 +524,7 @@ Content: {content}
|
|
|
524
524
|
score = len(matched_keywords) / len(keywords)
|
|
525
525
|
return score
|
|
526
526
|
|
|
527
|
-
def pick_results(self, query: List[str], initial_results: List[str]) -> List[str]:
|
|
527
|
+
def pick_results(self, query: List[str], initial_results: List[str]) -> List[Dict[str,str]]:
|
|
528
528
|
"""Use a large model to pick the search results
|
|
529
529
|
|
|
530
530
|
Args:
|
|
@@ -538,14 +538,14 @@ Content: {content}
|
|
|
538
538
|
return []
|
|
539
539
|
|
|
540
540
|
try:
|
|
541
|
-
PrettyOutput.print(f"Picking results
|
|
541
|
+
PrettyOutput.print(f"Picking results ...", output_type=OutputType.INFO)
|
|
542
542
|
|
|
543
543
|
# Maximum content length per batch
|
|
544
544
|
max_batch_length = self.max_token_count - 1000 # Reserve space for prompt
|
|
545
545
|
max_file_length = max_batch_length // 3 # Limit individual file size
|
|
546
546
|
|
|
547
547
|
# Process files in batches
|
|
548
|
-
all_selected_files =
|
|
548
|
+
all_selected_files = []
|
|
549
549
|
current_batch = []
|
|
550
550
|
current_token_count = 0
|
|
551
551
|
|
|
@@ -565,7 +565,7 @@ Content: {content}
|
|
|
565
565
|
# Process current batch
|
|
566
566
|
if current_batch:
|
|
567
567
|
selected = self._process_batch('\n'.join(query), current_batch)
|
|
568
|
-
all_selected_files.
|
|
568
|
+
all_selected_files.extend(selected)
|
|
569
569
|
# Start new batch
|
|
570
570
|
current_batch = [file_info]
|
|
571
571
|
current_token_count = tokens_count
|
|
@@ -574,23 +574,22 @@ Content: {content}
|
|
|
574
574
|
current_token_count += tokens_count
|
|
575
575
|
|
|
576
576
|
except Exception as e:
|
|
577
|
-
PrettyOutput.print(f"
|
|
577
|
+
PrettyOutput.print(f"读取 {path} 失败: {str(e)}", OutputType.ERROR)
|
|
578
578
|
continue
|
|
579
579
|
|
|
580
580
|
# Process final batch
|
|
581
581
|
if current_batch:
|
|
582
582
|
selected = self._process_batch('\n'.join(query), current_batch)
|
|
583
|
-
all_selected_files.
|
|
583
|
+
all_selected_files.extend(selected)
|
|
584
584
|
|
|
585
585
|
# Convert set to list and maintain original order
|
|
586
|
-
|
|
587
|
-
return final_results
|
|
586
|
+
return all_selected_files
|
|
588
587
|
|
|
589
588
|
except Exception as e:
|
|
590
|
-
PrettyOutput.print(f"
|
|
591
|
-
return initial_results
|
|
589
|
+
PrettyOutput.print(f"选择失败: {str(e)}", OutputType.ERROR)
|
|
590
|
+
return [{"file": f, "reason": "" } for f in initial_results]
|
|
592
591
|
|
|
593
|
-
def _process_batch(self, query: str, files_info: List[str]) -> List[str]:
|
|
592
|
+
def _process_batch(self, query: str, files_info: List[str]) -> List[Dict[str, str]]:
|
|
594
593
|
"""Process a batch of files"""
|
|
595
594
|
prompt = f"""As a code analysis expert, please help identify the most relevant files for the given query using chain-of-thought reasoning.
|
|
596
595
|
|
|
@@ -611,8 +610,10 @@ Think through this step by step:
|
|
|
611
610
|
|
|
612
611
|
Please output your selection in YAML format:
|
|
613
612
|
<FILES>
|
|
614
|
-
- path/to/most/relevant.py
|
|
613
|
+
- file: path/to/most/relevant.py
|
|
614
|
+
reason: xxxxxxxxxx
|
|
615
615
|
- path/to/next/relevant.py
|
|
616
|
+
reason: yyyyyyyyyy
|
|
616
617
|
</FILES>
|
|
617
618
|
|
|
618
619
|
Important:
|
|
@@ -638,7 +639,7 @@ Important:
|
|
|
638
639
|
selected_files = yaml.safe_load(files_match.group(1))
|
|
639
640
|
return selected_files if selected_files else []
|
|
640
641
|
except Exception as e:
|
|
641
|
-
PrettyOutput.print(f"
|
|
642
|
+
PrettyOutput.print(f"解析响应失败: {str(e)}", OutputType.ERROR)
|
|
642
643
|
return []
|
|
643
644
|
|
|
644
645
|
def _generate_query_variants(self, query: str) -> List[str]:
|
|
@@ -686,7 +687,7 @@ Please provide 10 search-optimized expressions in the specified format.
|
|
|
686
687
|
try:
|
|
687
688
|
variants = yaml.safe_load(question_match.group(1))
|
|
688
689
|
except Exception as e:
|
|
689
|
-
PrettyOutput.print(f"
|
|
690
|
+
PrettyOutput.print(f"解析变体失败: {str(e)}", OutputType.ERROR)
|
|
690
691
|
|
|
691
692
|
# Add original query
|
|
692
693
|
variants.append(query)
|
|
@@ -724,7 +725,7 @@ Please provide 10 search-optimized expressions in the specified format.
|
|
|
724
725
|
return results
|
|
725
726
|
|
|
726
727
|
|
|
727
|
-
def search_similar(self, query: str, top_k: int = 30) -> List[str]:
|
|
728
|
+
def search_similar(self, query: str, top_k: int = 30) -> List[Dict[str, str]]:
|
|
728
729
|
"""Search related files with optimized retrieval"""
|
|
729
730
|
try:
|
|
730
731
|
self.generate_codebase()
|
|
@@ -764,32 +765,27 @@ Please provide 10 search-optimized expressions in the specified format.
|
|
|
764
765
|
all_results.sort(key=lambda x: x[1], reverse=True)
|
|
765
766
|
results = all_results[:top_k]
|
|
766
767
|
|
|
767
|
-
# Display results with scores
|
|
768
|
-
message = "Found related files:\n"
|
|
769
|
-
for path, score, _ in results:
|
|
770
|
-
message += f"File: {path} (Score: {score:.3f})\n"
|
|
771
|
-
PrettyOutput.print(message.rstrip(), output_type=OutputType.INFO, lang="markdown")
|
|
772
|
-
|
|
773
768
|
results = self.pick_results(query_variants, [path for path, _, _ in results])
|
|
769
|
+
|
|
770
|
+
output = "Found related files:\n"
|
|
771
|
+
for file in results:
|
|
772
|
+
output += f'''- {file['file']} ({file['reason']})\n'''
|
|
773
|
+
PrettyOutput.print(output, output_type=OutputType.INFO, lang="markdown")
|
|
774
|
+
|
|
774
775
|
|
|
775
776
|
return results
|
|
776
777
|
|
|
777
778
|
except Exception as e:
|
|
778
|
-
PrettyOutput.print(f"
|
|
779
|
+
PrettyOutput.print(f"搜索失败: {str(e)}", output_type=OutputType.ERROR)
|
|
779
780
|
return []
|
|
780
781
|
|
|
781
|
-
def ask_codebase(self, query: str, top_k: int=20) -> str:
|
|
782
|
+
def ask_codebase(self, query: str, top_k: int=20) -> Tuple[List[Dict[str, str]], str]:
|
|
782
783
|
"""Query the codebase with enhanced context building"""
|
|
783
784
|
files_from_codebase = self.search_similar(query, top_k)
|
|
784
785
|
|
|
785
786
|
if not files_from_codebase:
|
|
786
|
-
PrettyOutput.print("
|
|
787
|
-
return ""
|
|
788
|
-
|
|
789
|
-
output = "Found related files:\n"
|
|
790
|
-
for path in files_from_codebase:
|
|
791
|
-
output += f"- {path}\n"
|
|
792
|
-
PrettyOutput.print(output, output_type=OutputType.INFO, lang="markdown")
|
|
787
|
+
PrettyOutput.print("没有找到相关文件", output_type=OutputType.WARNING)
|
|
788
|
+
return [],""
|
|
793
789
|
|
|
794
790
|
# Build enhanced prompt
|
|
795
791
|
prompt = f"""Based on the following code files, please provide a comprehensive and accurate answer to the user's question.
|
|
@@ -799,6 +795,8 @@ Important guidelines:
|
|
|
799
795
|
2. Explain technical concepts clearly
|
|
800
796
|
3. Include relevant code snippets when helpful
|
|
801
797
|
4. If the code doesn't fully answer the question, indicate what's missing
|
|
798
|
+
5. Answer in user's language.
|
|
799
|
+
6. Answer with professional language.
|
|
802
800
|
|
|
803
801
|
Question: {query}
|
|
804
802
|
|
|
@@ -810,16 +808,16 @@ Relevant code files (ordered by relevance):
|
|
|
810
808
|
|
|
811
809
|
for path in files_from_codebase:
|
|
812
810
|
try:
|
|
813
|
-
content = open(path, "r", encoding="utf-8").read()
|
|
811
|
+
content = open(path["file"], "r", encoding="utf-8").read()
|
|
814
812
|
file_content = f"""
|
|
815
|
-
File: {path}
|
|
813
|
+
File: {path["file"]}
|
|
816
814
|
Content:
|
|
817
815
|
{content}
|
|
818
816
|
----------------------------------------
|
|
819
817
|
"""
|
|
820
818
|
if current_count + get_context_token_count(file_content) > available_count:
|
|
821
819
|
PrettyOutput.print(
|
|
822
|
-
"
|
|
820
|
+
"由于上下文长度限制, 一些文件被省略",
|
|
823
821
|
output_type=OutputType.WARNING
|
|
824
822
|
)
|
|
825
823
|
break
|
|
@@ -828,25 +826,21 @@ Content:
|
|
|
828
826
|
current_count += get_context_token_count(file_content)
|
|
829
827
|
|
|
830
828
|
except Exception as e:
|
|
831
|
-
PrettyOutput.print(f"
|
|
829
|
+
PrettyOutput.print(f"读取 {path} 失败: {str(e)}",
|
|
832
830
|
output_type=OutputType.ERROR)
|
|
833
831
|
continue
|
|
834
832
|
|
|
833
|
+
model = PlatformRegistry.get_global_platform_registry().get_thinking_platform()
|
|
834
|
+
|
|
835
835
|
prompt += """
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
5. Add reference files and code snippets at the end of the answer.
|
|
842
|
-
|
|
843
|
-
Answer in Chinese using professional language.
|
|
836
|
+
Output Format:
|
|
837
|
+
- question: the question to answer
|
|
838
|
+
answer: the answer to the question
|
|
839
|
+
- question: the question to answer
|
|
840
|
+
answer: the answer to the question
|
|
844
841
|
"""
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
response = model.chat_until_success(prompt)
|
|
848
|
-
|
|
849
|
-
return response
|
|
842
|
+
|
|
843
|
+
return files_from_codebase, model.chat_until_success(prompt)
|
|
850
844
|
|
|
851
845
|
def is_index_generated(self) -> bool:
|
|
852
846
|
"""Check if the index has been generated"""
|
|
@@ -886,7 +880,7 @@ Answer in Chinese using professional language.
|
|
|
886
880
|
return True
|
|
887
881
|
|
|
888
882
|
except Exception as e:
|
|
889
|
-
PrettyOutput.print(f"
|
|
883
|
+
PrettyOutput.print(f"检查索引状态失败: {str(e)}",
|
|
890
884
|
output_type=OutputType.ERROR)
|
|
891
885
|
return False
|
|
892
886
|
|
|
@@ -931,24 +925,24 @@ def main():
|
|
|
931
925
|
if args.command == 'generate':
|
|
932
926
|
try:
|
|
933
927
|
codebase.generate_codebase(force=args.force)
|
|
934
|
-
PrettyOutput.print("
|
|
928
|
+
PrettyOutput.print("代码库生成完成", output_type=OutputType.SUCCESS)
|
|
935
929
|
except Exception as e:
|
|
936
|
-
PrettyOutput.print(f"
|
|
930
|
+
PrettyOutput.print(f"代码库生成失败: {str(e)}", output_type=OutputType.ERROR)
|
|
937
931
|
|
|
938
932
|
elif args.command == 'search':
|
|
939
933
|
results = codebase.search_similar(args.query, args.top_k)
|
|
940
934
|
if not results:
|
|
941
|
-
PrettyOutput.print("
|
|
935
|
+
PrettyOutput.print("没有找到相似的文件", output_type=OutputType.WARNING)
|
|
942
936
|
return
|
|
943
937
|
|
|
944
|
-
output = "
|
|
938
|
+
output = "搜索结果:\n"
|
|
945
939
|
for path in results:
|
|
946
940
|
output += f"""- {path}\n"""
|
|
947
941
|
PrettyOutput.print(output, output_type=OutputType.INFO, lang="markdown")
|
|
948
942
|
|
|
949
943
|
elif args.command == 'ask':
|
|
950
944
|
response = codebase.ask_codebase(args.question, args.top_k)
|
|
951
|
-
output = f"""
|
|
945
|
+
output = f"""{response}"""
|
|
952
946
|
PrettyOutput.print(output, output_type=OutputType.INFO)
|
|
953
947
|
|
|
954
948
|
else:
|
jarvis/jarvis_lsp/cpp.py
CHANGED
|
@@ -41,7 +41,7 @@ class CPPLSP(BaseLSP):
|
|
|
41
41
|
|
|
42
42
|
return True
|
|
43
43
|
except Exception as e:
|
|
44
|
-
PrettyOutput.print(f"C++ LSP
|
|
44
|
+
PrettyOutput.print(f"C++ LSP 初始化失败: {str(e)}", OutputType.ERROR)
|
|
45
45
|
return False
|
|
46
46
|
|
|
47
47
|
def _send_request(self, method: str, params: Dict) -> Optional[Dict]:
|
jarvis/jarvis_lsp/go.py
CHANGED
|
@@ -47,7 +47,7 @@ class GoLSP(BaseLSP):
|
|
|
47
47
|
|
|
48
48
|
return True
|
|
49
49
|
except Exception as e:
|
|
50
|
-
PrettyOutput.print(f"Go LSP
|
|
50
|
+
PrettyOutput.print(f"Go LSP 初始化失败: {str(e)}", OutputType.ERROR)
|
|
51
51
|
return False
|
|
52
52
|
|
|
53
53
|
def _send_request(self, method: str, params: Dict) -> Optional[Dict]:
|
jarvis/jarvis_lsp/python.py
CHANGED
jarvis/jarvis_lsp/registry.py
CHANGED
|
@@ -33,7 +33,7 @@ class LSPRegistry:
|
|
|
33
33
|
with open(os.path.join(user_lsp_dir, "__init__.py"), "w") as f:
|
|
34
34
|
pass
|
|
35
35
|
except Exception as e:
|
|
36
|
-
PrettyOutput.print(f"
|
|
36
|
+
PrettyOutput.print(f"创建 LSP 目录失败: {str(e)}", OutputType.ERROR)
|
|
37
37
|
return ""
|
|
38
38
|
return user_lsp_dir
|
|
39
39
|
|
|
@@ -72,7 +72,7 @@ class LSPRegistry:
|
|
|
72
72
|
lsp_servers = {}
|
|
73
73
|
|
|
74
74
|
if not os.path.exists(directory):
|
|
75
|
-
PrettyOutput.print(f"LSP
|
|
75
|
+
PrettyOutput.print(f"LSP 目录不存在: {directory}", OutputType.ERROR)
|
|
76
76
|
return lsp_servers
|
|
77
77
|
|
|
78
78
|
package_name = None
|
|
@@ -104,7 +104,7 @@ class LSPRegistry:
|
|
|
104
104
|
lsp_servers[obj.language] = obj
|
|
105
105
|
break
|
|
106
106
|
except Exception as e:
|
|
107
|
-
PrettyOutput.print(f"
|
|
107
|
+
PrettyOutput.print(f"加载 LSP {module_name} 失败: {str(e)}", OutputType.ERROR)
|
|
108
108
|
|
|
109
109
|
return lsp_servers
|
|
110
110
|
|
|
@@ -138,14 +138,14 @@ class LSPRegistry:
|
|
|
138
138
|
def create_lsp(self, language: str) -> Optional[BaseLSP]:
|
|
139
139
|
"""Create LSP instance for specified language."""
|
|
140
140
|
if language not in self.lsp_servers:
|
|
141
|
-
PrettyOutput.print(f"LSP
|
|
141
|
+
PrettyOutput.print(f"没有找到 LSP 支持的语言: {language}", OutputType.ERROR)
|
|
142
142
|
return None
|
|
143
143
|
|
|
144
144
|
try:
|
|
145
145
|
lsp = self.lsp_servers[language]()
|
|
146
146
|
return lsp
|
|
147
147
|
except Exception as e:
|
|
148
|
-
PrettyOutput.print(f"
|
|
148
|
+
PrettyOutput.print(f"创建 LSP 失败: {str(e)}", OutputType.ERROR)
|
|
149
149
|
return None
|
|
150
150
|
|
|
151
151
|
def get_supported_languages(self) -> List[str]:
|
|
@@ -186,11 +186,11 @@ def main():
|
|
|
186
186
|
lsp = registry.create_lsp(args.language)
|
|
187
187
|
|
|
188
188
|
if not lsp:
|
|
189
|
-
PrettyOutput.print(f"
|
|
189
|
+
PrettyOutput.print(f"没有 LSP 支持的语言: {args.language}", OutputType.ERROR)
|
|
190
190
|
return 1
|
|
191
191
|
|
|
192
192
|
if not lsp.initialize(os.path.dirname(os.path.abspath(args.file))):
|
|
193
|
-
PrettyOutput.print("LSP
|
|
193
|
+
PrettyOutput.print("LSP 初始化失败", OutputType.ERROR)
|
|
194
194
|
return 1
|
|
195
195
|
|
|
196
196
|
try:
|
|
@@ -204,26 +204,26 @@ def main():
|
|
|
204
204
|
diagnostics = lsp.get_diagnostics(args.file)
|
|
205
205
|
for diag in diagnostics:
|
|
206
206
|
severity = ['Error', 'Warning', 'Info', 'Hint'][diag['severity'] - 1]
|
|
207
|
-
print(f"{severity}
|
|
207
|
+
PrettyOutput.print(f"{severity} 在 {diag['range']['start']['line']}:{diag['range']['start']['character']}: {diag['message']}", OutputType.INFO)
|
|
208
208
|
|
|
209
209
|
elif args.action in ('references', 'definition'):
|
|
210
210
|
if args.line is None or args.character is None:
|
|
211
|
-
PrettyOutput.print("
|
|
211
|
+
PrettyOutput.print("需要行和字符位置用于 references/definition", OutputType.ERROR)
|
|
212
212
|
return 1
|
|
213
213
|
|
|
214
214
|
if args.action == 'references':
|
|
215
215
|
refs = lsp.find_references(args.file, (args.line, args.character))
|
|
216
216
|
for ref in refs:
|
|
217
|
-
print(f"
|
|
217
|
+
PrettyOutput.print(f"引用在 {ref['uri']} 在 {ref['range']['start']['line']}:{ref['range']['start']['character']}\n行: {LSPRegistry.get_line_at_position(ref['uri'], ref['range']['start']['line'])}", OutputType.INFO)
|
|
218
218
|
else:
|
|
219
219
|
defn = lsp.find_definition(args.file, (args.line, args.character))
|
|
220
220
|
if defn:
|
|
221
|
-
print(f"
|
|
221
|
+
PrettyOutput.print(f"定义在 {defn['uri']} 在 {defn['range']['start']['line']}:{defn['range']['start']['character']}\n行: {LSPRegistry.get_line_at_position(defn['uri'], defn['range']['start']['line'])}", OutputType.INFO)
|
|
222
222
|
else:
|
|
223
|
-
print("
|
|
223
|
+
PrettyOutput.print("没有找到定义", OutputType.WARNING)
|
|
224
224
|
|
|
225
225
|
except Exception as e:
|
|
226
|
-
PrettyOutput.print(f"
|
|
226
|
+
PrettyOutput.print(f"错误: {str(e)}", OutputType.ERROR)
|
|
227
227
|
return 1
|
|
228
228
|
finally:
|
|
229
229
|
lsp.shutdown()
|
jarvis/jarvis_lsp/rust.py
CHANGED
|
@@ -49,7 +49,7 @@ class RustLSP(BaseLSP):
|
|
|
49
49
|
|
|
50
50
|
return True
|
|
51
51
|
except Exception as e:
|
|
52
|
-
PrettyOutput.print(f"Rust LSP
|
|
52
|
+
PrettyOutput.print(f"Rust LSP 初始化失败: {str(e)}", OutputType.ERROR)
|
|
53
53
|
return False
|
|
54
54
|
|
|
55
55
|
def _send_request(self, method: str, params: Dict) -> Optional[Dict]:
|