openrunner-sdk 2.41.2__tar.gz → 2.41.4__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 (227) hide show
  1. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/PKG-INFO +1 -1
  2. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/__init__.py +1 -1
  3. openrunner_sdk-2.41.4/openrunner/_native.py +35 -0
  4. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/api_client.py +107 -0
  5. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/cli.py +166 -0
  6. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/datasets.py +101 -2
  7. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/install_commands.py +20 -0
  8. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/mcp_server.py +119 -5
  9. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/pyproject.toml +1 -1
  10. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_mcp_server.py +6 -1
  11. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/.gitignore +0 -0
  12. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/=6.0 +0 -0
  13. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/=8.1 +0 -0
  14. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/README.md +0 -0
  15. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/CLAUDE.md +0 -0
  16. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/_retry_transport.py +0 -0
  17. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/_safepath.py +0 -0
  18. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/_secret_rules.py +0 -0
  19. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/artifact.py +0 -0
  20. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/autoresearch.py +0 -0
  21. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/buffer.py +0 -0
  22. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/cache.py +0 -0
  23. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/config.py +0 -0
  24. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/cost.py +0 -0
  25. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/dataset.py +0 -0
  26. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/environment.py +0 -0
  27. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/evaluation.py +0 -0
  28. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/feedback.py +0 -0
  29. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/git_info.py +0 -0
  30. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/gpu/__init__.py +0 -0
  31. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/gpu/api_integration.py +0 -0
  32. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/gpu/cost_guard.py +0 -0
  33. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/gpu/gpu_types.py +0 -0
  34. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/gpu/price_compare.py +0 -0
  35. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/gpu/provider_base.py +0 -0
  36. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/gpu/providers/__init__.py +0 -0
  37. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/gpu/providers/forge_provider.py +0 -0
  38. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/gpu/providers/lambda_cloud.py +0 -0
  39. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/gpu/providers/modal_provider.py +0 -0
  40. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/gpu/providers/ovhcloud.py +0 -0
  41. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/gpu/providers/runpod.py +0 -0
  42. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/gpu/providers/vastai.py +0 -0
  43. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/gpu/runner.py +0 -0
  44. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/gpu/workspace_sync.py +0 -0
  45. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/guardrails.py +0 -0
  46. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/handover.py +0 -0
  47. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/hooks/patent-lab-post-commit +0 -0
  48. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/__init__.py +0 -0
  49. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/accelerate.py +0 -0
  50. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/anthropic_tracer.py +0 -0
  51. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/catboost.py +0 -0
  52. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/diffusers.py +0 -0
  53. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/fastai.py +0 -0
  54. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/forced_alignment.py +0 -0
  55. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/gladia.py +0 -0
  56. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/gymnasium.py +0 -0
  57. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/huggingface.py +0 -0
  58. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/hydra.py +0 -0
  59. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/ignite.py +0 -0
  60. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/jax.py +0 -0
  61. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/keras.py +0 -0
  62. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/langchain.py +0 -0
  63. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/lightgbm.py +0 -0
  64. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/lightning.py +0 -0
  65. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/llamaindex.py +0 -0
  66. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/openai_finetune.py +0 -0
  67. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/openai_tracer.py +0 -0
  68. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/optuna.py +0 -0
  69. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/pytorch.py +0 -0
  70. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/sb3.py +0 -0
  71. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/sklearn.py +0 -0
  72. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/skypilot.py +0 -0
  73. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/tensorflow.py +0 -0
  74. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/trl.py +0 -0
  75. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/tts.py +0 -0
  76. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/ultralytics.py +0 -0
  77. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/voice_agent.py +0 -0
  78. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/whisper.py +0 -0
  79. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/integration/xgboost.py +0 -0
  80. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/launch.py +0 -0
  81. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/machine.py +0 -0
  82. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/media.py +0 -0
  83. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/migrate.py +0 -0
  84. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/model.py +0 -0
  85. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/model_card.py +0 -0
  86. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/offline.py +0 -0
  87. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/patent_scan.py +0 -0
  88. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/pii.py +0 -0
  89. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/plot.py +0 -0
  90. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/projects.py +0 -0
  91. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/prompt.py +0 -0
  92. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/query_api.py +0 -0
  93. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/redact.py +0 -0
  94. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/reload.py +0 -0
  95. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/research.py +0 -0
  96. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/research_policy.py +0 -0
  97. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/run.py +0 -0
  98. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/scorers.py +0 -0
  99. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/sender.py +0 -0
  100. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/session.py +0 -0
  101. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/settings.py +0 -0
  102. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/summary.py +0 -0
  103. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/sweep.py +0 -0
  104. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/sync.py +0 -0
  105. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/system_metrics.py +0 -0
  106. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/tensorboard.py +0 -0
  107. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/tools/__init__.py +0 -0
  108. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/tools/patent_report.py +0 -0
  109. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/trace.py +0 -0
  110. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/transcript_formatter.py +0 -0
  111. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/wal.py +0 -0
  112. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/wandb_compat/__init__.py +0 -0
  113. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/wandb_compat/_shim.py +0 -0
  114. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/openrunner/wer.py +0 -0
  115. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/scripts/gen_betterleaks_rules.py +0 -0
  116. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/__init__.py +0 -0
  117. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/conftest.py +0 -0
  118. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/fixtures/wandb-export-mini/run-aaa/config.json +0 -0
  119. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/fixtures/wandb-export-mini/run-aaa/metadata.json +0 -0
  120. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/fixtures/wandb-export-mini/run-aaa/metrics.jsonl +0 -0
  121. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/fixtures/wandb-export-mini/run-aaa/summary.json +0 -0
  122. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/model-checkpoint/manifest.json +0 -0
  123. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/model-checkpoint/weights.bin +0 -0
  124. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/sample-image/manifest.json +0 -0
  125. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/sample-image/preview.png +0 -0
  126. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/fixtures/wandb-export-mini/run-bbb/config.json +0 -0
  127. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/fixtures/wandb-export-mini/run-bbb/metadata.json +0 -0
  128. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/fixtures/wandb-export-mini/run-bbb/metrics.jsonl +0 -0
  129. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/fixtures/wandb-export-mini/run-bbb/summary.json +0 -0
  130. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/integration/__init__.py +0 -0
  131. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/integration/conftest.py +0 -0
  132. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/integration/test_artifact_round_trip.py +0 -0
  133. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_alert.py +0 -0
  134. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_aliases.py +0 -0
  135. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_api_client.py +0 -0
  136. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_api_client_methods.py +0 -0
  137. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_api_client_transport.py +0 -0
  138. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_artifact.py +0 -0
  139. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_buffer.py +0 -0
  140. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_cache.py +0 -0
  141. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_class_scorers.py +0 -0
  142. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_cli.py +0 -0
  143. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_cli_artifact_sweep.py +0 -0
  144. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_cli_datasets.py +0 -0
  145. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_cli_gpu_registry_deploy.py +0 -0
  146. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_cli_infra.py +0 -0
  147. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_cli_remainder.py +0 -0
  148. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_cli_topup.py +0 -0
  149. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_config.py +0 -0
  150. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_datasets_module.py +0 -0
  151. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_evaluation.py +0 -0
  152. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_finish.py +0 -0
  153. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_git_info.py +0 -0
  154. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/__init__.py +0 -0
  155. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/conftest.py +0 -0
  156. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/test_cost_guard.py +0 -0
  157. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/test_engine_serverside.py +0 -0
  158. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/test_forge.py +0 -0
  159. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/test_governed_jobs.py +0 -0
  160. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/test_gpu_cluster.py +0 -0
  161. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/test_gpu_types.py +0 -0
  162. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/test_lambda.py +0 -0
  163. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/test_modal.py +0 -0
  164. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/test_ovh_training.py +0 -0
  165. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/test_ovhcloud.py +0 -0
  166. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/test_price_compare.py +0 -0
  167. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/test_provider_base.py +0 -0
  168. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/test_runner.py +0 -0
  169. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/test_runpod.py +0 -0
  170. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/test_sdk_gpu_api.py +0 -0
  171. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/test_vastai.py +0 -0
  172. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu/test_workspace_sync.py +0 -0
  173. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_gpu_modal_creds.py +0 -0
  174. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_init.py +0 -0
  175. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_init_facade.py +0 -0
  176. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_install_commands.py +0 -0
  177. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_integration_fastai.py +0 -0
  178. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_integration_huggingface.py +0 -0
  179. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_integration_keras.py +0 -0
  180. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_integration_langchain.py +0 -0
  181. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_integration_lightning.py +0 -0
  182. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_integration_pytorch.py +0 -0
  183. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_integration_setA.py +0 -0
  184. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_integration_setB.py +0 -0
  185. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_integration_setC.py +0 -0
  186. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_integration_sklearn.py +0 -0
  187. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_integration_xgboost.py +0 -0
  188. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_launch.py +0 -0
  189. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_log.py +0 -0
  190. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_log_code.py +0 -0
  191. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_machine_migrate.py +0 -0
  192. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_mcp_decisions.py +0 -0
  193. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_mcp_monitor.py +0 -0
  194. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_mcp_predictions.py +0 -0
  195. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_media.py +0 -0
  196. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_media_extra.py +0 -0
  197. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_migrate.py +0 -0
  198. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_offline.py +0 -0
  199. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_offline_sync.py +0 -0
  200. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_patent_scan.py +0 -0
  201. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_pii.py +0 -0
  202. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_plot.py +0 -0
  203. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_plot_media_extra2.py +0 -0
  204. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_prompt_module.py +0 -0
  205. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_pure_modules.py +0 -0
  206. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_query_api.py +0 -0
  207. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_redact.py +0 -0
  208. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_reload_module.py +0 -0
  209. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_research_module.py +0 -0
  210. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_resume.py +0 -0
  211. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_retry_transport.py +0 -0
  212. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_run_module.py +0 -0
  213. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_sdk_cluster_j1.py +0 -0
  214. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_sdk_cluster_j2.py +0 -0
  215. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_sdk_cluster_j3.py +0 -0
  216. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_sdk_cluster_j4.py +0 -0
  217. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_sdk_core_edges.py +0 -0
  218. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_sdk_features.py +0 -0
  219. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_sender.py +0 -0
  220. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_session_module.py +0 -0
  221. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_session_sync.py +0 -0
  222. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_summary.py +0 -0
  223. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_sweep.py +0 -0
  224. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_system_metrics.py +0 -0
  225. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_topup_hi.py +0 -0
  226. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_trace.py +0 -0
  227. {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.4}/tests/test_wandb_compat.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openrunner-sdk
