Ports¶
Ports are the Protocol contracts that isolate the domain and application
layers from infrastructure. Every adapter under refindery.adapters.*
implements one of these; the composition root
wires the selected adapter behind its port. Because they are Protocols, no
adapter type ever leaks into domain/ or application/.
The module inventory below is discovered from refindery/application/ports at
build time, so adding a port automatically adds it to this page.
chunker
¶
Chunking port: canonical, model-independent, sentence-aware chunking.
clock
¶
cluster_engine
¶
Clustering engine port (implemented in M4).
content_extractor
¶
Content extraction ports: fetching a URL and extracting body text.
FetchResult is a pydantic model because it wraps a raw HTTP response —
an external input that must be validated (size caps, content type parsing).
embedder
¶
Embedding port; one instance per registered embedding model.
Embedder
¶
Bases: Protocol
Embeds documents and queries into a single model's vector space.
dim and max_input_tokens come from the model registry / settings
(not every provider SDK exposes them); adapters must assert returned
vector length equals dim on first use.
embed_query
async
¶
Embed a query (query side; providers may use asymmetric prompts).
entity_extractor
¶
job_queue
¶
Durable job queue port.
Implementations pair an execution engine (huey) with the jobs ledger in the metadata store; the ledger is the source of truth for status.
metadata_store
¶
Metadata store port: pages, chunks, models, jobs, blacklist.
Grows in later milestones (entities, clusters, tombstones). Implementations must keep all SQL dialect-neutral outside the adapter.
PageVectorRow
dataclass
¶
ClusterMemberRow
dataclass
¶
ChunkStats
dataclass
¶
MetadataStore
¶
Bases: Protocol
Transactional metadata + durable job ledger.
get_page_by_canonical_url
async
¶
Fetch a page by canonical URL (revisit detection).
get_pages
async
¶
Fetch multiple pages preserving input order; missing ids dropped.
list_page_ids_by_domain
async
¶
Page ids for one domain, most recently seen first; optionally by status.
set_page_status
async
¶
Update page lifecycle status.
set_page_body
async
¶
Fill the body after a deferred fetch resolved it.
metadata, when given, is shallow-merged into the page's existing
metadata JSON (e.g. to persist section boundaries alongside the body).
replace_chunks
async
¶
Replace all chunks of a page (canonical chunking).
upsert_page_vector
async
¶
Store the pooled page vector (float32 bytes) for one model.
get_page_vectors
async
¶
All page vectors for one model (clustering / similarity).
clear_index_artifacts
async
¶
Remove chunks and page vectors for a page after a core index failure.
list_models
async
¶
List registered models, optionally filtered by status.
create_job
async
¶
Insert a ledger row; False when the idempotency key already exists.
latest_job_for_page
async
¶
Newest job row for a page payload, optionally restricted by kind.
indexed_pages_missing_entity_extraction
async
¶
Indexed pages whose current content has no extract_entities job.
mark_job_running
async
¶
Transition a job to running with a lease.
mark_job_failed
async
¶
Record a failed attempt (job stays retryable).
mark_job_dead
async
¶
Dead-letter a job after attempts are exhausted.
reset_job_for_retry
async
¶
Reset a dead job to pending with attempts=0 (manual re-enqueue).
reset_expired_leases
async
¶
Flip running-past-lease jobs back to pending; return them.
list_pending_jobs
async
¶
All pending ledger rows (startup recovery re-enqueues them).
list_expired_running_jobs
async
¶
Return running jobs whose lease expired (read-only; watchdog telemetry).
count_jobs_by_status
async
¶
Job counts grouped by ledger status (absent statuses omitted).
count_tombstones_by_status
async
¶
Vector tombstone counts grouped by status (absent statuses omitted).
update_watch
async
¶
Full-row update by id; False when the watch does not exist.
list_due_watches
async
¶
Return enabled watches with next_run_at <= now, most overdue first.
mark_watch_run
async
¶
Advance the schedule at enqueue time (decoupled from poll outcome).
record_watch_result
async
¶
Record the outcome of a poll.
find_blacklist_match
async
¶
Return the first blacklist rule matching this URL/domain, if any.
purge_and_blacklist
async
¶
Atomically: upsert the rule, tombstone + delete matching pages.
Idempotent on pattern (re-forgetting returns the existing rule). Returns the effective rule and the purged page ids. Metadata is authoritative immediately; vectors are deleted asynchronously.
delete_blacklist
async
¶
Remove a rule (does not restore purged content); False if missing.
list_tombstones
async
¶
Tombstones in one status, oldest first.
set_tombstone_status
async
¶
Advance tombstones.
delete_tombstones
async
¶
Remove tombstones (after verified retention expires).
find_entity_by_alias
async
¶
Exact normalized-alias match within a type.
entities_in_block
async
¶
Candidate entities sharing (type, first-token block key).
create_entity
async
¶
Insert an entity with its first alias.
add_alias
async
¶
Attach an alias (idempotent on (surface_form, entity_id)).
add_mentions
async
¶
Record mentions (idempotent) and refresh affected entity counts.
resolve_entity
async
¶
Resolve id -> canonical form -> alias (merge-stable references).
entity_blocks_with_duplicates
async
¶
Blocks holding more than one entity (periodic re-canonicalization).
merge_entities
async
¶
Merge source into target (snapshot logged first); returns merge id.
undo_merge
async
¶
Restore a merged entity (LIFO only); returns the restored id.
refresh_entity_idf
async
¶
Recompute idf = ln(N_pages / page_count) for all entities.
upsert_cluster
async
¶
Insert or update a cluster row.
Label/keywords are preserved on update when the new values are empty.
replace_cluster_members
async
¶
Replace a cluster's membership.
tombstone_clusters
async
¶
Mark clusters tombstoned (rows retained, excluded from listings).
get_cluster
async
¶
Fetch a cluster (tombstoned included — stale refs degrade gracefully).
list_clusters
async
¶
Live clusters (optionally tombstoned too), largest first.
cluster_for_page
async
¶
Return the live cluster containing this page, if any.
clusters_for_pages
async
¶
Live cluster per page id; pages without one are absent.
insert_cluster_projection
async
¶
Persist a run's page and cluster projection coordinates.
get_cluster_projection
async
¶
Read page and centroid coordinates for a run.
recent_run_durations_ms
async
¶
Durations of the most recent finished runs.
pages_with_chunks_after
async
¶
Page ids (with chunks) ordered by id, after the cursor.
put_eval_replay_result
async
¶
Persist a completed replay report or terminal error.
podcast_producer
¶
Podcast producer port: resolve a podcast episode into a transcript envelope.
Given the transcript/chapters URLs discovered from a podcast feed, the producer
fetches and normalizes them into a FetchResult carrying the synthetic
podcast-transcript content type, so the result rides the normal extraction
rails (router -> extractor -> chunker) without special-casing indexing.
query_log
¶
Query log port: the eval substrate.
Every search logs its full pre-rerank candidate set and both retrieval arms, so reranker lift and per-arm contribution can be measured offline without re-running retrieval. Logging is non-blocking by contract — implementations buffer and must never stall the query path.
LoggedHit
dataclass
¶
LoggedPage
dataclass
¶
QueryLogRecord
dataclass
¶
QueryLogRecord(
query_id,
ts,
kind,
query_text,
params,
active_model,
reranker_model,
candidate_set,
dense_hits,
sparse_hits,
final_pages,
timing_ms,
compare_id=None,
)
One /search execution (or one /compare arm).
FeedbackRecord
dataclass
¶
query_log_reader
¶
Read side of the query log: the offline eval substrate.
The sink (QueryLogSink) is write-only by design; eval opens the log
separately and read-only, so scoring never contends with the serving path.
Rows are projected down to page-level rankings — feedback labels are
page-level, so pages are the unit every metric is computed over.
LoggedRun
dataclass
¶
LoggedRun(
query_id,
ts,
kind,
query_text,
params,
active_model,
reranker_model,
final_page_ids,
final_page_ranks,
prererank_page_ids,
)
One query_log row projected to page-level rankings.
final_page_ids is the ranking the user actually saw (post-rerank,
post-rollup, exact matches pinned); final_page_ranks preserves its
absolute ranks when the response was paginated. A paginated row only
sees its own slice: relevant pages outside it count as absent, so
metrics computed from such a row understate the full ranking.
prererank_page_ids is the first-occurrence page order of the fused
candidate set — under the default max rollup this is the ranking
rerank-off would have produced, modulo exact-match pins and recency
decay.
reranker
¶
surface_embedder
¶
Surface-form embedding port for entity canonicalization.
A dedicated small static model (not the active document embedder) keeps the cosine threshold calibrated across active-model swaps and costs nothing at ingest. The active-embedder alternative exists behind config.
transcriber
¶
vector_store
¶
Vector store port: dense + sparse retrieval over chunk vectors.
Write shape: a ChunkPoint carries the vectors of every model being
indexed (in Qdrant, one point holds all named vectors — a per-model upsert
would clobber the others). upsert_chunks fully replaces points and is
used by the indexing pipeline (which always embeds for all active/backfilling
models); backfill_vectors adds one model's vectors to existing points
without touching the rest.
The sparse arm takes raw text; how it is represented (BM25 sparse vectors,
tantivy FTS, ...) is an adapter concern. Hybrid fusion is client-side via
refindery.domain.retrieval.rrf_fuse in every adapter so results are
identical across stores (the conformance suite asserts this).
ChunkPoint
dataclass
¶
One chunk to upsert: per-model vectors plus the filterable payload.
StoreFilter
dataclass
¶
Filters pushed into the store (pre-filter where the store supports it).
HybridQuery
dataclass
¶
HybridQuery(
model_id,
dense_vector,
sparse_text,
per_arm_limit,
fused_limit,
rrf_k=60,
filters=None,
)
A hybrid retrieval request over one model's space plus the shared sparse arm.
ArmTiming
dataclass
¶
HybridHits
dataclass
¶
VectorStore
¶
Bases: Protocol
Upsert/query dense + sparse chunk vectors with payload filtering.
ensure_schema
async
¶
Create/update storage so every model has a vector space.
upsert_chunks
async
¶
Idempotently upsert chunks (full point replace, all models).
backfill_vectors
async
¶
Add one model's vectors to existing chunks without clobbering others.
delete_pages
async
¶
Delete all chunks of the given pages across every model space.
dense_query
async
¶
Run dense kNN over one model's vectors.
sparse_query
async
¶
Sparse/lexical arm; shared across models.
watch_source
¶
Watch source port: enumerate the current child items of a watched URL.
Sources own their I/O (an RSS source fetches bytes and parses them; a
yt-dlp-backed source drives its own extraction), so the port is a single
discover call rather than a raw-bytes parser.
WatchItem
¶
Bases: BaseModel
One discovered child URL; validates parser/backend output.
Podcast feeds additionally surface the episode's audio enclosure and any
Podcasting 2.0 <podcast:transcript> / <podcast:chapters> links, which
the watch fan-out threads into ingest so the transcript is chapter-chunked.