openrunner-sdk 2.41.5__tar.gz → 2.41.7__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 (231) hide show
  1. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/PKG-INFO +1 -1
  2. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/CLAUDE.md +44 -1
  3. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/__init__.py +10 -0
  4. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/_native.py +2 -2
  5. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/cli.py +38 -6
  6. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/datasets.py +73 -3
  7. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/gpu/__init__.py +55 -18
  8. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/install_commands.py +92 -3
  9. openrunner_sdk-2.41.7/openrunner/llms-full.txt +258 -0
  10. openrunner_sdk-2.41.7/openrunner/llms.py +49 -0
  11. openrunner_sdk-2.41.7/openrunner/llms.txt +42 -0
  12. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/sweep.py +31 -27
  13. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/pyproject.toml +7 -1
  14. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_datasets_module.py +70 -1
  15. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/test_gpu_cluster.py +46 -8
  16. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/test_sdk_gpu_api.py +20 -3
  17. openrunner_sdk-2.41.7/tests/test_llms.py +43 -0
  18. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_sweep.py +55 -2
  19. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/.gitignore +0 -0
  20. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/=6.0 +0 -0
  21. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/=8.1 +0 -0
  22. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/README.md +0 -0
  23. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/_retry_transport.py +0 -0
  24. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/_safepath.py +0 -0
  25. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/_secret_rules.py +0 -0
  26. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/api_client.py +0 -0
  27. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/artifact.py +0 -0
  28. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/autoresearch.py +0 -0
  29. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/buffer.py +0 -0
  30. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/cache.py +0 -0
  31. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/config.py +0 -0
  32. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/cost.py +0 -0
  33. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/dataset.py +0 -0
  34. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/environment.py +0 -0
  35. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/evaluation.py +0 -0
  36. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/feedback.py +0 -0
  37. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/git_info.py +0 -0
  38. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/gpu/api_integration.py +0 -0
  39. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/gpu/cost_guard.py +0 -0
  40. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/gpu/gpu_types.py +0 -0
  41. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/gpu/price_compare.py +0 -0
  42. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/gpu/provider_base.py +0 -0
  43. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/gpu/providers/__init__.py +0 -0
  44. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/gpu/providers/forge_provider.py +0 -0
  45. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/gpu/providers/lambda_cloud.py +0 -0
  46. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/gpu/providers/modal_provider.py +0 -0
  47. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/gpu/providers/ovhcloud.py +0 -0
  48. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/gpu/providers/runpod.py +0 -0
  49. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/gpu/providers/vastai.py +0 -0
  50. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/gpu/runner.py +0 -0
  51. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/gpu/workspace_sync.py +0 -0
  52. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/guardrails.py +0 -0
  53. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/handover.py +0 -0
  54. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/hooks/patent-lab-post-commit +0 -0
  55. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/__init__.py +0 -0
  56. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/accelerate.py +0 -0
  57. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/anthropic_tracer.py +0 -0
  58. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/catboost.py +0 -0
  59. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/diffusers.py +0 -0
  60. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/fastai.py +0 -0
  61. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/forced_alignment.py +0 -0
  62. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/gladia.py +0 -0
  63. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/gymnasium.py +0 -0
  64. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/huggingface.py +0 -0
  65. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/hydra.py +0 -0
  66. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/ignite.py +0 -0
  67. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/jax.py +0 -0
  68. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/keras.py +0 -0
  69. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/langchain.py +0 -0
  70. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/lightgbm.py +0 -0
  71. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/lightning.py +0 -0
  72. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/llamaindex.py +0 -0
  73. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/openai_finetune.py +0 -0
  74. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/openai_tracer.py +0 -0
  75. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/optuna.py +0 -0
  76. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/pytorch.py +0 -0
  77. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/sb3.py +0 -0
  78. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/sklearn.py +0 -0
  79. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/skypilot.py +0 -0
  80. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/tensorflow.py +0 -0
  81. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/trl.py +0 -0
  82. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/tts.py +0 -0
  83. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/ultralytics.py +0 -0
  84. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/voice_agent.py +0 -0
  85. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/whisper.py +0 -0
  86. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/integration/xgboost.py +0 -0
  87. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/launch.py +0 -0
  88. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/machine.py +0 -0
  89. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/mcp_server.py +0 -0
  90. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/media.py +0 -0
  91. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/migrate.py +0 -0
  92. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/model.py +0 -0
  93. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/model_card.py +0 -0
  94. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/offline.py +0 -0
  95. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/patent_scan.py +0 -0
  96. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/pii.py +0 -0
  97. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/plot.py +0 -0
  98. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/projects.py +0 -0
  99. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/prompt.py +0 -0
  100. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/query_api.py +0 -0
  101. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/redact.py +0 -0
  102. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/reload.py +0 -0
  103. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/research.py +0 -0
  104. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/research_policy.py +0 -0
  105. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/run.py +0 -0
  106. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/scorers.py +0 -0
  107. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/sender.py +0 -0
  108. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/session.py +0 -0
  109. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/settings.py +0 -0
  110. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/summary.py +0 -0
  111. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/sync.py +0 -0
  112. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/system_metrics.py +0 -0
  113. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/tensorboard.py +0 -0
  114. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/tools/__init__.py +0 -0
  115. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/tools/patent_report.py +0 -0
  116. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/trace.py +0 -0
  117. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/transcript_formatter.py +0 -0
  118. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/wal.py +0 -0
  119. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/wandb_compat/__init__.py +0 -0
  120. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/wandb_compat/_shim.py +0 -0
  121. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/openrunner/wer.py +0 -0
  122. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/scripts/gen_betterleaks_rules.py +0 -0
  123. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/__init__.py +0 -0
  124. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/conftest.py +0 -0
  125. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-aaa/config.json +0 -0
  126. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-aaa/metadata.json +0 -0
  127. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-aaa/metrics.jsonl +0 -0
  128. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-aaa/summary.json +0 -0
  129. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/model-checkpoint/manifest.json +0 -0
  130. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/model-checkpoint/weights.bin +0 -0
  131. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/sample-image/manifest.json +0 -0
  132. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/sample-image/preview.png +0 -0
  133. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-bbb/config.json +0 -0
  134. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-bbb/metadata.json +0 -0
  135. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-bbb/metrics.jsonl +0 -0
  136. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-bbb/summary.json +0 -0
  137. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/integration/__init__.py +0 -0
  138. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/integration/conftest.py +0 -0
  139. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/integration/test_artifact_round_trip.py +0 -0
  140. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_alert.py +0 -0
  141. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_aliases.py +0 -0
  142. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_api_client.py +0 -0
  143. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_api_client_methods.py +0 -0
  144. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_api_client_transport.py +0 -0
  145. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_artifact.py +0 -0
  146. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_buffer.py +0 -0
  147. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_cache.py +0 -0
  148. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_class_scorers.py +0 -0
  149. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_cli.py +0 -0
  150. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_cli_artifact_sweep.py +0 -0
  151. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_cli_datasets.py +0 -0
  152. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_cli_gpu_registry_deploy.py +0 -0
  153. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_cli_infra.py +0 -0
  154. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_cli_remainder.py +0 -0
  155. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_cli_topup.py +0 -0
  156. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_config.py +0 -0
  157. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_evaluation.py +0 -0
  158. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_finish.py +0 -0
  159. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_git_info.py +0 -0
  160. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/__init__.py +0 -0
  161. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/conftest.py +0 -0
  162. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/test_cost_guard.py +0 -0
  163. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/test_engine_serverside.py +0 -0
  164. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/test_forge.py +0 -0
  165. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/test_governed_jobs.py +0 -0
  166. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/test_gpu_types.py +0 -0
  167. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/test_lambda.py +0 -0
  168. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/test_modal.py +0 -0
  169. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/test_ovh_training.py +0 -0
  170. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/test_ovhcloud.py +0 -0
  171. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/test_price_compare.py +0 -0
  172. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/test_provider_base.py +0 -0
  173. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/test_runner.py +0 -0
  174. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/test_runpod.py +0 -0
  175. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/test_vastai.py +0 -0
  176. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu/test_workspace_sync.py +0 -0
  177. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_gpu_modal_creds.py +0 -0
  178. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_init.py +0 -0
  179. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_init_facade.py +0 -0
  180. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_install_commands.py +0 -0
  181. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_integration_fastai.py +0 -0
  182. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_integration_huggingface.py +0 -0
  183. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_integration_keras.py +0 -0
  184. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_integration_langchain.py +0 -0
  185. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_integration_lightning.py +0 -0
  186. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_integration_pytorch.py +0 -0
  187. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_integration_setA.py +0 -0
  188. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_integration_setB.py +0 -0
  189. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_integration_setC.py +0 -0
  190. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_integration_sklearn.py +0 -0
  191. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_integration_xgboost.py +0 -0
  192. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_launch.py +0 -0
  193. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_log.py +0 -0
  194. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_log_code.py +0 -0
  195. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_machine_migrate.py +0 -0
  196. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_mcp_decisions.py +0 -0
  197. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_mcp_monitor.py +0 -0
  198. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_mcp_predictions.py +0 -0
  199. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_mcp_server.py +0 -0
  200. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_media.py +0 -0
  201. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_media_extra.py +0 -0
  202. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_migrate.py +0 -0
  203. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_offline.py +0 -0
  204. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_offline_sync.py +0 -0
  205. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_patent_scan.py +0 -0
  206. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_pii.py +0 -0
  207. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_plot.py +0 -0
  208. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_plot_media_extra2.py +0 -0
  209. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_prompt_module.py +0 -0
  210. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_pure_modules.py +0 -0
  211. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_query_api.py +0 -0
  212. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_redact.py +0 -0
  213. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_reload_module.py +0 -0
  214. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_research_module.py +0 -0
  215. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_resume.py +0 -0
  216. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_retry_transport.py +0 -0
  217. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_run_module.py +0 -0
  218. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_sdk_cluster_j1.py +0 -0
  219. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_sdk_cluster_j2.py +0 -0
  220. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_sdk_cluster_j3.py +0 -0
  221. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_sdk_cluster_j4.py +0 -0
  222. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_sdk_core_edges.py +0 -0
  223. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_sdk_features.py +0 -0
  224. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_sender.py +0 -0
  225. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_session_module.py +0 -0
  226. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_session_sync.py +0 -0
  227. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_summary.py +0 -0
  228. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_system_metrics.py +0 -0
  229. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_topup_hi.py +0 -0
  230. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/tests/test_trace.py +0 -0
  231. {openrunner_sdk-2.41.5 → openrunner_sdk-2.41.7}/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.5
