Skip to content

Progress events and metrics

Every pipeline run collects metrics, and can report its progress as it goes.

Run metrics

pipeline.last_run_metrics returns a RunMetrics for the most recent run, however it ended:

from sci_etl_core import AsyncETLPipeline

pipeline = AsyncETLPipeline(..., usage_sources=[llm, embedder])
try:
    await pipeline.run(query="all:galaxy", total_limit=200, newest_first=True)
finally:
    metrics = pipeline.last_run_metrics
    print(
        f"{metrics.outcome}: {metrics.processed} processed, {metrics.irrelevant} irrelevant, "
        f"{metrics.failed} failed in {metrics.duration_seconds:.0f} s"
    )
Field Meaning
pages, listed Listing pages fetched and the entries they held
processed, irrelevant, deferred, failed, skipped Records by outcome; deferred records wait for the next run because of total_limit or a shutdown, and skipped records had no id
entities_exported Entities handed to the exporter
memory_faults Memory ingest faults that were logged without failing their record
duration_seconds Wall time of the run
token_usage Tokens the usage_sources used during this run, or None without sources
outcome completed, aborted, interrupted, cancelled, or failed

usage_sources takes any objects with a usage property, such as AsyncOpenAICompatibleClient, AsyncOpenAIEmbedder, or CachingLLMClient. Their usage before the run is subtracted, so a client shared across runs reports each run separately.

Progress events

Pass on_event to receive typed events from sci_etl_core.observability as the run progresses:

from sci_etl_core.observability import PageFinished, RecordFinished, RunFinished


def report(event):
    if isinstance(event, RecordFinished) and event.outcome == "failed":
        print(f"{event.record_id} failed after {event.duration_seconds:.1f} s: {event.error!r}")
    elif isinstance(event, PageFinished):
        print(f"page at {event.offset}: {event.metrics.processed} processed so far")
    elif isinstance(event, RunFinished):
        print(f"run {event.metrics.outcome}")


pipeline = AsyncETLPipeline(..., on_event=report)
Event When
RunStarted(query, start_index, total_limit, newest_first) Before the first listing request
PageFetched(offset, entries, new_records) A listing page arrived; new_records aren't processed yet
RecordFinished(record_id, title, outcome, duration_seconds, entities, error) A record left the pipeline for this run
PageFinished(offset, duration_seconds, metrics) Every record of a page finished; metrics is the run so far
RunFinished(metrics) The run ended, however it ended

The handler runs on the event loop, so keep it quick: hand slow work, such as a network call, to a queue. An exception it raises is logged as Event handler failed: ... and never stops the run. ETLPipeline takes the same arguments and exposes last_run_metrics too.