This commit is contained in:
@@ -50,6 +50,7 @@
|
||||
- `tests/test_pg_sync.py` 已改為 opt-in integration test:預設不再連 localhost PostgreSQL 或建立/刪除測試表,需 `RUN_PG_SYNC_INTEGRATION=1` 且提供 `POSTGRES_PASSWORD` 才執行。
|
||||
- `services/pg_sync_service.py` 是顯式 opt-in legacy CLI,不是生產自動同步路徑;`tests/test_pg_sync_contract.py` 已守住預設 OFF 與 runtime paths 不自動 import。
|
||||
- `qwen3:14b` 不是未使用 Ollama 模型:OpenClaw QA、NemoTron dispatch 與 LLM model router 仍有現役路徑;`tests/test_qwen3_runtime_usage.py` 已守住,不能只因體積大就三主機移除。
|
||||
- Ollama host env 已加白名單護欄:`OLLAMA_HOST*` / `EMBEDDING_HOST` 只接受 GCP-A、GCP-B、111 或 110 proxy,誤設 188/localhost 會回到核准主機。
|
||||
- `routes/price_comparison_routes.py` 的 MOMO crawler TODO 已接到既有 `services.momo_crawler.search_momo_products()`;未手動上傳 MOMO 商品時會自動抓 MOMO,再交給比價服務。
|
||||
- Telegram `momo:eig:<event_id>` callback 已在 `routes/openclaw_bot_routes.py` 與 `services/telegram_bot_service.py` 實作並有 webhook 測試覆蓋,不是未實作缺口。
|
||||
- Telegram `date_*` / `goal_*` 不是死 callback handler:按鈕先送 `await:*` 進入輸入等待狀態,使用者下一則文字才由 pending action 消費;`tests/test_openclaw_bot_menu_keyboards.py` 與 `tests/test_openclaw_bot_routes_webhook.py` 已覆蓋。
|
||||
|
||||
@@ -14,12 +14,43 @@ from dataclasses import dataclass
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Ollama 設定 - 支援環境變數覆蓋
|
||||
OLLAMA_HOST_PRIMARY = os.getenv('OLLAMA_HOST_PRIMARY', 'http://34.143.170.20:11434')
|
||||
OLLAMA_HOST_SECONDARY = os.getenv('OLLAMA_HOST_SECONDARY', 'http://34.21.145.224:11434')
|
||||
OLLAMA_HOST_FALLBACK = os.getenv('OLLAMA_HOST_FALLBACK', 'http://192.168.0.111:11434')
|
||||
# OLLAMA_HOST 優先使用舊環境變數(向下相容),若未設定則以 PRIMARY 為主
|
||||
OLLAMA_HOST = os.getenv('OLLAMA_HOST', OLLAMA_HOST_PRIMARY)
|
||||
APPROVED_OLLAMA_HOST_SUBSTRINGS = (
|
||||
'34.143.170.20:11434', # GCP-A / Primary
|
||||
'34.21.145.224:11434', # GCP-B / Secondary
|
||||
'192.168.0.111:11434', # 111 / final fallback
|
||||
'192.168.0.110:11435', # 110 proxy to GCP-A
|
||||
'192.168.0.110:11436', # 110 proxy to GCP-B
|
||||
)
|
||||
|
||||
|
||||
def is_approved_ollama_host(host: str) -> bool:
|
||||
"""只允許 ADR-028 指定的 Ollama 主機或 110 轉發端口。"""
|
||||
if not host:
|
||||
return False
|
||||
return any(approved in host for approved in APPROVED_OLLAMA_HOST_SUBSTRINGS)
|
||||
|
||||
|
||||
def approved_ollama_env(name: str, default: str = '') -> str:
|
||||
"""讀取 Ollama host env,拒絕非 GCP-A/GCP-B/111 的舊值或誤設值。"""
|
||||
value = os.getenv(name, '').strip()
|
||||
if not value:
|
||||
return default
|
||||
if is_approved_ollama_host(value):
|
||||
return value
|
||||
logger.warning(
|
||||
"[OllamaHost] 忽略未核准的 %s=%s;LLM 只能走 GCP-A/GCP-B/111",
|
||||
name,
|
||||
value,
|
||||
)
|
||||
return default
|
||||
|
||||
|
||||
# Ollama 設定 - 僅允許 GCP-A → GCP-B → 111 三主機
|
||||
OLLAMA_HOST_PRIMARY = approved_ollama_env('OLLAMA_HOST_PRIMARY', 'http://34.143.170.20:11434')
|
||||
OLLAMA_HOST_SECONDARY = approved_ollama_env('OLLAMA_HOST_SECONDARY', 'http://34.21.145.224:11434')
|
||||
OLLAMA_HOST_FALLBACK = approved_ollama_env('OLLAMA_HOST_FALLBACK', 'http://192.168.0.111:11434')
|
||||
# 舊 OLLAMA_HOST 只接受核准主機;否則回到 primary,由 resolve_ollama_host() 管控級聯
|
||||
OLLAMA_HOST = approved_ollama_env('OLLAMA_HOST', OLLAMA_HOST_PRIMARY)
|
||||
DEFAULT_MODEL = os.getenv('OLLAMA_MODEL', 'llama3.1:8b') # 較快速的模型
|
||||
TIMEOUT = int(os.getenv('OLLAMA_TIMEOUT', '120')) # 秒 - 2 分鐘
|
||||
COPY_TIMEOUT = int(os.getenv('OLLAMA_COPY_TIMEOUT', '180')) # 文案生成專用超時 - 3 分鐘
|
||||
@@ -768,7 +799,7 @@ class OllamaService:
|
||||
# HOTFIX 三主機 retry 鏈(與 generate() 同模式)
|
||||
attempted_hosts: List[str] = []
|
||||
for attempt in range(3):
|
||||
target_host = (os.getenv("EMBEDDING_HOST") or resolve_ollama_host()).rstrip("/")
|
||||
target_host = (approved_ollama_env("EMBEDDING_HOST") or resolve_ollama_host()).rstrip("/")
|
||||
if target_host in attempted_hosts:
|
||||
break # cache 還沒過期或同主機,避免無限迴圈
|
||||
attempted_hosts.append(target_host)
|
||||
|
||||
@@ -171,11 +171,21 @@ def test_mark_unhealthy_ignores_empty():
|
||||
# B1/B2 — config lazy getters
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
def test_get_ollama_host_uses_env_when_set(monkeypatch):
|
||||
monkeypatch.setenv('OLLAMA_HOST', 'http://override.example:11434')
|
||||
def test_get_ollama_host_uses_approved_env_when_set(monkeypatch):
|
||||
monkeypatch.setenv('OLLAMA_HOST', 'http://34.21.145.224:11434')
|
||||
import config
|
||||
importlib.reload(config) # 確保 env 變更生效
|
||||
assert config.get_ollama_host() == 'http://override.example:11434'
|
||||
assert config.get_ollama_host() == 'http://34.21.145.224:11434'
|
||||
|
||||
|
||||
def test_get_ollama_host_rejects_unapproved_env(monkeypatch):
|
||||
monkeypatch.setenv('OLLAMA_HOST', 'http://192.168.0.188:11434')
|
||||
fake_resp = MagicMock(status_code=200)
|
||||
with patch('services.ollama_service.requests.get', return_value=fake_resp):
|
||||
import config
|
||||
importlib.reload(config)
|
||||
host = config.get_ollama_host()
|
||||
assert host == 'http://34.143.170.20:11434'
|
||||
|
||||
|
||||
def test_get_ollama_host_falls_back_to_resolve_without_env(monkeypatch):
|
||||
@@ -191,17 +201,17 @@ def test_get_ollama_host_falls_back_to_resolve_without_env(monkeypatch):
|
||||
|
||||
|
||||
def test_get_embedding_host_prefers_env(monkeypatch):
|
||||
monkeypatch.setenv('EMBEDDING_HOST', 'http://embed.example:11434')
|
||||
monkeypatch.setenv('EMBEDDING_HOST', 'http://192.168.0.111:11434')
|
||||
import config
|
||||
importlib.reload(config)
|
||||
assert config.get_embedding_host() == 'http://embed.example:11434'
|
||||
assert config.get_embedding_host() == 'http://192.168.0.111:11434'
|
||||
|
||||
|
||||
def test_get_hermes_url_prefers_env(monkeypatch):
|
||||
monkeypatch.setenv('HERMES_URL', 'http://hermes.example:11434')
|
||||
monkeypatch.setenv('HERMES_URL', 'http://34.143.170.20:11434')
|
||||
import config
|
||||
importlib.reload(config)
|
||||
assert config.get_hermes_url() == 'http://hermes.example:11434'
|
||||
assert config.get_hermes_url() == 'http://34.143.170.20:11434'
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
Reference in New Issue
Block a user