jarvis-ai-assistant 0.1.73__tar.gz → 0.1.75__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.

Files changed (40) hide show
  1. {jarvis_ai_assistant-0.1.73/src/jarvis_ai_assistant.egg-info → jarvis_ai_assistant-0.1.75}/PKG-INFO +1 -1
  2. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/pyproject.toml +1 -1
  3. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/setup.py +1 -1
  4. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/__init__.py +1 -1
  5. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/agent.py +104 -8
  6. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/jarvis_codebase/main.py +124 -18
  7. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/jarvis_coder/main.py +73 -24
  8. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/tools/codebase_qa.py +2 -1
  9. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75/src/jarvis_ai_assistant.egg-info}/PKG-INFO +1 -1
  10. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/LICENSE +0 -0
  11. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/MANIFEST.in +0 -0
  12. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/README.md +0 -0
  13. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/setup.cfg +0 -0
  14. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/jarvis_codebase/__init__.py +0 -0
  15. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/jarvis_coder/__init__.py +0 -0
  16. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/main.py +0 -0
  17. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/models/__init__.py +0 -0
  18. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/models/ai8.py +0 -0
  19. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/models/base.py +0 -0
  20. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/models/kimi.py +0 -0
  21. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/models/openai.py +0 -0
  22. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/models/oyi.py +0 -0
  23. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/models/registry.py +0 -0
  24. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/tools/__init__.py +0 -0
  25. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/tools/base.py +0 -0
  26. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/tools/coder.py +0 -0
  27. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/tools/file_ops.py +0 -0
  28. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/tools/generator.py +0 -0
  29. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/tools/methodology.py +0 -0
  30. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/tools/registry.py +0 -0
  31. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/tools/search.py +0 -0
  32. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/tools/shell.py +0 -0
  33. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/tools/sub_agent.py +0 -0
  34. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/tools/webpage.py +0 -0
  35. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis/utils.py +0 -0
  36. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis_ai_assistant.egg-info/SOURCES.txt +0 -0
  37. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis_ai_assistant.egg-info/dependency_links.txt +0 -0
  38. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis_ai_assistant.egg-info/entry_points.txt +0 -0
  39. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis_ai_assistant.egg-info/requires.txt +0 -0
  40. {jarvis_ai_assistant-0.1.73 → jarvis_ai_assistant-0.1.75}/src/jarvis_ai_assistant.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.1.73
3
+ Version: 0.1.75
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "jarvis-ai-assistant"
7
- version = "0.1.73"
7
+ version = "0.1.75"
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.73",
5
+ version="0.1.75",
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",
@@ -1,3 +1,3 @@
1
1
  """Jarvis AI Assistant"""
2
2
 
3
- __version__ = "0.1.73"
3
+ __version__ = "0.1.75"
@@ -2,6 +2,9 @@ import time
2
2
  from typing import Dict, List, Optional
3
3
 
4
4
  import yaml
5
+ import numpy as np
6
+ import faiss
7
+ import json
5
8
 
6
9
  from .models.registry import PlatformRegistry
7
10
  from .tools import ToolRegistry
@@ -9,6 +12,7 @@ from .utils import PrettyOutput, OutputType, get_multiline_input, while_success
9
12
  import os
10
13
  from datetime import datetime
11
14
  from prompt_toolkit import prompt
15
+ from sentence_transformers import SentenceTransformer
12
16
 
13
17
  class Agent:
14
18
  def __init__(self, name: str = "Jarvis", is_sub_agent: bool = False):
@@ -26,7 +30,37 @@ class Agent:
26
30
  self.is_sub_agent = is_sub_agent
27
31
  self.prompt = ""
28
32
  self.conversation_turns = 0
