jarvis-ai-assistant 0.1.86__tar.gz → 0.1.87__tar.gz
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_ai_assistant-0.1.86/src/jarvis_ai_assistant.egg-info → jarvis_ai_assistant-0.1.87}/PKG-INFO +1 -2
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/README.md +0 -1
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/pyproject.toml +1 -1
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/setup.py +1 -1
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/__init__.py +1 -1
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/jarvis_codebase/main.py +113 -98
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/jarvis_coder/main.py +89 -21
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/jarvis_rag/main.py +190 -147
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/models/ai8.py +1 -1
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/models/oyi.py +3 -3
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/tools/registry.py +7 -5
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/utils.py +41 -1
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87/src/jarvis_ai_assistant.egg-info}/PKG-INFO +1 -2
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/MANIFEST.in +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/setup.cfg +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/agent.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/jarvis_codebase/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/jarvis_coder/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/jarvis_rag/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/jarvis_smart_shell/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/jarvis_smart_shell/main.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/main.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/models/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/models/base.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/models/kimi.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/models/ollama.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/models/openai.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/models/registry.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/tools/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/tools/ask_user.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/tools/base.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/tools/chdir.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/tools/codebase_qa.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/tools/coder.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/tools/file_ops.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/tools/generator.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/tools/methodology.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/tools/search.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/tools/shell.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/tools/sub_agent.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/tools/webpage.py +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis_ai_assistant.egg-info/SOURCES.txt +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis_ai_assistant.egg-info/dependency_links.txt +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis_ai_assistant.egg-info/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis_ai_assistant.egg-info/requires.txt +0 -0
- {jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis_ai_assistant.egg-info/top_level.txt +0 -0
{jarvis_ai_assistant-0.1.86/src/jarvis_ai_assistant.egg-info → jarvis_ai_assistant-0.1.87}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: jarvis-ai-assistant
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.87
|
|
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
|
|
@@ -134,7 +134,6 @@ Jarvis supports configuration through environment variables that can be set in t
|
|
|
134
134
|
| JARVIS_CODEGEN_MODEL | Model name for code generation | Same as JARVIS_MODEL | No |
|
|
135
135
|
| JARVIS_CHEAP_PLATFORM | AI platform for cheap operations | Same as JARVIS_PLATFORM | No |
|
|
136
136
|
| JARVIS_CHEAP_MODEL | Model name for cheap operations | Same as JARVIS_MODEL | No |
|
|
137
|
-
| JARVIS_EMBEDDING_MODEL | Embedding model for code analysis | BAAI/bge-large-zh-v1.5 | No |
|
|
138
137
|
| OPENAI_API_KEY | API key for OpenAI platform | - | Required for OpenAI |
|
|
139
138
|
| OPENAI_API_BASE | Base URL for OpenAI API | https://api.deepseek.com | No |
|
|
140
139
|
| OPENAI_MODEL_NAME | Model name for OpenAI | deepseek-chat | No |
|
|
@@ -70,7 +70,6 @@ Jarvis supports configuration through environment variables that can be set in t
|
|
|
70
70
|
| JARVIS_CODEGEN_MODEL | Model name for code generation | Same as JARVIS_MODEL | No |
|
|
71
71
|
| JARVIS_CHEAP_PLATFORM | AI platform for cheap operations | Same as JARVIS_PLATFORM | No |
|
|
72
72
|
| JARVIS_CHEAP_MODEL | Model name for cheap operations | Same as JARVIS_MODEL | No |
|
|
73
|
-
| JARVIS_EMBEDDING_MODEL | Embedding model for code analysis | BAAI/bge-large-zh-v1.5 | No |
|
|
74
73
|
| OPENAI_API_KEY | API key for OpenAI platform | - | Required for OpenAI |
|
|
75
74
|
| OPENAI_API_BASE | Base URL for OpenAI API | https://api.deepseek.com | No |
|
|
76
75
|
| OPENAI_MODEL_NAME | Model name for OpenAI | deepseek-chat | No |
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "jarvis-ai-assistant"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.87"
|
|
8
8
|
description = "Jarvis: An AI assistant that uses tools to interact with the system"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "Your Name", email = "your.email@example.com" }]
|
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name="jarvis-ai-assistant",
|
|
5
|
-
version="0.1.
|
|
5
|
+
version="0.1.87",
|
|
6
6
|
author="skyfire",
|
|
7
7
|
author_email="skyfireitdiy@hotmail.com",
|
|
8
8
|
description="An AI assistant that uses various tools to interact with the system",
|
{jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/jarvis_codebase/main.py
RENAMED
|
@@ -7,7 +7,7 @@ from jarvis.models.registry import PlatformRegistry
|
|
|
7
7
|
import concurrent.futures
|
|
8
8
|
from threading import Lock
|
|
9
9
|
from concurrent.futures import ThreadPoolExecutor
|
|
10
|
-
from jarvis.utils import OutputType, PrettyOutput, find_git_root, get_max_context_length, get_thread_count, load_embedding_model
|
|
10
|
+
from jarvis.utils import OutputType, PrettyOutput, find_git_root, get_max_context_length, get_thread_count, load_embedding_model, load_rerank_model
|
|
11
11
|
from jarvis.utils import load_env_from_file
|
|
12
12
|
import argparse
|
|
13
13
|
from sentence_transformers import SentenceTransformer
|
|
@@ -98,7 +98,7 @@ class CodeBase:
|
|
|
98
98
|
5. 关键业务逻辑和处理流程
|
|
99
99
|
6. 特殊功能点和亮点特性
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
请用简洁专业的语言描述,突出代码的技术特征和功能特点,以便后续进行关联代码检索。
|
|
102
102
|
|
|
103
103
|
文件路径:{file_path}
|
|
104
104
|
代码内容:
|
|
@@ -196,10 +196,15 @@ class CodeBase:
|
|
|
196
196
|
if cached_vector is not None:
|
|
197
197
|
return cached_vector
|
|
198
198
|
|
|
199
|
-
#
|
|
199
|
+
# 读取文件内容并组合信息
|
|
200
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
201
|
+
content = f.read()[:self.max_context_length] # 限制文件内容长度
|
|
202
|
+
|
|
203
|
+
# 组合文件信息,包含文件内容
|
|
200
204
|
combined_text = f"""
|
|
201
205
|
文件路径: {file_path}
|
|
202
206
|
文件描述: {description}
|
|
207
|
+
文件内容: {content}
|
|
203
208
|
"""
|
|
204
209
|
vector = self.get_embedding(combined_text)
|
|
205
210
|
|
|
@@ -398,116 +403,126 @@ class CodeBase:
|
|
|
398
403
|
PrettyOutput.print("没有检测到文件变更,无需重建索引", output_type=OutputType.INFO)
|
|
399
404
|
|
|
400
405
|
def rerank_results(self, query: str, initial_results: List[Tuple[str, float, str]]) -> List[Tuple[str, float, str]]:
|
|
401
|
-
"""
|
|
406
|
+
"""使用 BAAI/bge-reranker-v2-m3 对搜索结果重新排序"""
|
|
402
407
|
if not initial_results:
|
|
403
408
|
return []
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
描述: {desc}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
409
|
+
|
|
410
|
+
try:
|
|
411
|
+
import torch
|
|
412
|
+
|
|
413
|
+
# 加载模型和分词器
|
|
414
|
+
model, tokenizer = load_rerank_model()
|
|
415
|
+
|
|
416
|
+
# 准备数据 - 加入文件内容进行更准确的重排序
|
|
417
|
+
pairs = []
|
|
418
|
+
for path, _, desc in initial_results:
|
|
419
|
+
try:
|
|
420
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
421
|
+
content = f.read()[:512] # 限制内容长度
|
|
422
|
+
# 组合文件路径、描述和内容
|
|
423
|
+
doc_content = f"文件: {path}\n描述: {desc}\n内容: {content}"
|
|
424
|
+
pairs.append([query, doc_content])
|
|
425
|
+
except Exception as e:
|
|
426
|
+
PrettyOutput.print(f"读取文件失败 {path}: {str(e)}",
|
|
427
|
+
output_type=OutputType.ERROR)
|
|
428
|
+
doc_content = f"文件: {path}\n描述: {desc}"
|
|
429
|
+
pairs.append([query, doc_content])
|
|
430
|
+
|
|
431
|
+
# 使用更大的batch size提高处理速度
|
|
432
|
+
batch_size = 16 # 根据GPU显存调整
|
|
433
|
+
|
|
434
|
+
with torch.no_grad():
|
|
435
|
+
for i in range(0, len(pairs), batch_size):
|
|
436
|
+
batch_pairs = pairs[i:i + batch_size]
|
|
437
|
+
encoded = tokenizer(
|
|
438
|
+
batch_pairs,
|
|
439
|
+
padding=True,
|
|
440
|
+
truncation=True,
|
|
441
|
+
max_length=512,
|
|
442
|
+
return_tensors='pt'
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
if torch.cuda.is_available():
|
|
446
|
+
encoded = {k: v.cuda() for k, v in encoded.items()}
|
|
447
|
+
|
|
448
|
+
outputs = model(**encoded)
|
|
449
|
+
# 修改这里:直接使用 outputs.logits 作为分数
|
|
450
|
+
batch_scores = outputs.logits.squeeze(-1).cpu().numpy()
|
|
451
|
+
|
|
452
|
+
# 归一化分数到 0-1 范围
|
|
453
|
+
if batch_scores:
|
|
454
|
+
min_score = min(batch_scores)
|
|
455
|
+
max_score = max(batch_scores)
|
|
456
|
+
if max_score > min_score:
|
|
457
|
+
batch_scores = [(s - min_score) / (max_score - min_score) for s in batch_scores]
|
|
458
|
+
|
|
459
|
+
# 将分数与原始结果组合并排序
|
|
460
|
+
scored_results = []
|
|
461
|
+
for (path, _, desc), score in zip(initial_results, batch_scores):
|
|
462
|
+
if score >= 0.5: # 只保留相关度大于 0.5 的结果
|
|
463
|
+
scored_results.append((path, float(score), desc))
|
|
464
|
+
|
|
465
|
+
# 按分数降序排序
|
|
466
|
+
scored_results.sort(key=lambda x: x[1], reverse=True)
|
|
467
|
+
|
|
468
|
+
return scored_results
|
|
469
|
+
|
|
470
|
+
except Exception as e:
|
|
471
|
+
PrettyOutput.print(f"重排序失败,使用原始排序: {str(e)}", output_type=OutputType.WARNING)
|
|
472
|
+
return initial_results
|
|
455
473
|
|
|
456
474
|
def search_similar(self, query: str, top_k: int = 30) -> List[Tuple[str, float, str]]:
|
|
457
|
-
"""
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
475
|
+
"""搜索关联文件"""
|
|
476
|
+
try:
|
|
477
|
+
# 生成多个查询变体以提高召回率
|
|
478
|
+
model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
|
|
479
|
+
model.set_suppress_output(True)
|
|
480
|
+
|
|
481
|
+
prompt = f"""请根据以下查询,生成3个不同的表述,每个表述都要完整表达原始查询的意思。这些表述将用于代码搜索,要保持专业性和准确性。
|
|
463
482
|
原始查询: {query}
|
|
464
483
|
|
|
465
|
-
|
|
484
|
+
请直接输出3个表述,用换行分隔,不要有编号或其他标记。
|
|
466
485
|
"""
|
|
467
|
-
|
|
468
|
-
|
|
486
|
+
query_variants = model.chat(prompt).strip().split('\n')
|
|
487
|
+
query_variants.append(query) # 添加原始查询
|
|
469
488
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
q_vector = self.get_embedding(query)
|
|
476
|
-
q_vector = q_vector.reshape(1, -1)
|
|
477
|
-
|
|
478
|
-
distances, indices = self.index.search(q_vector, top_k)
|
|
479
|
-
|
|
480
|
-
PrettyOutput.print(f"查询 {query} 的结果: ", output_type=OutputType.INFO)
|
|
481
|
-
|
|
482
|
-
initial_results = []
|
|
483
|
-
|
|
484
|
-
for i, distance in zip(indices[0], distances[0]):
|
|
485
|
-
if i == -1: # faiss返回-1表示无效结果
|
|
486
|
-
continue
|
|
489
|
+
# 对每个查询变体进行搜索
|
|
490
|
+
all_results = {}
|
|
491
|
+
for q in query_variants:
|
|
492
|
+
q_vector = self.get_embedding(q)
|
|
493
|
+
q_vector = q_vector.reshape(1, -1)
|
|
487
494
|
|
|
488
|
-
|
|
489
|
-
# 只保留相似度大于等于0.5的结果
|
|
490
|
-
if similarity >= 0.5:
|
|
491
|
-
PrettyOutput.print(f" {self.file_paths[i]} : 距离 {distance:.3f}, 相似度 {similarity:.3f}",
|
|
492
|
-
output_type=OutputType.INFO)
|
|
495
|
+
distances, indices = self.index.search(q_vector, top_k)
|
|
493
496
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
497
|
+
for i, distance in zip(indices[0], distances[0]):
|
|
498
|
+
if i == -1:
|
|
499
|
+
continue
|
|
500
|
+
|
|
501
|
+
similarity = 1.0 / (1.0 + float(distance))
|
|
502
|
+
if similarity >= 0.5:
|
|
503
|
+
file_path = self.file_paths[i]
|
|
504
|
+
# 使用最高的相似度分数
|
|
505
|
+
if file_path not in all_results or similarity > all_results[file_path][1]:
|
|
506
|
+
data = self.vector_cache[file_path]
|
|
507
|
+
all_results[file_path] = (file_path, similarity, data["description"])
|
|
508
|
+
|
|
509
|
+
# 转换为列表并排序
|
|
510
|
+
results = list(all_results.values())
|
|
511
|
+
results.sort(key=lambda x: x[1], reverse=True)
|
|
512
|
+
|
|
513
|
+
return results[:top_k]
|
|
514
|
+
|
|
515
|
+
except Exception as e:
|
|
516
|
+
PrettyOutput.print(f"搜索失败: {str(e)}", output_type=OutputType.ERROR)
|
|
500
517
|
return []
|
|
501
518
|
|
|
502
|
-
# 使用大模型重新排序
|
|
503
|
-
PrettyOutput.print("使用大模型重新排序...", output_type=OutputType.INFO)
|
|
504
|
-
reranked_results = self.rerank_results(query, initial_results)
|
|
505
|
-
|
|
506
|
-
return reranked_results
|
|
507
|
-
|
|
508
519
|
def ask_codebase(self, query: str, top_k: int=20) -> str:
|
|
509
520
|
"""查询代码库"""
|
|
510
521
|
results = self.search_similar(query, top_k)
|
|
522
|
+
if not results:
|
|
523
|
+
PrettyOutput.print("没有找到关联的文件", output_type=OutputType.WARNING)
|
|
524
|
+
return ""
|
|
525
|
+
|
|
511
526
|
PrettyOutput.print(f"找到的关联文件: ", output_type=OutputType.SUCCESS)
|
|
512
527
|
for path, score, _ in results:
|
|
513
528
|
PrettyOutput.print(f"文件: {path} 关联度: {score:.3f}",
|
|
@@ -127,27 +127,55 @@ class JarvisCoder:
|
|
|
127
127
|
修改格式说明:
|
|
128
128
|
1. 每个修改块格式如下:
|
|
129
129
|
<PATCH>
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
> path/to/file
|
|
131
|
+
def old_function():
|
|
132
|
+
print("old code")
|
|
133
|
+
return False
|
|
132
134
|
=======
|
|
133
|
-
|
|
134
|
-
|
|
135
|
+
def old_function():
|
|
136
|
+
print("new code")
|
|
137
|
+
return True
|
|
135
138
|
</PATCH>
|
|
136
139
|
|
|
137
140
|
2. 如果是新文件或者替换整个文件内容,格式如下:
|
|
138
141
|
<PATCH>
|
|
139
|
-
|
|
142
|
+
> src/new_module.py
|
|
140
143
|
=======
|
|
141
|
-
|
|
142
|
-
|
|
144
|
+
from typing import List
|
|
145
|
+
|
|
146
|
+
def new_function():
|
|
147
|
+
return "This is a new file"
|
|
143
148
|
</PATCH>
|
|
144
149
|
|
|
145
150
|
3. 如果要删除文件中的某一段,格式如下:
|
|
146
151
|
<PATCH>
|
|
147
|
-
|
|
148
|
-
|
|
152
|
+
> path/to/file
|
|
153
|
+
# 这是要删除的注释
|
|
154
|
+
deprecated_code = True
|
|
155
|
+
if deprecated_code:
|
|
156
|
+
print("old feature")
|
|
157
|
+
=======
|
|
158
|
+
</PATCH>
|
|
159
|
+
|
|
160
|
+
4. 如果要修改导入语句,格式如下:
|
|
161
|
+
<PATCH>
|
|
162
|
+
> src/main.py
|
|
163
|
+
from old_module import old_class
|
|
164
|
+
=======
|
|
165
|
+
from new_module import new_class
|
|
166
|
+
</PATCH>
|
|
167
|
+
|
|
168
|
+
5. 如果要修改类定义,格式如下:
|
|
169
|
+
<PATCH>
|
|
170
|
+
> src/models.py
|
|
171
|
+
class OldModel:
|
|
172
|
+
def __init__(self):
|
|
173
|
+
self.value = 0
|
|
149
174
|
=======
|
|
150
|
-
|
|
175
|
+
class OldModel:
|
|
176
|
+
def __init__(self):
|
|
177
|
+
self.value = 1
|
|
178
|
+
self.name = "new"
|
|
151
179
|
</PATCH>
|
|
152
180
|
|
|
153
181
|
文件列表如下:
|
|
@@ -170,7 +198,7 @@ class JarvisCoder:
|
|
|
170
198
|
3、要替换的内容,一定要与文件内容完全一致,不要有任何多余或者缺失的内容
|
|
171
199
|
4、每个patch不超过20行,超出20行,请生成多个patch
|
|
172
200
|
"""
|
|
173
|
-
|
|
201
|
+
|
|
174
202
|
success, response = self._call_model_with_retry(self.main_model, prompt)
|
|
175
203
|
if not success:
|
|
176
204
|
return []
|
|
@@ -204,7 +232,7 @@ class JarvisCoder:
|
|
|
204
232
|
continue
|
|
205
233
|
|
|
206
234
|
# 获取文件路径
|
|
207
|
-
file_path_match = re.search(r'
|
|
235
|
+
file_path_match = re.search(r'> (.*)', lines[0])
|
|
208
236
|
if not file_path_match:
|
|
209
237
|
error_info.append(f"无法解析文件路径: {lines[0]}")
|
|
210
238
|
return False, "\n".join(error_info)
|
|
@@ -220,7 +248,7 @@ class JarvisCoder:
|
|
|
220
248
|
return False, "\n".join(error_info)
|
|
221
249
|
|
|
222
250
|
old_content = parts[0]
|
|
223
|
-
new_content = parts[1].split("
|
|
251
|
+
new_content = parts[1].split("</PATCH>")[0]
|
|
224
252
|
|
|
225
253
|
# 处理新文件
|
|
226
254
|
if not old_content:
|
|
@@ -348,19 +376,59 @@ class JarvisCoder:
|
|
|
348
376
|
}
|
|
349
377
|
else:
|
|
350
378
|
self._revert_changes()
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
379
|
+
|
|
380
|
+
# 让用户输入调整意见
|
|
381
|
+
user_feedback = get_multiline_input("""
|
|
382
|
+
请提供修改建议,帮助生成更好的补丁:
|
|
383
|
+
1. 修改的位置是否正确?
|
|
384
|
+
2. 修改的内容是否合适?
|
|
385
|
+
3. 是否有遗漏的修改?
|
|
386
|
+
4. 其他调整建议?
|
|
387
|
+
|
|
388
|
+
请输入调整意见(直接回车跳过):""")
|
|
389
|
+
|
|
390
|
+
if not user_feedback:
|
|
391
|
+
return {
|
|
392
|
+
"success": False,
|
|
393
|
+
"stdout": "",
|
|
394
|
+
"stderr": "修改被用户取消,文件未发生任何变化",
|
|
395
|
+
"error": UserWarning("用户取消修改")
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
retry_prompt = f"""补丁被用户拒绝,请根据用户意见重新生成补丁:
|
|
399
|
+
|
|
400
|
+
用户意见:
|
|
401
|
+
{user_feedback}
|
|
402
|
+
|
|
403
|
+
请重新生成补丁,确保:
|
|
404
|
+
1. 按照用户意见调整修改内容
|
|
405
|
+
2. 准确定位要修改的代码位置
|
|
406
|
+
3. 正确处理代码缩进
|
|
407
|
+
4. 考虑代码上下文
|
|
408
|
+
"""
|
|
409
|
+
patches = self._remake_patch(retry_prompt)
|
|
410
|
+
continue
|
|
357
411
|
else:
|
|
358
|
-
PrettyOutput.print(f"
|
|
359
|
-
|
|
412
|
+
PrettyOutput.print(f"补丁应用失败: {error_info}", OutputType.WARNING)
|
|
413
|
+
|
|
414
|
+
# 让用户输入补充信息
|
|
415
|
+
user_info = get_multiline_input("""
|
|
416
|
+
补丁应用失败。请提供更多信息来帮助修复问题:
|
|
417
|
+
1. 是否需要调整代码位置?
|
|
418
|
+
2. 是否有特殊的格式要求?
|
|
419
|
+
3. 是否需要考虑其他文件的依赖?
|
|
420
|
+
4. 其他补充说明?
|
|
421
|
+
|
|
422
|
+
请输入补充信息(直接回车跳过):""")
|
|
423
|
+
|
|
424
|
+
retry_prompt = f"""补丁应用失败,请根据以下信息重新生成补丁:
|
|
360
425
|
|
|
361
426
|
错误信息:
|
|
362
427
|
{error_info}
|
|
363
428
|
|
|
429
|
+
用户补充信息:
|
|
430
|
+
{user_info if user_info else "用户未提供补充信息"}
|
|
431
|
+
|
|
364
432
|
请确保:
|
|
365
433
|
1. 准确定位要修改的代码位置
|
|
366
434
|
2. 正确处理代码缩进
|
|
@@ -5,7 +5,7 @@ import faiss
|
|
|
5
5
|
from typing import List, Tuple, Optional, Dict
|
|
6
6
|
from sentence_transformers import SentenceTransformer
|
|
7
7
|
import pickle
|
|
8
|
-
from jarvis.utils import OutputType, PrettyOutput, find_git_root, get_max_context_length, load_embedding_model
|
|
8
|
+
from jarvis.utils import OutputType, PrettyOutput, find_git_root, get_max_context_length, load_embedding_model, load_rerank_model
|
|
9
9
|
from jarvis.utils import load_env_from_file
|
|
10
10
|
import tiktoken
|
|
11
11
|
from dataclasses import dataclass
|
|
@@ -14,6 +14,8 @@ import fitz # PyMuPDF for PDF files
|
|
|
14
14
|
from docx import Document as DocxDocument # python-docx for DOCX files
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
from jarvis.models.registry import PlatformRegistry
|
|
17
|
+
import shutil
|
|
18
|
+
from datetime import datetime
|
|
17
19
|
|
|
18
20
|
@dataclass
|
|
19
21
|
class Document:
|
|
@@ -193,15 +195,30 @@ class RAGTool:
|
|
|
193
195
|
self.index = None
|
|
194
196
|
|
|
195
197
|
def _save_cache(self, vectors: np.ndarray):
|
|
196
|
-
"""
|
|
198
|
+
"""优化缓存保存"""
|
|
197
199
|
try:
|
|
200
|
+
# 添加版本号和时间戳
|
|
198
201
|
cache_data = {
|
|
202
|
+
"version": "1.0",
|
|
203
|
+
"timestamp": datetime.now().isoformat(),
|
|
199
204
|
"documents": self.documents,
|
|
200
|
-
"vectors": vectors
|
|
205
|
+
"vectors": vectors,
|
|
206
|
+
"metadata": {
|
|
207
|
+
"vector_dim": self.vector_dim,
|
|
208
|
+
"total_docs": len(self.documents),
|
|
209
|
+
"model_name": self.embedding_model.__class__.__name__
|
|
210
|
+
}
|
|
201
211
|
}
|
|
212
|
+
|
|
213
|
+
# 使用压缩存储
|
|
202
214
|
with open(self.cache_path, 'wb') as f:
|
|
203
|
-
pickle.dump(cache_data, f)
|
|
204
|
-
|
|
215
|
+
pickle.dump(cache_data, f, protocol=pickle.HIGHEST_PROTOCOL)
|
|
216
|
+
|
|
217
|
+
# 创建备份
|
|
218
|
+
backup_path = f"{self.cache_path}.backup"
|
|
219
|
+
shutil.copy2(self.cache_path, backup_path)
|
|
220
|
+
|
|
221
|
+
PrettyOutput.print(f"缓存已保存: {len(self.documents)} 个文档片段",
|
|
205
222
|
output_type=OutputType.INFO)
|
|
206
223
|
except Exception as e:
|
|
207
224
|
PrettyOutput.print(f"保存缓存失败: {str(e)}",
|
|
@@ -209,100 +226,74 @@ class RAGTool:
|
|
|
209
226
|
|
|
210
227
|
def _build_index(self, vectors: np.ndarray):
|
|
211
228
|
"""构建FAISS索引"""
|
|
212
|
-
#
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
# 用IndexIDMap包装HNSW索引
|
|
218
|
-
self.index = faiss.IndexIDMap(hnsw_index)
|
|
229
|
+
# 添加IVF索引以提高大规模检索性能
|
|
230
|
+
nlist = max(4, int(vectors.shape[0] / 1000)) # 每1000个向量一个聚类中心
|
|
231
|
+
quantizer = faiss.IndexFlatIP(self.vector_dim)
|
|
232
|
+
self.index = faiss.IndexIVFFlat(quantizer, self.vector_dim, nlist, faiss.METRIC_INNER_PRODUCT)
|
|
219
233
|
|
|
220
|
-
# 添加向量到索引
|
|
221
234
|
if vectors.shape[0] > 0:
|
|
222
|
-
|
|
235
|
+
# 训练IVF索引
|
|
236
|
+
self.index.train(vectors)
|
|
237
|
+
self.index.add(vectors)
|
|
238
|
+
# 设置搜索时探测的聚类数
|
|
239
|
+
self.index.nprobe = min(nlist, 10)
|
|
223
240
|
else:
|
|
224
241
|
self.index = None
|
|
225
242
|
|
|
226
243
|
def _split_text(self, text: str) -> List[str]:
|
|
227
|
-
"""
|
|
244
|
+
"""使用更智能的分块策略"""
|
|
245
|
+
# 添加重叠分块以保持上下文连贯性
|
|
246
|
+
overlap_size = min(200, self.max_paragraph_length // 4)
|
|
228
247
|
|
|
229
|
-
Args:
|
|
230
|
-
text: 要分割的文本
|
|
231
|
-
|
|
232
|
-
Returns:
|
|
233
|
-
分割后的段落列表
|
|
234
|
-
"""
|
|
235
|
-
# 首先按空行分割
|
|
236
248
|
paragraphs = []
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
for line in text.split('\n'):
|
|
240
|
-
line = line.strip()
|
|
241
|
-
if not line: # 空行表示段落结束
|
|
242
|
-
if current_paragraph:
|
|
243
|
-
paragraph_text = ' '.join(current_paragraph)
|
|
244
|
-
if len(paragraph_text) >= self.min_paragraph_length:
|
|
245
|
-
paragraphs.append(paragraph_text)
|
|
246
|
-
current_paragraph = []
|
|
247
|
-
else:
|
|
248
|
-
current_paragraph.append(line)
|
|
249
|
+
current_chunk = []
|
|
250
|
+
current_length = 0
|
|
249
251
|
|
|
250
|
-
#
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
paragraphs.append(paragraph_text)
|
|
252
|
+
# 首先按句子分割
|
|
253
|
+
sentences = []
|
|
254
|
+
current_sentence = []
|
|
255
|
+
sentence_ends = {'。', '!', '?', '…', '.', '!', '?'}
|
|
255
256
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
# 按句子分割过长的段落
|
|
263
|
-
sentences = []
|
|
257
|
+
for char in text:
|
|
258
|
+
current_sentence.append(char)
|
|
259
|
+
if char in sentence_ends:
|
|
260
|
+
sentence = ''.join(current_sentence)
|
|
261
|
+
if sentence.strip():
|
|
262
|
+
sentences.append(sentence)
|
|
264
263
|
current_sentence = []
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
sentences.append(sentence)
|
|
275
|
-
current_sentence = []
|
|
276
|
-
|
|
277
|
-
# 处理最后一个句子
|
|
278
|
-
if current_sentence:
|
|
279
|
-
sentence = ''.join(current_sentence)
|
|
280
|
-
if sentence.strip():
|
|
281
|
-
sentences.append(sentence)
|
|
282
|
-
|
|
283
|
-
# 组合句子成适当长度的段落
|
|
284
|
-
current_chunk = []
|
|
285
|
-
current_length = 0
|
|
286
|
-
|
|
287
|
-
for sentence in sentences:
|
|
288
|
-
sentence_length = len(sentence)
|
|
289
|
-
if current_length + sentence_length > self.max_paragraph_length:
|
|
290
|
-
if current_chunk:
|
|
291
|
-
final_paragraphs.append(''.join(current_chunk))
|
|
292
|
-
current_chunk = [sentence]
|
|
293
|
-
current_length = sentence_length
|
|
294
|
-
else:
|
|
295
|
-
current_chunk.append(sentence)
|
|
296
|
-
current_length += sentence_length
|
|
297
|
-
|
|
298
|
-
# 处理最后一个chunk
|
|
264
|
+
|
|
265
|
+
if current_sentence:
|
|
266
|
+
sentence = ''.join(current_sentence)
|
|
267
|
+
if sentence.strip():
|
|
268
|
+
sentences.append(sentence)
|
|
269
|
+
|
|
270
|
+
# 基于句子构建重叠块
|
|
271
|
+
for sentence in sentences:
|
|
272
|
+
if current_length + len(sentence) > self.max_paragraph_length:
|
|
299
273
|
if current_chunk:
|
|
300
|
-
|
|
274
|
+
chunk_text = ' '.join(current_chunk)
|
|
275
|
+
if len(chunk_text) >= self.min_paragraph_length:
|
|
276
|
+
paragraphs.append(chunk_text)
|
|
277
|
+
|
|
278
|
+
# 保留部分内容作为重叠
|
|
279
|
+
overlap_text = ' '.join(current_chunk[-2:]) # 保留最后两句
|
|
280
|
+
current_chunk = []
|
|
281
|
+
if overlap_text:
|
|
282
|
+
current_chunk.append(overlap_text)
|
|
283
|
+
current_length = len(overlap_text)
|
|
284
|
+
else:
|
|
285
|
+
current_length = 0
|
|
286
|
+
|
|
287
|
+
current_chunk.append(sentence)
|
|
288
|
+
current_length += len(sentence)
|
|
301
289
|
|
|
302
|
-
#
|
|
303
|
-
|
|
290
|
+
# 处理最后一个chunk
|
|
291
|
+
if current_chunk:
|
|
292
|
+
chunk_text = ' '.join(current_chunk)
|
|
293
|
+
if len(chunk_text) >= self.min_paragraph_length:
|
|
294
|
+
paragraphs.append(chunk_text)
|
|
304
295
|
|
|
305
|
-
return
|
|
296
|
+
return paragraphs
|
|
306
297
|
|
|
307
298
|
def _get_embedding(self, text: str) -> np.ndarray:
|
|
308
299
|
"""获取文本的向量表示"""
|
|
@@ -410,82 +401,131 @@ class RAGTool:
|
|
|
410
401
|
output_type=OutputType.SUCCESS)
|
|
411
402
|
|
|
412
403
|
def search(self, query: str, top_k: int = 5) -> List[Tuple[Document, float]]:
|
|
413
|
-
"""
|
|
414
|
-
|
|
415
|
-
Args:
|
|
416
|
-
query: 查询文本
|
|
417
|
-
top_k: 返回结果数量
|
|
418
|
-
|
|
419
|
-
Returns:
|
|
420
|
-
文档和相似度得分的列表
|
|
421
|
-
"""
|
|
404
|
+
"""优化搜索策略"""
|
|
422
405
|
if not self.index:
|
|
423
406
|
PrettyOutput.print("索引未构建,正在构建...", output_type=OutputType.INFO)
|
|
424
407
|
self.build_index(self.root_dir)
|
|
408
|
+
|
|
409
|
+
# 实现MMR (Maximal Marginal Relevance) 来增加结果多样性
|
|
410
|
+
def mmr(query_vec, doc_vecs, doc_ids, lambda_param=0.5, n_docs=top_k):
|
|
411
|
+
selected = []
|
|
412
|
+
selected_ids = []
|
|
413
|
+
|
|
414
|
+
while len(selected) < n_docs and len(doc_ids) > 0:
|
|
415
|
+
best_score = -1
|
|
416
|
+
best_idx = -1
|
|
417
|
+
|
|
418
|
+
for i, (doc_vec, doc_id) in enumerate(zip(doc_vecs, doc_ids)):
|
|
419
|
+
# 计算与查询的相似度
|
|
420
|
+
query_sim = float(np.dot(query_vec, doc_vec))
|
|
421
|
+
|
|
422
|
+
# 计算与已选文档的最大相似度
|
|
423
|
+
if selected:
|
|
424
|
+
doc_sims = [float(np.dot(doc_vec, selected_doc)) for selected_doc in selected]
|
|
425
|
+
max_doc_sim = max(doc_sims)
|
|
426
|
+
else:
|
|
427
|
+
max_doc_sim = 0
|
|
428
|
+
|
|
429
|
+
# MMR score
|
|
430
|
+
score = lambda_param * query_sim - (1 - lambda_param) * max_doc_sim
|
|
431
|
+
|
|
432
|
+
if score > best_score:
|
|
433
|
+
best_score = score
|
|
434
|
+
best_idx = i
|
|
435
|
+
|
|
436
|
+
if best_idx == -1:
|
|
437
|
+
break
|
|
438
|
+
|
|
439
|
+
selected.append(doc_vecs[best_idx])
|
|
440
|
+
selected_ids.append(doc_ids[best_idx])
|
|
441
|
+
doc_vecs = np.delete(doc_vecs, best_idx, axis=0)
|
|
442
|
+
doc_ids = np.delete(doc_ids, best_idx)
|
|
425
443
|
|
|
426
|
-
|
|
444
|
+
return selected_ids
|
|
445
|
+
|
|
446
|
+
# 获取查询向量
|
|
427
447
|
query_vector = self._get_embedding(query)
|
|
428
448
|
query_vector = query_vector.reshape(1, -1)
|
|
429
449
|
|
|
430
|
-
#
|
|
431
|
-
|
|
450
|
+
# 初始搜索更多结果用于MMR
|
|
451
|
+
initial_k = min(top_k * 2, len(self.documents))
|
|
452
|
+
distances, indices = self.index.search(query_vector, initial_k)
|
|
453
|
+
|
|
454
|
+
# 获取有效结果
|
|
455
|
+
valid_indices = indices[0][indices[0] != -1]
|
|
456
|
+
valid_vectors = np.vstack([self._get_embedding(self.documents[idx].content) for idx in valid_indices])
|
|
432
457
|
|
|
433
|
-
#
|
|
458
|
+
# 应用MMR
|
|
459
|
+
final_indices = mmr(query_vector[0], valid_vectors, valid_indices, n_docs=top_k)
|
|
460
|
+
|
|
461
|
+
# 构建结果
|
|
434
462
|
results = []
|
|
435
|
-
|
|
463
|
+
for idx in final_indices:
|
|
464
|
+
doc = self.documents[idx]
|
|
465
|
+
similarity = 1.0 / (1.0 + float(distances[0][np.where(indices[0] == idx)[0][0]]))
|
|
466
|
+
results.append((doc, similarity))
|
|
436
467
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
468
|
+
return results
|
|
469
|
+
|
|
470
|
+
def _rerank_results(self, query: str, initial_results: List[Tuple[Document, float]]) -> List[Tuple[Document, float]]:
|
|
471
|
+
"""使用 rerank 模型重新排序搜索结果"""
|
|
472
|
+
try:
|
|
473
|
+
import torch
|
|
474
|
+
model, tokenizer = load_rerank_model()
|
|
475
|
+
|
|
476
|
+
# 准备数据
|
|
477
|
+
pairs = []
|
|
478
|
+
for doc, _ in initial_results:
|
|
479
|
+
# 组合文档信息
|
|
480
|
+
doc_content = f"""
|
|
481
|
+
文件: {doc.metadata['file_path']}
|
|
482
|
+
内容: {doc.content}
|
|
483
|
+
"""
|
|
484
|
+
pairs.append([query, doc_content])
|
|
440
485
|
|
|
441
|
-
|
|
442
|
-
|
|
486
|
+
# 对每个文档对进行打分
|
|
487
|
+
scores = []
|
|
488
|
+
batch_size = 8
|
|
443
489
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
490
|
+
with torch.no_grad():
|
|
491
|
+
for i in range(0, len(pairs), batch_size):
|
|
492
|
+
batch_pairs = pairs[i:i + batch_size]
|
|
493
|
+
encoded = tokenizer(
|
|
494
|
+
batch_pairs,
|
|
495
|
+
padding=True,
|
|
496
|
+
truncation=True,
|
|
497
|
+
max_length=512,
|
|
498
|
+
return_tensors='pt'
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
if torch.cuda.is_available():
|
|
502
|
+
encoded = {k: v.cuda() for k, v in encoded.items()}
|
|
503
|
+
|
|
504
|
+
outputs = model(**encoded)
|
|
505
|
+
batch_scores = outputs.logits.squeeze(-1).cpu().numpy()
|
|
506
|
+
scores.extend(batch_scores.tolist())
|
|
447
507
|
|
|
448
|
-
#
|
|
449
|
-
|
|
508
|
+
# 归一化分数到 0-1 范围
|
|
509
|
+
if scores:
|
|
510
|
+
min_score = min(scores)
|
|
511
|
+
max_score = max(scores)
|
|
512
|
+
if max_score > min_score:
|
|
513
|
+
scores = [(s - min_score) / (max_score - min_score) for s in scores]
|
|
450
514
|
|
|
451
|
-
#
|
|
452
|
-
|
|
453
|
-
for
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
content_parts.extend(file_docs[i].content for i in range(start_idx, current_idx))
|
|
460
|
-
content_parts.append(doc.content)
|
|
461
|
-
content_parts.extend(file_docs[i].content for i in range(current_idx + 1, end_idx))
|
|
462
|
-
|
|
463
|
-
merged_content = "\n".join(content_parts)
|
|
464
|
-
|
|
465
|
-
# 创建文档对象
|
|
466
|
-
context_doc = Document(
|
|
467
|
-
content=merged_content,
|
|
468
|
-
metadata={
|
|
469
|
-
**doc.metadata,
|
|
470
|
-
"similarity": similarity
|
|
471
|
-
}
|
|
472
|
-
)
|
|
473
|
-
|
|
474
|
-
# 计算添加这个结果后的总长度
|
|
475
|
-
total_content_length = len(merged_content)
|
|
476
|
-
|
|
477
|
-
# 检查是否在长度限制内
|
|
478
|
-
if current_length + total_content_length <= self.max_context_length:
|
|
479
|
-
results.append((context_doc, similarity))
|
|
480
|
-
current_length += total_content_length
|
|
481
|
-
added = True
|
|
482
|
-
break
|
|
515
|
+
# 将分数与文档组合并排序
|
|
516
|
+
scored_results = []
|
|
517
|
+
for (doc, _), score in zip(initial_results, scores):
|
|
518
|
+
if score >= 0.5: # 只保留关联度大于 0.5 的结果
|
|
519
|
+
scored_results.append((doc, float(score)))
|
|
520
|
+
|
|
521
|
+
# 按分数降序排序
|
|
522
|
+
scored_results.sort(key=lambda x: x[1], reverse=True)
|
|
483
523
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
524
|
+
return scored_results
|
|
525
|
+
|
|
526
|
+
except Exception as e:
|
|
527
|
+
PrettyOutput.print(f"重排序失败,使用原始排序: {str(e)}", output_type=OutputType.WARNING)
|
|
528
|
+
return initial_results
|
|
489
529
|
|
|
490
530
|
def is_index_built(self):
|
|
491
531
|
"""检查索引是否已构建"""
|
|
@@ -567,9 +607,12 @@ def main():
|
|
|
567
607
|
args = parser.parse_args()
|
|
568
608
|
|
|
569
609
|
try:
|
|
570
|
-
current_dir =
|
|
610
|
+
current_dir = os.getcwd()
|
|
571
611
|
rag = RAGTool(current_dir)
|
|
572
612
|
|
|
613
|
+
if not args.dir:
|
|
614
|
+
args.dir = current_dir
|
|
615
|
+
|
|
573
616
|
if args.dir and args.build:
|
|
574
617
|
PrettyOutput.print(f"正在处理目录: {args.dir}", output_type=OutputType.INFO)
|
|
575
618
|
rag.build_index(args.dir)
|
|
@@ -2,7 +2,7 @@ import mimetypes
|
|
|
2
2
|
import os
|
|
3
3
|
from typing import Dict, List
|
|
4
4
|
from jarvis.models.base import BasePlatform
|
|
5
|
-
from jarvis.utils import PrettyOutput, OutputType
|
|
5
|
+
from jarvis.utils import PrettyOutput, OutputType, get_max_context_length
|
|
6
6
|
import requests
|
|
7
7
|
import json
|
|
8
8
|
|
|
@@ -72,10 +72,10 @@ class OyiModel(BasePlatform):
|
|
|
72
72
|
"is_webSearch": True,
|
|
73
73
|
"message": [],
|
|
74
74
|
"systemMessage": None,
|
|
75
|
-
"requestMsgCount":
|
|
75
|
+
"requestMsgCount": 65536,
|
|
76
76
|
"temperature": 0.8,
|
|
77
77
|
"speechVoice": "Alloy",
|
|
78
|
-
"max_tokens":
|
|
78
|
+
"max_tokens": get_max_context_length(),
|
|
79
79
|
"chatPluginIds": []
|
|
80
80
|
})
|
|
81
81
|
}
|
|
@@ -19,7 +19,8 @@ class ToolRegistry:
|
|
|
19
19
|
# 加载内置工具和外部工具
|
|
20
20
|
self._load_builtin_tools()
|
|
21
21
|
self._load_external_tools()
|
|
22
|
-
|
|
22
|
+
# 确保 max_context_length 是整数
|
|
23
|
+
self.max_context_length = int(get_max_context_length() * 0.8)
|
|
23
24
|
|
|
24
25
|
@staticmethod
|
|
25
26
|
def get_global_tool_registry():
|
|
@@ -181,10 +182,11 @@ class ToolRegistry:
|
|
|
181
182
|
PrettyOutput.print("输出较长,正在总结...", OutputType.PROGRESS)
|
|
182
183
|
model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
|
|
183
184
|
|
|
184
|
-
#
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
185
|
+
# 如果输出超过最大上下文长度,只取最后部分
|
|
186
|
+
max_len = self.max_context_length
|
|
187
|
+
if len(output) > max_len:
|
|
188
|
+
output_to_summarize = output[-max_len:]
|
|
189
|
+
truncation_notice = f"\n(注意: 由于输出过长,仅总结最后{max_len}字符)"
|
|
188
190
|
else:
|
|
189
191
|
output_to_summarize = output
|
|
190
192
|
truncation_notice = ""
|
|
@@ -10,6 +10,8 @@ from prompt_toolkit import PromptSession
|
|
|
10
10
|
from prompt_toolkit.styles import Style as PromptStyle
|
|
11
11
|
from prompt_toolkit.formatted_text import FormattedText
|
|
12
12
|
from sentence_transformers import SentenceTransformer
|
|
13
|
+
from transformers import AutoModelForSequenceClassification, AutoTokenizer
|
|
14
|
+
import torch
|
|
13
15
|
|
|
14
16
|
# 初始化colorama
|
|
15
17
|
colorama.init()
|
|
@@ -211,7 +213,7 @@ def find_git_root(dir="."):
|
|
|
211
213
|
|
|
212
214
|
def load_embedding_model():
|
|
213
215
|
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
|
214
|
-
model_name =
|
|
216
|
+
model_name = "BAAI/bge-large-zh-v1.5"
|
|
215
217
|
PrettyOutput.print(f"正在加载嵌入模型: {model_name}...", OutputType.INFO)
|
|
216
218
|
try:
|
|
217
219
|
# 首先尝试离线加载
|
|
@@ -234,6 +236,44 @@ def load_embedding_model():
|
|
|
234
236
|
|
|
235
237
|
return embedding_model
|
|
236
238
|
|
|
239
|
+
def load_rerank_model():
|
|
240
|
+
"""加载重排序模型"""
|
|
241
|
+
model_name = "BAAI/bge-reranker-v2-m3"
|
|
242
|
+
PrettyOutput.print(f"正在加载重排序模型: {model_name}...", OutputType.INFO)
|
|
243
|
+
|
|
244
|
+
try:
|
|
245
|
+
# 首先尝试离线加载
|
|
246
|
+
tokenizer = AutoTokenizer.from_pretrained(
|
|
247
|
+
model_name,
|
|
248
|
+
local_files_only=True,
|
|
249
|
+
cache_dir=os.path.expanduser("~/.cache/huggingface/hub")
|
|
250
|
+
)
|
|
251
|
+
model = AutoModelForSequenceClassification.from_pretrained(
|
|
252
|
+
model_name,
|
|
253
|
+
local_files_only=True,
|
|
254
|
+
cache_dir=os.path.expanduser("~/.cache/huggingface/hub")
|
|
255
|
+
)
|
|
256
|
+
PrettyOutput.print("使用本地缓存加载模型成功", OutputType.SUCCESS)
|
|
257
|
+
except Exception as local_error:
|
|
258
|
+
PrettyOutput.print(f"本地加载失败,尝试在线下载: {str(local_error)}", OutputType.WARNING)
|
|
259
|
+
# 如果离线加载失败,尝试在线下载
|
|
260
|
+
tokenizer = AutoTokenizer.from_pretrained(
|
|
261
|
+
model_name,
|
|
262
|
+
cache_dir=os.path.expanduser("~/.cache/huggingface/hub")
|
|
263
|
+
)
|
|
264
|
+
model = AutoModelForSequenceClassification.from_pretrained(
|
|
265
|
+
model_name,
|
|
266
|
+
cache_dir=os.path.expanduser("~/.cache/huggingface/hub")
|
|
267
|
+
)
|
|
268
|
+
PrettyOutput.print("模型下载并加载成功", OutputType.SUCCESS)
|
|
269
|
+
|
|
270
|
+
# 如果有 GPU 就使用 GPU
|
|
271
|
+
if torch.cuda.is_available():
|
|
272
|
+
model = model.cuda()
|
|
273
|
+
model.eval()
|
|
274
|
+
|
|
275
|
+
return model, tokenizer
|
|
276
|
+
|
|
237
277
|
def get_max_context_length():
|
|
238
278
|
return int(os.getenv('JARVIS_MAX_CONTEXT_LENGTH', '131072')) # 默认128k
|
|
239
279
|
|
{jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87/src/jarvis_ai_assistant.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: jarvis-ai-assistant
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.87
|
|
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
|
|
@@ -134,7 +134,6 @@ Jarvis supports configuration through environment variables that can be set in t
|
|
|
134
134
|
| JARVIS_CODEGEN_MODEL | Model name for code generation | Same as JARVIS_MODEL | No |
|
|
135
135
|
| JARVIS_CHEAP_PLATFORM | AI platform for cheap operations | Same as JARVIS_PLATFORM | No |
|
|
136
136
|
| JARVIS_CHEAP_MODEL | Model name for cheap operations | Same as JARVIS_MODEL | No |
|
|
137
|
-
| JARVIS_EMBEDDING_MODEL | Embedding model for code analysis | BAAI/bge-large-zh-v1.5 | No |
|
|
138
137
|
| OPENAI_API_KEY | API key for OpenAI platform | - | Required for OpenAI |
|
|
139
138
|
| OPENAI_API_BASE | Base URL for OpenAI API | https://api.deepseek.com | No |
|
|
140
139
|
| OPENAI_MODEL_NAME | Model name for OpenAI | deepseek-chat | No |
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/jarvis_codebase/__init__.py
RENAMED
|
File without changes
|
{jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/jarvis_coder/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/jarvis_smart_shell/__init__.py
RENAMED
|
File without changes
|
{jarvis_ai_assistant-0.1.86 → jarvis_ai_assistant-0.1.87}/src/jarvis/jarvis_smart_shell/main.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|