Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 31 additions & 6 deletions docs/STABILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,15 @@ source identity reject credential material rather than persist it.
| Module | Level | Notes |
|--------|-------|-------|
| `tributo.exporting.service` — `BundleExportService` | `beta` | Primary export orchestration |
| `tributo.exporting.models` — all public models | `beta` | Configuration, artifact, canonical Bundle reference, and exact publication result models |
| `tributo.exporting.models` — core storage values | `stable` | AliasConfig, artifact/producer/reference/validation values, execution/failure results, BundleResult, BundleRef, ResolvedArtifact, and PublishedBundle |
| `tributo.exporting.models` — export configuration, Hook and plugin values | `beta` | Exporter orchestration and Hook payloads retain independent contracts |
| `tributo.exporting.protocols` — all protocols | `beta` | Exporter/Validator/SourceProvider contracts |
| `tributo.exporting.manifest` — `ExportManifest` | `beta` | Bundle manifest (schema v1) |
| `tributo.exporting.bundle_reader` — `BundleReader` | `beta` | Repository-routed Bundle consumption with exact manifest-byte and artifact verification |
| `tributo.exporting.manifest` — schema-v1 models and registry | `stable` | Source info, typed signatures, execution records, ExportManifest, and schema reader registration |
| `tributo.exporting.manifest` — `ExportManifestV2` and `compute_bundle_digest` | `beta` | Explainability extension and compatibility digest helper |
| `tributo.exporting.bundle_reader` — `BundleReader` | `stable` | Repository-routed Bundle consumption with exact manifest-byte and artifact verification |
| `tributo.exporting.planner` | `beta` | Export plan builder |
| `tributo.exporting.executor` | `beta` | Export executor |
| `tributo.exporting.publisher` | `beta` | Bundle publisher |
| `tributo.exporting.publisher` | `stable` | Local/file/S3 core publication; the optional Explainability parameter remains Alpha |
| `tributo.exporting.validators` | `beta` | Artifact validator runner |
| `tributo.exporting.registries` | `beta` | Exporter/validator registries |
| `tributo.exporting.options` | `beta` | Compatibility re-exports; schemas are owned by integration exporters |
Expand All @@ -173,10 +175,33 @@ source identity reject credential material rather than persist it.
| `tributo.exporting.hooks` | `beta` | Adapter and committed-artifact access contracts |
| `tributo.exporting.dispatch` | `beta` | Inline Hook dispatch policy |
| `tributo.exporting.capabilities` | `beta` | Exporter/Flavor-derived capability declarations |
| `tributo.exporting.repository` | `beta` | Bundle repository and alias store ports |
| `tributo.exporting.repository` | `stable` | Bundle repository/alias ports and their value objects; internal routing remains DeveloperAPI |
| `tributo.exporting.runtime` | `beta` | Bundle model runtime and Flavor protocol |
| `tributo.exporting.conftest` | `beta` | Public plugin conformance test kit |

The Stable storage model allowlist is `AliasConfig`, `ArtifactFile`,
`ProducerInfo`, `ArtifactRef`, `LogicalArtifact`, `ResolvedArtifact`,
`FailureInfo`, `NodeResult`, `ExportExecutionResult`, `BundleResult`, `BundleRef`,
`PublishedBundle`, and `ValidationResult`. The Stable manifest allowlist is
`ManifestSourceInfo`, `SignatureField`, `ManifestSignature`,
`ManifestExecutionNode`, `ManifestExecution`, `ExportManifest`, and
`ManifestSchemaRegistry`. `BundleCommitBusyError` and `AliasConflict` are Stable
storage errors. The high-level `export`, `ExportSpec`, and `load_bundle`
compatibility facade remain Beta.

The Stable promise covers the core storage operation and fields. Passing Alpha
`ExplainabilityConfig` opts into its independent contract, and
`ExportManifestV2` remains Beta. `BundleResult.hook_receipts` keeps the Beta
`HookReceipt` payload; Hook delivery and orchestration do not become Stable.
Developer router/assembler injection remains an advanced extension with its
existing constructor parameters. No signature or extension payload is removed.

`ResolvedArtifact` paths are valid inside their materialization context.
`PublishedBundle.local_dir_ephemeral` distinguishes transient S3 staging from
persistent local publication. Ephemeral paths belong to the staging owner;
BundleExportService guarantees their callback window. Consumers persist
`BundleRef` or `BundleResult.canonical_uri` instead of transient local paths.

### Explainability (tributo.explainability.*)

| Module | Level | Notes |
Expand Down Expand Up @@ -288,7 +313,7 @@ source identity reject credential material rather than persist it.

