Skip to content

Domain models

refindery.domain holds the pure domain types and algorithms — dataclasses, enums, identifiers, URL normalization, ranking metrics, clustering helpers, and retrieval functions with no I/O. For the SQL shape these map onto, see the Data model.

audio

Pure audio URL and MIME predicates for transcribable audio.

Used by the fetch router (audio URLs get the transcript fetcher instead of the plain HTTP fetcher), podcast discovery, download validation, and watch creation (a podcast watch takes a feed URL, not a direct audio file).

AUDIO_EXTENSIONS module-attribute

AUDIO_EXTENSIONS = frozenset(
    {
        ".mp3",
        ".m4a",
        ".m4b",
        ".aac",
        ".ogg",
        ".oga",
        ".opus",
        ".wav",
        ".flac",
    }
)

GENERIC_AUDIO_CONTENT_TYPES module-attribute

GENERIC_AUDIO_CONTENT_TYPES = frozenset(
    {"application/octet-stream", "application/ogg"}
)

is_audio_content_type

is_audio_content_type(content_type)

Accept normalized audio MIME types and generic podcast CDN types.

is_audio_url

is_audio_url(url)

Report whether the URL path ends in a known audio file extension.

canonical_url

Pure URL canonicalization.

Rules (per spec): lowercase scheme + host; strip default port; strip www.; strip fragment; strip tracking params (configurable glob patterns); sort the remaining query params; strip trailing slash. Per-domain overrides can restrict which query params survive (e.g. YouTube keeps only v).

DEFAULT_TRACKING_PARAMS module-attribute

DEFAULT_TRACKING_PARAMS = (
    "utm_*",
    "fbclid",
    "gclid",
    "ref",
    "si",
)

DEFAULT_DOMAIN_RULES module-attribute

DEFAULT_DOMAIN_RULES = {
    "youtube.com": DomainRule(keep_params=frozenset({"v"}))
}

DomainRule dataclass

DomainRule(keep_params)

Per-domain override: only keep_params survive canonicalization.

keep_params instance-attribute

keep_params

CanonicalizationRules dataclass

CanonicalizationRules(
    tracking_params=DEFAULT_TRACKING_PARAMS,
    domain_rules=(lambda: dict(DEFAULT_DOMAIN_RULES))(),
)

Configurable canonicalization behavior.

tracking_params class-attribute instance-attribute

tracking_params = DEFAULT_TRACKING_PARAMS

domain_rules class-attribute instance-attribute

domain_rules = field(
    default_factory=lambda: dict(DEFAULT_DOMAIN_RULES)
)

CanonicalUrl dataclass

CanonicalUrl(url, domain)

A canonicalized URL and its domain.

url instance-attribute

url

domain instance-attribute

domain

canonicalize

canonicalize(url, rules=None)

Canonicalize url and derive its domain.

Raises ValueError for URLs without a scheme or host.

clustering

Clustering domain: dynamic parameters and the stable-ID matching layer.

HDBSCAN gives no stable ids across runs; match_clusters does, via Jaccard cost + Hungarian assignment. Noise (label -1) is a legitimate outcome and never becomes a cluster.

INHERIT_JACCARD module-attribute

INHERIT_JACCARD = 0.5

SPLIT_OVERLAP module-attribute

SPLIT_OVERLAP = 0.3

MERGE_ABSORPTION module-attribute

MERGE_ABSORPTION = 0.5

LineageEvent

Bases: StrEnum

What happened to a cluster between runs.

CREATED class-attribute instance-attribute

CREATED = 'created'

PERSISTED class-attribute instance-attribute

PERSISTED = 'persisted'

SPLIT class-attribute instance-attribute

SPLIT = 'split'

MERGED class-attribute instance-attribute

MERGED = 'merged'

DISSOLVED class-attribute instance-attribute

DISSOLVED = 'dissolved'

LineageRecord dataclass

LineageRecord(
    event, cluster_id, parent_ids=(), jaccard=None
)

One lineage event emitted by a run.

event instance-attribute

event

cluster_id instance-attribute

cluster_id

parent_ids class-attribute instance-attribute

parent_ids = ()

jaccard class-attribute instance-attribute

jaccard = None

MatchOutcome dataclass

MatchOutcome(ids_by_label, lineage, tombstoned=())

