jarvis-ai-assistant 0.1.77__tar.gz → 0.1.79__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 (44) hide show
  1. {jarvis_ai_assistant-0.1.77/src/jarvis_ai_assistant.egg-info → jarvis_ai_assistant-0.1.79}/PKG-INFO +1 -1
  2. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/pyproject.toml +1 -1
  3. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/setup.py +1 -1
  4. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/__init__.py +1 -1
  5. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/agent.py +17 -16
  6. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/jarvis_codebase/main.py +6 -26
  7. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/jarvis_coder/main.py +0 -11
  8. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/main.py +1 -16
  9. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/models/registry.py +19 -14
  10. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/rag/main.py +122 -38
  11. jarvis_ai_assistant-0.1.79/src/jarvis/tools/ask_user.py +67 -0
  12. jarvis_ai_assistant-0.1.79/src/jarvis/tools/chdir.py +89 -0
  13. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/tools/generator.py +1 -5
  14. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/tools/registry.py +41 -1
  15. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/tools/search.py +1 -1
  16. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/utils.py +3 -1
  17. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79/src/jarvis_ai_assistant.egg-info}/PKG-INFO +1 -1
  18. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis_ai_assistant.egg-info/SOURCES.txt +2 -0
  19. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/LICENSE +0 -0
  20. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/MANIFEST.in +0 -0
  21. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/README.md +0 -0
  22. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/setup.cfg +0 -0
  23. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/jarvis_codebase/__init__.py +0 -0
  24. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/jarvis_coder/__init__.py +0 -0
  25. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/models/__init__.py +0 -0
  26. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/models/ai8.py +0 -0
  27. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/models/base.py +0 -0
  28. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/models/kimi.py +0 -0
  29. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/models/openai.py +0 -0
  30. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/models/oyi.py +0 -0
  31. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/rag/__init__.py +0 -0
  32. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/tools/__init__.py +0 -0
  33. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/tools/base.py +0 -0
  34. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/tools/codebase_qa.py +0 -0
  35. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/tools/coder.py +0 -0
  36. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/tools/file_ops.py +0 -0
  37. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/tools/methodology.py +0 -0
  38. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/tools/shell.py +0 -0
  39. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/tools/sub_agent.py +0 -0
  40. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis/tools/webpage.py +0 -0
  41. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis_ai_assistant.egg-info/dependency_links.txt +0 -0
  42. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis_ai_assistant.egg-info/entry_points.txt +0 -0
  43. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/src/jarvis_ai_assistant.egg-info/requires.txt +0 -0
  44. {jarvis_ai_assistant-0.1.77 → jarvis_ai_assistant-0.1.79}/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.77
3
+ Version: 0.1.79
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.77"
7
+ version = "0.1.79"
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.77",
5
+ version="0.1.79",
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.77"
3
+ __version__ = "0.1.79"
@@ -24,20 +24,20 @@ class Agent:
24
24
  name: Agent名称,默认为"Jarvis"
25
25
  is_sub_agent: 是否为子Agent,默认为False
26
26
  """
27
- self.model = PlatformRegistry.get_global_platform()
27
+ self.model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
28
28
  self.tool_registry = ToolRegistry.get_global_tool_registry()
29
29
  self.name = name
30
30
  self.is_sub_agent = is_sub_agent
31
31
  self.prompt = ""
32
- self.conversation_turns = 0
32
+ self.conversation_length = 0 # 改用长度计数器
33
33
 
34
- # 从环境变量加载嵌入模型配置
35
- self.embedding_model_name = os.environ.get("JARVIS_EMBEDDING_MODEL", "BAAI/bge-large-zh-v1.5")
34
+ # 从环境变量加载配置
36
35
  self.embedding_dimension = 1536 # Default for many embedding models
36
+ self.max_context_length = int(os.getenv('JARVIS_MAX_CONTEXT_LENGTH', '30720')) # 默认30k
37
37
 
38
38
  # 初始化嵌入模型
39
39
  try:
40
- self.embedding_model = load_embedding_model(self.embedding_model_name)
40
+ self.embedding_model = load_embedding_model()
41
41
 
42
42
  # 预热模型并获取正确的维度
43
43
  test_text = "这是一段测试文本,用于确保模型完全加载。"
@@ -223,6 +223,7 @@ class Agent:
223
223
 
224
224
  # 清空当前对话历史,但保留系统消息
225
225
  self.model.delete_chat()
226
+ self.conversation_length = 0 # 重置对话长度
226
227
 
227
228
  # 添加总结作为新的上下文
228
229
  self.prompt = f"""以下是之前对话的关键信息总结:
@@ -231,9 +232,7 @@ class Agent:
231
232
 
232
233
  请基于以上信息继续完成任务。