3
- Version: 2.41.2
3
+ Version: 2.41.4
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
@@ -181,7 +181,7 @@ launch.from_run = _launch_from_run # type: ignore[attr-defined]
181
181
  # openrunner.trace.patch_openai() syntax
182
182
  trace.patch_openai = _patch_openai # type: ignore[attr-defined]
183
183
 
184
- __version__ = "2.41.2"
184
+ __version__ = "2.41.4"
185
185
 
186
186
  logger = logging.getLogger("openrunner")
187
187
 
@@ -0,0 +1,35 @@
1
+ """Optional native core: PyO3 bindings over the Rust openrunner-sdk.
2
+
3
+ The Python SDK progressively delegates to `openrunner_core` (built from
4
+ sdk-rust/openrunner-core-py with maturin) when it is installed; every call
5
+ site keeps a pure-Python fallback, so the native core is an accelerator and
6
+ single-implementation point, never a hard dependency.
7
+
8
+ pip install openrunner-core # optional
9
+
10
+ Covered surface today: projects (incl. linked repo), artifacts/versions,
11
+ presigned downloads (selective, GIL released during transfers), container
12
+ registries + broker login tokens, and dataset editable-overlay columns
13
+ (add column / set cell / edit row / get row — source columns stay read-only).
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from typing import Any, Optional
19
+
20
+ try: # pragma: no cover - depends on optional wheel
21
+ import openrunner_core as _core
22
+ except ImportError: # pragma: no cover
23
+ _core = None
24
+
25
+
26
+ def available() -> bool:
27
+ """True when the native core wheel is importable."""
28
+ return _core is not None
29
+
30
+
31
+ def native_client(base_url: str, api_key: str) -> Optional[Any]:
32
+ """A native Client for the given credentials, or None without the wheel."""
33
+ if _core is None:
34
+ return None
35
+ return _core.Client(base_url, api_key)
@@ -465,6 +465,110 @@ class APIClient:
465
465
  logger.warning("link_project_image failed: %s", e)
466
466
  return None
467
467
 
468
+ def update_project_repo(
469
+ self,
470
+ org_id: str,
471
+ project_id: str,
472
+ github_repo: str | None = ...,
473
+ github_path: str | None = ...,
474
+ github_branch: str | None = ...,
475
+ ) -> dict[str, Any] | None:
476
+ """PATCH /organizations/{org_id}/projects/{id}/repo -- link source repo.
477
+
478
+ Only pass the fields to change; an explicit None clears the stored
479
+ value (Ellipsis sentinel = leave unchanged).
480
+ """
481
+ try:
482
+ body: dict[str, Any] = {}
483
+ if github_repo is not ...:
484
+ body["github_repo"] = github_repo
485
+ if github_path is not ...:
486
+ body["github_path"] = github_path
487
+ if github_branch is not ...:
488
+ body["github_branch"] = github_branch
489
+ r = self._request(
490
+ "patch",
491
+ f"/organizations/{org_id}/projects/{project_id}/repo",
492
+ json=body,
493
+ )
494
+ r.raise_for_status()
495
+ return r.json()
496
+ except Exception as e:
497
+ logger.warning("update_project_repo failed: %s", e)
498
+ return None
499
+
500
+ # -- Container registries (org-level, tokens encrypted server-side) ------
501
+
502
+ def list_container_registries(self, org_id: str) -> list[dict[str, Any]]:
503
+ """GET /gpu/registries -- the org's connected registries (no tokens)."""
504
+ try:
505
+ r = self._request("get", "/gpu/registries", params={"org_id": org_id})
506
+ r.raise_for_status()
507
+ return r.json()
508
+ except Exception as e:
509
+ logger.warning("list_container_registries failed: %s", e)
510
+ return []
511
+
512
+ def create_container_registry(
513
+ self,
514
+ org_id: str,
515
+ name: str,
516
+ registry_url: str,
517
+ token: str,
518
+ username: str | None = None,
519
+ namespace: str | None = None,
520
+ ) -> dict[str, Any] | None:
521
+ """POST /gpu/registries -- store a registry credential (encrypted)."""
522
+ try:
523
+ r = self._request(
524
+ "post", "/gpu/registries", params={"org_id": org_id},
525
+ json={"name": name, "registry_url": registry_url, "token": token,
526
+ "username": username, "namespace": namespace},
527
+ )
528
+ r.raise_for_status()
529
+ return r.json()
530
+ except Exception as e:
531
+ logger.warning("create_container_registry failed: %s", e)
532
+ return None
533
+
534
+ def verify_container_registry(self, org_id: str, registry_id: str) -> dict[str, Any] | None:
535
+ """POST /gpu/registries/{id}/verify -- probe the stored credential."""
536
+ try:
537
+ r = self._request("post", f"/gpu/registries/{registry_id}/verify",
538
+ params={"org_id": org_id})
539
+ r.raise_for_status()
540
+ return r.json()
541
+ except Exception as e:
542
+ logger.warning("verify_container_registry failed: %s", e)
543
+ return None
544
+
545
+ def set_default_container_registry(self, org_id: str, registry_id: str) -> dict[str, Any] | None:
546
+ """POST /gpu/registries/{id}/default -- mark the org default registry."""
547
+ try:
548
+ r = self._request("post", f"/gpu/registries/{registry_id}/default",
549
+ params={"org_id": org_id})
550
+ r.raise_for_status()
551
+ return r.json()
552
+ except Exception as e:
553
+ logger.warning("set_default_container_registry failed: %s", e)
554
+ return None
555
+
556
+ def registry_login_token(self) -> dict[str, Any] | None:
557
+ """POST /registry/login-token -- short-lived broker docker-login credential.
558
+
559
+ Returns {token, registry_host, username, expires_in}; docker login the
560
+ registry_host with it, then push/pull <registry_host>/<ns>/<repo> — the
561
+ broker proxies to the upstream registry with the stored credential.
562
+ None when the broker is unavailable (503) or on failure.
563
+ """
564
+ try:
565
+ r = self._request("post", "/registry/login-token")
566
+ r.raise_for_status()
567
+ return r.json()
568
+ except Exception as e:
569
+ logger.warning("registry_login_token failed: %s", e)
570
+ return None
571
+
468
572
  def list_deployments(self, project_id: str) -> list[dict[str, Any]]:
