Skip to content

Artifact sets

An ArtifactSet is one immutable, named collection of exact Artifact references. Use it when a consumer needs a package—such as a model, its feature contract, and its evaluation report—as one lineage input or output.

Create a set from one Computation

Use ComputationArtifactSet when one Computation emits all members:

from oclp import ComputationArtifactSet, JsonArtifact, computation


@computation(
    name="Evaluate candidate",
    outputs={
        "model": JsonArtifact(name="Candidate model"),
        "evaluation": JsonArtifact(name="Candidate evaluation"),
    },
    artifact_set=ComputationArtifactSet(
        name="Evaluated candidate",
        port="candidate_release",
        members={
            "model": ("model", "model"),
            "evaluation": ("evaluation", "validation-report"),
        },
    ),
)
def evaluate_candidate() -> dict[str, object]:
    return {"model": {"version": 1}, "evaluation": {"rmse": 212.4}}

candidate_release becomes another real output of the same Execution. It does not create a packaging Computation or a synthetic Execution.

Assemble a release across Computations

For members produced by separate Computations, declare each contribution with @artifact_set outside its @computation. Matching names are assembled when the observed run completes successfully:

from oclp import artifact_set, computation


@artifact_set(
    name="Demand model release",
    output_port="model",
    role="model",
)
@computation(...)
def train_model(...): ...


@artifact_set(
    name="Demand model release",
    output_port="evaluation",
    role="validation-report",
)
@computation(...)
def evaluate_model(...): ...

Each member must materialize exactly once. The SDK does not guess across workers or invent a selection policy when a declared member is ambiguous.

Assemble a terminal collection across runs

Use assemble_artifact_set(...) when a scheduler needs a visible terminal node that collects exact upstream Artifact handles from separate runs. It declares only the direct ArtifactSet operation: it does not claim to be an Artifact or fabricate an Execution. See the Dagster integration for this canonical declaration in a cross-run asset graph.

from oclp import ArtifactHandle, assemble_artifact_set


@assemble_artifact_set(
    name="Demand model release",
    members={
        "model": ("model", "model"),
        "evaluation": ("evaluation", "validation-report"),
    },
)
def assemble_release(
    model: ArtifactHandle,
    evaluation: ArtifactHandle,
) -> None:
    pass

Call assemble_release(model, evaluation) inside an observed run. The return value is the resulting ArtifactSetHandle; the marker body may remain empty.

When membership itself is genuinely dynamic, call observed.publish_artifact_set(...) with exact handles instead. It is the imperative direct-collection API; it likewise creates no synthetic Computation or Execution.

Use ArtifactSetHandle.member(name) or load_member(name, target_type) to retrieve a member. A Computation that consumes the package should declare artifact_set_input(...), preserving the ArtifactSet reference as one real Execution input.