3
+ Version: 2.41.7
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
@@ -96,10 +96,11 @@ falling back to pure-Python httpx otherwise:
96
96
  ord.search_dataset(repo, q, mode="mixed"|"exact", lang="fr") # BM25 / phrase, lang filter
97
97
  ord.optimize_embedding_search(repo) # build ANN index (once)
98
98
  ord.semantic_search(repo, q, lang="en,fr") # vector (cosine) search
99
+ ord.regex_search(repo, r"\bcancel(led)?\b", lang="en") # Python-re over the text column (bounded)
99
100
  ord.word_lookup(repo, "medical,doctor", op="union"|"intersect", lang="fr") # lexical_map
100
101
  ord.keyword_expand("medical terms in french", count=300) # LLM -> {keywords, csv, count}
101
102
 
102
- # derive: mode = word (op union/intersect) | fts | exact | semantic; lang filter; append via target_repo_id
103
+ # derive: mode = word (op union/intersect) | fts | exact | regex | semantic; lang filter; append via target_repo_id
103
104
  child = ord.derive_by_query(parent, keywords_csv, name="org/medical-fr",
104
105
  mode="word", lang="fr", limit=5000)
105
106
  ord.derive_by_query(parent, "more,terms", target_repo_id="org/medical-fr", mode="word") # append, de-dup
