> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bioptimus.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> Itemized, per-version release notes for the Bioptimus SDK.

<Tip>
  Install or upgrade with `pip install -U bioptimus-sdk`.
</Tip>

<Update label="1.3.0" description="2026-09-18">
  Adds the combined `predict_with_embedding` mode across every backend, an
  in-process Hugging Face backbone loader, slide-level aggregation, and the
  observability surface (support bundles, diagnostic probes, endpoint
  verification, PII anonymization). Also clamps whole-slide reads at slide edges
  and moves the SDK runtime onto zarr 3.

  ### Added

  * Combined inference: `EndpointModel.predict_with_embedding` returns a spatial
    gene-expression prediction and its tile embedding from a single forward pass,
    with whole-slide persistence of both outputs. Available on the `local`,
    `aws`, and `huggingface` backends, exposed over HTTP by the inference server,
    and served by one M-Optimus forward rather than two. Requesting it on an
    embedding-only model raises rather than silently degrading.
  * In-process Hugging Face backend for embedding backbones, selected with
    `Backbone(..., backend="huggingface")`. Configuration-driven, with
    backend-aware model discovery.
  * `huggingface` optional-dependency extra for the in-process Hugging Face
    backbone loader. Install with `pip install bioptimus-sdk[huggingface]` to pull
    `timm`, `huggingface_hub`, and the PyTorch stack. Missing `timm`/
    `huggingface_hub` now raise an `ImportError` pointing at the extra.
  * Slide-level aggregation utilities in `bioptimus.inference`: `slide_embedding`
    (pools a slide's tile embeddings into one vector) and `pseudobulk` (pools
    predicted spatial gene expression into a bulk RNA-seq–like `Pseudobulk`
    profile with gene names when available). Both build on the pure `slide_reduce`
    helper (with a `slide_mean` shorthand), operate on written `predict`/`embed`
    output (Zarr/HDF5/NPZ), and can optionally save the vector next to the input
    as `<name>.slide.npy` when `save=True`. A `pool` selector (`"mean"` default,
    `"max"`, `"sum"`, `"median"`) chooses the reduction; also on
    `SlideInference.embed_slide` / `predict_pseudobulk`.
  * Endpoint verification: `bioptimus.observability.healthcheck.verify_endpoint`
    sends one synthetic tile through a deployed endpoint on any backend
    (`remote`, `aws`, `local`, `huggingface`) and validates that the request
    succeeds, that the response matches the model's declared output contract
    (length, finiteness, tile echo), and that failures carry an `ErrorCode` and a
    remediation hint. Returns a `HealthReport` (JSON-serializable) and is fail-safe
    by default, or raises with `raise_on_failure=True` for CI gating. Exposed as
    `bioptimus verify --model h1 --backend remote --base-url ...` (`--json` for
    machine-readable output, non-zero exit on failure) and as
    `build_support_bundle(verify={...})`, which adds a `healthcheck.json` artifact
    to the diagnostic bundle. New `ErrorCode.ENDPOINT_CONTRACT_MISMATCH`
    (`BIO-NET-003`) covers a reachable endpoint whose response does not match the
    requested model.
  * Anonymized support bundles and diagnostic probes for reporting a failure
    without shipping slide data: `build_support_bundle` collects environment,
    configuration and probe results, the server exposes `/diagnostics`, and both
    are reachable from the `bioptimus` CLI.
  * Configurable PII anonymization primitives, applied to log fields and support
    bundles so file paths and identifiers can be redacted at the boundary.
  * Coordinate-driven tile extraction: an explicit tile-coordinate list can drive
    a whole-slide run instead of the tissue mask, for spatial gene-expression
    benchmarking against a fixed grid.
  * Warning when a precomputed tissue mask's aspect ratio does not match the
    slide's, which previously produced silently misaligned tiles.
  * One-time per-slide warning when a non-pyramidal slide forces a Level 0
    downsample fallback, so the resulting read cost is visible.

  ### Changed

  * Whole-slide inference modes are unified behind an `InferenceMode` enum, with a
    coercion shim that still accepts the legacy mode strings. An unknown mode now
    raises instead of being silently ignored.
  * The SDK runtime supports zarr 3, and Zarr writes are buffered by chunk rather
    than issued per tile.
  * Slide pooling accumulates in float64 without an upcast copy of the tile
    matrix, so aggregating a large slide no longer doubles peak memory.
  * CUDA version detection recognizes Blackwell devices.
  * The published changelog is scoped to the `bioptimus-sdk` package; repo-wide
    changes stay in the internal root changelog and are not shipped.

  ### Fixed

  * Whole-slide reads at slide edges are clamped to the slide bounds, both at
    Level 0 and in read-level space, and the cuCIM and OpenSlide backends now
    share one read path instead of clamping differently.
  * Display helpers no longer append MPP and objective units to a value that
    already carries them.
  * The single-WSI coordinate guard is enforced on every entry point, and tiles
    are anchored on tile size rather than on the requested stride.
  * An empty bulk-RNA input raises a typed `RNA_EMPTY_FILE` error instead of
    failing later with an opaque message.
  * The server reports a terminal unhealthy state when every model fails to load,
    rather than staying up with no usable model.
  * Non-finite MPP and magnification values in slide metadata are rejected
    instead of propagating `NaN` into tile geometry.
  * A single failing tile no longer aborts the slide: per-tile failures are
    isolated in `TorchModel.forward` and reported individually.
  * Bulk RNA file extensions resolve deterministically, and a non-mapping
    workspace configuration or an unrecognized `create_transform` keyword argument
    raises a typed error rather than a bare `KeyError`.
  * `AWSClient` rejects a non-object JSON payload, and the async client creates a
    one-shot `aiohttp` session when none is supplied instead of reusing a closed
    one.
  * In-process checkpoint load failures are classified with an `ErrorCode`,
    embedding width is selected by named `.pt2` output, and a failed
    support-bundle health check keeps its typed error code.
