jarvis-ai-assistant 0.1.64__py3-none-any.whl → 0.1.65__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 CHANGED
@@ -1,3 +1,3 @@
1
1
  """Jarvis AI Assistant"""
2
2
 
3
- __version__ = "0.1.64"
3
+ __version__ = "0.1.65"
File without changes
@@ -0,0 +1,342 @@
1
+ import hashlib
2
+ import os
3
+ import sqlite3
4
+ import time
5
+ import numpy as np
6
+ import faiss
7
+ from typing import List, Tuple, Optional
8
+ from jarvis.models.registry import PlatformRegistry
9
+ import concurrent.futures
10
+ from threading import Lock
11
+ from concurrent.futures import ThreadPoolExecutor
12
+ from jarvis.utils import OutputType, PrettyOutput, find_git_root
13
+ from jarvis.utils import load_env_from_file
14
+ import argparse
15
+ from sentence_transformers import SentenceTransformer
16
+
17
+ class CodeBase:
18
+ def __init__(self, root_dir: str, thread_count: int = 10):
19
+ load_env_from_file()
20
+ self.root_dir = root_dir
21
+ os.chdir(self.root_dir)
22
+ self.thread_count = thread_count
23
+ self.cheap_platform = os.environ.get("JARVIS_CHEAP_PLATFORM") or os.environ.get("JARVIS_PLATFORM") or "kimi"
24
+ self.cheap_model = os.environ.get("JARVIS_CHEAP_MODEL") or os.environ.get("JARVIS_MODEL") or "kimi"
25
+ self.normal_platform = os.environ.get("JARVIS_PLATFORM") or "kimi"
26
+ self.normal_model = os.environ.get("JARVIS_MODEL") or "kimi"
27
+ self.embedding_model_name = os.environ.get("JARVIS_EMBEDDING_MODEL") or "BAAI/bge-large-zh-v1.5"
28
+ if not self.cheap_platform or not self.cheap_model or not self.embedding_model_name or not self.normal_platform or not self.normal_model:
29
+ raise ValueError("JARVIS_CHEAP_PLATFORM or JARVIS_CHEAP_MODEL or JARVIS_EMBEDDING_MODEL or JARVIS_PLATFORM or JARVIS_MODEL is not set")
30
+
31
+ PrettyOutput.print(f"廉价模型使用平台: {self.cheap_platform} 模型: {self.cheap_model}", output_type=OutputType.INFO)
32
+ PrettyOutput.print(f"分析模型使用平台: {self.normal_platform} 模型: {self.normal_model}", output_type=OutputType.INFO)
33
+ PrettyOutput.print(f"嵌入模型: {self.embedding_model_name}", output_type=OutputType.INFO)
34
+ PrettyOutput.print(f"检索算法:分层导航小世界算法", output_type=OutputType.INFO)
35
+
36
+ # 初始化数据目录
37
+ self.data_dir = os.path.join(self.root_dir, ".jarvis-codebase")
38
+ if not os.path.exists(self.data_dir):
39
+ os.makedirs(self.data_dir)
40
+
41
+ # 初始化嵌入模型,使用系统默认缓存目录
42
+ try:
43
+ PrettyOutput.print("正在加载/下载模型,请稍候...", output_type=OutputType.INFO)
44
+ self.embedding_model = SentenceTransformer(self.embedding_model_name)
45
+
46
+ # 强制完全加载所有模型组件
47
+ test_text = """
48
+ 这是一段测试文本,用于确保模型完全加载。
49
+ 包含多行内容,以模拟实际使用场景。
50
+ """
51
+ # 预热模型,确保所有组件都被加载
52
+ self.embedding_model.encode([test_text],
53
+ convert_to_tensor=True,
54
+ normalize_embeddings=True)
55
+ PrettyOutput.print("模型加载完成", output_type=OutputType.SUCCESS)
56
+ except Exception as e:
57
+ PrettyOutput.print(f"加载模型失败: {str(e)}", output_type=OutputType.ERROR)
58
+ raise
59
+
60
+ self.vector_dim = self.embedding_model.get_sentence_embedding_dimension()
61
+
62
+
63
+ self.db_path = os.path.join(self.data_dir, "codebase.db")
64
+ if not os.path.exists(self.db_path):
65
+ self.create_db()
66
+ self.git_file_list = self.get_git_file_list()
67
+ self.platform_registry = PlatformRegistry().get_global_platform_registry()
68
+ self.index_path = os.path.join(self.data_dir, "vectors.index")
69
+ self.index = None
70
+ if os.path.exists(self.index_path):
71
+ PrettyOutput.print("正在加载向量数据库", output_type=OutputType.INFO)
72
+ self.index = faiss.read_index(self.index_path)
73
+
74
+ def get_git_file_list(self):
75
+ return os.popen("git ls-files").read().splitlines()
76
+
77
+ def get_db_connection(self):
78
+ """创建并返回一个新的数据库连接"""
79
+ return sqlite3.connect(self.db_path)
80
+
81
+ def clean_db(self) -> bool:
82
+ """清理数据库和向量索引中的过期记录"""
83
+ db = self.get_db_connection()
84
+ try:
85
+ # 获取所有数据库记录
86
+ all_records = db.execute("SELECT path FROM codebase").fetchall()
87
+ files_to_delete = []
88
+
89
+ # 找出需要删除的文件
90
+ for row in all_records:
91
+ if row[0] not in self.git_file_list:
92
+ files_to_delete.append(row[0])
93
+
94
+ if not files_to_delete:
95
+ return False
96
+
97
+ for file_path in files_to_delete:
98
+ db.execute("DELETE FROM codebase WHERE path = ?", (file_path,))
99
+
100
+ db.commit()
101
+
102
+ PrettyOutput.print(f"清理了 {len(files_to_delete)} 个文件的记录",
103
+ output_type=OutputType.INFO)
104
+ return True
105
+ finally:
106
+ db.close()
107
+
108
+ def create_db(self):
109
+ db = self.get_db_connection()
110
+ try:
111
+ db.execute("CREATE TABLE IF NOT EXISTS codebase (path TEXT, md5 TEXT ,description TEXT)")
112
+ db.commit()
113
+ finally:
114
+ db.close()
115
+
116
+ def is_text_file(self, file_path: str):
117
+ with open(file_path, "r", encoding="utf-8") as f:
118
+ try:
119
+ f.read()
120
+ return True
121
+ except UnicodeDecodeError:
122
+ return False
123
+
124
+ def make_description(self, file_path: str) -> str:
125
+ model = self.platform_registry.create_platform(self.cheap_platform)
126
+ model.set_model_name(self.cheap_model)
127
+ model.set_suppress_output(True)
128
+ content = open(file_path, "r", encoding="utf-8").read()
129
+ prompt = f"""请分析以下代码文件,并生成一个详细的描述。描述应该包含以下要点:
130
+
131
+ 1. 主要功能和用途
132
+ 2. 关键类和方法的作用
133
+ 3. 重要的依赖和技术特征(如使用了什么框架、算法、设计模式等)
134
+ 4. 代码处理的主要数据类型和数据结构
135
+ 5. 关键业务逻辑和处理流程
136
+ 6. 特殊功能点和亮点特性
137
+
138
+ 请用简洁专业的语言描述,突出代码的技术特征和功能特点,以便后续进行相似代码检索。
139
+
140
+ 文件路径:{file_path}
141
+ 代码内容:
142
+ {content}
143
+ """
144
+ response = model.chat(prompt)
145
+ return response
146
+
147
+ def get_embedding(self, text: str) -> np.ndarray:
148
+ """使用 transformers 模型获取文本的向量表示"""
149
+ # 对长文本进行截断
150
+ max_length = 512 # 或其他合适的长度
151
+ text = ' '.join(text.split()[:max_length])
152
+
153
+ # 获取嵌入向量
154
+ embedding = self.embedding_model.encode(text,
155
+ normalize_embeddings=True, # L2归一化
156
+ show_progress_bar=False)
157
+ return np.array(embedding, dtype=np.float32)
158
+
159
+ def vectorize_file(self, file_path: str, description: str) -> np.ndarray:
160
+ """将文件内容和描述向量化"""
161
+ try:
162
+ # 组合文件信息
163
+ combined_text = f"""
164
+ 文件路径: {file_path}
165
+ 文件描述: {description}
166
+ """
167
+ return self.get_embedding(combined_text)
168
+ except Exception as e:
169
+ PrettyOutput.print(f"Error vectorizing file {file_path}: {str(e)}",
170
+ output_type=OutputType.ERROR)
171
+ return np.zeros(self.vector_dim, dtype=np.float32)
172
+
173
+ def process_file(self, file):
174
+ """处理单个文件的辅助方法"""
175
+ db = self.get_db_connection()
176
+ try:
177
+ if not self.is_text_file(file):
178
+ return None
179
+ md5 = hashlib.md5(open(file, "rb").read()).hexdigest()
180
+ if db.execute("SELECT path FROM codebase WHERE md5 = ?", (md5,)).fetchone():
181
+ return None
182
+ description = self.make_description(file)
183
+ return (file, md5, description)
184
+ finally:
185
+ db.close()
186
+
187
+ def gen_vector_db_from_sqlite(self):
188
+ self.index = faiss.IndexHNSWFlat(self.vector_dim, 16)
189
+ self.index.hnsw.efConstruction = 40
190
+ self.index.hnsw.efSearch = 16
191
+ db = self.get_db_connection()
192
+ try:
193
+ all_records = db.execute("SELECT path, description FROM codebase").fetchall()
194
+ for row in all_records:
195
+ file, description = row
196
+ PrettyOutput.print(f"正在向量化文件: {file}", output_type=OutputType.INFO)
197
+ vector = self.vectorize_file(file, description)
198
+ vector = vector.reshape(1, -1)
199
+ self.index.add(vector)
200
+ faiss.write_index(self.index, self.index_path)
201
+ finally:
202
+ db.close()
203
+
204
+ def generate_codebase(self):
205
+ updated =self.clean_db()
206
+ db_lock = Lock()
207
+ processed_files = [] # 用于跟踪已处理的文件
208
+
209
+ def process_and_save(file):
210
+ result = self.process_file(file)
211
+ if result:
212
+ file, md5, description = result
213
+ db = self.get_db_connection()
214
+ try:
215
+ with db_lock:
216
+ db.execute("DELETE FROM codebase WHERE path = ?", (file,))
217
+ db.execute("INSERT INTO codebase (path, md5, description) VALUES (?, ?, ?)",
218
+ (file, md5, description))
219
+ db.commit()
220
+ PrettyOutput.print(f"索引文件: {file}", output_type=OutputType.INFO)
221
+ processed_files.append(file)
222
+ finally:
223
+ db.close()
224
+
225
+ # 使用 ThreadPoolExecutor 并等待所有任务完成
226
+ with ThreadPoolExecutor(max_workers=self.thread_count) as executor:
227
+ futures = [executor.submit(process_and_save, file) for file in self.git_file_list]
228
+ # 等待所有任务完成
229
+ concurrent.futures.wait(futures)
230
+
231
+ if updated or len(processed_files) > 0:
232
+ PrettyOutput.print("有新的文件被删除或添加,正在重新生成向量数据库", output_type=OutputType.INFO)
233
+ self.gen_vector_db_from_sqlite()
234
+ else:
235
+ PrettyOutput.print("没有新的文件被删除或添加,跳过向量数据库生成", output_type=OutputType.INFO)
236
+
237
+ PrettyOutput.print(f"成功索引 {len(processed_files)} 个文件", output_type=OutputType.INFO)
238
+
239
+ def search_similar(self, query: str, top_k: int = 5) -> List[Tuple[str, float, str]]:
240
+ """搜索与查询最相似的文件
241
+
242
+ Args:
243
+ query: 查询文本
244
+ top_k: 返回结果数量
245
+
246
+ Returns:
247
+ List of (file_path, similarity_score, description) tuples
248
+ """
249
+ # 获取查询文本的向量表示
250
+ query_vector = self.get_embedding(query)
251
+ query_vector = query_vector.reshape(1, -1)
252
+
253
+ # 搜索最相似的向量
254
+ distances, indices = self.index.search(query_vector, top_k)
255
+
256
+ # 获取对应的文件信息
257
+ db = self.get_db_connection()
258
+ try:
259
+ results = []
260
+ for i, distance in zip(indices[0], distances[0]):
261
+ if i == -1: # faiss返回-1表示无效结果
262
+ continue
263
+
264
+ # 将numpy.int64转换为Python int
265
+ offset = int(i)
266
+ # 获取文件路径和描述
267
+ cursor = db.execute("SELECT path, description FROM codebase LIMIT 1 OFFSET ?", (offset,))
268
+ row = cursor.fetchone()
269
+ if row:
270
+ path, description = row
271
+ # 将distance转换为相似度分数(0-1之间)
272
+ similarity = 1.0 / (1.0 + float(distance)) # 确保使用Python float
273
+ results.append((path, similarity, description))
274
+
275
+ return results
276
+ finally:
277
+ db.close()
278
+
279
+ def ask_codebase(self, query: str, top_k: int = 5) -> List[Tuple[str, float, str]]:
280
+ """Ask a question about the codebase"""
281
+ # 使用搜索函数获取相似文件
282
+ results = self.search_similar(query, top_k)
283
+ PrettyOutput.print(f"找到的关联文件: ", output_type=OutputType.INFO)
284
+ for path, score, _ in results:
285
+ PrettyOutput.print(f"文件: {path} 关联度: {score:.3f}", output_type=OutputType.INFO)
286
+
287
+ prompt = f"""你是一个代码专家,请根据以下文件信息回答用户的问题:
288
+ """
289
+ for path, _, _ in results:
290
+ content = open(path, "r", encoding="utf-8").read()
291
+ prompt += f"""
292
+ 文件路径: {path}
293
+ 文件内容:
294
+ {content}
295
+ ========================================
296
+ """
297
+ prompt += f"""
298
+ 用户问题: {query}
299
+
300
+ 请用专业的语言回答用户的问题,如果给出的文件内容不足以回答用户的问题,请告诉用户,绝对不要胡编乱造。
301
+ """
302
+ model = self.platform_registry.create_platform(self.normal_platform)
303
+ model.set_model_name(self.normal_model)
304
+ response = model.chat(prompt)
305
+ return response
306
+
307
+
308
+ def main():
309
+ parser = argparse.ArgumentParser(description='Codebase management and search tool')
310
+ parser.add_argument('--search', type=str, help='Search query to find similar code files')
311
+ parser.add_argument('--top-k', type=int, default=5, help='Number of results to return (default: 5)')
312
+ parser.add_argument('--ask', type=str, help='Ask a question about the codebase')
313
+ args = parser.parse_args()
314
+
315
+ current_dir = find_git_root()
316
+ codebase = CodeBase(current_dir)
317
+
318
+ try:
319
+ codebase.generate_codebase()
320
+ PrettyOutput.print("\nCodebase generation completed", output_type=OutputType.SUCCESS)
321
+ except Exception as e:
322
+ PrettyOutput.print(f"Error during codebase generation: {str(e)}", output_type=OutputType.ERROR)
323
+
324
+ if args.search:
325
+ results = codebase.search_similar(args.search, args.top_k)
326
+ if not results:
327
+ PrettyOutput.print("No similar files found", output_type=OutputType.WARNING)
328
+ return
329
+
330
+ PrettyOutput.print("\nSearch Results:", output_type=OutputType.INFO)
331
+ for path, score, desc in results:
332
+ PrettyOutput.print("\n" + "="*50, output_type=OutputType.INFO)
333
+ PrettyOutput.print(f"File: {path}", output_type=OutputType.INFO)
334
+ PrettyOutput.print(f"Similarity: {score:.3f}", output_type=OutputType.INFO)
335
+ PrettyOutput.print(f"Description: {desc[100:]}", output_type=OutputType.INFO)
336
+
337
+ if args.ask:
338
+ codebase.ask_codebase(args.ask, args.top_k)
339
+
340
+
341
+ if __name__ == "__main__":
342
+ exit(main())
@@ -9,8 +9,9 @@ from typing import Dict, Any, List, Optional, Tuple
9
9
 
