|
4 | 4 |
|
5 | 5 | import difflib |
6 | 6 | import json |
| 7 | +import logging |
7 | 8 | from dataclasses import dataclass, field |
8 | 9 | from pathlib import Path |
9 | 10 |
|
10 | | -from synix.build.refs import synix_dir_for_build_dir |
11 | | -from synix.build.snapshot_view import SnapshotArtifactCache |
| 11 | +from synix.build.refs import RefStore, synix_dir_for_build_dir |
| 12 | +from synix.build.snapshot_view import SnapshotArtifactCache, SnapshotView |
12 | 13 | from synix.core.models import Artifact |
13 | 14 |
|
| 15 | +logger = logging.getLogger(__name__) |
| 16 | + |
14 | 17 |
|
15 | 18 | @dataclass |
16 | 19 | class ArtifactDiff: |
@@ -134,7 +137,52 @@ def diff_artifact_by_label( |
134 | 137 | return None |
135 | 138 | return diff_artifact(old_art, new_art) |
136 | 139 |
|
137 | | - # No previous build dir — check for version history |
| 140 | + # No previous build dir — try snapshot-era ref lookup. |
| 141 | + # Find the previous run ref whose OID differs from HEAD. |
| 142 | + try: |
| 143 | + ref_store = RefStore(synix_dir) |
| 144 | + head_oid = ref_store.read_ref("HEAD") |
| 145 | + if head_oid is not None: |
| 146 | + run_refs = ref_store.iter_refs("refs/runs") |
| 147 | + # Find runs whose OID differs from HEAD (i.e. previous runs) |
| 148 | + prev_ref: str | None = None |
| 149 | + # iter_refs returns sorted ascending; walk in reverse to find |
| 150 | + # the most recent run that is NOT the current HEAD. |
| 151 | + for ref_name, oid in reversed(run_refs): |
| 152 | + if oid != head_oid: |
| 153 | + prev_ref = ref_name |
| 154 | + break |
| 155 | + |
| 156 | + if prev_ref is not None: |
| 157 | + prev_view = SnapshotView.open(synix_dir, ref=prev_ref) |
| 158 | + try: |
| 159 | + prev_data = prev_view.get_artifact(label) |
| 160 | + except KeyError: |
| 161 | + prev_data = None |
| 162 | + |
| 163 | + if prev_data is not None: |
| 164 | + from datetime import datetime |
| 165 | + |
| 166 | + created_at_str = prev_data.get("metadata", {}).get("created_at") |
| 167 | + created_at = ( |
| 168 | + datetime.fromisoformat(created_at_str) if created_at_str else datetime.now() |
| 169 | + ) |
| 170 | + old_art = Artifact( |
| 171 | + label=prev_data["label"], |
| 172 | + artifact_type=prev_data["artifact_type"], |
| 173 | + artifact_id=prev_data["artifact_id"], |
| 174 | + input_ids=prev_data.get("input_ids", []), |
| 175 | + prompt_id=prev_data.get("prompt_id"), |
| 176 | + model_config=prev_data.get("model_config"), |
| 177 | + created_at=created_at, |
| 178 | + content=prev_data["content"], |
| 179 | + metadata=prev_data.get("metadata", {}), |
| 180 | + ) |
| 181 | + return diff_artifact(old_art, new_art) |
| 182 | + except (ValueError, FileNotFoundError, KeyError): |
| 183 | + logger.debug("Snapshot-era ref lookup failed for diff of %r", label, exc_info=True) |
| 184 | + |
| 185 | + # Legacy fallback: check for build/versions/<label> directories |
138 | 186 | versions_dir = Path(build_dir) / "versions" / label |
139 | 187 | if not versions_dir.exists(): |
140 | 188 | return None |
|
0 commit comments