Public Backend API

Backend packages should import this public surface, not the internal app.adapters modules. sfmapi ships no real backend. It provides layered backend protocols, registry hooks, contract checkers, and a no-op stub for tests / ephemeral demos.

Action-only packages can satisfy the minimal Backend protocol and expose native tools through backend actions. Complete engines can satisfy SfmBackend and implement the full portable feature, match, mapping, refinement, and export surface.

Stable backend-authoring API for sfmapi plugins.

class sfmapi.backends.ArtifactConversionBackend(*args, **kwargs)[source]

Bases: Backend, Protocol

Convert one stage artifact format into another.

convert_artifact(*, input_artifact, output_dir, to_format, to_kind=None, options=None)[source]

Convert an artifact and return one or more artifact descriptors.

Parameters:
  • input_artifact (dict[str, Any])

  • output_dir (Path)

  • to_format (str)

  • to_kind (str | None)

  • options (dict[str, Any] | None)

Return type:

dict[str, Any]

class sfmapi.backends.Backend(*args, **kwargs)[source]

Bases: BackendIdentity, Protocol

Minimum contract every registered backend must satisfy.

capabilities()[source]

Portable sfmapi capability names this backend implements.

This set must contain only names from app.core.capabilities.ALL_KNOWN. Backend-native tools belong in the action catalog, not here.

Return type:

set[str]

runtime_versions()[source]

Engine/dependency versions that influence cache identity.

Return type:

dict[str, str]

class sfmapi.backends.BackendActionProvider(*args, **kwargs)[source]

Bases: Protocol

Optional structural protocol implemented by richer backends.

list_backend_actions(*, include_schemas=False)[source]
Parameters:

include_schemas (bool)

Return type:

list[dict[str, Any]]

get_backend_action(action_id)[source]
Parameters:

action_id (str)

Return type:

dict[str, Any]

validate_backend_action(action_id, inputs)[source]
Parameters:
  • action_id (str)

  • inputs (dict[str, Any])

Return type:

dict[str, Any]

run_backend_action(action_id, inputs, *, workspace=None, progress=None)[source]
Parameters:
  • action_id (str)

  • inputs (dict[str, Any])

  • workspace (Path | None)

  • progress (Any | None)

Return type:

dict[str, Any]

class sfmapi.backends.BackendArtifactContractProvider(*args, **kwargs)[source]

Bases: Protocol

Optional structural protocol for backends with explicit artifact I/O.

list_backend_artifact_contracts()[source]
Return type:

list[dict[str, Any]]

class sfmapi.backends.BackendConfigSchemaProvider(*args, **kwargs)[source]

Bases: Protocol

Optional structural protocol implemented by richer backends.

list_backend_config_schemas(*, include_schemas=True)[source]
Parameters:

include_schemas (bool)

Return type:

list[dict[str, Any]]

class sfmapi.backends.BackendIdentity(*args, **kwargs)[source]

Bases: Protocol

Backend identity fields surfaced by /v1/version and discovery.

property name: str

Canonical short name, e.g. "pycolmap" or "realityscan_cli".

property version: str

Backend adapter version string.

property vendor: str

Optional human-readable vendor or upstream project.

class sfmapi.backends.BatchLocalizationBackend(*args, **kwargs)[source]

Bases: Backend, Protocol

Batch or sequence localization helpers.

localize_batch(**kwargs)[source]

Localize multiple query images.

Parameters:

kwargs (Any)

Return type:

list[Any]

class sfmapi.backends.ExportBackend(*args, **kwargs)[source]

Bases: Backend, Protocol

Portable reconstruction export stages.

export(*, model_path, output_path, format)[source]

Export a sparse model.

Parameters:
  • model_path (Path)

  • output_path (Path)

  • format (str)

Return type:

dict[str, Any]

class sfmapi.backends.FeatureBackend(*args, **kwargs)[source]

Bases: Backend, Protocol

Portable feature, pair-selection, matching, and verification stages.

extract_features(*, database_path, image_root, image_list, options)[source]

Run feature extraction over image_list into the SfM database.