10
10
  import yaml
11
11
  from jarvis.models.base import BasePlatform
12
- from jarvis.utils import OutputType, PrettyOutput, get_multiline_input, load_env_from_file
12
+ from jarvis.utils import OutputType, PrettyOutput, find_git_root, get_multiline_input, load_env_from_file
13
13
  from jarvis.models.registry import PlatformRegistry
14
+ from jarvis.jarvis_codebase.main import CodeBase
14
15
  from prompt_toolkit import PromptSession
15
16
  from prompt_toolkit.completion import WordCompleter, Completer, Completion
16
17
  from prompt_toolkit.formatted_text import FormattedText
@@ -23,12 +24,15 @@ index_lock = threading.Lock()
23
24
  class JarvisCoder:
24
25
  def __init__(self, root_dir: str, language: str):
25
26
  """初始化代码修改工具"""
27
+
28
+ self.platform = os.environ.get("JARVIS_CODEGEN_PLATFORM") or os.environ.get("JARVIS_PLATFORM")
29
+ self.model = os.environ.get("JARVIS_CODEGEN_MODEL") or os.environ.get("JARVIS_MODEL")
26
30
 
27
- self.root_dir = root_dir
28
- self.platform = os.environ.get("JARVIS_CODEGEN_PLATFORM")
29
- self.model = os.environ.get("JARVIS_CODEGEN_MODEL")
30
31
 