</Update>

<Update label="1.2.0" description="2026-07-28">
  The first release on the 1.2 line, shipping the in-process `local` backend
  alongside SDK observability, omics-preprocessing fixes, and a PyTorch adapter
  for endpoint-backed models.

  <Note>
    **Breaking change.** The `MultiModelData` dataset was renamed to
    `MultiModalData` and moved from
    `bioptimus.data.multi_model.multi_model_dataset` to
    `bioptimus.data.multi_modal.multi_modal_dataset`. No compatibility alias is
    provided: code importing the old name or module path must be updated. The
    class joins a single WSI with optional bulk RNA data across modalities
    (histology + transcriptomics), so *multi-modal* is the accurate term; the old
    name also collided conceptually with the unrelated multi-model inference
    server.
  </Note>

  ### Added

  * Notify SDK users when a newer release is available: on first `Backbone` (or
    `Inference`) creation the installed version is compared against the latest
    stable release on PyPI and a one-time warning is logged if it is behind.
  * Structured JSON logging, typed error propagation, and boundary logging across
    the SDK.
  * `bioptimus.utils.pytorch.TorchModel`, an `nn.Module` adapter that turns tiles
    into endpoint requests and returns stacked tensors, plus a demo notebook
    comparing Hugging Face H1 embeddings against endpoint embeddings.

  ### Changed

  * Rename `MultiModelData` to `MultiModalData` and move the module to
    `bioptimus/data/multi_modal/`. See the breaking-change note above.
  * Clarify the `WSIReader.mpp_downsample` error when a requested target MPP is
    finer than the slide's native resolution: the message now reports the slide's
    actual native MPP instead of an internal epsilon-adjusted threshold, and
    states that downsampling can only raise MPP. The non-positive-MPP case gets
    its own message.

  ### Fixed

  * Preserve the missing-gene sentinel (`-1`) through the server-side `log1p`
    bulk RNA preprocess. Previously `log1p(-1)` produced `-inf`, yielding `NaN`
    embeddings that failed `ModelResponse` validation with one error per output
    dimension.
  * Strip oversized ICC color profiles before PNG-encoding tiles, fixing
    `ValueError: Decompressed Data Too Large` on Focus slides and any slide whose
    embedded ICC profile exceeds Pillow's 1 MB text-chunk limit.
  * Validate prediction-writer patch indices against the allocated tile count so
    an out-of-range index raises `IndexError` instead of writing to an unexpected
    row or silently wrapping to the tail.
  * Preserve the missing-gene sentinel through `TPMNormaliser`, excluding
    sentinels from the row total so the scale is not skewed by a meaningless
    negative sum.
  * Raise a clear `ValueError` on a header-only bulk RNA file instead of failing
    later with an opaque error, and validate `SubsetColumns` indices.
  * Skip a malformed model config entry instead of aborting the whole model load.
  * Keep batch runs resilient to a single failing slide rather than aborting the
    remaining slides.
  * Handle zero-pixel and zero-tile slides without raising.
</Update>

## Prior releases

Release notes for versions published before this changelog was introduced are
not reconstructed here. See the release history on
[PyPI](https://pypi.org/project/bioptimus-sdk/#history).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.