Skip to content

CapabilityRegistry

Discovery, validation, ordering, and the content-addressed snapshot.

Protocols:

  • CapabilityRegistry.snapshot(snapshot_id: str | None = None) -> CapabilitySnapshot
  • CapabilityRegistry.create(capability_id: str) -> Any
  • CapabilityRegistry.observers() -> tuple[SourceObserver, ...]
  • CapabilityRegistry.loaders() -> tuple[SourceLoader, ...]
  • CapabilityRegistry.chunkers() -> tuple[Chunker, ...]
  • CapabilityRegistry.language_detectors() -> tuple[LanguageDetector, ...]
  • CapabilityRegistry.document_classifiers() -> tuple[DocumentClassifier, ...]
  • CapabilityRegistry.page_classifiers() -> tuple[PageClassifier, ...]
  • CapabilityRegistry.chunk_classifiers() -> tuple[ChunkClassifier, ...]
  • CapabilityRegistry.page_entity_extractors() -> tuple[PageEntityExtractor, ...]
  • CapabilityRegistry.chunk_entity_extractors() -> tuple[ChunkEntityExtractor, ...]
  • CapabilityRegistry.document_enrichers() -> tuple[DocumentEnricher, ...]
  • CapabilityRegistry.page_enrichers() -> tuple[PageEnricher, ...]
  • CapabilityRegistry.chunk_enrichers() -> tuple[ChunkEnricher, ...]

CapabilityRegistry is the boundary for obtaining a versioned inventory of the processing capabilities and embedding spaces available to indx. Calling snapshot() without an ID returns the current inventory; supplying an ID resolves that pinned snapshot from retained history.

The registry discovers installed providers, validates their public declarations, rejects duplicate identifiers, orders the inventory deterministically, and assigns a content-addressed snapshot ID.

create() is the way back: it turns an ID a plan selected into the implementation that runs it. Discovery is the only step that knows which provider declared which capability, so the registry that answered GET /v1/capabilities is also the one execution asks. A second lookup built elsewhere could disagree with the snapshot a plan was decided against.

observers() is there for the same reason one layer over: which provider declared a SourceObserver is discovery’s answer to give, and preflight must not look it up again. They are returned with the observers indx ships ordered last, so an installed observer claiming a format indx ships one for wins deliberately rather than by whichever entry point loaded first. Observers are not snapshot content – they carry no ID and change what can be planned, while the snapshot records what can be run. chunkers() follows the same rule for the executor’s side, with one extra rung in the ordering: a Chunker declaring fallback = True – the shipped one-chunk-per-page floor – sorts after everything, because an answer for every page must be the last answer asked for. The eight annotation accessors – document_classifiers(), page_classifiers(), chunk_classifiers(), page_entity_extractors(), chunk_entity_extractors(), document_enrichers(), page_enrichers() and chunk_enrichers() – are each sorted by ID and nothing else: a request names the classifier, extractor or enricher it wants, so the order here only decides the advertised list. Their IDs are one namespace across all eight, so the duplicate check is cross-port rather than per-accessor: a page classifier and a chunk extractor claiming one string are rejected the way duplicate capability IDs are, because a request naming that string could not say which it meant.

Capability availability varies with installed packages, configuration, hardware, and provider health. Planning against an unversioned live list would allow the same request to produce or execute a different route after the environment changes.

A snapshot freezes the relevant decision context. Plans record its ID so they can be reproduced, compared, or rejected explicitly when the required inventory is unavailable or incompatible.

The optional input is a snapshot ID. Omitting it requests the current snapshot; providing it requests the matching retained snapshot.

The output is CapabilitySnapshot, which contains:

  • A content-addressed snapshot ID.
  • The routing policy version associated with the inventory.
  • Uniquely identified capability descriptors.
  • Uniquely identified embedding-space descriptors.

Capability descriptors identify version, kind, supported devices and media types, requirements, and availability. Embedding-space descriptors identify dimension, metric, normalization, and compatible document/query embedders.

  • Discover providers advertised through the indx.capabilities Python entry-point group.
  • Validate provider declarations before publishing them.
  • Reject duplicate capability and embedding-space IDs.
  • Sort snapshot content deterministically.
  • Derive snapshot identity from canonical content so the same inventory has the same identity.
  • Retain sufficient history to resolve caller-supplied snapshot IDs.
  • Keep discovery and description lightweight; heavyweight initialization belongs to capability creation and execution.
  • Create the implementation for any capability ID present in the current snapshot, and report an unknown or uncreatable one as temporarily unavailable rather than as a crash: the plan already names the fallbacks to try instead.

The protocol specifies snapshot retrieval, not the storage mechanism or retention policy used by a registry implementation.

The public indx.capabilities() facade and GET /v1/capabilities operation expose registry snapshots. The planner uses a current or caller-pinned snapshot and records its ID in every RoutePlan; execution validates supplied plans against that decision context.