31
- self.root_dir = self._find_git_root_dir(self.root_dir)
32
+ if not self.platform or not self.model:
33
+ raise ValueError("JARVIS_CODEGEN_PLATFORM or JARVIS_CODEGEN_MODEL is not set")
34
+
35
+ self.root_dir = find_git_root(root_dir)
32
36
  if not self.root_dir:
33
37
  self.root_dir = root_dir
34
38
 
@@ -46,10 +50,6 @@ class JarvisCoder:
46
50
  if not os.path.exists(self.jarvis_dir):
47
51
  os.makedirs(self.jarvis_dir)
48
52
 
49
- self.index_db_path = os.path.join(self.jarvis_dir, "index.db")
50
- if not os.path.exists(self.index_db_path):
51
- self._create_index_db()
52
-
53
53
  self.record_dir = os.path.join(self.jarvis_dir, "record")
54
54
  if not os.path.exists(self.record_dir):
55
55
  os.makedirs(self.record_dir)
@@ -70,6 +70,9 @@ class JarvisCoder:
70
70
  os.system(f"git add .")
71
71
  os.system(f"git commit -m 'commit before code edit'")
72
72
 
73
+ # 4. 初始化代码库
74
+ self._codebase = CodeBase(self.root_dir)
75
+
73
76
  def _new_model(self):
74
77
  """获取大模型"""
75
78
  model = PlatformRegistry().get_global_platform_registry().create_platform(self.platform)
@@ -114,341 +117,6 @@ class JarvisCoder:
114
117
  time.sleep(delay)
115
118
  delay *= 2 # 指数退避
116
119
 