469
573
  """GET /projects/{id}/deployments -- list a project's GPU deployments."""
470
574
  try:
@@ -674,6 +778,7 @@ class APIClient:
674
778
  description: str | None = None,
675
779
  metadata: dict[str, Any] | None = None,
676
780
  base_version_id: str | None = None,
781
+ message: str | None = None,
677
782
  ) -> dict[str, Any] | None:
678
783
  """POST /runs/{run_id}/artifacts -- create a new artifact version.
679
784
 
@@ -700,6 +805,8 @@ class APIClient:
700
805
  body["metadata"] = metadata
701
806
  if base_version_id is not None:
702
807
  body["base_version_id"] = base_version_id
808
+ if message is not None:
809
+ body["message"] = message
703
810
  response = self._request("post",f"/runs/{run_id}/artifacts", json=body)
704
811
  response.raise_for_status()
705
812
  return response.json()
@@ -649,12 +649,178 @@ def _format_size(size_bytes: int) -> str:
649
649
  return f"{size_bytes / (1024 * 1024 * 1024):.2f} GB"
650
650
 
651
651
 
652
+ def _resolve_org_and_project(
653
+ client: "APIClient", project_name: str
654
+ ) -> tuple[str, dict] | tuple[None, None]:
655
+ """Resolve 'org/name' (or bare name) to (org_id, project dict)."""
656
+ name = project_name.split("/")[-1]
657
+ for p in client.list_projects():
658
+ if p.get("name") == name or p.get("slug") == name or p.get("id") == project_name:
659
+ return str(p.get("org_id")), p
660
+ return None, None
661
+
662
+
663
+ @main.group()
664
+ def repo() -> None:
665
+ """Linked source repository (project <-> GitHub repo/subdir)."""
666
+ pass
667
+
668
+
669
+ @repo.command("link")
670
+ @click.argument("project")
671
+ @click.option("--repo", "repo_", required=True,
672
+ help="GitHub repo as 'org/name' or full URL")
673
+ @click.option("--path", "subdir", default=None,
674
+ help="Subdirectory of the repo holding this project's code (monorepo)")
675
+ @click.option("--branch", default=None, help="Branch to track (server default: main)")
676
+ def repo_link(project: str, repo_: str, subdir: str | None, branch: str | None) -> None:
677
+ """Link PROJECT to its source repository (with optional subdirectory)."""
678
+ client = _get_client()
679
+ org_id, proj = _resolve_org_and_project(client, project)
680
+ if proj is None:
681
+ click.echo(f"Error: project '{project}' not found.", err=True)
682
+ raise SystemExit(1)
683
+ kwargs: dict = {"github_repo": repo_}
684
+ if subdir is not None:
685
+ kwargs["github_path"] = subdir
686
+ if branch is not None:
687
+ kwargs["github_branch"] = branch
688
+ updated = client.update_project_repo(org_id, str(proj["id"]), **kwargs)
689
+ client.close()
690
+ if updated is None:
691
+ click.echo("Error: failed to link repository.", err=True)
692
+ raise SystemExit(1)
693
+ loc = updated.get("github_repo") or ""
694
+ if updated.get("github_path"):
695
+ loc += f"/{updated['github_path']}"
696
+ click.echo(f"Linked {proj.get('name')} -> {loc} @ {updated.get('github_branch') or 'main'}")
697
+
698
+
699
+ @repo.command("show")
700
+ @click.argument("project")
701
+ def repo_show(project: str) -> None:
702
+ """Show PROJECT's linked repository."""
703
+ client = _get_client()
704
+ _org_id, proj = _resolve_org_and_project(client, project)
705
+ client.close()
706
+ if proj is None:
707
+ click.echo(f"Error: project '{project}' not found.", err=True)
708
+ raise SystemExit(1)
709
+ if not proj.get("github_repo"):
710
+ click.echo("No repository linked.")
711
+ return
712
+ click.echo(f"repo: {proj['github_repo']}")
713
+ click.echo(f"path: {proj.get('github_path') or '(repo root)'}")
714
+ click.echo(f"branch: {proj.get('github_branch') or 'main'}")
715
+
716
+
717
+ @repo.command("unlink")
718
+ @click.argument("project")
719
+ def repo_unlink(project: str) -> None:
720
+ """Remove PROJECT's repository link."""
721
+ client = _get_client()
722
+ org_id, proj = _resolve_org_and_project(client, project)
723
+ if proj is None:
724
+ click.echo(f"Error: project '{project}' not found.", err=True)
725
+ raise SystemExit(1)
726
+ updated = client.update_project_repo(
727
+ org_id, str(proj["id"]),
728
+ github_repo=None, github_path=None, github_branch=None,
729
+ )
730
+ client.close()
731
+ if updated is None:
732
+ click.echo("Error: failed to unlink repository.", err=True)
733
+ raise SystemExit(1)
734
+ click.echo(f"Unlinked repository from {proj.get('name')}.")
735
+
736
+
652
737
  @main.group()