233
234
  """
234
-
235
- # 重置对话轮数
236
- self.conversation_turns = 0
235
+ self.conversation_length = len(self.prompt) # 设置新的起始长度
237
236
 
238
237
  except Exception as e:
239
238
  PrettyOutput.print(f"总结对话历史失败: {str(e)}", OutputType.ERROR)
@@ -319,6 +318,7 @@ class Agent:
319
318
  tools_prompt = ""
320
319
 
321
320
  # 选择工具
321
+ PrettyOutput.section("选择工具", OutputType.PLANNING)
322
322
  tools = self.tool_registry.get_all_tools()
323
323
  if tools:
324
324
  tools_prompt += "可用工具:\n"
@@ -331,8 +331,7 @@ class Agent:
331
331
  # 显示任务开始
332
332
  PrettyOutput.section(f"开始新任务: {self.name}", OutputType.PLANNING)
333
333
 
334
- self.clear_history()
335
- self.conversation_turns = 0
334
+ self.clear_history()
336
335
 
337
336
  self.model.set_system_message(f"""你是 {self.name},一个问题处理能力强大的 AI 助手。
338
337
 
@@ -401,14 +400,15 @@ arguments:
401
400
  # 显示思考状态
402
401
  PrettyOutput.print("分析任务...", OutputType.PROGRESS)
403
402
 
404
- # 增加对话轮次
405
- self.conversation_turns += 1
403
+ # 累加对话长度
404
+ self.conversation_length += len(self.prompt)
406
405
 
407
- # 如果对话超过10轮,在提示中添加提醒
408
- if self.conversation_turns > 10:
409
- self.prompt = f"{self.prompt}\n(提示:当前对话已超过10轮,建议使用 !<<SUMMARIZE>>! 指令总结对话历史,避免token超限)"
406
+ # 如果对话历史长度超过限制,在提示中添加提醒
407
+ if self.conversation_length > self.max_context_length:
408
+ self.prompt = f"{self.prompt}\n(提示:当前对话历史长度已超过{self.max_context_length}字符,建议使用 !<<SUMMARIZE>>! 指令总结对话历史,避免token超限)"
410
409
 
411
410
  current_response = self._call_model(self.prompt)
411
+ self.conversation_length += len(current_response) # 添加响应长度
412
412
 
413
413
  # 检查是否需要总结对话历史
414
414
  if "!<<SUMMARIZE>>!" in current_response:
@@ -463,6 +463,7 @@ arguments:
463
463
  """清除对话历史,只保留系统提示"""
464
464
  self.prompt = ""
465
465
  self.model.reset()
466
- self.conversation_turns = 0 # 重置对话轮次
466
+ self.conversation_length = 0 # 重置对话长度
467
+
467
468
 
468
469
 
@@ -19,22 +19,6 @@ class CodeBase:
19
19
  self.root_dir = root_dir
20
20
  os.chdir(self.root_dir)
21
21
  self.thread_count = int(os.environ.get("JARVIS_THREAD_COUNT") or 10)
22
- self.cheap_platform = os.environ.get("JARVIS_CHEAP_PLATFORM") or os.environ.get("JARVIS_PLATFORM") or "kimi"
23
- self.cheap_model = os.environ.get("JARVIS_CHEAP_MODEL") or os.environ.get("JARVIS_MODEL") or "kimi"
24
- self.normal_platform = os.environ.get("JARVIS_PLATFORM") or "kimi"
25
- self.codegen_platform = os.environ.get("JARVIS_CODEGEN_PLATFORM") or os.environ.get("JARVIS_PLATFORM") or "kimi"
26
- self.codegen_model = os.environ.get("JARVIS_CODEGEN_MODEL") or os.environ.get("JARVIS_MODEL") or "kimi"
27
- self.normal_model = os.environ.get("JARVIS_MODEL") or "kimi"
28
- self.embedding_model_name = os.environ.get("JARVIS_EMBEDDING_MODEL") or "BAAI/bge-large-zh-v1.5"
29
- if not self.cheap_platform or not self.cheap_model or not self.codegen_platform or not self.codegen_model or not self.embedding_model_name or not self.normal_platform or not self.normal_model:
30
- raise ValueError("JARVIS_CHEAP_PLATFORM or JARVIS_CHEAP_MODEL or JARVIS_CODEGEN_PLATFORM or JARVIS_CODEGEN_MODEL or JARVIS_EMBEDDING_MODEL or JARVIS_PLATFORM or JARVIS_MODEL is not set")
31
-
32
- PrettyOutput.print(f"廉价模型使用平台: {self.cheap_platform} 模型: {self.cheap_model}", output_type=OutputType.INFO)
33
- PrettyOutput.print(f"代码生成模型使用平台: {self.codegen_platform} 模型: {self.codegen_model}", output_type=OutputType.INFO)
34
- PrettyOutput.print(f"分析模型使用平台: {self.normal_platform} 模型: {self.normal_model}", output_type=OutputType.INFO)
35
- PrettyOutput.print(f"嵌入模型: {self.embedding_model_name}", output_type=OutputType.INFO)
36
- PrettyOutput.print(f"索引建立线程数: {self.thread_count}", output_type=OutputType.INFO)
37
- PrettyOutput.print(f"检索算法:分层导航小世界算法", output_type=OutputType.INFO)
38
22
 
