openrunner-sdk 0.2.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/.gitignore +1 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/PKG-INFO +233 -1
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/README.md +230 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/__init__.py +53 -2
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/api_client.py +93 -2
- openrunner_sdk-0.4.0/openrunner/artifact.py +165 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/cli.py +365 -0
- openrunner_sdk-0.4.0/openrunner/evaluation.py +280 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/media.py +282 -0
- openrunner_sdk-0.4.0/openrunner/prompt.py +278 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/run.py +176 -6
- openrunner_sdk-0.4.0/openrunner/scorers.py +74 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/sender.py +74 -2
- openrunner_sdk-0.4.0/openrunner/system_metrics.py +157 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/pyproject.toml +3 -3
- openrunner_sdk-0.4.0/tests/test_evaluation.py +375 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_media.py +411 -1
- openrunner_sdk-0.2.0/openrunner/artifact.py +0 -88
- openrunner_sdk-0.2.0/openrunner/system_metrics.py +0 -97
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/=6.0 +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/=8.1 +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/buffer.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/cache.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/config.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/git_info.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/integration/__init__.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/integration/fastai.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/integration/huggingface.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/integration/keras.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/integration/langchain.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/integration/lightning.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/integration/pytorch.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/integration/sklearn.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/integration/xgboost.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/launch.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/offline.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/query_api.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/settings.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/summary.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/sweep.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/trace.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/wandb_compat/__init__.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/wandb_compat/_shim.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/__init__.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/conftest.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_alert.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_aliases.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_api_client.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_artifact.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_buffer.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_cache.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_cli.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_config.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_finish.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_git_info.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_init.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_integration_fastai.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_integration_huggingface.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_integration_keras.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_integration_langchain.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_integration_lightning.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_integration_pytorch.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_integration_sklearn.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_integration_xgboost.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_launch.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_log.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_offline.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_offline_sync.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_query_api.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_resume.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_sender.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_summary.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_sweep.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_system_metrics.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_trace.py +0 -0
- {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_wandb_compat.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openrunner-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: OpenRunner SDK - W&B-compatible ML experiment tracking client
|
|
5
5
|
Project-URL: Homepage, https://github.com/jqueguiner/openrunner
|
|
6
6
|
Project-URL: Repository, https://github.com/jqueguiner/openrunner
|
|
@@ -21,9 +21,11 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
|
21
21
|
Requires-Python: >=3.10
|
|
22
22
|
Requires-Dist: click>=8.1
|
|
23
23
|
Requires-Dist: httpx>=0.27
|
|
24
|
+
Requires-Dist: nvidia-ml-py>=12.0
|
|
24
25
|
Requires-Dist: pillow>=10.0
|
|
25
26
|
Requires-Dist: psutil>=6.0
|
|
26
27
|
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: numpy>=1.24; extra == 'dev'
|
|
27
29
|
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
28
30
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
29
31
|
Provides-Extra: fastai
|
|
@@ -238,6 +240,175 @@ print(run.config) # Config object
|
|
|
238
240
|
print(run.summary) # Summary object
|
|
239
241
|
```
|
|
240
242
|
|
|
243
|
+
### HTML
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
# Log raw HTML for rich reports, custom visualizations, or formatted output
|
|
247
|
+
openrunner.log({"report": openrunner.Html("<h1>Training Report</h1><p>Loss converged at epoch 42.</p>")})
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Histograms
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
import numpy as np
|
|
254
|
+
|
|
255
|
+
weights = np.random.randn(10000)
|
|
256
|
+
openrunner.log({"weight_dist": openrunner.Histogram(weights, num_bins=50)})
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Plotly Charts
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
import plotly.graph_objects as go
|
|
263
|
+
|
|
264
|
+
fig = go.Figure(data=go.Scatter(x=[1, 2, 3], y=[4, 5, 6]))
|
|
265
|
+
openrunner.log({"interactive_plot": openrunner.Plotly(fig)})
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Point Clouds
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
import numpy as np
|
|
272
|
+
|
|
273
|
+
points = np.random.randn(1000, 3)
|
|
274
|
+
colors = np.random.randint(0, 255, (1000, 3), dtype=np.uint8)
|
|
275
|
+
openrunner.log({"lidar": openrunner.PointCloud3D(points, colors=colors)})
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Bounding Boxes
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
img = openrunner.Image("photo.jpg")
|
|
282
|
+
boxes = [{"position": {"minX": 10, "minY": 20, "maxX": 100, "maxY": 150}, "class_id": 0}]
|
|
283
|
+
openrunner.log({"detections": openrunner.BoundingBoxes2D(img, boxes, class_labels={0: "cat"})})
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Audio
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
import numpy as np
|
|
290
|
+
|
|
291
|
+
# From numpy array (mono, float32, -1 to 1)
|
|
292
|
+
audio = openrunner.Audio(np.random.randn(44100).astype(np.float32), sample_rate=44100)
|
|
293
|
+
openrunner.log({"audio_sample": audio})
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Video
|
|
297
|
+
|
|
298
|
+
```python
|
|
299
|
+
# From file path
|
|
300
|
+
openrunner.log({"demo": openrunner.Video("output.mp4", caption="training demo")})
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Matplotlib Figures
|
|
304
|
+
|
|
305
|
+
```python
|
|
306
|
+
import matplotlib.pyplot as plt
|
|
307
|
+
|
|
308
|
+
plt.figure()
|
|
309
|
+
plt.plot([1, 2, 3], [4, 5, 6])
|
|
310
|
+
openrunner.log({"chart": openrunner.MatplotlibFigure()}) # captures current figure
|
|
311
|
+
plt.close()
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## LLM Tracing
|
|
315
|
+
|
|
316
|
+
Trace LLM API calls for debugging and cost tracking.
|
|
317
|
+
|
|
318
|
+
```python
|
|
319
|
+
import openrunner
|
|
320
|
+
|
|
321
|
+
openrunner.init(project="llm-app")
|
|
322
|
+
|
|
323
|
+
# Auto-trace OpenAI calls
|
|
324
|
+
openrunner.trace.patch_openai()
|
|
325
|
+
|
|
326
|
+
# Or manually trace any function
|
|
327
|
+
@openrunner.trace
|
|
328
|
+
def generate(prompt):
|
|
329
|
+
return client.chat.completions.create(
|
|
330
|
+
model="gpt-4", messages=[{"role": "user", "content": prompt}]
|
|
331
|
+
)
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## Hyperparameter Sweeps
|
|
335
|
+
|
|
336
|
+
Run distributed hyperparameter searches.
|
|
337
|
+
|
|
338
|
+
```python
|
|
339
|
+
import openrunner
|
|
340
|
+
|
|
341
|
+
sweep_config = {
|
|
342
|
+
"method": "bayes",
|
|
343
|
+
"metric": {"name": "val_loss", "goal": "minimize"},
|
|
344
|
+
"parameters": {
|
|
345
|
+
"lr": {"min": 1e-5, "max": 1e-2, "distribution": "log_uniform"},
|
|
346
|
+
"epochs": {"values": [10, 20, 50]},
|
|
347
|
+
},
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
sweep_id = openrunner.sweep(sweep_config, project="my-project")
|
|
351
|
+
|
|
352
|
+
def train():
|
|
353
|
+
run = openrunner.init()
|
|
354
|
+
lr = openrunner.config.lr
|
|
355
|
+
# ... training loop ...
|
|
356
|
+
openrunner.finish()
|
|
357
|
+
|
|
358
|
+
openrunner.agent(sweep_id, function=train, count=20)
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Remote Launch
|
|
362
|
+
|
|
363
|
+
Submit training jobs to remote infrastructure.
|
|
364
|
+
|
|
365
|
+
```python
|
|
366
|
+
import openrunner
|
|
367
|
+
|
|
368
|
+
job = openrunner.launch(
|
|
369
|
+
project="my-project",
|
|
370
|
+
config={"lr": 0.001, "epochs": 50},
|
|
371
|
+
resource="gpu-a100",
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
job.wait() # block until finished
|
|
375
|
+
print(job.state) # "finished"
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## Model Registry
|
|
379
|
+
|
|
380
|
+
Version and alias models for production deployment.
|
|
381
|
+
|
|
382
|
+
```python
|
|
383
|
+
# Log a model with aliases
|
|
384
|
+
artifact = openrunner.Artifact(name="classifier", type="model")
|
|
385
|
+
artifact.add_file("model.pt")
|
|
386
|
+
openrunner.link_artifact(artifact, aliases=["staging"])
|
|
387
|
+
|
|
388
|
+
# Use a model by alias
|
|
389
|
+
model_dir = openrunner.use_artifact("classifier:production")
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## Alerts
|
|
393
|
+
|
|
394
|
+
Send notifications when training reaches milestones or encounters issues.
|
|
395
|
+
|
|
396
|
+
```python
|
|
397
|
+
openrunner.alert(title="Training complete", text="Final accuracy: 95.2%", level="INFO")
|
|
398
|
+
openrunner.alert(title="Loss spike detected", level="WARN")
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
## Query API
|
|
402
|
+
|
|
403
|
+
Read-only access to runs, metrics, and projects for analysis and dashboards.
|
|
404
|
+
|
|
405
|
+
```python
|
|
406
|
+
api = openrunner.Api()
|
|
407
|
+
runs = api.runs("my-project", filters={"state": "finished"})
|
|
408
|
+
for run in runs:
|
|
409
|
+
print(f"{run.name}: {run.summary.get('accuracy')}")
|
|
410
|
+
```
|
|
411
|
+
|
|
241
412
|
## Migrating from W&B
|
|
242
413
|
|
|
243
414
|
Change one import — everything else stays the same:
|
|
@@ -302,6 +473,67 @@ trainer = pl.Trainer(logger=logger)
|
|
|
302
473
|
trainer.fit(model)
|
|
303
474
|
```
|
|
304
475
|
|
|
476
|
+
### Keras
|
|
477
|
+
|
|
478
|
+
```python
|
|
479
|
+
from openrunner.integration.keras import OpenRunnerCallback
|
|
480
|
+
|
|
481
|
+
openrunner.init(project="keras-example")
|
|
482
|
+
|
|
483
|
+
model.fit(x_train, y_train, callbacks=[OpenRunnerCallback()])
|
|
484
|
+
|
|
485
|
+
openrunner.finish()
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### XGBoost
|
|
489
|
+
|
|
490
|
+
```python
|
|
491
|
+
from openrunner.integration.xgboost import OpenRunnerCallback
|
|
492
|
+
|
|
493
|
+
openrunner.init(project="xgboost-example")
|
|
494
|
+
|
|
495
|
+
bst = xgb.train(params, dtrain, callbacks=[OpenRunnerCallback()])
|
|
496
|
+
|
|
497
|
+
openrunner.finish()
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### scikit-learn
|
|
501
|
+
|
|
502
|
+
```python
|
|
503
|
+
from openrunner.integration.sklearn import log_model
|
|
504
|
+
|
|
505
|
+
openrunner.init(project="sklearn-example")
|
|
506
|
+
model.fit(X_train, y_train)
|
|
507
|
+
log_model(model) # Logs parameters and metrics
|
|
508
|
+
openrunner.finish()
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### FastAI
|
|
512
|
+
|
|
513
|
+
```python
|
|
514
|
+
from openrunner.integration.fastai import OpenRunnerCallback
|
|
515
|
+
|
|
516
|
+
openrunner.init(project="fastai-example")
|
|
517
|
+
|
|
518
|
+
learn = cnn_learner(dls, resnet34, cbs=[OpenRunnerCallback()])
|
|
519
|
+
learn.fine_tune(5)
|
|
520
|
+
|
|
521
|
+
openrunner.finish()
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
### LangChain
|
|
525
|
+
|
|
526
|
+
```python
|
|
527
|
+
from openrunner.integration.langchain import OpenRunnerTracer
|
|
528
|
+
|
|
529
|
+
openrunner.init(project="langchain-example")
|
|
530
|
+
|
|
531
|
+
tracer = OpenRunnerTracer()
|
|
532
|
+
chain.invoke({"input": "Hello"}, config={"callbacks": [tracer]})
|
|
533
|
+
|
|
534
|
+
openrunner.finish()
|
|
535
|
+
```
|
|
536
|
+
|
|
305
537
|
## Offline Mode
|
|
306
538
|
|
|
307
539
|
Train without connectivity, sync later:
|
|
@@ -190,6 +190,175 @@ print(run.config) # Config object
|
|
|
190
190
|
print(run.summary) # Summary object
|
|
191
191
|
```
|
|
192
192
|
|
|
193
|
+
### HTML
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
# Log raw HTML for rich reports, custom visualizations, or formatted output
|
|
197
|
+
openrunner.log({"report": openrunner.Html("<h1>Training Report</h1><p>Loss converged at epoch 42.</p>")})
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Histograms
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
import numpy as np
|
|
204
|
+
|
|
205
|
+
weights = np.random.randn(10000)
|
|
206
|
+
openrunner.log({"weight_dist": openrunner.Histogram(weights, num_bins=50)})
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Plotly Charts
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
import plotly.graph_objects as go
|
|
213
|
+
|
|
214
|
+
fig = go.Figure(data=go.Scatter(x=[1, 2, 3], y=[4, 5, 6]))
|
|
215
|
+
openrunner.log({"interactive_plot": openrunner.Plotly(fig)})
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Point Clouds
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
import numpy as np
|
|
222
|
+
|
|
223
|
+
points = np.random.randn(1000, 3)
|
|
224
|
+
colors = np.random.randint(0, 255, (1000, 3), dtype=np.uint8)
|
|
225
|
+
openrunner.log({"lidar": openrunner.PointCloud3D(points, colors=colors)})
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Bounding Boxes
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
img = openrunner.Image("photo.jpg")
|
|
232
|
+
boxes = [{"position": {"minX": 10, "minY": 20, "maxX": 100, "maxY": 150}, "class_id": 0}]
|
|
233
|
+
openrunner.log({"detections": openrunner.BoundingBoxes2D(img, boxes, class_labels={0: "cat"})})
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Audio
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
import numpy as np
|
|
240
|
+
|
|
241
|
+
# From numpy array (mono, float32, -1 to 1)
|
|
242
|
+
audio = openrunner.Audio(np.random.randn(44100).astype(np.float32), sample_rate=44100)
|
|
243
|
+
openrunner.log({"audio_sample": audio})
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Video
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
# From file path
|
|
250
|
+
openrunner.log({"demo": openrunner.Video("output.mp4", caption="training demo")})
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Matplotlib Figures
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
import matplotlib.pyplot as plt
|
|
257
|
+
|
|
258
|
+
plt.figure()
|
|
259
|
+
plt.plot([1, 2, 3], [4, 5, 6])
|
|
260
|
+
openrunner.log({"chart": openrunner.MatplotlibFigure()}) # captures current figure
|
|
261
|
+
plt.close()
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## LLM Tracing
|
|
265
|
+
|
|
266
|
+
Trace LLM API calls for debugging and cost tracking.
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
import openrunner
|
|
270
|
+
|
|
271
|
+
openrunner.init(project="llm-app")
|
|
272
|
+
|
|
273
|
+
# Auto-trace OpenAI calls
|
|
274
|
+
openrunner.trace.patch_openai()
|
|
275
|
+
|
|
276
|
+
# Or manually trace any function
|
|
277
|
+
@openrunner.trace
|
|
278
|
+
def generate(prompt):
|
|
279
|
+
return client.chat.completions.create(
|
|
280
|
+
model="gpt-4", messages=[{"role": "user", "content": prompt}]
|
|
281
|
+
)
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Hyperparameter Sweeps
|
|
285
|
+
|
|
286
|
+
Run distributed hyperparameter searches.
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
import openrunner
|
|
290
|
+
|
|
291
|
+
sweep_config = {
|
|
292
|
+
"method": "bayes",
|
|
293
|
+
"metric": {"name": "val_loss", "goal": "minimize"},
|
|
294
|
+
"parameters": {
|
|
295
|
+
"lr": {"min": 1e-5, "max": 1e-2, "distribution": "log_uniform"},
|
|
296
|
+
"epochs": {"values": [10, 20, 50]},
|
|
297
|
+
},
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
sweep_id = openrunner.sweep(sweep_config, project="my-project")
|
|
301
|
+
|
|
302
|
+
def train():
|
|
303
|
+
run = openrunner.init()
|
|
304
|
+
lr = openrunner.config.lr
|
|
305
|
+
# ... training loop ...
|
|
306
|
+
openrunner.finish()
|
|
307
|
+
|
|
308
|
+
openrunner.agent(sweep_id, function=train, count=20)
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Remote Launch
|
|
312
|
+
|
|
313
|
+
Submit training jobs to remote infrastructure.
|
|
314
|
+
|
|
315
|
+
```python
|
|
316
|
+
import openrunner
|
|
317
|
+
|
|
318
|
+
job = openrunner.launch(
|
|
319
|
+
project="my-project",
|
|
320
|
+
config={"lr": 0.001, "epochs": 50},
|
|
321
|
+
resource="gpu-a100",
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
job.wait() # block until finished
|
|
325
|
+
print(job.state) # "finished"
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Model Registry
|
|
329
|
+
|
|
330
|
+
Version and alias models for production deployment.
|
|
331
|
+
|
|
332
|
+
```python
|
|
333
|
+
# Log a model with aliases
|
|
334
|
+
artifact = openrunner.Artifact(name="classifier", type="model")
|
|
335
|
+
artifact.add_file("model.pt")
|
|
336
|
+
openrunner.link_artifact(artifact, aliases=["staging"])
|
|
337
|
+
|
|
338
|
+
# Use a model by alias
|
|
339
|
+
model_dir = openrunner.use_artifact("classifier:production")
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Alerts
|
|
343
|
+
|
|
344
|
+
Send notifications when training reaches milestones or encounters issues.
|
|
345
|
+
|
|
346
|
+
```python
|
|
347
|
+
openrunner.alert(title="Training complete", text="Final accuracy: 95.2%", level="INFO")
|
|
348
|
+
openrunner.alert(title="Loss spike detected", level="WARN")
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Query API
|
|
352
|
+
|
|
353
|
+
Read-only access to runs, metrics, and projects for analysis and dashboards.
|
|
354
|
+
|
|
355
|
+
```python
|
|
356
|
+
api = openrunner.Api()
|
|
357
|
+
runs = api.runs("my-project", filters={"state": "finished"})
|
|
358
|
+
for run in runs:
|
|
359
|
+
print(f"{run.name}: {run.summary.get('accuracy')}")
|
|
360
|
+
```
|
|
361
|
+
|
|
193
362
|
## Migrating from W&B
|
|
194
363
|
|
|
195
364
|
Change one import — everything else stays the same:
|
|
@@ -254,6 +423,67 @@ trainer = pl.Trainer(logger=logger)
|
|
|
254
423
|
trainer.fit(model)
|
|
255
424
|
```
|
|
256
425
|
|
|
426
|
+
### Keras
|
|
427
|
+
|
|
428
|
+
```python
|
|
429
|
+
from openrunner.integration.keras import OpenRunnerCallback
|
|
430
|
+
|
|
431
|
+
openrunner.init(project="keras-example")
|
|
432
|
+
|
|
433
|
+
model.fit(x_train, y_train, callbacks=[OpenRunnerCallback()])
|
|
434
|
+
|
|
435
|
+
openrunner.finish()
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### XGBoost
|
|
439
|
+
|
|
440
|
+
```python
|
|
441
|
+
from openrunner.integration.xgboost import OpenRunnerCallback
|
|
442
|
+
|
|
443
|
+
openrunner.init(project="xgboost-example")
|
|
444
|
+
|
|
445
|
+
bst = xgb.train(params, dtrain, callbacks=[OpenRunnerCallback()])
|
|
446
|
+
|
|
447
|
+
openrunner.finish()
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
### scikit-learn
|
|
451
|
+
|
|
452
|
+
```python
|
|
453
|
+
from openrunner.integration.sklearn import log_model
|
|
454
|
+
|
|
455
|
+
openrunner.init(project="sklearn-example")
|
|
456
|
+
model.fit(X_train, y_train)
|
|
457
|
+
log_model(model) # Logs parameters and metrics
|
|
458
|
+
openrunner.finish()
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
### FastAI
|
|
462
|
+
|
|
463
|
+
```python
|
|
464
|
+
from openrunner.integration.fastai import OpenRunnerCallback
|
|
465
|
+
|
|
466
|
+
openrunner.init(project="fastai-example")
|
|
467
|
+
|
|
468
|
+
learn = cnn_learner(dls, resnet34, cbs=[OpenRunnerCallback()])
|
|
469
|
+
learn.fine_tune(5)
|
|
470
|
+
|
|
471
|
+
openrunner.finish()
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### LangChain
|
|
475
|
+
|
|
476
|
+
```python
|
|
477
|
+
from openrunner.integration.langchain import OpenRunnerTracer
|
|
478
|
+
|
|
479
|
+
openrunner.init(project="langchain-example")
|
|
480
|
+
|
|
481
|
+
tracer = OpenRunnerTracer()
|
|
482
|
+
chain.invoke({"input": "Hello"}, config={"callbacks": [tracer]})
|
|
483
|
+
|
|
484
|
+
openrunner.finish()
|
|
485
|
+
```
|
|
486
|
+
|
|
257
487
|
## Offline Mode
|
|
258
488
|
|
|
259
489
|
Train without connectivity, sync later:
|
|
@@ -8,11 +8,16 @@ Public API:
|
|
|
8
8
|
openrunner.summary -> Summary proxy
|
|
9
9
|
openrunner.run -> active Run or None
|
|
10
10
|
openrunner.Image -> Image class for media logging
|
|
11
|
+
openrunner.Html -> Html class for raw HTML logging
|
|
11
12
|
openrunner.Table -> Table class for structured data
|
|
12
13
|
openrunner.Audio -> Audio class for audio logging
|
|
13
14
|
openrunner.Video -> Video class for video logging
|
|
14
15
|
openrunner.Histogram -> Histogram class for distribution logging
|
|
15
16
|
openrunner.Plotly -> Plotly class for interactive charts
|
|
17
|
+
openrunner.PlotlyChart -> Enhanced Plotly with static PNG fallback
|
|
18
|
+
openrunner.MatplotlibFigure -> Capture matplotlib figure as PNG
|
|
19
|
+
openrunner.PointCloud3D -> 3D point cloud visualization
|
|
20
|
+
openrunner.Embedding -> Embedding projection (t-SNE/PCA/UMAP)
|
|
16
21
|
openrunner.BoundingBoxes2D -> Bounding box overlay on images
|
|
17
22
|
openrunner.Artifact -> Artifact class for versioned file collections
|
|
18
23
|
openrunner.Api -> Query API for read-only access to runs/projects
|
|
@@ -26,6 +31,10 @@ Public API:
|
|
|
26
31
|
openrunner.LaunchJob -> Job handle with .wait() and .cancel()
|
|
27
32
|
openrunner.trace -> Decorator for LLM call tracing
|
|
28
33
|
openrunner.trace.patch_openai -> Auto-trace OpenAI chat completions
|
|
34
|
+
openrunner.Prompt -> Versioned prompt management (get/render/publish)
|
|
35
|
+
openrunner.evaluate -> Run LLM evaluation against a dataset
|
|
36
|
+
openrunner.scorer -> Decorator to mark a function as an eval scorer
|
|
37
|
+
openrunner.scorers -> Built-in scorers (exact_match, contains, etc.)
|
|
29
38
|
"""
|
|
30
39
|
|
|
31
40
|
from __future__ import annotations
|
|
@@ -36,14 +45,30 @@ from typing import Any
|
|
|
36
45
|
|
|
37
46
|
from openrunner.artifact import Artifact
|
|
38
47
|
from openrunner.config import Config
|
|
48
|
+
from openrunner.prompt import Prompt
|
|
39
49
|
from openrunner.launch import LaunchJob, launch, from_run as _launch_from_run
|
|
40
|
-
from openrunner.media import
|
|
50
|
+
from openrunner.media import (
|
|
51
|
+
Audio,
|
|
52
|
+
BoundingBoxes2D,
|
|
53
|
+
Embedding,
|
|
54
|
+
Histogram,
|
|
55
|
+
Html,
|
|
56
|
+
Image,
|
|
57
|
+
MatplotlibFigure,
|
|
58
|
+
Plotly,
|
|
59
|
+
PlotlyChart,
|
|
60
|
+
PointCloud3D,
|
|
61
|
+
Table,
|
|
62
|
+
Video,
|
|
63
|
+
)
|
|
41
64
|
from openrunner.query_api import Api
|
|
42
65
|
from openrunner.run import Run
|
|
43
66
|
from openrunner.settings import SDKSettings
|
|
44
67
|
from openrunner.summary import Summary
|
|
45
68
|
from openrunner.sweep import agent, sweep
|
|
69
|
+
from openrunner.evaluation import evaluate, scorer
|
|
46
70
|
from openrunner.trace import trace, patch_openai as _patch_openai
|
|
71
|
+
import openrunner.scorers as scorers
|
|
47
72
|
|
|
48
73
|
# Attach from_run as a method on the launch function for
|
|
49
74
|
# openrunner.launch.from_run() syntax
|
|
@@ -53,7 +78,7 @@ launch.from_run = _launch_from_run # type: ignore[attr-defined]
|
|
|
53
78
|
# openrunner.trace.patch_openai() syntax
|
|
54
79
|
trace.patch_openai = _patch_openai # type: ignore[attr-defined]
|
|
55
80
|
|
|
56
|
-
__version__ = "0.
|
|
81
|
+
__version__ = "0.3.0"
|
|
57
82
|
|
|
58
83
|
logger = logging.getLogger("openrunner")
|
|
59
84
|
|
|
@@ -352,6 +377,32 @@ def use_artifact(
|
|
|
352
377
|
return None
|
|
353
378
|
|
|
354
379
|
|
|
380
|
+
def should_stop() -> bool:
|
|
381
|
+
"""Check if the server has requested the active run to stop.
|
|
382
|
+
|
|
383
|
+
Returns False if no active run. Users call this in their training
|
|
384
|
+
loop: ``if openrunner.should_stop(): break``
|
|
385
|
+
"""
|
|
386
|
+
if _active_run is None:
|
|
387
|
+
return False
|
|
388
|
+
return _active_run.should_stop
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def log_text(text: str) -> None:
|
|
392
|
+
"""Log explicit text to the console log without printing. Never raises.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
text: Text string to log.
|
|
396
|
+
"""
|
|
397
|
+
try:
|
|
398
|
+
if _active_run is None:
|
|
399
|
+
logger.warning("openrunner.log_text(): no active run")
|
|
400
|
+
return
|
|
401
|
+
_active_run.log_text(text)
|
|
402
|
+
except Exception as e:
|
|
403
|
+
logger.warning("openrunner.log_text() failed: %s", e)
|
|
404
|
+
|
|
405
|
+
|
|
355
406
|
@property # type: ignore[misc]
|
|
356
407
|
def run() -> Run | None:
|
|
357
408
|
"""Return the active run, or None."""
|