守住 runtime 環境變數文件契約

This commit is contained in:
OoO
2026-05-13 12:59:29 +08:00
parent 2b2233d385
commit acef0fa6ff
2 changed files with 79 additions and 0 deletions

View File

@@ -272,11 +272,15 @@ MOMO_AI_AUTOMATION_SMOKE_HISTORY_LIMIT=200
# [選填] OpenClaw Telegram bot
OPENCLAW_BOT_TOKEN=your_openclaw_bot_token_here
TELEGRAM_BOT_USERNAME=@OpenClawAwoooI_Bot
OPENCLAW_BOT_USERNAME=@OpenClawAwoooI_Bot
OPENCLAW_GROUP_ID=-1003940688311
OPENCLAW_ALLOWED_USERS=
OPENCLAW_ADMIN_USER_IDS=
# [預設 1] 舊行為:空白名單仍允許私訊;正式環境建議設 0 並填 OPENCLAW_ALLOWED_USERS
OPENCLAW_ALLOW_PRIVATE_WITHOUT_WHITELIST=1
# [預設 24] PPT 報表快取保留時間(小時)
OPENCLAW_PPT_CACHE_TTL_HOURS=24
# [預設 OFF] ADR-019 agent dispatch啟用後只讓白名單 cmd 轉 NL agent 處理
OPENCLAW_AGENT_DISPATCH=0
OPENCLAW_AGENT_DISPATCH_CMDS=sales,top,vendor
@@ -287,15 +291,30 @@ YOUTUBE_API_KEY=
GEMINI_TIMEOUT=60
# [預設 OFF] AI runtime feature flags未完成部署驗收前不要在正式環境打開
AI_CALL_LOGGING_ENABLED=true
MODEL_ROUTER_ENABLED=false
COST_THROTTLE_ENABLED=false
COST_THROTTLE_PROJECT_RATIO=1.10
COST_UNTHROTTLE_PROJECT_RATIO=0.95
RAG_ENABLED=false
RAG_DEFAULT_THRESHOLD=0.85
RAG_DEFAULT_TOP_K=5
RAG_EMBED_MODEL=bge-m3:latest
RAG_EMBED_DIM=1024
RAG_EMBED_NORMALIZE=true
PPT_VISION_ENABLED=false
PPT_VISION_MODEL=minicpm-v:latest
PPT_VISION_TIMEOUT=60
DEEPSEEK_DIRECT_ENABLED=false
DEEPSEEK_API_KEY=
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
DEEPSEEK_MODEL=deepseek-chat
DEEPSEEK_TIMEOUT=60
OPENCLAW_DAILY_HERMES_TEMPLATE=true
OPENCLAW_OLLAMA_MODEL=qwen2.5-coder:7b
PROMOTION_PENDING_BATCH_SIZE=50
AWAITING_REVIEW_PUSH_BATCH=5
TELEGRAM_ADMIN_CHAT_ID=
# ──────────────────────────────────────────────────────────────────────────
# Ollama / MCP / 密碼政策
@@ -315,6 +334,8 @@ OPENCLAW_QA_OLLAMA_FIRST=true
OPENCLAW_QA_OLLAMA_MODEL=qwen3:14b
OPENCLAW_QA_OLLAMA_HOST=http://34.143.170.20:11434
OPENCLAW_QA_OLLAMA_TIMEOUT=60
NEMOTRON_OLLAMA_FIRST=true
NEMOTRON_OLLAMA_MODEL=qwen3:14b
NEMOTRON_OLLAMA_TIMEOUT=180
# [預設 OFF] MCP Router需先部署 docker-compose.mcp.yml 並完成健康檢查再開
@@ -323,6 +344,9 @@ MCP_POSTGRES_URL=http://127.0.0.1:3001
MCP_FIRECRAWL_URL=http://127.0.0.1:3002
MCP_OMNISEARCH_URL=http://127.0.0.1:3003
MCP_FILESYSTEM_URL=http://127.0.0.1:3004
MCP_TIMEOUT_SEC=30
MCP_CACHE_TTL_SEC=3600
MCP_MAX_RESULT_BYTES=65536
MCP_CACHE_TTL_HOURS=24
MCP_GEMINI_MODEL=gemini-2.0-flash

View File

