Metadata-Version: 2.4
Name: lizyml-widget
Version: 0.9.0
Summary: Jupyter Notebook / Google Colab / VS Code Notebooks widget for LizyML
Project-URL: Homepage, https://github.com/nbx-liz/LizyML-Widget
Project-URL: Repository, https://github.com/nbx-liz/LizyML-Widget
Project-URL: Bug Tracker, https://github.com/nbx-liz/LizyML-Widget/issues
Project-URL: Changelog, https://github.com/nbx-liz/LizyML-Widget/blob/main/CHANGELOG.md
Author: NBX
License-Expression: MIT
License-File: LICENSE
Keywords: anywidget,automl,jupyter,lightgbm,machine-learning,widget
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Jupyter
Classifier: Framework :: Jupyter :: JupyterLab
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Requires-Dist: anywidget>=0.9
Requires-Dist: pandas>=1.5
Requires-Dist: pyyaml>=6.0
Requires-Dist: traitlets>=5.0
Provides-Extra: dev
Requires-Dist: ipykernel>=6.0; extra == 'dev'
Requires-Dist: jupyterlab>=4.0; extra == 'dev'
Requires-Dist: lizyml[calibration,explain,plots,tuning]<0.13,>=0.10.0; extra == 'dev'
Requires-Dist: mypy>=1.0; extra == 'dev'
Requires-Dist: pandas-stubs>=2.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Requires-Dist: scikit-learn>=1.0; extra == 'dev'
Requires-Dist: watchfiles>=1.1; extra == 'dev'
Provides-Extra: lizyml
Requires-Dist: lizyml[calibration,explain,plots,tuning]<0.13,>=0.10.0; extra == 'lizyml'
Description-Content-Type: text/markdown

# LizyML Widget