117
- def _get_key_info(self, file_path: str, content: str) -> Optional[Dict[str, Any]]:
118
- """获取文件的关键信息
119
-
120
- Args:
121
- file_path: 文件路径
122
- content: 文件内容
123
-
124
- Returns:
125
- Optional[Dict[str, Any]]: 文件信息,包含文件描述
126
- """
127
- model = self._new_model() # 创建新的模型实例
128
- model.set_suppress_output(True)
129
-
130
- prompt = f"""你是一个资深程序员,请根据文件内容,生成文件的关键信息,要求如下,除了代码,不要输出任何内容:
131
-
132
- 1. 文件路径: {file_path}
133
- 2. 文件内容:(<CONTENT_START>和<CONTENT_END>之间的部分)
134
- <CONTENT_START>
135
- {content}
136
- <CONTENT_END>
137
- 3. 关键信息: 请生成这个文件的主要功能和作用描述,包含的特征符号(函数和类、变量等),不超过100字
138
- """
139
- try:
140
- return model.chat(prompt)
141
- except Exception as e:
142
- PrettyOutput.print(f"解析文件信息失败: {str(e)}", OutputType.ERROR)
143
- return None
144
- finally:
145
- # 确保清理模型资源
146
- try:
147
- model.delete_chat()
148
- except:
149
- pass
150
-
151
-
152
-
153
- def _get_file_md5(self, file_path: str) -> str:
154
- """获取文件MD5"""
155
- return hashlib.md5(open(file_path, "rb").read()).hexdigest()
156
-
157
-
158
- def _create_index_db(self):
159
- """创建索引数据库"""
160
- with index_lock:
161
- if not os.path.exists(self.index_db_path):
162
- PrettyOutput.print("Index database does not exist, creating...", OutputType.INFO)
163
- index_db = sqlite3.connect(self.index_db_path)
164
- index_db.execute(
165
- "CREATE TABLE files (file_path TEXT PRIMARY KEY, file_md5 TEXT, file_description TEXT)")
166
- index_db.commit()
167
- index_db.close()
168
- PrettyOutput.print("Index database created", OutputType.SUCCESS)
169
- # commit
170
- os.chdir(self.root_dir)
171
- os.system(f"git add .gitignore -f")
172
- os.system(f"git commit -m 'add index database'")
173
-
174
-
175
- def _find_file_by_md5(self, file_md5: str) -> Optional[str]:
176
- """根据文件MD5查找文件路径"""
177
- with index_lock:
178
- index_db = sqlite3.connect(self.index_db_path)
179
- cursor = index_db.cursor()
180
- cursor.execute(
181
- "SELECT file_path FROM files WHERE file_md5 = ?", (file_md5,))
182
- result = cursor.fetchone()
183
- index_db.close()
184
- return result[0] if result else None
185
-
186
-
187
- def _update_file_path(self, file_path: str, file_md5: str):
188
- """更新文件路径"""
189
- with index_lock:
190
- index_db = sqlite3.connect(self.index_db_path)
191
- cursor = index_db.cursor()
192
- cursor.execute(
193
- "UPDATE files SET file_path = ? WHERE file_md5 = ?", (file_path, file_md5))
194
- index_db.commit()
195
- index_db.close()
196
-
197
-
198
- def _insert_info(self, file_path: str, file_md5: str, file_description: str):
199
- """插入文件信息"""
200
- with index_lock:
201
- index_db = sqlite3.connect(self.index_db_path)
202
- cursor = index_db.cursor()
203
- cursor.execute("DELETE FROM files WHERE file_path = ?", (file_path,))
204
- cursor.execute("INSERT INTO files (file_path, file_md5, file_description) VALUES (?, ?, ?)",
205
- (file_path, file_md5, file_description))
206
- index_db.commit()
207
- index_db.close()
208
-
209
- def _is_text_file(self, file_path: str) -> bool:
210
- """判断文件是否是文本文件"""
211
- try:
212
- with open(file_path, 'rb') as f:
213
- # 读取文件前1024个字节
214
- chunk = f.read(1024)
215
- # 检查是否包含空字节
216
- if b'\x00' in chunk:
217
- return False
218
- # 尝试解码为文本
219
- chunk.decode('utf-8')
220
- return True
221
- except:
222
- return False
223
-
224
- def _index_project(self):
225
- """建立代码库索引"""
226
- import threading
227
- from concurrent.futures import ThreadPoolExecutor, as_completed
228
-
229
- git_files = os.popen("git ls-files").read().splitlines()
230
-
231
- index_db = sqlite3.connect(self.index_db_path)
232
- cursor = index_db.cursor()
233
- cursor.execute("SELECT file_path FROM files")
234
- db_files = [row[0] for row in cursor.fetchall()]
235
- for db_file in db_files:
236
- if not os.path.exists(db_file):
237
- cursor.execute("DELETE FROM files WHERE file_path = ?", (db_file,))
238
- PrettyOutput.print(f"删除不存在的文件记录: {db_file}", OutputType.INFO)
239
- index_db.commit()
240
- index_db.close()
241
-
242
- def process_file(file_path: str):
243
- """处理单个文件的索引任务"""
244
- if not self._is_text_file(file_path):
245
- return
246
-
247
- # 计算文件MD5
248
- file_md5 = self._get_file_md5(file_path)
249
-
250
- # 查找文件
251
- file_path_in_db = self._find_file_by_md5(file_md5)
252
- if file_path_in_db:
253
- PrettyOutput.print(
254
- f"文件 {file_path} 重复,跳过", OutputType.INFO)
255
- if file_path_in_db != file_path:
256
- self._update_file_path(file_path, file_md5)
257
- PrettyOutput.print(
258
- f"文件 {file_path} 重复,更新路径为 {file_path}", OutputType.INFO)
259
- return
260
-
261
- with open(file_path, "r", encoding="utf-8") as f:
262
- file_content = f.read()
263
- key_info = self._get_key_info(file_path, file_content)
264
- if not key_info:
265
- PrettyOutput.print(
266
- f"文件 {file_path} 索引失败", OutputType.INFO)
267
- return
268
-
269
- self._insert_info(file_path, file_md5, key_info)
270
- PrettyOutput.print(
271
- f"文件 {file_path} 已建立索引", OutputType.INFO)
272
-
273
-
274
- # 使用线程池处理文件索引
275
- with ThreadPoolExecutor(max_workers=10) as executor:
276
- futures = [executor.submit(process_file, file_path) for file_path in git_files]
277
- for future in as_completed(futures):
278
- try:
279
- future.result()
280
- except Exception as e:
281
- PrettyOutput.print(f"处理文件时发生错误: {str(e)}", OutputType.ERROR)
282
-
283
- PrettyOutput.print("项目索引完成", OutputType.INFO)
284
-
285
- def _get_files_from_db(self) -> List[Tuple[str, str]]:
286
- """从数据库获取所有文件信息
287
-
288
- Returns:
289
- List[Tuple[str, str]]: [(file_path, file_description), ...]
290
- """
291
- try:
292
- index_db = sqlite3.connect(self.index_db_path)
293
- cursor = index_db.cursor()
294
- cursor.execute("SELECT file_path, file_description FROM files")
295
- all_files = cursor.fetchall()
296
- index_db.close()
297
- return all_files
298
- except sqlite3.Error as e:
299
- PrettyOutput.print(f"数据库操作失败: {str(e)}", OutputType.ERROR)
300
- return []
301
-
302
- def _analyze_files_in_batches(self, all_files: List[Tuple[str, str]], feature: str, batch_size: int = 100) -> List[Dict]:
303
- """批量分析文件相关性
304
-
305
- Args:
306
- all_files: 所有文件列表
307
- feature: 需求描述
308
- batch_size: 批处理大小
309
-
310
- Returns:
311
- List[Dict]: 带评分的文件列表
312
- """
313
- batch_results = []
314
-
315
- with ThreadPoolExecutor(max_workers=10) as executor:
316
- futures = []
317
- for i in range(0, len(all_files), batch_size):
318
- batch_files = all_files[i:i + batch_size]
319
- prompt = self._create_batch_analysis_prompt(batch_files, feature)
320
- model = self._new_model()
321
- model.set_suppress_output(True)
322
- futures.append(executor.submit(self._call_model_with_retry, model, prompt))
323
-
324
- for future in as_completed(futures):
325
- success, response = future.result()
326
- if not success:
327
- continue
328
-
329
- batch_start = futures.index(future) * batch_size
330
- batch_end = min(batch_start + batch_size, len(all_files))
331
- current_batch = all_files[batch_start:batch_end]
332
-
333
- results = self._process_batch_response(response, current_batch)
334
- batch_results.extend(results)
335
-
336
- return batch_results
337
-
338
- def _create_batch_analysis_prompt(self, batch_files: List[Tuple[str, str]], feature: str) -> str:
339
- """创建批量分析的提示词
340
-
341
- Args:
342
- batch_files: 批次文件列表
343
- feature: 需求描述
344
-
345
- Returns:
346
- str: 提示词
347
- """
348
- prompt = """你是资深程序员,请根据需求描述,从以下文件路径中选出最相关的文件,按相关度从高到低排序。
349
-
350
- 相关度打分标准(0-9分):
351
- - 9分:文件名直接包含需求中的关键词,且文件功能与需求完全匹配
352
- - 7-8分:文件名包含需求相关词,或文件功能与需求高度相关
353
- - 5-6分:文件名暗示与需求有关,或文件功能与需求部分相关
354
- - 3-4分:文件可能需要小幅修改以配合需求
355
- - 1-2分:文件与需求关系较远,但可能需要少量改动
356
- - 0分:文件与需求完全无关
357
-
358
- 请输出yaml格式,仅输出以下格式内容:
359
- <RELEVANT_FILES_START>
360
- file1.py: 9
361
- file2.py: 7
362
- <RELEVANT_FILES_END>
363
-
364
- 文件列表:
365
- """
366
- for file_path, _ in batch_files:
367
- prompt += f"- {file_path}\n"
368
- prompt += f"\n需求描述: {feature}\n"
369
- prompt += "\n注意:\n1. 只输出最相关的文件,不超过5个\n2. 根据上述打分标准判断相关性\n3. 相关度必须是0-9的整数"
370
-
371
- return prompt
372
-
373
- def _process_batch_response(self, response: str, batch_files: List[Tuple[str, str]]) -> List[Dict]:
374
- """处理批量分析的响应
375
-
376
- Args:
377
- response: 模型响应
378
- batch_files: 批次文件列表
379
-
380
- Returns:
381
- List[Dict]: 处理后的文件列表
382
- """
383
- try:
384
- response = response.replace("<RELEVANT_FILES_START>", "").replace("<RELEVANT_FILES_END>", "")
385
- result = yaml.safe_load(response)
386
-
387
- batch_files_dict = {f[0]: f[1] for f in batch_files}
388
- results = []
389
- for file_path, score in result.items():
390
- if isinstance(file_path, str) and isinstance(score, int):
391
- score = max(0, min(9, score)) # Ensure score is between 0-9
392
- if file_path in batch_files_dict:
393
- results.append({
394
- "file_path": file_path,
395
- "file_description": batch_files_dict[file_path],
396
- "score": score
397
- })
398
- return results
399
- except Exception as e:
400
- PrettyOutput.print(f"处理批次文件失败: {str(e)}", OutputType.ERROR)
401
- return []
402
-
403
-
404
- def _process_content_response(self, response: str, top_files: List[Dict]) -> List[Dict]:
405
- """处理内容分析的响应"""
406
- try:
407
- response = response.replace("<FILE_RELATION_START>", "").replace("<FILE_RELATION_END>", "")
408
- file_relation = yaml.safe_load(response)
409
- if not file_relation:
410
- return top_files[:5]
411
-
412
- score = [[] for _ in range(10)] # 创建10个空列表,对应0-9分
413
- for file_id, relation in file_relation.items():
414
- id = int(file_id)
415
- relation = max(0, min(9, relation)) # 确保范围在0-9之间
416
- score[relation].append(top_files[id])
417
-
418
- files = []
419
- for scores in reversed(score): # 从高分到低分遍历
420
- files.extend(scores)
421
- if len(files) >= 5: # 直接取相关性最高的5个文件
422
- break
423
-
424
- return files[:5]
425
- except Exception as e:
426
- PrettyOutput.print(f"处理文件关系失败: {str(e)}", OutputType.ERROR)
427
- return top_files[:5]
428
-
429
- def _find_related_files(self, feature: str) -> List[Dict]:
430
- """根据需求描述,查找相关文件
431
-
432
- Args:
433
- feature: 需求描述
434
-
435
- Returns:
436
- List[Dict]: 相关文件列表
437
- """
438
- # 1. 从数据库获取所有文件
439
- all_files = self._get_files_from_db()
440
- if not all_files:
441
- return []
442
-
443
- # 2. 批量分析文件相关性
444
- batch_results = self._analyze_files_in_batches(all_files, feature)
445
-
446
- # 3. 排序并获取前5个文件
447
- batch_results.sort(key=lambda x: x["score"], reverse=True)
448
- return batch_results[:5]
449
-
450
-
451
-
452
120
  def _remake_patch(self, prompt: str) -> List[str]:
453
121
  success, response = self._call_model_with_retry(self.main_model, prompt, max_retries=5) # 增加重试次数
454
122
  if not success:
@@ -487,7 +155,7 @@ file2.py: 7
487
155
  文件列表如下:
488
156
  """
489
157
  for i, file in enumerate(related_files):
490
- prompt += f"""{i}. {file["file_path"]} : {file["file_description"]}\n"""
158
+ prompt += f"""{i}. {file["file_path"]}\n"""
491
159
  prompt += f"""文件内容:\n"""
492
160
  prompt += f"<FILE_CONTENT_START>\n"
493
161
  prompt += f'{file["file_content"]}\n'
@@ -499,6 +167,7 @@ file2.py: 7
499
167
  1、仅输出补丁内容,不要输出任何其他内容,每个补丁必须用<PATCH_START>和<PATCH_END>标记
500
168
  2、如果在大段代码中有零星修改,生成多个补丁
501
169
  3、要替换的内容,一定要与文件内容完全一致,不要有任何多余或者缺失的内容
170
+ 4、每个patch不超过20行,超出20行,请生成多个patch
502
171
  """
503
172
 
504
173
  success, response = self._call_model_with_retry(self.main_model, prompt)
@@ -629,28 +298,23 @@ file2.py: 7
629
298
 
630
299
  PrettyOutput.print(f"已保存修改记录: {record_path}", OutputType.SUCCESS)
631
300
 
632
- def _find_git_root_dir(self, root_dir: str) -> str:
633
- """查找git根目录"""
634
- while not os.path.exists(os.path.join(root_dir, ".git")):
635
- root_dir = os.path.dirname(root_dir)
636
- if root_dir == "/":
637
- return None
638
- return root_dir
639
301
 
640
302
 
641
303
  def _prepare_execution(self) -> None:
642
304
  """准备执行环境"""
643
305
  self.main_model = self._new_model()
644
- self._index_project()
306
+ self._codebase.generate_codebase()
645
307
 
646
308
  def _load_related_files(self, feature: str) -> List[Dict]:
647
309
  """加载相关文件内容"""
648
- related_files = self._find_related_files(feature)
649
- for file in related_files:
650
- PrettyOutput.print(f"Related file: {file['file_path']}", OutputType.INFO)
651
- with open(file["file_path"], "r", encoding="utf-8") as f:
652
- file["file_content"] = f.read()
653
- return related_files
310
+ ret = []
311
+ related_files = self._codebase.search_similar(feature, top_k=5)
312
+ for file, score, _ in related_files:
313
+ PrettyOutput.print(f"相关文件: {file} 相关度: {score:.3f}", OutputType.INFO)
314
+ with open(file, "r", encoding="utf-8") as f:
315
+ content = f.read()
316
+ ret.append({"file_path": file, "file_content": content})
317
+ return ret
654
318
 
655
319
  def _handle_patch_application(self, related_files: List[Dict], patches: List[str], feature: str) -> Dict[str, Any]:
656
320
  """处理补丁应用流程"""
@@ -703,7 +367,6 @@ file2.py: 7
703
367
  os.system(f"git add .")
704
368
  os.system(f"git commit -m '{feature}'")
705
369
  self._save_edit_record(feature, patches)
706
- self._index_project()
707
370
 
708
371
  def _revert_changes(self) -> None:
709
372
  """回退所有修改"""
@@ -780,7 +443,7 @@ def main():
780
443
  if result["stderr"]:
781
444
  PrettyOutput.print(result["stderr"], OutputType.ERROR)
782
445
  if result["error"]:
783
- PrettyOutput.print(f"错误类型: {type(result['error']).__name__}", OutputType.ERROR)
446
+ PrettyOutput.print(f"错误类型: {type(result['error']).__name__}", OutputType.WARNING)
784
447
 