39
23
  # 初始化数据目录
40
24
  self.data_dir = os.path.join(self.root_dir, ".jarvis-codebase")
@@ -43,7 +27,7 @@ class CodeBase:
43
27
 
44
28
  # 初始化嵌入模型,使用系统默认缓存目录
45
29
  try:
46
- self.embedding_model = load_embedding_model(self.embedding_model_name)
30
+ self.embedding_model = load_embedding_model()
47
31
 
48
32
  # 强制完全加载所有模型组件
49
33
  test_text = """
@@ -62,7 +46,7 @@ class CodeBase:
62
46
  self.vector_dim = self.embedding_model.get_sentence_embedding_dimension()
63
47
 
64
48
  self.git_file_list = self.get_git_file_list()
65
- self.platform_registry = PlatformRegistry().get_global_platform_registry()
49
+ self.platform_registry = PlatformRegistry.get_global_platform_registry()
66
50
 
67
51
  # 初始化缓存和索引
68
52
  self.cache_path = os.path.join(self.data_dir, "cache.pkl")
@@ -102,8 +86,7 @@ class CodeBase:
102
86
  return False
103
87
 
104
88
  def make_description(self, file_path: str) -> str:
105
- model = self.platform_registry.create_platform(self.cheap_platform)
106
- model.set_model_name(self.cheap_model)
89
+ model = PlatformRegistry.get_global_platform_registry().get_codegen_platform()
107
90
  model.set_suppress_output(True)
108
91
  content = open(file_path, "r", encoding="utf-8").read()
109
92
  prompt = f"""请分析以下代码文件,并生成一个详细的描述。描述应该包含以下要点:
@@ -411,8 +394,7 @@ class CodeBase:
411
394
  if not initial_results:
412
395
  return []
413
396
 
414
- model = self.platform_registry.create_platform(self.normal_platform)
415
- model.set_model_name(self.normal_model)
397
+ model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
416
398
  model.set_suppress_output(True)
417
399
 
418
400
  try:
@@ -470,8 +452,7 @@ class CodeBase:
470
452
 
471
453
  def search_similar(self, query: str, top_k: int = 20) -> List[Tuple[str, float, str]]:
472
454
  """搜索相似文件"""
473
- model = self.platform_registry.create_platform(self.normal_platform)
474
- model.set_model_name(self.normal_model)
455
+ model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
475
456
  model.set_suppress_output(True)
476
457
 
477
458
  try:
@@ -554,8 +535,7 @@ class CodeBase:
554
535
 
555
536
  请用专业的语言回答用户的问题,如果给出的文件内容不足以回答用户的问题,请告诉用户,绝对不要胡编乱造。
556
537
  """
557
- model = self.platform_registry.create_platform(self.codegen_platform)
558
- model.set_model_name(self.codegen_model)
538
+ model = PlatformRegistry.get_global_platform_registry().get_codegen_platform()
559
539
  try:
560
540
  response = model.chat(prompt)
561
541
  return response
@@ -21,13 +21,7 @@ index_lock = threading.Lock()
21
21
  class JarvisCoder:
22
22
  def __init__(self, root_dir: str, language: str):
23
23
  """初始化代码修改工具"""
24
-
25
- self.platform = os.environ.get("JARVIS_CODEGEN_PLATFORM") or os.environ.get("JARVIS_PLATFORM")
26
- self.model = os.environ.get("JARVIS_CODEGEN_MODEL") or os.environ.get("JARVIS_MODEL")
27
-
28
24
 
29
- if not self.platform or not self.model:
30
- raise ValueError("JARVIS_CODEGEN_PLATFORM or JARVIS_CODEGEN_MODEL is not set")
31
25
 
32
26
  self.root_dir = find_git_root(root_dir)
33
27
  if not self.root_dir:
@@ -486,8 +480,6 @@ def main():
486
480
  load_env_from_file()
487
481
 
488
482
  parser = argparse.ArgumentParser(description='代码修改工具')
489
- parser.add_argument('-p', '--platform', help='AI平台名称', default=os.environ.get('JARVIS_CODEGEN_PLATFORM'))
490
- parser.add_argument('-m', '--model', help='模型名称', default=os.environ.get('JARVIS_CODEGEN_MODEL'))
491
483
  parser.add_argument('-d', '--dir', help='项目根目录', default=os.getcwd())
492
484
  parser.add_argument('-l', '--language', help='编程语言', default="python")
493
485
  args = parser.parse_args()
@@ -495,9 +487,6 @@ def main():
495
487
  # 设置平台
496
488
  if not args.platform:
497
489
  print("错误: 未指定AI平台,请使用 -p 参数")
498
- # 设置模型
499
- if args.model:
500
- os.environ['JARVIS_CODEGEN_MODEL'] = args.model
501
490
 
502
491
  tool = JarvisCoder(args.dir, args.language)
503
492
 
@@ -104,31 +104,16 @@ def main():
104
104
  parser = argparse.ArgumentParser(description='Jarvis AI 助手')
105
105
  parser.add_argument('-f', '--files', nargs='*', help='要处理的文件列表')
106
106
  parser.add_argument('--keep-history', action='store_true', help='保持聊天历史(不删除会话)')
107
- parser.add_argument('-p', '--platform', default=os.getenv('JARVIS_PLATFORM') or 'kimi', help='选择AI平台')
108
- parser.add_argument('-m', '--model', default=os.getenv('JARVIS_MODEL') or '', help='模型') # 用于指定使用的模型名称,默认使用环境变量或平台默认模型
109
107
  args = parser.parse_args()
110
108
 
111
- platform = args.platform if args.platform else os.getenv('JARVIS_PLATFORM')
112
-
113
- if not platform:
114
- PrettyOutput.print("未指定AI平台,请使用 -p 参数或者设置 JARVIS_PLATFORM 环境变量", OutputType.ERROR)
115
- return 1
116
-
117
- PlatformRegistry.get_global_platform_registry().set_global_platform_name(platform)
118
-
119
- if args.model:
120
- os.environ["JARVIS_MODEL"] = args.model
121
-
122
109
  try:
123
110
  # 获取全局模型实例
124
111
  agent = Agent()
125
112
 
126
113
  # 如果用户传入了模型参数,则更换当前模型为用户指定的模型
127
- if args.model:
128
- agent.model.set_model_name(args.model)
129
114
 
130
115
  # 欢迎信息
131
- PrettyOutput.print(f"Jarvis 已初始化 - With {platform} 平台,模型: {agent.model.name()}", OutputType.SYSTEM)
116
+ PrettyOutput.print(f"Jarvis 已初始化 - With {agent.model.name()}", OutputType.SYSTEM)
132
117
  if args.keep_history:
133
118
  PrettyOutput.print("已启用历史保留模式", OutputType.INFO)
134
119
 
@@ -152,14 +152,26 @@ class PlatformRegistry:
152
152
  """
