- callback_dispatcher: params 型別放寬支援 numeric - failover_alerter: alert TTL 修正 - model_version_tracker: 小調整 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
111 lines
3.6 KiB
Python
111 lines
3.6 KiB
Python
"""
|
||
AI Provider 版本追蹤器 — 每小時探測 5 Provider 並寫入 DB,偵測版本變更
|
||
|
||
職責:
|
||
- 排程呼叫 probe_all_providers()
|
||
- 與 DB 最後一筆比對,判斷 changed 旗標
|
||
- 寫入 AIProviderVersionHistory
|
||
- 若有 changed → 記錄 warning log(P3.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
|