Skip to main content

OpenRunner SDK

PyPI License: MIT Python 3.10+

Open-source, self-hosted ML experiment tracking — a drop-in replacement for Weights & Biases.

Install

pip install openrunner-sdk

Setup

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

Or use the CLI:

openrunner login

Quick Start

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.

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.

# 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.

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.

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.

# 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.

# 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

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

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

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

HTML

# Log raw HTML for rich reports, custom visualizations, or formatted output
openrunner.log({"report": openrunner.Html("<h1>Training Report</h1><p>Loss converged at epoch 42.</p>")})

Histograms

import numpy as np

weights = np.random.randn(10000)
openrunner.log({"weight_dist": openrunner.Histogram(weights, num_bins=50)})

Plotly Charts

import plotly.graph_objects as go

fig = go.Figure(data=go.Scatter(x=[1, 2, 3], y=[4, 5, 6]))
openrunner.log({"interactive_plot": openrunner.Plotly(fig)})

Point Clouds

import numpy as np

points = np.random.randn(1000, 3)
colors = np.random.randint(0, 255, (1000, 3), dtype=np.uint8)
openrunner.log({"lidar": openrunner.PointCloud3D(points, colors=colors)})

Bounding Boxes

img = openrunner.Image("photo.jpg")
boxes = [{"position": {"minX": 10, "minY": 20, "maxX": 100, "maxY": 150}, "class_id": 0}]
openrunner.log({"detections": openrunner.BoundingBoxes2D(img, boxes, class_labels={0: "cat"})})

Audio

import numpy as np

# From numpy array (mono, float32, -1 to 1)
audio = openrunner.Audio(np.random.randn(44100).astype(np.float32), sample_rate=44100)
openrunner.log({"audio_sample": audio})

Video

# From file path
openrunner.log({"demo": openrunner.Video("output.mp4", caption="training demo")})

Matplotlib Figures

import matplotlib.pyplot as plt

plt.figure()
plt.plot([1, 2, 3], [4, 5, 6])
openrunner.log({"chart": openrunner.MatplotlibFigure()})  # captures current figure
plt.close()

LLM Tracing

Trace LLM API calls for debugging and cost tracking.

import openrunner

openrunner.init(project="llm-app")

# Auto-trace OpenAI calls
openrunner.trace.patch_openai()

# Or manually trace any function
@openrunner.trace
def generate(prompt):
    return client.chat.completions.create(
        model="gpt-4", messages=[{"role": "user", "content": prompt}]
    )

Hyperparameter Sweeps

Run distributed hyperparameter searches.

import openrunner

sweep_config = {
    "method": "bayes",
    "metric": {"name": "val_loss", "goal": "minimize"},
    "parameters": {
        "lr": {"min": 1e-5, "max": 1e-2, "distribution": "log_uniform"},
        "epochs": {"values": [10, 20, 50]},
    },
}

sweep_id = openrunner.sweep(sweep_config, project="my-project")

def train():
    run = openrunner.init()
    lr = openrunner.config.lr
    # ... training loop ...
    openrunner.finish()

openrunner.agent(sweep_id, function=train, count=20)

Remote Launch

Submit training jobs to remote infrastructure.

import openrunner

job = openrunner.launch(
    project="my-project",
    config={"lr": 0.001, "epochs": 50},
    resource="gpu-a100",
)

job.wait()  # block until finished
print(job.state)  # "finished"

Model Registry

Version and alias models for production deployment.

# Log a model with aliases
artifact = openrunner.Artifact(name="classifier", type="model")
artifact.add_file("model.pt")
openrunner.link_artifact(artifact, aliases=["staging"])

# Use a model by alias
model_dir = openrunner.use_artifact("classifier:production")

Alerts

Send notifications when training reaches milestones or encounters issues.

openrunner.alert(title="Training complete", text="Final accuracy: 95.2%", level="INFO")
openrunner.alert(title="Loss spike detected", level="WARN")

Query API

Read-only access to runs, metrics, and projects for analysis and dashboards.

api = openrunner.Api()
runs = api.runs("my-project", filters={"state": "finished"})
for run in runs:
    print(f"{run.name}: {run.summary.get('accuracy')}")

Migrating from W&B

Change one import — everything else stays the same:

# 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

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

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

from openrunner.integration.lightning import OpenRunnerLogger

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

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

Keras

from openrunner.integration.keras import OpenRunnerCallback

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

model.fit(x_train, y_train, callbacks=[OpenRunnerCallback()])

openrunner.finish()

XGBoost

from openrunner.integration.xgboost import OpenRunnerCallback

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

bst = xgb.train(params, dtrain, callbacks=[OpenRunnerCallback()])

openrunner.finish()

scikit-learn

from openrunner.integration.sklearn import log_model

openrunner.init(project="sklearn-example")
model.fit(X_train, y_train)
log_model(model)  # Logs parameters and metrics
openrunner.finish()

FastAI

from openrunner.integration.fastai import OpenRunnerCallback

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

learn = cnn_learner(dls, resnet34, cbs=[OpenRunnerCallback()])
learn.fine_tune(5)