153
153
  self.platforms: Dict[str, Type[BasePlatform]] = {}
154
154
 
155
- @staticmethod
156
- def get_global_platform() -> BasePlatform:
157
- """获取全局平台实例"""
158
- platform = PlatformRegistry.get_global_platform_registry().create_platform(PlatformRegistry.global_platform_name)
159
- if not platform:
160
- raise Exception(f"Failed to create platform: {PlatformRegistry.global_platform_name}")
155
+ def get_normal_platform(self) -> BasePlatform:
156
+ platform_name = os.environ.get("JARVIS_PLATFORM", "kimi")
157
+ model_name = os.environ.get("JARVIS_MODEL", "kimi")
158
+ platform = self.create_platform(platform_name)
159
+ platform.set_model_name(model_name)
160
+ return platform
161
+
162
+ def get_codegen_platform(self) -> BasePlatform:
163
+ platform_name = os.environ.get("JARVIS_CODEGEN_PLATFORM", os.environ.get("JARVIS_PLATFORM", "kimi"))
164
+ model_name = os.environ.get("JARVIS_CODEGEN_MODEL", os.environ.get("JARVIS_MODEL", "kimi"))
165
+ platform = self.create_platform(platform_name)
166
+ platform.set_model_name(model_name)
167
+ return platform
168
+
169
+ def get_cheap_platform(self) -> BasePlatform:
170
+ platform_name = os.environ.get("JARVIS_CHEAP_PLATFORM", os.environ.get("JARVIS_PLATFORM", "kimi"))
171
+ model_name = os.environ.get("JARVIS_CHEAP_MODEL", os.environ.get("JARVIS_MODEL", "kimi"))
172
+ platform = self.create_platform(platform_name)
173
+ platform.set_model_name(model_name)
161
174
  return platform
162
-
163
175
 
164
176
  def register_platform(self, name: str, platform_class: Type[BasePlatform]):