785
448
  except KeyboardInterrupt:
786
449
  print("\n用户中断执行")
jarvis/main.py CHANGED
@@ -117,7 +117,6 @@ def main():
117
117
  PlatformRegistry.get_global_platform_registry().set_global_platform_name(platform)
118
118
 
119
119
  if args.model:
120
- PrettyOutput.print(f"用户传入了模型参数,更换模型: {args.model}", OutputType.USER)
121
120
  os.environ["JARVIS_MODEL"] = args.model
122
121
 
123
122
  try:
@@ -126,7 +125,6 @@ def main():
126
125
 
127
126
  # 如果用户传入了模型参数,则更换当前模型为用户指定的模型
128
127
  if args.model:
129
- PrettyOutput.print(f"用户传入了模型参数,更换模型: {args.model}", OutputType.USER)
130
128
  agent.model.set_model_name(args.model)
131
129
 
132
130
  # 欢迎信息
jarvis/models/ai8.py CHANGED
@@ -64,11 +64,10 @@ class AI8Model(BasePlatform):
64
64
 
65
65
  PrettyOutput.print("使用AI8_MODEL环境变量配置模型", OutputType.SUCCESS)
66
66
 
67
- self.model_name = os.getenv("AI8_MODEL") or os.getenv("JARVIS_MODEL") or "deepseek-chat"
67
+ self.model_name = os.getenv("JARVIS_MODEL") or "deepseek-chat"
68
68
  if self.model_name not in self.models:
69
69
  PrettyOutput.print(f"警告: 当前选择的模型 {self.model_name} 不在可用列表中", OutputType.WARNING)
70
70
 
71
- PrettyOutput.print(f"当前使用模型: {self.model_name}", OutputType.SYSTEM)
72
71
 
73
72
  def set_model_name(self, model_name: str):
74
73
  """设置模型名称"""
jarvis/models/openai.py CHANGED
@@ -33,7 +33,6 @@ class OpenAIModel(BasePlatform):
33
33
  self.base_url = os.getenv("OPENAI_API_BASE", "https://api.deepseek.com")
34
34
  self.model_name = os.getenv("OPENAI_MODEL_NAME") or os.getenv("JARVIS_MODEL") or "deepseek-chat"
35
35
 
36
- PrettyOutput.print(f"当前使用模型: {self.model_name}", OutputType.SYSTEM)
37
36
 