Stable ids per new label plus the lineage of the transition.

ids_by_label instance-attribute

ids_by_label

lineage instance-attribute

lineage

tombstoned class-attribute instance-attribute

tombstoned = field(default=())

dynamic_hdbscan_params

dynamic_hdbscan_params(n_pages)

Corpus-size-scaled (min_cluster_size, min_samples).

Heuristic (config-overridable): sqrt(n)/2 clamped to [5, 25]; min_samples is half that, clamped to [3, 15]. Reproduces the spec's static defaults (5, 3) around n=100. TODO: tune against a real corpus.

match_clusters

match_clusters(*, old, new)

Assign stable ids to new labels and emit lineage.

Hungarian assignment over the Jaccard cost matrix; a match holds only at Jaccard >= 0.5. Unmatched new clusters are created (or splits when they overlap an old cluster > 0.3); unmatched old clusters are tombstoned as dissolved, or merged when most of their members landed in one new cluster.

content_hash

Content hashing for revisit detection and v2 near-duplicate collapsing.

content_hash

content_hash(body_text)

Return the hex sha256 of the page body text.

ctfidf

Class-based TF-IDF keywords for cluster labeling (pure, in-house).

The formula from BERTopic's c-TF-IDF, over one pseudo-document per cluster, without the BERTopic dependency. Keywords are always populated regardless of whether an LLM label lands later.

compute_ctfidf

compute_ctfidf(docs_by_cluster, *, top_n=10)

Top c-TF-IDF terms per cluster from one pseudo-document each.

entities

Entity domain: taxonomy, surface-form normalization, blocking.

Canonicalization is corpus-internal (no Wikidata): normalization + blocking here, matching/merging in the canonicalization service. inflect (pure Python) is the one allowed non-stdlib dependency for singularization.

EntityType

Bases: StrEnum

Fixed extraction taxonomy.

PERSON class-attribute instance-attribute

PERSON = 'person'

ORG class-attribute instance-attribute

ORG = 'org'

PRODUCT class-attribute instance-attribute

PRODUCT = 'product'

TECHNOLOGY class-attribute instance-attribute

TECHNOLOGY = 'technology'

CONCEPT class-attribute instance-attribute

CONCEPT = 'concept'

PLACE class-attribute instance-attribute

PLACE = 'place'

WORK class-attribute instance-attribute

WORK = 'work'

Entity dataclass

Entity(
    id,
    canonical_form,
    type,
    mention_count=0,
    page_count=0,
    idf=None,
)

A canonical entity aggregated over the corpus.

id instance-attribute

id

canonical_form instance-attribute

canonical_form

type instance-attribute

type

mention_count class-attribute instance-attribute

mention_count = 0

page_count class-attribute instance-attribute

page_count = 0

idf class-attribute instance-attribute

idf = None

normalize_surface_form

normalize_surface_form(surface)

Casefold, strip diacritics and punctuation, singularize the last word.

block_key

block_key(normalized)

Blocking key for candidate matching: the first token.

normalized_edit_distance

normalized_edit_distance(a, b)

Levenshtein distance normalized to [0, 1] (0 = identical).

Small pure implementation; the canonicalization service uses rapidfuzz when available and falls back to this.

errors

Domain error hierarchy.

Messages are built inside each exception class (keeps call sites clean and satisfies structured error handling at the API layer).

RefinderyError

Bases: Exception

Base class for all domain errors.

ConfigurationError

Bases: RefinderyError

The application cannot start with the supplied configuration.

BlacklistedError

BlacklistedError(pattern)

Bases: RefinderyError

The URL matches a blacklist rule.

pattern instance-attribute

pattern = pattern

ModelBudgetError

ModelBudgetError(*, model_id, max_input_tokens, hard_max)

Bases: RefinderyError

A model's token budget is below the canonical chunk hard max.

model_id instance-attribute

model_id = model_id

max_input_tokens instance-attribute

max_input_tokens = max_input_tokens

hard_max instance-attribute

hard_max = hard_max

PageNotFoundError

PageNotFoundError(page_id)

Bases: RefinderyError

No page with the given id exists.

page_id instance-attribute

page_id = page_id

EntityNotFoundError

EntityNotFoundError(ref)