Parameters:
  • database_path (Path)

  • image_root (Path)

  • image_list (list[str])

  • options (dict[str, Any])

Return type:

dict[str, Any]

match(*, database_path, mode, options)[source]

Run feature matching for the given pair-selection strategy.

Parameters:
  • database_path (Path)

  • mode (str)

  • options (dict[str, Any])

Return type:

dict[str, Any]

verify_matches(*, database_path, options)[source]

Run geometric verification on existing matches.

Parameters:
  • database_path (Path)

  • options (dict[str, Any])

Return type:

dict[str, Any]

class sfmapi.backends.LocalizationBackend(*args, **kwargs)[source]

Bases: Backend, Protocol

Single-image localization helpers.

localize_from_memory(*, sparse_dir, query_image, spec)[source]

Localize a query image against a sparse model.

Parameters:
  • sparse_dir (Path)

  • query_image (Path)

  • spec (dict[str, Any])

Return type:

dict[str, Any]

class sfmapi.backends.MappingBackend(*args, **kwargs)[source]

Bases: Backend, Protocol

Portable mapping stages.

run_mapping(*, kind, db_path, image_root, sparse_root, job_dir, spec, pose_priors=None)[source]

Run incremental/global/hierarchical/spherical mapping.

Parameters:
  • kind (str)

  • db_path (Path)

  • image_root (Path)

  • sparse_root (Path)

  • job_dir (Path)

  • spec (dict[str, Any])

  • pose_priors (dict[str, Any] | None)

Return type:

tuple[list[dict[str, Any]], list[Any]]

class sfmapi.backends.NoopProgressReporter[source]

Bases: object

Drop-in reporter for tests and backend code paths without a job.

phase_started(phase)[source]
Parameters:

phase (Literal['feature_extraction', 'matching', 'geometric_verification', 'incremental_init', 'incremental_register', 'incremental_ba', 'global_rotation_avg', 'global_positioning', 'global_ba', 'hierarchical_cluster', 'hierarchical_merge', 'panorama', 'spherical', 'bundle_adjust', 'triangulate', 'relocalize', 'pose_graph_optimize', 'segment', 'export', 'vlad_index', 'backend_action', 'artifact_conversion'])

Return type:

None

phase_progress(phase, *, current, total=None, rate=None)[source]
Parameters:
  • phase (Literal['feature_extraction', 'matching', 'geometric_verification', 'incremental_init', 'incremental_register', 'incremental_ba', 'global_rotation_avg', 'global_positioning', 'global_ba', 'hierarchical_cluster', 'hierarchical_merge', 'panorama', 'spherical', 'bundle_adjust', 'triangulate', 'relocalize', 'pose_graph_optimize', 'segment', 'export', 'vlad_index', 'backend_action', 'artifact_conversion'])

  • current (int)

  • total (int | None)

  • rate (float | None)

Return type:

None

phase_completed(phase)[source]
Parameters:

phase (Literal['feature_extraction', 'matching', 'geometric_verification', 'incremental_init', 'incremental_register', 'incremental_ba', 'global_rotation_avg', 'global_positioning', 'global_ba', 'hierarchical_cluster', 'hierarchical_merge', 'panorama', 'spherical', 'bundle_adjust', 'triangulate', 'relocalize', 'pose_graph_optimize', 'segment', 'export', 'vlad_index', 'backend_action', 'artifact_conversion'])

Return type:

None

metric(key, value)[source]
Parameters:
  • key (str)

  • value (float)

Return type:

None

snapshot_available(*, snapshot_seq, summary)[source]
Parameters:
  • snapshot_seq (int)

  • summary (dict[str, Any])

Return type:

None

log_line(level, message)[source]
Parameters:
  • level (Literal['DEBUG', 'INFO', 'WARNING', 'ERROR'])

  • message (str)

Return type:

None

warning(message)[source]
Parameters:

message (str)

Return type:

None

error(*, error_class, message, detail=None)[source]
Parameters:
  • error_class (str)

  • message (str)

  • detail (dict[str, Any] | None)

Return type:

None

