Source code for fastrag.ingestion.logger.source

"""Inspect fetched source bytes without losing binary content."""

from __future__ import annotations

import base64
from typing import Any

from fastrag.ingestion.events import DocumentFetched, PipelineEvent
from fastrag.ingestion.logger._inspection import _InspectionLogger


[docs] class SourceLogger(_InspectionLogger): """Display unique fetched sources with complete metadata and content. Accepts ``pipeline``, ``format="rich"`` or ``"jsonl"``, ``stream``, ``configuration`` and optional ``stages``. UTF-8 content is displayed as literal text; other content is displayed as base64. JSON Lines always includes ``content_base64`` for exact byte recovery. ``source_count`` counts unique source IDs, deduplicating events from Sources aggregates. Counters reset on each run; failures propagate after a partial summary. Set ``content_lines=N`` to show only the first and last N text lines, separated by an omitted-line marker. Short content is shown in full. The default ``None`` shows everything. This applies to Rich and JSONL text output; metadata, base64 bytes and size statistics remain complete. ``content_chars=N`` additionally caps preview text at N characters, useful for minified HTML with very few lines. Both limits default to ``None``. ``metadata_only=True`` omits text, ``content_base64`` and embeddings, keeping metadata and size statistics. Non-text content is trimmed to a short base64 preview (``content_truncated``) unless ``show_binary=True``. """ # content_lines limits displayed text; statistics always use full content. label = "Source inspection" def _records(self, event: PipelineEvent) -> list[dict[str, Any]]: if not isinstance(event, DocumentFetched): return [] source = event.source try: text = source.content.decode("utf-8") except UnicodeDecodeError: text = None return [ { "type": "source", "id": source.id, "source_id": source.id, "uri": source.uri, "content_type": source.content_type, "content_hash": source.content_hash, "etag": source.etag, "last_modified": source.last_modified, "last_validated": source.last_validated, "step": event.step, "event_name": event.event_name, "cached": source.cached, "metadata": source.metadata, "length_bytes": len(source.content), "length": len(text) if text is not None else 0, "word_count": len(text.split()) if text is not None else 0, "line_count": len(text.splitlines()) if text is not None else 0, "text": text, "content_base64": base64.b64encode(source.content).decode("ascii"), } ]