@@ -16,6 +16,29 @@ def _env_example_keys() -> set[str]:
return keys
def _runtime_env_keys_from_code() -> set[str]:
pattern = re.compile(r"""os\.(?:getenv|environ\.get)\(\s*['"]([A-Z][A-Z0-9_]+)['"]""")
scan_paths = [
ROOT / "app.py",
ROOT / "config.py",
ROOT / "scheduler.py",
ROOT / "run_scheduler.py",
ROOT / "routes",
ROOT / "services",
ROOT / "utils",
]
keys = set()
for scan_path in scan_paths:
paths = scan_path.rglob("*.py") if scan_path.is_dir() else [scan_path]
for path in paths:
if not path.exists() or "__pycache__" in path.parts:
continue
content = path.read_text(encoding="utf-8", errors="ignore")
keys.update(match.group(1) for match in pattern.finditer(content))
return keys
def test_phase3f_orphan_ai_services_stay_removed():
orphan_services = [
"services/elephant_alpha_decision_router.py",
@@ -44,9 +67,13 @@ def test_active_guides_do_not_point_to_removed_ai_services():
def test_env_example_documents_runtime_and_ai_automation_variables():
expected_keys = {
"AI_CALL_LOGGING_ENABLED",
"AUTO_FIX_ENABLED",
"AWAITING_REVIEW_PUSH_BATCH",
"CODE_REVIEW_AUTO_FIX_ENABLED",
"COST_THROTTLE_ENABLED",
"COST_THROTTLE_PROJECT_RATIO",
"COST_UNTHROTTLE_PROJECT_RATIO",
"DEEPSEEK_API_KEY",
"DEEPSEEK_BASE_URL",
"DEEPSEEK_DIRECT_ENABLED",
@@ -69,26 +96,45 @@ def test_env_example_documents_runtime_and_ai_automation_variables():
"MOMO_EVENT_ROUTER_REPLAY_ON_SUCCESS",
"MCP_FILESYSTEM_URL",
"MCP_FIRECRAWL_URL",
"MCP_CACHE_TTL_SEC",
"MCP_MAX_RESULT_BYTES",
"MCP_OMNISEARCH_URL",
"MCP_POSTGRES_URL",
"MCP_ROUTER_ENABLED",
"MCP_TIMEOUT_SEC",
"MODEL_ROUTER_ENABLED",
"N8N_HOST",
"N8N_PASSWORD",
"N8N_PROTOCOL",
"N8N_USER",
"N8N_WEBHOOK_BASE_URL",
"NEMOTRON_OLLAMA_FIRST",
"NEMOTRON_OLLAMA_MODEL",
"NEMOTRON_OLLAMA_TIMEOUT",
"OLLAMA_EMBED_TIMEOUT",
"OPENCLAW_ADMIN_USER_IDS",
"OPENCLAW_AGENT_DISPATCH",
"OPENCLAW_AGENT_DISPATCH_CMDS",
"OPENCLAW_ALLOW_PRIVATE_WITHOUT_WHITELIST",
"OPENCLAW_DAILY_HERMES_TEMPLATE",
"OPENCLAW_OLLAMA_MODEL",
"OPENCLAW_PPT_CACHE_TTL_HOURS",
"OPENCLAW_QA_OLLAMA_FIRST",
"OPENCLAW_QA_OLLAMA_HOST",
"OPENCLAW_QA_OLLAMA_MODEL",
"OPENCLAW_QA_OLLAMA_TIMEOUT",
"PPT_VISION_ENABLED",
"PPT_VISION_MODEL",
"PPT_VISION_TIMEOUT",
"PROMOTION_PENDING_BATCH_SIZE",
"RAG_ENABLED",
"RAG_DEFAULT_THRESHOLD",
"RAG_DEFAULT_TOP_K",
"RAG_EMBED_DIM",
"RAG_EMBED_MODEL",
"RAG_EMBED_NORMALIZE",
"TELEGRAM_ADMIN_CHAT_ID",
"TELEGRAM_BOT_USERNAME",
"TELEGRAM_CHAT_ID",
"WEB_CONCURRENCY",
}
@@ -96,6 +142,15 @@ def test_env_example_documents_runtime_and_ai_automation_variables():
assert expected_keys <= _env_example_keys()
def test_env_example_documents_runtime_os_env_keys():
internal_runtime_keys = {
"MOMO_ALLOW_INSECURE_CONFIG_FOR_TESTS",
"PYTEST_CURRENT_TEST",
}
assert _runtime_env_keys_from_code() - internal_runtime_keys <= _env_example_keys()
def test_scheduler_does_not_silently_swallow_exceptions():
scheduler_source = (ROOT / "scheduler.py").read_text(encoding="utf-8")