| Module | Level | Notes |
|--------|-------|-------|
| `tributo._common.storage_profiles` | `beta` | Storage profile resolution |
| `tributo._common.storage_profiles` | `stable` | S3 profile values, named/default resolution, and credential-safe diagnostics |
| `tributo._common.dependencies` | `beta` | Unified dependency probing layer |
| `tributo._common` | `developer` | Internal shared package |
| All other `tributo._common.*` | `developer` | Internal shared utilities |
Expand Down
20 changes: 20 additions & 0 deletions docs/reference/api/model-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,14 @@ documentation for every public stability tier.
:no-members:
```

```{autoclass} tributo.exporting.models.PublishedBundle
:no-members:
```

```{autoclass} tributo.exporting.models.ResolvedArtifact
:no-members:
```

```{autoclass} tributo.exporting.models.SupportRequest
:no-members:
```
Expand Down Expand Up @@ -331,6 +339,10 @@ documentation for every public stability tier.

## `tributo.exporting.repository`

```{autoclass} tributo.exporting.repository.AliasUpdate
:no-members:
```

```{autoclass} tributo.exporting.repository.BundleAliasStore
:no-members:
```
Expand All @@ -343,6 +355,14 @@ documentation for every public stability tier.
:no-members:
```

```{autoclass} tributo.exporting.repository.RepositoryCommit
:no-members:
```

```{autoclass} tributo.exporting.repository.StagedBundle
:no-members:
```


## `tributo.exporting.runtime`

Expand Down
5 changes: 3 additions & 2 deletions docs/reference/support-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,9 @@ matrix are generated from the same Registry projection.

| Capability | Status | Boundary |
| --- | --- | --- |
| Local and `file://` bundle publication | Beta | Manifest and digest validation |
| S3 bundle publication | Beta | Manifest-last and alias compare-and-set |
| Local and `file://` bundle publication | Stable | Core Publisher/repository contracts, immutable identity, manifest and digest validation; Explainability and Hook extensions retain their own levels |
| S3 bundle publication | Stable | Core Publisher/repository contracts, manifest-last, leases, idempotency, and alias compare-and-set |
| Bundle manifest/artifact reading and integrity | Stable | BundleReader, schema-v1 fields, exact committed bytes, digest/identity checks, bounded materialization, and context lifetime; extension payload semantics are independent |
| HDFS bundle publication | Not implemented | Storage backend extension |
| Ray Data batch inference | Beta | Actor-based model reuse |
| Batch output to local/S3 Parquet | Implemented | Database sinks are separate |
Expand Down
15 changes: 10 additions & 5 deletions src/tributo/_common/storage_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,15 @@
from tributo.util.annotations import PublicAPI


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
@dataclass(frozen=True)
class StorageProfile:
"""Resolved S3-compatible storage parameters."""
"""Resolved S3-compatible storage parameters.

Credential fields are trusted runtime data and excluded from repr.
Profile resolution and the existing boto3 keyword projection are stable;
no credentials are persisted into bundle manifests by this value object.
"""

endpoint: str | None = field(default=None, repr=False)
region: str | None = None
Expand Down Expand Up @@ -54,7 +59,7 @@ def to_boto3_kwargs(self) -> dict[str, Any]:
return kwargs


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class StorageProfileResolver:
"""Resolves a profile *name* to connection parameters.

Expand All @@ -64,8 +69,8 @@ class StorageProfileResolver:
2. ``boto3`` standard credential chain (env vars, ``~/.aws/credentials``,
instance metadata).

Plugins may register additional resolvers via entry-point
``tributo.storage_resolvers``.
Consumer constructors may receive a custom resolver. The built-in resolver
does not discover an additional resolver entry-point group.
"""

def __init__(self) -> None:
Expand Down
4 changes: 2 additions & 2 deletions src/tributo/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ def __init__(self, message: str, execution_result: Any = None) -> None:
self.execution_result = execution_result


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class BundleCommitBusyError(TributoError):
"""A bundle commit is temporarily blocked by another active writer.

Expand All @@ -161,7 +161,7 @@ class BundleCommitBusyError(TributoError):
retryable = True


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class AliasConflict(TributoError):
"""Alias CAS update failed — concurrent modification detected."""

Expand Down
11 changes: 9 additions & 2 deletions src/tributo/exporting/bundle_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,16 @@
from tributo.util.annotations import PublicAPI


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class BundleReader:
"""Read and verify bundles through storage-independent repository ports."""
"""Read and verify bundles through storage-independent repository ports.

Stable consumption covers local/file/S3 locations, exact manifest bytes,
immutable references, resource limits, and verified artifact contexts.
Manifest v2 remains readable, but its explainability payload keeps its
existing experimental contract. Custom schema readers and developer router
injection do not turn their extensions into stable first-party capabilities.
"""

def __init__(
self,
Expand Down
14 changes: 7 additions & 7 deletions src/tributo/exporting/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
# ── Manifest models ────────────────────────────────────────────────────────────


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class ManifestSourceInfo(BaseModel):
"""Stable source identification — no temp paths, credentials, or samples."""

Expand All @@ -52,7 +52,7 @@ class ManifestSourceInfo(BaseModel):
task_type: str | None = None


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class SignatureField(BaseModel):
"""A single input/output field with its data type and shape.

Expand Down Expand Up @@ -87,7 +87,7 @@ def _check_shape(cls, v: tuple[Any, ...]) -> tuple[Any, ...]:
return v


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class ManifestSignature(BaseModel):
"""Input / output signature recorded in the manifest.

Expand Down Expand Up @@ -176,7 +176,7 @@ def _check_dynamic_axes_consistency(
)


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class ManifestExecutionNode(BaseModel):
"""A single DAG node as recorded in the manifest.