165
177
  """注册平台类
@@ -195,10 +207,3 @@ class PlatformRegistry:
195
207
  """获取可用平台列表"""
196
208
  return list(self.platforms.keys())
197
209
 
198
- def set_global_platform_name(self, platform_name: str):
199
- """设置全局平台"""
200
- PlatformRegistry.global_platform_name = platform_name
201
-
202
- def get_global_platform_name(self) -> str:
203
- """获取全局平台名称"""
204
- return PlatformRegistry.global_platform_name
@@ -13,6 +13,7 @@ from tqdm import tqdm
13
13
  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
+ from jarvis.models.registry import PlatformRegistry
16
17
 
17
18
  @dataclass
18
19
  class Document:
@@ -140,7 +141,6 @@ class RAGTool:
140
141
  # 初始化配置
141
142
  self.min_paragraph_length = int(os.environ.get("JARVIS_MIN_PARAGRAPH_LENGTH", "50")) # 最小段落长度
142
143
  self.max_paragraph_length = int(os.environ.get("JARVIS_MAX_PARAGRAPH_LENGTH", "1000")) # 最大段落长度
143
- self.embedding_model_name = os.environ.get("JARVIS_EMBEDDING_MODEL", "BAAI/bge-large-zh-v1.5")
144
144
 
145
145
  # 初始化数据目录
146
146
  self.data_dir = os.path.join(self.root_dir, ".jarvis-rag")
@@ -149,7 +149,7 @@ class RAGTool:
149
149
 
150
150
  # 初始化嵌入模型
151
151
  try:
152
- self.embedding_model = load_embedding_model(self.embedding_model_name)
152
+ self.embedding_model = load_embedding_model()
153
153
  self.vector_dim = self.embedding_model.get_sentence_embedding_dimension()
154
154
  PrettyOutput.print("模型加载完成", output_type=OutputType.SUCCESS)
155
155
  except Exception as e:
@@ -363,17 +363,17 @@ class RAGTool:
363
363
  output_type=OutputType.ERROR)
364
364
  return []
365
365
 
366
- def build_index(self):
366
+ def build_index(self, dir: str):
367
367
  """构建文档索引"""
368
368
  # 获取所有文件
369
369
  all_files = []
370
- for root, _, files in os.walk(self.root_dir):
370
+ for root, _, files in os.walk(dir):
371
371
  if any(ignored in root for ignored in ['.jarvis-rag', '.git', '__pycache__', 'node_modules']):
372
372
  continue
373
373
  for file in files:
374
374
  file_path = os.path.join(root, file)
375
375
  # 跳过大文件
376
- if os.path.getsize(file_path) > 10 * 1024 * 1024: # 10MB
376
+ if os.path.getsize(file_path) > 100 * 1024 * 1024: # 100MB
377
377
  PrettyOutput.print(f"跳过大文件: {file_path}",
378
378
  output_type=OutputType.WARNING)
379
379
  continue
@@ -412,7 +412,8 @@ class RAGTool:
412
412
  文档和相似度得分的列表
413
413
  """
414
414
  if not self.index:
415
- raise ValueError("索引未构建,请先调用build_index()")
415
+ PrettyOutput.print("索引未构建,正在构建...", output_type=OutputType.INFO)
416
+ self.build_index(self.root_dir)
416
417
 
417
418
  # 获取查询的向量表示
418
419
  query_vector = self._get_embedding(query)
@@ -431,8 +432,73 @@ class RAGTool:
431
432
 
432
433
  return results
433
434
 
435
+ def is_index_built(self):
436
+ """检查索引是否已构建"""
437
+ return self.index is not None
438
+
439
+ def query(self, query: str) -> List[Document]:
440
+ """查询相关文档
441
+
442
+ Args:
443
+ query: 查询文本
444
+
445
+ Returns:
446
+ 相关文档列表
447
+ """
448
+ if not self.is_index_built():
449
+ raise ValueError("索引未构建,请先调用build_index()")
450
+
451
+ results = self.search(query)
452
+ return [doc for doc, _ in results]
453
+
454
+ def ask(self, question: str) -> Optional[str]:
455
+ """询问关于文档的问题
456
+
457
+ Args:
458
+ question: 用户问题
459
+
460
+ Returns:
461
+ 模型回答,如果失败则返回 None
462
+ """
463
+ try:
464
+ # 搜索相关文档片段
465
+ results = self.query(question)
466
+ if not results:
467
+ return None
468
+
469
+ # 构建上下文
470
+ context = []
471
+ for doc in results:
472
+ context.append(f"""
473
+ 来源文件: {doc.metadata['file_path']}
474
+ 片段位置: {doc.metadata['chunk_index'] + 1}/{doc.metadata['total_chunks']}
475
+ 内容:
476
+ {doc.content}
477
+ ---
478
+ """)
479
+
480
+ # 构建提示词
481
+ prompt = f"""请基于以下文档片段回答用户的问题。如果文档片段中的信息不足以完整回答问题,请明确指出。
482
+
483
+ 用户问题: {question}
484
+
485
+ 相关文档片段:
486
+ {''.join(context)}
487
+
488
+ 请提供准确、简洁的回答,并在适当时引用具体的文档来源。
489
+ """
490
+ # 获取模型实例并生成回答
491
+ model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
492
+ response = model.chat(prompt)
493
+
494
+ return response
495
+
496
+ except Exception as e:
497
+ PrettyOutput.print(f"问答失败: {str(e)}", output_type=OutputType.ERROR)
498
+ return None
499
+
434
500
  def main():
435
- """命令行入口"""
501
+ """主函数"""
436
502
  import argparse
437
503
  import sys
438
504
 
@@ -442,39 +508,57 @@ def main():
442
508
  sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict')
443
509
  sys.stderr = codecs.getwriter('utf-8')(sys.stderr.buffer, 'strict')
444
510
 