openrunner.finish()

LangChain

from openrunner.integration.langchain import OpenRunnerTracer

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

tracer = OpenRunnerTracer()
chain.invoke({"input": "Hello"}, config={"callbacks": [tracer]})

openrunner.finish()

Offline Mode

Train without connectivity, sync later:

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

# 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:

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 for server setup with Docker Compose.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openrunner_sdk-2.37.21.tar.gz (511.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

openrunner_sdk-2.37.21-py3-none-any.whl (465.6 kB view details)

Uploaded Python 3

File details

Details for the file openrunner_sdk-2.37.21.tar.gz.

File metadata

  • Download URL: openrunner_sdk-2.37.21.tar.gz
  • Upload date:
  • Size: 511.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for openrunner_sdk-2.37.21.tar.gz
Algorithm Hash digest
SHA256 96a1a75523b1f18e4f4d38b9d37c3cd82d28b5fe62f267b53aae102b6d359cf1
MD5 64a0bea471a41a0ee1034f6007e28bc2
BLAKE2b-256 39313fe9948fa6f360a13df305954afa014e31cad869cdafa41629b0e5a35b0d

See more details on using hashes here.

File details

Details for the file openrunner_sdk-2.37.21-py3-none-any.whl.

File metadata

File hashes

Hashes for openrunner_sdk-2.37.21-py3-none-any.whl
Algorithm Hash digest
SHA256 6ba84ee90dd80456d33b6e8a6df839f4b952f3d0939620f979c1fb3685ae77a7
MD5 62589880e203138c8fd1d71560b2e4b9
BLAKE2b-256 70cd10ab2f719cb521583104dc878b548e5317260f08ef0d43a90be7bc738e02

See more details on using hashes here.

Release history Release notifications | RSS feed

2.41.7

2 files

2.41.6

2 files

2.41.5

2 files

2.41.4

2 files

2.41.3

2 files

2.41.2

2 files

2.41.1

2 files

2.41.0

2 files

2.40.1

2 files

2.40.0

2 files

2.39.0

2 files

2.38.3

2 files

2.38.2

2 files

2.38.1

2 files

2.38.0

2 files

2.37.36

2 files

2.37.35

2 files

2.37.34

2 files

2.37.33

2 files

2.37.32

2 files

2.37.31

2 files

2.37.30

2 files

2.37.29

2 files

2.37.28

2 files

2.37.27

2 files

2.37.26

2 files

2.37.25

2 files

2.37.24

2 files

2.37.23

2 files

2.37.22

2 files

This release

2.37.21 This release

2 files

2.37.20

2 files

2.37.19

2 files

2.37.18

2 files

2.37.17

2 files

2.37.16

2 files

2.37.15

2 files

2.37.14

2 files

2.37.13

2 files

2.37.12

2 files

2.37.11

2 files

2.37.10

2 files

2.37.9

2 files

2.37.8

2 files

2.37.7

2 files

2.37.6

2 files

2.37.5

2 files

2.37.4

2 files

2.37.1

2 files

2.37.0

2 files

2.25.0

2 files

2.24.2

2 files

2.24.1

2 files

2.24.0

2 files

2.23.0

2 files

2.22.0

2 files

2.21.0

2 files

2.20.9

2 files

2.20.8

2 files

2.20.7

2 files

2.20.6

2 files

2.20.5

2 files

2.20.4

2 files

2.20.3

2 files

2.20.2

2 files

2.20.1

2 files

2.20.0

2 files

2.19.2

2 files

2.19.1

2 files

2.19.0

2 files

2.18.6

2 files

2.18.5

2 files

2.18.4

2 files

2.18.3

2 files

2.18.2

2 files

2.18.1

2 files

2.18.0

2 files

2.17.4

2 files

2.17.3

2 files

2.17.2

2 files

2.17.1

2 files

2.17.0

2 files

2.16.7

2 files

2.16.6

2 files

2.16.5

2 files

2.16.4

2 files

2.16.3

2 files

2.16.2

2 files

2.16.1

2 files

2.16.0

2 files

2.15.6

2 files

2.15.5

2 files

2.15.4

2 files

2.15.3

2 files

2.15.2

2 files

2.15.1

2 files

2.15.0

2 files

2.14.4

2 files

2.14.3

2 files

2.14.2

2 files

2.14.1

2 files

2.14.0

2 files

2.13.4

2 files

2.13.3

2 files

2.13.2

2 files

2.13.1

2 files

2.13.0

2 files

2.12.0

2 files

2.11.0

2 files

2.10.0

2 files

2.9.0

2 files

2.8.0

2 files

2.7.1

2 files

2.7.0

2 files

2.6.0

2 files

2.5.0

2 files

2.4.4

2 files

2.4.3

2 files

2.4.2

2 files

2.4.1

2 files

2.4.0

2 files

2.3.0

2 files

2.2.0

2 files

2.1.0

2 files

2.0.0

2 files

1.6.0

2 files

1.5.0

2 files

1.4.0

2 files

1.3.0

2 files

1.2.0

2 files

1.1.0

2 files

1.0.0

2 files

0.8.0

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page