Skip to content

Commit 0018956

Browse files
source-archive: skip only complete captures on re-runs (#298)
ContentStore.lookup now treats a capture as a cache hit only when it has every expected format (html + markdown + screenshot; PDFs need only markdown). A partial capture (e.g. a failed screenshot encode) becomes a miss, so a re-run retries the missing format instead of skipping it forever.
1 parent f1d24c5 commit 0018956

2 files changed

Lines changed: 53 additions & 0 deletions

File tree

code_tests/unit_tests/test_agents_and_tools/test_source_archive/test_content_store.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,3 +159,36 @@ def test_different_content_not_aliased(tmp_path):
159159
b = store.store(_result("https://b.test/y", "<p>two different</p>"))
160160
assert b.capture.content_alias_of is None
161161
assert b.capture.html_key != a.capture.html_key
162+
163+
164+
def test_incomplete_capture_is_not_a_cache_hit(tmp_path):
165+
# A browser capture whose screenshot failed to encode (screenshot_key=None)
166+
# is not "done" — the next run should retry it to fill the missing format.
167+
store = _store(tmp_path, ttl_days=14)
168+
store.store(
169+
CaptureResult(
170+
url="https://a.test",
171+
final_url="https://a.test",
172+
status_code=200,
173+
html="<p>one</p>",
174+
markdown="md " * 50,
175+
screenshot=None,
176+
fetcher="cloakbrowser",
177+
)
178+
)
179+
assert store.lookup("https://a.test") is None
180+
181+
182+
def test_pdf_capture_without_screenshot_is_complete(tmp_path):
183+
# PDFs have no screenshot by nature, so markdown alone counts as complete.
184+
store = _store(tmp_path, ttl_days=14)
185+
store.store(
186+
CaptureResult(
187+
url="https://a.test/x.pdf",
188+
final_url="https://a.test/x.pdf",
189+
status_code=200,
190+
markdown="md " * 50,
191+
fetcher="pdf",
192+
)
193+
)
194+
assert store.lookup("https://a.test/x.pdf") is not None

forecasting_tools/agents_and_tools/source_archive/content_store.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,20 @@ def _parse_iso(ts: str) -> datetime:
6969
return dt
7070

7171

72+
def _capture_is_complete(cap: dict) -> bool:
73+
"""Whether a stored capture has every format we expect for its type.
74+
75+
A browser capture is complete only with html + markdown + screenshot; a PDF
76+
(which has no screenshot) only needs its markdown. Used by :meth:`lookup` so
77+
an incomplete capture is re-fetched rather than treated as already done.
78+
"""
79+
if (cap.get("fetcher") or "").lower() == "pdf":
80+
return bool(cap.get("markdown_key"))
81+
return bool(
82+
cap.get("html_key") and cap.get("markdown_key") and cap.get("screenshot_key")
83+
)
84+
85+
7286
class ContentStore:
7387
def __init__(self, blob_store: BlobStore, config: ArchiveConfig | None = None):
7488
self.blobs = blob_store
@@ -166,6 +180,12 @@ def lookup(self, url: str) -> StoredCapture | None:
166180
age = datetime.now(timezone.utc) - last_seen
167181
if age > timedelta(days=self.config.ttl_days):
168182
return None
183+
# Skip only a COMPLETE capture. A partial one (e.g. a failed screenshot
184+
# encode left screenshot_key=None) is treated as a miss so the next run
185+
# retries the missing format instead of skipping it forever. PDFs have no
186+
# screenshot by nature, so they only need their markdown.
187+
if not _capture_is_complete(latest):
188+
return None
169189
return StoredCapture.model_validate(latest)
170190

171191
def store(self, result: CaptureResult) -> StoreResult:

0 commit comments

Comments
 (0)