Bases: RefinderyError

No entity matches the given reference (id, canonical form, or alias).

ref instance-attribute

ref = ref

PageHasNoBodyError

PageHasNoBodyError(page_id)

Bases: RefinderyError

The page reached the indexing pipeline without a resolved body.

page_id instance-attribute

page_id = page_id

JobNotFoundError

JobNotFoundError(job_id)

Bases: RefinderyError

No job with the given id exists.

job_id instance-attribute

job_id = job_id

WatchNotFoundError

WatchNotFoundError(watch_id)

Bases: RefinderyError

No watch with the given id exists.

watch_id instance-attribute

watch_id = watch_id

WatchSourceUnavailableError

WatchSourceUnavailableError(*, kind)

Bases: RefinderyError

The watch's kind has no wired source (its extra is not installed).

kind instance-attribute

kind = kind

WatchFanOutError

WatchFanOutError(*, watch_id, item_count)

Bases: RefinderyError

None of a watch poll's discovered items could be ingested.

watch_id instance-attribute

watch_id = watch_id

item_count instance-attribute

item_count = item_count

ModelNotFoundError

ModelNotFoundError(model_id)

Bases: RefinderyError

No embedding model with the given id is registered.

model_id instance-attribute

model_id = model_id

NoActiveModelError

NoActiveModelError()

Bases: RefinderyError

No embedding model is currently active.

BodyConflictError

BodyConflictError()

Bases: RefinderyError

Both body_extracted and body_html were supplied.

FeatureUnavailableError

FeatureUnavailableError(*, feature, milestone)

Bases: RefinderyError

A requested capability lands in a later milestone.

feature instance-attribute

feature = feature

milestone instance-attribute

milestone = milestone

FetchFailedError

FetchFailedError(*, url, detail)

Bases: RefinderyError

Fetching the URL failed (network error or non-2xx status).

url instance-attribute

url = url

detail instance-attribute

detail = detail

ProviderUnavailableError

ProviderUnavailableError(*, provider, retry_after_s)

Bases: RefinderyError

An external provider's circuit breaker is open; the call was not attempted.

provider instance-attribute

provider = provider

retry_after_s instance-attribute

retry_after_s = retry_after_s

UnsupportedContentTypeError

UnsupportedContentTypeError(content_type)

Bases: RefinderyError

No extractor is registered for the fetched content type.

content_type instance-attribute

content_type = content_type

ExtractionUnavailableError

ExtractionUnavailableError(*, content_type, extra)

Bases: RefinderyError

The extractor for this content type needs an optional extra installed.

content_type instance-attribute

content_type = content_type

extra instance-attribute

extra = extra

ids

Typed identifiers and uuid7 generation.

uuid7 identifiers are time-ordered, which keeps SQLite b-tree inserts append-mostly and makes ORDER BY id a creation-time ordering.

PageId module-attribute

PageId = NewType('PageId', str)

ChunkId module-attribute

ChunkId = NewType('ChunkId', str)

JobId module-attribute

JobId = NewType('JobId', str)

EntityId module-attribute

EntityId = NewType('EntityId', str)

ClusterId module-attribute

ClusterId = NewType('ClusterId', str)

ClusterRunId module-attribute

ClusterRunId = NewType('ClusterRunId', str)

BlacklistId module-attribute

BlacklistId = NewType('BlacklistId', str)

QueryId module-attribute

QueryId = NewType('QueryId', str)

WatchId module-attribute

WatchId = NewType('WatchId', str)

new_page_id

new_page_id()

Generate a fresh time-ordered page id.

new_chunk_id

new_chunk_id()

Generate a fresh time-ordered chunk id.

new_job_id

new_job_id()

Generate a fresh time-ordered job id.

new_entity_id

new_entity_id()

Generate a fresh time-ordered entity id.

new_cluster_id

new_cluster_id()

Generate a fresh time-ordered cluster id.

new_cluster_run_id

new_cluster_run_id()

Generate a fresh time-ordered cluster-run id.

new_blacklist_id

new_blacklist_id()

Generate a fresh time-ordered blacklist-rule id.

new_query_id

new_query_id()

Generate a fresh time-ordered query id.

new_watch_id

new_watch_id()

Generate a fresh time-ordered watch id.

job_keys