29
-
33
+
34
+ # 从环境变量加载嵌入模型配置
35
+ self.embedding_model_name = os.environ.get("JARVIS_EMBEDDING_MODEL", "BAAI/bge-large-zh-v1.5")
36
+ self.embedding_dimension = 1536 # Default for many embedding models
37
+
38
+ # 初始化嵌入模型
39
+ try:
40
+ os.environ["TOKENIZERS_PARALLELISM"] = "false"
41
+ PrettyOutput.print(f"正在加载嵌入模型: {self.embedding_model_name}...", OutputType.INFO)
42
+ self.embedding_model = SentenceTransformer(self.embedding_model_name)
43
+
44
+ # 预热模型并获取正确的维度
45
+ test_text = "这是一段测试文本,用于确保模型完全加载。"
46
+ test_embedding = self.embedding_model.encode(test_text,
47
+ convert_to_tensor=True,
48
+ normalize_embeddings=True)
49
+ self.embedding_dimension = len(test_embedding)
50
+ PrettyOutput.print("嵌入模型加载完成", OutputType.SUCCESS)
51
+
52
+ # 初始化HNSW索引(使用正确的维度)
53
+ hnsw_index = faiss.IndexHNSWFlat(self.embedding_dimension, 16)
54
+ hnsw_index.hnsw.efConstruction = 40
55
+ hnsw_index.hnsw.efSearch = 16
56
+ self.methodology_index = faiss.IndexIDMap(hnsw_index)
57
+
58
+ except Exception as e:
59
+ PrettyOutput.print(f"加载嵌入模型失败: {str(e)}", OutputType.ERROR)
60
+ raise
61
+
62
+ # 初始化方法论相关属性
63
+ self.methodology_data = []
30
64
 
31
65
  @staticmethod
32
66
  def extract_tool_calls(content: str) -> List[Dict]:
@@ -87,16 +121,77 @@ class Agent:
87
121
  sleep_time = 30
88
122
  continue
89
123
 
124
+ def _create_methodology_embedding(self, methodology_text: str) -> np.ndarray:
125
+ """为方法论文本创建嵌入向量"""
126
+ try:
127
+ # 对长文本进行截断
128
+ max_length = 512
129
+ text = ' '.join(methodology_text.split()[:max_length])
130
+
131
+ # 使用sentence_transformers模型获取嵌入向量
132
+ embedding = self.embedding_model.encode([text],
133
+ convert_to_tensor=True,
134
+ normalize_embeddings=True)
135
+ vector = np.array(embedding, dtype=np.float32)
136
+ return vector[0] # 返回第一个向量,因为我们只编码了一个文本
137
+ except Exception as e:
138
+ PrettyOutput.print(f"创建方法论嵌入向量失败: {str(e)}", OutputType.ERROR)
139
+ return np.zeros(self.embedding_dimension, dtype=np.float32)
90
140
 
91
- def _load_methodology(self) -> Dict[str, str]:
92
- """加载方法论"""
141
+ def _load_methodology(self, user_input: str) -> Dict[str, str]:
142
+ """加载方法论并构建向量索引"""
93
143
  user_jarvis_methodology = os.path.expanduser("~/.jarvis_methodology")
94
- if os.path.exists(user_jarvis_methodology):
144
+ if not os.path.exists(user_jarvis_methodology):
145
+ return {}
146
+
147
+ try:
95
148
  with open(user_jarvis_methodology, "r", encoding="utf-8") as f:
96
149
  data = yaml.safe_load(f)
