jarvis-ai-assistant 0.1.88__tar.gz → 0.1.89__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 (51) hide show
  1. {jarvis_ai_assistant-0.1.88/src/jarvis_ai_assistant.egg-info → jarvis_ai_assistant-0.1.89}/PKG-INFO +1 -1
  2. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/pyproject.toml +1 -1
  3. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/setup.py +1 -1
  4. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/__init__.py +1 -1
  5. jarvis_ai_assistant-0.1.89/src/jarvis/jarvis_coder/git_utils.py +67 -0
  6. jarvis_ai_assistant-0.1.89/src/jarvis/jarvis_coder/main.py +358 -0
  7. jarvis_ai_assistant-0.1.89/src/jarvis/jarvis_coder/model_utils.py +30 -0
  8. jarvis_ai_assistant-0.1.89/src/jarvis/jarvis_coder/patch_handler.py +390 -0
  9. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89/src/jarvis_ai_assistant.egg-info}/PKG-INFO +1 -1
  10. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis_ai_assistant.egg-info/SOURCES.txt +3 -0
  11. jarvis_ai_assistant-0.1.88/src/jarvis/jarvis_coder/main.py +0 -706
  12. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/LICENSE +0 -0
  13. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/MANIFEST.in +0 -0
  14. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/README.md +0 -0
  15. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/setup.cfg +0 -0
  16. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/agent.py +0 -0
  17. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/jarvis_codebase/__init__.py +0 -0
  18. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/jarvis_codebase/main.py +0 -0
  19. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/jarvis_coder/__init__.py +0 -0
  20. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/jarvis_rag/__init__.py +0 -0
  21. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/jarvis_rag/main.py +0 -0
  22. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/jarvis_smart_shell/__init__.py +0 -0
  23. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/jarvis_smart_shell/main.py +0 -0
  24. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/main.py +0 -0
  25. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/models/__init__.py +0 -0
  26. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/models/ai8.py +0 -0
  27. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/models/base.py +0 -0
  28. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/models/kimi.py +0 -0
  29. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/models/ollama.py +0 -0
  30. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/models/openai.py +0 -0
  31. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/models/oyi.py +0 -0
  32. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/models/registry.py +0 -0
  33. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/tools/__init__.py +0 -0
  34. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/tools/ask_user.py +0 -0
  35. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/tools/base.py +0 -0
  36. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/tools/chdir.py +0 -0
  37. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/tools/codebase_qa.py +0 -0
  38. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/tools/coder.py +0 -0
  39. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/tools/file_ops.py +0 -0
  40. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/tools/generator.py +0 -0
  41. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/tools/methodology.py +0 -0
  42. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/tools/registry.py +0 -0
  43. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/tools/search.py +0 -0
  44. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/tools/shell.py +0 -0
  45. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/tools/sub_agent.py +0 -0
  46. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/tools/webpage.py +0 -0
  47. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis/utils.py +0 -0
  48. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis_ai_assistant.egg-info/dependency_links.txt +0 -0
  49. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis_ai_assistant.egg-info/entry_points.txt +0 -0
  50. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/src/jarvis_ai_assistant.egg-info/requires.txt +0 -0
  51. {jarvis_ai_assistant-0.1.88 → jarvis_ai_assistant-0.1.89}/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.88
