jarvis-ai-assistant 0.2.6__py3-none-any.whl → 0.2.8__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 +12 -1
- jarvis/jarvis_agent/jarvis.py +467 -7
- jarvis/jarvis_code_agent/code_agent.py +1 -0
- jarvis/jarvis_data/config_schema.json +10 -0
- jarvis/jarvis_stats/storage.py +2 -2
- jarvis/jarvis_tools/clear_memory.py +252 -0
- jarvis/jarvis_tools/registry.py +47 -20
- jarvis/jarvis_tools/retrieve_memory.py +17 -11
- jarvis/jarvis_tools/save_memory.py +136 -75
- jarvis/jarvis_utils/config.py +20 -0
- jarvis/jarvis_utils/input.py +5 -3
- jarvis/jarvis_utils/methodology.py +41 -6
- jarvis/jarvis_utils/output.py +1 -1
- jarvis/jarvis_utils/utils.py +27 -1
- {jarvis_ai_assistant-0.2.6.dist-info → jarvis_ai_assistant-0.2.8.dist-info}/METADATA +1 -1
- {jarvis_ai_assistant-0.2.6.dist-info → jarvis_ai_assistant-0.2.8.dist-info}/RECORD +21 -20
- {jarvis_ai_assistant-0.2.6.dist-info → jarvis_ai_assistant-0.2.8.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.2.6.dist-info → jarvis_ai_assistant-0.2.8.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.2.6.dist-info → jarvis_ai_assistant-0.2.8.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.2.6.dist-info → jarvis_ai_assistant-0.2.8.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
jarvis/jarvis_agent/__init__.py
CHANGED
@@ -614,8 +614,19 @@ class Agent:
|
|
614
614
|
memory_tags = get_all_memory_tags()
|
615
615
|
memory_tags_prompt = ""
|
616
616
|
|
617
|
+
# 检查是否有save_memory工具
|
618
|
+
tool_registry = self.get_tool_registry()
|
619
|
+
has_save_memory = False
|
620
|
+
if tool_registry:
|
621
|
+
tool_names = [tool.name for tool in tool_registry.tools.values()]
|
622
|
+
has_save_memory = "save_memory" in tool_names
|
623
|
+
|
624
|
+
# 如果有save_memory工具,添加记录关键信息的提示
|
625
|
+
if has_save_memory:
|
626
|
+
memory_tags_prompt = "\n\n💡 提示:在分析任务之前,建议使用 save_memory 工具将关键信息记录下来,便于后续检索和复用。"
|
627
|
+
|
617
628
|
if any(tags for tags in memory_tags.values()):
|
618
|
-
memory_tags_prompt
|
629
|
+
memory_tags_prompt += "\n\n系统中存在以下记忆标签,你可以使用 retrieve_memory 工具检索相关记忆:"
|
619
630
|
for memory_type, tags in memory_tags.items():
|
620
631
|
if tags:
|
621
632
|
type_name = {
|
jarvis/jarvis_agent/jarvis.py
CHANGED
@@ -4,7 +4,7 @@ import shutil
|
|
4
4
|
import subprocess
|
5
5
|
import sys
|
6
6
|
from pathlib import Path
|
7
|
-
from typing import Dict, Optional
|
7
|
+
from typing import Dict, Optional, List
|
8
8
|
|
9
9
|
import typer
|
10
10
|
import yaml # type: ignore
|
@@ -91,13 +91,19 @@ def _select_task(tasks: Dict[str, str]) -> str:
|
|
91
91
|
selected_task = tasks[task_names[choice - 1]]
|
92
92
|
PrettyOutput.print(f"将要执行任务:\n {selected_task}", OutputType.INFO)
|
93
93
|
# 询问是否需要补充信息
|
94
|
-
need_additional = user_confirm(
|
94
|
+
need_additional = user_confirm(
|
95
|
+
"需要为此任务添加补充信息吗?", default=False
|
96
|
+
)
|
95
97
|
if need_additional:
|
96
98
|
additional_input = get_multiline_input("请输入补充信息:")
|
97
99
|
if additional_input:
|
98
|
-
selected_task =
|
100
|
+
selected_task = (
|
101
|
+
f"{selected_task}\n\n补充信息:\n{additional_input}"
|
102
|
+
)
|
99
103
|
return selected_task
|
100
|
-
PrettyOutput.print(
|
104
|
+
PrettyOutput.print(
|
105
|
+
"无效的选择。请选择列表中的一个号码。", OutputType.WARNING
|
106
|
+
)
|
101
107
|
|
102
108
|
except (KeyboardInterrupt, EOFError):
|
103
109
|
return ""
|
@@ -183,6 +189,436 @@ def _get_and_run_task(agent: Agent, task_content: Optional[str] = None) -> None:
|
|
183
189
|
raise typer.Exit(code=0)
|
184
190
|
|
185
191
|
|
192
|
+
def _parse_selection(selection_str: str, max_value: int) -> List[int]:
|
193
|
+
"""解析用户输入的选择字符串,支持逗号分隔和范围选择
|
194
|
+
|
195
|
+
例如: "1,2,3,4-9,20" -> [1, 2, 3, 4, 5, 6, 7, 8, 9, 20]
|
196
|
+
"""
|
197
|
+
selected: set[int] = set()
|
198
|
+
parts = selection_str.split(",")
|
199
|
+
|
200
|
+
for part in parts:
|
201
|
+
part = part.strip()
|
202
|
+
if "-" in part:
|
203
|
+
# 处理范围选择
|
204
|
+
try:
|
205
|
+
start_str, end_str = part.split("-")
|
206
|
+
start_num = int(start_str.strip())
|
207
|
+
end_num = int(end_str.strip())
|
208
|
+
if 1 <= start_num <= max_value and 1 <= end_num <= max_value:
|
209
|
+
selected.update(range(start_num, end_num + 1))
|
210
|
+
except ValueError:
|
211
|
+
continue
|
212
|
+
else:
|
213
|
+
# 处理单个数字
|
214
|
+
try:
|
215
|
+
num = int(part)
|
216
|
+
if 1 <= num <= max_value:
|
217
|
+
selected.add(num)
|
218
|
+
except ValueError:
|
219
|
+
continue
|
220
|
+
|
221
|
+
return sorted(list(selected))
|
222
|
+
|
223
|
+
|
224
|
+
def _handle_share_tool(config_file: Optional[str] = None) -> None:
|
225
|
+
"""处理工具分享功能"""
|
226
|
+
from jarvis.jarvis_utils.config import (
|
227
|
+
get_central_tool_repo,
|
228
|
+
get_data_dir,
|
229
|
+
)
|
230
|
+
import glob
|
231
|
+
import shutil
|
232
|
+
|
233
|
+
# 获取中心工具仓库配置
|
234
|
+
central_repo = get_central_tool_repo()
|
235
|
+
if not central_repo:
|
236
|
+
PrettyOutput.print(
|
237
|
+
"错误:未配置中心工具仓库(JARVIS_CENTRAL_TOOL_REPO)",
|
238
|
+
OutputType.ERROR,
|
239
|
+
)
|
240
|
+
PrettyOutput.print("请在配置文件中设置中心工具仓库的Git地址", OutputType.INFO)
|
241
|
+
raise typer.Exit(code=1)
|
242
|
+
|
243
|
+
# 克隆或更新中心工具仓库
|
244
|
+
central_repo_path = os.path.join(get_data_dir(), "central_tool_repo")
|
245
|
+
if not os.path.exists(central_repo_path):
|
246
|
+
PrettyOutput.print(f"正在克隆中心工具仓库...", OutputType.INFO)
|
247
|
+
subprocess.run(["git", "clone", central_repo, central_repo_path], check=True)
|
248
|
+
else:
|
249
|
+
PrettyOutput.print(f"正在更新中心工具仓库...", OutputType.INFO)
|
250
|
+
# 检查是否是空仓库
|
251
|
+
try:
|
252
|
+
# 先尝试获取远程分支信息
|
253
|
+
result = subprocess.run(
|
254
|
+
["git", "ls-remote", "--heads", "origin"],
|
255
|
+
cwd=central_repo_path,
|
256
|
+
capture_output=True,
|
257
|
+
text=True,
|
258
|
+
check=True,
|
259
|
+
)
|
260
|
+
# 如果有远程分支,执行pull
|
261
|
+
if result.stdout.strip():
|
262
|
+
subprocess.run(["git", "pull"], cwd=central_repo_path, check=True)
|
263
|
+
else:
|
264
|
+
PrettyOutput.print(
|
265
|
+
"中心工具仓库是空的,将初始化为新仓库", OutputType.INFO
|
266
|
+
)
|
267
|
+
except subprocess.CalledProcessError:
|
268
|
+
# 如果命令失败,可能是网络问题或其他错误
|
269
|
+
PrettyOutput.print("无法连接到远程仓库,将跳过更新", OutputType.WARNING)
|
270
|
+
|
271
|
+
# 获取中心仓库中已有的工具文件名
|
272
|
+
existing_tools = set()
|
273
|
+
for filepath in glob.glob(os.path.join(central_repo_path, "*.py")):
|
274
|
+
existing_tools.add(os.path.basename(filepath))
|
275
|
+
|
276
|
+
# 只从数据目录的tools目录获取工具
|
277
|
+
local_tools_dir = os.path.join(get_data_dir(), "tools")
|
278
|
+
if not os.path.exists(local_tools_dir):
|
279
|
+
PrettyOutput.print(
|
280
|
+
f"本地工具目录不存在: {local_tools_dir}",
|
281
|
+
OutputType.WARNING,
|
282
|
+
)
|
283
|
+
raise typer.Exit(code=0)
|
284
|
+
|
285
|
+
# 收集本地工具文件(排除已存在的)
|
286
|
+
tool_files = []
|
287
|
+
for filepath in glob.glob(os.path.join(local_tools_dir, "*.py")):
|
288
|
+
filename = os.path.basename(filepath)
|
289
|
+
# 跳过__init__.py和已存在的文件
|
290
|
+
if filename == "__init__.py" or filename in existing_tools:
|
291
|
+
continue
|
292
|
+
|
293
|
+
# 尝试获取工具名称(通过简单解析)
|
294
|
+
tool_name = filename[:-3] # 移除.py后缀
|
295
|
+
tool_files.append(
|
296
|
+
{
|
297
|
+
"path": filepath,
|
298
|
+
"filename": filename,
|
299
|
+
"tool_name": tool_name,
|
300
|
+
}
|
301
|
+
)
|
302
|
+
|
303
|
+
if not tool_files:
|
304
|
+
PrettyOutput.print(
|
305
|
+
"没有找到新的工具文件(所有工具可能已存在于中心仓库)",
|
306
|
+
OutputType.WARNING,
|
307
|
+
)
|
308
|
+
raise typer.Exit(code=0)
|
309
|
+
|
310
|
+
# 显示可选的工具
|
311
|
+
tool_list = ["\n可分享的工具(已排除中心仓库中已有的):"]
|
312
|
+
for i, tool in enumerate(tool_files, 1):
|
313
|
+
tool_list.append(f"[{i}] {tool['tool_name']} ({tool['filename']})")
|
314
|
+
|
315
|
+
# 一次性打印所有工具
|
316
|
+
PrettyOutput.print("\n".join(tool_list), OutputType.INFO)
|
317
|
+
|
318
|
+
# 让用户选择要分享的工具
|
319
|
+
while True:
|
320
|
+
try:
|
321
|
+
choice_str = prompt(
|
322
|
+
"\n请选择要分享的工具编号(支持格式: 1,2,3,4-9,20 或 all):"
|
323
|
+
).strip()
|
324
|
+
if choice_str == "0":
|
325
|
+
raise typer.Exit(code=0)
|
326
|
+
|
327
|
+
selected_tools = []
|
328
|
+
if choice_str.lower() == "all":
|
329
|
+
selected_tools = tool_files
|
330
|
+
else:
|
331
|
+
selected_indices = _parse_selection(choice_str, len(tool_files))
|
332
|
+
if not selected_indices:
|
333
|
+
PrettyOutput.print("无效的选择", OutputType.WARNING)
|
334
|
+
continue
|
335
|
+
selected_tools = [tool_files[i - 1] for i in selected_indices]
|
336
|
+
|
337
|
+
# 确认操作
|
338
|
+
share_list = [
|
339
|
+
"\n将要分享以下工具到中心仓库(注意:文件将被移动而非复制):"
|
340
|
+
]
|
341
|
+
for tool in selected_tools:
|
342
|
+
share_list.append(f"- {tool['tool_name']} ({tool['filename']})")
|
343
|
+
PrettyOutput.print("\n".join(share_list), OutputType.WARNING)
|
344
|
+
|
345
|
+
if not user_confirm("确认移动这些工具到中心仓库吗?(原文件将被删除)"):
|
346
|
+
continue
|
347
|
+
|
348
|
+
# 移动选中的工具到中心仓库
|
349
|
+
moved_list = []
|
350
|
+
for tool in selected_tools:
|
351
|
+
src_file = tool["path"]
|
352
|
+
dst_file = os.path.join(central_repo_path, tool["filename"])
|
353
|
+
shutil.move(src_file, dst_file) # 使用move而不是copy
|
354
|
+
moved_list.append(f"已移动: {tool['tool_name']}")
|
355
|
+
|
356
|
+
# 一次性显示所有移动结果
|
357
|
+
if moved_list:
|
358
|
+
PrettyOutput.print("\n".join(moved_list), OutputType.SUCCESS)
|
359
|
+
|
360
|
+
# 提交并推送更改
|
361
|
+
PrettyOutput.print("\n正在提交更改...", OutputType.INFO)
|
362
|
+
subprocess.run(["git", "add", "."], cwd=central_repo_path, check=True)
|
363
|
+
|
364
|
+
commit_msg = f"Add {len(selected_tools)} tool(s) from local collection"
|
365
|
+
subprocess.run(
|
366
|
+
["git", "commit", "-m", commit_msg], cwd=central_repo_path, check=True
|
367
|
+
)
|
368
|
+
|
369
|
+
PrettyOutput.print("正在推送到远程仓库...", OutputType.INFO)
|
370
|
+
# 检查是否需要设置上游分支(空仓库的情况)
|
371
|
+
try:
|
372
|
+
# 先尝试普通推送
|
373
|
+
subprocess.run(["git", "push"], cwd=central_repo_path, check=True)
|
374
|
+
except subprocess.CalledProcessError:
|
375
|
+
# 如果失败,可能是空仓库,尝试设置上游分支
|
376
|
+
try:
|
377
|
+
subprocess.run(
|
378
|
+
["git", "push", "-u", "origin", "main"],
|
379
|
+
cwd=central_repo_path,
|
380
|
+
check=True,
|
381
|
+
)
|
382
|
+
except subprocess.CalledProcessError:
|
383
|
+
# 如果main分支不存在,尝试master分支
|
384
|
+
subprocess.run(
|
385
|
+
["git", "push", "-u", "origin", "master"],
|
386
|
+
cwd=central_repo_path,
|
387
|
+
check=True,
|
388
|
+
)
|
389
|
+
|
390
|
+
PrettyOutput.print("\n工具已成功分享到中心仓库!", OutputType.SUCCESS)
|
391
|
+
PrettyOutput.print(
|
392
|
+
f"原文件已从 {local_tools_dir} 移动到中心仓库", OutputType.INFO
|
393
|
+
)
|
394
|
+
break
|
395
|
+
|
396
|
+
except ValueError:
|
397
|
+
PrettyOutput.print("请输入有效的数字", OutputType.WARNING)
|
398
|
+
except subprocess.CalledProcessError as e:
|
399
|
+
PrettyOutput.print(f"Git操作失败: {str(e)}", OutputType.ERROR)
|
400
|
+
raise typer.Exit(code=1)
|
401
|
+
except Exception as e:
|
402
|
+
PrettyOutput.print(f"分享工具时出错: {str(e)}", OutputType.ERROR)
|
403
|
+
raise typer.Exit(code=1)
|
404
|
+
|
405
|
+
|
406
|
+
def _handle_share_methodology(config_file: Optional[str] = None) -> None:
|
407
|
+
"""处理方法论分享功能"""
|
408
|
+
from jarvis.jarvis_utils.config import (
|
409
|
+
get_central_methodology_repo,
|
410
|
+
get_methodology_dirs,
|
411
|
+
get_data_dir,
|
412
|
+
)
|
413
|
+
import glob
|
414
|
+
import json
|
415
|
+
import shutil
|
416
|
+
|
417
|
+
# 获取中心方法论仓库配置
|
418
|
+
central_repo = get_central_methodology_repo()
|
419
|
+
if not central_repo:
|
420
|
+
PrettyOutput.print(
|
421
|
+
"错误:未配置中心方法论仓库(JARVIS_CENTRAL_METHODOLOGY_REPO)",
|
422
|
+
OutputType.ERROR,
|
423
|
+
)
|
424
|
+
PrettyOutput.print("请在配置文件中设置中心方法论仓库的Git地址", OutputType.INFO)
|
425
|
+
raise typer.Exit(code=1)
|
426
|
+
|
427
|
+
# 克隆或更新中心方法论仓库
|
428
|
+
central_repo_path = os.path.join(get_data_dir(), "central_methodology_repo")
|
429
|
+
if not os.path.exists(central_repo_path):
|
430
|
+
PrettyOutput.print(f"正在克隆中心方法论仓库...", OutputType.INFO)
|
431
|
+
subprocess.run(["git", "clone", central_repo, central_repo_path], check=True)
|
432
|
+
else:
|
433
|
+
PrettyOutput.print(f"正在更新中心方法论仓库...", OutputType.INFO)
|
434
|
+
# 检查是否是空仓库
|
435
|
+
try:
|
436
|
+
# 先尝试获取远程分支信息
|
437
|
+
result = subprocess.run(
|
438
|
+
["git", "ls-remote", "--heads", "origin"],
|
439
|
+
cwd=central_repo_path,
|
440
|
+
capture_output=True,
|
441
|
+
text=True,
|
442
|
+
check=True,
|
443
|
+
)
|
444
|
+
# 如果有远程分支,执行pull
|
445
|
+
if result.stdout.strip():
|
446
|
+
subprocess.run(["git", "pull"], cwd=central_repo_path, check=True)
|
447
|
+
else:
|
448
|
+
PrettyOutput.print(
|
449
|
+
"中心方法论仓库是空的,将初始化为新仓库", OutputType.INFO
|
450
|
+
)
|
451
|
+
except subprocess.CalledProcessError:
|
452
|
+
# 如果命令失败,可能是网络问题或其他错误
|
453
|
+
PrettyOutput.print("无法连接到远程仓库,将跳过更新", OutputType.WARNING)
|
454
|
+
|
455
|
+
# 获取中心仓库中已有的方法论
|
456
|
+
existing_methodologies = {} # 改为字典,存储 problem_type -> content 的映射
|
457
|
+
for filepath in glob.glob(os.path.join(central_repo_path, "*.json")):
|
458
|
+
try:
|
459
|
+
with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
|
460
|
+
methodology = json.load(f)
|
461
|
+
problem_type = methodology.get("problem_type", "")
|
462
|
+
content = methodology.get("content", "")
|
463
|
+
if problem_type and content:
|
464
|
+
existing_methodologies[problem_type] = content
|
465
|
+
except Exception:
|
466
|
+
pass
|
467
|
+
|
468
|
+
# 获取所有方法论目录
|
469
|
+
from jarvis.jarvis_utils.methodology import _get_methodology_directory
|
470
|
+
|
471
|
+
methodology_dirs = [_get_methodology_directory()] + get_methodology_dirs()
|
472
|
+
|
473
|
+
# 收集所有方法论文件(排除中心仓库目录和已存在的方法论)
|
474
|
+
all_methodologies = {}
|
475
|
+
methodology_files = []
|
476
|
+
seen_problem_types = set() # 用于去重
|
477
|
+
|
478
|
+
for directory in set(methodology_dirs):
|
479
|
+
# 跳过中心仓库目录
|
480
|
+
if os.path.abspath(directory) == os.path.abspath(central_repo_path):
|
481
|
+
continue
|
482
|
+
|
483
|
+
if not os.path.isdir(directory):
|
484
|
+
continue
|
485
|
+
|
486
|
+
for filepath in glob.glob(os.path.join(directory, "*.json")):
|
487
|
+
try:
|
488
|
+
with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
|
489
|
+
methodology = json.load(f)
|
490
|
+
problem_type = methodology.get("problem_type", "")
|
491
|
+
content = methodology.get("content", "")
|
492
|
+
# 基于内容判断是否已存在于中心仓库
|
493
|
+
is_duplicate = False
|
494
|
+
if problem_type in existing_methodologies:
|
495
|
+
# 如果problem_type相同,比较内容
|
496
|
+
if (
|
497
|
+
content.strip()
|
498
|
+
== existing_methodologies[problem_type].strip()
|
499
|
+
):
|
500
|
+
is_duplicate = True
|
501
|
+
|
502
|
+
# 排除已存在于中心仓库的方法论(基于内容),以及本地重复的方法论
|
503
|
+
if (
|
504
|
+
problem_type
|
505
|
+
and content
|
506
|
+
and not is_duplicate
|
507
|
+
and problem_type not in seen_problem_types
|
508
|
+
):
|
509
|
+
methodology_files.append(
|
510
|
+
{
|
511
|
+
"path": filepath,
|
512
|
+
"problem_type": problem_type,
|
513
|
+
"directory": directory,
|
514
|
+
}
|
515
|
+
)
|
516
|
+
all_methodologies[problem_type] = methodology
|
517
|
+
seen_problem_types.add(problem_type)
|
518
|
+
except Exception:
|
519
|
+
pass
|
520
|
+
|
521
|
+
if not methodology_files:
|
522
|
+
PrettyOutput.print(
|
523
|
+
"没有找到新的方法论文件(所有方法论可能已存在于中心仓库)",
|
524
|
+
OutputType.WARNING,
|
525
|
+
)
|
526
|
+
raise typer.Exit(code=0)
|
527
|
+
|
528
|
+
# 显示可选的方法论
|
529
|
+
methodology_list = ["\n可分享的方法论(已排除中心仓库中已有的):"]
|
530
|
+
for i, meth in enumerate(methodology_files, 1):
|
531
|
+
dir_name = os.path.basename(meth["directory"])
|
532
|
+
methodology_list.append(f"[{i}] {meth['problem_type']} (来自: {dir_name})")
|
533
|
+
|
534
|
+
# 一次性打印所有方法论
|
535
|
+
PrettyOutput.print("\n".join(methodology_list), OutputType.INFO)
|
536
|
+
|
537
|
+
# 让用户选择要分享的方法论
|
538
|
+
while True:
|
539
|
+
try:
|
540
|
+
choice_str = prompt(
|
541
|
+
"\n请选择要分享的方法论编号(支持格式: 1,2,3,4-9,20 或 all):"
|
542
|
+
).strip()
|
543
|
+
if choice_str == "0":
|
544
|
+
raise typer.Exit(code=0)
|
545
|
+
|
546
|
+
selected_methodologies = []
|
547
|
+
if choice_str.lower() == "all":
|
548
|
+
selected_methodologies = methodology_files
|
549
|
+
else:
|
550
|
+
selected_indices = _parse_selection(choice_str, len(methodology_files))
|
551
|
+
if not selected_indices:
|
552
|
+
PrettyOutput.print("无效的选择", OutputType.WARNING)
|
553
|
+
continue
|
554
|
+
selected_methodologies = [
|
555
|
+
methodology_files[i - 1] for i in selected_indices
|
556
|
+
]
|
557
|
+
|
558
|
+
# 确认操作
|
559
|
+
share_list = ["\n将要分享以下方法论到中心仓库:"]
|
560
|
+
for meth in selected_methodologies:
|
561
|
+
share_list.append(f"- {meth['problem_type']}")
|
562
|
+
PrettyOutput.print("\n".join(share_list), OutputType.INFO)
|
563
|
+
|
564
|
+
if not user_confirm("确认分享这些方法论吗?"):
|
565
|
+
continue
|
566
|
+
|
567
|
+
# 复制选中的方法论到中心仓库
|
568
|
+
copied_list = []
|
569
|
+
for meth in selected_methodologies:
|
570
|
+
src_file = meth["path"]
|
571
|
+
dst_file = os.path.join(central_repo_path, os.path.basename(src_file))
|
572
|
+
shutil.copy2(src_file, dst_file)
|
573
|
+
copied_list.append(f"已复制: {meth['problem_type']}")
|
574
|
+
|
575
|
+
# 一次性显示所有复制结果
|
576
|
+
if copied_list:
|
577
|
+
PrettyOutput.print("\n".join(copied_list), OutputType.SUCCESS)
|
578
|
+
|
579
|
+
# 提交并推送更改
|
580
|
+
PrettyOutput.print("\n正在提交更改...", OutputType.INFO)
|
581
|
+
subprocess.run(["git", "add", "."], cwd=central_repo_path, check=True)
|
582
|
+
|
583
|
+
commit_msg = f"Add {len(selected_methodologies)} methodology(ies) from local collection"
|
584
|
+
subprocess.run(
|
585
|
+
["git", "commit", "-m", commit_msg], cwd=central_repo_path, check=True
|
586
|
+
)
|
587
|
+
|
588
|
+
PrettyOutput.print("正在推送到远程仓库...", OutputType.INFO)
|
589
|
+
# 检查是否需要设置上游分支(空仓库的情况)
|
590
|
+
try:
|
591
|
+
# 先尝试普通推送
|
592
|
+
subprocess.run(["git", "push"], cwd=central_repo_path, check=True)
|
593
|
+
except subprocess.CalledProcessError:
|
594
|
+
# 如果失败,可能是空仓库,尝试设置上游分支
|
595
|
+
try:
|
596
|
+
subprocess.run(
|
597
|
+
["git", "push", "-u", "origin", "main"],
|
598
|
+
cwd=central_repo_path,
|
599
|
+
check=True,
|
600
|
+
)
|
601
|
+
except subprocess.CalledProcessError:
|
602
|
+
# 如果main分支不存在,尝试master分支
|
603
|
+
subprocess.run(
|
604
|
+
["git", "push", "-u", "origin", "master"],
|
605
|
+
cwd=central_repo_path,
|
606
|
+
check=True,
|
607
|
+
)
|
608
|
+
|
609
|
+
PrettyOutput.print("\n方法论已成功分享到中心仓库!", OutputType.SUCCESS)
|
610
|
+
break
|
611
|
+
|
612
|
+
except ValueError:
|
613
|
+
PrettyOutput.print("请输入有效的数字", OutputType.WARNING)
|
614
|
+
except subprocess.CalledProcessError as e:
|
615
|
+
PrettyOutput.print(f"Git操作失败: {str(e)}", OutputType.ERROR)
|
616
|
+
raise typer.Exit(code=1)
|
617
|
+
except Exception as e:
|
618
|
+
PrettyOutput.print(f"分享方法论时出错: {str(e)}", OutputType.ERROR)
|
619
|
+
raise typer.Exit(code=1)
|
620
|
+
|
621
|
+
|
186
622
|
@app.callback(invoke_without_command=True)
|
187
623
|
def run_cli(
|
188
624
|
ctx: typer.Context,
|
@@ -191,17 +627,27 @@ def run_cli(
|
|
191
627
|
"--llm_type",
|
192
628
|
help="使用的LLM类型,可选值:'normal'(普通)或 'thinking'(思考模式)",
|
193
629
|
),
|
194
|
-
task: Optional[str] = typer.Option(
|
630
|
+
task: Optional[str] = typer.Option(
|
631
|
+
None, "-t", "--task", help="从命令行直接输入任务内容"
|
632
|
+
),
|
195
633
|
model_group: Optional[str] = typer.Option(
|
196
634
|
None, "--llm_group", help="使用的模型组,覆盖配置文件中的设置"
|
197
635
|
),
|
198
|
-
config_file: Optional[str] = typer.Option(
|
636
|
+
config_file: Optional[str] = typer.Option(
|
637
|
+
None, "-f", "--config", help="自定义配置文件路径"
|
638
|
+
),
|
199
639
|
restore_session: bool = typer.Option(
|
200
640
|
False,
|
201
641
|
"--restore-session",
|
202
642
|
help="从 .jarvis/saved_session.json 恢复会话",
|
203
643
|
),
|
204
644
|
edit: bool = typer.Option(False, "-e", "--edit", help="编辑配置文件"),
|
645
|
+
share_methodology: bool = typer.Option(
|
646
|
+
False, "--share-methodology", help="分享本地方法论到中心方法论仓库"
|
647
|
+
),
|
648
|
+
share_tool: bool = typer.Option(
|
649
|
+
False, "--share-tool", help="分享本地工具到中心工具仓库"
|
650
|
+
),
|
205
651
|
) -> None:
|
206
652
|
"""Jarvis AI assistant command-line interface."""
|
207
653
|
if ctx.invoked_subcommand is not None:
|
@@ -209,7 +655,21 @@ def run_cli(
|
|
209
655
|
|
210
656
|
_handle_edit_mode(edit, config_file)
|
211
657
|
|
212
|
-
|
658
|
+
# 处理方法论分享
|
659
|
+
if share_methodology:
|
660
|
+
init_env("", config_file=config_file) # 初始化配置但不显示欢迎信息
|
661
|
+
_handle_share_methodology(config_file)
|
662
|
+
raise typer.Exit(code=0)
|
663
|
+
|
664
|
+
# 处理工具分享
|
665
|
+
if share_tool:
|
666
|
+
init_env("", config_file=config_file) # 初始化配置但不显示欢迎信息
|
667
|
+
_handle_share_tool(config_file)
|
668
|
+
raise typer.Exit(code=0)
|
669
|
+
|
670
|
+
init_env(
|
671
|
+
"欢迎使用 Jarvis AI 助手,您的智能助理已准备就绪!", config_file=config_file
|
672
|
+
)
|
213
673
|
|
214
674
|
try:
|
215
675
|
agent = _initialize_agent(llm_type, model_group, restore_session)
|
@@ -225,6 +225,16 @@
|
|
225
225
|
},
|
226
226
|
"default": []
|
227
227
|
},
|
228
|
+
"JARVIS_CENTRAL_METHODOLOGY_REPO": {
|
229
|
+
"type": "string",
|
230
|
+
"description": "中心方法论Git仓库地址,该仓库会自动添加到方法论加载路径中",
|
231
|
+
"default": ""
|
232
|
+
},
|
233
|
+
"JARVIS_CENTRAL_TOOL_REPO": {
|
234
|
+
"type": "string",
|
235
|
+
"description": "中心工具库Git仓库地址,该仓库会自动克隆到数据目录并加载其中的工具",
|
236
|
+
"default": ""
|
237
|
+
},
|
228
238
|
"JARVIS_PRINT_PROMPT": {
|
229
239
|
"type": "boolean",
|
230
240
|
"description": "是否打印提示",
|
jarvis/jarvis_stats/storage.py
CHANGED
@@ -245,7 +245,7 @@ class StatsStorage:
|
|
245
245
|
metrics_from_meta = set(meta.get("metrics", {}).keys())
|
246
246
|
|
247
247
|
# 扫描所有数据文件获取实际存在的指标
|
248
|
-
metrics_from_data = set()
|
248
|
+
metrics_from_data: set[str] = set()
|
249
249
|
for data_file in self.data_dir.glob("stats_*.json"):
|
250
250
|
try:
|
251
251
|
data = self._load_json(data_file)
|
@@ -285,7 +285,7 @@ class StatsStorage:
|
|
285
285
|
return {}
|
286
286
|
|
287
287
|
# 聚合数据
|
288
|
-
aggregated = defaultdict(
|
288
|
+
aggregated: Dict[str, Dict[str, Any]] = defaultdict(
|
289
289
|
lambda: {
|
290
290
|
"count": 0,
|
291
291
|
"sum": 0,
|