97
- PrettyOutput.print(f"从 {user_jarvis_methodology} 加载方法论: {', '.join(data.keys())}", OutputType.INFO)
98
- return data
99
- return {}
150
+
151
+ # 重置数据结构
152
+ self.methodology_data = []
153
+ vectors = []
154
+ ids = []
155
+
156
+ # 为每个方法论创建嵌入向量
157
+ for i, (key, value) in enumerate(data.items()):
158
+ PrettyOutput.print(f"向量化方法论: {key} ...", OutputType.INFO)
159
+ methodology_text = f"{key}\n{value}"
160
+ embedding = self._create_methodology_embedding(methodology_text)
161
+ vectors.append(embedding)
162
+ ids.append(i)
163
+ self.methodology_data.append({"key": key, "value": value})
164
+
165
+ if vectors:
166
+ vectors_array = np.vstack(vectors)
167
+ self.methodology_index.add_with_ids(vectors_array, np.array(ids))
168
+ query_embedding = self._create_methodology_embedding(user_input)
169
+ k = min(5, len(self.methodology_data))
170
+ PrettyOutput.print(f"检索方法论...", OutputType.INFO)
171
+ distances, indices = self.methodology_index.search(
172
+ query_embedding.reshape(1, -1), k
173
+ )
174
+
175
+ relevant_methodologies = {}
176
+ for dist, idx in zip(distances[0], indices[0]):
177
+ if idx >= 0:
178
+ similarity = 1.0 / (1.0 + float(dist))
179
+ methodology = self.methodology_data[idx]
180
+ PrettyOutput.print(
181
+ f"方法论 '{methodology['key']}' 相似度: {similarity:.3f}",
182
+ OutputType.INFO
183
+ )
184
+ if similarity >= 0.5:
185
+ relevant_methodologies[methodology["key"]] = methodology["value"]
186
+
187
+ if relevant_methodologies:
188
+ return relevant_methodologies
189
+
190
+ return {}
191
+
192
+ except Exception as e:
193
+ PrettyOutput.print(f"加载方法论时发生错误: {str(e)}", OutputType.ERROR)
194
+ return {}
100
195
 
101
196
  def _summarize_and_clear_history(self) -> None:
102
197
  """总结当前对话历史并清空历史记录,只保留系统消息和总结
@@ -211,7 +306,7 @@ class Agent:
211
306
  self.model.upload_files(file_list)
212
307
 
213
308
  # 加载方法论
214
- methodology = self._load_methodology()
309
+ methodology = self._load_methodology(user_input)
215
310
  methodology_prompt = ""
216
311
  if methodology:
217
312
  methodology_prompt = f"""这是以往处理问题的标准方法论,如果当前任务与此类似,可参考:
@@ -277,6 +372,7 @@ arguments:
277
372
  6. 处理问题的每个步骤不是必须有的,可按情况省略
278
373
  7. 在执行一些可能对系统或者用户代码库造成破坏的工具时,请先询问用户
279
374
  8. 在多次迭代却没有任何进展时,可请求用户指导
375
+ 9. 如果返回的yaml字符串中包含冒号,请将整个字符串用引号包裹,避免yaml解析错误
280
376
 
281
377
  -------------------------------------------------------------
282
378
 
@@ -310,27 +310,103 @@ class CodeBase:
310
310
  self.build_index()
311
311
  self.save_cache()
312
312
 
313
- def generate_codebase(self):
314
- """生成代码库索引"""
315
- files_deleted = self.clean_cache() # 清理过期缓存
316
- processed_files = []
313
+
314
+ def generate_codebase(self, force: bool = False):
315
+ """生成代码库索引
316
+ Args:
317
+ force: 是否强制重建索引,不询问用户
318
+ """
319
+ # 更新 git 文件列表
320
+ self.git_file_list = self.get_git_file_list()
321
+
322
+ # 检查文件变化
323
+ changes_detected = False
324
+ new_files = []
325
+ modified_files = []
326
+ deleted_files = []
327
+
328
+ # 检查删除的文件
329
+ files_to_delete = []
330
+ for file_path in list(self.vector_cache.keys()):
331
+ if file_path not in self.git_file_list:
332
+ deleted_files.append(file_path)
333
+ files_to_delete.append(file_path)
334
+ changes_detected = True
335
+
336
+ # 检查新增和修改的文件
337
+ for file_path in self.git_file_list:
338
+ if not os.path.exists(file_path) or not self.is_text_file(file_path):
339
+ continue
340
+
341
+ try:
342
+ current_md5 = hashlib.md5(open(file_path, "rb").read()).hexdigest()
343
+
344
+ if file_path not in self.vector_cache:
345
+ new_files.append(file_path)
346
+ changes_detected = True
347
+ elif self.vector_cache[file_path].get("md5") != current_md5:
348
+ modified_files.append(file_path)
349
+ changes_detected = True
350
+ except Exception as e:
351
+ PrettyOutput.print(f"检查文件失败 {file_path}: {str(e)}",
352
+ output_type=OutputType.ERROR)
353
+ continue
317
354
 