class sfmapi.backends.ObservationBackend(*args, **kwargs)[source]

Bases: Backend, Protocol

Read observation sidecars from a backend feature/match store.

read_keypoints(*, database_path, image_id)[source]

Read keypoints and descriptors for one image.

Parameters:
  • database_path (Path)

  • image_id (int)

Return type:

tuple[list[list[float]], bytes, int]

iter_two_view_geometries(*, database_path)[source]

Yield verified two-view geometry rows.

Parameters:

database_path (Path)

Return type:

Iterator[tuple[int, int, Any]]

iter_correspondences(*, database_path)[source]

Yield raw match correspondences.

Parameters:

database_path (Path)

Return type:

Iterator[tuple[int, int, Any]]

class sfmapi.backends.ProgressReporter(*args, **kwargs)[source]

Bases: Protocol

Best-effort sink for backend progress telemetry.

The reporter writes the existing ProgressEvent wire shape. It must never be required for backend correctness: implementations should tolerate reporter failures and keep the reconstruction work moving.

phase_started(phase)[source]
Parameters:

phase (Literal['feature_extraction', 'matching', 'geometric_verification', 'incremental_init', 'incremental_register', 'incremental_ba', 'global_rotation_avg', 'global_positioning', 'global_ba', 'hierarchical_cluster', 'hierarchical_merge', 'panorama', 'spherical', 'bundle_adjust', 'triangulate', 'relocalize', 'pose_graph_optimize', 'segment', 'export', 'vlad_index', 'backend_action', 'artifact_conversion'])

Return type:

None

phase_progress(phase, *, current, total=None, rate=None)[source]
Parameters:
  • phase (Literal['feature_extraction', 'matching', 'geometric_verification', 'incremental_init', 'incremental_register', 'incremental_ba', 'global_rotation_avg', 'global_positioning', 'global_ba', 'hierarchical_cluster', 'hierarchical_merge', 'panorama', 'spherical', 'bundle_adjust', 'triangulate', 'relocalize', 'pose_graph_optimize', 'segment', 'export', 'vlad_index', 'backend_action', 'artifact_conversion'])

  • current (int)

  • total (int | None)

  • rate (float | None)

Return type:

None

phase_completed(phase)[source]
Parameters:

phase (Literal['feature_extraction', 'matching', 'geometric_verification', 'incremental_init', 'incremental_register', 'incremental_ba', 'global_rotation_avg', 'global_positioning', 'global_ba', 'hierarchical_cluster', 'hierarchical_merge', 'panorama', 'spherical', 'bundle_adjust', 'triangulate', 'relocalize', 'pose_graph_optimize', 'segment', 'export', 'vlad_index', 'backend_action', 'artifact_conversion'])

Return type:

None

metric(key, value)[source]
Parameters:
  • key (str)

  • value (float)

Return type:

None

snapshot_available(*, snapshot_seq, summary)[source]
Parameters:
  • snapshot_seq (int)

  • summary (dict[str, Any])

Return type:

None

log_line(level, message)[source]
Parameters:
  • level (Literal['DEBUG', 'INFO', 'WARNING', 'ERROR'])

  • message (str)

Return type:

None

warning(message)[source]
Parameters:

message (str)

Return type:

None

error(*, error_class, message, detail=None)[source]
Parameters:
  • error_class (str)

  • message (str)

  • detail (dict[str, Any] | None)

Return type:

None

class sfmapi.backends.ReconstructionMergeBackend(*args, **kwargs)[source]

Bases: Backend, Protocol

Multi-reconstruction merge helpers.

merge_reconstructions(*, model_paths, output_path, sim3_aligners=None)[source]

Merge multiple sparse models into one output model.

Parameters:
  • model_paths (list[Path])

  • output_path (Path)

  • sim3_aligners (Any)

Return type:

dict[str, Any]

class sfmapi.backends.ReconstructionReaderBackend(*args, **kwargs)[source]

Bases: Backend, Protocol

Read a sparse model into a snapshot-emitter-compatible object.

read_reconstruction(path)[source]

Return a duck-typed reconstruction object.

