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