feat(p3.2+adr-100): Model Version Tracker + SLO 自治 + KB rot cleaner
Wave 8 P3.2 模型版本追蹤 + ADR-100 SLO 自我治理 + 配套: P3.2 — Model Version Tracking: - model_version_probe.py (268 行) — 探測 Ollama / OpenRouter 等 provider 的 model version - model_version_tracker.py (101 行) — 對齊 PG provider_version_history 表 - migrations/p3_2_provider_version_history.sql + rollback — 25 行 schema - db/models.py +32 行 — ProviderVersionHistory ORM ADR-100 — AI 自主化 SLO: - docs/adr/ADR-100-ai-autonomous-slo.md (167 行) — 飛輪 SLO 設計與閾值 - ops/monitoring/slo-rules.yml (254 行) — Prometheus SLO recording rules + alerts - ops/monitoring/tests/test_slo_rules.yaml (242 行) — promtool unit tests 整合修改: - main.py +72 行 — Lifespan 啟動 model_version_probe + KB rot cleaner schedule - gitea_webhook.py +45 行 — webhook 接收 model 版本變化通知 - ci_auto_repair.py / evidence_snapshot.py / pre_decision_investigator.py — 配合接線 新測試: - test_kb_rot_cleaner_schedule.py (120 行) — 9 tests pass - test_slo_rules.yaml — promtool 驗收 Tests: 9 passed (test_kb_rot_cleaner_schedule) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Multiple Engineers (P3.2 + ADR-100) <noreply@anthropic.com>
This commit is contained in:
101
apps/api/src/services/model_version_tracker.py
Normal file
101
apps/api/src/services/model_version_tracker.py
Normal file
@@ -0,0 +1,101 @@
|
||||
"""
|
||||
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),
|
||||
)
|
||||
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
|
||||
Reference in New Issue
Block a user