jarvis-ai-assistant 0.3.20__py3-none-any.whl → 0.3.22__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 +24 -3
- jarvis/jarvis_agent/config_editor.py +5 -1
- jarvis/jarvis_agent/edit_file_handler.py +15 -9
- jarvis/jarvis_agent/jarvis.py +99 -3
- jarvis/jarvis_agent/memory_manager.py +3 -3
- jarvis/jarvis_agent/share_manager.py +3 -1
- jarvis/jarvis_agent/task_analyzer.py +0 -1
- jarvis/jarvis_agent/task_manager.py +15 -5
- jarvis/jarvis_agent/tool_executor.py +2 -2
- jarvis/jarvis_code_agent/code_agent.py +42 -18
- jarvis/jarvis_git_utils/git_commiter.py +3 -6
- jarvis/jarvis_mcp/sse_mcp_client.py +9 -3
- jarvis/jarvis_mcp/streamable_mcp_client.py +15 -5
- jarvis/jarvis_memory_organizer/memory_organizer.py +1 -1
- jarvis/jarvis_methodology/main.py +4 -4
- jarvis/jarvis_multi_agent/__init__.py +3 -3
- jarvis/jarvis_platform/base.py +10 -5
- jarvis/jarvis_platform/kimi.py +18 -6
- jarvis/jarvis_platform/tongyi.py +18 -5
- jarvis/jarvis_platform/yuanbao.py +10 -3
- jarvis/jarvis_platform_manager/main.py +21 -7
- jarvis/jarvis_platform_manager/service.py +4 -3
- jarvis/jarvis_rag/cli.py +61 -22
- jarvis/jarvis_rag/embedding_manager.py +10 -3
- jarvis/jarvis_rag/llm_interface.py +4 -1
- jarvis/jarvis_rag/query_rewriter.py +3 -1
- jarvis/jarvis_rag/rag_pipeline.py +47 -3
- jarvis/jarvis_rag/retriever.py +240 -2
- jarvis/jarvis_smart_shell/main.py +59 -18
- jarvis/jarvis_stats/cli.py +11 -9
- jarvis/jarvis_stats/stats.py +14 -8
- jarvis/jarvis_stats/storage.py +23 -6
- jarvis/jarvis_tools/cli/main.py +63 -29
- jarvis/jarvis_tools/edit_file.py +17 -90
- jarvis/jarvis_tools/file_analyzer.py +0 -1
- jarvis/jarvis_tools/generate_new_tool.py +3 -3
- jarvis/jarvis_tools/read_code.py +0 -1
- jarvis/jarvis_tools/read_webpage.py +14 -4
- jarvis/jarvis_tools/registry.py +16 -9
- jarvis/jarvis_tools/retrieve_memory.py +0 -1
- jarvis/jarvis_tools/save_memory.py +0 -1
- jarvis/jarvis_tools/search_web.py +0 -2
- jarvis/jarvis_tools/sub_agent.py +197 -0
- jarvis/jarvis_tools/sub_code_agent.py +194 -0
- jarvis/jarvis_tools/virtual_tty.py +21 -13
- jarvis/jarvis_utils/config.py +35 -5
- jarvis/jarvis_utils/input.py +297 -56
- jarvis/jarvis_utils/methodology.py +3 -1
- jarvis/jarvis_utils/output.py +5 -2
- jarvis/jarvis_utils/utils.py +483 -170
- {jarvis_ai_assistant-0.3.20.dist-info → jarvis_ai_assistant-0.3.22.dist-info}/METADATA +10 -2
- {jarvis_ai_assistant-0.3.20.dist-info → jarvis_ai_assistant-0.3.22.dist-info}/RECORD +57 -55
- {jarvis_ai_assistant-0.3.20.dist-info → jarvis_ai_assistant-0.3.22.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.3.20.dist-info → jarvis_ai_assistant-0.3.22.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.3.20.dist-info → jarvis_ai_assistant-0.3.22.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.3.20.dist-info → jarvis_ai_assistant-0.3.22.dist-info}/top_level.txt +0 -0
jarvis/jarvis_utils/utils.py
CHANGED
@@ -29,6 +29,9 @@ from jarvis.jarvis_utils.globals import get_in_chat, get_interrupt, set_interrup
|
|
29
29
|
from jarvis.jarvis_utils.input import user_confirm
|
30
30
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
31
31
|
|
32
|
+
# 向后兼容:导出 get_yes_no 供外部模块引用
|
33
|
+
get_yes_no = user_confirm
|
34
|
+
|
32
35
|
g_config_file = None
|
33
36
|
|
34
37
|
COMMAND_MAPPING = {
|
@@ -601,6 +604,19 @@ def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
|
|
601
604
|
welcome_str: 欢迎信息字符串
|
602
605
|
config_file: 配置文件路径,默认为None(使用~/.jarvis/config.yaml)
|
603
606
|
"""
|
607
|
+
# 0. 检查是否处于Jarvis打开的终端环境,避免嵌套
|
608
|
+
try:
|
609
|
+
if os.environ.get("JARVIS_TERMINAL") == "1":
|
610
|
+
PrettyOutput.print(
|
611
|
+
"检测到当前终端由 Jarvis 打开。再次启动可能导致嵌套。",
|
612
|
+
OutputType.WARNING,
|
613
|
+
)
|
614
|
+
if not user_confirm("是否仍要继续启动 Jarvis?", default=False):
|
615
|
+
PrettyOutput.print("已取消启动以避免终端嵌套。", OutputType.INFO)
|
616
|
+
sys.exit(0)
|
617
|
+
except Exception:
|
618
|
+
pass
|
619
|
+
|
604
620
|
# 1. 设置信号处理
|
605
621
|
_setup_signal_handler()
|
606
622
|
|
@@ -717,7 +733,7 @@ def _interactive_config_setup(config_file_path: Path):
|
|
717
733
|
except Exception:
|
718
734
|
PrettyOutput.print("测试失败", OutputType.ERROR)
|
719
735
|
|
720
|
-
# 5.
|
736
|
+
# 5. 交互式确认并应用配置(不直接生成配置文件)
|
721
737
|
config_data = {
|
722
738
|
"ENV": env_vars,
|
723
739
|
"JARVIS_PLATFORM": platform_name,
|
@@ -726,57 +742,36 @@ def _interactive_config_setup(config_file_path: Path):
|
|
726
742
|
"JARVIS_THINKING_MODEL": model_name,
|
727
743
|
}
|
728
744
|
|
729
|
-
if test_passed:
|
730
|
-
|
731
|
-
|
732
|
-
if not get_yes_no("配置测试失败,您确定要保存这个配置吗?"):
|
733
|
-
PrettyOutput.print("配置未保存。", OutputType.INFO)
|
745
|
+
if not test_passed:
|
746
|
+
if not get_yes_no("配置测试失败,是否仍要应用该配置并继续?", default=False):
|
747
|
+
PrettyOutput.print("已取消配置。", OutputType.INFO)
|
734
748
|
sys.exit(0)
|
735
749
|
|
750
|
+
# 6. 选择其他功能开关与可选项(复用统一逻辑)
|
751
|
+
_collect_optional_config_interactively(config_data)
|
752
|
+
|
753
|
+
# 7. 应用到当前会话并写入配置文件(基于交互结果,不从默认值生成)
|
754
|
+
set_global_env_data(config_data)
|
755
|
+
_process_env_variables(config_data)
|
736
756
|
try:
|
737
757
|
schema_path = (
|
738
758
|
Path(__file__).parent.parent / "jarvis_data" / "config_schema.json"
|
739
759
|
)
|
760
|
+
config_file_path.parent.mkdir(parents=True, exist_ok=True)
|
761
|
+
header = ""
|
740
762
|
if schema_path.exists():
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
# 合并用户配置
|
753
|
-
if default_config is None:
|
754
|
-
default_config = {}
|
755
|
-
default_config.update(config_data)
|
756
|
-
|
757
|
-
# 写回合并后的配置
|
758
|
-
final_content = (
|
759
|
-
f"# yaml-language-server: $schema={str(schema_path.absolute())}\n"
|
760
|
-
)
|
761
|
-
final_content += yaml.dump(
|
762
|
-
default_config, allow_unicode=True, sort_keys=False
|
763
|
-
)
|
764
|
-
with open(config_file_path, "w", encoding="utf-8") as f:
|
765
|
-
f.write(final_content)
|
766
|
-
|
767
|
-
PrettyOutput.print(
|
768
|
-
f"配置文件已生成: {config_file_path}", OutputType.SUCCESS
|
769
|
-
)
|
770
|
-
PrettyOutput.print("配置完成,请重新启动Jarvis。", OutputType.INFO)
|
771
|
-
sys.exit(0)
|
772
|
-
else:
|
773
|
-
PrettyOutput.print(
|
774
|
-
"未找到config schema,无法生成配置文件。", OutputType.ERROR
|
775
|
-
)
|
776
|
-
sys.exit(1)
|
777
|
-
|
763
|
+
header = f"# yaml-language-server: $schema={str(schema_path.absolute())}\n"
|
764
|
+
_prune_defaults_with_schema(config_data)
|
765
|
+
yaml_str = yaml.dump(config_data, allow_unicode=True, sort_keys=False)
|
766
|
+
with open(config_file_path, "w", encoding="utf-8") as f:
|
767
|
+
if header:
|
768
|
+
f.write(header)
|
769
|
+
f.write(yaml_str)
|
770
|
+
PrettyOutput.print(f"配置文件已生成: {config_file_path}", OutputType.SUCCESS)
|
771
|
+
PrettyOutput.print("配置完成,请重新启动Jarvis。", OutputType.INFO)
|
772
|
+
sys.exit(0)
|
778
773
|
except Exception:
|
779
|
-
PrettyOutput.print("
|
774
|
+
PrettyOutput.print("写入配置文件失败", OutputType.ERROR)
|
780
775
|
sys.exit(1)
|
781
776
|
|
782
777
|
|
@@ -857,152 +852,410 @@ def _process_env_variables(config_data: dict) -> None:
|
|
857
852
|
)
|
858
853
|
|
859
854
|
|
860
|
-
def
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
config_file: 配置文件路径
|
855
|
+
def _collect_optional_config_interactively(
|
856
|
+
config_data: dict, ask_all: bool = False
|
857
|
+
) -> bool:
|
858
|
+
"""
|
859
|
+
复用的交互式配置收集逻辑:
|
860
|
+
- ask_all=False(默认):仅对缺省的新功能开关/可选项逐项询问,已存在项跳过
|
861
|
+
- ask_all=True:对所有项进行询问,默认值取自当前配置文件,可覆盖现有设置
|
862
|
+
- 修改传入的 config_data
|
863
|
+
- 包含更多来自 config.py 的可选项
|
864
|
+
返回:
|
865
|
+
bool: 是否有变更
|
872
866
|
"""
|
873
867
|
from jarvis.jarvis_utils.input import user_confirm as get_yes_no
|
874
868
|
from jarvis.jarvis_utils.input import get_single_line_input
|
875
869
|
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
870
|
+
def _ask_and_set(_key, _tip, _default, _type="bool"):
|
871
|
+
try:
|
872
|
+
if not ask_all and _key in config_data:
|
873
|
+
return False
|
874
|
+
if _type == "bool":
|
875
|
+
cur = bool(config_data.get(_key, _default))
|
876
|
+
val = get_yes_no(_tip, default=cur)
|
877
|
+
# 与当前值相同则不写入,避免冗余
|
878
|
+
if bool(val) == cur:
|
879
|
+
return False
|
880
|
+
config_data[_key] = bool(val)
|
881
|
+
else:
|
882
|
+
cur = str(config_data.get(_key, _default or ""))
|
883
|
+
val = get_single_line_input(f"{_tip}", default=cur)
|
884
|
+
v = ("" if val is None else str(val)).strip()
|
885
|
+
# 输入与当前值相同则不写入
|
886
|
+
if v == cur:
|
887
|
+
return False
|
888
|
+
config_data[_key] = v
|
889
|
+
return True
|
890
|
+
except Exception:
|
891
|
+
# 异常时不写入,保持精简
|
892
|
+
return False
|
893
|
+
|
894
|
+
def _ask_and_set_optional_str(_key, _tip, _default: str = "") -> bool:
|
895
|
+
try:
|
896
|
+
if not ask_all and _key in config_data:
|
897
|
+
return False
|
898
|
+
cur = str(config_data.get(_key, _default or ""))
|
899
|
+
val = get_single_line_input(f"{_tip}", default=cur)
|
900
|
+
if val is None:
|
901
|
+
return False
|
902
|
+
s = str(val).strip()
|
903
|
+
# 空输入表示不改变
|
904
|
+
if s == "":
|
905
|
+
return False
|
906
|
+
if s == cur:
|
907
|
+
return False
|
908
|
+
config_data[_key] = s
|
909
|
+
return True
|
910
|
+
except Exception:
|
911
|
+
return False
|
881
912
|
|
882
|
-
|
883
|
-
|
913
|
+
def _ask_and_set_int(_key, _tip, _default: int) -> bool:
|
914
|
+
try:
|
915
|
+
if not ask_all and _key in config_data:
|
916
|
+
return False
|
917
|
+
cur = str(config_data.get(_key, _default))
|
918
|
+
val_str = get_single_line_input(f"{_tip}", default=cur)
|
919
|
+
s = "" if val_str is None else str(val_str).strip()
|
920
|
+
if s == "" or s == cur:
|
921
|
+
return False
|
884
922
|
try:
|
885
|
-
|
886
|
-
return False
|
887
|
-
if _type == "bool":
|
888
|
-
val = get_yes_no(_tip, default=bool(_default))
|
889
|
-
config_data[_key] = bool(val)
|
890
|
-
else:
|
891
|
-
val = get_single_line_input(f"{_tip}", default=str(_default or ""))
|
892
|
-
config_data[_key] = val.strip()
|
893
|
-
return True
|
923
|
+
v = int(s)
|
894
924
|
except Exception:
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
925
|
+
return False
|
926
|
+
if str(v) == cur:
|
927
|
+
return False
|
928
|
+
config_data[_key] = v
|
929
|
+
return True
|
930
|
+
except Exception:
|
931
|
+
return False
|
900
932
|
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
933
|
+
def _ask_and_set_list(_key, _tip) -> bool:
|
934
|
+
try:
|
935
|
+
if not ask_all and _key in config_data:
|
936
|
+
return False
|
937
|
+
cur_val = config_data.get(_key, [])
|
938
|
+
if isinstance(cur_val, list):
|
939
|
+
cur_display = ", ".join([str(x) for x in cur_val])
|
940
|
+
else:
|
941
|
+
cur_display = str(cur_val or "")
|
942
|
+
val = get_single_line_input(f"{_tip}", default=cur_display)
|
943
|
+
if val is None:
|
944
|
+
return False
|
945
|
+
s = str(val).strip()
|
946
|
+
if s == cur_display.strip():
|
947
|
+
return False
|
948
|
+
if not s:
|
949
|
+
# 输入为空表示不改变
|
950
|
+
return False
|
951
|
+
items = [x.strip() for x in s.split(",") if x.strip()]
|
952
|
+
if isinstance(cur_val, list) and items == cur_val:
|
953
|
+
return False
|
954
|
+
config_data[_key] = items
|
955
|
+
return True
|
956
|
+
except Exception:
|
957
|
+
return False
|
958
|
+
|
959
|
+
changed = False
|
960
|
+
# 现有两个开关
|
961
|
+
changed = (
|
962
|
+
_ask_and_set(
|
963
|
+
"JARVIS_ENABLE_GIT_JCA_SWITCH",
|
964
|
+
"是否在检测到Git仓库时,提示并可自动切换到代码开发模式(jca)?",
|
965
|
+
False,
|
966
|
+
"bool",
|
911
967
|
)
|
912
|
-
changed
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
968
|
+
or changed
|
969
|
+
)
|
970
|
+
changed = (
|
971
|
+
_ask_and_set(
|
972
|
+
"JARVIS_ENABLE_STARTUP_CONFIG_SELECTOR",
|
973
|
+
"在进入默认通用代理前,是否先列出可用配置(agent/multi_agent/roles)供选择?",
|
974
|
+
False,
|
975
|
+
"bool",
|
920
976
|
)
|
977
|
+
or changed
|
978
|
+
)
|
921
979
|
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
)
|
930
|
-
or changed
|
980
|
+
# 新增的配置项交互(通用体验相关)
|
981
|
+
changed = (
|
982
|
+
_ask_and_set(
|
983
|
+
"JARVIS_PRETTY_OUTPUT",
|
984
|
+
"是否启用更美观的终端输出(Pretty Output)?",
|
985
|
+
False,
|
986
|
+
"bool",
|
931
987
|
)
|
932
|
-
changed
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
988
|
+
or changed
|
989
|
+
)
|
990
|
+
changed = (
|
991
|
+
_ask_and_set(
|
992
|
+
"JARVIS_PRINT_PROMPT",
|
993
|
+
"是否打印发送给模型的提示词(Prompt)?",
|
994
|
+
False,
|
995
|
+
"bool",
|
940
996
|
)
|
941
|
-
changed
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
997
|
+
or changed
|
998
|
+
)
|
999
|
+
changed = (
|
1000
|
+
_ask_and_set(
|
1001
|
+
"JARVIS_IMMEDIATE_ABORT",
|
1002
|
+
"是否启用立即中断?\n- 选择 是/true:在对话输出流的每次迭代中检测到用户中断(例如 Ctrl+C)时,立即返回当前已生成的内容并停止继续输出。\n- 选择 否/false:不会在输出过程中立刻返回,而是按既有流程处理(不中途打断输出)。",
|
1003
|
+
False,
|
1004
|
+
"bool",
|
949
1005
|
)
|
950
|
-
changed
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
1006
|
+
or changed
|
1007
|
+
)
|
1008
|
+
changed = (
|
1009
|
+
_ask_and_set(
|
1010
|
+
"JARVIS_ENABLE_STATIC_ANALYSIS",
|
1011
|
+
"是否启用静态代码分析(Static Analysis)?",
|
1012
|
+
True,
|
1013
|
+
"bool",
|
958
1014
|
)
|
959
|
-
changed
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
1015
|
+
or changed
|
1016
|
+
)
|
1017
|
+
changed = (
|
1018
|
+
_ask_and_set(
|
1019
|
+
"JARVIS_USE_METHODOLOGY",
|
1020
|
+
"是否启用方法论系统(Methodology)?",
|
1021
|
+
True,
|
1022
|
+
"bool",
|
967
1023
|
)
|
968
|
-
changed
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
1024
|
+
or changed
|
1025
|
+
)
|
1026
|
+
changed = (
|
1027
|
+
_ask_and_set(
|
1028
|
+
"JARVIS_USE_ANALYSIS",
|
1029
|
+
"是否启用分析流程(Analysis)?",
|
1030
|
+
True,
|
1031
|
+
"bool",
|
976
1032
|
)
|
977
|
-
changed
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
1033
|
+
or changed
|
1034
|
+
)
|
1035
|
+
changed = (
|
1036
|
+
_ask_and_set(
|
1037
|
+
"JARVIS_FORCE_SAVE_MEMORY",
|
1038
|
+
"是否强制保存会话记忆?",
|
1039
|
+
True,
|
1040
|
+
"bool",
|
985
1041
|
)
|
986
|
-
changed
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
1042
|
+
or changed
|
1043
|
+
)
|
1044
|
+
|
1045
|
+
# 代码与工具操作安全提示
|
1046
|
+
changed = (
|
1047
|
+
_ask_and_set(
|
1048
|
+
"JARVIS_EXECUTE_TOOL_CONFIRM",
|
1049
|
+
"执行工具前是否需要确认?",
|
1050
|
+
False,
|
1051
|
+
"bool",
|
1052
|
+
)
|
1053
|
+
or changed
|
1054
|
+
)
|
1055
|
+
changed = (
|
1056
|
+
_ask_and_set(
|
1057
|
+
"JARVIS_CONFIRM_BEFORE_APPLY_PATCH",
|
1058
|
+
"应用补丁前是否需要确认?",
|
1059
|
+
False,
|
1060
|
+
"bool",
|
1061
|
+
)
|
1062
|
+
or changed
|
1063
|
+
)
|
1064
|
+
|
1065
|
+
# 数据目录与最大输入Token
|
1066
|
+
from jarvis.jarvis_utils.config import get_data_dir as _get_data_dir # lazy import
|
1067
|
+
|
1068
|
+
changed = (
|
1069
|
+
_ask_and_set_optional_str(
|
1070
|
+
"JARVIS_DATA_PATH",
|
1071
|
+
f"是否自定义数据目录路径(JARVIS_DATA_PATH)?留空使用默认: {_get_data_dir()}",
|
1072
|
+
)
|
1073
|
+
or changed
|
1074
|
+
)
|
1075
|
+
changed = (
|
1076
|
+
_ask_and_set_int(
|
1077
|
+
"JARVIS_MAX_INPUT_TOKEN_COUNT",
|
1078
|
+
"自定义最大输入Token数量(留空使用默认: 32000)",
|
1079
|
+
32000,
|
1080
|
+
)
|
1081
|
+
or changed
|
1082
|
+
)
|
1083
|
+
|
1084
|
+
# 目录类配置(逗号分隔)
|
1085
|
+
changed = (
|
1086
|
+
_ask_and_set_list(
|
1087
|
+
"JARVIS_TOOL_LOAD_DIRS",
|
1088
|
+
"指定工具加载目录(逗号分隔,留空跳过):",
|
1089
|
+
)
|
1090
|
+
or changed
|
1091
|
+
)
|
1092
|
+
changed = (
|
1093
|
+
_ask_and_set_list(
|
1094
|
+
"JARVIS_METHODOLOGY_DIRS",
|
1095
|
+
"指定方法论加载目录(逗号分隔,留空跳过):",
|
1096
|
+
)
|
1097
|
+
or changed
|
1098
|
+
)
|
1099
|
+
changed = (
|
1100
|
+
_ask_and_set_list(
|
1101
|
+
"JARVIS_AGENT_DEFINITION_DIRS",
|
1102
|
+
"指定 agent 定义加载目录(逗号分隔,留空跳过):",
|
1103
|
+
)
|
1104
|
+
or changed
|
1105
|
+
)
|
1106
|
+
changed = (
|
1107
|
+
_ask_and_set_list(
|
1108
|
+
"JARVIS_MULTI_AGENT_DIRS",
|
1109
|
+
"指定 multi_agent 加载目录(逗号分隔,留空跳过):",
|
1110
|
+
)
|
1111
|
+
or changed
|
1112
|
+
)
|
1113
|
+
changed = (
|
1114
|
+
_ask_and_set_list(
|
1115
|
+
"JARVIS_ROLES_DIRS",
|
1116
|
+
"指定 roles 加载目录(逗号分隔,留空跳过):",
|
1117
|
+
)
|
1118
|
+
or changed
|
1119
|
+
)
|
1120
|
+
|
1121
|
+
# Web 搜索配置(可选)
|
1122
|
+
changed = (
|
1123
|
+
_ask_and_set_optional_str(
|
1124
|
+
"JARVIS_WEB_SEARCH_PLATFORM",
|
1125
|
+
"配置 Web 搜索平台名称(留空跳过):",
|
1126
|
+
)
|
1127
|
+
or changed
|
1128
|
+
)
|
1129
|
+
changed = (
|
1130
|
+
_ask_and_set_optional_str(
|
1131
|
+
"JARVIS_WEB_SEARCH_MODEL",
|
1132
|
+
"配置 Web 搜索模型名称(留空跳过):",
|
1133
|
+
)
|
1134
|
+
or changed
|
1135
|
+
)
|
1136
|
+
|
1137
|
+
# Git 提交提示词(可选)
|
1138
|
+
changed = (
|
1139
|
+
_ask_and_set_optional_str(
|
1140
|
+
"JARVIS_GIT_COMMIT_PROMPT",
|
1141
|
+
"自定义 Git 提交提示模板(留空跳过):",
|
1142
|
+
)
|
1143
|
+
or changed
|
1144
|
+
)
|
1145
|
+
|
1146
|
+
# RAG 配置(可选)
|
1147
|
+
try:
|
1148
|
+
from jarvis.jarvis_utils.config import (
|
1149
|
+
get_rag_embedding_model as _get_rag_embedding_model,
|
1150
|
+
get_rag_rerank_model as _get_rag_rerank_model,
|
1151
|
+
)
|
1152
|
+
|
1153
|
+
rag_default_embed = _get_rag_embedding_model()
|
1154
|
+
rag_default_rerank = _get_rag_rerank_model()
|
1155
|
+
except Exception:
|
1156
|
+
rag_default_embed = "BAAI/bge-m3"
|
1157
|
+
rag_default_rerank = "BAAI/bge-reranker-v2-m3"
|
1158
|
+
|
1159
|
+
try:
|
1160
|
+
if "JARVIS_RAG" not in config_data:
|
1161
|
+
if get_yes_no("是否配置 RAG 检索增强参数?", default=False):
|
1162
|
+
rag_conf = {}
|
1163
|
+
emb = get_single_line_input(
|
1164
|
+
f"RAG 嵌入模型(留空使用默认: {rag_default_embed}):",
|
1165
|
+
default="",
|
1166
|
+
).strip()
|
1167
|
+
rerank = get_single_line_input(
|
1168
|
+
f"RAG rerank 模型(留空使用默认: {rag_default_rerank}):",
|
1169
|
+
default="",
|
1170
|
+
).strip()
|
1171
|
+
use_bm25 = get_yes_no("RAG 是否使用 BM25?", default=True)
|
1172
|
+
use_rerank = get_yes_no("RAG 是否使用 rerank?", default=True)
|
1173
|
+
if emb:
|
1174
|
+
rag_conf["embedding_model"] = emb
|
1175
|
+
else:
|
1176
|
+
rag_conf["embedding_model"] = rag_default_embed
|
1177
|
+
if rerank:
|
1178
|
+
rag_conf["rerank_model"] = rerank
|
1179
|
+
else:
|
1180
|
+
rag_conf["rerank_model"] = rag_default_rerank
|
1181
|
+
rag_conf["use_bm25"] = bool(use_bm25)
|
1182
|
+
rag_conf["use_rerank"] = bool(use_rerank)
|
1183
|
+
config_data["JARVIS_RAG"] = rag_conf
|
1184
|
+
changed = True
|
1185
|
+
except Exception:
|
1186
|
+
pass
|
1187
|
+
|
1188
|
+
# 中心仓库配置
|
1189
|
+
changed = (
|
1190
|
+
_ask_and_set(
|
1191
|
+
"JARVIS_CENTRAL_METHODOLOGY_REPO",
|
1192
|
+
"请输入中心方法论仓库地址(可留空跳过):",
|
1193
|
+
"",
|
1194
|
+
"str",
|
1195
|
+
)
|
1196
|
+
or changed
|
1197
|
+
)
|
1198
|
+
changed = (
|
1199
|
+
_ask_and_set(
|
1200
|
+
"JARVIS_CENTRAL_TOOL_REPO",
|
1201
|
+
"请输入中心工具仓库地址(可留空跳过):",
|
1202
|
+
"",
|
1203
|
+
"str",
|
994
1204
|
)
|
1205
|
+
or changed
|
1206
|
+
)
|
1207
|
+
|
1208
|
+
# 已移除 LLM 组配置交互
|
1209
|
+
|
1210
|
+
# 已移除 RAG 组配置交互
|
1211
|
+
|
1212
|
+
# 已移除 工具组配置交互
|
1213
|
+
|
1214
|
+
# 已移除:替换映射(JARVIS_REPLACE_MAP)的交互式配置,保持最简交互
|
1215
|
+
# SHELL 覆盖(可选)
|
1216
|
+
try:
|
1217
|
+
default_shell = os.getenv("SHELL", "/bin/bash")
|
995
1218
|
changed = (
|
996
|
-
|
997
|
-
"
|
998
|
-
"
|
999
|
-
|
1000
|
-
"str",
|
1219
|
+
_ask_and_set_optional_str(
|
1220
|
+
"SHELL",
|
1221
|
+
f"覆盖 SHELL 路径(留空使用系统默认: {default_shell}):",
|
1222
|
+
default_shell,
|
1001
1223
|
)
|
1002
1224
|
or changed
|
1003
1225
|
)
|
1226
|
+
except Exception:
|
1227
|
+
pass
|
1228
|
+
|
1229
|
+
# 已移除:MCP(JARVIS_MCP)的交互式配置,保持最简交互
|
1230
|
+
return changed
|
1231
|
+
|
1232
|
+
|
1233
|
+
def _load_and_process_config(jarvis_dir: str, config_file: str) -> None:
|
1234
|
+
"""加载并处理配置文件
|
1235
|
+
|
1236
|
+
功能:
|
1237
|
+
1. 读取配置文件
|
1238
|
+
2. 确保schema声明存在
|
1239
|
+
3. 保存配置到全局变量
|
1240
|
+
4. 处理环境变量
|
1241
|
+
|
1242
|
+
参数:
|
1243
|
+
jarvis_dir: Jarvis数据目录路径
|
1244
|
+
config_file: 配置文件路径
|
1245
|
+
"""
|
1246
|
+
from jarvis.jarvis_utils.input import user_confirm as get_yes_no
|
1247
|
+
from jarvis.jarvis_utils.input import get_single_line_input
|
1004
1248
|
|
1005
|
-
|
1249
|
+
try:
|
1250
|
+
content, config_data = _load_config_file(config_file)
|
1251
|
+
_ensure_schema_declaration(jarvis_dir, config_file, content, config_data)
|
1252
|
+
set_global_env_data(config_data)
|
1253
|
+
_process_env_variables(config_data)
|
1254
|
+
|
1255
|
+
# 加载 schema 默认并剔除等于默认值的项
|
1256
|
+
pruned = _prune_defaults_with_schema(config_data)
|
1257
|
+
|
1258
|
+
if pruned:
|
1006
1259
|
# 保留schema声明,如无则自动补充
|
1007
1260
|
header = ""
|
1008
1261
|
try:
|
@@ -1079,6 +1332,69 @@ def generate_default_config(schema_path: str, output_path: str) -> None:
|
|
1079
1332
|
f.write(content)
|
1080
1333
|
|
1081
1334
|
|
1335
|
+
def _load_default_config_from_schema() -> dict:
|
1336
|
+
"""从 schema 生成默认配置字典,用于对比并剔除等于默认值的键"""
|
1337
|
+
try:
|
1338
|
+
schema_path = (
|
1339
|
+
Path(__file__).parent.parent / "jarvis_data" / "config_schema.json"
|
1340
|
+
)
|
1341
|
+
if not schema_path.exists():
|
1342
|
+
return {}
|
1343
|
+
with open(schema_path, "r", encoding="utf-8") as f:
|
1344
|
+
schema = json.load(f)
|
1345
|
+
|
1346
|
+
def _generate_from_schema(schema_dict: Dict[str, Any]) -> Dict[str, Any]:
|
1347
|
+
cfg: Dict[str, Any] = {}
|
1348
|
+
if isinstance(schema_dict, dict) and "properties" in schema_dict:
|
1349
|
+
for key, value in schema_dict["properties"].items():
|
1350
|
+
if "default" in value:
|
1351
|
+
cfg[key] = value["default"]
|
1352
|
+
elif value.get("type") == "array":
|
1353
|
+
cfg[key] = []
|
1354
|
+
elif "properties" in value:
|
1355
|
+
cfg[key] = _generate_from_schema(value)
|
1356
|
+
return cfg
|
1357
|
+
|
1358
|
+
return _generate_from_schema(schema)
|
1359
|
+
except Exception:
|
1360
|
+
return {}
|
1361
|
+
|
1362
|
+
|
1363
|
+
def _prune_defaults_with_schema(config_data: dict) -> bool:
|
1364
|
+
"""
|
1365
|
+
删除与 schema 默认值一致的配置项,返回是否发生了变更
|
1366
|
+
仅处理 schema 中定义的键,未在 schema 中的键不会被修改
|
1367
|
+
"""
|
1368
|
+
defaults = _load_default_config_from_schema()
|
1369
|
+
if not defaults or not isinstance(config_data, dict):
|
1370
|
+
return False
|
1371
|
+
|
1372
|
+
changed = False
|
1373
|
+
|
1374
|
+
def _prune_node(node: dict, default_node: dict):
|
1375
|
+
nonlocal changed
|
1376
|
+
for key in list(node.keys()):
|
1377
|
+
if key in default_node:
|
1378
|
+
dv = default_node[key]
|
1379
|
+
v = node[key]
|
1380
|
+
if isinstance(dv, dict) and isinstance(v, dict):
|
1381
|
+
_prune_node(v, dv)
|
1382
|
+
if not v:
|
1383
|
+
del node[key]
|
1384
|
+
changed = True
|
1385
|
+
elif isinstance(dv, list) and isinstance(v, list):
|
1386
|
+
if v == dv:
|
1387
|
+
del node[key]
|
1388
|
+
changed = True
|
1389
|
+
else:
|
1390
|
+
if v == dv:
|
1391
|
+
del node[key]
|
1392
|
+
changed = True
|
1393
|
+
|
1394
|
+
_prune_node(config_data, defaults)
|
1395
|
+
return changed
|
1396
|
+
|
1397
|
+
|
1082
1398
|
def _read_old_config_file(config_file):
|
1083
1399
|
"""读取并解析旧格式的env配置文件
|
1084
1400
|
|
@@ -1151,9 +1467,7 @@ def while_success(func: Callable[[], Any], sleep_time: float = 0.1) -> Any:
|
|
1151
1467
|
try:
|
1152
1468
|
return func()
|
1153
1469
|
except Exception as e:
|
1154
|
-
PrettyOutput.print(
|
1155
|
-
f"重试中,等待 {sleep_time}s...", OutputType.WARNING
|
1156
|
-
)
|
1470
|
+
PrettyOutput.print(f"重试中,等待 {sleep_time}s...", OutputType.WARNING)
|
1157
1471
|
time.sleep(sleep_time)
|
1158
1472
|
continue
|
1159
1473
|
|
@@ -1258,7 +1572,6 @@ def _pull_git_repo(repo_path: Path, repo_type: str):
|
|
1258
1572
|
if not git_dir.is_dir():
|
1259
1573
|
return
|
1260
1574
|
|
1261
|
-
|
1262
1575
|
try:
|
1263
1576
|
# 检查是否有远程仓库
|
1264
1577
|
remote_result = subprocess.run(
|