445
- parser = argparse.ArgumentParser(description='RAG工具')
446
- parser.add_argument('--dir', type=str, default=os.getcwd(), help='项目根目录')
447
- parser.add_argument('--build', action='store_true', help='构建索引')
448
- parser.add_argument('--query', type=str, help='搜索查询')
449
- parser.add_argument('--top-k', type=int, default=5, help='返回结果数量')
450
-
511
+ parser = argparse.ArgumentParser(description='文档检索和分析工具')
512
+ parser.add_argument('--dir', type=str, help='要处理的文档目录')
513
+ parser.add_argument('--build', action='store_true', help='构建文档索引')
514
+ parser.add_argument('--search', type=str, help='搜索文档内容')
515
+ parser.add_argument('--ask', type=str, help='询问关于文档的问题')
451
516
  args = parser.parse_args()
452
-
517
+
453
518
  try:
454
- rag = RAGTool(args.dir)
455
-
456
- if args.build:
457
- rag.build_index()
458
-
459
- if args.query:
460
- results = rag.search(args.query, args.top_k)
461
-
462
- if not results:
463
- PrettyOutput.print("未找到相关内容", output_type=OutputType.WARNING)
464
- return
465
-
466
- PrettyOutput.print("\n搜索结果:", output_type=OutputType.INFO)
467
- for doc, score in results:
468
- PrettyOutput.print("\n" + "="*50, output_type=OutputType.INFO)
469
- PrettyOutput.print(f"文件: {doc.metadata['file_path']}", output_type=OutputType.INFO)
470
- PrettyOutput.print(f"相似度: {score:.3f}", output_type=OutputType.INFO)
471
- PrettyOutput.print(f"片段 {doc.metadata['chunk_index'] + 1}/{doc.metadata['total_chunks']}",
472
- output_type=OutputType.INFO)
473
- PrettyOutput.print("\n内容:", output_type=OutputType.INFO)
474
- # 确保内容是UTF-8编码
475
- content = doc.content.encode('utf-8', errors='replace').decode('utf-8')
476
- PrettyOutput.print(content, output_type=OutputType.INFO)
477
-
519
+ current_dir = find_git_root()
520
+ rag = RAGTool(current_dir)
521
+
522
+ if args.dir and args.build:
523
+ PrettyOutput.print(f"正在处理目录: {args.dir}", output_type=OutputType.INFO)
524
+ rag.build_index(args.dir)
525
+ return 0
526
+
527
+ if args.search or args.ask:
528
+ if not rag.is_index_built():
529
+ PrettyOutput.print("索引尚未构建,请先使用 --dir 和 --build 参数构建索引", output_type=OutputType.WARNING)
530
+ return 1
531
+
532
+ if args.search:
533
+ results = rag.query(args.search)
534
+ if not results:
535
+ PrettyOutput.print("未找到相关内容", output_type=OutputType.WARNING)
536
+ return 1
537
+
538
+ for doc in results:
539
+ PrettyOutput.print(f"\n文件: {doc.metadata['file_path']}", output_type=OutputType.INFO)
540
+ PrettyOutput.print(f"片段 {doc.metadata['chunk_index'] + 1}/{doc.metadata['total_chunks']}",
541
+ output_type=OutputType.INFO)
542
+ PrettyOutput.print("\n内容:", output_type=OutputType.INFO)
543
+ content = doc.content.encode('utf-8', errors='replace').decode('utf-8')
544
+ PrettyOutput.print(content, output_type=OutputType.INFO)
545
+ return 0
546
+
547
+ if args.ask:
548
+ # 调用 ask 方法
549
+ response = rag.ask(args.ask)
550
+ if not response:
551
+ PrettyOutput.print("未能获取答案", output_type=OutputType.WARNING)
552
+ return 1
553
+
554
+ # 显示回答
555
+ PrettyOutput.print("\n回答:", output_type=OutputType.INFO)
556
+ PrettyOutput.print(response, output_type=OutputType.INFO)
557
+ return 0
558
+
559
+ PrettyOutput.print("请指定操作参数。使用 -h 查看帮助。", output_type=OutputType.WARNING)
560
+ return 1
561
+
478
562
  except Exception as e:
479
563
  PrettyOutput.print(f"执行失败: {str(e)}", output_type=OutputType.ERROR)
480
564
  return 1
