Skip to main content

OpenRunner SDK - W&B-compatible ML experiment tracking client

Project description

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

Project details


Release history Release notifications | RSS feed

This version

1.4.0

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-1.4.0.tar.gz (151.9 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-1.4.0-py3-none-any.whl (121.0 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for openrunner_sdk-1.4.0.tar.gz
Algorithm Hash digest
SHA256 8cea00ce3026dba3f8efbf30057e55391251ed889ab62ae2ba3156d5363962e0
MD5 34b9227e9ca8958893da445ae1fe4217
BLAKE2b-256 b66d022e0741533ee704bd6f0b12c7f747083b2518af9495e1a0cf9e81987790

See more details on using hashes here.

File details

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

File metadata

  • Download URL: openrunner_sdk-1.4.0-py3-none-any.whl
  • Upload date:
  • Size: 121.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.7

File hashes

Hashes for openrunner_sdk-1.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7535b4b9c4221c7c606808a3cda97cf8a22c1cb71517253c0dd2c5e5eee04855
MD5 4d5c450c3def09ef446e014ac916f4e7
BLAKE2b-256 bc771dc0e2d7571dfa34112fe65f8735e2e8d4b13d91a816f7f1b876280f655f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page