Parameters:

path (Path)

Return type:

Any

class sfmapi.backends.RefinementBackend(*args, **kwargs)[source]

Bases: Backend, Protocol

Portable refinement and model-edit stages.

bundle_adjustment(*, model_path, output_path, spec)[source]

Run bundle adjustment.

Parameters:
  • model_path (Path)

  • output_path (Path)

  • spec (dict[str, Any])

Return type:

dict[str, Any]

triangulate(*, model_path, database_path, image_root, output_path)[source]

Re-triangulate against an existing database.

Parameters:
  • model_path (Path)

  • database_path (Path)

  • image_root (Path)

  • output_path (Path)

Return type:

dict[str, Any]

relocalize(*, model_path, database_path, image_root, output_path, image_ids)[source]

Register additional images into an existing reconstruction.

Parameters:
  • model_path (Path)

  • database_path (Path)

  • image_root (Path)

  • output_path (Path)

  • image_ids (list[int])

Return type:

dict[str, Any]

pose_graph_optimize(*, model_path, output_path, spec)[source]

Run pose-graph optimization.

Parameters:
  • model_path (Path)

  • output_path (Path)

  • spec (dict[str, Any])

Return type:

dict[str, Any]

class sfmapi.backends.RetrievalBackend(*args, **kwargs)[source]

Bases: Backend, Protocol

Image retrieval and similarity helpers.

build_vlad_index(*, image_paths_by_id, spec)[source]

Compute VLAD descriptors for a set of images.

Parameters:
  • image_paths_by_id (dict[str, Path])

  • spec (dict[str, Any])

Return type:

tuple[list[str], Any]

class sfmapi.backends.SfmBackend(*args, **kwargs)[source]

Bases: FeatureBackend, ObservationBackend, MappingBackend, RefinementBackend, ExportBackend, SphericalBackend, RetrievalBackend, LocalizationBackend, BatchLocalizationBackend, TransformBackend, ReconstructionReaderBackend, ReconstructionMergeBackend, ArtifactConversionBackend, Protocol

Full portable SfM backend protocol kept for complete engines.

New backend packages should usually target the smallest protocol layer they actually implement and expose native tools through backend actions where no portable stage contract exists.

class sfmapi.backends.SphericalBackend(*args, **kwargs)[source]

Bases: Backend, Protocol

Spherical/panorama conversion helpers.

project_images(*, operation, input_image_path, output_path, spec)[source]

Run a portable image projection transform over a dataset.

Parameters:
  • operation (str)

  • input_image_path (Path)

  • output_path (Path)

  • spec (dict[str, Any])

Return type:

dict[str, Any]

convert_spherical_to_cubemap(*, input_model_path, input_image_path, output_path)[source]

Convert a spherical reconstruction to a cubemap rig.

Parameters:
  • input_model_path (Path)

  • input_image_path (Path)

  • output_path (Path)

Return type:

dict[str, Any]

render_spherical_cubemap_images(*, input_image_path, output_path, face_size=None)[source]

Render every panorama into six cubemap face images.

Parameters:
  • input_image_path (Path)

  • output_path (Path)

  • face_size (int | None)

Return type:

dict[str, Any]

class sfmapi.backends.TransformBackend(*args, **kwargs)[source]

Bases: Backend, Protocol

Geometry transform helpers.

apply_sim3(*, model_path, output_path, sim3)[source]

Apply a Sim(3) similarity transform to a sparse model.

Parameters:
  • model_path (Path)

  • output_path (Path)

  • sim3 (dict[str, Any])

Return type:

dict[str, Any]

sfmapi.backends.accepts_progress(call)[source]

Return true when call can receive progress=....

Native-extension callables do not always expose inspectable signatures. In that case the safe compatibility choice is to omit the optional argument.

Parameters:

call (Callable[[...], Any])

Return type:

bool

sfmapi.backends.assert_backend_action_contract(backend)[source]

Raise AssertionError if a backend mixes capabilities and actions.

Parameters:

backend (Any)

Return type:

None

sfmapi.backends.assert_backend_artifact_contract(backend)[source]
Parameters:

backend (Any)

Return type:

None

sfmapi.backends.assert_backend_config_contract(backend)[source]

Raise AssertionError if backend option schemas are malformed.

Parameters:

backend (Any)

Return type:

None

sfmapi.backends.assert_backend_contract(backend)[source]

Raise AssertionError if backend extension metadata is unsafe.

Parameters:

backend (Any)

Return type:

None

sfmapi.backends.backend_action_contract_violations(backend)[source]

Return backend action contract violations for backend authors.

Backend packages can use this in their own test suites:

from app.adapters.backend_actions import assert_backend_action_contract

def test_backend_contract():
    assert_backend_action_contract(MyBackend())

This catches the most common extension-surface mistake: putting backend-native action ids such as vendor.tool into capabilities() instead of exposing them through list_backend_actions().

Parameters:

backend (Any)

Return type:

list[str]

sfmapi.backends.backend_artifact_contract_violations(backend)[source]
Parameters:

backend (Any)

Return type:

list[str]

sfmapi.backends.backend_capability_contract_violations(backend)[source]

Return capability-specific contract violations for backend authors.

Parameters:

backend (Any)

Return type:

list[str]

sfmapi.backends.backend_config_contract_violations(backend)[source]

Return backend config-schema contract violations for backend authors.

Parameters:

backend (Any)

Return type:

list[str]

sfmapi.backends.backend_contract_violations(backend)[source]

Return all extension-contract violations for a backend.

Parameters:

backend (Any)

Return type:

list[str]

sfmapi.backends.call_with_optional_progress(call, *, progress, **kwargs)[source]

Call a backend method, adding progress only when supported.

Parameters:
  • call (Callable[[...], Any])

  • progress (ProgressReporter | None)

  • kwargs (Any)

Return type:

Any

sfmapi.backends.get_backend(name=None)[source]

Resolve and instantiate the configured backend.

name overrides the env var when set (mostly for tests). Raises KeyError if no backend is registered under the resolved name — sfmapi ships no default implementation, so the caller (or test fixture, or app-startup hook) must register one before workers can run.

Parameters:

name (str | None)

Return type:

Backend

sfmapi.backends.get_backend_action(action_id, backend=None)[source]

Read one normalized action descriptor with its schema.

Parameters:
  • action_id (str)

  • backend (Any | None)

Return type:

dict[str, Any]

sfmapi.backends.get_backend_artifact_contract(contract_id, backend=None)[source]
Parameters:
  • contract_id (str)

  • backend (Any | None)

Return type:

dict[str, Any]

sfmapi.backends.get_backend_config_schema(config_id, backend=None)[source]
Parameters:
  • config_id (str)

  • backend (Any | None)

Return type:

dict[str, Any]

sfmapi.backends.has_backend_actions(backend=None)[source]

Cheap capability probe for the public capabilities envelope.

Parameters:

backend (Any | None)

Return type:

bool

sfmapi.backends.has_backend_artifact_contracts(backend=None)[source]
Parameters:

backend (Any | None)

Return type:

bool

sfmapi.backends.has_backend_config_schemas(backend=None)[source]
Parameters:

backend (Any | None)

Return type:

bool

sfmapi.backends.has_backend_method(backend, method_name)[source]

Return whether backend exposes a callable method.

Parameters:
  • backend (object)

  • method_name (str)

Return type:

bool

sfmapi.backends.list_backend_actions(backend=None, *, include_schemas=False)[source]

List normalized backend action descriptors.

Parameters:
  • backend (Any | None)

  • include_schemas (bool)

Return type:

list[dict[str, Any]]

sfmapi.backends.list_backend_artifact_contracts(backend=None)[source]
Parameters:

backend (Any | None)

Return type:

list[dict[str, Any]]

sfmapi.backends.list_backend_config_schemas(backend=None, *, include_schemas=True)[source]

List normalized backend-specific option schemas.

Parameters:
  • backend (Any | None)

  • include_schemas (bool)

Return type:

list[dict[str, Any]]

sfmapi.backends.list_backends()[source]
Return type:

list[str]

sfmapi.backends.register_backend(name, factory)[source]

Register a backend factory. Re-registering an existing name overwrites it (useful for tests).

Parameters:
  • name (str)

  • factory (Callable[[], Backend])

Return type:

None

sfmapi.backends.require_backend_method(backend, method_name, *, capability, reason=None)[source]

Return a backend method or raise a clean capability error.

This is the guard portable workers use before calling optional protocol surfaces. It lets action-only or artifact-only backends omit unsupported methods entirely while preserving sfmapi’s normal 501 response semantics.

Parameters:
  • backend (object)

  • method_name (str)

  • capability (str)

  • reason (str | None)

Return type:

Callable[[…], Any]

sfmapi.backends.run_backend_action(action_id, inputs, *, workspace=None, progress=None, backend=None)[source]

Execute an action through the backend implementation.

Parameters:
  • action_id (str)

  • inputs (dict[str, Any])

  • workspace (Path | None)

  • progress (Any | None)

  • backend (Any | None)

Return type:

dict[str, Any]

sfmapi.backends.validate_backend_action(action_id, inputs, backend=None)[source]

Validate input for one action without submitting work.

Parameters:
  • action_id (str)

  • inputs (dict[str, Any])

  • backend (Any | None)

Return type:

dict[str, Any]

sfmapi.backends.validate_backend_options(*, stage, options, capability=None, provider=None, backend=None)[source]

Validate one stage’s backend_options if the backend exposes a schema.

Backends may omit schemas; in that case sfmapi passes options through and the backend remains the source of truth. When a schema is available, sfmapi catches unknown keys and simple type mistakes before creating a job.

Parameters:
  • stage (str)

  • options (dict[str, Any] | None)

  • capability (str | None)

  • provider (str | None)

  • backend (Any | None)

Return type:

dict[str, Any]

Runtime hooks for embedding sfmapi with backend plugins.

sfmapi.runtime.create_app()[source]

Create the FastAPI app without side effects at sfmapi.runtime import time.

Return type:

FastAPI

sfmapi.runtime.get_backend(name=None)[source]

Resolve and instantiate the configured backend.

name overrides the env var when set (mostly for tests). Raises KeyError if no backend is registered under the resolved name — sfmapi ships no default implementation, so the caller (or test fixture, or app-startup hook) must register one before workers can run.

Parameters:

name (str | None)

Return type:

Backend

sfmapi.runtime.list_backends()[source]
Return type:

list[str]

sfmapi.runtime.load_installed_plugins()[source]

Load installed backend entry points into the process-local registry.

Return type:

list[Any]

sfmapi.runtime.register_backend(name, factory)[source]

Register a backend factory. Re-registering an existing name overwrites it (useful for tests).

Parameters:
  • name (str)

  • factory (Callable[[], Backend])

Return type:

None

sfmapi.runtime.register_plugin(plugin)[source]

Register one plugin object with the process-local backend registry.

Parameters:

plugin (BackendPlugin)

Return type:

None

Stable exception classes for sfmapi extensions.

exception sfmapi.errors.BadRequestError(detail='', **extras)[source]

Bases: SfmApiError

400 — request couldn’t be parsed or violates a non-schema invariant.

Use ValidationError (422) when the request shape is well-formed but a field value is wrong; use this when the request couldn’t even be interpreted (e.g., malformed Content-Range, conflicting query params).

Parameters:
  • detail (str)

  • extras (Any)

Return type:

None

status_code: int = 400
error_type: str = 'bad_request'
title: str = 'Bad request'
exception sfmapi.errors.CapabilityUnavailableError(*, capability, reason='')[source]

Bases: SfmApiError

The requested SfM feature isn’t supported by the current backend.

Returns 501 Not Implemented rather than 5xx because the request itself is well-formed and the server is healthy — it just doesn’t expose the requested capability. The capability extra carries the canonical feature name (see app.core.capabilities) so clients can correlate with GET /v1/capabilities.

Parameters:
  • capability (str)

  • reason (str)

Return type:

None