653
738
  def artifact() -> None:
654
739
  """Artifact management commands."""
655
740
  pass
656
741
 
657
742
 
743
+ @artifact.command("link-commit")
744
+ @click.argument("name")
745
+ @click.option("--commit", required=True, help="Git commit SHA the version points at")
746
+ @click.option("--repo", "repo_", default=None,
747
+ help="Repo 'org/name' (default: the project's linked repo)")
748
+ @click.option("--path", "subdir", default=None,
749
+ help="Subdirectory (default: the project's linked path)")
750
+ @click.option("--branch", default=None, help="Branch the commit lives on")
751
+ @click.option("--project", "-p", required=True, help="Project name or org/name")
752
+ @click.option("--message", "-m", default=None, help="Version message (e.g. commit subject)")
753
+ def artifact_link_commit(
754
+ name: str,
755
+ commit: str,
756
+ repo_: str | None,
757
+ subdir: str | None,
758
+ branch: str | None,
759
+ project: str,
760
+ message: str | None,
761
+ ) -> None:
762
+ """Record a new version of artifact NAME as a git commit reference.
763
+
764
+ Instead of uploading a code snapshot, the version stores {repo, commit,
765
+ path} in its metadata (zero bytes stored). Use for code artifacts whose
766
+ source of truth is the linked repository.
767
+ """
768
+ client = _get_client()
769
+ org_id, proj = _resolve_org_and_project(client, project)
770
+ if proj is None:
771
+ click.echo(f"Error: project '{project}' not found.", err=True)
772
+ client.close()
773
+ raise SystemExit(1)
774
+ repo_ = repo_ or proj.get("github_repo")
775
+ if subdir is None:
776
+ subdir = proj.get("github_path")
777
+ if not repo_:
778
+ click.echo("Error: no --repo given and project has no linked repository "
779
+ "(openrunner repo link ...).", err=True)
780
+ client.close()
781
+ raise SystemExit(1)
782
+ client.close()
783
+
784
+ import openrunner
785
+
786
+ run = openrunner.init(
787
+ project=project,
788
+ name=f"link-commit-{name}",
789
+ save_code=False,
790
+ )
791
+ if run is None:
792
+ click.echo("Error: failed to initialize run.", err=True)
793
+ raise SystemExit(1)
794
+ try:
795
+ client = _get_client()
796
+ git_ref = {"repo": repo_, "commit": commit}
797
+ if subdir:
798
+ git_ref["path"] = subdir
799
+ if branch:
800
+ git_ref["branch"] = branch
801
+ result = client.create_artifact_version(
802
+ run.id, name, "code", [],
803
+ metadata={"git_ref": git_ref},
804
+ message=message or f"git ref {commit[:12]}",
805
+ )
806
+ if not result:
807
+ click.echo("Error: create_artifact_version failed.", err=True)
808
+ client.close()
809
+ raise SystemExit(1)
810
+ client.confirm_artifact_version(result["version_id"])
811
+ client.close()
812
+ finally:
813
+ openrunner.finish()
814
+ url = f"https://github.com/{repo_}/tree/{commit}"
815
+ if subdir:
816
+ url += f"/{subdir}"
817
+ click.echo(
818
+ f"'{name}' v{result.get('version', '?')} -> {repo_}@{commit[:12]}"
819
+ + (f" path {subdir}" if subdir else "")
820
+ )
821
+ click.echo(url)
822
+
823
+
658
824
  @artifact.command("list")
