jarvis-ai-assistant 0.1.165__py3-none-any.whl → 0.1.167__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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +15 -3
- jarvis/jarvis_code_analysis/code_review.py +4 -4
- jarvis/jarvis_git_details/main.py +153 -65
- jarvis/jarvis_platform/base.py +14 -2
- jarvis/jarvis_platform/kimi.py +4 -0
- jarvis/jarvis_platform/yuanbao.py +5 -0
- jarvis/jarvis_smart_shell/main.py +12 -2
- jarvis/jarvis_tools/edit_file.py +2 -2
- jarvis/jarvis_tools/find_methodology.py +5 -1
- jarvis/jarvis_tools/generate_new_tool.py +288 -0
- jarvis/jarvis_tools/read_webpage.py +5 -0
- jarvis/jarvis_tools/search_web.py +6 -1
- jarvis/jarvis_utils/config.py +9 -0
- jarvis/jarvis_utils/git_utils.py +40 -1
- jarvis/jarvis_utils/methodology.py +11 -5
- jarvis/jarvis_utils/utils.py +9 -1
- {jarvis_ai_assistant-0.1.165.dist-info → jarvis_ai_assistant-0.1.167.dist-info}/METADATA +45 -45
- {jarvis_ai_assistant-0.1.165.dist-info → jarvis_ai_assistant-0.1.167.dist-info}/RECORD +23 -22
- {jarvis_ai_assistant-0.1.165.dist-info → jarvis_ai_assistant-0.1.167.dist-info}/WHEEL +1 -1
- {jarvis_ai_assistant-0.1.165.dist-info/licenses → jarvis_ai_assistant-0.1.167.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.165.dist-info → jarvis_ai_assistant-0.1.167.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.165.dist-info → jarvis_ai_assistant-0.1.167.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
jarvis/jarvis_agent/__init__.py
CHANGED
|
@@ -310,6 +310,17 @@ class Agent:
|
|
|
310
310
|
"""
|
|
311
311
|
self.after_tool_call_cb = cb
|
|
312
312
|
|
|
313
|
+
def get_tool_registry(self) -> Optional[ToolRegistry]:
|
|
314
|
+
"""获取工具注册器。
|
|
315
|
+
|
|
316
|
+
返回:
|
|
317
|
+
ToolRegistry: 工具注册器实例
|
|
318
|
+
"""
|
|
319
|
+
for handler in self.output_handler:
|
|
320
|
+
if isinstance(handler, ToolRegistry):
|
|
321
|
+
return handler
|
|
322
|
+
return None
|
|
323
|
+
|
|
313
324
|
def make_default_addon_prompt(self, need_complete: bool) -> str:
|
|
314
325
|
"""生成附加提示。
|
|
315
326
|
|
|
@@ -321,7 +332,7 @@ class Agent:
|
|
|
321
332
|
action_handlers = '\n'.join([f'- {handler.name()}' for handler in self.output_handler])
|
|
322
333
|
|
|
323
334
|
# 任务完成提示
|
|
324
|
-
complete_prompt = f"
|
|
335
|
+
complete_prompt = f"3. 输出{ot('!!!COMPLETE!!!')}" if need_complete and self.auto_complete else ""
|
|
325
336
|
|
|
326
337
|
addon_prompt = f"""
|
|
327
338
|
<addon>
|
|
@@ -342,9 +353,10 @@ class Agent:
|
|
|
342
353
|
</actions>
|
|
343
354
|
|
|
344
355
|
<completion>
|
|
345
|
-
|
|
356
|
+
如果任务已完成,请:
|
|
346
357
|
1. 说明完成原因
|
|
347
358
|
2. 保持输出格式规范
|
|
359
|
+
{complete_prompt}
|
|
348
360
|
</completion>
|
|
349
361
|
</addon>
|
|
350
362
|
"""
|
|
@@ -658,7 +670,7 @@ arguments:
|
|
|
658
670
|
msg = user_input
|
|
659
671
|
for handler in self.input_handler:
|
|
660
672
|
msg, _ = handler(msg, self)
|
|
661
|
-
self.prompt = f"{user_input}\n\n以下是历史类似问题的执行经验,可参考:\n{load_methodology(msg)}"
|
|
673
|
+
self.prompt = f"{user_input}\n\n以下是历史类似问题的执行经验,可参考:\n{load_methodology(msg, self.get_tool_registry())}"
|
|
662
674
|
self.first = False
|
|
663
675
|
|
|
664
676
|
while True:
|
|
@@ -8,7 +8,6 @@ import tempfile
|
|
|
8
8
|
from yaspin import yaspin
|
|
9
9
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
10
10
|
from jarvis.jarvis_tools.read_code import ReadCodeTool
|
|
11
|
-
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
12
11
|
from jarvis.jarvis_agent import Agent
|
|
13
12
|
|
|
14
13
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
@@ -488,7 +487,7 @@ class CodeReviewTool:
|
|
|
488
487
|
|
|
489
488
|
我将分析上传的代码差异文件,进行全面的代码审查。
|
|
490
489
|
</code_review_guide>"""
|
|
491
|
-
|
|
490
|
+
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
492
491
|
tool_registry = ToolRegistry()
|
|
493
492
|
tool_registry.dont_use_tools(["code_review"])
|
|
494
493
|
agent = Agent(
|
|
@@ -554,8 +553,9 @@ class CodeReviewTool:
|
|
|
554
553
|
如果检测到了特定编程语言,请参考语言特定的审查清单进行评估,并在报告中包含相关分析。
|
|
555
554
|
</notes>
|
|
556
555
|
|
|
556
|
+
输出格式:
|
|
557
557
|
{ot("REPORT")}
|
|
558
|
-
[
|
|
558
|
+
[在此处插入完整MARKDOWN格式的审查报告]
|
|
559
559
|
{ct("REPORT")}""",
|
|
560
560
|
output_handler=[tool_registry],
|
|
561
561
|
platform=PlatformRegistry().get_thinking_platform(),
|
|
@@ -654,7 +654,7 @@ def extract_code_report(result: str) -> str:
|
|
|
654
654
|
sm = re.search(ot("REPORT")+r'\n(.*?)\n'+ct("REPORT"), result, re.DOTALL)
|
|
655
655
|
if sm:
|
|
656
656
|
return sm.group(1)
|
|
657
|
-
return
|
|
657
|
+
return result
|
|
658
658
|
|
|
659
659
|
def main():
|
|
660
660
|
"""CLI entry point"""
|
|
@@ -31,41 +31,140 @@ class GitCommitAnalyzer:
|
|
|
31
31
|
"""执行commit分析
|
|
32
32
|
|
|
33
33
|
Args:
|
|
34
|
-
args: 包含commit_sha和root_dir的参数字典
|
|
34
|
+
args: 包含commit_sha/commit_range和root_dir的参数字典
|
|
35
|
+
commit_sha: 单个commit的SHA
|
|
36
|
+
commit_range: 两个commit的SHA范围,格式为"commit1..commit2"
|
|
37
|
+
root_dir: 代码库根目录
|
|
35
38
|
|
|
36
39
|
Returns:
|
|
37
|
-
|
|
38
|
-
- success: 操作是否成功
|
|
39
|
-
- stdout: 包含commit_info和diff_content的结果
|
|
40
|
-
- stderr: 错误信息(如果操作失败)
|
|
40
|
+
包含分析结果的字典
|
|
41
41
|
"""
|
|
42
42
|
try:
|
|
43
|
-
commit_sha = args
|
|
43
|
+
commit_sha = args.get("commit_sha")
|
|
44
|
+
commit_range = args.get("commit_range")
|
|
44
45
|
root_dir = args.get("root_dir", ".")
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
47
|
+
if commit_range:
|
|
48
|
+
return self.analyze_commit_range(commit_range, root_dir)
|
|
49
|
+
elif commit_sha:
|
|
50
|
+
return self.analyze_single_commit(commit_sha, root_dir)
|
|
51
|
+
else:
|
|
52
|
+
raise ValueError("Either commit_sha or commit_range must be provided")
|
|
53
|
+
except Exception as e:
|
|
54
|
+
return {
|
|
55
|
+
"success": False,
|
|
56
|
+
"stdout": {},
|
|
57
|
+
"stderr": f"Failed to analyze commit: {str(e)}"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
def analyze_single_commit(self, commit_sha: str, root_dir: str) -> Dict[str, Any]:
|
|
61
|
+
"""分析单个commit
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
commit_sha: commit的SHA
|
|
65
|
+
root_dir: 代码库根目录
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
包含分析结果的字典
|
|
69
|
+
"""
|
|
70
|
+
original_dir = os.getcwd()
|
|
71
|
+
try:
|
|
72
|
+
os.chdir(root_dir)
|
|
73
|
+
|
|
74
|
+
# 获取commit详细信息
|
|
75
|
+
commit_info = subprocess.check_output(
|
|
76
|
+
f"git show {commit_sha} --pretty=fuller",
|
|
77
|
+
shell=True,
|
|
78
|
+
text=True
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# 获取commit修改内容
|
|
82
|
+
diff_content = subprocess.check_output(
|
|
83
|
+
f"git show {commit_sha} --patch",
|
|
84
|
+
shell=True,
|
|
85
|
+
text=True
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# 分析commit的功能、原因和逻辑
|
|
89
|
+
analysis_result = self._analyze_diff_content(diff_content)
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
"success": True,
|
|
93
|
+
"stdout": {
|
|
94
|
+
"commit_info": commit_info,
|
|
95
|
+
"diff_content": diff_content,
|
|
96
|
+
"analysis_result": analysis_result
|
|
97
|
+
},
|
|
98
|
+
"stderr": ""
|
|
99
|
+
}
|
|
100
|
+
except subprocess.CalledProcessError as error:
|
|
101
|
+
return {
|
|
102
|
+
"success": False,
|
|
103
|
+
"stdout": {},
|
|
104
|
+
"stderr": f"Failed to analyze commit: {str(error)}"
|
|
105
|
+
}
|
|
106
|
+
finally:
|
|
107
|
+
os.chdir(original_dir)
|
|
108
|
+
|
|
109
|
+
def analyze_commit_range(self, commit_range: str, root_dir: str) -> Dict[str, Any]:
|
|
110
|
+
"""分析两个commit之间的代码变更
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
commit_range: 两个commit的SHA范围,格式为"commit1..commit2"
|
|
114
|
+
root_dir: 代码库根目录
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
包含分析结果的字典
|
|
118
|
+
"""
|
|
119
|
+
original_dir = os.getcwd()
|
|
120
|
+
try:
|
|
121
|
+
os.chdir(root_dir)
|
|
122
|
+
|
|
123
|
+
# 获取commit范围差异
|
|
124
|
+
diff_content = subprocess.check_output(
|
|
125
|
+
f"git diff {commit_range} --patch",
|
|
126
|
+
shell=True,
|
|
127
|
+
text=True
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# 获取commit范围信息
|
|
131
|
+
commit_info = subprocess.check_output(
|
|
132
|
+
f"git log {commit_range} --pretty=fuller",
|
|
133
|
+
shell=True,
|
|
134
|
+
text=True
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# 使用相同的分析方法处理差异内容
|
|
138
|
+
analysis_result = self._analyze_diff_content(diff_content)
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
"success": True,
|
|
142
|
+
"stdout": {
|
|
143
|
+
"commit_info": commit_info,
|
|
144
|
+
"diff_content": diff_content,
|
|
145
|
+
"analysis_result": analysis_result
|
|
146
|
+
},
|
|
147
|
+
"stderr": ""
|
|
148
|
+
}
|
|
149
|
+
except subprocess.CalledProcessError as error:
|
|
150
|
+
return {
|
|
151
|
+
"success": False,
|
|
152
|
+
"stdout": {},
|
|
153
|
+
"stderr": f"Failed to analyze commit range: {str(error)}"
|
|
154
|
+
}
|
|
155
|
+
finally:
|
|
156
|
+
os.chdir(original_dir)
|
|
157
|
+
|
|
158
|
+
def _analyze_diff_content(self, diff_content: str) -> str:
|
|
159
|
+
"""分析diff内容并生成报告
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
diff_content: git diff或git show的输出内容
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
分析结果字符串
|
|
166
|
+
"""
|
|
167
|
+
system_prompt = """你是一位资深代码分析专家,拥有多年代码审查和重构经验。你需要对Git commit进行深入分析,包括:
|
|
69
168
|
1. 修改的功能:明确说明本次commit实现或修改了哪些功能
|
|
70
169
|
2. 修改的原因:分析为什么要进行这些修改(如修复bug、优化性能、添加新功能等)
|
|
71
170
|
3. 修改的逻辑:详细说明代码修改的具体实现逻辑和思路
|
|
@@ -80,11 +179,11 @@ class GitCommitAnalyzer:
|
|
|
80
179
|
- 保持结构清晰,便于理解
|
|
81
180
|
- 重点关注关键修改和潜在风险"""
|
|
82
181
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
182
|
+
tool_registry = ToolRegistry()
|
|
183
|
+
agent = Agent(
|
|
184
|
+
system_prompt=system_prompt,
|
|
185
|
+
name="Commit Analysis Agent",
|
|
186
|
+
summary_prompt=f"""请生成一份详细的commit分析报告,包含以下内容:
|
|
88
187
|
{ot("REPORT")}
|
|
89
188
|
# 功能分析
|
|
90
189
|
[说明本次commit实现或修改了哪些功能]
|
|
@@ -107,31 +206,12 @@ class GitCommitAnalyzer:
|
|
|
107
206
|
# 最佳实践
|
|
108
207
|
[检查代码是否符合行业最佳实践和项目规范]
|
|
109
208
|
{ct("REPORT")}""",
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
"success": True,
|
|
119
|
-
"stdout": {
|
|
120
|
-
"commit_info": commit_info,
|
|
121
|
-
"diff_content": diff_content,
|
|
122
|
-
"analysis_result": analysis_result
|
|
123
|
-
},
|
|
124
|
-
"stderr": ""
|
|
125
|
-
}
|
|
126
|
-
finally:
|
|
127
|
-
# Always restore original directory
|
|
128
|
-
os.chdir(original_dir)
|
|
129
|
-
except subprocess.CalledProcessError as error:
|
|
130
|
-
return {
|
|
131
|
-
"success": False,
|
|
132
|
-
"stdout": {},
|
|
133
|
-
"stderr": f"Failed to analyze commit: {str(error)}"
|
|
134
|
-
}
|
|
209
|
+
output_handler=[tool_registry],
|
|
210
|
+
platform=PlatformRegistry().get_thinking_platform(),
|
|
211
|
+
auto_complete=True
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
return agent.run(diff_content)
|
|
135
215
|
|
|
136
216
|
|
|
137
217
|
def extract_analysis_report(result: str) -> str:
|
|
@@ -146,7 +226,7 @@ def extract_analysis_report(result: str) -> str:
|
|
|
146
226
|
search_match = re.search(ot("REPORT")+r'\n(.*?)\n'+ct("REPORT"), result, re.DOTALL)
|
|
147
227
|
if search_match:
|
|
148
228
|
return search_match.group(1)
|
|
149
|
-
return
|
|
229
|
+
return result
|
|
150
230
|
|
|
151
231
|
|
|
152
232
|
def main():
|
|
@@ -156,16 +236,24 @@ def main():
|
|
|
156
236
|
init_env()
|
|
157
237
|
|
|
158
238
|
parser = argparse.ArgumentParser(description='Git Commit Analyzer')
|
|
159
|
-
parser.
|
|
239
|
+
group = parser.add_mutually_exclusive_group(required=True)
|
|
240
|
+
group.add_argument('commit', nargs='?', help='Commit SHA to analyze')
|
|
241
|
+
group.add_argument('--range', type=str, help='Commit range to analyze (commit1..commit2)')
|
|
160
242
|
parser.add_argument('--root-dir', type=str, help='Root directory of the codebase', default=".")
|
|
161
243
|
|
|
162
244
|
args = parser.parse_args()
|
|
163
245
|
|
|
164
246
|
analyzer = GitCommitAnalyzer()
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
247
|
+
if args.range:
|
|
248
|
+
result = analyzer.execute({
|
|
249
|
+
"commit_range": args.range,
|
|
250
|
+
"root_dir": args.root_dir
|
|
251
|
+
})
|
|
252
|
+
else:
|
|
253
|
+
result = analyzer.execute({
|
|
254
|
+
"commit_sha": args.commit,
|
|
255
|
+
"root_dir": args.root_dir
|
|
256
|
+
})
|
|
169
257
|
|
|
170
258
|
if result["success"]:
|
|
171
259
|
PrettyOutput.section("Commit Information:", OutputType.SUCCESS)
|
jarvis/jarvis_platform/base.py
CHANGED
|
@@ -40,13 +40,20 @@ class BasePlatform(ABC):
|
|
|
40
40
|
raise NotImplementedError("upload_files is not implemented")
|
|
41
41
|
|
|
42
42
|
def chat_big_content(self, content: str, prompt: str) -> str:
|
|
43
|
+
# 检查content大小不超过10MB
|
|
44
|
+
if len(content.encode('utf-8')) > 10 * 1024 * 1024:
|
|
45
|
+
return "Error: Content size exceeds 10MB limit"
|
|
46
|
+
|
|
43
47
|
prefix_prompt = f"""
|
|
44
48
|
我将分多次提供大量的上下文内容,在我明确告诉你内容已经全部提供完毕之前,每次仅需要输出“已收到”。
|
|
45
49
|
"""
|
|
46
50
|
self.chat_until_success(prefix_prompt)
|
|
47
|
-
split_content = split_text_into_chunks(content, get_max_input_token_count() - 1024)
|
|
51
|
+
split_content = split_text_into_chunks(content, get_max_input_token_count() - 1024, get_max_input_token_count() - 2048)
|
|
52
|
+
submit_count = 0
|
|
48
53
|
for chunk in split_content:
|
|
49
|
-
|
|
54
|
+
submit_count += 1
|
|
55
|
+
PrettyOutput.print(f"已提交{submit_count}次(总{len(split_content)}次)", OutputType.INFO)
|
|
56
|
+
self.chat_until_success(f"<part_content>{chunk}</part_content>请返回已收到")
|
|
50
57
|
return self.chat_until_success(f"内容已经全部提供完毕\n\n{prompt}")
|
|
51
58
|
|
|
52
59
|
|
|
@@ -109,3 +116,8 @@ class BasePlatform(ABC):
|
|
|
109
116
|
def set_web(self, web: bool):
|
|
110
117
|
"""Set web flag"""
|
|
111
118
|
self.web = web
|
|
119
|
+
|
|
120
|
+
@abstractmethod
|
|
121
|
+
def support_web(self) -> bool:
|
|
122
|
+
"""Check if platform supports web functionality"""
|
|
123
|
+
raise NotImplementedError("support_web is not implemented")
|
jarvis/jarvis_platform/kimi.py
CHANGED
|
@@ -213,6 +213,7 @@ class YuanbaoPlatform(BasePlatform):
|
|
|
213
213
|
uploaded_files.append(file_metadata)
|
|
214
214
|
spinner.text = f"文件 {file_name} 上传成功"
|
|
215
215
|
spinner.ok("✅")
|
|
216
|
+
time.sleep(3) # 上传成功后等待3秒
|
|
216
217
|
|
|
217
218
|
except Exception as e:
|
|
218
219
|
spinner.text = f"上传文件 {file_path} 时出错: {str(e)}"
|
|
@@ -533,3 +534,7 @@ class YuanbaoPlatform(BasePlatform):
|
|
|
533
534
|
def name(self) -> str:
|
|
534
535
|
"""模型名称"""
|
|
535
536
|
return self.model_name
|
|
537
|
+
|
|
538
|
+
def support_web(self) -> bool:
|
|
539
|
+
"""Yuanbao平台支持web功能"""
|
|
540
|
+
return True
|
|
@@ -17,6 +17,8 @@ def execute_command(command: str, should_run: bool) -> None:
|
|
|
17
17
|
os.system(command)
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
|
|
21
|
+
|
|
20
22
|
def install_fish_completion() -> int:
|
|
21
23
|
"""Install fish shell command completion with interactive choice
|
|
22
24
|
|
|
@@ -110,7 +112,7 @@ def process_request(request: str) -> Optional[str]:
|
|
|
110
112
|
except Exception:
|
|
111
113
|
return None
|
|
112
114
|
|
|
113
|
-
def main():
|
|
115
|
+
def main() -> int:
|
|
114
116
|
# 创建参数解析器
|
|
115
117
|
init_env()
|
|
116
118
|
parser = argparse.ArgumentParser(
|
|
@@ -135,6 +137,12 @@ Example:
|
|
|
135
137
|
|
|
136
138
|
# install子命令
|
|
137
139
|
install_parser = subparsers.add_parser('install', help='安装fish shell的命令补全功能')
|
|
140
|
+
install_parser.add_argument(
|
|
141
|
+
"--shell",
|
|
142
|
+
choices=["fish"],
|
|
143
|
+
default="fish",
|
|
144
|
+
help="指定shell类型(仅支持fish)"
|
|
145
|
+
)
|
|
138
146
|
|
|
139
147
|
|
|
140
148
|
# 解析参数
|
|
@@ -144,9 +152,11 @@ Example:
|
|
|
144
152
|
|
|
145
153
|
# 处理install命令
|
|
146
154
|
if args.command == "install":
|
|
155
|
+
if args.shell != "fish":
|
|
156
|
+
print(f"错误: 不支持的shell类型: {args.shell}, 仅支持fish")
|
|
157
|
+
return 1
|
|
147
158
|
return install_fish_completion()
|
|
148
159
|
|
|
149
|
-
|
|
150
160
|
# 处理request命令
|
|
151
161
|
if not args.request:
|
|
152
162
|
# 检查是否在交互式终端中运行
|
jarvis/jarvis_tools/edit_file.py
CHANGED
|
@@ -141,7 +141,7 @@ class FileSearchReplaceTool:
|
|
|
141
141
|
content = f.read()
|
|
142
142
|
original_content = content
|
|
143
143
|
|
|
144
|
-
success, temp_content =
|
|
144
|
+
success, temp_content = patch_apply(file_path, yaml.safe_dump(changes))
|
|
145
145
|
|
|
146
146
|
# 只有当所有替换操作都成功时,才写回文件
|
|
147
147
|
if success and (temp_content != original_content or not file_exists):
|
|
@@ -208,7 +208,7 @@ class FileSearchReplaceTool:
|
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
|
|
211
|
-
def
|
|
211
|
+
def patch_apply(filepath: str, patch_content: str) -> Tuple[bool, str]:
|
|
212
212
|
"""执行精确的文件编辑操作,使用AI模型生成差异补丁并应用。
|
|
213
213
|
|
|
214
214
|
功能概述:
|
|
@@ -35,10 +35,14 @@ class FindMethodologyTool:
|
|
|
35
35
|
"stdout": "",
|
|
36
36
|
"stderr": "参数中必须包含查询文本"
|
|
37
37
|
}
|
|
38
|
+
|
|
39
|
+
agent = args.get("agent", None)
|
|
40
|
+
|
|
41
|
+
tool_registry = agent.get_tool_registry() if agent else None
|
|
38
42
|
|
|
39
43
|
with yaspin(text="搜索相关方法论...", color="cyan") as spinner:
|
|
40
44
|
with spinner.hidden():
|
|
41
|
-
methodology_prompt = load_methodology(args["query"])
|
|
45
|
+
methodology_prompt = load_methodology(args["query"], tool_registry)
|
|
42
46
|
|
|
43
47
|
if methodology_prompt:
|
|
44
48
|
spinner.text = "找到相关方法论"
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import re
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Dict, Any, Tuple
|
|
5
|
+
|
|
6
|
+
from jarvis.jarvis_utils.config import get_data_dir
|
|
7
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
8
|
+
|
|
9
|
+
class generate_new_tool:
|
|
10
|
+
name = "generate_new_tool"
|
|
11
|
+
description = """
|
|
12
|
+
生成并注册新的Jarvis工具。该工具会在用户数据目录下创建新的工具文件,
|
|
13
|
+
并自动注册到当前的工具注册表中。适用场景:1. 需要创建新的自定义工具;
|
|
14
|
+
2. 扩展Jarvis功能;3. 自动化重复性操作;4. 封装特定领域的功能。
|
|
15
|
+
|
|
16
|
+
使用示例:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
# 创建一个将文本转换为大写/小写的工具
|
|
20
|
+
name: generate_new_tool
|
|
21
|
+
arguments:
|
|
22
|
+
tool_name: text_transformer
|
|
23
|
+
tool_code: |
|
|
24
|
+
# -*- coding: utf-8 -*-
|
|
25
|
+
from typing import Dict, Any
|
|
26
|
+
|
|
27
|
+
class text_transformer:
|
|
28
|
+
name = "text_transformer"
|
|
29
|
+
description = \"\"\"
|
|
30
|
+
文本转换工具,可以将输入的文本转换为大写、小写或首字母大写格式。
|
|
31
|
+
适用场景:1. 格式化文本; 2. 处理标题; 3. 标准化输出
|
|
32
|
+
\"\"\"
|
|
33
|
+
|
|
34
|
+
parameters = {
|
|
35
|
+
"type": "object",
|
|
36
|
+
"properties": {
|
|
37
|
+
"text": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"description": "需要转换格式的文本"
|
|
40
|
+
},
|
|
41
|
+
"transform_type": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"description": "转换类型,可选值为 upper(大写)、lower(小写)或 title(首字母大写)",
|
|
44
|
+
"enum": ["upper", "lower", "title"]
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"required": ["text", "transform_type"]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def check() -> bool:
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
55
|
+
try:
|
|
56
|
+
text = args["text"]
|
|
57
|
+
transform_type = args["transform_type"]
|
|
58
|
+
|
|
59
|
+
if transform_type == "upper":
|
|
60
|
+
result = text.upper()
|
|
61
|
+
elif transform_type == "lower":
|
|
62
|
+
result = text.lower()
|
|
63
|
+
elif transform_type == "title":
|
|
64
|
+
result = text.title()
|
|
65
|
+
else:
|
|
66
|
+
return {
|
|
67
|
+
"success": False,
|
|
68
|
+
"stdout": "",
|
|
69
|
+
"stderr": f"不支持的转换类型: {transform_type}"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
"success": True,
|
|
74
|
+
"stdout": result,
|
|
75
|
+
"stderr": ""
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
except Exception as e:
|
|
79
|
+
return {
|
|
80
|
+
"success": False,
|
|
81
|
+
"stdout": "",
|
|
82
|
+
"stderr": f"转换失败: {str(e)}"
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
创建完成后可以立即使用:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
name: text_transformer
|
|
90
|
+
arguments:
|
|
91
|
+
text: hello world
|
|
92
|
+
transform_type: upper
|
|
93
|
+
```
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
parameters = {
|
|
97
|
+
"type": "object",
|
|
98
|
+
"properties": {
|
|
99
|
+
"tool_name": {
|
|
100
|
+
"type": "string",
|
|
101
|
+
"description": "新工具的名称,将用作文件名和工具类名"
|
|
102
|
+
},
|
|
103
|
+
"tool_code": {
|
|
104
|
+
"type": "string",
|
|
105
|
+
"description": "工具的完整Python代码,包含类定义、名称、描述、参数和execute方法"
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"required": ["tool_name", "tool_code"]
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@staticmethod
|
|
112
|
+
def check() -> bool:
|
|
113
|
+
"""检查工具是否可用"""
|
|
114
|
+
# 检查数据目录是否存在
|
|
115
|
+
data_dir = get_data_dir()
|
|
116
|
+
tools_dir = Path(data_dir) / "tools"
|
|
117
|
+
|
|
118
|
+
# 如果tools目录不存在,尝试创建
|
|
119
|
+
if not tools_dir.exists():
|
|
120
|
+
try:
|
|
121
|
+
tools_dir.mkdir(parents=True, exist_ok=True)
|
|
122
|
+
return True
|
|
123
|
+
except Exception as e:
|
|
124
|
+
PrettyOutput.print(f"无法创建工具目录 {tools_dir}: {e}", OutputType.ERROR)
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
return True
|
|
128
|
+
|
|
129
|
+
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
130
|
+
"""
|
|
131
|
+
生成新工具并注册到当前的工具注册表中
|
|
132
|
+
|
|
133
|
+
参数:
|
|
134
|
+
args: 包含工具名称和工具代码的字典
|
|
135
|
+
|
|
136
|
+
返回:
|
|
137
|
+
Dict: 包含生成结果的字典
|
|
138
|
+
"""
|
|
139
|
+
try:
|
|
140
|
+
# 从参数中获取工具信息
|
|
141
|
+
tool_name = args["tool_name"]
|
|
142
|
+
tool_code = args["tool_code"]
|
|
143
|
+
agent = args.get("agent", None)
|
|
144
|
+
|
|
145
|
+
# 验证工具名称
|
|
146
|
+
if not tool_name.isidentifier():
|
|
147
|
+
return {
|
|
148
|
+
"success": False,
|
|
149
|
+
"stdout": "",
|
|
150
|
+
"stderr": f"工具名称 '{tool_name}' 不是有效的Python标识符"
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# 准备工具目录
|
|
154
|
+
tools_dir = Path(get_data_dir()) / "tools"
|
|
155
|
+
tools_dir.mkdir(parents=True, exist_ok=True)
|
|
156
|
+
|
|
157
|
+
# 生成工具文件路径
|
|
158
|
+
tool_file_path = tools_dir / f"{tool_name}.py"
|
|
159
|
+
|
|
160
|
+
# 检查是否已存在同名工具
|
|
161
|
+
if tool_file_path.exists():
|
|
162
|
+
return {
|
|
163
|
+
"success": False,
|
|
164
|
+
"stdout": "",
|
|
165
|
+
"stderr": f"工具 '{tool_name}' 已经存在于 {tool_file_path}"
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
# 验证并处理工具代码
|
|
169
|
+
processed_code, error_msg = self._validate_and_process_code(tool_name, tool_code)
|
|
170
|
+
if error_msg:
|
|
171
|
+
return {
|
|
172
|
+
"success": False,
|
|
173
|
+
"stdout": "",
|
|
174
|
+
"stderr": error_msg
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
# 写入工具文件
|
|
178
|
+
with open(tool_file_path, "w", encoding="utf-8") as f:
|
|
179
|
+
f.write(processed_code)
|
|
180
|
+
|
|
181
|
+
# 注册新工具到当前的工具注册表
|
|
182
|
+
success_message = f"工具 '{tool_name}' 已成功生成在 {tool_file_path}"
|
|
183
|
+
|
|
184
|
+
if agent:
|
|
185
|
+
tool_registry = agent.get_tool_registry()
|
|
186
|
+
if tool_registry:
|
|
187
|
+
# 尝试加载并注册新工具
|
|
188
|
+
if tool_registry.register_tool_by_file(str(tool_file_path)):
|
|
189
|
+
success_message += f"\n已成功注册到当前会话的工具注册表中"
|
|
190
|
+
else:
|
|
191
|
+
success_message += f"\n注册到当前会话失败,可能需要重新启动Jarvis"
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
"success": True,
|
|
195
|
+
"stdout": success_message,
|
|
196
|
+
"stderr": ""
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
except Exception as e:
|
|
200
|
+
# 如果发生异常,返回失败响应,包含错误信息
|
|
201
|
+
return {
|
|
202
|
+
"success": False,
|
|
203
|
+
"stdout": "",
|
|
204
|
+
"stderr": f"生成工具失败: {str(e)}"
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
def _validate_and_process_code(self, tool_name: str, tool_code: str) -> Tuple[str, str]:
|
|
208
|
+
"""
|
|
209
|
+
验证并处理工具代码
|
|
210
|
+
|
|
211
|
+
参数:
|
|
212
|
+
tool_name: 工具名称
|
|
213
|
+
tool_code: 工具代码
|
|
214
|
+
|
|
215
|
+
返回:
|
|
216
|
+
Tuple[str, str]: (处理后的代码, 错误信息)
|
|
217
|
+
"""
|
|
218
|
+
# 检查工具代码中是否包含类定义
|
|
219
|
+
if f"class {tool_name}" not in tool_code:
|
|
220
|
+
# 尝试找到任何类定义
|
|
221
|
+
class_match = re.search(r"class\s+(\w+)", tool_code)
|
|
222
|
+
if class_match:
|
|
223
|
+
old_class_name = class_match.group(1)
|
|
224
|
+
# 替换类名为工具名
|
|
225
|
+
tool_code = tool_code.replace(f"class {old_class_name}", f"class {tool_name}")
|
|
226
|
+
tool_code = tool_code.replace(f'name = "{old_class_name}"', f'name = "{tool_name}"')
|
|
227
|
+
else:
|
|
228
|
+
# 没有找到类定义,返回错误
|
|
229
|
+
return "", f"工具代码中缺少类定义 'class {tool_name}'"
|
|
230
|
+
|
|
231
|
+
# 检查工具代码中是否包含必要的属性和方法
|
|
232
|
+
missing_components = []
|
|
233
|
+
|
|
234
|
+
if f'name = "{tool_name}"' not in tool_code and f"name = '{tool_name}'" not in tool_code:
|
|
235
|
+
# 尝试查找任何name属性并修复
|
|
236
|
+
name_match = re.search(r'name\s*=\s*["\'](\w+)["\']', tool_code)
|
|
237
|
+
if name_match:
|
|
238
|
+
old_name = name_match.group(1)
|
|
239
|
+
tool_code = re.sub(r'name\s*=\s*["\'](\w+)["\']', f'name = "{tool_name}"', tool_code)
|
|
240
|
+
else:
|
|
241
|
+
missing_components.append(f"name = \"{tool_name}\"")
|
|
242
|
+
|
|
243
|
+
if "description = " not in tool_code:
|
|
244
|
+
missing_components.append("description 属性")
|
|
245
|
+
|
|
246
|
+
if "parameters = " not in tool_code:
|
|
247
|
+
missing_components.append("parameters 属性")
|
|
248
|
+
|
|
249
|
+
if "def execute(self, args:" not in tool_code:
|
|
250
|
+
missing_components.append("execute 方法")
|
|
251
|
+
|
|
252
|
+
if "def check(" not in tool_code:
|
|
253
|
+
# 添加默认的check方法
|
|
254
|
+
class_match = re.search(r"class\s+(\w+).*?:", tool_code, re.DOTALL)
|
|
255
|
+
if class_match:
|
|
256
|
+
indent = " " # 默认缩进
|
|
257
|
+
# 找到类定义后的第一个属性
|
|
258
|
+
first_attr_match = re.search(r"class\s+(\w+).*?:(.*?)(\w+\s*=)", tool_code, re.DOTALL)
|
|
259
|
+
if first_attr_match:
|
|
260
|
+
# 获取属性前的缩进
|
|
261
|
+
attr_indent = re.search(r"\n([ \t]*)\w+\s*=", first_attr_match.group(2))
|
|
262
|
+
if attr_indent:
|
|
263
|
+
indent = attr_indent.group(1)
|
|
264
|
+
|
|
265
|
+
check_method = f"\n{indent}@staticmethod\n{indent}def check() -> bool:\n{indent} \"\"\"检查工具是否可用\"\"\"\n{indent} return True\n"
|
|
266
|
+
|
|
267
|
+
# 在类定义后插入check方法
|
|
268
|
+
pattern = r"(class\s+(\w+).*?:.*?)(\n\s*\w+\s*=|\n\s*@|\n\s*def)"
|
|
269
|
+
replacement = r"\1" + check_method + r"\3"
|
|
270
|
+
tool_code = re.sub(pattern, replacement, tool_code, 1, re.DOTALL)
|
|
271
|
+
|
|
272
|
+
# 如果缺少必要组件,返回错误信息
|
|
273
|
+
if missing_components:
|
|
274
|
+
return "", f"工具代码中缺少以下必要组件: {', '.join(missing_components)}"
|
|
275
|
+
|
|
276
|
+
# 确保代码有正确的Python文件头部
|
|
277
|
+
if not tool_code.startswith("# -*- coding:") and not tool_code.startswith("# coding="):
|
|
278
|
+
tool_code = "# -*- coding: utf-8 -*-\n" + tool_code
|
|
279
|
+
|
|
280
|
+
# 确保导入了必要的模块
|
|
281
|
+
if "from typing import Dict, Any" not in tool_code:
|
|
282
|
+
imports_pos = tool_code.find("\n\n")
|
|
283
|
+
if imports_pos > 0:
|
|
284
|
+
tool_code = tool_code[:imports_pos] + "\nfrom typing import Dict, Any" + tool_code[imports_pos:]
|
|
285
|
+
else:
|
|
286
|
+
tool_code = "from typing import Dict, Any\n\n" + tool_code
|
|
287
|
+
|
|
288
|
+
return tool_code, ""
|
|
@@ -23,4 +23,9 @@ class SearchWebTool:
|
|
|
23
23
|
"stdout": model.chat_until_success(query), # type: ignore
|
|
24
24
|
"stderr": "",
|
|
25
25
|
"success": True,
|
|
26
|
-
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def check() -> bool:
|
|
30
|
+
"""检查当前平台是否支持web功能"""
|
|
31
|
+
return PlatformRegistry().get_normal_platform().support_web()
|
jarvis/jarvis_utils/config.py
CHANGED
|
@@ -143,3 +143,12 @@ def get_data_dir() -> str:
|
|
|
143
143
|
if not data_path:
|
|
144
144
|
return os.path.expanduser('~/.jarvis')
|
|
145
145
|
return data_path
|
|
146
|
+
|
|
147
|
+
def get_auto_update() -> bool:
|
|
148
|
+
"""
|
|
149
|
+
获取是否自动更新git仓库。
|
|
150
|
+
|
|
151
|
+
返回:
|
|
152
|
+
bool: 如果需要自动更新则返回True,默认为True
|
|
153
|
+
"""
|
|
154
|
+
return os.getenv('JARVIS_AUTO_UPDATE', 'true') == 'true'
|
jarvis/jarvis_utils/git_utils.py
CHANGED
|
@@ -13,7 +13,7 @@ import os
|
|
|
13
13
|
import re
|
|
14
14
|
import subprocess
|
|
15
15
|
from typing import List, Tuple, Dict
|
|
16
|
-
from jarvis.jarvis_utils.config import is_confirm_before_apply_patch
|
|
16
|
+
from jarvis.jarvis_utils.config import get_auto_update, is_confirm_before_apply_patch
|
|
17
17
|
from jarvis.jarvis_utils.output import PrettyOutput, OutputType
|
|
18
18
|
from jarvis.jarvis_utils.utils import user_confirm
|
|
19
19
|
def find_git_root(start_dir: str = ".") -> str:
|
|
@@ -294,3 +294,42 @@ def is_file_in_git_repo(filepath: str) -> bool:
|
|
|
294
294
|
return os.path.abspath(filepath).startswith(os.path.abspath(repo_root))
|
|
295
295
|
except:
|
|
296
296
|
return False
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def check_and_update_git_repo(repo_path: str) -> bool:
|
|
300
|
+
"""检查并更新git仓库
|
|
301
|
+
|
|
302
|
+
参数:
|
|
303
|
+
repo_path: 仓库路径
|
|
304
|
+
|
|
305
|
+
返回:
|
|
306
|
+
bool: 是否执行了更新
|
|
307
|
+
"""
|
|
308
|
+
curr_dir = os.path.abspath(os.getcwd())
|
|
309
|
+
git_root = find_git_root(repo_path)
|
|
310
|
+
if git_root is None:
|
|
311
|
+
return False
|
|
312
|
+
|
|
313
|
+
try:
|
|
314
|
+
if not get_auto_update():
|
|
315
|
+
return False
|
|
316
|
+
# 检查是否有未提交的修改
|
|
317
|
+
if has_uncommitted_changes():
|
|
318
|
+
return False
|
|
319
|
+
|
|
320
|
+
# 获取远程更新
|
|
321
|
+
subprocess.run(["git", "fetch"], cwd=git_root, check=True)
|
|
322
|
+
# 检查本地是否落后
|
|
323
|
+
result = subprocess.run(["git", "rev-list", "--count", "HEAD..origin/main"],
|
|
324
|
+
cwd=git_root, capture_output=True, text=True)
|
|
325
|
+
if result.returncode == 0 and int(result.stdout.strip()) > 0:
|
|
326
|
+
PrettyOutput.print("检测到新版本,正在更新Jarvis...", OutputType.INFO)
|
|
327
|
+
subprocess.run(["git", "pull"], cwd=git_root, check=True)
|
|
328
|
+
PrettyOutput.print("Jarvis已更新到最新版本", OutputType.SUCCESS)
|
|
329
|
+
return True
|
|
330
|
+
return False
|
|
331
|
+
except Exception as e:
|
|
332
|
+
PrettyOutput.print(f"Git仓库更新检查失败: {e}", OutputType.WARNING)
|
|
333
|
+
return False
|
|
334
|
+
finally:
|
|
335
|
+
os.chdir(curr_dir)
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import os
|
|
11
11
|
import json
|
|
12
12
|
import tempfile
|
|
13
|
-
from typing import Dict, Optional
|
|
13
|
+
from typing import Any, Dict, Optional
|
|
14
14
|
|
|
15
15
|
from jarvis.jarvis_utils.config import get_data_dir
|
|
16
16
|
from jarvis.jarvis_utils.output import PrettyOutput, OutputType
|
|
@@ -92,7 +92,7 @@ def _create_methodology_temp_file(methodologies: Dict[str, str]) -> Optional[str
|
|
|
92
92
|
PrettyOutput.print(f"创建方法论临时文件失败: {str(e)}", OutputType.ERROR)
|
|
93
93
|
return None
|
|
94
94
|
|
|
95
|
-
def load_methodology(user_input: str) -> str:
|
|
95
|
+
def load_methodology(user_input: str, tool_registery: Optional[Any] = None) -> str:
|
|
96
96
|
"""
|
|
97
97
|
加载方法论并上传到大模型。
|
|
98
98
|
|
|
@@ -103,6 +103,9 @@ def load_methodology(user_input: str) -> str:
|
|
|
103
103
|
str: 相关的方法论提示,如果未找到方法论则返回空字符串
|
|
104
104
|
"""
|
|
105
105
|
from yaspin import yaspin # type: ignore
|
|
106
|
+
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
107
|
+
|
|
108
|
+
prompt = tool_registery.prompt() if tool_registery else ""
|
|
106
109
|
|
|
107
110
|
# 获取方法论目录
|
|
108
111
|
methodology_dir = _get_methodology_directory()
|
|
@@ -133,10 +136,13 @@ def load_methodology(user_input: str) -> str:
|
|
|
133
136
|
full_content = base_prompt
|
|
134
137
|
for problem_type, content in methodologies.items():
|
|
135
138
|
full_content += f"## {problem_type}\n\n{content}\n\n---\n\n"
|
|
139
|
+
|
|
140
|
+
full_content += f"以下是所有可用的工具内容:\n\n"
|
|
141
|
+
full_content += prompt
|
|
136
142
|
|
|
137
143
|
# 添加用户输入和输出要求
|
|
138
144
|
full_content += f"""
|
|
139
|
-
|
|
145
|
+
请根据以上方法论和可调用的工具内容,规划/总结出以下用户需求的执行步骤: {user_input}
|
|
140
146
|
|
|
141
147
|
请按以下格式回复:
|
|
142
148
|
### 与该任务/需求相关的方法论
|
|
@@ -175,7 +181,7 @@ def load_methodology(user_input: str) -> str:
|
|
|
175
181
|
if upload_success:
|
|
176
182
|
# 使用上传的文件生成摘要
|
|
177
183
|
return platform.chat_until_success(base_prompt + f"""
|
|
178
|
-
|
|
184
|
+
请根据已上传的方法论和可调用的工具文件内容,规划/总结出以下用户需求的执行步骤: {user_input}
|
|
179
185
|
|
|
180
186
|
请按以下格式回复:
|
|
181
187
|
### 与该任务/需求相关的方法论
|
|
@@ -192,7 +198,7 @@ def load_methodology(user_input: str) -> str:
|
|
|
192
198
|
elif hasattr(platform, 'chat_big_content'):
|
|
193
199
|
# 如果上传失败但支持大内容处理,使用chat_big_content
|
|
194
200
|
return platform.chat_big_content(full_content, base_prompt + f"""
|
|
195
|
-
|
|
201
|
+
请根据以上方法论和可调用的工具文件内容,规划/总结出以下用户需求的执行步骤: {user_input}
|
|
196
202
|
|
|
197
203
|
请按以下格式回复:
|
|
198
204
|
### 与该任务/需求相关的方法论
|
jarvis/jarvis_utils/utils.py
CHANGED
|
@@ -17,6 +17,7 @@ def init_env() -> None:
|
|
|
17
17
|
1. 创建不存在的jarvis_data目录
|
|
18
18
|
2. 加载环境变量到os.environ
|
|
19
19
|
3. 处理文件读取异常
|
|
20
|
+
4. 检查git仓库状态并在落后时更新
|
|
20
21
|
"""
|
|
21
22
|
jarvis_dir = Path(get_data_dir())
|
|
22
23
|
env_file = jarvis_dir / "env"
|
|
@@ -52,6 +53,13 @@ def init_env() -> None:
|
|
|
52
53
|
continue
|
|
53
54
|
except Exception as e:
|
|
54
55
|
PrettyOutput.print(f"警告: 读取 {env_file} 失败: {e}", OutputType.WARNING)
|
|
56
|
+
|
|
57
|
+
# 检查是否是git仓库并更新
|
|
58
|
+
from jarvis.jarvis_utils.git_utils import check_and_update_git_repo
|
|
59
|
+
|
|
60
|
+
check_and_update_git_repo(str(script_dir))
|
|
61
|
+
|
|
62
|
+
|
|
55
63
|
def while_success(func: Callable[[], Any], sleep_time: float = 0.1) -> Any:
|
|
56
64
|
"""循环执行函数直到成功
|
|
57
65
|
|
|
@@ -66,7 +74,7 @@ def while_success(func: Callable[[], Any], sleep_time: float = 0.1) -> Any:
|
|
|
66
74
|
try:
|
|
67
75
|
return func()
|
|
68
76
|
except Exception as e:
|
|
69
|
-
PrettyOutput.print(f"执行失败: {str(e)}, 等待 {sleep_time}s...", OutputType.
|
|
77
|
+
PrettyOutput.print(f"执行失败: {str(e)}, 等待 {sleep_time}s...", OutputType.WARNING)
|
|
70
78
|
time.sleep(sleep_time)
|
|
71
79
|
continue
|
|
72
80
|
def while_true(func: Callable[[], bool], sleep_time: float = 0.1) -> Any:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: jarvis-ai-assistant
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.167
|
|
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
|
|
@@ -65,7 +65,6 @@ Requires-Dist: build; extra == "dev"
|
|
|
65
65
|
Requires-Dist: twine; extra == "dev"
|
|
66
66
|
Dynamic: author
|
|
67
67
|
Dynamic: home-page
|
|
68
|
-
Dynamic: license-file
|
|
69
68
|
Dynamic: requires-python
|
|
70
69
|
|
|
71
70
|
# 🤖 Jarvis AI 助手
|
|
@@ -106,11 +105,19 @@ Dynamic: requires-python
|
|
|
106
105
|
|
|
107
106
|
|
|
108
107
|
## 🚀 快速开始 <a id="quick-start"></a>
|
|
108
|
+
### 系统要求
|
|
109
|
+
- 目前只能在Linux系统下使用(很多工具依赖Linux系统)
|
|
110
|
+
- Windows没有测试过,但Windows 10以上的用户可以在WSL上使用此工具
|
|
111
|
+
|
|
109
112
|
### 安装
|
|
110
113
|
```bash
|
|
114
|
+
# 从源码安装(推荐)
|
|
111
115
|
git clone https://github.com/skyfireitdiy/Jarvis
|
|
112
116
|
cd Jarvis
|
|
113
117
|
pip3 install -e .
|
|
118
|
+
|
|
119
|
+
# 或者从PyPI安装(可能更新不及时)
|
|
120
|
+
pip3 install jarvis-ai-assistant
|
|
114
121
|
```
|
|
115
122
|
|
|
116
123
|
### 最小化配置
|
|
@@ -149,55 +156,43 @@ Kimi API Key获取方式:
|
|
|
149
156
|
|
|
150
157
|
删除Bearer前缀,剩下的内容就是Kimi API Key。
|
|
151
158
|
|
|
152
|
-
以上配置编写到`~/.jarvis/env`文件中。
|
|
153
159
|
|
|
154
|
-
|
|
160
|
+
#### OpenAI
|
|
155
161
|
```bash
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
jarvis-code-agent
|
|
161
|
-
# 或者 jca
|
|
162
|
-
|
|
163
|
-
# 使用智能shell的功能
|
|
164
|
-
jarvis-smart-shell --help
|
|
165
|
-
# 或者 jss
|
|
166
|
-
|
|
167
|
-
# 使用平台管理的功能
|
|
168
|
-
jarvis-platform-manager --help
|
|
169
|
-
|
|
170
|
-
# 使用代码审查的功能
|
|
171
|
-
jarvis-code-review --help
|
|
172
|
-
|
|
173
|
-
# 使用自动化git commit的功能
|
|
174
|
-
jarvis-git-commit --help
|
|
175
|
-
# 或者 jgc
|
|
162
|
+
JARVIS_PLATFORM=openai
|
|
163
|
+
JARVIS_MODEL=gpt-4o # 默认模型,可选gpt-4-turbo, gpt-3.5-turbo等
|
|
164
|
+
JARVIS_THINKING_PLATFORM=openai
|
|
165
|
+
JARVIS_THINKING_MODEL=gpt-4o
|
|
176
166
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
# 使用git squash的功能
|
|
181
|
-
jarvis-git-squash --help
|
|
182
|
-
|
|
183
|
-
# 使用多代理的功能
|
|
184
|
-
jarvis-multi-agent --help
|
|
185
|
-
|
|
186
|
-
# 使用agent的功能
|
|
187
|
-
jarvis-agent --help
|
|
167
|
+
OPENAI_API_KEY=<OpenAI API Key>
|
|
168
|
+
OPENAI_API_BASE=https://api.openai.com/v1 # 可选,默认为官方API地址
|
|
169
|
+
```
|
|
188
170
|
|
|
189
|
-
|
|
190
|
-
|
|
171
|
+
配置说明:
|
|
172
|
+
1. `OPENAI_API_KEY`: 必填。
|
|
173
|
+
2. `OPENAI_API_BASE`: 可选,用于自定义API端点
|
|
191
174
|
|
|
192
|
-
|
|
193
|
-
jarvis-ask-codebase --help
|
|
175
|
+
以上配置编写到`~/.jarvis/env`文件中。
|
|
194
176
|
|
|
195
|
-
|
|
196
|
-
jarvis-git-details --help
|
|
177
|
+
支持的模型可通过`jarvis-platform-manager --list-models`查看完整列表。
|
|
197
178
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
179
|
+
### 基本使用
|
|
180
|
+
| 命令 | 快捷方式 | 功能描述 |
|
|
181
|
+
|------|----------|----------|
|
|
182
|
+
| `jarvis` | - | 使用通用代理 |
|
|
183
|
+
| `jarvis-code-agent` | `jca` | 使用代码代理 |
|
|
184
|
+
| `jarvis-smart-shell` | `jss` | 使用智能shell功能 |
|
|
185
|
+
| `jarvis-platform-manager` | - | 使用平台管理功能 |
|
|
186
|
+
| `jarvis-code-review` | - | 使用代码审查功能 |
|
|
187
|
+
| `jarvis-git-commit` | `jgc` | 使用自动化git commit功能 |
|
|
188
|
+
| `jarvis-dev` | - | 使用dev功能(开发中) |
|
|
189
|
+
| `jarvis-git-squash` | - | 使用git squash功能 |
|
|
190
|
+
| `jarvis-multi-agent` | - | 使用多代理功能 |
|
|
191
|
+
| `jarvis-agent` | - | 使用agent功能 |
|
|
192
|
+
| `jarvis-tool` | - | 使用工具功能 |
|
|
193
|
+
| `jarvis-ask-codebase` | `jac` | 使用代码库查询功能 |
|
|
194
|
+
| `jarvis-git-details` | - | 使用git details功能 |
|
|
195
|
+
| `jarvis-methodology` | - | 使用方法论功能 |
|
|
201
196
|
|
|
202
197
|
---
|
|
203
198
|
|
|
@@ -216,6 +211,9 @@ jarvis-methodology --help
|
|
|
216
211
|
| `JARVIS_EXECUTE_TOOL_CONFIRM` | false | 执行工具前是否需要确认 |
|
|
217
212
|
| `JARVIS_CONFIRM_BEFORE_APPLY_PATCH` | true | 应用补丁前是否需要确认 |
|
|
218
213
|
| `JARVIS_MAX_TOOL_CALL_COUNT` | 20 | 最大连续工具调用次数 |
|
|
214
|
+
| `JARVIS_AUTO_UPDATE` | true | 是否自动更新Jarvis(仅在以git仓库方式安装时有效) |
|
|
215
|
+
|
|
216
|
+
所有配置编写到`~/.jarvis/env`文件中即可生效。
|
|
219
217
|
|
|
220
218
|
|
|
221
219
|
---
|
|
@@ -412,3 +410,5 @@ class CustomPlatform(BasePlatform):
|
|
|
412
410
|
<div align="center">
|
|
413
411
|
由 Jarvis 团队用 ❤️ 制作
|
|
414
412
|
</div>
|
|
413
|
+
|
|
414
|
+

|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
jarvis/__init__.py,sha256=
|
|
2
|
-
jarvis/jarvis_agent/__init__.py,sha256=
|
|
1
|
+
jarvis/__init__.py,sha256=VoCqUtOk8zQixRfRJnQKEG0d7yF3JDV_1pUvP0_XpdY,74
|
|
2
|
+
jarvis/jarvis_agent/__init__.py,sha256=uCyjBD1ib9ewmJ1RJVy6QrVWOxy_4oNVFjvggqMRYg0,25712
|
|
3
3
|
jarvis/jarvis_agent/builtin_input_handler.py,sha256=KhvlV_QdB3P-M0TCkWvdxidNie1jU7KoMOqTIXCpwwA,1529
|
|
4
4
|
jarvis/jarvis_agent/file_input_handler.py,sha256=EwaitWczbwLCKNpWU9C7m829_G5uLZ_hNcVXlX2ANes,3437
|
|
5
5
|
jarvis/jarvis_agent/jarvis.py,sha256=rn0rLMGuVDyUa0_xdAmPV3M4yhIvE9ldSwD5DaJKo-8,5819
|
|
@@ -8,7 +8,7 @@ jarvis/jarvis_agent/output_handler.py,sha256=7qori-RGrQmdiFepoEe3oPPKJIvRt90l_JD
|
|
|
8
8
|
jarvis/jarvis_agent/shell_input_handler.py,sha256=pi3AtPKrkKc6K9e99S1djKXQ_XrxtP6FrSWebQmRT6E,1261
|
|
9
9
|
jarvis/jarvis_code_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
10
|
jarvis/jarvis_code_agent/code_agent.py,sha256=awCuRODHek434ixuG62PeAtFjQ7fQheJERJDpT4Bjuk,16859
|
|
11
|
-
jarvis/jarvis_code_analysis/code_review.py,sha256=
|
|
11
|
+
jarvis/jarvis_code_analysis/code_review.py,sha256=hVuKUfOKpgAyXG7pNKJG5SOcuGri8UcBza_W4fk34wY,30265
|
|
12
12
|
jarvis/jarvis_code_analysis/checklists/__init__.py,sha256=cKQ_FOGy5TQgM-YkRCqORo-mUOZaPAJ9VDmZoFX58us,78
|
|
13
13
|
jarvis/jarvis_code_analysis/checklists/c_cpp.py,sha256=SXPpYCNeCtU1PpKdKPiYDuOybfY9vaL0ejDn4imxDwA,1317
|
|
14
14
|
jarvis/jarvis_code_analysis/checklists/csharp.py,sha256=vS-cu6RCGg5SyK9MJ3RE381gt3xYl-yea3Bj2UQEcwQ,2420
|
|
@@ -32,7 +32,7 @@ jarvis/jarvis_code_analysis/checklists/web.py,sha256=-Pnj1FQTsGVZUQK7-4ptDsGd7a2
|
|
|
32
32
|
jarvis/jarvis_data/huggingface.tar.gz,sha256=dWKnc_tvyx-I_ZkXo91O0b38KxDmLW1ZbmJ3E6fCl_k,1120205
|
|
33
33
|
jarvis/jarvis_dev/main.py,sha256=zfL9rl-Jfhpi4E4OxMKw3eOVjy6kSzQdxhn3yGv1UTw,42952
|
|
34
34
|
jarvis/jarvis_git_details/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
|
-
jarvis/jarvis_git_details/main.py,sha256=
|
|
35
|
+
jarvis/jarvis_git_details/main.py,sha256=l4Ol96DFISq2ctAgzmfUuS4i6JdWh0JAu_isY3iuzVo,8919
|
|
36
36
|
jarvis/jarvis_git_squash/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
37
|
jarvis/jarvis_git_squash/main.py,sha256=uZf05Y7UN8kwlrfsSeX2NEGCaowwzZsm9LqjgmQxeic,2203
|
|
38
38
|
jarvis/jarvis_git_utils/git_commiter.py,sha256=0ABGCpZUcJIDFiCi0naQeJ1oySeQ2MDmyMzI96oXD58,13006
|
|
@@ -49,16 +49,16 @@ jarvis/jarvis_methodology/main.py,sha256=_KDGEQw6j_VZ9O8eDe-c8F84zl6JrKmsNRva9PG
|
|
|
49
49
|
jarvis/jarvis_multi_agent/__init__.py,sha256=LEJofDjh80U34RyZv2ECAzpt2zkhA0Jn3KZh-ABoAKA,4343
|
|
50
50
|
jarvis/jarvis_multi_agent/main.py,sha256=Z6N5VMjzaernnRjPkqgYRv09cIhWIFQ6a__AqHA8xrQ,1567
|
|
51
51
|
jarvis/jarvis_platform/__init__.py,sha256=0YnsUoM4JkIBOtImFdjfuDbrqQZT3dEaAwSJ62DrpCc,104
|
|
52
|
-
jarvis/jarvis_platform/base.py,sha256=
|
|
52
|
+
jarvis/jarvis_platform/base.py,sha256=Y5K0c6c6-74wFW38OWc7Boqtyfmf3sMZkR0S9rDSM8g,4588
|
|
53
53
|
jarvis/jarvis_platform/human.py,sha256=0sbEhST4rKKGGV45dAdJqvVBnRPPeCe6HqxR245S4Z8,2462
|
|
54
|
-
jarvis/jarvis_platform/kimi.py,sha256=
|
|
54
|
+
jarvis/jarvis_platform/kimi.py,sha256=yz7OVT54YDSUVuFlijDB7VdsJmtqRgj-qYKPHy8f0WQ,16787
|
|
55
55
|
jarvis/jarvis_platform/openai.py,sha256=8enxCISjHtCs0qoqEag68v68m_clKr7jgEpUA0CyUBo,4139
|
|
56
56
|
jarvis/jarvis_platform/registry.py,sha256=UjCdPT9WIRxU-F0uuPpKmKRRCcNNxjr-bRTEPgRSNx4,7740
|
|
57
|
-
jarvis/jarvis_platform/yuanbao.py,sha256=
|
|
57
|
+
jarvis/jarvis_platform/yuanbao.py,sha256=gPFy03DA0PemBNQTtxbDCBJ6mqUtSZclfiZbdNq1Ivk,22036
|
|
58
58
|
jarvis/jarvis_platform_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
59
|
jarvis/jarvis_platform_manager/main.py,sha256=gFqXKNweU9mE-IRqV7qhXmZ5483D01s9bI76POX1uXc,22596
|
|
60
60
|
jarvis/jarvis_smart_shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
61
|
-
jarvis/jarvis_smart_shell/main.py,sha256=
|
|
61
|
+
jarvis/jarvis_smart_shell/main.py,sha256=uq5NCdNAdcHqvtG0zpajz85SigSKH1SSAcEpyHa_BOc,5180
|
|
62
62
|
jarvis/jarvis_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
63
63
|
jarvis/jarvis_tools/ask_codebase.py,sha256=kIQwlSrCqtjXyl3YE8m6i188U62sZ78LAOz06Vsm-2k,9659
|
|
64
64
|
jarvis/jarvis_tools/ask_user.py,sha256=cWSLG33b79IbIZEWsSNV5RHvGX6eo3nTM8TUhOMnGh8,2167
|
|
@@ -67,34 +67,35 @@ jarvis/jarvis_tools/chdir.py,sha256=wYVBqWF5kaUkKqH3cUAOKUsACzYsFtCCJJyd8UJsp4o,
|
|
|
67
67
|
jarvis/jarvis_tools/code_plan.py,sha256=EzLdbJnVCkJ7lL8XIQyuDJdxU1i3CFiBpqyNG-GdJw8,7753
|
|
68
68
|
jarvis/jarvis_tools/create_code_agent.py,sha256=cxYkjr4rhI2EWpK78psZSRB9mxiP1IUT0SEfFIqCJzY,3411
|
|
69
69
|
jarvis/jarvis_tools/create_sub_agent.py,sha256=ppTOFRd0ygSJUFr3oQ8IrCLOqbZ7vwnbdadfTDjpDgs,3025
|
|
70
|
-
jarvis/jarvis_tools/edit_file.py,sha256=
|
|
70
|
+
jarvis/jarvis_tools/edit_file.py,sha256=1SllJmc-1gHf8h8xwQylJs84Ac0xowVpg0-X6yabwkM,13827
|
|
71
71
|
jarvis/jarvis_tools/execute_script.py,sha256=cc0NlPwhkZinEexqT63d1ofEkzQddVWGsZOCVL1v_60,5739
|
|
72
72
|
jarvis/jarvis_tools/file_analyzer.py,sha256=tzU1cPKyDa54hVZewP0bDzdsjvdjGQ1BRt5k8N4li3s,4868
|
|
73
73
|
jarvis/jarvis_tools/file_operation.py,sha256=lP8EpsnSdA3FW8ofSAdoA8qPGMAG3UhYd6LFEzOFUVY,9044
|
|
74
|
-
jarvis/jarvis_tools/find_methodology.py,sha256=
|
|
74
|
+
jarvis/jarvis_tools/find_methodology.py,sha256=FwGKSV4fHNkiAnaVUwP8GkqXl8PEqMPZBxAyvTSPFGA,2438
|
|
75
|
+
jarvis/jarvis_tools/generate_new_tool.py,sha256=On4dXSmZZ_rVkNaZ151QE_3OWXXqtjs0lC-Dmxv9jSQ,11572
|
|
75
76
|
jarvis/jarvis_tools/lsp_get_diagnostics.py,sha256=paz1CVZ2Y8nk0U74n1QiG01oDINiZqpVlPc2f4_B150,5346
|
|
76
77
|
jarvis/jarvis_tools/methodology.py,sha256=Md8W2et0xUiuTjUSRCdnlwEPYqah2dCAAkxW_95BXBY,5238
|
|
77
78
|
jarvis/jarvis_tools/read_code.py,sha256=pgztSBRh8RORFalqwzzsLHQogooFvDm1ePBL0E5O1C4,5961
|
|
78
|
-
jarvis/jarvis_tools/read_webpage.py,sha256=
|
|
79
|
+
jarvis/jarvis_tools/read_webpage.py,sha256=LLvAOvaQJodaeNJKQ6dU9MYEE227NMdHyLs7esluUQ4,2217
|
|
79
80
|
jarvis/jarvis_tools/registry.py,sha256=rRdvLzsAkMPfRFZz9GfTP9K8mGedoDt8Pj9drRI_yL8,27133
|
|
80
81
|
jarvis/jarvis_tools/rewrite_file.py,sha256=rEPPSNU7uF1iKfEW9npEpZJ2LSoQXjt2OC-_troBToE,7003
|
|
81
|
-
jarvis/jarvis_tools/search_web.py,sha256
|
|
82
|
+
jarvis/jarvis_tools/search_web.py,sha256=-h1WYOqTcYC_8fdkm-4RfwKpbtLTVxOfRROul51NgO0,951
|
|
82
83
|
jarvis/jarvis_tools/virtual_tty.py,sha256=AKAaKY5KcPxifNQoXjzHaL4U6EUVA7irHLwVvz2wLVs,16396
|
|
83
84
|
jarvis/jarvis_utils/__init__.py,sha256=l-fsyQ-KzyqAhrJYur8eZAqsgaifGzSm24R2qtRGJ0g,849
|
|
84
85
|
jarvis/jarvis_utils/builtin_replace_map.py,sha256=A-cJ8deht2vDl2iKRhoZ7qECyJ6sboVH5Zx-L9vIBUs,4314
|
|
85
|
-
jarvis/jarvis_utils/config.py,sha256=
|
|
86
|
+
jarvis/jarvis_utils/config.py,sha256=T7-RIPIZx6qehTFuu8AFZA-SVTDpsYUCopJ6KYZ8RAQ,4315
|
|
86
87
|
jarvis/jarvis_utils/embedding.py,sha256=05KvmZvtI2-7xxmj13kAknRezWuVeold-D68wyPvZSA,7015
|
|
87
88
|
jarvis/jarvis_utils/file_processors.py,sha256=tSZSMJ4qCJ_lXI0dyLgJ0j5qEh6CDXDSVI7vQiFmcuQ,2976
|
|
88
|
-
jarvis/jarvis_utils/git_utils.py,sha256=
|
|
89
|
+
jarvis/jarvis_utils/git_utils.py,sha256=MxhUcQ_gFUFyBxBiorEJ1wUk9a2TerFdq3-Z11FB-AE,11324
|
|
89
90
|
jarvis/jarvis_utils/globals.py,sha256=Zs0chxA_giYiolYvawFFpcnTWgCUnn6GEusAh42jbz8,2275
|
|
90
91
|
jarvis/jarvis_utils/input.py,sha256=qGf2q-yWhgT-OX-j_WYi7aZ11jYmuFNiMz2_W1nUOiM,7432
|
|
91
|
-
jarvis/jarvis_utils/methodology.py,sha256=
|
|
92
|
+
jarvis/jarvis_utils/methodology.py,sha256=d25o61UtnuwgIQBgnajxx9zPsxsVzv4Ptb9BGwC93Nk,8156
|
|
92
93
|
jarvis/jarvis_utils/output.py,sha256=PVG4fQ3P-eGOZUNZTowPtnjqq3GN91OE8fHa68lFOOg,8440
|
|
93
94
|
jarvis/jarvis_utils/tag.py,sha256=YJHmuedLb7_AiqvKQetHr4R1FxyzIh7HN0RRkWMmYbU,429
|
|
94
|
-
jarvis/jarvis_utils/utils.py,sha256=
|
|
95
|
-
jarvis_ai_assistant-0.1.
|
|
96
|
-
jarvis_ai_assistant-0.1.
|
|
97
|
-
jarvis_ai_assistant-0.1.
|
|
98
|
-
jarvis_ai_assistant-0.1.
|
|
99
|
-
jarvis_ai_assistant-0.1.
|
|
100
|
-
jarvis_ai_assistant-0.1.
|
|
95
|
+
jarvis/jarvis_utils/utils.py,sha256=R3SS4v35HwgZxvQmCKrdUWdu4AeceRZ1gEHH8KK6ST8,5471
|
|
96
|
+
jarvis_ai_assistant-0.1.167.dist-info/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
|
|
97
|
+
jarvis_ai_assistant-0.1.167.dist-info/METADATA,sha256=AHbi3nL5geVT43ECivtOUMao921-InbKknih9aBBoWU,14455
|
|
98
|
+
jarvis_ai_assistant-0.1.167.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
99
|
+
jarvis_ai_assistant-0.1.167.dist-info/entry_points.txt,sha256=cKz_9SEpOvElTubKPMZMAdskD4GHz-NyKWRNssIVAWE,973
|
|
100
|
+
jarvis_ai_assistant-0.1.167.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
|
|
101
|
+
jarvis_ai_assistant-0.1.167.dist-info/RECORD,,
|
{jarvis_ai_assistant-0.1.165.dist-info/licenses → jarvis_ai_assistant-0.1.167.dist-info}/LICENSE
RENAMED
|
File without changes
|
{jarvis_ai_assistant-0.1.165.dist-info → jarvis_ai_assistant-0.1.167.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{jarvis_ai_assistant-0.1.165.dist-info → jarvis_ai_assistant-0.1.167.dist-info}/top_level.txt
RENAMED
|
File without changes
|