318
- # 使用线程池处理文件
319
- with ThreadPoolExecutor(max_workers=self.thread_count) as executor:
320
- futures = [executor.submit(self.process_file, file) for file in self.git_file_list]
321
- for future in concurrent.futures.as_completed(futures):
322
- result = future.result()
323
- if result:
324
- processed_files.append(result)
325
- PrettyOutput.print(f"索引文件: {result}", output_type=OutputType.INFO)
326
-
327
- if files_deleted or processed_files:
355
+ # 如果检测到变化,显示变化并询问用户
356
+ if changes_detected:
357
+ PrettyOutput.print("\n检测到以下变化:", output_type=OutputType.WARNING)
358
+ if new_files:
359
+ PrettyOutput.print("\n新增文件:", output_type=OutputType.INFO)
360
+ for f in new_files:
361
+ PrettyOutput.print(f" {f}", output_type=OutputType.INFO)
362
+ if modified_files:
363
+ PrettyOutput.print("\n修改的文件:", output_type=OutputType.INFO)
364
+ for f in modified_files:
365
+ PrettyOutput.print(f" {f}", output_type=OutputType.INFO)
366
+ if deleted_files:
367
+ PrettyOutput.print("\n删除的文件:", output_type=OutputType.INFO)
368
+ for f in deleted_files:
369
+ PrettyOutput.print(f" {f}", output_type=OutputType.INFO)
370
+
371
+
372
+ # 如果force为True,直接继续
373
+ if not force:
374
+ # 询问用户是否继续
375
+ while True:
376
+ response = input("\n是否重建索引?[y/N] ").lower().strip()
377
+ if response in ['y', 'yes']:
378
+ break
379
+ elif response in ['', 'n', 'no']:
380
+ PrettyOutput.print("取消重建索引", output_type=OutputType.INFO)
381
+ return
382
+ else:
383
+ PrettyOutput.print("请输入 y 或 n", output_type=OutputType.WARNING)
384
+
385
+ # 清理已删除的文件
386
+ for file_path in files_to_delete:
387
+ del self.vector_cache[file_path]
388
+ if files_to_delete:
389
+ PrettyOutput.print(f"清理了 {len(files_to_delete)} 个文件的缓存",
390
+ output_type=OutputType.INFO)
391
+
392
+ # 处理新文件和修改的文件
393
+ processed_files = []
394
+ files_to_process = new_files + modified_files
395
+
396
+ # 使用线程池处理文件
397
+ with ThreadPoolExecutor(max_workers=self.thread_count) as executor:
398
+ futures = [executor.submit(self.process_file, file) for file in files_to_process]
399
+ for future in concurrent.futures.as_completed(futures):
400
+ result = future.result()
401
+ if result:
402
+ processed_files.append(result)
403
+ PrettyOutput.print(f"索引文件: {result}", output_type=OutputType.INFO)
404
+
328
405
  PrettyOutput.print("重新生成向量数据库", output_type=OutputType.INFO)
329
406
  self.gen_vector_db_from_cache()
407
+ PrettyOutput.print(f"成功为 {len(processed_files)} 个文件生成索引", output_type=OutputType.INFO)
330
408
  else:
331
- PrettyOutput.print("没有新的文件变更,跳过向量数据库生成", output_type=OutputType.INFO)
332
-
333
- PrettyOutput.print(f"成功为 {len(processed_files)} 个文件生成索引", output_type=OutputType.INFO)
409
+ PrettyOutput.print("没有检测到文件变更,无需重建索引", output_type=OutputType.INFO)
334
410
 
335
411
  def rerank_results(self, query: str, initial_results: List[Tuple[str, float, str]]) -> List[Tuple[str, float, str]]:
336
412
  """使用大模型对搜索结果重新排序"""