659
825
  @click.argument("project", required=False)
660
826
  @click.option("--project", "-p", "project_opt", default=None, help="Project name")
@@ -194,14 +194,27 @@ class DatasetsClient:
194
194
  or _g.get("base_url") or "http://localhost:8000")
195
195
  if not key:
196
196
  raise ValueError("API key required. Set OPENRUNNER_API_KEY, pass api_key=, or run `openrunner login`.")
197
+ # Raw creds kept so the optional native core (Rust) can be built lazily —
198
+ # the crate appends its own /api/v1, so hand it the bare base URL.
199
+ self._base_url = url.rstrip("/")
200
+ self._api_key = key
201
+ self._native: Any = "unset" # sentinel: native core not yet resolved
197
202
  from ._retry_transport import RetryTransport
198
203
  self._http = httpx.Client(
199
- base_url=f"{url.rstrip('/')}/api/v1",
200
- headers={"Authorization": f"Bearer {key}"},
204
+ base_url=f"{self._base_url}/api/v1",
205
+ headers={"Authorization": f"Bearer {self._api_key}"},
201
206
  timeout=timeout,
202
207
  transport=RetryTransport(),
203
208
  )
204
209
 
210
+ def _native_client(self):
211
+ """The native (Rust/PyO3) client if the ``openrunner-core`` wheel is
212
+ installed, else ``None`` — an accelerator with a pure-Python fallback."""
213
+ if self._native == "unset":
214
+ from openrunner import _native
215
+ self._native = _native.native_client(self._base_url, self._api_key)
216
+ return self._native
217
+
205
218
  # -- resolution ----------------------------------------------------------
