Metadata-Version: 2.5
Name: welt-client
Version: 0.14.1
Summary: Welt Python client and scikit-learn estimators
License-Expression: Apache-2.0
License-File: LICENSE
Requires-Python: <3.14,>=3.11
Requires-Dist: httpx<1,>=0.28
Requires-Dist: numpy<3,>=2
Requires-Dist: pandas<4,>=2.2
Requires-Dist: scikit-learn<2,>=1.6
Provides-Extra: ergodic
Requires-Dist: ergodic<0.4,>=0.2; extra == 'ergodic'
Provides-Extra: parquet
Requires-Dist: pyarrow<30,>=19; extra == 'parquet'
Provides-Extra: research
Requires-Dist: pandas<4,>=2.2; extra == 'research'
Requires-Dist: pyarrow<30,>=19; extra == 'research'
Description-Content-Type: text/markdown

# Welt Python SDK

From a DataFrame to a prediction. Use familiar `Classifier` and `Regressor`
estimators backed by durable predictors in your private Welt workspace.
Install `welt-client`, import `welt`. Python 3.11–3.13.

[Start here](https://ergodic-ai.github.io/welt-python/) ·
[Guides](https://ergodic-ai.github.io/welt-python/own-data.html) ·
[Reference](https://ergodic-ai.github.io/welt-python/reference.html) ·
[Models and limits](https://ergodic-ai.github.io/welt-python/capabilities.html)

## Install

```sh
python -m pip install "welt-client[parquet]"
```

Run `welt login` and approve your workspace in the browser. This saves a private,
origin-bound credential for later Python sessions. In a notebook, connect once:

```python
from welt import Client

with Client() as client:
    client.connect()
```

Open its printed verification link on your own computer if Python is remote.
Python connection stays in this process; use `save=True` explicitly to persist it.
Constructors never open a browser. Explicit keys and `WELT_API_KEY` still work and
have precedence over connected/saved credentials. [Connection guide](https://ergodic-ai.github.io/welt-python/connect.html).

Modelling and research downloads may consume workspace credits. Check **Workspace
→ Billing** for available credits, pending reservations and spend caps before
running the examples. [Usage and recovery](https://ergodic-ai.github.io/welt-python/usage.html#usage-and-workspace-credits)
explains accounting; payments and account changes require browser authorization.

## Learn with real data

Download Iris from our research inventory, look at the table, split train/test,
fit a classifier, predict unseen rows, and measure the result:

```python
from pathlib import Path
from tempfile import mkdtemp
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report
from welt import Client, Classifier

# 1. Get a real table from Welt's research inventory.
dataset_id = "asset-d839644b6de36fc5cafdda18c5daeca3"
path = Path(mkdtemp(prefix="welt-iris-")) / "iris.parquet"
with Client() as client:
    info = client.research_dataset(dataset_id)
    print("Dataset:", info["name"])
    client.download_research_dataset(dataset_id, path)
data = pd.read_parquet(path)
target = "target"  # The outcome column in this table.

# 2. Look at the rows before modelling.
print(data.head())
print("Rows and columns:", data.shape)
data.info()
print(data[target].value_counts())

# 3. Set aside test rows before fitting; never train on their answers.
train, test = train_test_split(
    data, test_size=0.2, random_state=9, stratify=data[target])
query = test.drop(columns=target)
y_test = test[target]
print("Training rows:", len(train), "— Test rows:", len(test))

# 4. Create a model and train it on the training table.
model = Classifier(model="tabicl-v2", random_state=9)
model.fit(train, target=target)

# 5. Predict the held-out rows, without passing their target column.
predictions = model.predict(query)
print(pd.DataFrame({"actual": y_test.to_numpy(), "predicted": predictions}).head())

# 6. Compare predictions with the answers we kept aside.
print("Test accuracy:", accuracy_score(y_test, predictions))
print(classification_report(y_test, predictions, zero_division=0))
print("Keep this predictor ID:", model.predictor_id_)
```

Accuracy and per-class precision/recall/F1 describe your held-out run, not a
promised benchmark score. [Start](https://ergodic-ai.github.io/welt-python/start.html) explains each step and teaches
numeric predictions with real Yacht data. `fit` uploads only your training rows;
your workspace retains the resulting dataset and predictor for reuse.

### Research metadata and one-step loading

Keep the catalogue detail (schema, column descriptions and provenance)
beside a download with `metadata=True`, or load a table directly with the
`research` extra (`pip install "welt-client[research]"`):

```python
with Client() as client:
    saved = client.download_research_dataset(
        dataset_id, path.with_name("iris-2.parquet"), metadata=True)
    # saved.path, saved.metadata_path ("iris-2.parquet.metadata.json"), saved.metadata
    data, meta = client.load_research_dataset(dataset_id, metadata=True)
print(meta.name, meta.column_descriptions)
```

Without `metadata=True` the download returns its local path. Omitting `version`
selects the currently published file; the SDK verifies its size and checksum.
`load_research_dataset` removes its temporary file after loading. See
[Research datasets](https://ergodic-ai.github.io/welt-python/research-datasets.html).

The released client includes sync/async resources, resumable file uploads, durable
batch jobs, workspace selection and native causal objects. Estimator CSV/Parquet
convenience reads load files into memory; `Client.upload_file` uses upload sessions
and supports recovery with a retained upload ID. Available formats, models, tasks
and limits depend on the connected server. Inspect its catalogue before executing.
Each TabICLv2, Kumo Tabular Medium, TabDPT 1.3 and Mitra-v2 classification and regression
profile has measured shapes: TabICLv2 and Kumo Tabular Medium up to 8,000 training rows × 20
features (or 4,000 × 40, 2,000 × 80), Mitra-v2 up to 4,000 × 20, TabDPT 1.3 up to 500 × 20.
Direct predictions take up to 100 rows per call, and classification up to 10 classes.
`Client().models()` reports the current limits. [Capabilities](https://ergodic-ai.github.io/welt-python/capabilities.html) explains scope.


A local wait timeout leaves server work running. Reconnect by its job ID rather
than fitting again; [recovery](https://ergodic-ai.github.io/welt-python/async-and-errors.html) describes typed failures,
authentication, cancellation and retry behavior. Probabilities are not calibrated
coverage. ArrowFM, CDFM and AVICI discovery preserve native graph and score semantics;
[causal conversion](https://ergodic-ai.github.io/welt-python/causal-discovery.html) is restricted to declared native DAGs
and needs the `welt-client[ergodic]` extra; [ergodic](https://docs.ergodic.dev) then identifies
and estimates effects locally. The complete all-model catalogue remains gated.

## Examples and development

[Runnable script](https://ergodic-ai.github.io/welt-python/downloads/first_prediction.py) · [Jupyter notebooks](https://ergodic-ai.github.io/welt-python/notebooks.html) ·
[Research datasets](https://ergodic-ai.github.io/welt-python/research-datasets.html) · [Contributing](https://github.com/ergodic-ai/welt-python/blob/main/CONTRIBUTING.md)

```sh
uv sync --frozen
uv run --frozen pytest -q
uv build
```

Offline tests patch HTTP transport outside user examples; they neither contact the
hosted service nor measure foundation-model quality. Notebook sources have cleared outputs; clean-wheel execution and
bounded authorized live acceptance are separate evidence. CI requires no private
backend, credentials or checkpoints.

Agent workflows, workspace selection and the released command reference: [CLI](https://ergodic-ai.github.io/welt-python/cli.html).

## Explicit hidden-model runs

`client.model("chronos-2")` inspects an explicit hidden ID while
`client.models()` keeps the main catalogue. `client.submit_model_run(payload,
idempotency_key="your-key")` returns a durable Job; `job.result()` returns the
native task result. AsyncClient has matching methods. The payload follows the
API's tabular support/query, causal table or univariate forecasting contract;
check the model metadata for enabled tasks, limits, readiness and workspace access.

```python
from welt import Client
with Client() as client:
    job = client.submit_model_run({
        "model_id": "chronos-2", "task": "univariate_forecasting",
        "series": {"values": [float(i) for i in range(48)],
                   "start": "2026-01-01T00:00:00Z", "frequency_seconds": 3600},
        "horizon": 12,
    }, idempotency_key="forecast-example-1")
    result = job.result()
    print(result["predictions"])
```

CLI equivalents: `welt models get chronos-2`, `welt run --input request.json`,
`welt jobs wait JOB_ID`. Use `welt model-runs delete JOB_ID --yes` or
`client.delete_model_run(id)` to explicitly remove a terminal run's input/result.
Deleting keeps the idempotency/usage record; a new run needs a fresh key. Native
model libraries remain server-side. Restricted models need an internal testing
workspace; hidden status alone does not grant execution access. TabSTAR's trained
state stays alive for the single run, and these runs do not publish reusable
support-only fitted predictors. LimiX query splitting can change its transductive
results. API errors, cancellation and timeouts retain existing typed semantics.

## Compare models with cross-validation

`client.evaluate(data, "target", task="classification")` cross-validates every
eligible model (or up to 8 you name with `models=[...]`) on the same seeded folds and
returns an `Evaluation`: `summary()` is the leaderboard against a baseline, `folds()`
and `predictions()` are DataFrames, `show()` displays the HTML report in Jupyter and
`report_link()` creates a signed link anyone holding it can open for up to seven days.
`evaluation.fit()` then fits the chosen model on the full dataset. It requires SDK 0.12
or later and a server release with evaluations; an older server raises `NotFoundError` with
`code="evaluations_unavailable"`. [Evaluations guide](https://ergodic-ai.github.io/welt-python/evaluations.html).

## Forecast time series

`Forecast(model="chronos-2")` forecasts one series or a long-format panel with
Chronos-2. Fit with `time`, an optional `id` column, one or more `target` columns, and
`past_covariates`, `future_covariates` and `categorical` drivers; then call
`forecast(horizon, future=known_future_table)`. One request covers every series and
target, and `to_frame()` returns one row per series, target and step with the median
and `q_<level>` quantiles. Limits come from the model's published envelopes
(`client.model("chronos-2")["forecast_envelopes"]`), and from the forecasting release a
forecast is priced at the prediction rate per token: Σ (history rows + horizon) ×
(targets + covariates), rounded up.
Quantiles are not calibrated intervals; crossed native quantiles are sorted and
counted in `rearranged_steps`. Panels and covariates need SDK 0.14 and a server with
the `forecasting` task. History and future can also be workspace dataset IDs; tables are
sent inline, never uploaded automatically, and bodies above 10 MiB are refused before
sending. `client.delete_forecast(id)` removes a finished forecast's input and result.
The univariate `predict` API from SDK 0.11 stays supported.
`Forecast(model="timestral-209m")` (or `timestral-28m`), listed after Chronos-2, uses the same
calls and price but forecasts one series per request: SDK 0.14.1 refuses a panel before sending
with `unsupported_capability`, so fit and forecast each series separately. Timestral weights:
2026 - Mistral AI, Modified MIT licence ([Timestral-209M](https://huggingface.co/mistralai/Timestral-209M/blob/9874d252ab0aae16f7f7c999f98f98d6ad7d8365/LICENSE),
[Timestral-28M](https://huggingface.co/mistralai/Timestral-28M/blob/180698bbf5a50dda3516aa6419798437fb03a430/LICENSE)) ([forecast with Timestral](https://ergodic-ai.github.io/welt-python/forecasting.html#timestral)).
[Forecast guide](https://ergodic-ai.github.io/welt-python/forecasting.html) ·
[tutorial notebook](https://ergodic-ai.github.io/welt-python/notebook-forecasting-with-covariates.html) ·
Use Chronos-2 inside ergodic's backtest and reconcile:
https://docs.ergodic.dev/forecast-with-welt.html