Idempotency-key builders, one per durable job kind.

These strings are persisted dedupe keys: jobs.idempotency_key is UNIQUE and enqueueing a duplicate is a silent no-op, so the format for an existing kind must never change — a new format would stop deduplicating against keys already in the ledger. Golden tests in tests/unit/test_job_keys.py lock every format and the one-prefix-per-kind invariant.

Builders are pure; impure inputs (clock readings, fresh uuids) stay at the call sites and are passed in.

fetch_and_index_key

fetch_and_index_key(page_id)

Deferred-fetch key; keyed by page only — content is unknown until fetched.

index_page_key

index_page_key(*, page_id, content_hash)

Index key; a new content hash is new work, same hash dedupes.

extract_entities_key

extract_entities_key(*, page_id, content_hash)

Entity-extraction key over one resolved body.

cluster_key

cluster_key(now)

Clustering-run key; every request timestamp is distinct work.

canonicalize_entities_key

canonicalize_entities_key(run_id)

Canonicalization key; one pass per cluster run.

backfill_model_key

backfill_model_key(*, model_id, now)

Backfill key; re-requesting the same model later is distinct work.

purge_vectors_key

purge_vectors_key(page_ids)

Purge key; order-insensitive digest of the page-id set.

eval_replay_key

eval_replay_key(token)

Eval-replay key; callers pass a fresh uuid so replays never dedupe.

poll_watch_key

poll_watch_key(*, watch_id, run_at, manual=False)

Poll key; one scheduled run dedupes, while manual runs are namespaced.

models

Core domain entities and their status enums.

These are plain dataclasses, not pydantic models: rows come from our own migrations, so re-validating them per row buys nothing. Pydantic lives at trust boundaries (HTTP requests/responses, fetched content, settings).

MAX_WATCH_INTERVAL_HOURS module-attribute

MAX_WATCH_INTERVAL_HOURS = 8760

IngestOutcome module-attribute

PageStatus

Bases: StrEnum

Lifecycle of a page through the indexing pipeline.

QUEUED class-attribute instance-attribute

QUEUED = 'queued'

INDEXING class-attribute instance-attribute

INDEXING = 'indexing'

INDEXED class-attribute instance-attribute

INDEXED = 'indexed'

FAILED class-attribute instance-attribute

FAILED = 'failed'

DEAD class-attribute instance-attribute

DEAD = 'dead'

ModelStatus

Bases: StrEnum

Lifecycle of a registered embedding model.

REGISTERED class-attribute instance-attribute

REGISTERED = 'registered'

BACKFILLING class-attribute instance-attribute

BACKFILLING = 'backfilling'

READY class-attribute instance-attribute

READY = 'ready'

RETIRED class-attribute instance-attribute

RETIRED = 'retired'

JobStatus

Bases: StrEnum

Ledger status of a durable job.

PENDING class-attribute instance-attribute

PENDING = 'pending'

RUNNING class-attribute instance-attribute

RUNNING = 'running'

DONE class-attribute instance-attribute

DONE = 'done'

FAILED class-attribute instance-attribute

FAILED = 'failed'

DEAD class-attribute instance-attribute

DEAD = 'dead'

JobKind

Bases: StrEnum

Kinds of durable jobs the queue executes.

INDEX_PAGE class-attribute instance-attribute

INDEX_PAGE = 'index_page'

FETCH_AND_INDEX class-attribute instance-attribute

FETCH_AND_INDEX = 'fetch_and_index'

EXTRACT_ENTITIES class-attribute instance-attribute

EXTRACT_ENTITIES = 'extract_entities'

BACKFILL_MODEL class-attribute instance-attribute

BACKFILL_MODEL = 'backfill_model'

CLUSTER class-attribute instance-attribute

CLUSTER = 'cluster'

LABEL_CLUSTERS class-attribute instance-attribute

LABEL_CLUSTERS = 'label_clusters'

CANONICALIZE_ENTITIES class-attribute instance-attribute

CANONICALIZE_ENTITIES = 'canonicalize_entities'

PURGE_VECTORS class-attribute instance-attribute

PURGE_VECTORS = 'purge_vectors'

EVAL_REPLAY class-attribute instance-attribute

EVAL_REPLAY = 'eval_replay'

POLL_WATCH class-attribute instance-attribute