206
219
  def _org_id(self, org: str) -> str:
207
220
  r = self._http.get("/organizations")
@@ -801,6 +814,70 @@ class DatasetsClient:
801
814
  _check(r)
802
815
  return DatasetInfo._from_json(r.json())
803
816
 
817
+ def add_column(self, repo_id: str, name: str, *, default: str | None = None) -> list[str]:
818
+ """Add an editable column to an indexed dataset.
819
+
820
+ The dataset's indexed source ("parent") columns stay **read-only**: the new
821
+ column lives in a separate overlay table keyed on the primary key
822
+ (``debug_id``), so the source data is never mutated. Works on a plain
823
+ indexed dataset or a query-derived one — ``index_dataset`` the dataset first
824
+ if it isn't already. A name that collides with a read-only source column is
825
+ rejected. Returns the editable column names.
826
+ """
827
+ ds = self._resolve(repo_id)
828
+ nat = self._native_client()
829
+ if nat is not None:
830
+ return nat.add_dataset_column(str(ds.id), name, default)
831
+ r = self._http.post(f"/datasets/{ds.id}/columns",
832
+ json={"name": name, "default": default})
833
+ _check(r)
834
+ return r.json().get("columns", [])
835
+
836
+ def set_cell(self, repo_id: str, debug_id: str, column: str,
837
+ value: str | None = None) -> dict:
838
+ """Set ONE editable cell (creates the column on first write).
839
+
840
+ ``debug_id`` is the row's primary-key value; ``value=None`` clears it. A
841
+ read-only source column (or the primary key) is rejected. Returns
842
+ ``{ok, debug_id, column}``.
843
+ """
844
+ ds = self._resolve(repo_id)
845
+ nat = self._native_client()
846
+ if nat is not None:
847
+ return nat.set_dataset_cell(str(ds.id), debug_id, column, value)
848
+ r = self._http.patch(f"/datasets/{ds.id}/cell",
849
+ json={"debug_id": debug_id, "column": column, "value": value})
850
+ _check(r)
851
+ return r.json()
852
+
853
+ def edit_row(self, repo_id: str, debug_id: str, values: dict[str, str | None]) -> dict:
854
+ """Edit one row — set several editable columns at once.
855
+
856
+ ``values`` maps column -> value (``None`` clears). Only user-added overlay /
857
+ derived columns are writable; the whole edit is rejected (400) if any key is
858
+ a read-only source column. Returns ``{ok, debug_id, columns}``.
859
+ """
860
+ ds = self._resolve(repo_id)
861
+ nat = self._native_client()
862
+ if nat is not None:
863
+ return nat.edit_dataset_row(str(ds.id), debug_id, values)
864
+ r = self._http.patch(f"/datasets/{ds.id}/rows/{debug_id}", json={"values": values})
865
+ _check(r)
866
+ return r.json()
867
+
868
+ def get_row(self, repo_id: str, debug_id: str) -> dict:
869
+ """Fetch one full row by ``debug_id``: read-only source fields with any
870
+ editable overlay values merged on top. Returns
871
+ ``{debug_id, row, editable_columns}`` (everything in ``row`` not listed in
872
+ ``editable_columns`` is a read-only parent column)."""
873
+ ds = self._resolve(repo_id)
874
+ nat = self._native_client()
875
+ if nat is not None:
876
+ return nat.get_dataset_row(str(ds.id), debug_id)
877
+ r = self._http.get(f"/datasets/{ds.id}/row", params={"debug_id": debug_id})
878
+ _check(r)
879
+ return r.json()
880
+
804
881
  def delete_dataset(self, repo_id: str) -> None:
805
882
  ds = self._resolve(repo_id)
806
883
  self._http.delete(f"/datasets/{ds.id}")
@@ -1003,6 +1080,28 @@ def add_derivation(repo_id: str, parent_repo_id: str) -> "DatasetInfo":
1003
1080
  return _c().add_derivation(repo_id, parent_repo_id)
1004
1081
 
1005
1082
 
1083
+ def add_column(repo_id: str, name: str, *, default: str | None = None) -> list[str]:
1084
+ """Add an editable overlay column to an indexed dataset (source columns stay
1085
+ read-only). Module-level mirror of DatasetsClient.add_column."""
1086
+ return _c().add_column(repo_id, name, default=default)
1087
+
1088
+
1089
+ def set_cell(repo_id: str, debug_id: str, column: str, value: str | None = None) -> dict:
1090
+ """Set one editable cell of a dataset row (creates the column if new)."""
1091
+ return _c().set_cell(repo_id, debug_id, column, value)
1092
+
1093
+
1094
+ def edit_row(repo_id: str, debug_id: str, values: dict[str, str | None]) -> dict:
1095
+ """Edit one row — set several editable columns at once (read-only source
1096
+ columns are rejected)."""
1097
+ return _c().edit_row(repo_id, debug_id, values)
1098
+
1099
+
1100
+ def get_row(repo_id: str, debug_id: str) -> dict:
1101
+ """Fetch one full row by debug_id (source fields + editable overlay values)."""
1102
+ return _c().get_row(repo_id, debug_id)
1103
+
1104
+
1006
1105
  def presign_batch(repo_id: str, paths: list[str], **kwargs: Any) -> list[dict]:
1007
1106
  return _c().presign_batch(repo_id, paths, **kwargs)
1008
1107
 
@@ -2188,6 +2188,26 @@ python3 -c "from openrunner import datasets as ds; ds.add_derivation('ORG/CHILD'
2188
2188
  ### 5. Card + verify lineage
2189
2189
  Write a short card explaining the heuristic + thresholds + per-language stats, then `ds.set_dataset_card('ORG/CHILD', md)`. Verify: `GET /datasets/{{id}}/lineage` (or the dataset page Lineage tab) now shows CHILD under the parent's children, and the parent can no longer be deleted without removing the child first.
2190
2190
 
2191
+ ## Alternative: add an editable column IN PLACE (no new dataset)
2192
+
2193
+ When you don't need a separate child dataset — you just want to **annotate / label / correct** rows of an existing **indexed** dataset — add an editable column directly. The dataset's indexed source columns stay **read-only** ("parent" columns); your new column lives in a separate overlay table keyed on `debug_id`, so the source data is never touched. This is the right tool for human review, GT correction, or attaching a quality flag you edit over time.
2194
+
2195
+ ```bash
2196
+ python3 -c "
2197
+ from openrunner import datasets as ds
2198
+ # 1. Add the editable column (index the dataset first if it isn't: ds.index_dataset('ORG/NAME', text_columns=[...]))
2199
+ ds.add_column('ORG/NAME', 'review_status', default='pending') # -> editable column names
2200
+ # 2. Edit one cell of a row (debug_id = the row's primary key)
2201
+ ds.set_cell('ORG/NAME', 'ROW_DEBUG_ID', 'review_status', 'approved')
2202
+ # 3. Or edit several editable columns of a row at once
2203
+ ds.edit_row('ORG/NAME', 'ROW_DEBUG_ID', {{'review_status': 'rejected', 'note': 'misaligned'}})
2204
+ # 4. Read the row back (source fields + your overlay values merged)
2205
+ row = ds.get_row('ORG/NAME', 'ROW_DEBUG_ID'); print(row['editable_columns'], row['row'])
2206
+ "
2207
+ ```
2208
+
2209
+ Rules: writing a read-only **source** column (or the primary key) is rejected — only user-added columns are editable. `edit_row` validates every column up front and rejects the whole edit if any is read-only. Use this for in-place edits; use the derive flow above when you want a distinct, lineage-linked child dataset.
2210
+
2191
2211
  ## Common mistakes
2192
2212
  - ❌ `openrunner.Artifact(...).add_file(parquet); run.log_artifact(a)` — that's a run artifact, **not** a dataset; no lineage, not queryable.
2193
2213
  - ❌ Re-uploading the parent's audio into the child — derived datasets reference parent media.