status_code: int = 501
error_type: str = 'capability_unavailable'
title: str = 'Capability not available in this deployment'
exception sfmapi.errors.ConflictError(detail='', **extras)[source]

Bases: SfmApiError

409 — the request conflicts with the current resource state.

Use for optimistic-concurrency / state-machine violations (e.g., finalizing an upload that’s already finalized, deleting a project with active jobs). Use ValidationError (422) when the request itself is wrong; use this when the request would be valid against a different server state.

Parameters:
  • detail (str)

  • extras (Any)

Return type:

None

status_code: int = 409
error_type: str = 'conflict'
title: str = 'Conflict'
exception sfmapi.errors.NotFoundError(detail='', **extras)[source]

Bases: SfmApiError

404 — the addressed resource doesn’t exist for this tenant.

Routes MUST raise this rather than returning None so the failure shows up as application/problem+json. Tenant scoping is implicit: a row that exists under a different tenant looks like 404 to the caller (see L2 — multi-tenant boundary).

Parameters:
  • detail (str)

  • extras (Any)

Return type:

None

status_code: int = 404
error_type: str = 'not_found'
title: str = 'Resource not found'
exception sfmapi.errors.PycolmapUnavailableError(reason='')[source]

Bases: CapabilityUnavailableError

Backwards-compat: the colmap_mod backend can’t load pycolmap.

Subclass of CapabilityUnavailableError so callers that catch the parent see this too. Status code stays 501 — the failure is the same shape (a capability the deployment doesn’t expose), even though the reason is backend-specific.

Parameters:

reason (str)

Return type:

None

error_type: str = 'pycolmap_unavailable'
title: str = 'pycolmap not available in this deployment'
exception sfmapi.errors.QuotaExceededError(detail='', **extras)[source]

Bases: SfmApiError

429 — caller hit a per-tenant or per-request rate / size cap.

Currently fires on oneshot request-body caps (oneshot_max_request_bytes); Phase 5 plumbs in fair-share scheduling + per-tenant job quotas through this same error. SDK ergonomics shims interpret 429 as retryable when retry_after is set.

Parameters:
  • detail (str)

  • extras (Any)

Return type:

None

status_code: int = 429
error_type: str = 'quota_exceeded'
title: str = 'Quota exceeded'
exception sfmapi.errors.SfmApiError(detail='', **extras)[source]

Bases: Exception

Base class for every domain error sfmapi raises.

