Metadata-Version: 2.4
Name: openrunner-sdk
Version: 0.2.0
Summary: OpenRunner SDK - W&B-compatible ML experiment tracking client
Project-URL: Homepage, https://github.com/jqueguiner/openrunner
Project-URL: Repository, https://github.com/jqueguiner/openrunner
Project-URL: Issues, https://github.com/jqueguiner/openrunner/issues
Author-email: JL Queguiner <jl@gladia.io>
License: MIT
Keywords: experiment-tracking,machine-learning,ml,mlops,wandb
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
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: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Requires-Dist: click>=8.1
Requires-Dist: httpx>=0.27
Requires-Dist: pillow>=10.0
Requires-Dist: psutil>=6.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Provides-Extra: fastai
Requires-Dist: fastai>=2.7; extra == 'fastai'
Provides-Extra: gpu
Requires-Dist: nvidia-ml-py>=12.0; extra == 'gpu'
Provides-Extra: huggingface
Requires-Dist: transformers>=4.30; extra == 'huggingface'
Provides-Extra: keras
Requires-Dist: keras>=3.0; extra == 'keras'
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.2; extra == 'langchain'
Provides-Extra: lightning
Requires-Dist: lightning>=2.0; extra == 'lightning'
Provides-Extra: pytorch
Requires-Dist: torch>=2.0; extra == 'pytorch'
Provides-Extra: sklearn
Requires-Dist: scikit-learn>=1.3; extra == 'sklearn'
Provides-Extra: xgboost
Requires-Dist: xgboost>=2.0; extra == 'xgboost'
Description-Content-Type: text/markdown

# OpenRunner SDK