@@ -0,0 +1,67 @@
1
+ from typing import Dict, Any
2
+ from jarvis.tools.base import Tool
3
+ from jarvis.utils import get_multiline_input, PrettyOutput, OutputType
4
+
5
+ class AskUserTool(Tool):
6
+ def __init__(self):
7
+ super().__init__(
8
+ name="ask_user",
9
+ description="""当缺少完成任务的信息或有关键决策信息缺失时,询问用户。
10
+ 用户可以输入多行文本,空行结束输入。
11
+
12
+ 使用场景:
13
+ 1. 需要用户提供更多信息来完成任务
14
+ 2. 需要用户做出关键决策
15
+ 3. 需要用户确认某些重要操作
16
+ 4. 需要用户提供额外信息
17
+
18
+ 参数说明:
19
+ - question: 要询问用户的问题,应该清晰明确""",
20
+ parameters={
21
+ "type": "object",
22
+ "properties": {
23
+ "question": {
24
+ "type": "string",
25
+ "description": "要询问用户的问题"
26
+ }
27
+ },
28
+ "required": ["question"]
29
+ }
30
+ )
31
+
32
+ def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
33
+ """执行询问用户操作
34
+
35
+ Args:
36
+ args: 包含问题的字典
37
+
38
+ Returns:
39
+ Dict: 包含用户响应的字典
40
+ """
41
+ try:
42
+ question = args["question"]
43
+
44
+ # 显示问题
45
+ PrettyOutput.print("\n问题:", OutputType.QUESTION)
46
+ PrettyOutput.print(question, OutputType.QUESTION)
47
+
48
+ # 获取用户输入
49
+ PrettyOutput.print("\n请输入您的回答(输入空行结束):", OutputType.INPUT)
50
+ user_response = get_multiline_input()
51
+
52
+ if user_response == "__interrupt__":
53
+ return {
54
+ "success": False,
55
+ "error": "用户取消了输入"
56
+ }
57
+
58
+ return {
59
+ "success": True,
60
+ "stdout": user_response
61
+ }
62
+
63
+ except Exception as e:
64
+ return {
65
+ "success": False,
66
+ "error": f"询问用户失败: {str(e)}"
67
+ }
@@ -0,0 +1,89 @@
1
+ from typing import Dict, Any
2
+ import os
3
+ from jarvis.utils import PrettyOutput, OutputType
4
+
5
+ class ChdirTool:
6
+ """修改当前工作目录的工具"""
7
+
8
+ name = "chdir"
9
+ description = "修改当前工作目录"
10
+ parameters = {
11
+ "type": "object",
12
+ "properties": {
13
+ "path": {
14
+ "type": "string",
15
+ "description": "要切换到的目录路径,支持相对路径和绝对路径"
16
+ }
17
+ },
18
+ "required": ["path"]
19
+ }
20
+
21
+ def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
22
+ """执行目录切换
23
+
24
+ Args:
25
+ args: 包含 path 参数的字典
26
+
27
+ Returns:
28
+ 执行结果字典,包含:
29
+ - success: 是否成功
30
+ - stdout: 成功时的输出信息
31
+ - error: 失败时的错误信息
32
+ """
33
+ try:
34
+ path = os.path.expanduser(args["path"]) # 展开 ~ 等路径
35
+ path = os.path.abspath(path) # 转换为绝对路径
36
+
37
+ # 检查目录是否存在
38
+ if not os.path.exists(path):
39
+ return {
40
+ "success": False,
41
+ "error": f"目录不存在: {path}"
42
+ }
43
+
44
+ # 检查是否是目录
45
+ if not os.path.isdir(path):
46
+ return {
47
+ "success": False,
48
+ "error": f"路径不是目录: {path}"
49
+ }
50
+
51
+ # 尝试切换目录
52
+ old_path = os.getcwd()
53
+ os.chdir(path)
54
+
55
+ return {
56
+ "success": True,
57
+ "stdout": f"已切换工作目录:\n从: {old_path}\n到: {path}",
58
+ "stderr": ""
59
+ }
60
+
61
+ except PermissionError:
62
+ return {
63
+ "success": False,
64
+ "error": f"没有权限访问目录: {path}"
65
+ }
66
+ except Exception as e:
67
+ return {
68
+ "success": False,
69
+ "error": f"切换目录失败: {str(e)}"
70
+ }
71
+
72
+ def main():
73
+ """命令行直接运行工具"""
74
+ import argparse
75
+
76
+ parser = argparse.ArgumentParser(description='修改当前工作目录')
77
+ parser.add_argument('path', help='要切换到的目录路径')
78
+ args = parser.parse_args()
79
+
80
+ tool = ChdirTool()
81
+ result = tool.execute({"path": args.path})
82
+
83
+ if result["success"]:
84
+ PrettyOutput.print(result["stdout"], OutputType.SUCCESS)
85
+ else:
86
+ PrettyOutput.print(result["error"], OutputType.ERROR)
87
+
88
+ if __name__ == "__main__":
89
+ main()
@@ -42,11 +42,7 @@ class ToolGeneratorTool:
42
42
 
43
43
  def _generate_tool_code(self, tool_name: str, class_name: str, description: str, parameters: Dict) -> str:
44
44
  """使用大模型生成工具代码"""
45
- platform_name = os.getenv("JARVIS_CODEGEN_PLATFORM") or PlatformRegistry.get_global_platform_name()
46
- model = PlatformRegistry.create_platform(platform_name)
47
- model_name = os.getenv("JARVIS_CODEGEN_MODEL")
48
- if model_name:
49
- model.set_model_name(model_name)
45
+ model = PlatformRegistry.get_global_platform_registry().get_codegen_platform()
50
46
 