Expand All @@ -196,7 +196,7 @@ class ManifestExecutionNode(BaseModel):
duration_ms: int = 0


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class ManifestExecution(BaseModel):
"""Execution summary recorded in the manifest."""

Expand All @@ -206,7 +206,7 @@ class ManifestExecution(BaseModel):
nodes: tuple[ManifestExecutionNode, ...] = ()


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class ExportManifest(BaseModel):
"""Canonical manifest for a published model bundle (schema v1).

Expand Down Expand Up @@ -291,7 +291,7 @@ def _rebuild_manifest_v2() -> None:
# ── Schema registry ────────────────────────────────────────────────────────────


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class ManifestSchemaRegistry:
"""Versioned manifest reader registry.

Expand Down
40 changes: 24 additions & 16 deletions src/tributo/exporting/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
)

from tributo.exporting.formats import validate_format_id
from tributo.util.annotations import DeveloperAPI, PublicAPI
from tributo.util.annotations import PublicAPI

if TYPE_CHECKING:
from tributo.exporting.manifest import ManifestSignature
Expand Down Expand Up @@ -110,7 +110,7 @@ class HookReceipt(BaseModel):
external_references: dict[str, str] = Field(default_factory=dict)


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class AliasConfig(BaseModel):
"""Optional stable alias for a published bundle."""

Expand Down Expand Up @@ -475,7 +475,7 @@ def to_manifest_signatures(
# ── Artifact models ──────────────────────────────────────────────────────────


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class ArtifactFile(BaseModel):
"""A single file within a logical artifact."""

Expand Down Expand Up @@ -511,7 +511,7 @@ def _check_path(cls, v: str) -> str:
return _validate_posix_relative(v, "relative_path")


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class ProducerInfo(BaseModel):
"""Exporter identity and version information."""

Expand All @@ -525,7 +525,7 @@ class ProducerInfo(BaseModel):
effective_options_digest: str = Field(default="", min_length=0)


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class ArtifactRef(BaseModel):
"""A reference to a published artifact by node_id and tree digest."""

Expand Down Expand Up @@ -581,7 +581,7 @@ def _check_entrypoint(self) -> ArtifactDraft:
return self


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class LogicalArtifact(BaseModel):
"""A verified, hash-materialized artifact ready for publishing."""

Expand Down Expand Up @@ -638,7 +638,7 @@ def compute_tree_digest(cls, files: tuple[ArtifactFile, ...]) -> str:
return hashlib.sha256(payload).hexdigest()


@DeveloperAPI
@PublicAPI(stability="stable")
class ResolvedArtifact:
"""In-process view of an artifact with a local root directory.

Expand All @@ -661,7 +661,7 @@ def path_for(self, relative_path: str) -> Path:
# ── Execution results ────────────────────────────────────────────────────────


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class FailureInfo(BaseModel):
"""Structured, sanitised failure detail — no tracebacks or credentials."""

Expand All @@ -675,7 +675,7 @@ class FailureInfo(BaseModel):
retryable: bool = False


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class NodeResult(BaseModel):
"""Result of a single DAG node execution."""

Expand All @@ -694,7 +694,7 @@ class NodeResult(BaseModel):
duration_ms: int = Field(default=0, ge=0)


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class ExportExecutionResult(BaseModel):
"""Complete result of an export DAG execution (before publishing)."""

Expand All @@ -716,9 +716,14 @@ def succeeded_artifacts(self) -> dict[str, ArtifactRef]:
}


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class BundleResult(BaseModel):
"""Immutable result returned to the caller after a successful publish."""
"""Immutable result returned to the caller after a successful publish.

Core storage identity, artifact, role, and alias fields are stable.
``hook_receipts`` retains the beta HookReceipt payload contract; storing a
receipt does not promise a hook's external delivery behavior.
"""

model_config = ConfigDict(frozen=True, extra="forbid")

Expand All @@ -740,7 +745,7 @@ class BundleResult(BaseModel):
hook_receipts: tuple[HookReceipt, ...] = ()


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class BundleRef(BaseModel):
"""Immutable reference to a committed bundle.

Expand All @@ -756,11 +761,14 @@ class BundleRef(BaseModel):
manifest_sha256: str


@DeveloperAPI
@PublicAPI(stability="stable")
class PublishedBundle:
"""Transient handle returned within BundleExportService context.

``local_bundle_dir`` is only valid during the callback window.
``local_dir_ephemeral`` distinguishes caller-owned staging (S3) from a
persistent local publication directory. Ephemeral files are valid only while
the staging owner keeps them alive; BundleExportService provides that window
to its callback. Persist BundleRef or the result's canonical URI instead.
"""

def __init__(
Expand All @@ -779,7 +787,7 @@ def __init__(
# ── Validation models ────────────────────────────────────────────────────────


@PublicAPI(stability="beta")
@PublicAPI(stability="stable")
class ValidationResult(BaseModel):
"""Result of a single validator run."""

Expand Down
Loading
Loading