POLL_WATCH = 'poll_watch'

BlacklistKind

Bases: StrEnum

Whether a blacklist rule matches a URL exactly or a domain suffix.

URL class-attribute instance-attribute

URL = 'url'

DOMAIN class-attribute instance-attribute

DOMAIN = 'domain'

WatchKind

Bases: StrEnum

Kinds of pull sources a watch can poll.

RSS class-attribute instance-attribute

RSS = 'rss'

YOUTUBE class-attribute instance-attribute

YOUTUBE = 'youtube'

PODCAST class-attribute instance-attribute

PODCAST = 'podcast'

WatchStatus

Bases: StrEnum

Outcome of a watch's most recent poll.

PENDING class-attribute instance-attribute

PENDING = 'pending'

OK class-attribute instance-attribute

OK = 'ok'

ERROR class-attribute instance-attribute

ERROR = 'error'

Page dataclass

Page(
    id,
    canonical_url,
    original_url,
    domain,
    title,
    body_text,
    content_hash,
    source,
    metadata,
    first_seen_at,
    last_seen_at,
    visit_count,
    indexed_at,
    status,
)

One canonical web page. One row per canonical_url; never versioned.

body_text and content_hash are None only while a fetch_and_index job is still resolving the body.

id instance-attribute

id

canonical_url instance-attribute

canonical_url

original_url instance-attribute

original_url

domain instance-attribute

domain

title instance-attribute

title

body_text instance-attribute

body_text

content_hash instance-attribute

content_hash

source instance-attribute

source

metadata instance-attribute

metadata

first_seen_at instance-attribute

first_seen_at

last_seen_at instance-attribute

last_seen_at

visit_count instance-attribute

visit_count

indexed_at instance-attribute

indexed_at

status instance-attribute

status

Section dataclass

Section(title, char_start, char_end, start_time_s=None)

A titled, contiguous span of a page's body text (e.g. a podcast chapter).

char_start/char_end index into the page body text; start_time_s is the source timestamp (podcast chapter start), when the section derives from timed media.

title instance-attribute

title

char_start instance-attribute

char_start

char_end instance-attribute

char_end

start_time_s class-attribute instance-attribute

start_time_s = None

Chunk dataclass

Chunk(
    id,
    page_id,
    ordinal,
    text,
    token_count,
    char_start,
    char_end,
    section_title=None,
    section_start_s=None,
)

A canonical, model-independent span of a page's body text.

section_title/section_start_s label the chunk with the chapter it belongs to when the page was chunked along section boundaries; both are None for ordinary flat chunking.

id instance-attribute

id

page_id instance-attribute

page_id

ordinal instance-attribute

ordinal

text instance-attribute

text

token_count instance-attribute

token_count

char_start instance-attribute

char_start

char_end instance-attribute

char_end

section_title class-attribute instance-attribute

section_title = None

section_start_s class-attribute instance-attribute

section_start_s = None

EmbeddingModel dataclass

EmbeddingModel(
    id,
    provider,
    model_name,
    dim,
    max_input_tokens,
    is_active,
    status,
    created_at,
)

A registered embedding model and its vector space.

id instance-attribute

id

provider instance-attribute

provider

model_name instance-attribute

model_name

dim instance-attribute

dim

max_input_tokens instance-attribute

max_input_tokens

is_active instance-attribute

is_active

status instance-attribute

status

created_at instance-attribute

created_at

Job dataclass

Job(
    id,
    kind,
    payload,
    status,
    idempotency_key,
    created_at,
    updated_at,
    attempts=0,
    max_attempts=5,
    lease_until=None,
    last_error=None,
)

Durable job ledger row; huey executes, this row is the source of truth.

id instance-attribute

id

kind instance-attribute

kind

payload instance-attribute

payload

status instance-attribute

status

idempotency_key instance-attribute

idempotency_key

created_at instance-attribute

created_at

updated_at instance-attribute

updated_at

attempts class-attribute instance-attribute

attempts = 0

max_attempts class-attribute instance-attribute

max_attempts = 5

lease_until class-attribute instance-attribute

lease_until = None

last_error class-attribute instance-attribute

last_error = None

BlacklistRule dataclass

BlacklistRule(id, pattern, kind, created_at, reason=None)

