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.
Files changed (76) hide show
  1. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/.gitignore +1 -0
  2. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/PKG-INFO +233 -1
  3. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/README.md +230 -0
  4. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/__init__.py +53 -2
  5. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/api_client.py +93 -2
  6. openrunner_sdk-0.4.0/openrunner/artifact.py +165 -0
  7. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/cli.py +365 -0
  8. openrunner_sdk-0.4.0/openrunner/evaluation.py +280 -0
  9. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/media.py +282 -0
  10. openrunner_sdk-0.4.0/openrunner/prompt.py +278 -0
  11. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/run.py +176 -6
  12. openrunner_sdk-0.4.0/openrunner/scorers.py +74 -0
  13. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/sender.py +74 -2
  14. openrunner_sdk-0.4.0/openrunner/system_metrics.py +157 -0
  15. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/pyproject.toml +3 -3
  16. openrunner_sdk-0.4.0/tests/test_evaluation.py +375 -0
  17. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_media.py +411 -1
  18. openrunner_sdk-0.2.0/openrunner/artifact.py +0 -88
  19. openrunner_sdk-0.2.0/openrunner/system_metrics.py +0 -97
  20. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/=6.0 +0 -0
  21. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/=8.1 +0 -0
  22. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/buffer.py +0 -0
  23. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/cache.py +0 -0
  24. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/config.py +0 -0
  25. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/git_info.py +0 -0
  26. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/integration/__init__.py +0 -0
  27. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/integration/fastai.py +0 -0
  28. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/integration/huggingface.py +0 -0
  29. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/integration/keras.py +0 -0
  30. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/integration/langchain.py +0 -0
  31. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/integration/lightning.py +0 -0
  32. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/integration/pytorch.py +0 -0
  33. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/integration/sklearn.py +0 -0
  34. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/integration/xgboost.py +0 -0
  35. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/launch.py +0 -0
  36. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/offline.py +0 -0
  37. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/query_api.py +0 -0
  38. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/settings.py +0 -0
  39. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/summary.py +0 -0
  40. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/sweep.py +0 -0
  41. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/trace.py +0 -0
  42. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/wandb_compat/__init__.py +0 -0
  43. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/openrunner/wandb_compat/_shim.py +0 -0
  44. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/__init__.py +0 -0
  45. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/conftest.py +0 -0
  46. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_alert.py +0 -0
  47. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_aliases.py +0 -0
  48. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_api_client.py +0 -0
  49. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_artifact.py +0 -0
  50. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_buffer.py +0 -0
  51. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_cache.py +0 -0
  52. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_cli.py +0 -0
  53. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_config.py +0 -0
  54. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_finish.py +0 -0
  55. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_git_info.py +0 -0
  56. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_init.py +0 -0
  57. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_integration_fastai.py +0 -0
  58. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_integration_huggingface.py +0 -0
  59. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_integration_keras.py +0 -0
  60. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_integration_langchain.py +0 -0
  61. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_integration_lightning.py +0 -0
  62. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_integration_pytorch.py +0 -0
  63. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_integration_sklearn.py +0 -0
  64. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_integration_xgboost.py +0 -0
  65. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_launch.py +0 -0
  66. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_log.py +0 -0
  67. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_offline.py +0 -0
  68. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_offline_sync.py +0 -0
  69. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_query_api.py +0 -0
  70. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_resume.py +0 -0
  71. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_sender.py +0 -0
  72. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_summary.py +0 -0
  73. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_sweep.py +0 -0
  74. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_system_metrics.py +0 -0
  75. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_trace.py +0 -0
  76. {openrunner_sdk-0.2.0 → openrunner_sdk-0.4.0}/tests/test_wandb_compat.py +0 -0
@@ -224,3 +224,4 @@ __marimo__/
224
224
  # Streamlit
225
225
  .streamlit/secrets.toml
226
226
  data/
227
+ .data/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openrunner-sdk
3
- Version: 0.2.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 Audio, BoundingBoxes2D, Histogram, Image, Plotly, Table, Video
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.1.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."""