Skip to content

API reference

Cairn is a library as well as a command. Everything the CLI and the service do goes through Pipeline, so a script behaves the same as a command.

Pipeline

from cairn.core.config import load_config
from cairn.pipeline import Pipeline

cfg = load_config("configs/default.yaml")
pipeline = Pipeline.from_config(cfg)

manifest = pipeline.ingest()          # build the index, return the manifest
answer = pipeline.answer("How many days to request a credit?")

print(answer.status.value, answer.confidence)
for claim in answer.claims:
    print(claim.supported, claim.text)
    for citation in claim.citations:
        print(f"  {citation.quote!r}")
Method Returns Notes
Pipeline.from_config(cfg) Pipeline builds every component the configuration names
ingest() IndexManifest parses, chunks, embeds and writes the index atomically
load_index() IndexManifest raises ManifestMismatch when the configuration and the index disagree
retrieve(question, k=None) list[ScoredChunk] k defaults to retrieval.k
retrieve_timed(question, k=None) (list[ScoredChunk], dict[str, int]) also per-stage latency
answer(question, threshold=None) Answer uses the calibrated threshold when one exists
calibration_threshold() float | None None when absent or fitted for a different configuration
calibration_record() CalibrationRecord | None the record and its evidence

The index loads on first use, so retrieve and answer work without an explicit load_index().

Evaluating in a script

from cairn.eval.golden import load_golden, select_split
from cairn.eval.report import write_report
from cairn.eval.runner import run_eval

cases = load_golden(cfg.golden_path())
evaluation = select_split(cases, "eval", cfg.calibration.split, cfg.calibration.seed)
report = run_eval(cfg, evaluation, pipeline.calibration_threshold(), pipeline)
write_report(report, "reports")

print(report.headline())

Gating in a script

from cairn.core.types import GateThresholds
from cairn.eval.gate import gate
from cairn.eval.report import read_report

result = gate(
    read_report("reports/latest/default.json"),
    read_report("reports/ci/default.json"),
    GateThresholds(max_quality_drop=0.01, max_cost_increase=0.2),
)
if not result.promote:
    for reason in result.reasons:
        print(reason)
    raise SystemExit(1)

The data model

Everything components exchange is a pydantic model, so it validates at the boundary and serialises without ceremony.

Type What it is
Document a parsed file: text, page offsets, content hash
Chunk a slice with offsets into its document, plus page and section
ScoredChunk a chunk with a score, the stage that produced it and its rank
Citation a span inside a chunk, with the quote
Claim an assertion, its citations, and whether they verified
Answer status, text, claims, confidence, passages, usage, cost, latencies
GoldenCase one evaluation row
IndexManifest what built an index, and what makes it compatible
EvalReport the four metric groups plus per-case rows
GateResult promote or not, with reasons and deltas
CalibrationRecord the threshold and the evidence for it

ScoredChunk.stage matters: a BM25 score, a cosine similarity and a reranker logit live on different scales and must never be compared directly.

Components and the registry

from cairn.core.registry import build, load_builtins, names

load_builtins()
print(names("chunker"))     # ['fixed', 'sentence_window', 'structure']
chunker = build("chunker", "sentence_window", {"window": 4, "overlap": 1})

Kinds: parser, chunker, embedder, vector_index, lexical_index, retriever, reranker, rewriter, provider, judge, confidence. See Add a component.

Errors

Every exception derives from CairnError, and each names a stage so a failure says where it happened.

Exception Raised when
ConfigError a configuration or a component name is invalid
IngestError a document could not be parsed or chunked
IndexError_ an index could not be built, saved or loaded
ManifestMismatch the index was built with a different corpus, chunker or embedder; carries fields
RetrievalError a retrieval stage failed
ProviderError a provider failed, refused, or returned output that did not validate; carries retryable
MissingDependency an optional extra is needed; the message names it
EvalError the harness or the gate hit an inconsistency
from cairn.core.errors import ManifestMismatch

try:
    pipeline.load_index()
except ManifestMismatch as e:
    print("rebuild the index; these differ:", e.fields)

Serving

from cairn.serve.app import create_app

app = create_app(cfg)   # a FastAPI application, mountable in your own

Observability

from cairn.observability.drift import DriftMonitor, load_baseline
from cairn.observability.tracing import configure_tracing, span

configure_tracing()                 # reads the environment; a no-op when nothing is set
with span("cairn.retrieve", k=8):
    ...

monitor = DriftMonitor(load_baseline(pipeline.drift_baseline_path()))
monitor.observe(embedding, top1_score)
print(monitor.drift())

See Serve.