Skip to content

Executor

The execution port: run a plan into blocks, embeddings, and a trace.

Port: encode(request: EncodeRequest) -> EncodeResult, reached as indx.encode(). A stage rather than an extension point, so no Executor protocol is declared: DocumentExecutor is the one implementation and nothing outside this workspace provides another.

Executor is the boundary for turning a route plan into processed document data. It accepts a source, output and embedding preferences, constraints, and an optional plan. It returns the plan used, an addressable block tree, embedding space descriptions, an execution trace, and aggregate usage.

Execution invokes the capability selected for each scope and may proceed through only the ordered fallbacks declared in the plan. It records what actually ran and distinguishes completed, failed, and unreadable outcomes.

The executor keeps expensive and failure-prone work separate from planning. The planner can remain deterministic and inspectable, while execution can report real latency, cost, quality, resource use, validation results, and fallback behavior.

Treating the plan as an execution boundary also protects caller intent. A supplied plan is either compatible with the source and snapshot or rejected; it is never silently replaced with a newly chosen route.

The input is EncodeRequest, which carries a request ID, source, constraints, requested granularities, embedding-space IDs, signature-detection flag, optional RoutePlan, and metadata — the caller’s own labels, carried onto the document block and enforced nowhere. That field is on EncodeRequest and deliberately not on PlanRequest: plan_id hashes every field of the plan it is decided into, so a tenant label would give two identical documents two plans for a value that routes nothing. It refuses the keys languages and classification, which the executor writes from an installed language detector and from the classifiers the request enabled under classification – IDs in the order they are asked, named per unit. It refuses entities the same way, which the executor writes from the entity extractors a request enabled under extraction. Nothing bounds how much of the document an annotation is shown: each is handed the full text of its unit and applies its own bound if it needs one.

Annotations are refused before the source is fetched, never mid-run: an ID nothing installed declares is a 422 enumerating the ones that are; an implementation whose device is external under a data_residency constraint is a 422 rather than a silent omission; and naming a chunk-targeted classifier or extractor without asking for CHUNK granularity is a 422 too, because there would be no chunks to annotate and an empty answer is indistinguishable from no opinion. The last is the same refusal REGION already gets, for the same reason.

The output is EncodeResult, which carries:

  • The request and document IDs and the exact plan used.
  • Addressable document, page, region, and chunk blocks.
  • Text, metadata, provenance, status, and embeddings on those blocks. The document block’s provenance names the media type and the source URI or filename beside the plan it came from; its metadata carries the caller’s labels, where a detector is installed the languages it was read in, where the request enabled a classifier the facets it was labelled with, and where it enabled an entity extractor the spans it found, keyed by the block each was found in, with document-level counts beside them.
  • The complete embedding-space descriptors needed to interpret vectors.
  • A scoped execution trace identifying planned and actual capabilities.
  • Aggregate bytes, pages, cost, latency, quality, and resource use.
  • Obtain a plan before capability execution when the request does not supply one, through the composed encoding workflow.
  • Validate a supplied plan against the source digest, media type and capability snapshot.
  • Execute only selected capabilities or fallbacks explicitly declared by the plan.
  • Preserve scope and parent relationships in the returned block tree.
  • Identify every vector by embedding-space and embedder ID and honor the declared vector dimension.
  • Record actual capability, device, fallback index, validation, latency, cost, and quality for each trace event.
  • Report unreadable scopes and terminal failures explicitly rather than silently omitting content.

An unsatisfied plan is reported, not raised. Nothing conflicts and the request was valid: the caller’s constraints simply cannot be met by what is installed. That outcome is a failed document block carrying the reason and a failed trace saying the same, which keeps the constraints visible instead of flattening them into an error string.

Validation of what a capability produced is first-party and not a declared port, and it asks three questions of every page a rung was given. Did anything come back for it? Did the capability declare the page failed? And did it report a confidence below the floor this deployment set in INDX_VALIDATION_MIN_CONFIDENCE, which is 0.0 – off – unless an operator has measured one against their own output? Any of the three sends that page, and only that page, to the next rung, and the refused output is discarded rather than carried alongside its replacement. An unreadable status is deliberately not one of the three: it is a verdict about the content rather than the attempt.

A route whose every capability failed is a 503. The routing ladder ends in manual review by construction, so exhausting one means an installation that stripped the terminal fallback out – a deployment fault the caller should hear about, rather than a document quietly returned with pages missing.

The protocol does not prescribe adapter internals or how the composed service obtains a missing plan. Those choices remain behind the boundary.

The public indx.encode() facade and POST /v1/encode operation expose the encoding workflow. The executor consumes planning decisions and capability implementations but returns only public indx-interfaces contracts.