[![PyPI](https://img.shields.io/pypi/v/lizyml-widget)](https://pypi.org/project/lizyml-widget/)
[![Python](https://img.shields.io/pypi/pyversions/lizyml-widget)](https://pypi.org/project/lizyml-widget/)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

Interactive Jupyter widget for [LizyML](https://github.com/lizyml/lizyml) — fit, tune, and run inference on machine learning models without writing code.

## Features

- **Data Tab** — Load a DataFrame, select target, configure columns and cross-validation
- **Config Tab** — Edit LightGBM hyperparameters, configure tuning search space
- **Results Tab** — View scores, Plotly plots, feature importance, and inference results
- **Config Import/Export** — Save and load configurations as YAML
- **Python API** — Programmatic access to all widget functionality

## Requirements

- Python `>= 3.10`
- Jupyter Notebook, JupyterLab, Google Colab, or VS Code Notebooks
- `lizyml >= 0.9.0, < 0.10` (auto-resolved by the `[lizyml]` extras)

## Installation

```bash
# Recommended: installs a compatible lizyml automatically
pip install "lizyml-widget[lizyml]"
```

The `[lizyml]` extras pin `lizyml[plots,tuning,calibration,explain]>=0.9.0,<0.10`
so `pip` / `uv` / `poetry` always select a backend version that matches the
widget's expected contract.

If you manage `lizyml` separately, install the widget without extras and pin
`lizyml` yourself:

```bash
pip install lizyml-widget
pip install "lizyml[plots,tuning,calibration,explain]>=0.9.0,<0.10"
```

> Widget's `LizyMLAdapter` validates the installed `lizyml` version at import
> time and raises a clear `ImportError` if the backend is out of range.

### Version compatibility

LizyML Widget is tightly coupled to the `lizyml` ML contract (types,
`BackendAdapter` protocol, tune/fit result shapes), so each minor version of
the widget targets a specific `lizyml` range:

| lizyml-widget | lizyml           | Highlights                                                    |
| ------------- | ---------------- | ------------------------------------------------------------- |
| **0.8.x**     | `>=0.9.0,<0.10`  | Re-tune (round progress, boundary expansion, tuning history, `w.retune()` API) |
| 0.7.x         | `>=0.7.0,<0.9`   | Calibration / Search Space default refresh                    |
| 0.6.x / 0.5.x | `>=0.5.0,<0.7`   | Learning curve metric filter, CV strategy metadata            |

See [docs/VERSION_COMPAT.md](docs/VERSION_COMPAT.md) for the full matrix,
troubleshooting, and upgrade guidance.

## Quick Start

```python
import pandas as pd
from lizyml_widget import LizyWidget

df = pd.read_csv("train.csv")
w = LizyWidget()
w.load(df, target="price")
w  # display widget in notebook cell
```

### Programmatic Usage

```python
w = LizyWidget()
w.load(df, target="y").fit()

summary = w.get_fit_summary()
print(summary.metrics)

w.save_model("./model")
w.save_config("config.yaml")
```

### Re-tune (Study Resume + Boundary Expansion)

When the initial Tune run hits a search-space boundary or you simply want
more trials, call `w.retune()` to resume the existing Optuna study in
place.  The widget reuses the backend model so no history is lost.

```python
w = LizyWidget()
w.load(df, target="y")

# 1. Initial tune (e.g. 50 trials)
w.tune()

# 2. Resume with 30 more trials and let the backend widen boundaries
#    if the best trial lands on an edge.
w.retune(n_trials=30, expand_boundary=True, boundary_threshold=0.05)

summary = w.get_tune_summary()
for r in summary.rounds:
    print(f"Round {r['round']}: {r['n_trials']} trials, "
          f"best={r['best_score_after']}, expanded={r['expanded_dims']}")
```

The Results tab shows a **Re-tune (resume)** button inside the *Best Params*
accordion after the initial Tune completes — clicking it runs the same
resume flow from the UI, and the round-aware progress bar, Score History
chart, and Boundary Expansion panel update in place.

Requires `lizyml >= 0.9.0` (auto-resolved by `lizyml-widget[lizyml]`).

### Version

```python
import lizyml_widget
print(lizyml_widget.__version__)
```

## Tutorials

| Notebook | Task | Dataset |
|----------|------|---------|
| [Regression](notebooks/tutorial_regression.ipynb) | Regression | California Housing (sklearn) |
| [Binary Classification](notebooks/tutorial_binary.ipynb) | Binary | Breast Cancer Wisconsin (sklearn) |
| [Multiclass Classification](notebooks/tutorial_multiclass.ipynb) | Multiclass | Wine (sklearn) |

## Supported Environments

- Jupyter Notebook
- JupyterLab
- Google Colab
- VS Code Notebooks

Powered by [anywidget](https://anywidget.dev/) for cross-environment compatibility.

### Execution strategy on Linux + libgomp

On Linux hosts where ``lightgbm`` is dynamically linked against GCC's
``libgomp`` (the common apt / pip distribution), Fit and Tune jobs run in a
fresh subprocess by default. This avoids a libgomp pool-affinity bug that
makes worker-thread training ~30x slower than main-thread training (and
multi-trial Tune compounds to 20–50x). See [issue #147](https://github.com/nbx-liz/LizyML-Widget/issues/147).

The trade-off is a fixed startup cost on every Fit/Tune call:

| Path | Typical wall-clock (5000 rows × 30 cols) |
|---|---|
| in-process Fit (`LZW_FORCE_THREAD=1`) | ~0.7 s |
| subprocess startup + lightgbm import (overhead) | ~0.8–1.2 s |
| subprocess Fit (default) | ~1.6–2.0 s |

For interactive Notebook workflows where you fit small datasets repeatedly
and the libgomp affinity bug does not manifest in your environment (e.g.
fresh kernel, single Fit per session), set the opt-out env var:

```bash
export LZW_FORCE_THREAD=1
```

Tune still benefits from subprocess execution — leave the default in place
when running multi-trial hyperparameter searches.

## Development

```bash
# Python
uv sync --all-extras    # installs dev + lizyml dependencies
uv run pytest
uv run ruff check .
uv run mypy src/lizyml_widget/

# TypeScript
cd js
pnpm install
pnpm dev               # watch build
pnpm build             # production build
pnpm lint
pnpm test              # vitest run
pnpm test:coverage     # vitest run --coverage (CI gate at 75% statements / 70% branches)
```

### Test coverage targets

- Python (pytest): **80% line coverage** — enforced in CI via `--cov-fail-under=80`.
- TypeScript (vitest): **75% statements / lines, 70% branches, 50% functions** —
  enforced via thresholds in [`js/vitest.config.ts`](js/vitest.config.ts).
- E2E (Playwright + JupyterLab): suite under [`tests/e2e/`](tests/e2e/); CI prints
  the test count so additions/removals are visible in PR diffs.

### Stable Notebook Launch

If VS Code gets stuck reconnecting to an old kernel, prefer launching Jupyter with
workspace-local runtime files instead of the default global runtime directory:

```bash
./scripts/jupyter-reset.sh
./scripts/jupyter-lab.sh
```

This keeps runtime/config state under the repository and makes stale kernel/server
state easier to clear than relying on `Reload Window` alone.

## License

MIT