Subclasses MUST set status_code (HTTP) and error_type (the slug used to build type: https://sfmapi.github.io/errors/<slug> in the RFC 7807 envelope). The FastAPI exception handler in app/main.py::sfmapi_error_handler turns these into application/problem+json responses; never raise raw HTTPException from services — keep the wire shape consistent.

Free-form extras are merged into the envelope as top-level keys (capability on 501, retry_after on 429); see the typed surface in ProblemResponse.

Parameters:
  • detail (str)

  • extras (Any)

Return type:

None

status_code: int = 500
error_type: str = 'internal'
title: str = 'Internal error'
as_problem(instance=None)[source]
Parameters:

instance (str | None)

Return type:

dict[str, Any]

exception sfmapi.errors.StorageError(detail='', **extras)[source]

Bases: SfmApiError

507 Insufficient Storage — the backing store rejected a write.

Used for blob-store write failures, snapshot rename collisions, workspace-out-of-space conditions. Distinct from a generic 500: the request itself was valid, the storage layer couldn’t hold it.

Parameters:
  • detail (str)

  • extras (Any)

Return type:

None

status_code: int = 507
error_type: str = 'storage'
title: str = 'Storage error'
exception sfmapi.errors.TenantViolationError(detail='', **extras)[source]

Bases: SfmApiError

403 — tenant scoping was bypassed or auth resolution failed.

Raised by app.core.tenancy.current_tenant() when the Authorization header is missing / unrecognized, and by repository helpers that detect a tenant_id mismatch. See L2 in decisions.md (multi-tenant from day 1, default until auth lands). Once real auth ships, 401 (missing) and 403 (rejected) will split — for now both surface here.

Parameters:
  • detail (str)

  • extras (Any)

Return type:

None

status_code: int = 403
error_type: str = 'tenant_violation'
title: str = 'Tenant boundary violation'
exception sfmapi.errors.ValidationError(detail='', **extras)[source]

Bases: SfmApiError

422 — semantic validation failed after parsing succeeded.

Distinct from FastAPI’s fastapi.RequestValidationError (Pydantic schema-shape failures): this fires from inside services when a value is well-typed but rejected on a domain rule (cross-field consistency, foreign-key lookup, plausibility). Both paths emit the same RFC 7807 problem+json shape — see L19 for the structured errors[] invariant.

Parameters:
  • detail (str)

  • extras (Any)

Return type:

None

status_code: int = 422
error_type: str = 'validation'
title: str = 'Validation error'

Stub full backend used by tests + ephemeral mode.

sfmapi ships no real SfM engine; the wire surface and orchestration shell live in this repo, while engine implementations (pycolmap, OpenSfM, hloc, custom forks) live in separate packages. This module provides a no-op stub that satisfies the protocol structurally — useful for:

  • unit / integration / contract tests that exercise routing, scheduling, snapshot writing, etc., without needing a real engine,

  • the SFMAPI_EPHEMERAL=true self-contained demo runtime, which registers it on lifespan startup,

  • SDK live tests that boot the app in a subprocess.

Most operations raise CapabilityUnavailableError — only capabilities() and trivial probes return real values. Production deployments register a real backend via app.adapters.registry.register_backend().

class app.adapters.stub_backend.StubBackend[source]

Bases: object

No-op stub satisfying the full SfmBackend union.

name = 'stub'
version = '0.0.1'
vendor = 'test'
capabilities()[source]
Return type:

set[str]

extract_features(*, database_path, image_root, image_list, options)[source]
Parameters:
  • database_path (Path)

  • image_root (Path)

  • image_list (list[str])

  • options (dict)

Return type:

dict

match(*, database_path, mode, options)[source]
Parameters:
  • database_path (Path)

  • mode (str)

  • options (dict)

Return type:

dict

verify_matches(*, database_path, options)[source]
Parameters:
  • database_path (Path)

  • options (dict)

Return type:

dict

read_keypoints(**_)[source]
Parameters:

_ (Any)

Return type:

tuple[list[list[float]], bytes, int]

iter_two_view_geometries(*, database_path)[source]
Parameters:

database_path (Path)

Return type:

Iterator

iter_correspondences(*, database_path)[source]
Parameters:

database_path (Path)

Return type:

Iterator

run_mapping(**_)[source]
Parameters:

_ (Any)

Return type:

tuple[list[dict], list[Any]]

bundle_adjustment(**_)[source]
Parameters:

_ (Any)

Return type:

dict

triangulate(**_)[source]
Parameters:

_ (Any)

Return type:

dict

relocalize(**_)[source]
Parameters:

_ (Any)

Return type:

dict

pose_graph_optimize(**_)[source]
Parameters:

_ (Any)

Return type:

dict

export(**_)[source]
Parameters:

_ (Any)

Return type:

dict

convert_spherical_to_cubemap(**_)[source]
Parameters:

_ (Any)

Return type:

dict

project_images(**_)[source]
Parameters:

_ (Any)

Return type:

dict

render_spherical_cubemap_images(**_)[source]
Parameters:

_ (Any)

Return type:

dict

build_vlad_index(**_)[source]
Parameters:

_ (Any)

Return type:

dict

convert_artifact(**_)[source]
Parameters:

_ (Any)

Return type:

dict

localize_from_memory(**_)[source]
Parameters:

_ (Any)

Return type:

dict

apply_sim3(**_)[source]
Parameters:

_ (Any)

Return type:

dict

merge_reconstructions(**_)[source]
Parameters:

_ (Any)

Return type:

dict

localize_batch(**_)[source]
Parameters:

_ (Any)

Return type:

list

read_reconstruction(path)[source]
Parameters:

path (Path)

Return type:

Any

runtime_versions()[source]
Return type:

dict[str, str]