Skip to content

Sources

A Source is a retrieval capability. Vector search is one strategy, not the strategy — the filesystem, CSV, and the web (WebSource) are equally first-class today, and embeddings are optional. The Source protocol (reactifact/sources.py) is intentionally small so this list grows by adding implementations, not by changing the abstraction — direct API, keyword, and SQL sources are on the roadmap, not yet shipped (see roadmap); don't reach for them until they land. Sources live in Context.resources.sources.

from reactifact import Context, RuntimeResources
from reactifact.sources import CSVSource, EmbeddingSource, FileSystemSource, WebSource

ctx = Context(
    resources=RuntimeResources(
        sources={
            "docs": FileSystemSource("./docs"),                 # keyword by default
            "catalog": CSVSource("data/"),                      # a directory of .csv files
            "rag": EmbeddingSource("data/", embedder=embedder),  # vector search needs an embedder
            "web": WebSource(["https://example.com/docs"]),     # pages are fetched lazily
        }
    )
)

SourceRef

The shared result of every search is a SourceRef — a ranked, scoped pointer:

class SourceRef(BaseModel):
    source_id: str         # which source produced it
    locator: str           # stable locator inside the source (path / URL / id)
    metadata: dict         # owner_id scoping, structured flag, extra context
    score: float | None    # rank/hit score, if the source scores
    title: str             # optional display title
    excerpt: str           # small preview snippet
    query_id: str          # the query that found it

SourceRef.stable_id() lets you build deterministic artifact ids (ref:{stable_id}:{owner_id} in fan_out_sources), so repeated searches are idempotent.

The four built-in sources

Source Purpose Notes
FileSystemSource keyword search over local files scorer optional; keyword overlap by default
EmbeddingSource vector search over an in-memory or prepared corpus needs an EmbeddingProvider
CSVSource query catalog/price rows, returns structured rows deterministic, no embeddings
WebSource live web: search + lazy page resolution fetches the promised docs on demand

Lazy materialization

WebSource (and remote sources generally) return references, not content. Resolving the actual document is explicit and lazy — the knowledge demo fetches only the pages it decides it needs, then links the matched document to the ref:

SourceRef --materialized_from--> TypedDoc

The materialize_doc recipe encodes exactly this flow.

Typical retrieval stages in a demo

query ──► fan_out_sources ──► SourceRefs (ranked, scoped to owner)
                                   │
                                   ▼  (select relevant + lazy resolve for web)
                              TypedDoc / row payloads
                                   │
                                   ▼
                            Evidence (extracted, scored)

Example: CSVSource for deterministic numbers

Catalog pricing never goes through an LLM. A CSV source returns exact rows; the estimate stage in the repair demo multiplies quantities × unit prices from the catalog, deterministically (§67):

catalog = CSVSource("data/price.csv", key="name")
rows = await catalog.asearch("штукатурка", limit=20)

You can point a CSVSource at any structured table and search it by keyword to get the rows you need for a calculation.