ReferenceWelt bridge
Welt bridge
Run Welt's hosted foundation models from ergodic. Import from ergodic.welt.
Welt hosts foundation models for tabular data. This optional module connects them to ergodic's discovery and estimation:
discover()runs a Welt causal discovery model (arrow,cdfm,avici) and returns aDiscoveryResult. The same run isergodic.discover(data, method="welt:arrow").WeltLearnerfills an estimator's nuisance slots with a Welt tabular model, soestimate_effect(..., learner=WeltLearner("tabicl-v2"))works.upload_dataset()sends aTabularDatato your Welt workspace, andload_research_dataset()reads a Welt research dataset as one.
Install it with pip install "ergodic[welt]", which adds welt-client on
Python 3.11 to 3.13 (welt-client does not support Python 3.14 yet). Every
call here is a request to Welt and most are paid; nothing runs at import. The
rest of ergodic never imports this module, welt or httpx, and makes no
network calls.
Each function takes an optional client: a welt.Client you opened (it is
used as is and never closed here). Without one, a short-lived welt.Client()
is opened per call, reading the key from WELT_API_KEY or welt login.
Also exported from ergodic: discover.
WeltDecodeError#
class WeltDecodeError(
model: str,
decoder: str,
error: Exception,
result: Any,
scores: pd.DataFrame | None,
) -> None
Decoding a paid Welt result's edge scores failed; the result is not lost.
ergodic checks every decoder setting before submitting, so this is rare. It carries the native result and its scores, which stay in your Welt workspace, so you can decode them again locally.
| Attribute | Description |
|---|---|
model |
The Welt model id. |
decoder |
The versioned decoder id that failed. |
result |
The native welt.CausalResult. |
scores |
Its edge-score matrix (rows the source, columns the target), or None if the scores themselves could not be read. |
WeltGraphNotDAGError#
class WeltGraphNotDAGError(
model: str,
native_graph_type: str,
result: Any = None,
scores: pd.DataFrame | None = None,
) -> None
A Welt model's native graph is not a declared DAG, so ergodic refuses it.
ergodic converts only declared DAGs and never projects or relabels a directed
graph as one. When the model is known to return such graphs, the refusal comes
before anything is uploaded or paid for, and result is None. When a
finished result turns out not to be a declared DAG, the paid result stays in
your Welt workspace and this error carries it.
| Attribute | Description |
|---|---|
model |
The Welt model id. |
native_graph_type |
The model's native graph type, for example "directed". |
result |
The native welt.CausalResult, or None if nothing was submitted. |
scores |
Its edge-score matrix (rows the source, columns the target), or None if nothing was submitted. |
WeltLearner#
class WeltLearner(
model: str = 'tabicl-v2',
task: str = 'regression',
*,
client: Any = None,
max_fits: int | None = 10,
configuration: str = 'default',
seed: int = 0,
timeout: float = 600.0,
) -> None
A Welt tabular model as an ergodic learner.
Use it wherever ergodic takes a learner, for example
estimate_effect(graph, data, "X", "Y", strategy="dml",
learner=WeltLearner("tabicl-v2")). A slot that needs probabilities (a
propensity) takes a classification learner; pass a slot mapping such as
{"outcome": WeltLearner(), "propensity": WeltLearner(task="classification")}.
Each fit uploads the training rows as a new Welt dataset and fits a predictor:
one paid fit. Both stay in your Welt workspace afterwards; nothing here deletes
them (welt.Client().predictors() and .datasets() list them). Each predict
is a paid prediction, sent in batches no larger than the model's prediction-row
limit. DML with n_folds=k fits 2 * k times.
A slot with no feature columns (an empty adjustment set) has nothing for a model
to learn: its conditional mean is the training mean, or the class frequencies for
classification. The learner fits that locally, with no upload, no paid fit and no
charge to max_fits, and predictor_id_ is None.
The learner and its clones share one budget of max_fits fits. It caps paid
fits only, not predictions: each fit is followed by one prediction call per
batch of held-out rows (DML on 8,000 rows with five folds and a 100-row limit
makes about 160). Fits take no row weights, so supports_sample_weight is
False and ergodic refuses this learner in a weighted slot (the R-learner's
final) before fitting anything.
| Parameter | Description |
|---|---|
model |
Any Welt classification or regression model id; none is checked here. The default follows what Welt serves: "tabicl-v2" is available today, while "tabpfn-3.5-fast" is still planned. See which models run now, with their row, column and prediction limits, with [(m["id"], p["task"], p["limits"]) for m in welt.Client().models() for p in m["task_profiles"] if p["execution_available"]], welt models list on the command line, or the Models page of the Welt SDK docs. |
task |
"regression" or "classification". Only a classification learner has predict_proba. |
client |
A welt.Client to use as is, shared with every clone. None opens a short-lived welt.Client() per request. |
max_fits |
The most paid fits this learner and its clones may make together; None removes the cap. The default 10 covers DML with five folds. Predictions are not counted. |
configuration |
The Welt model configuration. |
seed |
The fit seed Welt records with each predictor. |
timeout |
Seconds to wait for each fit or pending prediction. A timeout leaves the job running in Welt. |
| Attribute | Description |
|---|---|
predictor_id_ |
The fitted Welt predictor's id; None after a no-feature (local) fit. |
model_version_ |
The exact model version Welt fitted; None after a no-feature fit. |
classes_ |
The class labels, sorted (classification only). |
supports_sample_weight |
Always False; read by ergodic's weighted slots. |
WeltLearner.get_params#
def WeltLearner.get_params(deep: bool = True) -> dict[str, Any]
The constructor arguments, as scikit-learn's get_params returns them.
| Parameter | Description |
|---|---|
deep |
Accepted for scikit-learn compatibility; there are no nested estimators. |
Returns: A dict of every constructor argument.
WeltLearner.set_params#
def WeltLearner.set_params(**params: Any) -> Self
Set constructor arguments; a new max_fits starts a fresh budget.
| Parameter | Description |
|---|---|
**params |
Constructor arguments by name. |
Returns: This learner.
| Raises | Description |
|---|---|
ValueError |
A name is not a constructor argument, or a value is invalid. |
WeltLearner.fits_used#
property WeltLearner.fits_used: int
Paid fits made so far by this learner and its clones together.
WeltLearner.fit#
def WeltLearner.fit(
X: npt.NDArray[np.float64],
y: npt.NDArray[np.float64],
sample_weight: npt.NDArray[np.float64] | None = None,
) -> Self
Fit a Welt predictor on these rows: one paid fit.
| Parameter | Description |
|---|---|
X |
The feature matrix, one row per unit. With no columns the fit is the local training mean (or class frequencies) and costs nothing. |
y |
The target, one value per row: numbers for regression, labels for classification. |
sample_weight |
Must be None. Welt fits take no row weights, and a weight is never silently dropped. |
Returns: This learner, fitted.
| Raises | Description |
|---|---|
ValueError |
sample_weight was given, X has no rows, or y does not match X. Nothing is uploaded. |
RuntimeError |
The shared max_fits budget is spent. |
WeltLearner.predict#
def WeltLearner.predict(X: npt.NDArray[np.float64]) -> npt.NDArray[Any]
Predict with the fitted Welt predictor: one paid prediction per batch.
| Parameter | Description |
|---|---|
X |
Rows with the same columns as the fit. |
Returns: Numbers in the target's units for regression, class labels for classification.
WeltLearner.predict_proba#
property WeltLearner.predict_proba: Callable[[npt.NDArray[np.float64]], npt.NDArray[np.float64]]
Class probabilities, one column per class in classes_ order.
Only a classification learner has this; ergodic reads its presence to tell a classifier from a regressor.
| Raises | Description |
|---|---|
AttributeError |
The learner's task is regression. |
discover#
def discover(
data: TabularData,
model: str = 'arrow',
*,
seed: int = 0,
timeout: float = 600.0,
knowledge: DomainKnowledge | None = None,
decoder: str | Decoder | None = None,
client: Any = None,
) -> DiscoveryResult
Learn a DAG with a Welt causal discovery model.
Uploads the dataset's variables (continuous, no missing values), runs one
discovery job and waits for it. This is one paid Welt job; the uploaded
dataset and the native result stay in your workspace until you delete them.
ergodic.discover(data, method="welt:<model>") calls this.
Welt's models and limits come from its catalogue (welt.Client().models()).
Today arrow declares native DAGs. cdfm and avici return native
directed graphs that may hold cycles or reciprocal edges, so they are refused
with WeltGraphNotDAGError before anything is uploaded or paid for
(the catalogue's stated graph type wins over that built-in list). A result
that is still not a declared DAG is refused too, with the paid result attached.
decoder= opts into local decoding. The run then accepts any model,
reads the native edge scores (rows the source, columns the target) as
EdgeProbabilities of kind "independent" (each
score a separate directed-edge marginal, uncalibrated) with the Welt provenance,
and decodes them with decode(). The returned graph is that decoded
DAG, not the model's native graph, and its diagnostics say so. For the greedy
decoder without an explicit threshold, the model's native threshold is used when
Welt records one (decoder.parameters.threshold: 0.5 for AVICI, the adaptive
threshold for CDFM), so greedy keeps what the model's own rule keeps and only
drops edges that close a cycle or a reciprocal pair. On a DAG model (arrow)
the decoder replaces the native DAG with one decoded from the same scores, read
the same independent way. Any other choice can be made locally, with no new run,
from result.diagnostics["edge_probabilities"]. Every decoder setting, the
max_nodes cap against the data's variable count included, and the knowledge
are checked before anything is uploaded; if decoding still fails after the paid
run, WeltDecodeError carries the native result and its scores.
| Parameter | Description |
|---|---|
data |
Observational rows; every variable must be continuous and complete. |
model |
The Welt causal discovery model id, for example "arrow". |
seed |
The run's seed, recorded in the result's provenance. |
timeout |
Seconds to wait for the job. A timeout leaves the job running in Welt; reconnect with welt.Client().job(job_id). |
knowledge |
Must be None without a decoder (Welt discovery takes no constraints yet). With a decoder it constrains the local decoding only; Welt still runs unconstrained. It is checked before anything is uploaded. |
decoder |
None (native DAGs only), a decoder id such as "greedy", or a Decoder with its settings (a sampler needs Decoder("pair_sampler", samples=..., seed=...)). |
client |
A welt.Client to use as is; None opens a short-lived one. |
Returns: A DiscoveryResult whose graph is the native DAG with every variable, method is "welt:<model>", and whose diagnostics hold the Welt provenance: welt_job_id, welt_result_id, welt_dataset_id, welt_model, welt_model_version, welt_configuration_version, welt_decoder, welt_seed and welt_scores (the native edge scores as a dataframe, rows the source and columns the target; not calibrated confidence). With a decoder it is a DecodeResult (still a DiscoveryResult, method "welt:<model>") whose graph is the decoded DAG; diagnostics add the decode labels (decoded True, native False, decoder, parameters, threshold_source, rejected_cycles, note, ...), welt_native_graph_type, welt_native_edges (the native directed edges as pairs) and edge_probabilities.
| Raises | Description |
|---|---|
TypeError |
data is not tabular. |
WeltDecodeError |
Decoding failed after the paid run; the error carries the result and its scores. |
ValueError |
knowledge was given without a decoder (or names unknown variables or a confounded pair), the decoder settings are invalid or its max_nodes is below the variable count, or a variable is not continuous or has missing values. Nothing is uploaded. |
WeltGraphNotDAGError |
The model's native graph is not a declared DAG, either known before submitting (nothing is paid) or found in the result. |
load_research_dataset#
def load_research_dataset(
dataset_id: str,
*,
version: str | None = None,
types: Mapping[str, VariableType | str | Ordinal] | None = None,
exclude: Iterable[str] | None = None,
categorical_threshold: int = 10,
client: Any = None,
) -> TabularData
Load a dataset from Welt's research catalogue as tabular data.
Welt verifies the download's size and checksum and reads it into a dataframe;
ergodic then infers the schema as tabular() does. Each
download is billed per dataset (see Welt's pricing). Reading Parquet content
needs pyarrow: pip install "welt-client[research]".
| Parameter | Description |
|---|---|
dataset_id |
The research dataset's id, from client.research_datasets(). |
version |
A content version to pin; None takes the current one. |
types |
Per-column type overrides, as in tabular(). |
exclude |
Columns to keep but not treat as variables. |
categorical_threshold |
Integer columns with at most this many distinct values are read as categorical, with a warning. |
client |
A welt.Client to use as is; None opens a short-lived one. |
Returns: The table as TabularData.
upload_dataset#
def upload_dataset(
data: TabularData,
*,
target: str | None = None,
name: str = 'ergodic dataset',
client: Any = None,
) -> dict[str, Any]
Upload a tabular dataset to your Welt workspace.
The upload holds the dataset's variables in their column order; excluded
columns stay local. Values go up as they are stored (categories as their
labels, missing values as empty cells). Uploading is free, and the dataset
stays in the workspace until you delete it with client.delete_dataset(id).
| Parameter | Description |
|---|---|
data |
The rows to upload. |
target |
A variable to mark as the prediction target, for a later fit or evaluation. None uploads a target-free table, which is what causal discovery needs. |
name |
The dataset name shown in Welt. |
client |
A welt.Client to use as is; None opens a short-lived one. |
Returns: The Welt dataset record, with its "id".
| Raises | Description |
|---|---|
TypeError |
data is not a TabularData. |
ValueError |
target is not one of the variables, or a cell is not a number or a string. |