diff --git a/apps/api/src/services/decision_manager.py b/apps/api/src/services/decision_manager.py index cf16fb08..e05e7705 100644 --- a/apps/api/src/services/decision_manager.py +++ b/apps/api/src/services/decision_manager.py @@ -110,6 +110,7 @@ async def _push_decision_to_telegram( confidence = proposal_data.get("confidence", 0.0) # 🔴 預設 0.0 表示未經 AI 分析 source = proposal_data.get("source", "unknown") ai_provider = proposal_data.get("provider", "") # 2026-03-29 ogt: AI 模型來源 + ai_model = proposal_data.get("model", "") # 2026-04-04 ogt: 底層模型名稱 # 2026-04-02 ogt: Phase 22 Nemotron 協作資料 nemotron_enabled = proposal_data.get("nemotron_enabled", False) @@ -132,6 +133,7 @@ async def _push_decision_to_telegram( confidence=confidence, namespace=incident.signals[0].labels.get("namespace", "default") if incident.signals else "default", ai_provider=ai_provider, # 2026-03-29 ogt: 顯示 AI 模型來源 + ai_model=ai_model, # 2026-04-04 ogt: 底層模型名稱 # 2026-04-02 ogt: Phase 22 Nemotron 協作 (ADR-044) nemotron_enabled=nemotron_enabled, nemotron_tools=nemotron_tools, diff --git a/apps/api/src/services/openclaw.py b/apps/api/src/services/openclaw.py index fcb1ce1c..54ed2792 100644 --- a/apps/api/src/services/openclaw.py +++ b/apps/api/src/services/openclaw.py @@ -1553,6 +1553,7 @@ Focus on: "signoz_correlation": result.signoz_correlation, "from_cache": from_cache, "provider": provider, + "model": self._get_model_name(provider), # 2026-04-04 ogt: 底層模型名稱 "ai_tokens": ai_tokens, "ai_cost": ai_cost, } diff --git a/apps/api/src/services/playbook_rag.py b/apps/api/src/services/playbook_rag.py index 723230f2..86b9059e 100644 --- a/apps/api/src/services/playbook_rag.py +++ b/apps/api/src/services/playbook_rag.py @@ -153,11 +153,24 @@ class PlaybookRAGService: # Embedding Operations # ========================================================================= + async def _get_http_client(self) -> httpx.AsyncClient: + """ + 取得有效的 HTTP Client,若已關閉則重建 + + 2026-04-04 ogt: 修復滾動重啟後 is_closed=True 導致 embedding 失敗 + """ + if self._http_client.is_closed: + logger.warning("playbook_rag_http_client_closed_rebuilding") + from src.core.http_client import get_general_client + self._http_client = await get_general_client() + return self._http_client + async def embed_text(self, text: str) -> list[float] | None: """ 使用 Ollama 生成文字 embedding 2026-03-27 ogt: 改用 DI 注入的 http_client (P1 違規修復) + 2026-04-04 ogt: 加入 is_closed 自動重建機制 Args: text: 要向量化的文字 @@ -166,8 +179,8 @@ class PlaybookRAGService: 向量 (768 維) 或 None (失敗時) """ try: - # 使用 DI 注入的 http_client,設置單次請求 timeout - response = await self._http_client.post( + client = await self._get_http_client() + response = await client.post( f"{self.ollama_url}/api/embeddings", json={ "model": self.embedding_model, @@ -545,7 +558,8 @@ async def get_playbook_rag_service() -> PlaybookRAGService: 2026-03-27 ogt: 改用 DI 注入,從 Lifespan 取得 http_client 和 Redis """ global _rag_service - if _rag_service is None: + # 2026-04-04 ogt: 滾動重啟後 http_client is_closed,需重建 singleton + if _rag_service is None or _rag_service._http_client.is_closed: # 延遲導入避免循環依賴 from src.core.http_client import get_general_client from src.core.redis_client import get_redis diff --git a/apps/api/src/services/telegram_gateway.py b/apps/api/src/services/telegram_gateway.py index d0c60c9f..a5e7a2c9 100644 --- a/apps/api/src/services/telegram_gateway.py +++ b/apps/api/src/services/telegram_gateway.py @@ -162,6 +162,8 @@ class TelegramMessage: anomaly_frequency: dict | None = None # AnomalyCounter 統計 # 2026-03-29 ogt: AI Provider 來源顯示 ai_provider: str = "" # ollama/gemini/claude/expert_system/mock + # 2026-04-04 ogt: 底層模型名稱 (e.g. qwen2.5:7b-instruct, nemotron-70b) + ai_model: str = "" # ========================================================================== # Phase 22: Nemotron 協作欄位 (ADR-044) @@ -250,18 +252,20 @@ class TelegramMessage: # 2026-03-29 ogt: 根據 confidence + ai_provider 動態顯示來源 # confidence > 0 = 真實 AI 分析, confidence == 0 = 規則匹配/降級 + # 2026-04-04 ogt: 加入 ai_model 顯示底層模型名稱 if self.confidence > 0 and self.ai_provider: - # 顯示具體 AI 模型 provider_names = { "ollama": "Ollama", "gemini": "Gemini", "claude": "Claude", + "nvidia": "Nemotron", "openclaw_nemo": "OpenClaw Nemo", "openclaw_nvidia_nim": "OpenClaw Nemo", "openclaw_qwen": "OpenClaw Nemo", } provider_display = provider_names.get(self.ai_provider.lower(), self.ai_provider.upper()) - source_label = f"🤖 {provider_display} 仲裁" + model_suffix = f" ({html.escape(self.ai_model)})" if self.ai_model else "" + source_label = f"🤖 {provider_display} 仲裁{model_suffix}" elif self.confidence > 0: source_label = "🤖 AI 仲裁判定" else: @@ -351,6 +355,7 @@ class TelegramMessage: safe_downtime = html.escape(self.estimated_downtime) # AI Provider 顯示 + # 2026-04-04 ogt: 加入 ai_model 顯示底層模型名稱 if self.confidence > 0 and self.ai_provider: provider_names = { "ollama": "Ollama", @@ -362,7 +367,8 @@ class TelegramMessage: "openclaw_qwen": "OpenClaw Nemo", } provider_display = provider_names.get(self.ai_provider.lower(), self.ai_provider.upper()) - source_label = f"🤖 {provider_display} 仲裁" + model_suffix = f" ({html.escape(self.ai_model)})" if self.ai_model else "" + source_label = f"🤖 {provider_display} 仲裁{model_suffix}" elif self.confidence > 0: source_label = "🤖 OpenClaw 仲裁" else: @@ -1268,6 +1274,8 @@ class TelegramGateway: anomaly_frequency: dict | None = None, # 2026-03-29 ogt: AI Provider 來源顯示 ai_provider: str = "", + # 2026-04-04 ogt: 底層模型名稱 + ai_model: str = "", # 2026-04-02 ogt: Phase 22 Nemotron 協作 (ADR-044) nemotron_enabled: bool = False, nemotron_tools: list[dict] | None = None, @@ -1335,6 +1343,8 @@ class TelegramGateway: anomaly_frequency=anomaly_frequency, # 2026-03-29 ogt: AI Provider 來源顯示 ai_provider=ai_provider, + # 2026-04-04 ogt: 底層模型名稱 + ai_model=ai_model, # 2026-04-02 ogt: Phase 22 Nemotron 協作 (ADR-044) nemotron_enabled=nemotron_enabled, nemotron_tools=nemotron_tools,