Files
awoooi/apps/api/src/services/model_version_tracker.py
Your Name ae5e33d254 feat(failover+dispatcher): 補齊 unstaged 服務變更
- callback_dispatcher: params 型別放寬支援 numeric
- failover_alerter: alert TTL 修正
- model_version_tracker: 小調整

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:56:51 +08:00

111 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
AI Provider 版本追蹤器 — 每小時探測 5 Provider 並寫入 DB偵測版本變更
職責:
- 排程呼叫 probe_all_providers()
- 與 DB 最後一筆比對,判斷 changed 旗標
- 寫入 AIProviderVersionHistory
- 若有 changed → 記錄 warning logP3.2.3 alerter 後續整合)
# 2026-04-27 P3.2.2 by Claude
"""
from __future__ import annotations
import asyncio
import structlog
logger = structlog.get_logger(__name__)
class ModelVersionTracker:
"""每小時探測所有 AI Provider 版本並寫入 DB"""
async def run_probe_cycle(self) -> dict:
"""執行一輪探測probe → 比對上一筆 → 寫入 DB
Returns:
dict with keys:
- probed : int — 成功探測的 provider 數
- changed : list[str] — 版本有變更的 provider names
"""
from src.db.base import get_db_context
from src.db.models import AIProviderVersionHistory
from src.services.model_version_probe import probe_all_providers
from sqlalchemy import desc, select
results = await probe_all_providers()
changed_providers: list[str] = []
async with get_db_context() as db:
for info in results:
# 取最近一筆比對
stmt = (
select(AIProviderVersionHistory)
.where(AIProviderVersionHistory.provider == info.provider)
.order_by(desc(AIProviderVersionHistory.captured_at))
.limit(1)
)
last = (await db.execute(stmt)).scalar_one_or_none()
changed = (
last is None
or last.version != info.version
or last.digest != info.digest
)
if changed:
changed_providers.append(info.provider)
db.add(
AIProviderVersionHistory(
provider=info.provider,
model=info.model,
version=info.version,
digest=info.digest,
captured_at=info.captured_at,
prev_version=last.version if last else None,
changed=changed,
)
)
await db.commit()
if changed_providers:
logger.warning(
"provider_version_changed",
changed=changed_providers,
total_probed=len(results),
)
# P3.2.3: Telegram 告警dedup 1h/provider
try:
from src.services.failover_alerter import get_failover_alerter
await get_failover_alerter().alert_provider_version_changed(
changed_providers=changed_providers,
probed=len(results),
)
except Exception as _alert_err:
logger.warning("provider_version_alert_failed", error=str(_alert_err))
else:
logger.info(
"provider_version_stable",
total_probed=len(results),
)
return {"probed": len(results), "changed": changed_providers}
# =============================================================================
# Singleton
# =============================================================================
_tracker: ModelVersionTracker | None = None
def get_model_version_tracker() -> ModelVersionTracker:
"""取得 ModelVersionTracker singleton"""
global _tracker
if _tracker is None:
_tracker = ModelVersionTracker()
return _tracker