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.31.tar.gz (518.7 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.31-py3-none-any.whl (472.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: openrunner_sdk-2.37.31.tar.gz
  • Upload date:
  • Size: 518.7 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.31.tar.gz
Algorithm Hash digest
SHA256 67e612bf77324fcb77383e374bcf5cbbfc6d254c998dd4bb0f9d7089f5975206
MD5 0499790c591d4741ed44d4003014e3df
BLAKE2b-256 5e73847f979f7b1f29c0b7acbb13f9f0647375b738d6b004b38c9e61286311e5

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for openrunner_sdk-2.37.31-py3-none-any.whl
Algorithm Hash digest
SHA256 28a8bfb6659e86499f22f7c549734075e2bb1fc0bda77bf49f692e5394870ac8
MD5 131f919af42f8604a29475a91964d633
BLAKE2b-256 1b849260b7d54583b344b7f1465c97fce146b66fba282ec6d10f2c18ab15a61c

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

This release

2.37.31 This release

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

2.37.21

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