A forget rule: exact canonical URL or domain suffix.

id instance-attribute

id

pattern instance-attribute

pattern

kind instance-attribute

kind

created_at instance-attribute

created_at

reason class-attribute instance-attribute

reason = None

Watch dataclass

Watch(
    id,
    kind,
    url,
    title,
    enabled,
    interval_hours,
    config,
    next_run_at,
    last_run_at,
    last_status,
    last_error,
    last_item_count,
    created_at,
    updated_at,
)

A pull source polled on its own schedule; discovered URLs are ingested.

next_run_at is advanced by the scheduling tick at enqueue time, never by the poll handler, so a permanently failing poll cannot freeze the schedule. config holds per-kind options (e.g. max_entries).

id instance-attribute

id

kind instance-attribute

kind

url instance-attribute

url

title instance-attribute

title

enabled instance-attribute

enabled

interval_hours instance-attribute

interval_hours

config instance-attribute

config

next_run_at instance-attribute

next_run_at

last_run_at instance-attribute

last_run_at

last_status instance-attribute

last_status

last_error instance-attribute

last_error

last_item_count instance-attribute

last_item_count

created_at instance-attribute

created_at

updated_at instance-attribute

updated_at

Cluster dataclass

Cluster(
    id,
    label,
    keywords,
    size,
    model_id,
    created_at,
    updated_at,
    tombstoned_at=None,
    centroid=None,
)

A stable-id cluster of pages.

id instance-attribute

id

label instance-attribute

label

keywords instance-attribute

keywords

size instance-attribute

size

model_id instance-attribute

model_id

created_at instance-attribute

created_at

updated_at instance-attribute

updated_at

tombstoned_at class-attribute instance-attribute

tombstoned_at = None

centroid class-attribute instance-attribute

centroid = None

ClusterRun dataclass

ClusterRun(
    id,
    trigger,
    algorithm,
    params,
    started_at,
    finished_at=None,
    duration_ms=None,
    n_pages=None,
    n_clusters=None,
    n_noise=None,
)

One clustering run's record.

id instance-attribute

id

trigger instance-attribute

trigger

algorithm instance-attribute

algorithm

params instance-attribute

params

started_at instance-attribute

started_at

finished_at class-attribute instance-attribute

finished_at = None

duration_ms class-attribute instance-attribute

duration_ms = None

n_pages class-attribute instance-attribute

n_pages = None

n_clusters class-attribute instance-attribute

n_clusters = None

n_noise class-attribute instance-attribute

n_noise = None

ClusterProjectionPoint dataclass

ClusterProjectionPoint(run_id, page_id, x, y, cluster_id)

A page's persisted two-dimensional position for one cluster run.

run_id instance-attribute

run_id

page_id instance-attribute

page_id

x instance-attribute

x

y instance-attribute

y

cluster_id instance-attribute

cluster_id

ClusterProjectionCentroid dataclass

ClusterProjectionCentroid(run_id, cluster_id, x, y)

A cluster's centroid in the same projection space as its pages.

run_id instance-attribute

run_id

cluster_id instance-attribute

cluster_id

x instance-attribute

x

y instance-attribute

y

EvalReplayResult dataclass

EvalReplayResult(
    job_id, report, error, created_at, updated_at
)

Durable serialized output or failure for an eval replay job.

job_id instance-attribute

job_id

report instance-attribute

report

error instance-attribute

error

created_at instance-attribute

created_at

updated_at instance-attribute

updated_at

ModelBackfill dataclass

ModelBackfill(
    model_id,
    total_chunks,
    total_tokens,
    started_at,
    updated_at,
    cursor_page_id=None,
    embedded_chunks=0,
    finished_at=None,
    last_error=None,
)

Resumable backfill state for one model.

model_id instance-attribute

model_id

total_chunks instance-attribute

total_chunks

total_tokens instance-attribute

total_tokens

started_at instance-attribute

started_at

updated_at instance-attribute

updated_at

cursor_page_id class-attribute instance-attribute

cursor_page_id = None

embedded_chunks class-attribute instance-attribute

embedded_chunks = 0

finished_at class-attribute instance-attribute

finished_at = None

last_error class-attribute instance-attribute

last_error = None

TombstoneStatus

Bases: StrEnum

