openrunner-sdk 2.41.2__tar.gz → 2.41.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/PKG-INFO +1 -1
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/__init__.py +1 -1
- openrunner_sdk-2.41.3/openrunner/_native.py +34 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/api_client.py +107 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/cli.py +166 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/datasets.py +74 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/install_commands.py +20 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/mcp_server.py +119 -5
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/pyproject.toml +1 -1
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_mcp_server.py +6 -1
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/.gitignore +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/=6.0 +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/=8.1 +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/README.md +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/CLAUDE.md +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/_retry_transport.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/_safepath.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/_secret_rules.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/artifact.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/autoresearch.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/buffer.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/cache.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/config.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/cost.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/dataset.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/environment.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/evaluation.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/feedback.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/git_info.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/gpu/__init__.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/gpu/api_integration.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/gpu/cost_guard.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/gpu/gpu_types.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/gpu/price_compare.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/gpu/provider_base.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/gpu/providers/__init__.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/gpu/providers/forge_provider.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/gpu/providers/lambda_cloud.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/gpu/providers/modal_provider.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/gpu/providers/ovhcloud.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/gpu/providers/runpod.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/gpu/providers/vastai.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/gpu/runner.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/gpu/workspace_sync.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/guardrails.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/handover.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/hooks/patent-lab-post-commit +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/__init__.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/accelerate.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/anthropic_tracer.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/catboost.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/diffusers.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/fastai.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/forced_alignment.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/gladia.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/gymnasium.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/huggingface.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/hydra.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/ignite.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/jax.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/keras.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/langchain.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/lightgbm.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/lightning.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/llamaindex.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/openai_finetune.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/openai_tracer.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/optuna.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/pytorch.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/sb3.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/sklearn.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/skypilot.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/tensorflow.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/trl.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/tts.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/ultralytics.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/voice_agent.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/whisper.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/integration/xgboost.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/launch.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/machine.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/media.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/migrate.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/model.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/model_card.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/offline.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/patent_scan.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/pii.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/plot.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/projects.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/prompt.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/query_api.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/redact.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/reload.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/research.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/research_policy.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/run.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/scorers.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/sender.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/session.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/settings.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/summary.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/sweep.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/sync.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/system_metrics.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/tensorboard.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/tools/__init__.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/tools/patent_report.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/trace.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/transcript_formatter.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/wal.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/wandb_compat/__init__.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/wandb_compat/_shim.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/openrunner/wer.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/scripts/gen_betterleaks_rules.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/__init__.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/conftest.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/fixtures/wandb-export-mini/run-aaa/config.json +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/fixtures/wandb-export-mini/run-aaa/metadata.json +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/fixtures/wandb-export-mini/run-aaa/metrics.jsonl +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/fixtures/wandb-export-mini/run-aaa/summary.json +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/model-checkpoint/manifest.json +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/model-checkpoint/weights.bin +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/sample-image/manifest.json +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/sample-image/preview.png +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/fixtures/wandb-export-mini/run-bbb/config.json +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/fixtures/wandb-export-mini/run-bbb/metadata.json +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/fixtures/wandb-export-mini/run-bbb/metrics.jsonl +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/fixtures/wandb-export-mini/run-bbb/summary.json +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/integration/__init__.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/integration/conftest.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/integration/test_artifact_round_trip.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_alert.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_aliases.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_api_client.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_api_client_methods.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_api_client_transport.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_artifact.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_buffer.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_cache.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_class_scorers.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_cli.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_cli_artifact_sweep.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_cli_datasets.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_cli_gpu_registry_deploy.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_cli_infra.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_cli_remainder.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_cli_topup.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_config.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_datasets_module.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_evaluation.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_finish.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_git_info.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/__init__.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/conftest.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/test_cost_guard.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/test_engine_serverside.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/test_forge.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/test_governed_jobs.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/test_gpu_cluster.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/test_gpu_types.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/test_lambda.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/test_modal.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/test_ovh_training.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/test_ovhcloud.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/test_price_compare.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/test_provider_base.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/test_runner.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/test_runpod.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/test_sdk_gpu_api.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/test_vastai.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu/test_workspace_sync.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_gpu_modal_creds.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_init.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_init_facade.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_install_commands.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_integration_fastai.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_integration_huggingface.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_integration_keras.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_integration_langchain.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_integration_lightning.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_integration_pytorch.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_integration_setA.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_integration_setB.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_integration_setC.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_integration_sklearn.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_integration_xgboost.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_launch.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_log.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_log_code.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_machine_migrate.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_mcp_decisions.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_mcp_monitor.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_mcp_predictions.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_media.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_media_extra.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_migrate.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_offline.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_offline_sync.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_patent_scan.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_pii.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_plot.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_plot_media_extra2.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_prompt_module.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_pure_modules.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_query_api.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_redact.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_reload_module.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_research_module.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_resume.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_retry_transport.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_run_module.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_sdk_cluster_j1.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_sdk_cluster_j2.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_sdk_cluster_j3.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_sdk_cluster_j4.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_sdk_core_edges.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_sdk_features.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_sender.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_session_module.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_session_sync.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_summary.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_sweep.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_system_metrics.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_topup_hi.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/tests/test_trace.py +0 -0
- {openrunner_sdk-2.41.2 → openrunner_sdk-2.41.3}/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.
|
|
3
|
+
Version: 2.41.3
|
|
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.
|
|
184
|
+
__version__ = "2.41.3"
|
|
185
185
|
|
|
186
186
|
logger = logging.getLogger("openrunner")
|
|
187
187
|
|
|
@@ -0,0 +1,34 @@
|
|
|
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.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import Any, Optional
|
|
18
|
+
|
|
19
|
+
try: # pragma: no cover - depends on optional wheel
|
|
20
|
+
import openrunner_core as _core
|
|
21
|
+
except ImportError: # pragma: no cover
|
|
22
|
+
_core = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def available() -> bool:
|
|
26
|
+
"""True when the native core wheel is importable."""
|
|
27
|
+
return _core is not None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def native_client(base_url: str, api_key: str) -> Optional[Any]:
|
|
31
|
+
"""A native Client for the given credentials, or None without the wheel."""
|
|
32
|
+
if _core is None:
|
|
33
|
+
return None
|
|
34
|
+
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")
|
|
@@ -801,6 +801,58 @@ class DatasetsClient:
|
|
|
801
801
|
_check(r)
|
|
802
802
|
return DatasetInfo._from_json(r.json())
|
|
803
803
|
|
|
804
|
+
def add_column(self, repo_id: str, name: str, *, default: str | None = None) -> list[str]:
|
|
805
|
+
"""Add an editable column to an indexed dataset.
|
|
806
|
+
|
|
807
|
+
The dataset's indexed source ("parent") columns stay **read-only**: the new
|
|
808
|
+
column lives in a separate overlay table keyed on the primary key
|
|
809
|
+
(``debug_id``), so the source data is never mutated. Works on a plain
|
|
810
|
+
indexed dataset or a query-derived one — ``index_dataset`` the dataset first
|
|
811
|
+
if it isn't already. A name that collides with a read-only source column is
|
|
812
|
+
rejected. Returns the editable column names.
|
|
813
|
+
"""
|
|
814
|
+
ds = self._resolve(repo_id)
|
|
815
|
+
r = self._http.post(f"/datasets/{ds.id}/columns",
|
|
816
|
+
json={"name": name, "default": default})
|
|
817
|
+
_check(r)
|
|
818
|
+
return r.json().get("columns", [])
|
|
819
|
+
|
|
820
|
+
def set_cell(self, repo_id: str, debug_id: str, column: str,
|
|
821
|
+
value: str | None = None) -> dict:
|
|
822
|
+
"""Set ONE editable cell (creates the column on first write).
|
|
823
|
+
|
|
824
|
+
``debug_id`` is the row's primary-key value; ``value=None`` clears it. A
|
|
825
|
+
read-only source column (or the primary key) is rejected. Returns
|
|
826
|
+
``{ok, debug_id, column}``.
|
|
827
|
+
"""
|
|
828
|
+
ds = self._resolve(repo_id)
|
|
829
|
+
r = self._http.patch(f"/datasets/{ds.id}/cell",
|
|
830
|
+
json={"debug_id": debug_id, "column": column, "value": value})
|
|
831
|
+
_check(r)
|
|
832
|
+
return r.json()
|
|
833
|
+
|
|
834
|
+
def edit_row(self, repo_id: str, debug_id: str, values: dict[str, str | None]) -> dict:
|
|
835
|
+
"""Edit one row — set several editable columns at once.
|
|
836
|
+
|
|
837
|
+
``values`` maps column -> value (``None`` clears). Only user-added overlay /
|
|
838
|
+
derived columns are writable; the whole edit is rejected (400) if any key is
|
|
839
|
+
a read-only source column. Returns ``{ok, debug_id, columns}``.
|
|
840
|
+
"""
|
|
841
|
+
ds = self._resolve(repo_id)
|
|
842
|
+
r = self._http.patch(f"/datasets/{ds.id}/rows/{debug_id}", json={"values": values})
|
|
843
|
+
_check(r)
|
|
844
|
+
return r.json()
|
|
845
|
+
|
|
846
|
+
def get_row(self, repo_id: str, debug_id: str) -> dict:
|
|
847
|
+
"""Fetch one full row by ``debug_id``: read-only source fields with any
|
|
848
|
+
editable overlay values merged on top. Returns
|
|
849
|
+
``{debug_id, row, editable_columns}`` (everything in ``row`` not listed in
|
|
850
|
+
``editable_columns`` is a read-only parent column)."""
|
|
851
|
+
ds = self._resolve(repo_id)
|
|
852
|
+
r = self._http.get(f"/datasets/{ds.id}/row", params={"debug_id": debug_id})
|
|
853
|
+
_check(r)
|
|
854
|
+
return r.json()
|
|
855
|
+
|
|
804
856
|
def delete_dataset(self, repo_id: str) -> None:
|
|
805
857
|
ds = self._resolve(repo_id)
|
|
806
858
|
self._http.delete(f"/datasets/{ds.id}")
|
|
@@ -1003,6 +1055,28 @@ def add_derivation(repo_id: str, parent_repo_id: str) -> "DatasetInfo":
|
|
|
1003
1055
|
return _c().add_derivation(repo_id, parent_repo_id)
|
|
1004
1056
|
|
|
1005
1057
|
|
|
1058
|
+
def add_column(repo_id: str, name: str, *, default: str | None = None) -> list[str]:
|
|
1059
|
+
"""Add an editable overlay column to an indexed dataset (source columns stay
|
|
1060
|
+
read-only). Module-level mirror of DatasetsClient.add_column."""
|
|
1061
|
+
return _c().add_column(repo_id, name, default=default)
|
|
1062
|
+
|
|
1063
|
+
|
|
1064
|
+
def set_cell(repo_id: str, debug_id: str, column: str, value: str | None = None) -> dict:
|
|
1065
|
+
"""Set one editable cell of a dataset row (creates the column if new)."""
|
|
1066
|
+
return _c().set_cell(repo_id, debug_id, column, value)
|
|
1067
|
+
|
|
1068
|
+
|
|
1069
|
+
def edit_row(repo_id: str, debug_id: str, values: dict[str, str | None]) -> dict:
|
|
1070
|
+
"""Edit one row — set several editable columns at once (read-only source
|
|
1071
|
+
columns are rejected)."""
|
|
1072
|
+
return _c().edit_row(repo_id, debug_id, values)
|
|
1073
|
+
|
|
1074
|
+
|
|
1075
|
+
def get_row(repo_id: str, debug_id: str) -> dict:
|
|
1076
|
+
"""Fetch one full row by debug_id (source fields + editable overlay values)."""
|
|
1077
|
+
return _c().get_row(repo_id, debug_id)
|
|
1078
|
+
|
|
1079
|
+
|
|
1006
1080
|
def presign_batch(repo_id: str, paths: list[str], **kwargs: Any) -> list[dict]:
|
|
1007
1081
|
return _c().presign_batch(repo_id, paths, **kwargs)
|
|
1008
1082
|
|
|
@@ -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.
|
|
@@ -544,6 +544,37 @@ def create_server():
|
|
|
544
544
|
"required": ["repo_id", "run_id"],
|
|
545
545
|
},
|
|
546
546
|
),
|
|
547
|
+
Tool(
|
|
548
|
+
name="openrunner_link_repo",
|
|
549
|
+
description=(
|
|
550
|
+
"Link a project to its source GitHub repository, with optional "
|
|
551
|
+
"subdirectory (monorepo) and branch. The linked repo lets code "
|
|
552
|
+
"artifact versions be recorded as commit references instead of "
|
|
553
|
+
"uploaded snapshots."
|
|
554
|
+
),
|
|
555
|
+
inputSchema={
|
|
556
|
+
"type": "object",
|
|
557
|
+
"properties": {
|
|
558
|
+
"project": {
|
|
559
|
+
"type": "string",
|
|
560
|
+
"description": "Project name (format: org/project or just project)",
|
|
561
|
+
},
|
|
562
|
+
"repo": {
|
|
563
|
+
"type": "string",
|
|
564
|
+
"description": "GitHub repo 'org/name' or full URL",
|
|
565
|
+
},
|
|
566
|
+
"path": {
|
|
567
|
+
"type": "string",
|
|
568
|
+
"description": "Subdirectory of the repo holding this project's code",
|
|
569
|
+
},
|
|
570
|
+
"branch": {
|
|
571
|
+
"type": "string",
|
|
572
|
+
"description": "Branch to track (default: main)",
|
|
573
|
+
},
|
|
574
|
+
},
|
|
575
|
+
"required": ["repo"],
|
|
576
|
+
},
|
|
577
|
+
),
|
|
547
578
|
Tool(
|
|
548
579
|
name="openrunner_search_runs",
|
|
549
580
|
description="Search experiment runs by name, tags, or notes.",
|
|
@@ -1527,6 +1558,8 @@ def create_server():
|
|
|
1527
1558
|
result = await _tool_create_dataset(arguments)
|
|
1528
1559
|
elif name == "openrunner_link_dataset_run":
|
|
1529
1560
|
result = await _tool_link_dataset_run(arguments)
|
|
1561
|
+
elif name == "openrunner_link_repo":
|
|
1562
|
+
result = await _tool_link_repo(arguments)
|
|
1530
1563
|
elif name == "openrunner_drift_check":
|
|
1531
1564
|
result = await _tool_drift_check(arguments)
|
|
1532
1565
|
elif name == "openrunner_drift_monitors":
|
|
@@ -2533,6 +2566,47 @@ async def _tool_link_dataset_run(args: dict) -> str:
|
|
|
2533
2566
|
return f"Linked dataset {repo_id} -> run {run_id} as [{link_type}]."
|
|
2534
2567
|
|
|
2535
2568
|
|
|
2569
|
+
async def _tool_link_repo(args: dict) -> str:
|
|
2570
|
+
"""Link a project to its source repository (repo + subdir + branch)."""
|
|
2571
|
+
repo = (args.get("repo") or "").strip()
|
|
2572
|
+
if not repo:
|
|
2573
|
+
return "Error: repo is required."
|
|
2574
|
+
project = args.get("project") or _load_settings().get("project")
|
|
2575
|
+
if not project:
|
|
2576
|
+
return "Error: No project specified and none configured."
|
|
2577
|
+
data = await _api_get("/projects", params={})
|
|
2578
|
+
projects = data if isinstance(data, list) else []
|
|
2579
|
+
match = None
|
|
2580
|
+
for p in projects:
|
|
2581
|
+
org = p.get("org_name", "")
|
|
2582
|
+
name = p.get("name", "")
|
|
2583
|
+
slug = p.get("slug", "")
|
|
2584
|
+
if project in (f"{org}/{name}", f"{org}/{slug}", name, slug, str(p.get("id"))):
|
|
2585
|
+
match = p
|
|
2586
|
+
break
|
|
2587
|
+
if match is None:
|
|
2588
|
+
return f"Project '{project}' not found."
|
|
2589
|
+
body: dict = {"github_repo": repo}
|
|
2590
|
+
if args.get("path") is not None:
|
|
2591
|
+
body["github_path"] = args["path"]
|
|
2592
|
+
if args.get("branch") is not None:
|
|
2593
|
+
body["github_branch"] = args["branch"]
|
|
2594
|
+
try:
|
|
2595
|
+
updated = await _api_patch(
|
|
2596
|
+
f"/organizations/{match.get('org_id')}/projects/{match.get('id')}/repo",
|
|
2597
|
+
data=body,
|
|
2598
|
+
)
|
|
2599
|
+
except Exception as e:
|
|
2600
|
+
return f"Error linking repository: {e}"
|
|
2601
|
+
loc = updated.get("github_repo") or repo
|
|
2602
|
+
if updated.get("github_path"):
|
|
2603
|
+
loc += f"/{updated['github_path']}"
|
|
2604
|
+
return (
|
|
2605
|
+
f"Linked project {match.get('name')} -> {loc} "
|
|
2606
|
+
f"@ {updated.get('github_branch') or 'main'}"
|
|
2607
|
+
)
|
|
2608
|
+
|
|
2609
|
+
|
|
2536
2610
|
async def _tool_set_model_card(args: dict) -> str:
|
|
2537
2611
|
"""Set card_markdown on a model artifact, returning validation warnings."""
|
|
2538
2612
|
from .model_card import validate_model_card
|
|
@@ -3571,7 +3645,7 @@ async def _tool_patent_lab_list(arguments: dict) -> str:
|
|
|
3571
3645
|
for e in entries:
|
|
3572
3646
|
score = e.get("novelty_score") or 0
|
|
3573
3647
|
status = e.get("status", "?")
|
|
3574
|
-
commit = e.get("commit_hash"
|
|
3648
|
+
commit = (e.get("commit_hash") or "")[:8]
|
|
3575
3649
|
lines.append(f" [{score:3d}] {status:10s} {e.get('title', 'Untitled')}")
|
|
3576
3650
|
if commit:
|
|
3577
3651
|
lines.append(f" commit: {commit} branch: {e.get('branch', '?')}")
|
|
@@ -3837,10 +3911,50 @@ async def _tool_patent_agent(arguments: dict) -> str:
|
|
|
3837
3911
|
"max_budget_usd": max_budget,
|
|
3838
3912
|
},
|
|
3839
3913
|
)
|
|
3914
|
+
job_id = resp.get("job_id")
|
|
3840
3915
|
status = resp.get("status", "unknown")
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3916
|
+
cost = resp.get("cost_usd", 0) or 0
|
|
3917
|
+
duration = resp.get("duration_ms", 0) or 0
|
|
3918
|
+
|
|
3919
|
+
# The API launches the agent in background (202). Poll until the job
|
|
3920
|
+
# finishes (llm-api mode: seconds; claude-code mode: minutes).
|
|
3921
|
+
if status == "running" and job_id:
|
|
3922
|
+
import asyncio
|
|
3923
|
+
for _ in range(60):
|
|
3924
|
+
await asyncio.sleep(3)
|
|
3925
|
+
st = await _api_get(
|
|
3926
|
+
f"/projects/{project_id}/patent-lab/entries/{entry_id}/agent-status")
|
|
3927
|
+
job = next((j for j in st.get("jobs", [])
|
|
3928
|
+
if j.get("job_id") == job_id), None)
|
|
3929
|
+
if job and job.get("status") in ("done", "error"):
|
|
3930
|
+
status = job["status"]
|
|
3931
|
+
cost = job.get("cost_usd") or 0
|
|
3932
|
+
duration = job.get("duration_ms") or 0
|
|
3933
|
+
break
|
|
3934
|
+
else:
|
|
3935
|
+
return (f"Patent agent still running after 3min (job {job_id}).\n"
|
|
3936
|
+
f"Re-check later via openrunner_patent_lab_list or the web UI.")
|
|
3937
|
+
|
|
3938
|
+
# Full output is persisted as a patent document; previews are truncated.
|
|
3939
|
+
output = ""
|
|
3940
|
+
try:
|
|
3941
|
+
docs = await _api_get(
|
|
3942
|
+
f"/projects/{project_id}/patent-lab/entries/{entry_id}/documents")
|
|
3943
|
+
doc_list = docs.get("documents", [])
|
|
3944
|
+
if doc_list:
|
|
3945
|
+
latest = max(doc_list, key=lambda d: d.get("created_at", ""))
|
|
3946
|
+
import httpx as _httpx
|
|
3947
|
+
base_url, api_key = _get_api_config()
|
|
3948
|
+
async with _httpx.AsyncClient(timeout=30.0) as _c:
|
|
3949
|
+
_r = await _c.get(
|
|
3950
|
+
f"{base_url}/api/v1/projects/{project_id}/patent-lab"
|
|
3951
|
+
f"/entries/{entry_id}/documents/{latest['id']}",
|
|
3952
|
+
params={"format": "markdown"},
|
|
3953
|
+
headers={"Authorization": f"Bearer {api_key}"})
|
|
3954
|
+
_r.raise_for_status()
|
|
3955
|
+
output = _r.text
|
|
3956
|
+
except Exception:
|
|
3957
|
+
pass
|
|
3844
3958
|
|
|
3845
3959
|
return (
|
|
3846
3960
|
f"Patent agent completed.\n"
|
|
@@ -3848,7 +3962,7 @@ async def _tool_patent_agent(arguments: dict) -> str:
|
|
|
3848
3962
|
f" Status: {status}\n"
|
|
3849
3963
|
f" Cost: ${cost:.4f}\n"
|
|
3850
3964
|
f" Duration: {duration}ms\n"
|
|
3851
|
-
f" Output:\n{output[:
|
|
3965
|
+
f" Output:\n{output[:6000] if output else '(voir Patent Lab)'}"
|
|
3852
3966
|
)
|
|
3853
3967
|
except Exception as e:
|
|
3854
3968
|
return f"Error running patent agent: {e}"
|
|
@@ -2166,7 +2166,12 @@ class TestPatentAgent:
|
|
|
2166
2166
|
monkeypatch.setattr(mcp, "_resolve_project_id", resolver)
|
|
2167
2167
|
patch_post(monkeypatch, {"status": "done", "output": "result text", "cost_usd": 0.5, "duration_ms": 1000})
|
|
2168
2168
|
out = run(mcp._tool_patent_agent({"entry_id": "e", "task": "analyze"}))
|
|
2169
|
-
|
|
2169
|
+
# The full output is now persisted to the patent-lab entry and fetched as
|
|
2170
|
+
# the latest document; with no document mocked here, the tool shows the
|
|
2171
|
+
# "(voir Patent Lab)" pointer instead of inlining the raw output.
|
|
2172
|
+
assert "Patent agent completed" in out
|
|
2173
|
+
assert "Status: done" in out
|
|
2174
|
+
assert ("result text" in out) or ("voir Patent Lab" in out)
|
|
2170
2175
|
|
|
2171
2176
|
def test_error(self, monkeypatch) -> None:
|
|
2172
2177
|
monkeypatch.setattr(mcp, "_resolve_project", lambda: "org/p")
|
|
File without changes
|
|
File without changes
|