Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions src/tracer/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,17 +232,23 @@ def update(
existing_index = EmbeddingIndex.load(artifact_dir / "index")
X_combined = np.vstack([existing_index.embeddings, new_embeddings.astype(np.float32)])

# Load existing traces (we need to re-save combined)
# Load existing traces so we can re-save the combined set. fit() always
# writes all_traces.jsonl, so it should be present. If it is missing (e.g.
# it was deleted), the original records cannot be recovered from the index
# embeddings alone, and appending only the new records would leave the trace
# set out of sync with X_combined. Fail fast with an actionable message
# instead of surfacing a cryptic trace/embedding mismatch from fit().
existing_traces_path = artifact_dir / "all_traces.jsonl"
if existing_traces_path.exists():
from tracer.traces.loader import load_traces as _lt
existing_ds = _lt(existing_traces_path)
combined_records = existing_ds.records + new_ds.records
else:
# First update: reconstruct original traces from the initial fit
# by loading them from the original trace source if we can find it,
# or infer count from existing embeddings
combined_records = new_ds.records
if not existing_traces_path.exists():
raise FileNotFoundError(
f"{existing_traces_path} not found — cannot continue continual "
"learning. fit() writes this file; if it was removed, re-run "
"tracer.fit() on your full trace set to rebuild it before calling "
"update().")

from tracer.traces.loader import load_traces as _lt
existing_ds = _lt(existing_traces_path)
combined_records = existing_ds.records + new_ds.records

from tracer.types import TraceDataset
from tracer.traces.loader import save_traces
Expand Down
25 changes: 25 additions & 0 deletions tests/test_fit.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,30 @@ def test_update():
assert updated.manifest.n_traces == 300 # 200 + 100


def test_update_missing_all_traces_raises_clear_error():
"""If all_traces.jsonl is gone, update() should fail fast with a clear
message rather than a cryptic trace/embedding mismatch."""
from tracer.api import fit, update
with tempfile.TemporaryDirectory() as tmp:
path, X, _ = _make_traces(tmp, n=200)
artifact_dir = Path(tmp) / ".tracer"
fit(path, artifact_dir=artifact_dir)

# Simulate a user deleting the file AGENTS.md warns against removing.
(artifact_dir / "all_traces.jsonl").unlink()

new_path = Path(tmp) / "new_traces.jsonl"
rng = np.random.RandomState(99)
X_new = rng.randn(50, 32).astype(np.float32)
with new_path.open("w") as f:
for i in range(50):
f.write(json.dumps({"input": f"new {i}", "teacher": f"cls_{i % 4}"}) + "\n")
np.save(new_path.with_suffix(".npy"), X_new)

with pytest.raises(FileNotFoundError, match="all_traces.jsonl"):
update(new_path, artifact_dir=artifact_dir)


# ── Qualitative report ────────────────────────────────────────────────────────

def test_qualitative_report():
Expand Down Expand Up @@ -376,6 +400,7 @@ def test_public_exports():
test_fit_missing_embeddings_raises,
test_router_wrong_dim_raises,
test_update,
test_update_missing_all_traces_raises_clear_error,
test_qualitative_report,
test_qualitative_report_boundary_pairs,
test_report,
Expand Down
Loading