@@ -488,6 +564,31 @@ class CodeBase:
488
564
  finally:
489
565
  model.delete_chat()
490
566
 
567
+ def is_index_generated(self) -> bool:
568
+ """检查索引是否已经生成"""
569
+ # 检查缓存文件是否存在
570
+ if not os.path.exists(self.cache_path):
571
+ return False
572
+
573
+ # 检查缓存是否有效
574
+ try:
575
+ with open(self.cache_path, 'rb') as f:
576
+ cache_data = pickle.load(f)
577
+ if not cache_data.get("vectors") or not cache_data.get("file_paths"):
578
+ return False
579
+ except Exception:
580
+ return False
581
+
582
+ # 检查索引是否已构建
583
+ if not hasattr(self, 'index') or self.index is None:
584
+ return False
585
+
586
+ # 检查向量缓存和文件路径列表是否非空
587
+ if not self.vector_cache or not self.file_paths:
588
+ return False
589
+
590
+ return True
591
+
491
592
 
492
593
 
493
594
  def main():
@@ -501,10 +602,15 @@ def main():
501
602
  current_dir = find_git_root()
502
603
  codebase = CodeBase(current_dir)
503
604
 
605
+ # 如果没有生成索引,且不是生成命令,提示用户先生成索引
606
+ if not codebase.is_index_generated() and not args.generate:
607
+ PrettyOutput.print("索引尚未生成,请先运行 --generate 生成索引", output_type=OutputType.WARNING)
608
+ return
609
+
504
610
 
505
611
  if args.generate:
506
612
  try:
507
- codebase.generate_codebase()
613
+ codebase.generate_codebase(force=True)
508
614
  PrettyOutput.print("\nCodebase generation completed", output_type=OutputType.SUCCESS)
509
615
  except Exception as e:
510
616
  PrettyOutput.print(f"Error during codebase generation: {str(e)}", output_type=OutputType.ERROR)
@@ -278,12 +278,12 @@ class JarvisCoder:
278
278
 
279
279
  return True, ""
280
280
 
281
- def _save_edit_record(self, feature: str, patches: List[str]) -> None:
281
+ def _save_edit_record(self, commit_message: str, git_diff: str) -> None:
282
282
  """保存代码修改记录
283
283
 
284
284
  Args:
285
- feature: 需求描述
286
- patches: 补丁列表
285
+ commit_message: 提交信息
286
+ git_diff: git diff --cached的输出
287
287
  """
288
288
 
289
289
  # 获取下一个序号
@@ -296,8 +296,8 @@ class JarvisCoder:
296
296
  # 创建记录文件
297
297
  record = {
298
298
  "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
299
- "feature": feature,
300
- "patches": patches
299
+ "commit_message": commit_message,
300
+ "git_diff": git_diff
301
301
  }
302
302
 
303
303
  record_path = os.path.join(self.record_dir, f"{next_num:04d}.yaml")
@@ -312,20 +312,7 @@ class JarvisCoder:
312
312
  def _prepare_execution(self) -> None:
313
313
  """准备执行环境"""
314
314
  self.main_model = self._new_model()
315
-
316
-
317
- # 询问用户是否更新索引数据库
318
- while True:
319
- answer = get_multiline_input("是否要更新代码库索引数据库?(y/n)").strip().lower()
320
- if answer == 'y':
321
- PrettyOutput.print("正在更新代码库索引数据库...", OutputType.PROGRESS)
322
- self._codebase.generate_codebase()
323
- break
324
- elif answer == 'n' or not answer:
325
- PrettyOutput.print("跳过代码库索引数据库更新", OutputType.INFO)
326
- break
327
- else:
328
- PrettyOutput.print("请输入 y 或 n", OutputType.WARNING)
315
+ self._codebase.generate_codebase()
329
316
 
330
317
 
331
318
  def _load_related_files(self, feature: str) -> List[Dict]:
@@ -359,7 +346,7 @@ class JarvisCoder:
359
346
  if success:
360
347
  user_confirm = input("是否确认修改?(y/n)")
361
348
  if user_confirm.lower() == "y":
362
- self._finalize_changes(feature, patches)
349
+ self._finalize_changes(feature)
363
350
  return {
364
351
  "success": True,
365
352
  "stdout": f"已完成功能开发{feature}",
@@ -389,12 +376,74 @@ class JarvisCoder:
389
376
  """
390
377
  patches = self._remake_patch(retry_prompt)
391
378
 
392
- def _finalize_changes(self, feature: str, patches: List[str]) -> None:
379
+
380
+
381
+
382
+ def _generate_commit_message(self, git_diff: str, feature: str) -> str:
383
+ """根据git diff和功能描述生成commit信息
384
+
385
+ Args:
386
+ git_diff: git diff --cached的输出
387
+ feature: 用户的功能描述
388
+
389
+ Returns:
390
+ str: 生成的commit信息
391
+ """
392
+
393
+ # 生成提示词
394
+ prompt = f"""你是一个经验丰富的程序员,请根据以下代码变更和功能描述生成简洁明了的commit信息:
395
+
396
+ 功能描述:
397
+ {feature}
398
+
399
+ 代码变更:
400
+ """
401
+ # 添加git diff内容
402
+ prompt += f"Git Diff:\n{git_diff}\n\n"
403
+
404
+ prompt += """
405
+ 请遵循以下规则:
406
+ 1. 使用英文编写
407
+ 2. 采用常规的commit message格式:<type>(<scope>): <subject>
408
+ 3. 保持简洁,不超过50个字符
409
+ 4. 准确描述代码变更的主要内容
410
+ 5. 优先考虑功能描述和git diff中的变更内容
411
+ """
412
+
413
+ # 使用normal模型生成commit信息
414
+ model = PlatformRegistry().get_global_platform_registry().create_platform(self.platform)
415
+ model.set_model_name(self.model)
416
+ model.set_suppress_output(True)
417
+ success, response = self._call_model_with_retry(model, prompt)
418
+ if not success:
419
+ return "Update code changes"
420
+
421
+ # 清理响应内容
422
+ return response.strip().split("\n")[0]
423
+
424
+ def _finalize_changes(self, feature: str) -> None:
393
425
  """完成修改并提交"""
394
426
  PrettyOutput.print("修改确认成功,提交修改", OutputType.INFO)
395
- os.system(f"git add .")
396
- os.system(f"git commit -m '{feature}'")
397
- self._save_edit_record(feature, patches)
427
+
428
+ # 只添加已经在 git 控制下的修改文件
429
+ os.system("git add -u")
430
+
431
+ # 然后获取 git diff
432
+ git_diff = os.popen("git diff --cached").read()
433
+
434
+ # 自动生成commit信息,传入feature
435
+ commit_message = self._generate_commit_message(git_diff, feature)
436
+
437
+ # 显示并确认commit信息
438
+ PrettyOutput.print(f"自动生成的commit信息: {commit_message}", OutputType.INFO)
439
+ user_confirm = input("是否使用该commit信息?(y/n) [y]: ") or "y"
440
+
441
+ if user_confirm.lower() != "y":
442
+ commit_message = input("请输入新的commit信息: ")
443
+
444
+ # 不需要再次 git add,因为已经添加过了
445
+ os.system(f"git commit -m '{commit_message}'")
446
+ self._save_edit_record(commit_message, git_diff)
398
447
 
399
448
  def _revert_changes(self) -> None:
400
449
  """回退所有修改"""
@@ -49,7 +49,8 @@ class CodebaseQATool:
49
49
  os.chdir(root_dir)
50
50
  codebase = CodeBase(root_dir)
51
51
  # 生成索引
52
- codebase.generate_codebase()
52
+
53
+ codebase.generate_codebase(force=True)
53
54
  # 执行问答
54
55
  response = codebase.ask_codebase(question, top_k)
55
56
  os.chdir(current_dir)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.1.73
3
+ Version: 0.1.75
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