Lifecycle of a purged page's vector-deletion tombstone.

PENDING class-attribute instance-attribute

PENDING = 'pending'

DELETED class-attribute instance-attribute

DELETED = 'deleted'

VERIFIED class-attribute instance-attribute

VERIFIED = 'verified'

VectorTombstone dataclass

VectorTombstone(
    page_id, status, created_at, updated_at, last_error=None
)

A purged page whose vectors must be (verifiably) deleted.

page_id instance-attribute

page_id

status instance-attribute

status

created_at instance-attribute

created_at

updated_at instance-attribute

updated_at

last_error class-attribute instance-attribute

last_error = None

ExtractedContent dataclass

ExtractedContent(body_text, title=None, sections=None)

Output of a content extractor: markdown body plus optional title.

sections carries structural boundaries (e.g. podcast chapters) discovered during extraction; None means "no structure", and chunking falls back to the flat sentence-based path.

body_text instance-attribute

body_text

title class-attribute instance-attribute

title = None

sections class-attribute instance-attribute

sections = None

Mention dataclass

Mention(
    surface_form,
    type,
    char_start=None,
    char_end=None,
    chunk_id=None,
)

A single entity mention found in page text.

surface_form instance-attribute

surface_form

type instance-attribute

type

char_start class-attribute instance-attribute

char_start = None

char_end class-attribute instance-attribute

char_end = None

chunk_id class-attribute instance-attribute

chunk_id = None

IngestQueued dataclass

IngestQueued(page_id)

Outcome: a new page was accepted and queued for indexing.

page_id instance-attribute

page_id

IngestRevisit dataclass

IngestRevisit(page_id, status, content_hash_differs)

Outcome: the canonical URL was already known; visit recorded.

page_id instance-attribute

page_id

status instance-attribute

status

content_hash_differs instance-attribute

content_hash_differs

IngestBlacklisted dataclass

IngestBlacklisted(pattern)

Outcome: the URL matched a blacklist rule; nothing stored.

pattern instance-attribute

pattern

ranking_metrics

Ranking metrics: agreement stats for /compare, relevance metrics for eval.

Hand-implemented (small, pure, well-defined): no maintained PyPI RBO exists, and scipy's tau would be a heavy import for fifteen lines. RBO follows the extrapolated form of Webber, Moffat & Zobel (2010), eq. 32. Relevance metrics (nDCG, MRR, recall) use binary gains, matching the boolean feedback labels they are scored against.

jaccard_at_k

jaccard_at_k(a, b, k)

Set overlap of the top-k items; both-empty is defined as 1.0.

rbo_ext

rbo_ext(a, b, p=0.9)

Extrapolated rank-biased overlap for finite ranked lists.

kendall_tau_intersection

kendall_tau_intersection(a, b)

Tau-a over the ordered intersection; None when it has < 2 items.

Restricted to the intersection, both rankings are tie-free permutations, so plain pair counting suffices (O(n^2), trivial at n <= 100).

ndcg_at_k

ndcg_at_k(ranked, relevant, k, *, absolute_ranks=None)

Binary-gain nDCG@k; None when there are no relevant items.

DCG uses the 1/log2(rank + 1) discount; the ideal DCG places min(len(relevant), k) relevant items at the top. absolute_ranks preserves stored positions for a paginated ranking slice.

reciprocal_rank

reciprocal_rank(ranked, relevant, *, absolute_ranks=None)

1/rank of the first relevant item; optionally use stored positions.

recall_at_k

recall_at_k(ranked, relevant, k, *, absolute_ranks=None)

Relevant fraction in top k, optionally using stored absolute positions.

retrieval

Pure retrieval primitives shared by all vector-store adapters.

