modified: Makefile modified: README.md deleted: aborist/search/__init__.py renamed: aborist/__init__.py -> arborist/__init__.py renamed: aborist/cli.py -> arborist/cli.py renamed: aborist/compress.py -> arborist/compress.py renamed: aborist/concepts/__init__.py -> arborist/concepts/__init__.py renamed: aborist/concepts/extract.py -> arborist/concepts/extract.py renamed: aborist/concepts/query.py -> arborist/concepts/query.py renamed: aborist/concepts/seed.py -> arborist/concepts/seed.py renamed: aborist/concepts/store.py -> arborist/concepts/store.py renamed: aborist/distill/__init__.py -> arborist/distill/__init__.py renamed: aborist/distill/base.py -> arborist/distill/base.py renamed: aborist/distill/first_sentence.py -> arborist/distill/first_sentence.py renamed: aborist/distill/runner.py -> arborist/distill/runner.py renamed: aborist/distill/tfidf.py -> arborist/distill/tfidf.py renamed: aborist/document.py -> arborist/document.py renamed: aborist/evict.py -> arborist/evict.py renamed: aborist/ingest.py -> arborist/ingest.py renamed: aborist/journal.py -> arborist/journal.py renamed: aborist/merkle.py -> arborist/merkle.py renamed: aborist/mesh/__init__.py -> arborist/mesh/__init__.py renamed: aborist/mesh/crypto.py -> arborist/mesh/crypto.py renamed: aborist/mesh/members.py -> arborist/mesh/members.py renamed: aborist/mesh/state.py -> arborist/mesh/state.py renamed: aborist/mesh/wire.py -> arborist/mesh/wire.py renamed: aborist/progress.py -> arborist/progress.py renamed: aborist/qa/__init__.py -> arborist/qa/__init__.py renamed: aborist/qa/client.py -> arborist/qa/client.py renamed: aborist/qa/concepts.py -> arborist/qa/concepts.py renamed: aborist/qa/dag.py -> arborist/qa/dag.py renamed: aborist/qa/evidence.py -> arborist/qa/evidence.py renamed: aborist/qa/frame.py -> arborist/qa/frame.py renamed: aborist/qa/inspect.py -> arborist/qa/inspect.py renamed: aborist/qa/keys.py -> arborist/qa/keys.py renamed: aborist/qa/metacognition.py -> arborist/qa/metacognition.py renamed: aborist/qa/model_profiles.py -> arborist/qa/model_profiles.py renamed: aborist/qa/parse_claims.py -> arborist/qa/parse_claims.py renamed: aborist/qa/prompts.py -> arborist/qa/prompts.py renamed: aborist/qa/quantifier.py -> arborist/qa/quantifier.py renamed: aborist/qa/quantifier_reminder.py -> arborist/qa/quantifier_reminder.py renamed: aborist/qa/query.py -> arborist/qa/query.py renamed: aborist/qa/repair.py -> arborist/qa/repair.py renamed: aborist/qa/retrieval_plan.py -> arborist/qa/retrieval_plan.py renamed: aborist/qa/runner.py -> arborist/qa/runner.py renamed: aborist/qa/soft_preflight.py -> arborist/qa/soft_preflight.py renamed: aborist/qa/verify.py -> arborist/qa/verify.py renamed: aborist/qa/warrant.py -> arborist/qa/warrant.py new file: arborist/search/__init__.py renamed: aborist/search/base.py -> arborist/search/base.py renamed: aborist/search/fts5.py -> arborist/search/fts5.py renamed: aborist/snapshot.py -> arborist/snapshot.py renamed: aborist/source.py -> arborist/source.py renamed: aborist/sources/__init__.py -> arborist/sources/__init__.py renamed: aborist/sources/crawler/__init__.py -> arborist/sources/crawler/__init__.py renamed: aborist/sources/crawler/async_web_fetcher.py -> arborist/sources/crawler/async_web_fetcher.py renamed: aborist/sources/crawler/bridge.py -> arborist/sources/crawler/bridge.py renamed: aborist/sources/crawler/web_fetch.py -> arborist/sources/crawler/web_fetch.py renamed: aborist/sources/grok.py -> arborist/sources/grok.py renamed: aborist/sources/html_page.py -> arborist/sources/html_page.py renamed: aborist/sources/providence.py -> arborist/sources/providence.py renamed: aborist/sources/vcs.py -> arborist/sources/vcs.py renamed: aborist/sources/wikipedia.py -> arborist/sources/wikipedia.py renamed: aborist/sources/wikipedia_xml.py -> arborist/sources/wikipedia_xml.py renamed: aborist/store.py -> arborist/store.py renamed: aborist/wikitext.py -> arborist/wikitext.py modified: pyproject.toml
86 lines
2.6 KiB
Python
86 lines
2.6 KiB
Python
"""Periodic stderr progress reporting for long-running ingests/distillation.
|
|
|
|
Stdlib only. Prints on a rate-limited schedule (default every 2 seconds) so
|
|
ingests of 100k+ docs give live feedback without spamming. Optional total
|
|
estimate produces a percent + ETA.
|
|
|
|
Multiple parallel shard processes will interleave their lines; each can be
|
|
given a `prefix` to disambiguate.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import time
|
|
from typing import IO
|
|
|
|
|
|
class Progress:
|
|
"""Rate-limited progress reporter to stderr (or any stream)."""
|
|
|
|
def __init__(
|
|
self,
|
|
*,
|
|
interval: float = 2.0,
|
|
total_estimate: int | None = None,
|
|
prefix: str = "",
|
|
stream: IO[str] = sys.stderr,
|
|
):
|
|
self.start = time.monotonic()
|
|
self.last_print = self.start
|
|
self.last_seen = 0
|
|
self.interval = interval
|
|
self.total_estimate = total_estimate
|
|
self.prefix = prefix
|
|
self.stream = stream
|
|
|
|
def tick(
|
|
self,
|
|
seen: int,
|
|
*,
|
|
inserted: int | None = None,
|
|
force: bool = False,
|
|
) -> None:
|
|
"""Maybe print a status line. Call after each batch flush."""
|
|
now = time.monotonic()
|
|
if not force and (now - self.last_print) < self.interval:
|
|
return
|
|
elapsed = now - self.start
|
|
delta_seen = max(0, seen - self.last_seen)
|
|
delta_t = max(now - self.last_print, 1e-6)
|
|
rate_now = delta_seen / delta_t
|
|
rate_avg = seen / elapsed if elapsed > 0 else 0.0
|
|
|
|
bits = [
|
|
f"{self.prefix}[{_format_elapsed(elapsed)}]",
|
|
f"{seen:>10,} seen",
|
|
]
|
|
if inserted is not None:
|
|
bits.append(f"{inserted:>9,} new")
|
|
bits.append(f"{rate_now:>5.0f} now")
|
|
bits.append(f"({rate_avg:>4.0f} avg) docs/s")
|
|
if self.total_estimate and rate_now > 0:
|
|
pct = 100.0 * min(1.0, seen / self.total_estimate)
|
|
remaining = max(0, self.total_estimate - seen)
|
|
eta = remaining / rate_now
|
|
bits.append(f"{pct:>5.1f}%")
|
|
bits.append(f"ETA {_format_elapsed(eta)}")
|
|
|
|
print(" " + " | ".join(bits), file=self.stream, flush=True)
|
|
self.last_print = now
|
|
self.last_seen = seen
|
|
|
|
def done(self, seen: int, *, inserted: int | None = None) -> None:
|
|
"""Print a final status line regardless of interval."""
|
|
self.tick(seen, inserted=inserted, force=True)
|
|
|
|
|
|
def _format_elapsed(secs: float) -> str:
|
|
secs = int(secs)
|
|
h, rem = divmod(secs, 3600)
|
|
m, s = divmod(rem, 60)
|
|
if h:
|
|
return f"{h}h{m:02d}m"
|
|
if m:
|
|
return f"{m:>2}m{s:02d}s"
|
|
return f" {s:>2}s"
|