diff --git a/apps/api/src/services/ollama_auto_recovery.py b/apps/api/src/services/ollama_auto_recovery.py index 9b3dc4d8..167eeb39 100644 --- a/apps/api/src/services/ollama_auto_recovery.py +++ b/apps/api/src/services/ollama_auto_recovery.py @@ -150,8 +150,8 @@ class OllamaAutoRecoveryService: current_primary=self._current_primary, ) - # 若 primary 不是 ollama,立刻評估一次(不等 30s interval) - if self._current_primary != "ollama": + # 若 primary 不是 Ollama,立刻評估一次(不等 30s interval) + if not self._is_ollama_primary(self._current_primary): try: await self._check_and_recover() except Exception: @@ -217,9 +217,9 @@ class OllamaAutoRecoveryService: # Redis 持久化(跨重啟恢復) await self._persist_primary(provider) - # 2026-05-04 ogt: B5/B6 修復 — 判斷改用 startswith("ollama_") + # 2026-05-04 ogt: B5/B6 修復 — 判斷改用 _is_ollama_primary() # 原設計:provider != "ollama",但 callback 傳 "ollama_gcp_a" → 永遠觸發 tracking - if not provider.startswith("ollama_"): + if not self._is_ollama_primary(provider): # 切換到非 Ollama(gemini/nemotron/claude)→ 重置 counter,開始監控恢復 self._consecutive_healthy = 0 logger.info( @@ -302,6 +302,11 @@ class OllamaAutoRecoveryService: def is_running(self) -> bool: return self._task is not None and not self._task.done() + @staticmethod + def _is_ollama_primary(provider: str) -> bool: + """Return true for both legacy "ollama" and ADR-110 provider names.""" + return provider == "ollama" or provider.startswith("ollama_") + # ------------------------------------------------------------------------- # 背景監控迴圈 # ------------------------------------------------------------------------- @@ -359,7 +364,7 @@ class OllamaAutoRecoveryService: if ( self._consecutive_healthy >= self._stable_count_required - and not self._current_primary.startswith("ollama_") + and not self._is_ollama_primary(self._current_primary) ): await self._switch_back_to_ollama() else: diff --git a/apps/api/tests/test_ollama_auto_recovery.py b/apps/api/tests/test_ollama_auto_recovery.py index 72055ac0..49ad649a 100644 --- a/apps/api/tests/test_ollama_auto_recovery.py +++ b/apps/api/tests/test_ollama_auto_recovery.py @@ -22,7 +22,7 @@ OllamaAutoRecoveryService 單元測試 - P1.1d from __future__ import annotations import asyncio -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import AsyncMock, MagicMock, call, patch import pytest @@ -451,7 +451,7 @@ class TestRedisPersistence: @pytest.mark.asyncio async def test_set_current_primary_persists_to_redis(self): - """set_current_primary("gemini") → 寫 Redis ollama:current_primary""" + """set_current_primary("gemini") → Phase A 雙寫新舊 Redis key""" svc, _, _ = _make_service(current_primary="ollama") mock_redis = AsyncMock() mock_redis.set = AsyncMock() @@ -459,7 +459,12 @@ class TestRedisPersistence: with patch("src.core.redis_client.get_redis", return_value=mock_redis): await svc.set_current_primary("gemini") - mock_redis.set.assert_awaited_once_with("ollama:current_primary", "gemini") + mock_redis.set.assert_has_awaits( + [ + call("platform:ollama:current_primary", "gemini"), + call("ollama:current_primary", "gemini"), + ] + ) @pytest.mark.asyncio async def test_set_current_primary_same_value_no_redis_write(self):