38
37
  self.client = OpenAI(
39
38
  api_key=self.api_key,
jarvis/models/oyi.py CHANGED
@@ -25,8 +25,6 @@ class OyiModel(BasePlatform):
25
25
  else:
26
26
  PrettyOutput.print("获取模型列表失败", OutputType.WARNING)
27
27
 
28
- PrettyOutput.print("使用OYI_MODEL环境变量配置模型", OutputType.SUCCESS)
29
-
30
28
  self.messages = []
31
29
  self.system_message = ""
32
30
  self.conversation = None
@@ -37,11 +35,10 @@ class OyiModel(BasePlatform):
37
35
  if not self.token:
38
36
  raise Exception("OYI_API_KEY is not set")
39
37
 
40
- self.model_name = os.getenv("OYI_MODEL") or os.getenv("JARVIS_MODEL") or "deepseek-chat"
38
+ self.model_name = os.getenv("JARVIS_MODEL") or "deepseek-chat"
41
39
  if self.model_name not in [m.split()[0] for m in available_models]:
42
40
  PrettyOutput.print(f"警告: 当前选择的模型 {self.model_name} 不在可用列表中", OutputType.WARNING)
43
41
 
44
- PrettyOutput.print(f"当前使用模型: {self.model_name}", OutputType.SYSTEM)
45
42
 
46
43
  def set_model_name(self, model_name: str):
47
44
  """设置模型名称"""
jarvis/tools/__init__.py CHANGED
@@ -1,5 +1,9 @@
1
1
  from .registry import ToolRegistry
2
+ from jarvis.tools.codebase_qa import CodebaseQATool
2
3
 
3
4
  __all__ = [
4
5
  'ToolRegistry',
5
6
  ]
7
+
8
+ def register_tools():
9
+ register_tool(CodebaseQATool())
@@ -0,0 +1,70 @@
1
+ import os
2
+ from typing import Any, Dict
3
+ from jarvis.jarvis_codebase.main import CodeBase
4
+ from jarvis.utils import find_git_root, PrettyOutput, OutputType
5
+
6
+ class CodebaseQATool:
7
+ """代码库问答工具,用于回答关于代码库的问题"""
8
+
9
+ name = "codebase_qa"
10
+ description = "回答关于代码库的问题,可以查询和理解代码的功能、结构和实现细节"
11
+ parameters = {
12
+ "type": "object",
13
+ "properties": {
14
+ "dir": {
15
+ "type": "string",
16
+ "description": "项目根目录"
17
+ },
18
+ "question": {
19
+ "type": "string",
20
+ "description": "关于代码库的问题"
21
+ },
22
+ "top_k": {
23
+ "type": "integer",
24
+ "description": "搜索相关文件的数量",
25
+ "default": 5
26
+ }
27
+ },
28
+ "required": ["question"]
29
+ }
30
+
31
+ def execute(self, params: Dict[str, Any]) -> Dict[str, Any]:
32
+ """执行代码问答"""
33
+ try:
34
+ dir = params.get("dir")
35
+ question = params["question"]
36
+ top_k = params.get("top_k", 5)
37
+
38
+ # 初始化代码库
39
+ current_dir = os.getcwd()
40
+ root_dir = find_git_root(dir or current_dir)
41
+ if not root_dir:
42
+ return {
43
+ "success": False,
44
+ "stdout": "",
45
+ "stderr": "错误:当前目录不在Git仓库中",
46
+ "error": "NotInGitRepository"
47
+ }
48
+ os.chdir(root_dir)
49
+ codebase = CodeBase(root_dir)
50
+ # 执行问答
51
+ response = codebase.ask_codebase(question, top_k)
52
+ os.chdir(current_dir)
53
+ return {
54
+ "success": True,
55
+ "stdout": response,
56
+ "stderr": "",
57
+ "error": None
58
+ }
59
+
60
+ except Exception as e:
61
+ PrettyOutput.print(f"代码问答出错: {str(e)}", output_type=OutputType.ERROR)
62
+ return {
63
+ "success": False,
64
+ "stdout": "",
65
+ "stderr": f"执行代码问答时发生错误: {str(e)}",
66
+ "error": str(type(e).__name__)
67
+ }
68
+
69
+ def register():
70
+ return CodebaseQATool()
jarvis/utils.py CHANGED
@@ -200,3 +200,10 @@ def while_true(func, sleep_time: float = 0.1):
200
200
  PrettyOutput.print(f"执行失败,{sleep_time}s后重试...", OutputType.WARNING)
201
201
  time.sleep(sleep_time)
202
202
  return ret
203
+
204
+ def find_git_root(dir="."):
205
+ curr_dir = os.getcwd()
206
+ os.chdir(dir)
207
+ ret = os.popen("git rev-parse --show-toplevel").read().strip()
208
+ os.chdir(curr_dir)
209
+ return ret
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.1.64
3
+ Version: 0.1.65
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
@@ -44,6 +44,9 @@ Requires-Dist: colorama>=0.4.6
44
44
  Requires-Dist: prompt_toolkit>=3.0.0
45
45
  Requires-Dist: openai>=1.20.0
46
46
  Requires-Dist: playwright>=1.41.1
47
+ Requires-Dist: numpy>=1.26.0
48
+ Requires-Dist: faiss-cpu>=1.8.1
49
+ Requires-Dist: sentence-transformers>=2.2.2
47
50
  Provides-Extra: dev
48
51
  Requires-Dist: pytest; extra == "dev"
49
52
  Requires-Dist: black; extra == "dev"
@@ -120,8 +123,12 @@ Jarvis supports configuration through environment variables that can be set in t
120
123
  |---------|------|--------|------|
121
124
  | JARVIS_PLATFORM | AI platform to use, supports kimi/openai/ai8 etc | kimi | Yes |
122
125
  | JARVIS_MODEL | Model name to use | - | No |
126
+
123
127
  | JARVIS_CODEGEN_PLATFORM | AI platform for code generation | Same as JARVIS_PLATFORM | No |
124
128
  | JARVIS_CODEGEN_MODEL | Model name for code generation | Same as JARVIS_MODEL | No |
129
+ | JARVIS_CHEAP_PLATFORM | AI platform for cheap operations | Same as JARVIS_PLATFORM | No |
130
+ | JARVIS_CHEAP_MODEL | Model name for cheap operations | Same as JARVIS_MODEL | No |
131
+ | JARVIS_EMBEDDING_MODEL | Embedding model for code analysis | BAAI/bge-large-zh-v1.5 | No |
125
132
  | OPENAI_API_KEY | API key for OpenAI platform | - | Required for OpenAI |
126
133
  | OPENAI_API_BASE | Base URL for OpenAI API | https://api.deepseek.com | No |
127
134
  | OPENAI_MODEL_NAME | Model name for OpenAI | deepseek-chat | No |
@@ -139,15 +146,26 @@ Jarvis supports configuration through environment variables that can be set in t
139
146
  jarvis
140
147
  ```
141
148
 
149
+
142
150
  ### With Specific Model
143
151
  ```bash
144
152
  jarvis -p kimi # Use Kimi platform
145
153
  jarvis -p openai # Use OpenAI platform
146
154
  ```
147
155
 
148
- ### Process Files
156
+ ### Code Modification
157
+ ```bash
158
+ jarvis coder --feature "Add new feature" # Modify code to add new feature
159
+ ```
160
+
161
+ ### Codebase Search
149
162
  ```bash
150
- jarvis -f file1.py file2.py # Process specific files
163
+ jarvis codebase --search "database connection" # Search codebase
164
+ ```
165
+
166
+ ### Codebase Question
167
+ ```bash
168
+ jarvis codebase --ask "How to use the database?" # Ask about codebase
151
169
  ```
152
170
 
153
171
  ### Keep Chat History
@@ -157,6 +175,7 @@ jarvis --keep-history # Don't delete chat session after completion
157
175
 
158
176
  ## 🛠️ Tools
159
177
 
178
+
160
179
  ### Built-in Tools
161
180
 
162
181
  | Tool | Description |
@@ -166,27 +185,43 @@ jarvis --keep-history # Don't delete chat session after completion
166
185
  | generate_tool | AI-powered tool generation and integration |
167
186
  | methodology | Experience accumulation and methodology management |
168
187
  | create_sub_agent | Create specialized sub-agents for specific tasks |
188
+ | coder | Automatic code modification and generation tool |
189
+ | codebase | Codebase management and search tool |
169
190
 
170
191
  ### Tool Locations
171
192
  - Built-in tools: `src/jarvis/tools/`
172
193
  - User tools: `~/.jarvis_tools/`
173
194
 
195
+
174
196
  ### Key Features
175
197
 
176
198
  #### 1. Self-Extending Capabilities
177
199
  - Tool generation through natural language description
178
200
  - Automatic code generation and integration
179
201
  - Dynamic capability expansion through sub-agents
202
+ - Automatic code modification with version control
203
+ - Codebase indexing and semantic search
180
204
 
181
205
  #### 2. Methodology Learning
182
206
  - Automatic experience accumulation from interactions
183
207
  - Pattern recognition and methodology extraction
184
208
  - Continuous refinement through usage
209
+ - Code modification history tracking
210
+ - Codebase analysis and documentation generation
185
211
 
186
212
  #### 3. Adaptive Problem Solving
187
213
  - Context-aware sub-agent creation
188
214
  - Dynamic tool composition
189
215
  - Learning from execution feedback
216
+ - Codebase-aware problem solving
217
+ - Multi-model collaboration for complex tasks
218
+
219
+ #### 4. Code Intelligence
220
+ - Automatic codebase indexing
221
+ - Semantic code search
222
+ - Code modification with git integration
223
+ - Code analysis and documentation
224
+ - Multi-model code generation
190
225
 
191
226
  ## 🎯 Extending Jarvis
192
227
 
@@ -0,0 +1,32 @@
1
+ jarvis/__init__.py,sha256=OTJ6C0zyEuN5bTB5DR5pAinY8NCO0_DhAnkfroHE0qo,50
2
+ jarvis/agent.py,sha256=kl6pwNrluzb-9eZKgwmsk5Jh4CpWi4F8B3RvEQNvc5U,14921
3
+ jarvis/main.py,sha256=7EcSlxa5JFFXBujzKDWdNtwX6axLhFFdJMc2GxTjfdk,6295
4
+ jarvis/utils.py,sha256=bjC0PAR58RvcXHgabIFmNmYL1L_GhhiEwMFytWurcN4,7499
5
+ jarvis/jarvis_codebase/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ jarvis/jarvis_codebase/main.py,sha256=bncfOXKFthcr21cV9tONXzcchSWaaYXmo0o11R5hPzc,14799
7
+ jarvis/jarvis_coder/main.py,sha256=mK68MJyOerVnY7Fr9ibQ1swQLVIWyBAFmmVj08SHKyk,21825
8
+ jarvis/models/__init__.py,sha256=mrOt67nselz_H1gX9wdAO4y2DY5WPXzABqJbr5Des8k,63
9
+ jarvis/models/ai8.py,sha256=vgy-r_3HHxGMAalZrA65VWHC1PuwBTYgtprSgHkCbrk,12557
10
+ jarvis/models/base.py,sha256=ShV1H8Unee4RMaiFO4idROQA0Hc6wu4dyeRPX5fcszk,1433
11
+ jarvis/models/kimi.py,sha256=1iTB0Z_WOmCML3Ufsge6jmeKOYvccr7I5lS3JUXymU4,17611
12
+ jarvis/models/openai.py,sha256=ayaBWAN5VexMcKVrjEPDNB-Q9wx0sCV9Z4BCrvwYJ9w,4315
13
+ jarvis/models/oyi.py,sha256=X2c5SWDIuQDCCFBcEKbzIWEz3I34eOAi0d1XAFgxlpw,15001
14
+ jarvis/models/registry.py,sha256=hJyaROiOF_TkbtIXsjOD8-ArOvAvtxviawyqBFfLV6s,7617
15
+ jarvis/tools/__init__.py,sha256=xmROdzJTZz6JDLLuAbwVLjUD4xfUUYb6D1Ssu_desaE,183
16
+ jarvis/tools/base.py,sha256=EGRGbdfbLXDLwtyoWdvp9rlxNX7bzc20t0Vc2VkwIEY,652
17
+ jarvis/tools/codebase_qa.py,sha256=AEpusYxyWtALVVwPk1DMUH9cVI73mE1e3WFHJXDpXto,2333
18
+ jarvis/tools/coder.py,sha256=ZJfPInKms4Hj3-eQlBwamVsvZ-2nlZ-4jsqJ-tJc6mg,2040
19
+ jarvis/tools/file_ops.py,sha256=h8g0eT9UvlJf4kt0DLXvdSsjcPj7x19lxWdDApeDfpg,3842
20
+ jarvis/tools/generator.py,sha256=vVP3eN5cCDpRXf_fn0skETkPXAW1XZFWx9pt2_ahK48,5999
21
+ jarvis/tools/methodology.py,sha256=UG6s5VYRcd9wrKX4cg6f7zJhet5AIcthFGMOAdevBiw,5175
22
+ jarvis/tools/registry.py,sha256=mlOAmUq3yzRz-7yvwrrCwbe5Lmw8eh1v8-_Fa5sezwI,7209
23
+ jarvis/tools/search.py,sha256=1EqOVvLhg2Csh-i03-XeCrusbyfmH69FZ8khwZt8Tow,6131
24
+ jarvis/tools/shell.py,sha256=UPKshPyOaUwTngresUw-ot1jHjQIb4wCY5nkJqa38lU,2520
25
+ jarvis/tools/sub_agent.py,sha256=rEtAmSVY2ZjFOZEKr5m5wpACOQIiM9Zr_3dT92FhXYU,2621
26
+ jarvis/tools/webpage.py,sha256=d3w3Jcjcu1ESciezTkz3n3Zf-rp_l91PrVoDEZnckOo,2391
27
+ jarvis_ai_assistant-0.1.65.dist-info/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
28
+ jarvis_ai_assistant-0.1.65.dist-info/METADATA,sha256=h4jt2RuxMCTuoMTZcxA3ZdFYmQKNayDqe82I0NAitsU,12374
29
+ jarvis_ai_assistant-0.1.65.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
30
+ jarvis_ai_assistant-0.1.65.dist-info/entry_points.txt,sha256=QNUeqmUJd7nHufel2FO7cRttS1uKFfnbIyObv8eVyOY,140
31
+ jarvis_ai_assistant-0.1.65.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
32
+ jarvis_ai_assistant-0.1.65.dist-info/RECORD,,
@@ -1,3 +1,4 @@
1
1
  [console_scripts]
2
2
  jarvis = jarvis.main:main
3
+ jarvis-codebase = jarvis.jarvis_codebase.main:main
3
4
  jarvis-coder = jarvis.jarvis_coder.main:main
@@ -1,29 +0,0 @@
1
- jarvis/__init__.py,sha256=wMtxQxTakpumAd-K51g16_sMIW8so_8Pv15IEYnhfwI,50
2
- jarvis/agent.py,sha256=kl6pwNrluzb-9eZKgwmsk5Jh4CpWi4F8B3RvEQNvc5U,14921
3
- jarvis/main.py,sha256=gXXtnrkkvGwEswJL6qiYjVrg3bpzye-GJeAe0Nf2B9o,6509
4
- jarvis/utils.py,sha256=JlkuC9RtspXH2VWDmj9nR0vnb8ie1gIsKc4vC7WRco8,7321
5
- jarvis/jarvis_coder/main.py,sha256=TosDDiaYSjDpzKPNKcygxZ3XXWbcnvBmIIMn3UMBc60,35102
6
- jarvis/models/__init__.py,sha256=mrOt67nselz_H1gX9wdAO4y2DY5WPXzABqJbr5Des8k,63
7
- jarvis/models/ai8.py,sha256=AMBZRS9hKW1Ts_YoHMMelhT0CRMeMzHtVH31z0FpP-Q,12671
8
- jarvis/models/base.py,sha256=ShV1H8Unee4RMaiFO4idROQA0Hc6wu4dyeRPX5fcszk,1433
9
- jarvis/models/kimi.py,sha256=1iTB0Z_WOmCML3Ufsge6jmeKOYvccr7I5lS3JUXymU4,17611
10
- jarvis/models/openai.py,sha256=7oOxrL6EM7iaqJgZsaFvVmyzY0ars4BP3EKAUlX1RPw,4403
11
- jarvis/models/oyi.py,sha256=mW-uhUZbts2L_oI8WueZUTEmrLY1CiBHn4UV2HP7ZCE,15214
12
- jarvis/models/registry.py,sha256=hJyaROiOF_TkbtIXsjOD8-ArOvAvtxviawyqBFfLV6s,7617
13
- jarvis/tools/__init__.py,sha256=Kj1bKj34lwRDKMKHLOrLyQElf2lHbqA2tDgP359eaDo,71
14
- jarvis/tools/base.py,sha256=EGRGbdfbLXDLwtyoWdvp9rlxNX7bzc20t0Vc2VkwIEY,652
15
- jarvis/tools/coder.py,sha256=ZJfPInKms4Hj3-eQlBwamVsvZ-2nlZ-4jsqJ-tJc6mg,2040
16
- jarvis/tools/file_ops.py,sha256=h8g0eT9UvlJf4kt0DLXvdSsjcPj7x19lxWdDApeDfpg,3842
17
- jarvis/tools/generator.py,sha256=vVP3eN5cCDpRXf_fn0skETkPXAW1XZFWx9pt2_ahK48,5999
18
- jarvis/tools/methodology.py,sha256=UG6s5VYRcd9wrKX4cg6f7zJhet5AIcthFGMOAdevBiw,5175
19
- jarvis/tools/registry.py,sha256=mlOAmUq3yzRz-7yvwrrCwbe5Lmw8eh1v8-_Fa5sezwI,7209
20
- jarvis/tools/search.py,sha256=1EqOVvLhg2Csh-i03-XeCrusbyfmH69FZ8khwZt8Tow,6131
21
- jarvis/tools/shell.py,sha256=UPKshPyOaUwTngresUw-ot1jHjQIb4wCY5nkJqa38lU,2520
22
- jarvis/tools/sub_agent.py,sha256=rEtAmSVY2ZjFOZEKr5m5wpACOQIiM9Zr_3dT92FhXYU,2621
23
- jarvis/tools/webpage.py,sha256=d3w3Jcjcu1ESciezTkz3n3Zf-rp_l91PrVoDEZnckOo,2391
24
- jarvis_ai_assistant-0.1.64.dist-info/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
25
- jarvis_ai_assistant-0.1.64.dist-info/METADATA,sha256=akHo9a0miOBYWvtITaAuo1G3PZxiE2yPSZxuh0QE1C4,11213
26
- jarvis_ai_assistant-0.1.64.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
27
- jarvis_ai_assistant-0.1.64.dist-info/entry_points.txt,sha256=ieRI4ilnGNx1R6LlzT2P510mJ27lhLesVZToezDjSd8,89
28
- jarvis_ai_assistant-0.1.64.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
29
- jarvis_ai_assistant-0.1.64.dist-info/RECORD,,