flash-sim 0.0.1__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.
Files changed (143) hide show
  1. flash/__init__.py +157 -0
  2. flash/_bootstrap.py +35 -0
  3. flash/_core/__init__.py +39 -0
  4. flash/_core/credentials/__init__.py +44 -0
  5. flash/_core/credentials/_config.py +191 -0
  6. flash/_core/credentials/_core.py +277 -0
  7. flash/_core/credentials/_flash_entries.py +83 -0
  8. flash/_core/credentials/_minimal.py +73 -0
  9. flash/_core/credentials/api_keys.py +225 -0
  10. flash/_core/credentials/deepseek.py +187 -0
  11. flash/_core/credentials/flash_ssh.py +472 -0
  12. flash/_core/credentials/gitee.py +220 -0
  13. flash/_core/credentials/manage.py +359 -0
  14. flash/_core/credentials/pypi.py +144 -0
  15. flash/_core/credentials.py +1103 -0
  16. flash/_core/interface.py +90 -0
  17. flash/_core/registry.py +35 -0
  18. flash/_core/schema.py +118 -0
  19. flash/config/__init__.py +105 -0
  20. flash/flash_run/PROCESS.md +435 -0
  21. flash/flash_run/README.md +205 -0
  22. flash/flash_run/__init__.py +25 -0
  23. flash/flash_run/env/README.md +272 -0
  24. flash/flash_run/env/__init__.py +12 -0
  25. flash/flash_run/env/env_manager.py +535 -0
  26. flash/flash_run/env/resource_config.py +650 -0
  27. flash/flash_run/remote/README.md +313 -0
  28. flash/flash_run/remote/__init__.py +20 -0
  29. flash/flash_run/remote/remote_check.py +70 -0
  30. flash/flash_run/remote/remote_deploy.py +792 -0
  31. flash/flash_run/remote/remote_workflow.py +142 -0
  32. flash/flash_run/remote/route_tester.py +465 -0
  33. flash/flash_run/remote/ssh_check.py +107 -0
  34. flash/input_gen/README.md +535 -0
  35. flash/input_gen/__init__.py +170 -0
  36. flash/input_gen/gen_Grid_markRefineDerefine/GEN_GRID_GUIDE.md +68 -0
  37. flash/input_gen/gen_Grid_markRefineDerefine/README.md +327 -0
  38. flash/input_gen/gen_Grid_markRefineDerefine/__init__.py +0 -0
  39. flash/input_gen/gen_Grid_markRefineDerefine/generator.py +567 -0
  40. flash/input_gen/gen_checker/GEN_CHECKER_GUIDE.md +148 -0
  41. flash/input_gen/gen_checker/__init__.py +6 -0
  42. flash/input_gen/gen_checker/checker.py +274 -0
  43. flash/input_gen/gen_checker/ploter/__init__.py +13 -0
  44. flash/input_gen/gen_checker/ploter/density_plotter.py +195 -0
  45. flash/input_gen/gen_checker/ploter/pulse_plotter.py +158 -0
  46. flash/input_gen/gen_checker/ploter/ray_plotter.py +277 -0
  47. flash/input_gen/gen_config/GEN_CONFIG_GUIDE.md +534 -0
  48. flash/input_gen/gen_config/__init__.py +9 -0
  49. flash/input_gen/gen_config/generator.py +205 -0
  50. flash/input_gen/gen_eos_op/GEN_EOS_OP_GUIDE.md +224 -0
  51. flash/input_gen/gen_eos_op/__init__.py +10 -0
  52. flash/input_gen/gen_eos_op/generator.py +481 -0
  53. flash/input_gen/gen_f90/__init__.py +0 -0
  54. flash/input_gen/gen_makefile/GEN_MAKEFILE_GUIDE.md +56 -0
  55. flash/input_gen/gen_makefile/__init__.py +9 -0
  56. flash/input_gen/gen_makefile/generator.py +32 -0
  57. flash/input_gen/gen_newpara/README.md +400 -0
  58. flash/input_gen/gen_newpara/RP_Reference.md +639 -0
  59. flash/input_gen/gen_newpara/__init__.py +19 -0
  60. flash/input_gen/gen_newpara/generator.py +643 -0
  61. flash/input_gen/gen_otherf90s/GEN_OTHER_F90S_GUIDE.md +231 -0
  62. flash/input_gen/gen_otherf90s/__init__.py +0 -0
  63. flash/input_gen/gen_par/GEN_PAR_GUIDE.md +657 -0
  64. flash/input_gen/gen_par/__init__.py +18 -0
  65. flash/input_gen/gen_par/defaults.py +510 -0
  66. flash/input_gen/gen_par/generator.py +445 -0
  67. flash/input_gen/gen_par/materials.py +141 -0
  68. flash/input_gen/gen_shell_script/GEN_SHELL_SCRIPT_GUIDE.md +120 -0
  69. flash/input_gen/gen_shell_script/__init__.py +12 -0
  70. flash/input_gen/gen_shell_script/generator.py +850 -0
  71. flash/input_gen/gen_sim_data/GEN_SIM_DATA_GUIDE.md +306 -0
  72. flash/input_gen/gen_sim_data/__init__.py +10 -0
  73. flash/input_gen/gen_sim_data/generator.py +87 -0
  74. flash/input_gen/gen_sim_init/GEN_SIM_INIT_GUIDE.md +216 -0
  75. flash/input_gen/gen_sim_init/__init__.py +9 -0
  76. flash/input_gen/gen_sim_init/generator.py +104 -0
  77. flash/input_gen/gen_sim_initblock/GEN_SIM_INITBLOCK_GUIDE.md +330 -0
  78. flash/input_gen/gen_sim_initblock/__init__.py +17 -0
  79. flash/input_gen/gen_sim_initblock/generator.py +432 -0
  80. flash/input_gen/gen_sim_initblock/grid.py +278 -0
  81. flash/input_gen/gen_sim_initblock/visualizer.py +250 -0
  82. flash/interface.py +398 -0
  83. flash/output_processors/__init__.py +50 -0
  84. flash/output_processors/conftest.py +41 -0
  85. flash/output_processors/docs/api_reference.md +597 -0
  86. flash/output_processors/docs/how_to_add_custom_variables.md +417 -0
  87. flash/output_processors/docs/output_processors_usage.md +596 -0
  88. flash/output_processors/docs/performance_tuning.md +365 -0
  89. flash/output_processors/docs/workflow.md +101 -0
  90. flash/output_processors/hdf5processor/__init__.py +21 -0
  91. flash/output_processors/hdf5processor/flash_hdf5.py +1224 -0
  92. flash/output_processors/loader/__init__.py +7 -0
  93. flash/output_processors/loader/data_loader.py +392 -0
  94. flash/output_processors/parallel.py +580 -0
  95. flash/output_processors/plotter/__init__.py +8 -0
  96. flash/output_processors/plotter/plot_generator.py +471 -0
  97. flash/output_processors/plotter/plot_style.py +240 -0
  98. flash/physics/ele_nonlocal/Knudsen_Nonlocal_Summary.md +576 -0
  99. flash/projects/__init__.py +0 -0
  100. flash/projects/projects_demo/README.md +94 -0
  101. flash/projects/projects_demo/__init__.py +17 -0
  102. flash/projects/projects_demo/ch_center_demo/__init__.py +16 -0
  103. flash/projects/projects_demo/ch_center_demo/plot_compare.py +169 -0
  104. flash/projects/projects_demo/ch_center_demo/run_compare.py +148 -0
  105. flash/scenarios/README.md +669 -0
  106. flash/scenarios/SNBtest/__init__.py +0 -0
  107. flash/scenarios/__init__.py +18 -0
  108. flash/scenarios/base.py +50 -0
  109. flash/scenarios/center_evolution/__init__.py +2 -0
  110. flash/scenarios/center_evolution/ch_center/__init__.py +361 -0
  111. flash/scenarios/center_evolution/ch_center/laserslab1d_local_custom.py +804 -0
  112. flash/scenarios/collision_compression/__init__.py +38 -0
  113. flash/scenarios/flash_demo/__init__.py +0 -0
  114. flash/scenarios/flash_demo/demo_hpc/__init__.py +0 -0
  115. flash/scenarios/flash_demo/demo_hpc/_plot_utils.py +128 -0
  116. flash/scenarios/flash_demo/demo_hpc/laserslab1d_hpc_demo_batch.py +1035 -0
  117. flash/scenarios/flash_demo/demo_hpc/laserslab1d_supercomputer_demo.py +778 -0
  118. flash/scenarios/flash_demo/demo_hpc/remote_analysis.py +428 -0
  119. flash/scenarios/flash_demo/demo_hpc/remote_plot_script.py +444 -0
  120. flash/scenarios/flash_demo/demo_hpc/remote_ssh_helper.py +457 -0
  121. flash/scenarios/flash_demo/demo_local/__init__.py +0 -0
  122. flash/scenarios/flash_demo/demo_local/laserslab1d_local_demo.py +343 -0
  123. flash/scenarios/flash_demo/hello_flash/README.md +447 -0
  124. flash/scenarios/flash_demo/hello_flash/analyze_density.py +416 -0
  125. flash/scenarios/flash_demo/hello_flash/deploy_flash.py +828 -0
  126. flash/scenarios/interpolator.py +286 -0
  127. flash/scenarios/plasma_preparation/__init__.py +1 -0
  128. flash/scenarios/registry.py +61 -0
  129. flash/scenarios/simulator.py +1045 -0
  130. flash/test/__init__.py +1 -0
  131. flash/test/flash_mathtest_custom01.py +413 -0
  132. flash/test/math_test.py +596 -0
  133. flash/utils/__init__.py +9 -0
  134. flash/utils/check_hdf5_time.py +22 -0
  135. flash/utils/convert_pdf_to_md.py +134 -0
  136. flash/utils/convert_text_to_md.py +102 -0
  137. flash/utils/extract_knowledge.py +200 -0
  138. flash_sim-0.0.1.dist-info/METADATA +811 -0
  139. flash_sim-0.0.1.dist-info/RECORD +143 -0
  140. flash_sim-0.0.1.dist-info/WHEEL +4 -0
  141. flash_sim-0.0.1.dist-info/entry_points.txt +3 -0
  142. flash_sim-0.0.1.dist-info/licenses/LICENSE +386 -0
  143. flash_sim-0.0.1.dist-info/licenses/NOTICE +138 -0