@@ -175,3 +176,45 @@ When the user asks about papers, research, or sessions:
175
176
  - Use `mcp__openrunner__openrunner_search_research` for research plans
176
177
  - Use `mcp__openrunner__openrunner_search_sessions` for AI session history
177
178
  - Use `mcp__openrunner__openrunner_update_paper` to modify paper sections
179
+
180
+ ## Container registry & serverless deployment
181
+
182
+ Register a container registry (Docker Hub, GHCR, Google Artifact Registry, AWS
183
+ ECR, Azure ACR, OVH/Harbor) once; the upstream credential is stored encrypted
184
+ server-side and the OpenRunner **broker** proxies pull/push — the client never
185
+ holds the upstream key.
186
+
187
+ ```
188
+ openrunner registry add --name "Harbor ML" --url REG_URL --username U --token T --namespace NS
189
+ openrunner registry set-default <id> # broker fallback when the repo path is ambiguous
190
+ openrunner registry login # docker login the broker (short-lived token)
191
+ docker push <registry_host>/<namespace>/<project-slug>:<user>-<name>-<tag>
192
+ ```
193
+
194
+ Repo convention (`canonical_image_name`): `{namespace}/{project_slug}`, tag leads
195
+ with the user — e.g. `model-team/ner-email:jl-int8-v1`.
196
+
197
+ **Images link to a project = the deploy gate.** An image pushed **through the
198
+ broker** is **auto-linked** to its project on the manifest PUT (project resolved
199
+ from the repo's project-slug segment), so it appears under the project's
200
+ **Registry** tab (artifact page) and the project-overview **Registry** panel with
201
+ no manual step. Direct-to-upstream pushes (or registries that hide their catalog,
202
+ e.g. OVH Harbor robots can't list `_catalog`) must be linked explicitly:
203
+
204
+ ```
205
+ openrunner deploy link IMAGE -p ORG/NAME [--registry-id UUID]
206
+ openrunner deploy images -p ORG/NAME # list linked images
207
+ openrunner deploy gpus -p ORG/NAME # deployable GPU offers
208
+ openrunner deploy create ... ; openrunner deploy scale <id> N ; openrunner deploy call <id> ...
209
+ ```
210
+ API: `GET/POST /projects/{id}/images`, `.../deployments`. SDK:
211
+ `client.list_project_images(pid)`, `client.link_project_image(pid, image, registry_id=)`.
212
+ Slash: `/openrunner:registry`, `/openrunner:deploy`, `/openrunner:build-api`.
213
+
214
+ ## Full LLM map: llms.txt
215
+
216
+ The complete end-to-end, LLM-oriented map of OpenRunner (dataset → run
217
+ instrumentation → GPU training → sweep → registry → deploy → handover) lives at
218
+ **https://openrun.gladia.io/llms-full.txt** (source: `src/web/public/llms-full.txt`),
219
+ with a short index at **/llms.txt**. Read the full doc to ground yourself in the
220
+ real commands + conventions before driving OpenRunner end-to-end for a new project.
@@ -107,15 +107,25 @@ from openrunner.datasets import (
107
107
  optimize_embedding_search,
108
108
  push_dataset,
109
109
  search_dataset,
110
+ regex_search,
110
111
  semantic_search,
111
112
  word_lookup,
112
113
  set_dataset_card,
113
114
  set_storage_policy,
115
+ update_dataset,
114
116
  upload_file,
115
117
  upload_sharded,
116
118
  upload_folder,
117
119
  )
118
120
  from openrunner.config import Config
121
+ from openrunner.llms import (
122
+ llms_txt,
123
+ llms_txt_path,
124
+ LLMS_TXT_URL,
125
+ LLMS_FULL_TXT_URL,
126
+ LLMS_TXT_PATH,
127
+ LLMS_FULL_TXT_PATH,
128
+ )
119
129
  from openrunner.model import Model
120
130
  from openrunner.prompt import MessagesPrompt, Prompt, StringPrompt
121
131
  from openrunner.launch import LaunchJob, launch, from_run as _launch_from_run
@@ -11,8 +11,8 @@ Covered surface today: projects (incl. linked repo), artifacts/versions,
11
11
  presigned downloads (selective, GIL released during transfers), container
12
12
  registries + broker login tokens, dataset editable-overlay columns
13
13
  (add column / set cell / edit row / get row — source columns stay read-only),
14
- and dataset search + query-derivation (full-text / semantic / word union·intersect,
15
- LLM keyword-expand, derive-by-query + derived rows).
14
+ and dataset search + query-derivation (full-text / semantic / regex / word
15
+ union·intersect, LLM keyword-expand, derive-by-query + derived rows).
16
16
  """
17
17
 
18
18
  from __future__ import annotations
@@ -863,8 +863,11 @@ def artifact_list(project: str | None, project_opt: str | None) -> None:
863
863
 
864
864
  @artifact.command("get")
865
865
  @click.argument("name")
866
- @click.option("--root", "-r", default=".", help="Download directory (default: .)")
867
- def artifact_get(name: str, root: str) -> None:
866
+ @click.option("--root", "-r", "--output", "-o", "root", default=".",
867
+ help="Download directory (default: .). --output/-o is an alias.")
868
+ @click.option("--project", "-p", default=None,
869
+ help="Scope the lookup to ORG/PROJECT (else searches all accessible projects)")
870
+ def artifact_get(name: str, root: str, project: str | None = None) -> None:
868
871
  """Download an artifact to a local directory.
869
872
 
870
873
  NAME can use colon syntax: my-dataset:latest or my-model:v3.
@@ -886,8 +889,16 @@ def artifact_get(name: str, root: str) -> None:
886
889
  else:
887
890
  alias = ref
888
891
 
889
- # Find artifact by listing all accessible projects
892
+ # Find artifact by listing accessible projects. When --project is given we
893
+ # only search that one (faster, and disambiguates a name shared across
894
+ # projects); otherwise fall back to scanning every accessible project.
890
895
  projects = client.list_projects()
896
+ if project:
897
+ projects = [
898
+ p for p in projects
899
+ if project in (p.get("id"), p.get("name"), p.get("slug"),
900
+ f"{p.get('org_slug')}/{p.get('slug')}")
901
+ ] or projects
891
902
  artifact_detail = None
892
903
  target_version_id = None
893
904
 
@@ -1135,9 +1146,12 @@ def cache_cleanup(max_size: float | None) -> None:
1135
1146
 
1136
1147
  @main.command("download-artifact")
1137
1148
  @click.argument("name")
1138
- @click.option("--root", "-r", default=".", help="Download directory (default: .)")
1149
+ @click.option("--root", "-r", "--output", "-o", "root", default=".",
1150
+ help="Download directory (default: .). --output/-o is an alias.")
1151
+ @click.option("--project", "-p", default=None,
1152
+ help="Scope the lookup to ORG/PROJECT (else searches all accessible projects)")
1139
1153
  @click.pass_context
1140
- def download_artifact_cmd(ctx: click.Context, name: str, root: str) -> None:
1154
+ def download_artifact_cmd(ctx: click.Context, name: str, root: str, project: str | None) -> None:
1141
1155
  """Download an artifact to a local directory.
1142
1156
 
1143
1157
  NAME can use colon syntax: my-dataset:latest or my-model:v3.
@@ -1145,7 +1159,7 @@ def download_artifact_cmd(ctx: click.Context, name: str, root: str) -> None:
1145
1159
 
1146
1160
  This is a top-level alias for `openrunner artifact get`.
1147
1161
  """
1148
- ctx.invoke(artifact_get, name=name, root=root)
1162
+ ctx.invoke(artifact_get, name=name, root=root, project=project)
1149
1163
 
1150
1164
 
1151
1165
  @main.command("upload-artifact")
@@ -3221,6 +3235,24 @@ def version_cmd(check: bool) -> None:
3221
3235
  click.echo("Up to date.")
3222
3236
 
3223
3237
 
3238
+ @main.command("llms")
3239
+ @click.option("--full", is_flag=True, help="Print the complete guide (llms-full.txt) instead of the index")
3240
+ @click.option("--path", "show_path", is_flag=True, help="Print the bundled file path instead of its content")
3241
+ def llms_cmd(full: bool, show_path: bool) -> None:
3242
+ """Print the bundled LLM-oriented OpenRunner doc.
3243
+
3244
+ The short index by default, or the complete end-to-end guide with --full.
3245
+ Bundled in the package (works offline); also served at /llms.txt and
3246
+ /llms-full.txt on the instance.
3247
+ """
3248
+ from openrunner import llms as _llms
3249
+
3250
+ if show_path:
3251
+ click.echo(_llms.llms_txt_path(full=full))
3252
+ else:
3253
+ click.echo(_llms.llms_txt(full=full))
3254
+
3255
+
3224
3256
  @main.command("_update_nudge", hidden=True)
3225
3257
  def update_nudge_cmd() -> None:
3226
3258
  """Print ONE update-hint line if a newer SDK exists, else nothing. Cheap:
@@ -453,11 +453,24 @@ class DatasetsClient:
453
453
  complete — so the dataset's file count grows live and an interrupted run
454
454
  is genuinely resumable (re-run with ``skip_existing`` to continue). The
455
455
  API stays out of the byte path, so throughput is bandwidth-bound.
456
+
457
+ ``skip_existing`` skips a file only when the remote copy has the SAME
458
+ path AND the SAME content (sha256). A file whose bytes changed since the
459
+ last upload is re-uploaded — a pure path match would silently drop
460
+ edits, making a dataset update look successful while sending nothing.
456
461
  """
462
+ import hashlib
457
463
  import os
458
464
  import sys
459
465
  from concurrent.futures import ThreadPoolExecutor, as_completed
460
466
 
467
+ def _sha256(fp: str) -> str:
468
+ h = hashlib.sha256()
469
+ with open(fp, "rb") as fh:
470
+ for chunk in iter(lambda: fh.read(1024 * 1024), b""):
471
+ h.update(chunk)
472
+ return h.hexdigest()
473
+
461
474
  ds = self._resolve(repo_id)
462
475
  items: list[tuple[str, str]] = []
463
476
  for root, _dirs, names in os.walk(folder):
@@ -469,8 +482,16 @@ class DatasetsClient:
469
482
  items.append((full, rel))
470
483
  if skip_existing:
471
484
  try:
472
- have = {f["path"] for f in self.list_files(repo_id)}
473
- items = [it for it in items if it[1] not in have]
485
+ # Map remote path -> sha256. Skip a local file only when both the
486
+ # path and the content hash match; changed files are re-uploaded.
487
+ remote = {f["path"]: (f.get("sha256") or "") for f in self.list_files(repo_id)}
488
+ kept: list[tuple[str, str]] = []
489
+ for full, rel in items:
490
+ rsha = remote.get(rel)
491
+ if rsha and rsha == _sha256(full):
492
+ continue
493
+ kept.append((full, rel))
494
+ items = kept
474
495
  except Exception:
475
496
  pass
476
497
  if not items:
@@ -794,6 +815,25 @@ class DatasetsClient:
794
815
  _check(r)
795
816
  return r.json().get("rows", r.json().get("hits", []))
796
817
 
818
+ def regex_search(
819
+ self, repo_id: str, pattern: str, *, limit: int = 20, lang: str = ""
820
+ ) -> list[dict]:
821
+ """Regex search: match a Python-``re`` ``pattern`` against the dataset's
822
+ text column (streamed, bounded — no index needed). Case-insensitive is
823
+ opt-in via an inline ``(?i)`` flag. Native core when available, else httpx.
824
+ Pairs with ``derive_by_query(mode="regex")`` to build a regex-derived set.
825
+ """
826
+ ds = self._resolve(repo_id)
827
+ nat = self._native_client()
828
+ if nat is not None:
829
+ return nat.regex_search(str(ds.id), pattern, lang, limit).get("rows", [])
830
+ params: dict[str, Any] = {"q": pattern, "limit": limit, "mode": "regex"}
831
+ if lang:
832
+ params["lang"] = lang
833
+ r = self._http.get(f"/datasets/{ds.id}/search", params=params)
834
+ _check(r)
835
+ return r.json().get("rows", [])
836
+
797
837
  def optimize_embedding_search(
798
838
  self,
799
839
  repo_id: str,
@@ -869,7 +909,8 @@ class DatasetsClient:
869
909
  """Create (or append to) a dataset **derived from a query** over ``repo_id``.
870
910
 
871
911
  ``mode``: "word" (``q`` = term / CSV; pairs with LLM ``keyword_expand``),
872
- "fts" (BM25), "exact" (phrase), "semantic" (vector — needs
912
+ "fts" (BM25), "exact" (phrase), "regex" (``q`` = a Python-``re`` pattern
913
+ matched over the text column, bounded), "semantic" (vector — needs
873
914
  ``optimize_embedding_search``). ``lang`` filters by language / CSV. Pass
874
915
  ``name`` to create, or ``target_repo_id`` to append (must derive from the
875
916
  same parent). Lineage is recorded automatically. The query runs and
@@ -988,6 +1029,24 @@ class DatasetsClient:
988
1029
  _check(r)
989
1030
  return r.json().get("columns", [])
990
1031
 
1032
+ def delete_column(self, repo_id: str, name: str) -> list[str]:
1033
+ """Delete an editable column from an indexed dataset.
1034
+
1035
+ Miroir exact de :meth:`add_column` — retire la colonne overlay (ou de la
1036
+ members table d'un dérivé) ``name`` ; les colonnes source indexées
1037
+ ("parent") restent **en lecture seule** et ne peuvent pas être supprimées.
1038
+
1039
+ Exact mirror of :meth:`add_column`: removes the overlay (or derived-member)
1040
+ column ``name`` — the indexed source ("parent") columns stay **read-only**
1041
+ and cannot be deleted. A read-only source column (or the primary key) is
1042
+ rejected (400); an unknown column returns 404. Returns the remaining
1043
+ editable column names.
1044
+ """
1045
+ ds = self._resolve(repo_id)
1046
+ r = self._http.delete(f"/datasets/{ds.id}/columns/{name}")
1047
+ _check(r)
1048
+ return r.json().get("columns", [])
1049
+
991
1050
  def set_cell(self, repo_id: str, debug_id: str, column: str,
992
1051
  value: str | None = None) -> dict:
993
1052
  """Set ONE editable cell (creates the column on first write).
@@ -1241,6 +1300,12 @@ def add_column(repo_id: str, name: str, *, default: str | None = None) -> list[s
1241
1300
  return _c().add_column(repo_id, name, default=default)
1242
1301
 
1243
1302
 
1303
+ def delete_column(repo_id: str, name: str) -> list[str]:
1304
+ """Delete an editable column from an indexed dataset (source columns stay
1305
+ read-only). Module-level mirror of DatasetsClient.delete_column."""
1306
+ return _c().delete_column(repo_id, name)
1307
+
1308
+
1244
1309
  def set_cell(repo_id: str, debug_id: str, column: str, value: str | None = None) -> dict:
1245
1310
  """Set one editable cell of a dataset row (creates the column if new)."""
1246
1311
  return _c().set_cell(repo_id, debug_id, column, value)
@@ -1292,6 +1357,11 @@ def semantic_search(repo_id: str, query: str, **kwargs: Any) -> list[dict]:
1292
1357
  return _c().semantic_search(repo_id, query, **kwargs)
1293
1358
 
1294
1359
 
1360
+ def regex_search(repo_id: str, pattern: str, **kwargs: Any) -> list[dict]:
1361
+ """Regex search — Python-``re`` ``pattern`` over the text column (bounded)."""
1362
+ return _c().regex_search(repo_id, pattern, **kwargs)
1363
+
1364
+
1295
1365
  def optimize_embedding_search(repo_id: str, **kwargs: Any) -> dict:
1296
1366
  """Embed rows + build the ANN index for semantic search / semantic-derive."""
1297
1367
  return _c().optimize_embedding_search(repo_id, **kwargs)
@@ -534,25 +534,65 @@ async def terminate(
534
534
  provider: str | None = None,
535
535
  api_key: str | None = None,
536
536
  ) -> bool:
537
- """Terminate a GPU instance on the provider and update the platform.
538
-
539
- 1. Looks up instance details from the backend (to find provider if
540
- not specified).
541
- 2. Terminates the instance via the provider API.
542
- 3. Updates the backend status to ``"terminated"`` via PATCH.
543
- 4. Stops the cost guard thread if one is running.
537
+ """Terminate a GPU instance.
538
+
539
+ Preferred path (governed): when no explicit ``api_key`` is passed, the
540
+ platform terminates the pod SERVER-SIDE — it decrypts the org's stored
541
+ provider key (which never leaves the API), kills the pod, VERIFIES it is
542
+ gone, and only then marks the instance ``terminated``. This is the only
543
+ path that works when the provider key lives on the platform (the common
544
+ case): a client-side terminate cannot see that key and would fail with
545
+ "No API key found for provider …".
546
+
547
+ Fallback (BYO key): when an explicit ``api_key`` is given, terminate the
548
+ pod directly via the provider API. The backend status is marked
549
+ ``terminated`` ONLY if the provider kill actually succeeded — never
550
+ unconditionally, since a falsely-"terminated" record strands a BILLING pod
551
+ that our records claim is dead.
544
552
 
545
553
  Args:
546
554
  instance_id: Platform instance ID.
547
555
  provider: Provider name (auto-detected from backend if None).
548
- api_key: Explicit provider API key.
556
+ api_key: Explicit provider API key (opts into the client-side path).
549
557
 
550
558
  Returns:
551
- True if termination succeeded.
559
+ True if termination is confirmed.
552
560
  """
553
561
  api_client = _get_api_client()
554
562
 
555
- # Resolve provider and provider_instance_id from backend
563
+ def _stop_guard() -> None:
564
+ guard_entry = _active_guards.pop(instance_id, None)
565
+ if guard_entry:
566
+ guard_entry["stop"].set()
567
+ logger.info("Cost guard stopped for instance %s", instance_id)
568
+
569
+ # Governed server-side terminate: kills + verifies with the org's stored
570
+ # key. Used whenever we aren't handed an explicit BYO key.
571
+ if api_key is None and api_client is not None:
572
+ ok = api_client.terminate_instance(instance_id)
573
+ if ok:
574
+ # The API only reports success once the pod is confirmed gone.
575
+ inst = api_client.get_instance(instance_id) or {}
576
+ confirmed = inst.get("status") == "terminated"
577
+ if not confirmed:
578
+ # Non-terminal ("terminating") — the reconciler keeps retrying;
579
+ # surface that the pod is not yet confirmed dead.
580
+ logger.warning(
581
+ "terminate %s: server accepted but pod not yet confirmed "
582
+ "gone (status=%s) — the reconciler will keep retrying",
583
+ instance_id, inst.get("status"),
584
+ )
585
+ else:
586
+ _stop_guard()
587
+ return confirmed
588
+ logger.error(
589
+ "terminate %s: governed server-side termination failed — the pod "
590
+ "may still be BILLING; check `openrunner gpu ls` / provider console",
591
+ instance_id,
592
+ )
593
+ return False
594
+
595
+ # BYO-key path: resolve provider + provider_instance_id, then kill directly.
556
596
  provider_instance_id = instance_id
557
597
  if provider is None and api_client is not None:
558
598
  instances = api_client.list_instances()
@@ -569,7 +609,6 @@ async def terminate(
569
609
  logger.error("terminate: cannot determine provider for instance %s", instance_id)
570
610
  return False
571
611
 
572
- # Get provider instance and terminate
573
612
  try:
574
613
  prov = _get_provider_instance(provider, api_key=api_key, api_client=api_client)
575
614
  result = await prov.terminate_instance(provider_instance_id)
@@ -577,18 +616,16 @@ async def terminate(
577
616
  logger.warning("terminate: provider termination failed: %s", exc)
578
617
  result = False
579
618
 
580
- # Update backend
581
- if api_client is not None:
619
+ # Only mark terminated when the provider kill actually succeeded — a
620
+ # falsely-"terminated" record strands a billing pod.
621
+ if result and api_client is not None:
582
622
  try:
583
623
  api_client.update_instance_status(instance_id, "terminated")
584
624
  except Exception as exc:
585
625
  logger.warning("terminate: backend update failed: %s", exc)
586
626
 
587
- # Stop cost guard thread
588
- guard_entry = _active_guards.pop(instance_id, None)
589
- if guard_entry:
590
- guard_entry["stop"].set()
591
- logger.info("Cost guard stopped for instance %s", instance_id)
627
+ if result:
628
+ _stop_guard()
592
629
 
593
630
  return result
594
631
 
@@ -2135,7 +2135,7 @@ Give the dataset **URL** (`<base>/datasets/<id>`), the modality/tasks/languages,
2135
2135
 
2136
2136
  DERIVE_DATASET_CMD = """---
2137
2137
  name: {prefix}derive-dataset
2138
- description: Derive a dataset from an existing one — either by a SEARCH QUERY (semantic / word union·intersect / fts / exact, with language filter + LLM keyword expansion) or by adding a computed column. Keeps lineage, never re-uploads the parent's media.
2138
+ description: Derive a dataset from an existing one — either by a SEARCH QUERY (semantic / regex / word union·intersect / fts / exact, with language filter + LLM keyword expansion) or by adding a computed column. Keeps lineage, never re-uploads the parent's media.
2139
2139
  argument-hint: "[parent org/name] [child org/name] [--query ... | --column NAME]"
2140
2140
  allowed-tools:
2141
2141
  - Bash
@@ -2152,7 +2152,7 @@ Pick by intent: **subset/select rows -> A**; **annotate/flag rows -> B**.
2152
2152
 
2153
2153
  ---
2154
2154
 
2155
- ## A. Query-derivation (semantic / word union·intersect / fts / exact + lang + LLM keywords)
2155
+ ## A. Query-derivation (semantic / regex / word union·intersect / fts / exact + lang + LLM keywords)
2156
2156
 
2157
2157
  All server-side; the SDK delegates to the native Rust core when installed. `PARENT` must be indexed (`ds.index_dataset(...)`; for `semantic`, also `ds.optimize_embedding_search(...)`).
2158
2158
 
@@ -2177,7 +2177,7 @@ print(ds.derived_rows('ORG/medical-fr', limit=10))
2177
2177
  "
2178
2178
  ```
2179
2179
 
2180
- Query modes (`mode=`): `word` (CSV of terms; `op='union'|'intersect'`), `fts` (BM25), `exact` (phrase), `semantic` (vector ANN). `lang=` takes a language or CSV ("fr" / "en,fr"); "" = all. Lineage to `PARENT` is recorded automatically by `derive_by_query`.
2180
+ Query modes (`mode=`): `word` (CSV of terms; `op='union'|'intersect'`), `fts` (BM25), `exact` (phrase), `regex` (a Python-`re` pattern over the text column, e.g. `ds.regex_search('ORG/PARENT', r'\\bcancel(led)?\\b')` / `ds.derive_by_query('ORG/PARENT', r'\\brefund', name='ORG/refunds', mode='regex')`), `semantic` (vector ANN). `lang=` takes a language or CSV ("fr" / "en,fr"); "" = all. Lineage to `PARENT` is recorded automatically by `derive_by_query`.
2181
2181
 
2182
2182
  ---
2183
2183
 
@@ -3254,6 +3254,93 @@ directly SSH-reachable** (`publicIp:22`): SSH is only via the **proxy**
3254
3254
  """
3255
3255
 
3256
3256
 
3257
+ REPORT_BUG_CMD = """---
3258
+ name: {prefix}report-bug
3259
+ description: Open a GitHub issue on the OpenRunner open-source repo for a bug you hit
3260
+ allowed-tools:
3261
+ - Bash
3262
+ - Read
3263
+ - Grep
3264
+ - AskUserQuestion
3265
+ ---
3266
+ <objective>
3267
+ File a high-quality bug report as a GitHub issue on the OpenRunner open-source
3268
+ repository when the user hits a bug (or asks to report one). Gather REAL context
3269
+ from this session, avoid duplicates, confirm before creating (an issue is public),
3270
+ and return the issue URL.
3271
+ </objective>
3272
+
3273
+ <preconditions>
3274
+ - The `gh` CLI must be installed and authenticated. Check with `gh auth status`.
3275
+ If it is not authenticated, tell the user to run it themselves -- suggest they
3276
+ type `! gh auth login` in the prompt -- then stop.
3277
+ - Target repo = the OpenRunner open-source repository. Detect it (below); if it
3278
+ cannot be resolved, fall back to `jqueguiner/openrunner`.
3279
+ </preconditions>
3280
+
3281
+ <process>
3282
+ 1. Verify gh is ready: `gh auth status`. If not, stop and ask the user to
3283
+ `! gh auth login`.
3284
+ 2. Resolve the target repo. Prefer an explicit OpenRunner repo. If the current
3285
+ directory is the OpenRunner checkout, use its origin:
3286
+ `gh repo view --json nameWithOwner -q .nameWithOwner`. Otherwise default to
3287
+ `jqueguiner/openrunner`. Store it as REPO.
3288
+ 3. Collect context (do NOT ask the user for what you can already observe):
3289
+ - SDK version: `openrunner version`.
3290
+ - OS + Python: `python3 -V` and `uname -sr`.
3291
+ - The failing command and its EXACT error output (quote verbatim), the repro
3292
+ steps, and expected vs actual behaviour -- reconstruct these from the current
3293
+ session. Use AskUserQuestion ONLY for the pieces you genuinely cannot infer.
3294
+ 4. De-dupe: search existing issues before opening a new one:
3295
+ `gh issue list --repo REPO --state all --search "<key error phrase>"`.
3296
+ If a clear match exists, show it and ask whether to comment on it
3297
+ (`gh issue comment <n> --repo REPO --body ...`) instead of opening a new one.
3298
+ 5. Draft the issue using the body template below. SHOW the full title + body to
3299
+ the user and get EXPLICIT confirmation before creating -- an issue is public
3300
+ and outward-facing. Do not create it on your own initiative.
3301
+ 6. Create it:
3302
+ `gh issue create --repo REPO --title "<title>" --body "<body>" --label bug`.
3303
+ If the `bug` label does not exist the call fails -- retry without `--label`.
3304
+ 7. Print the returned issue URL to the user.
3305
+ </process>
3306
+
3307
+ <body-template>
3308
+ Compose the issue body as markdown with these sections (fill from real data;
3309
+ write "unknown" where you truly cannot determine a value -- never invent):
3310
+
3311
+ ### Summary
3312
+ One or two sentences: what broke.
3313
+
3314
+ ### Environment
3315
+ - openrunner-sdk: <version>
3316
+ - OS / Python: <uname> / <python -V>
3317
+ - Instance base_url: <from ~/.openrunner/settings.json, host only -- no api_key>
3318
+
3319
+ ### Steps to reproduce
3320
+ 1. ...
3321
+
3322
+ ### Expected
3323
+ ...
3324
+
3325
+ ### Actual
3326
+ ... (paste the exact error, in a fenced code block)
3327
+
3328
+ ### Notes
3329
+ Anything else relevant (workaround found, related area of the codebase).
3330
+ </body-template>
3331
+
3332
+ <safety>
3333
+ - NEVER create the issue (or a comment) without explicit user confirmation. It is
3334
+ public and cannot be un-published cleanly.
3335
+ - REDACT secrets before including anything: strip api keys, tokens, passwords,
3336
+ bearer headers, `Authorization:` values, and the api_key from settings.json.
3337
+ Include only the base_url host, never credentials.
3338
+ - Quote error text verbatim (do not paraphrase the failure) -- but with secrets
3339
+ stripped.
3340
+ </safety>
3341
+ """
3342
+
3343
+
3257
3344
  FORGE_CMD = """---
3258
3345
  name: {prefix}forge
3259
3346
  description: Run or write a Python function on the Forge (Shadow-PaaS) serverless GPU fleet. Use for "run/launch/train on forge", "forge GPU", or writing an @app.function. Forge is a serverless PROVIDER (like Modal) -- NOT an ssh host or runner, so don't hunt ~/.ssh/config or `hosts ls`.
@@ -3577,6 +3664,7 @@ def install_claude_code() -> list[str]:
3577
3664
  "unregister-machine.md": UNREGISTER_MACHINE_CMD,
3578
3665
  "train.md": TRAIN_CMD,
3579
3666
  "deploy.md": DEPLOY_CMD,
3667
+ "report-bug.md": REPORT_BUG_CMD,
3580
3668
  "forge.md": FORGE_CMD,
3581
3669
  }
3582
3670
 
@@ -3812,6 +3900,7 @@ def _install_all_commands_to_dir(cmd_dir: Path, prefix: str = "openrunner:") ->
3812
3900
  "unregister-machine.md": UNREGISTER_MACHINE_CMD,
3813
3901
  "train.md": TRAIN_CMD,
3814
3902
  "deploy.md": DEPLOY_CMD,
3903
+ "report-bug.md": REPORT_BUG_CMD,
3815
3904
  "forge.md": FORGE_CMD,
3816
3905
  }
3817
3906