diff --git a/apps/api/src/services/dynamic_baseline_service.py b/apps/api/src/services/dynamic_baseline_service.py index 669a5472..4cb110df 100644 --- a/apps/api/src/services/dynamic_baseline_service.py +++ b/apps/api/src/services/dynamic_baseline_service.py @@ -71,7 +71,7 @@ class BaselineState: } @classmethod - def from_dict(cls, d: dict[str, Any]) -> "BaselineState": + def from_dict(cls, d: dict[str, Any]) -> BaselineState: return cls( metric_name=d["metric_name"], mean=d["mean"], @@ -250,6 +250,7 @@ class DynamicBaselineService: ) -> list[MetricDatapoint]: """從 Prometheus query_range API 抓取歷史資料(1h 步進)。""" import httpx + from src.core.config import settings end_ts = now_taipei().timestamp() @@ -314,7 +315,7 @@ class DynamicBaselineService: seasonal="add" if len(arr) >= seasonal_periods * 2 else None, seasonal_periods=seasonal_periods, initialization_method="estimated", - ).fit(optimized=True, disp=False) + ).fit(optimized=True) fitted = model.fittedvalues residuals = arr - fitted @@ -447,6 +448,7 @@ class DynamicBaselineService: """從 PostgreSQL 載入最新一筆基線記錄""" try: from sqlalchemy import select + from src.db.base import get_session_factory from src.db.models import DynamicBaselineRecord diff --git a/apps/api/tests/test_dynamic_baseline_service.py b/apps/api/tests/test_dynamic_baseline_service.py new file mode 100644 index 00000000..06adcacb --- /dev/null +++ b/apps/api/tests/test_dynamic_baseline_service.py @@ -0,0 +1,9 @@ +from pathlib import Path + + +def test_holt_winters_fit_does_not_use_removed_disp_argument() -> None: + repo_root = Path(__file__).resolve().parents[3] + source = (repo_root / "apps/api/src/services/dynamic_baseline_service.py").read_text() + + assert ".fit(optimized=True, disp=False)" not in source + assert ".fit(optimized=True)" in source diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index eb18805c..5356c79c 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,19 @@ +## 2026-05-06 | 告警路徑 Ollama 實證與動態基線 statsmodels 相容修正 + +**背景**:188 Ollama 退場後,需確認告警主鏈是否仍實際 fallback 到 Gemini;同時 production log 持續出現 `holt_winters_failed_fallback_to_stats`,讓動態基線訓練一直降級成滑動統計。 + +**本次查證與修補**: +- production 近 30 分鐘 log 未看到真正 `provider=gemini` 的成功呼叫;告警路徑顯示 `ollama_gcp_a → ollama_gcp_b → ollama_local → gemini`,且 `ai_router_execute_success` 為 `ollama_gcp_a`。 +- 從 `awoooi-prod` Pod 內確認 GCP-A / GCP-B / 111 都有 `gemma3:4b`,且 `/api/generate` 可回 `OK`。 +- 移除 `DynamicBaselineService` 裡已被新版 statsmodels 移除的 `fit(..., disp=False)` 參數,避免 Holt-Winters 訓練固定 fallback。 +- 新增回歸測試,防止過期 `disp` 參數被加回。 + +**驗證**: +- GCP-A `gemma3:4b` generate:約 0.99s。 +- GCP-B `gemma3:4b` generate:約 3.4s。 +- 111 `gemma3:4b` generate:約 0.41s。 +- `provider=gemini` 精準 grep:近 30 分鐘無命中。 + ## 2026-05-06 | 188 Ollama gateway 暴露確認並永久綁定 localhost **背景**:統帥確認沒有 `192.168.0.88` 這台主機;重新盤點後發現 `.88` 是 188 的 default gateway,Ollama journal 裡的 `.88` 來源不是正常依賴,而是 gateway / NAT / port-forward / hairpin 入口。