51
47
  prompt = f"""请生成一个Python工具类的代码,要求如下,除了代码,不要输出任何内容:
52
48
 
@@ -1,6 +1,6 @@
1
-
2
1
  import importlib
3
2
  import json
3
+ import os
4
4
  from pathlib import Path
5
5
  import sys
6
6
  from typing import Any, Callable, Dict, List, Optional
@@ -19,6 +19,7 @@ class ToolRegistry:
19
19
  # 加载内置工具和外部工具
20
20
  self._load_builtin_tools()
21
21
  self._load_external_tools()
22
+ self.max_context_length = int(os.getenv('JARVIS_MAX_CONTEXT_LENGTH', '30720')) # 默认30k
22
23
 
23
24
  @staticmethod
24
25
  def get_global_tool_registry():
@@ -173,12 +174,51 @@ class ToolRegistry:
173
174
  output = "\n\n".join(output_parts)
174
175
  output = "没有输出和错误" if not output else output
175
176
  PrettyOutput.section("执行成功", OutputType.SUCCESS)
177
+
178
+ # 如果输出超过4k字符,使用大模型总结
179
+ if len(output) > 4096:
180
+ try:
181
+ PrettyOutput.print("输出较长,正在总结...", OutputType.PROGRESS)
182
+ model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
183
+
184
+ # 如果输出超过30k,只取最后30k字符
185
+ if len(output) > self.max_context_length:
186
+ output_to_summarize = output[-self.max_context_length:]
187
+ truncation_notice = "\n(注意: 由于输出过长,仅总结最后30720字符)"
188
+ else:
189
+ output_to_summarize = output
190
+ truncation_notice = ""
191
+
192
+ prompt = f"""请总结以下工具执行结果,提取关键信息和重要结果。注意:
193
+ 1. 保留所有重要的数值、路径、错误信息等关键数据
194
+ 2. 保持结果的准确性
195
+ 3. 用简洁的语言描述主要内容
196
+ 4. 如果有错误信息,确保包含在总结中
197
+
198
+ 工具名称: {name}
199
+ 执行结果:
200
+ {output_to_summarize}
201
+
202
+ 请提供总结:"""
203
+
204
+ summary = model.chat(prompt)
205
+ output = f"""--- 原始输出较长,以下是总结 ---{truncation_notice}
206
+
207
+ {summary}
208
+
209
+ --- 总结结束 ---"""
210
+
211
+ except Exception as e:
212
+ PrettyOutput.print(f"总结失败: {str(e)}", OutputType.WARNING)
213
+ output = f"输出较长 ({len(output)} 字符),建议查看原始输出。\n前300字符预览:\n{output[:300]}..."
214
+
176
215
  else:
177
216
  error_msg = result["error"]
178
217
  output = f"执行失败: {error_msg}"
179
218
  PrettyOutput.section("执行失败", OutputType.ERROR)
180
219
 
181
220
  return output
221
+
182
222
  except Exception as e:
183
223
  PrettyOutput.print(f"执行工具失败: {str(e)}", OutputType.ERROR)
184
224
  return f"Tool call failed: {str(e)}"
@@ -86,7 +86,7 @@ class SearchTool:
86
86
 
87
87
  def __init__(self):
88
88
  """初始化搜索工具,需要传入语言模型用于信息提取"""
89
- self.model = PlatformRegistry.get_global_platform_registry().get_global_platform()
89
+ self.model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
90
90
  self.webpage_tool = WebpageTool()
91
91
 
92
92
  def _search(self, query: str, max_results: int) -> List[Dict]:
@@ -209,8 +209,9 @@ def find_git_root(dir="."):
209
209
  os.chdir(curr_dir)
210
210
  return ret
211
211
 
212
- def load_embedding_model(model_name: str):
212
+ def load_embedding_model():
213
213
  os.environ["TOKENIZERS_PARALLELISM"] = "false"
214
+ model_name = os.environ.get("JARVIS_EMBEDDING_MODEL", "BAAI/bge-large-zh-v1.5")
214
215
  PrettyOutput.print(f"正在加载嵌入模型: {model_name}...", OutputType.INFO)
215
216
  try:
216
217
  # 首先尝试离线加载
@@ -230,4 +231,5 @@ def load_embedding_model(model_name: str):
230
231
  cache_folder=os.path.expanduser("~/.cache/huggingface/hub")
231
232
  )
232
233
  PrettyOutput.print("模型下载并加载成功", OutputType.SUCCESS)
234
+
233
235
  return embedding_model
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.1.77
3
+ Version: 0.1.79
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
@@ -21,7 +21,9 @@ src/jarvis/models/registry.py
21
21
  src/jarvis/rag/__init__.py
22
22
  src/jarvis/rag/main.py
23
23
  src/jarvis/tools/__init__.py
24
+ src/jarvis/tools/ask_user.py
24
25
  src/jarvis/tools/base.py
26
+ src/jarvis/tools/chdir.py
25
27
  src/jarvis/tools/codebase_qa.py
26
28
  src/jarvis/tools/coder.py
27
29
  src/jarvis/tools/file_ops.py