3
+ Version: 0.1.89
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.88"
7
+ version = "0.1.89"
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.88",
5
+ version="0.1.89",
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.88"
3
+ __version__ = "0.1.89"
@@ -0,0 +1,67 @@
1
+ import os
2
+ from typing import List
3
+ import yaml
4
+ import time
5
+ from jarvis.utils import OutputType, PrettyOutput
6
+ from jarvis.models.registry import PlatformRegistry
7
+ from .model_utils import call_model_with_retry
8
+
9
+ def has_uncommitted_files() -> bool:
10
+ """判断代码库是否有未提交的文件"""
11
+ # 获取未暂存的修改
12
+ unstaged = os.popen("git diff --name-only").read()
13
+ # 获取已暂存但未提交的修改
14
+ staged = os.popen("git diff --cached --name-only").read()
15
+ # 获取未跟踪的文件
16
+ untracked = os.popen("git ls-files --others --exclude-standard").read()
17
+
18
+ return bool(unstaged or staged or untracked)
19
+
20
+ def generate_commit_message(git_diff: str, feature: str) -> str:
21
+ """根据git diff和功能描述生成commit信息"""
22
+ prompt = f"""你是一个经验丰富的程序员,请根据以下代码变更和功能描述生成简洁明了的commit信息:
23
+
24
+ 功能描述:
25
+ {feature}
26
+
27
+ 代码变更:
28
+ Git Diff:
29
+ {git_diff}
30
+
31
+ 请遵循以下规则:
32
+ 1. 使用英文编写
33
+ 2. 采用常规的commit message格式:<type>(<scope>): <subject>
34
+ 3. 保持简洁,不超过50个字符
35
+ 4. 准确描述代码变更的主要内容
36
+ 5. 优先考虑功能描述和git diff中的变更内容
37
+ """
38
+
39
+ model = PlatformRegistry().get_global_platform_registry().get_codegen_platform()
40
+ model.set_suppress_output(True)
41
+ success, response = call_model_with_retry(model, prompt)
42
+ if not success:
43
+ return "Update code changes"
44
+
45
+ return response.strip().split("\n")[0]
46
+
47
+ def save_edit_record(record_dir: str, commit_message: str, git_diff: str) -> None:
48
+ """保存代码修改记录"""
49
+ # 获取下一个序号
50
+ existing_records = [f for f in os.listdir(record_dir) if f.endswith('.yaml')]
51
+ next_num = 1
52
+ if existing_records:
53
+ last_num = max(int(f[:4]) for f in existing_records)
54
+ next_num = last_num + 1
55
+
56
+ # 创建记录文件
57
+ record = {
58
+ "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
59
+ "commit_message": commit_message,
60
+ "git_diff": git_diff
61
+ }
62
+
63
+ record_path = os.path.join(record_dir, f"{next_num:04d}.yaml")
64
+ with open(record_path, "w", encoding="utf-8") as f:
65
+ yaml.safe_dump(record, f, allow_unicode=True)
66
+
67
+ PrettyOutput.print(f"已保存修改记录: {record_path}", OutputType.SUCCESS)
@@ -0,0 +1,358 @@
1
+ import os
2
+ import re
3
+ import threading
4
+ import time
5
+ from typing import Dict, Any, List, Tuple
6
+
7
+ import yaml
8
+ from jarvis.models.base import BasePlatform
9
+ from jarvis.utils import OutputType, PrettyOutput, find_git_root, get_max_context_length, get_multiline_input, load_env_from_file
10
+ from jarvis.models.registry import PlatformRegistry
11
+ from jarvis.jarvis_codebase.main import CodeBase
12
+ from prompt_toolkit import PromptSession
13
+ from prompt_toolkit.completion import WordCompleter, Completer, Completion
14
+ from prompt_toolkit.formatted_text import FormattedText
15
+ from prompt_toolkit.styles import Style
16
+ import fnmatch
17
+ from .patch_handler import PatchHandler
18
+ from .git_utils import has_uncommitted_files, generate_commit_message, save_edit_record
19
+
20
+ # 全局锁对象
21
+ index_lock = threading.Lock()
22
+
23
+ class JarvisCoder:
24
+ def __init__(self, root_dir: str, language: str):
25
+ """初始化代码修改工具"""
26
+ self.root_dir = root_dir
27
+ self.language = language
28
+ self._init_directories()
29
+ self._init_codebase()
30
+
31
+ def _init_directories(self):
32
+ """初始化目录"""
33
+ self.max_context_length = get_max_context_length()
34
+
35
+ self.root_dir = find_git_root(self.root_dir)
36
+ if not self.root_dir:
37
+ self.root_dir = self.root_dir
38
+
39
+ PrettyOutput.print(f"Git根目录: {self.root_dir}", OutputType.INFO)
40
+
41
+ # 1. 判断代码库路径是否存在,如果不存在,创建
42
+ if not os.path.exists(self.root_dir):
43
+ PrettyOutput.print(
44
+ "Root directory does not exist, creating...", OutputType.INFO)
45
+ os.makedirs(self.root_dir)
46
+
47
+ os.chdir(self.root_dir)
48
+
49
+ self.jarvis_dir = os.path.join(self.root_dir, ".jarvis-coder")
50
+ if not os.path.exists(self.jarvis_dir):
51
+ os.makedirs(self.jarvis_dir)
52
+
53
+ self.record_dir = os.path.join(self.jarvis_dir, "record")
54
+ if not os.path.exists(self.record_dir):
55
+ os.makedirs(self.record_dir)
56
+
57
+ # 2. 判断代码库是否是git仓库,如果不是,初始化git仓库
58
+ if not os.path.exists(os.path.join(self.root_dir, ".git")):
59
+ PrettyOutput.print(
60
+ "Git repository does not exist, initializing...", OutputType.INFO)
61
+ os.system(f"git init")
62
+ # 2.1 添加所有的文件
63
+ os.system(f"git add .")
64
+ # 2.2 提交
65
+ os.system(f"git commit -m 'Initial commit'")
66
+
67
+ PrettyOutput.print("代码库有未提交的文件,提交一次", OutputType.INFO)
68
+ os.system(f"git add .")
69
+ os.system(f"git commit -m 'commit before code edit'")
70
+ # 3. 查看代码库是否有未提交的文件,如果有,提交一次
71
+ if self._has_uncommitted_files():
72
+ PrettyOutput.print("代码库有未提交的文件,提交一次", OutputType.INFO)
73
+ os.system(f"git add .")
74
+ git_diff = os.popen("git diff --cached").read()
75
+ commit_message = generate_commit_message(git_diff, "Pre-edit commit")
76
+ os.system(f"git commit -m '{commit_message}'")
77
+ PrettyOutput.print("代码库有未提交的文件,提交一次", OutputType.INFO)
78
+ os.system(f"git add .")
79
+ os.system(f"git commit -m 'commit before code edit'")
80
+
81
+ def _init_codebase(self):
82
+ """初始化代码库"""
83
+ self._codebase = CodeBase(self.root_dir)
84
+
85
+ def _new_model(self):
86
+ """获取大模型"""
87
+ model = PlatformRegistry().get_global_platform_registry().get_codegen_platform()
88
+ return model
89
+
90
+ def _has_uncommitted_files(self) -> bool:
91
+ """判断代码库是否有未提交的文件"""
92
+ # 获取未暂存的修改
93
+ unstaged = os.popen("git diff --name-only").read()
94
+ # 获取已暂存但未提交的修改
95
+ staged = os.popen("git diff --cached --name-only").read()
96
+ # 获取未跟踪的文件
97
+ untracked = os.popen("git ls-files --others --exclude-standard").read()
98
+
99
+ return bool(unstaged or staged or untracked)
100
+
101
+ def _prepare_execution(self) -> None:
102
+ """准备执行环境"""
103
+ self.main_model = self._new_model()
104
+ self._codebase.generate_codebase()
105
+
106
+ def _load_related_files(self, feature: str) -> List[Dict]:
107
+ """加载相关文件内容"""
108
+ ret = []
109
+ # 确保索引数据库已生成
110
+ if not self._codebase.is_index_generated():
111
+ PrettyOutput.print("检测到索引数据库未生成,正在生成...", OutputType.WARNING)
112
+ self._codebase.generate_codebase()
113
+
114
+ related_files = self._codebase.search_similar(feature)
115
+ for file, score, _ in related_files:
116
+ PrettyOutput.print(f"相关文件: {file} 相关度: {score:.3f}", OutputType.SUCCESS)
117
+ with open(file, "r", encoding="utf-8") as f:
118
+ content = f.read()
119
+ ret.append({"file_path": file, "file_content": content})
120
+ return ret
121
+
122
+ def _finalize_changes(self, feature: str) -> None:
123
+ """完成修改并提交"""
124
+ PrettyOutput.print("修改确认成功,提交修改", OutputType.INFO)
125
+
126
+ # 只添加已经在 git 控制下的修改文件
127
+ os.system("git add -u")
128
+
129
+ # 然后获取 git diff
130
+ git_diff = os.popen("git diff --cached").read()
131
+
132
+ # 自动生成commit信息,传入feature
133
+ commit_message = generate_commit_message(git_diff, feature)
134
+
135
+ # 显示并确认commit信息
136
+ PrettyOutput.print(f"自动生成的commit信息: {commit_message}", OutputType.INFO)
137
+ user_confirm = input("是否使用该commit信息?(y/n) [y]: ") or "y"
138
+
139
+ if user_confirm.lower() != "y":
140
+ commit_message = input("请输入新的commit信息: ")
141
+
142
+ # 不需要再次 git add,因为已经添加过了
143
+ os.system(f"git commit -m '{commit_message}'")
144
+ save_edit_record(self.record_dir, commit_message, git_diff)
145
+
146
+ def _revert_changes(self) -> None:
147
+ """回退所有修改"""
148
+ PrettyOutput.print("修改已取消,回退更改", OutputType.INFO)
149
+ os.system(f"git reset --hard")
150
+ os.system(f"git clean -df")
151
+
152
+ def execute(self, feature: str) -> Dict[str, Any]:
153
+ """执行代码修改
154
+
155
+ Args:
156
+ feature: 要实现的功能描述
157
+
158
+ Returns:
159
+ Dict[str, Any]: 包含执行结果的字典
160
+ """
161
+ try:
162
+ self._prepare_execution()
163
+ related_files = self._load_related_files(feature)
164
+
165
+ patch_handler = PatchHandler(self.main_model)
166
+ if patch_handler.handle_patch_application(related_files, feature):
167
+ self._finalize_changes(feature)
168
+ return {
169
+ "success": True,
170
+ "stdout": "代码修改成功",
171
+ "stderr": "",
172
+ }
173
+ else:
174
+ self._revert_changes()
175
+ return {
176
+ "success": False,
177
+ "stdout": "",
178
+ "stderr": "代码修改失败,请修改需求后重试",
179
+ }
180
+
181
+ except Exception as e:
182
+ self._revert_changes()
183
+ return {
184
+ "success": False,
185
+ "stdout": "",
186
+ "stderr": f"执行失败: {str(e)},请修改需求后重试",
187
+ "error": e
188
+ }
189
+
190
+ def main():
191
+ """命令行入口"""
192
+ import argparse
193
+
194
+ load_env_from_file()
195
+
196
+ parser = argparse.ArgumentParser(description='代码修改工具')
197
+ parser.add_argument('-d', '--dir', help='项目根目录', default=os.getcwd())
198
+ parser.add_argument('-l', '--language', help='编程语言', default="python")
199
+ args = parser.parse_args()
200
+
201
+ tool = JarvisCoder(args.dir, args.language)
202
+
203
+ # 循环处理需求
204
+ while True:
205
+ try:
206
+ # 获取需求,传入项目根目录
207
+ feature = get_multiline_input("请输入开发需求 (输入空行退出):", tool.root_dir)
208
+
209
+ if not feature or feature == "__interrupt__":
210
+ break
211
+
212
+ # 执行修改
213
+ result = tool.execute(feature)
214
+
215
+ # 显示结果
216
+ if result["success"]:
217
+ PrettyOutput.print(result["stdout"], OutputType.SUCCESS)
218
+ else:
219
+ if result.get("stderr"):
220
+ PrettyOutput.print(result["stderr"], OutputType.WARNING)
221
+ if result.get("error"): # 使用 get() 方法避免 KeyError
222
+ error = result["error"]
223
+ PrettyOutput.print(f"错误类型: {type(error).__name__}", OutputType.WARNING)
224
+ PrettyOutput.print(f"错误信息: {str(error)}", OutputType.WARNING)
225
+ # 提示用户可以继续输入
226
+ PrettyOutput.print("\n您可以修改需求后重试", OutputType.INFO)
227
+
228
+ except KeyboardInterrupt:
229
+ print("\n用户中断执行")
230
+ break
231
+ except Exception as e:
232
+ PrettyOutput.print(f"执行出错: {str(e)}", OutputType.ERROR)
233
+ PrettyOutput.print("\n您可以修改需求后重试", OutputType.INFO)
234
+ continue
235
+
236
+ return 0
237
+
238
+ if __name__ == "__main__":
239
+ exit(main())
240
+
241
+ class FilePathCompleter(Completer):
242
+ """文件路径自动完成器"""
243
+
244
+ def __init__(self, root_dir: str):
245
+ self.root_dir = root_dir
246
+ self._file_list = None
247
+
248
+ def _get_files(self) -> List[str]:
249
+ """获取git管理的文件列表"""
250
+ if self._file_list is None:
251
+ try:
252
+ # 切换到项目根目录
253
+ old_cwd = os.getcwd()
254
+ os.chdir(self.root_dir)
255
+
256
+ # 获取git管理的文件列表
257
+ self._file_list = os.popen("git ls-files").read().splitlines()
258
+
259
+ # 恢复工作目录
260
+ os.chdir(old_cwd)
261
+ except Exception as e:
262
+ PrettyOutput.print(f"获取文件列表失败: {str(e)}", OutputType.WARNING)
263
+ self._file_list = []
264
+ return self._file_list
265
+
266
+ def get_completions(self, document, complete_event):
267
+ """获取补全建议"""
268
+ text_before_cursor = document.text_before_cursor
269
+
270
+ # 检查是否刚输入了@
271
+ if text_before_cursor.endswith('@'):
272
+ # 显示所有文件
273
+ for path in self._get_files():
274
+ yield Completion(path, start_position=0)
275
+ return
276
+
277
+ # 检查之前是否有@,并获取@后的搜索词
278
+ at_pos = text_before_cursor.rfind('@')
279
+ if at_pos == -1:
280
+ return
281
+
282
+ search = text_before_cursor[at_pos + 1:].lower().strip()
283
+
284
+ # 提供匹配的文件建议
285
+ for path in self._get_files():
286
+ path_lower = path.lower()
287
+ if (search in path_lower or # 直接包含
288
+ search in os.path.basename(path_lower) or # 文件名包含
289
+ any(fnmatch.fnmatch(path_lower, f'*{s}*') for s in search.split())): # 通配符匹配
290
+ # 计算正确的start_position
291
+ yield Completion(path, start_position=-(len(search)))
292
+
293
+ class SmartCompleter(Completer):
294
+ """智能自动完成器,组合词语和文件路径补全"""
295
+
296
+ def __init__(self, word_completer: WordCompleter, file_completer: FilePathCompleter):
297
+ self.word_completer = word_completer
298
+ self.file_completer = file_completer
299
+
300
+ def get_completions(self, document, complete_event):
301
+ """获取补全建议"""
302
+ # 如果当前行以@结尾,使用文件补全
303
+ if document.text_before_cursor.strip().endswith('@'):
304
+ yield from self.file_completer.get_completions(document, complete_event)
305
+ else:
306
+ # 否则使用词语补全
307
+ yield from self.word_completer.get_completions(document, complete_event)
308
+
309
+ def get_multiline_input(prompt_text: str, root_dir: str = None) -> str:
310
+ """获取多行输入,支持文件路径自动完成功能
311
+
312
+ Args:
313
+ prompt_text: 提示文本
314
+ root_dir: 项目根目录,用于文件补全
315
+
316
+ Returns:
317
+ str: 用户输入的文本
318
+ """
319
+ # 创建文件补全器
320
+ file_completer = FilePathCompleter(root_dir or os.getcwd())
321
+
322
+ # 创建提示样式
323
+ style = Style.from_dict({
324
+ 'prompt': 'ansicyan bold',
325
+ 'input': 'ansiwhite',
326
+ })
327
+
328
+ # 创建会话
329
+ session = PromptSession(
330
+ completer=file_completer,
331
+ style=style,
332
+ multiline=False,
333
+ enable_history_search=True,
334
+ complete_while_typing=True
335
+ )
336
+
337
+ # 显示初始提示文本
338
+ print(f"\n{prompt_text}")
339
+
340
+ # 创建提示符
341
+ prompt = FormattedText([
342
+ ('class:prompt', ">>> ")
343
+ ])
344
+
345
+ # 获取输入
346
+ lines = []
347
+ try:
348
+ while True:
349
+ line = session.prompt(prompt).strip()
350
+ if not line: # 空行表示输入结束
351
+ break
352
+ lines.append(line)
353
+ except KeyboardInterrupt:
354
+ return "__interrupt__"
355
+ except EOFError:
356
+ pass
357
+
358
+ return "\n".join(lines)
@@ -0,0 +1,30 @@
1
+ from typing import Tuple
2
+ import time
3
+ from jarvis.models.base import BasePlatform
4
+ from jarvis.utils import OutputType, PrettyOutput
5
+
6
+ def call_model_with_retry(model: BasePlatform, prompt: str, max_retries: int = 3, initial_delay: float = 1.0) -> Tuple[bool, str]:
7
+ """调用模型并支持重试
8
+
9
+ Args:
10
+ model: 模型实例
11
+ prompt: 提示词
12
+ max_retries: 最大重试次数
13
+ initial_delay: 初始延迟时间(秒)
14
+
15
+ Returns:
16
+ Tuple[bool, str]: (是否成功, 响应内容)
17
+ """
18
+ delay = initial_delay
19
+ for attempt in range(max_retries):
20
+ try:
21
+ response = model.chat(prompt)
22
+ return True, response
23
+ except Exception as e:
24
+ if attempt == max_retries - 1: # 最后一次尝试
25
+ PrettyOutput.print(f"调用模型失败: {str(e)}", OutputType.ERROR)
26
+ return False, str(e)
27
+
28
+ PrettyOutput.print(f"调用模型失败,{delay}秒后重试: {str(e)}", OutputType.WARNING)
29
+ time.sleep(delay)
30
+ delay *= 2 # 指数退避