flash/__init__.py ADDED
@@ -0,0 +1,157 @@
1
+ """FLASH plasma simulation package.
2
+
3
+ This package provides a Python interface for the FLASH high-energy-density
4
+ physics simulation code. It supports both standalone mode (without PhySimX)
5
+ and plugin mode (as part of PhySimX).
6
+
7
+ Standalone mode (without PhySimX installed):
8
+ >>> from flash import FlashSimulator
9
+ >>> sim = FlashSimulator(mock=True)
10
+ >>> result = sim.simulate(request)
11
+
12
+ Plugin mode (with PhySimX installed):
13
+ >>> from flash import FlashSimulator # Same API
14
+ >>> sim = FlashSimulator(mock=True)
15
+
16
+ The package automatically detects whether physimx_core is installed:
17
+ - If yes: uses physimx_core (plugin mode, full pydantic validation)
18
+ - If no: uses vendored _core/ (standalone mode, dataclass-based)
19
+
20
+ AI Agent Notes:
21
+ This package includes CLAUDE.md, _MODULE_DESCRIPTIONS, and .workbuddy/
22
+ for automatic discovery by AI coding agents.
23
+ """
24
+
25
+ # =======================================================================
26
+ # Smart Import Layer
27
+ # =======================================================================
28
+ # Priority 1: physimx_core installed (plugin mode, full features)
29
+ # Priority 2: standalone (fallback to vendored _core/)
30
+
31
+ _STANDALONE = False
32
+
33
+ try:
34
+ # Plugin mode: use physimx_core (full features, pydantic validation)
35
+ from physimx_core.interface import BaseSimulator
36
+ from physimx_core.schema import (
37
+ CapabilityCard,
38
+ InputVar,
39
+ OutputVar,
40
+ PhysicsDomain,
41
+ SimulationRequest,
42
+ SimulationResult,
43
+ SimulationStatus,
44
+ SimulatorType,
45
+ )
46
+
47
+ _STANDALONE = False
48
+ except ImportError:
49
+ # Standalone mode: use vendored _core/ (dataclass-based)
50
+ from ._core.interface import BaseSimulator
51
+ from ._core.schema import (
52
+ CapabilityCard,
53
+ InputVar,
54
+ OutputVar,
55
+ PhysicsDomain,
56
+ SimulationRequest,
57
+ SimulationResult,
58
+ SimulationStatus,
59
+ SimulatorType,
60
+ )
61
+
62
+ _STANDALONE = True
63
+
64
+
65
+ # =======================================================================
66
+ # Core Exports
67
+ # =======================================================================
68
+
69
+ from .interface import FlashSimulator
70
+ from .config import FlashConfig, get_default_config
71
+
72
+ # Optional exports (may not exist in all versions)
73
+ try:
74
+ from .flash_run.env.env_manager import (
75
+ FlashEnvironment,
76
+ FlashEnvManager,
77
+ get_env_manager,
78
+ )
79
+ except ImportError:
80
+ FlashEnvironment = None
81
+ FlashEnvManager = None
82
+ get_env_manager = None
83
+
84
+ try:
85
+ from .flash_run.remote.remote_deploy import FlashRemoteDeploy
86
+ except ImportError:
87
+ FlashRemoteDeploy = None
88
+
89
+ try:
90
+ from .test.math_test import LaserSlabCa1D, FlashMathTest
91
+ except ImportError:
92
+ LaserSlabCa1D = None
93
+ FlashMathTest = None
94
+
95
+
96
+ # =======================================================================
97
+ # __all__ and Metadata
98
+ # =======================================================================
99
+
100
+ __all__ = [
101
+ # Core
102
+ "FlashSimulator",
103
+ "FlashConfig",
104
+ "get_default_config",
105
+ # Environment management
106
+ "FlashEnvironment",
107
+ "FlashEnvManager",
108
+ "get_env_manager",
109
+ # Remote deployment
110
+ "FlashRemoteDeploy",
111
+ # Physics tests
112
+ "LaserSlabCa1D",
113
+ "FlashMathTest",
114
+ # Base classes (for extension)
115
+ "BaseSimulator",
116
+ # Schema (for type hints)
117
+ "CapabilityCard",
118
+ "InputVar",
119
+ "OutputVar",
120
+ "PhysicsDomain",
121
+ "SimulationRequest",
122
+ "SimulationResult",
123
+ "SimulationStatus",
124
+ "SimulatorType",
125
+ ]
126
+
127
+ __version__ = "0.0.1"
128
+ __standalone__ = _STANDALONE
129
+
130
+
131
+ # =======================================================================
132
+ # Module-level docstring for AI agents
133
+ # =======================================================================
134
+
135
+
136
+ def __dir__():
137
+ """Return list of public attributes (for AI agents)."""
138
+ return sorted(__all__)
139
+
140
+
141
+ # Warm-up: pre-import common submodules (improves tab completion)
142
+ import sys as _sys
143
+
144
+ if "flash" in _sys.modules:
145
+ # Pre-import common submodules for better UX
146
+ _common_submodules = [
147
+ "flash.config",
148
+ "flash._core.credentials",
149
+ "flash.flash_run",
150
+ "flash.input_gen",
151
+ "flash.output_processors",
152
+ ]
153
+ for _mod in _common_submodules:
154
+ try:
155
+ __import__(_mod)
156
+ except ImportError:
157
+ pass
flash/_bootstrap.py ADDED
@@ -0,0 +1,35 @@
1
+ """
2
+ _bootstrap.py — flash 包导入路径引导标记文件
3
+
4
+ 此文件作为路径标记, 用于脚本通过向上搜索找到项目根目录。
5
+
6
+ 关键发现 (2026-08-06 目录重组后): flash 包位于项目根的 flash/ 子目录
7
+ (标准包布局)。项目根含 pyproject.toml, 导入 flash 需将项目根加入 sys.path。
8
+
9
+ 标准引导代码模板:
10
+
11
+ ```python
12
+ import sys
13
+ from pathlib import Path
14
+
15
+ # Bootstrap: find flash package root by searching upward for marker
16
+ _ROOT = Path(__file__).resolve().parent
17
+ for _ in range(12):
18
+ if (_ROOT / "pyproject.toml").exists():
19
+ break
20
+ _ROOT = _ROOT.parent
21
+ else:
22
+ _ROOT = None # 已安装环境 (site-packages): 静默跳过
23
+ _PARENT = _ROOT
24
+ if _PARENT is not None and str(_PARENT) not in sys.path:
25
+ sys.path.insert(0, str(_PARENT))
26
+ ```
27
+ """
28
+
29
+ if __name__ == "__main__":
30
+ import sys
31
+ from pathlib import Path
32
+
33
+ _ROOT = Path(__file__).resolve().parent
34
+ print(f"flash package root: {_ROOT}")
35
+ print(f"flash/__init__.py exists: {(_ROOT / '__init__.py').exists()}")
@@ -0,0 +1,39 @@
1
+ """Vendored mini-core for standalone operation.
2
+
3
+ These are local copies of physimx_core base classes.
4
+ They are ONLY used when physimx_core is not installed.
5
+
6
+ When physimx_core IS installed, flash/__init__.py will import from
7
+ physimx_core instead (see the smart import layer in flash/__init__.py).
8
+ """
9
+
10
+ from .interface import BaseSimulator
11
+ from .schema import (
12
+ CapabilityCard, InputVar, OutputVar, PhysicsDomain,
13
+ SimulationRequest, SimulationResult, SimulationStatus, SimulatorType,
14
+ )
15
+ # 从新的模块化 credentials 包导入
16
+ from .credentials import (
17
+ get_credential_manager,
18
+ get_user_name,
19
+ set_user_name,
20
+ mask_secret,
21
+ collect_ssh_accounts,
22
+ get_primary_ssh,
23
+ set_primary_ssh,
24
+ interactive_menu as credentials_menu,
25
+ )
26
+
27
+ __all__ = [
28
+ "BaseSimulator",
29
+ "CapabilityCard", "InputVar", "OutputVar", "PhysicsDomain",
30
+ "SimulationRequest", "SimulationResult", "SimulationStatus", "SimulatorType",
31
+ "get_credential_manager",
32
+ "get_user_name",
33
+ "set_user_name",
34
+ "mask_secret",
35
+ "collect_ssh_accounts",
36
+ "get_primary_ssh",
37
+ "set_primary_ssh",
38
+ "credentials_menu",
39
+ ]
@@ -0,0 +1,44 @@
1
+ """
2
+ Flash 凭据管理 -- 包导出
3
+ ============================
4
+ 导出核心函数,方便其他模块导入。
5
+ """
6
+
7
+ from ._core import (
8
+ get_credential_manager,
9
+ get_user_name,
10
+ set_user_name,
11
+ mask_secret,
12
+ # SSH 辅助
13
+ collect_ssh_accounts,
14
+ get_primary_ssh,
15
+ set_primary_ssh,
16
+ next_ssh_number,
17
+ ssh_account_name,
18
+ # 旧接口桥接
19
+ load_ssh_credentials,
20
+ load_all_ssh_credentials,
21
+ get_best_route,
22
+ )
23
+ # 导入统一管理菜单
24
+ from .manage import main as interactive_menu
25
+
26
+ # 版本
27
+ __version__ = "1.0.0"
28
+
29
+ # 公开 API
30
+ __all__ = [
31
+ "get_credential_manager",
32
+ "get_user_name",
33
+ "set_user_name",
34
+ "mask_secret",
35
+ "collect_ssh_accounts",
36
+ "get_primary_ssh",
37
+ "set_primary_ssh",
38
+ "next_ssh_number",
39
+ "ssh_account_name",
40
+ "load_ssh_credentials",
41
+ "load_all_ssh_credentials",
42
+ "get_best_route",
43
+ "interactive_menu",
44
+ ]
@@ -0,0 +1,191 @@
1
+ """
2
+ Flash 凭据管理 -- 配置定义
3
+ ============================
4
+ 定义所有凭据模板及其默认值。
5
+ """
6
+
7
+ from typing import Any, Dict, List, Tuple
8
+
9
+ # 字段类型: (key, label, default)
10
+ FieldDef = Tuple[str, str, Any]
11
+
12
+ # 凭据条目类型
13
+ EntryDef = Dict[str, Any]
14
+
15
+ # 默认值
16
+ DEFAULT_USER_NAME = "hello"
17
+ DEFAULT_PASSWORD = "123"
18
+
19
+ # ── 凭据条目定义 ──────────────────────────────────
20
+
21
+ # 所有 ParaCloud 可用线路 (host, port)
22
+ ROUTES_ALL: List[Dict[str, Any]] = [
23
+ {"host": "ssh.cn-zhongwei-1.paracloud.com", "port": 8443, "label": "中卫-1 :8443"},
24
+ {"host": "ssh.cn-hongkong-1.paracloud.com", "port": 22, "label": "香港-1 :22"},
25
+ {"host": "ssh.cn-zhongwei-1.paracloud.com", "port": 22, "label": "中卫-1 :22"},
26
+ {"host": "ssh.cn-zhongwei-1-v6.paracloud.com", "port": 22, "label": "中卫-1 v6 :22"},
27
+ {"host": "ssh.cn-zhongwei-1.paracloud.com", "port": 2222, "label": "中卫-1 :2222"},
28
+ {"host": "ssh.cn-zhongwei-1-v6.paracloud.com", "port": 2222, "label": "中卫-1 v6 :2222"},
29
+ {"host": "ssh.cn-zhongwei-cstnet.paracloud.com", "port": 22, "label": "中卫 cstnet :22"},
30
+ {"host": "ssh.cn-zhongwei-cstnet-v6.paracloud.com","port": 22, "label": "中卫 cstnet v6 :22"},
31
+ {"host": "ssh.paracloud.com", "port": 2222, "label": "paracloud :2222"},
32
+ ]
33
+
34
+ ENTRIES: List[EntryDef] = [
35
+ # FLASH SSH 账户(支持多个)
36
+ {
37
+ "name": "flash_ssh",
38
+ "title": "FLASH 超算 SSH #1 (ParaCloud 中卫 NC-E)",
39
+ "fields": [
40
+ ("connection_mode", "连接模式 [auto/manual]", "auto"),
41
+ ("password", "密码", "123"),
42
+ ],
43
+ "route_key": "scfa2696",
44
+ "manual_fields": [
45
+ ("host", "SSH 主机", "ssh.cn-zhongwei-1.paracloud.com"),
46
+ ("port", "SSH 端口", 22),
47
+ ("username", "用户名", "scfa2696@NC-E"),
48
+ ],
49
+ "routes": ROUTES_ALL,
50
+ },
51
+ {
52
+ "name": "flash_ssh_2",
53
+ "title": "FLASH 超算 SSH #2 (ParaCloud 中卫 BSCC-T6)",
54
+ "fields": [
55
+ ("connection_mode", "连接模式 [auto/manual]", "auto"),
56
+ ("password", "密码", "123"),
57
+ ],
58
+ "route_key": "sch0348",
59
+ "manual_fields": [
60
+ ("host", "SSH 主机", "ssh.cn-zhongwei-1.paracloud.com"),
61
+ ("port", "SSH 端口", 22),
62
+ ("username", "用户名", "sch0348@BSCC-T6"),
63
+ ],
64
+ "routes": ROUTES_ALL,
65
+ },
66
+
67
+ # Gitee 凭据
68
+ {
69
+ "name": "gitee",
70
+ "title": "Gitee 访问令牌",
71
+ "fields": [
72
+ ("token", "访问令牌 (Personal Access Token)", "123"),
73
+ ("username", "用户名", DEFAULT_USER_NAME),
74
+ ("repo_url", "仓库 URL (可选)", "https://gitee.com/physimx/flash.git"),
75
+ ],
76
+ },
77
+
78
+ # PyPI 发布令牌 (pypi.org / test.pypi.org)
79
+ {
80
+ "name": "pypi",
81
+ "title": "PyPI 发布令牌 (pypi.org)",
82
+ "fields": [
83
+ ("token", "PyPI API Token (pypi-xxx)", ""),
84
+ ("username", "用户名", "__token__"),
85
+ ],
86
+ },
87
+ {
88
+ "name": "testpypi",
89
+ "title": "TestPyPI 发布令牌 (test.pypi.org)",
90
+ "fields": [
91
+ ("token", "TestPyPI API Token (pypi-xxx)", ""),
92
+ ("username", "用户名", "__token__"),
93
+ ],
94
+ },
95
+
96
+ # API 密钥 -- AI 服务 (可扩展)
97
+ {
98
+ "name": "deepseek_api",
99
+ "title": "DeepSeek API (太极网关)",
100
+ "fields": [
101
+ ("base_url", "API 地址", "https://gateway.taichuai.cn/modelhub/api/v1"),
102
+ ("api_key", "API Key", "123"),
103
+ ],
104
+ },
105
+ # 可扩展:添加更多 AI API 凭据
106
+ # {
107
+ # "name": "openai_api",
108
+ # "title": "OpenAI API",
109
+ # "fields": [
110
+ # ("api_key", "API Key", "123"),
111
+ # ("base_url", "API 地址 (可选)", ""),
112
+ # ],
113
+ # },
114
+ # {
115
+ # "name": "claude_api",
116
+ # "title": "Claude API (Anthropic)",
117
+ # "fields": [
118
+ # ("api_key", "API Key", "123"),
119
+ # ("base_url", "API 地址 (可选)", ""),
120
+ # ],
121
+ # },
122
+ # {
123
+ # "name": "gemini_api",
124
+ # "title": "Gemini API (Google)",
125
+ # "fields": [
126
+ # ("api_key", "API Key", "123"),
127
+ # ],
128
+ # },
129
+ # {
130
+ # "name": "qwen_api",
131
+ # "title": "通义千问 API (阿里云)",
132
+ # "fields": [
133
+ # ("api_key", "API Key", "123"),
134
+ # ("base_url", "API 地址 (可选)", ""),
135
+ # ],
136
+ # },
137
+ ]
138
+
139
+ # 按名称索引
140
+ ENTRIES_BY_NAME: Dict[str, EntryDef] = {e["name"]: e for e in ENTRIES}
141
+
142
+
143
+ # ── SSH 预配置辅助 ──────────────────────────────────
144
+
145
+ def _get_field(entry: EntryDef, key: str, default=None):
146
+ """从 entry 的 fields/manual_fields 里取默认值。"""
147
+ for k, _, v in entry.get("fields", []):
148
+ if k == key:
149
+ return v
150
+ for k, _, v in entry.get("manual_fields", []):
151
+ if k == key:
152
+ return v
153
+ return default
154
+
155
+
156
+ def get_ssh_username(name: str) -> str:
157
+ """获取 SSH 账户的用户名 (从预配置读取, 未找到则返回 name 本身)。"""
158
+ entry = ENTRIES_BY_NAME.get(name)
159
+ if entry:
160
+ return _get_field(entry, "username", name)
161
+ return name
162
+
163
+
164
+ def get_ssh_routes(name: str) -> List[Dict[str, Any]]:
165
+ """获取 SSH 账户的线路列表 (从预配置读取)。"""
166
+ entry = ENTRIES_BY_NAME.get(name)
167
+ if not entry:
168
+ return []
169
+ # 优先使用 entry 的 routes 字段(多线路)
170
+ routes = entry.get("routes")
171
+ if routes:
172
+ return list(routes)
173
+ # fallback: 从 manual_fields 构建单条线路
174
+ host = _get_field(entry, "host")
175
+ port = _get_field(entry, "port", 22)
176
+ if not host:
177
+ return []
178
+ label = entry.get("title", f"{host}:{port}")
179
+ return [{"host": host, "port": int(port), "label": label}]
180
+
181
+
182
+ # PRECONFIGURED_SSH: 从 ENTRES 自动构建
183
+ PRECONFIGURED_SSH: List[Dict[str, Any]] = [
184
+ {
185
+ "name": e["name"],
186
+ "title": e["title"],
187
+ "ssh_username": get_ssh_username(e["name"]),
188
+ "routes": get_ssh_routes(e["name"]),
189
+ }
190
+ for e in ENTRIES if e["name"].startswith("flash_ssh")
191
+ ]