AgentEvalTool/backend/agenteval/intelligent_eval/config_snapshot.py
sinohqb e6f98aaa6d feat(intelligent-eval): implement config snapshot management (ticket 05)
- Add config_snapshot.py with save/list/get/compare functions
- Auto-save snapshots on eval creation and plan submission
- Implement snapshot query APIs (list, get single)
- Implement snapshot comparison API (diff two snapshots)
- Add 8 unit tests and 7 integration tests

Snapshots track config changes over time (created/plan_submitted/config_updated).
All 813 tests passing.
2026-08-12 10:19:07 +08:00

110 lines
3.1 KiB
Python

"""Config snapshot management for intelligent evaluations (配置快照管理).
Automatically saves config snapshots when:
- Eval is created (snapshot_type: created)
- Plan is submitted (snapshot_type: plan_submitted)
- Config is updated (snapshot_type: config_updated)
"""
from typing import Any, Optional
from sqlmodel import Session, select
from agenteval.storage.db import IntelligentEvalConfigSnapshotDB, IntelligentEvalDB
def save_snapshot(
eval_db: IntelligentEvalDB,
snapshot_type: str,
created_by: str,
session: Session,
) -> IntelligentEvalConfigSnapshotDB:
"""Save a config snapshot.
Args:
eval_db: Evaluation database record
snapshot_type: Type of snapshot (created / plan_submitted / config_updated)
created_by: Who created the snapshot (user / openclaw)
session: Database session
Returns:
Created snapshot
"""
snapshot = IntelligentEvalConfigSnapshotDB(
eval_id=eval_db.id,
snapshot_type=snapshot_type,
goal=eval_db.goal,
seeds=eval_db.seeds,
intent=eval_db.intent,
role_description=eval_db.role_description,
time_window_hours=eval_db.time_window_hours,
plan=eval_db.plan,
created_by=created_by,
)
session.add(snapshot)
session.commit()
session.refresh(snapshot)
return snapshot
def list_snapshots(eval_id: str, session: Session) -> list[IntelligentEvalConfigSnapshotDB]:
"""List all snapshots for an evaluation.
Returns:
List of snapshots, ordered by created_at descending (newest first)
"""
snapshots = session.exec(
select(IntelligentEvalConfigSnapshotDB)
.where(IntelligentEvalConfigSnapshotDB.eval_id == eval_id)
.order_by(IntelligentEvalConfigSnapshotDB.created_at.desc())
).all()
return list(snapshots)
def get_snapshot(snapshot_id: str, session: Session) -> Optional[IntelligentEvalConfigSnapshotDB]:
"""Get a single snapshot by ID.
Returns:
Snapshot, or None if not found
"""
return session.get(IntelligentEvalConfigSnapshotDB, snapshot_id)
def compare_snapshots(
snapshot1: IntelligentEvalConfigSnapshotDB,
snapshot2: IntelligentEvalConfigSnapshotDB,
) -> dict[str, Any]:
"""Compare two snapshots and return differences.
Returns:
Dict with differences, format:
{
"field_name": {
"old": value1,
"new": value2,
}
}
"""
diffs = {}
# Compare simple fields
fields = ["goal", "intent", "role_description", "time_window_hours"]
for field in fields:
val1 = getattr(snapshot1, field)
val2 = getattr(snapshot2, field)
if val1 != val2:
diffs[field] = {"old": val1, "new": val2}
# Compare JSON fields
seeds1 = snapshot1.get_seeds()
seeds2 = snapshot2.get_seeds()
if seeds1 != seeds2:
diffs["seeds"] = {"old": seeds1, "new": seeds2}
plan1 = snapshot1.get_plan()
plan2 = snapshot2.get_plan()
if plan1 != plan2:
diffs["plan"] = {"old": plan1, "new": plan2}
return diffs