Fusion is deliberately client-side in every adapter (Qdrant's server-side RRF exposes no k parameter, and the query log needs both arms anyway), so this one function defines hybrid fusion for the whole system. The conformance suite asserts every adapter's fused output equals rrf_fuse of its arms.

ChunkHit dataclass

ChunkHit(chunk_id, page_id, ordinal, score)

A scored chunk reference returned by a vector store arm.

chunk_id instance-attribute

chunk_id

page_id instance-attribute

page_id

ordinal instance-attribute

ordinal

score instance-attribute

score

RollupStrategy

Bases: StrEnum

How chunk scores pool into a page score.

No magic-number bonuses in v1: rerankers are calibrated, and arbitrary boosts would destroy their ranking. A bonus variant may be added later only if eval proves lift.

MAX class-attribute instance-attribute

MAX = 'max'

MEAN_TOP_M class-attribute instance-attribute

MEAN_TOP_M = 'mean_top_m'

SUM_RRF class-attribute instance-attribute

SUM_RRF = 'sum_rrf'

ScoredChunk dataclass

ScoredChunk(
    chunk_id,
    page_id,
    ordinal,
    fusion_score,
    rerank_score=None,
)

A candidate chunk carrying both fusion and (optional) rerank scores.

chunk_id instance-attribute

chunk_id

page_id instance-attribute

page_id

ordinal instance-attribute

ordinal

fusion_score instance-attribute

fusion_score

rerank_score class-attribute instance-attribute

rerank_score = None

effective_score property

effective_score

Rerank score when present, fusion score otherwise.

PageScore dataclass

PageScore(page_id, score, chunks)

A page's rolled-up score plus its matching chunks, best first.

page_id instance-attribute

page_id

score instance-attribute

score

chunks instance-attribute

chunks

rollup_pages

rollup_pages(*, chunks, strategy=MAX, top_m=3)

Roll chunk scores up to ranked pages.

sum_rrf orders pages by summed fusion scores (the reranker then only affects chunk display order); the other strategies use effective scores.

apply_recency_decay

apply_recency_decay(
    pages, *, first_seen, now, half_life_days
)

Decay page scores by age: score * 0.5 ** (age_days / half_life).

rrf_fuse

rrf_fuse(*, dense, sparse, k=60)

Reciprocal-rank-fuse two ranked arms into one ranked list.

Score per chunk is sum(1 / (k + rank)) over the arms it appears in (rank is 1-based). Ties break on chunk_id for determinism.

rollup

Page-vector rollup: pool chunk vectors into a single page vector.

Vector

Vector = NDArray[float32]

PoolingStrategy

Bases: StrEnum

How chunk vectors are pooled into a page vector.

MEAN class-attribute instance-attribute

MEAN = 'mean'

MAX class-attribute instance-attribute

MAX = 'max'

l2_normalize

l2_normalize(vector)

L2-normalize a vector; zero vectors are returned unchanged.

page_vector

page_vector(chunk_vectors, strategy=MEAN)

Pool chunk vectors into one L2-normalized page vector.

Raises ValueError when no chunk vectors are given.

youtube

Pure YouTube URL logic: detection, classification, and canonical rewrites.

Used by canonicalization (folding youtu.be/shorts/live forms into the watch?v= page), by the fetch router (only video URLs get the caption fetcher), and by watch creation (only playlists/channels are watchable).

YoutubeUrlKind

Bases: StrEnum

What a YouTube URL points at.

VIDEO class-attribute instance-attribute

VIDEO = 'video'

PLAYLIST class-attribute instance-attribute

PLAYLIST = 'playlist'

CHANNEL class-attribute instance-attribute

CHANNEL = 'channel'

is_youtube_host

is_youtube_host(host)

Match youtu.be, youtube.com, youtube-nocookie.com, and subdomains.

video_id_from_url

video_id_from_url(url)

Extract the 11-char video id from watch/youtu.be/shorts/live URLs.

is_youtube_video_url

is_youtube_video_url(url)

Report whether the URL identifies a single video (routing predicate).

classify_youtube_url

classify_youtube_url(url)

Classify a YouTube URL as video, playlist, or channel; None otherwise.

normalize_listing_url

normalize_listing_url(url)

Fetch-time form for yt-dlp flat extraction; never persisted.

watch?list= becomes the explicit playlist page; a bare channel URL gets its /videos tab appended so extraction deterministically yields the uploads playlist.

rewrite_to_watch

rewrite_to_watch(*, host, path, query)

Fold youtu.be/, /shorts/, and /live/ into watch?v=.

Called by canonicalize() with the www-stripped lowercase host. The original query is discarded on rewrite (t=/si= would be dropped by the youtube.com keep-params rule anyway); non-matching URLs pass through unchanged — /watch paths in particular, so existing stored canonical URLs are unaffected.