[![PyPI](https://img.shields.io/pypi/v/openrunner-sdk)](https://pypi.org/project/openrunner-sdk/)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)

Open-source, self-hosted ML experiment tracking — a drop-in replacement for [Weights & Biases](https://wandb.ai).

## Install

```bash
pip install openrunner-sdk
```

## Setup

```bash
export OPENRUNNER_API_KEY="or_your_key"
export OPENRUNNER_BASE_URL="https://your-server.com"
```

Or use the CLI:

```bash
openrunner login
```

## Quick Start

```python
import openrunner

# Start a run
openrunner.init(project="my-project", config={"lr": 0.001, "epochs": 10})

# Log metrics in your training loop
for epoch in range(10):
    loss = train(epoch)
    acc = evaluate()
    openrunner.log({"loss": loss, "accuracy": acc, "epoch": epoch})

# End the run
openrunner.finish()
```

## API Reference

### Core Functions

#### `openrunner.init()`

Initialize a new experiment run.

```python
run = openrunner.init(
    project="my-project",          # Project name (auto-created if missing)
    name="experiment-1",           # Optional display name
    config={"lr": 0.001},          # Hyperparameters
    tags=["baseline", "v2"],       # Optional tags
    notes="Testing new arch",      # Optional notes
    group="sweep-1",               # Optional group name
    job_type="train",              # Optional job type
    resume=True,                   # Resume a previous run by ID
)
```

#### `openrunner.log()`

Log metrics. Non-blocking — never slows down training.

```python
# Basic logging
openrunner.log({"loss": 0.5, "accuracy": 0.85})

# With explicit step
openrunner.log({"loss": 0.3}, step=100)

# Log images
openrunner.log({"predictions": openrunner.Image(img_array, caption="epoch 5")})

# Log tables
table = openrunner.Table(
    columns=["input", "predicted", "actual"],
    data=[["img_01", 7, 7], ["img_02", 3, 5]],
)
openrunner.log({"eval_results": table})
```

#### `openrunner.finish()`

End the current run. Flushes all buffered metrics.

```python
openrunner.finish()
openrunner.finish(exit_code=0)    # With exit code
openrunner.finish(quiet=True)     # Suppress output
```

### Config

Dict-like object with dot notation. Set at `init()`, accessible throughout the run.

```python
openrunner.init(config={"optimizer": {"lr": 0.001, "weight_decay": 1e-5}})

# Access
print(openrunner.config["optimizer.lr"])    # 0.001 (flattened keys)
print(openrunner.config.optimizer.lr)       # 0.001 (dot notation)

# Update after init
openrunner.config.update({"batch_size": 64})
openrunner.config["new_param"] = "value"
```

### Summary

Auto-updated with the last logged value for each key. Can also be set explicitly.

```python
# Auto-populated from log()
openrunner.log({"loss": 0.5})
openrunner.log({"loss": 0.3})
print(openrunner.summary["loss"])  # 0.3 (last value)

# Explicit set
openrunner.summary["best_accuracy"] = 0.95
openrunner.summary["final_loss"] = 0.1
```

### Artifacts

Version datasets, models, and checkpoints with content-hash deduplication.

```python
# Log a model artifact
artifact = openrunner.Artifact(name="my-model", type="model")
artifact.add_file("model.pth")
artifact.add_file("config.json")
run.log_artifact(artifact)

# Use an artifact from a previous run
artifact = run.use_artifact("my-model:v2")
artifact.download("/path/to/dir")
```

### Media Types

#### Images

```python
import numpy as np

# From numpy array
img = openrunner.Image(np.random.rand(28, 28, 3), caption="sample")

# From PIL Image
from PIL import Image as PILImage
pil_img = PILImage.open("photo.png")
img = openrunner.Image(pil_img, caption="photo")

# From file path
img = openrunner.Image("output.png", caption="result")

openrunner.log({"examples": img})
```

#### Tables

```python
table = openrunner.Table(
    columns=["epoch", "loss", "accuracy"],
    data=[
        [1, 0.9, 0.65],
        [2, 0.5, 0.82],
        [3, 0.3, 0.91],
    ],
)
openrunner.log({"metrics_table": table})
```

### Run Properties

```python
run = openrunner.init(project="test")

print(run.id)          # "a1b2c3d4" (8-char ID)
print(run.name)        # Display name
print(run.project)     # Project name
print(run.config)      # Config object
print(run.summary)     # Summary object
```

## Migrating from W&B

Change one import — everything else stays the same:

```python
# Before
import wandb
wandb.init(project="my-project")
wandb.log({"loss": 0.5})
wandb.finish()

# After
import openrunner as wandb
wandb.init(project="my-project")
wandb.log({"loss": 0.5})
wandb.finish()
```

## Framework Integrations

### PyTorch

```python
from openrunner.integration.pytorch import log_gradients

openrunner.init(project="pytorch-example")

for batch in dataloader:
    loss = model(batch)
    loss.backward()
    log_gradients(model)  # Logs gradient norms
    optimizer.step()

openrunner.finish()
```

### HuggingFace Transformers

```python
from openrunner.integration.huggingface import OpenRunnerCallback

openrunner.init(project="hf-example")

trainer = Trainer(
    model=model,
    args=training_args,
    callbacks=[OpenRunnerCallback()],
)
trainer.train()

openrunner.finish()
```

### PyTorch Lightning

```python
from openrunner.integration.lightning import OpenRunnerLogger

logger = OpenRunnerLogger(project="lightning-example")

trainer = pl.Trainer(logger=logger)
trainer.fit(model)
```

## Offline Mode

Train without connectivity, sync later:

```bash
export OPENRUNNER_MODE=offline
python train.py

# When back online
openrunner sync
```

Offline runs are stored as JSONL files (human-readable, crash-safe). Sync is additive and idempotent — interrupted syncs resume without data loss.

## CLI

```bash
# Authenticate
openrunner login

# Sync offline runs
openrunner sync

# List projects and runs
openrunner ls
```

## System Metrics

Automatically collected during training (enabled by default):

- CPU utilization (%)
- System memory usage (%)
- GPU utilization (%) — requires `pip install openrunner-sdk[gpu]`
- GPU memory usage (%)

Disable with:

```bash
export OPENRUNNER_SYSTEM_METRICS=false
```

## Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `OPENRUNNER_API_KEY` | API key for authentication | (required) |
| `OPENRUNNER_BASE_URL` | Server URL | `http://localhost:8000` |
| `OPENRUNNER_PROJECT` | Default project name | (none) |
| `OPENRUNNER_MODE` | `online` or `offline` | `online` |
| `OPENRUNNER_SYSTEM_METRICS` | Enable system metrics | `true` |
| `OPENRUNNER_OFFLINE_DIR` | Offline storage directory | `~/.openrunner/offline` |

W&B env vars (`WANDB_API_KEY`, `WANDB_BASE_URL`) are also supported as fallback for migration.

## Self-Hosting

OpenRunner is designed to be self-hosted. See the [main repo](https://github.com/jqueguiner/openrunner) for server setup with Docker Compose.

## License

MIT
