openrunner-sdk 2.41.7__tar.gz → 2.41.8__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.7 → openrunner_sdk-2.41.8}/.gitignore +2 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/PKG-INFO +6 -6
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/README.md +1 -1
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/__init__.py +17 -3
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/artifact.py +49 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/cli.py +121 -31
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/datasets.py +139 -2
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/gpu/api_integration.py +29 -3
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/gpu/runner.py +14 -2
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/install_commands.py +318 -2
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/migrate.py +29 -7
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/run.py +22 -3
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/pyproject.toml +4 -4
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/integration/test_artifact_round_trip.py +1 -1
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_artifact.py +59 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_datasets_module.py +82 -0
- openrunner_sdk-2.41.8/tests/test_gpu/test_engine_serverside.py +62 -0
- openrunner_sdk-2.41.8/tests/test_gpu/test_engine_stream_keepalive.py +61 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_init_facade.py +8 -2
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_machine_migrate.py +47 -3
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_run_module.py +40 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_sdk_cluster_j3.py +5 -1
- openrunner_sdk-2.41.7/tests/test_gpu/test_engine_serverside.py +0 -30
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/=6.0 +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/=8.1 +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/CLAUDE.md +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/_native.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/_retry_transport.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/_safepath.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/_secret_rules.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/api_client.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/autoresearch.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/buffer.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/cache.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/config.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/cost.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/dataset.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/environment.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/evaluation.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/feedback.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/git_info.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/gpu/__init__.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/gpu/cost_guard.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/gpu/gpu_types.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/gpu/price_compare.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/gpu/provider_base.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/gpu/providers/__init__.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/gpu/providers/forge_provider.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/gpu/providers/lambda_cloud.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/gpu/providers/modal_provider.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/gpu/providers/ovhcloud.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/gpu/providers/runpod.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/gpu/providers/vastai.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/gpu/workspace_sync.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/guardrails.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/handover.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/hooks/patent-lab-post-commit +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/__init__.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/accelerate.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/anthropic_tracer.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/catboost.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/diffusers.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/fastai.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/forced_alignment.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/gladia.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/gymnasium.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/huggingface.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/hydra.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/ignite.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/jax.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/keras.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/langchain.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/lightgbm.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/lightning.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/llamaindex.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/openai_finetune.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/openai_tracer.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/optuna.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/pytorch.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/sb3.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/sklearn.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/skypilot.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/tensorflow.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/trl.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/tts.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/ultralytics.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/voice_agent.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/whisper.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/integration/xgboost.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/launch.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/llms-full.txt +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/llms.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/llms.txt +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/machine.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/mcp_server.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/media.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/model.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/model_card.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/offline.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/patent_scan.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/pii.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/plot.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/projects.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/prompt.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/query_api.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/redact.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/reload.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/research.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/research_policy.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/scorers.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/sender.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/session.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/settings.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/summary.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/sweep.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/sync.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/system_metrics.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/tensorboard.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/tools/__init__.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/tools/patent_report.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/trace.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/transcript_formatter.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/wal.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/wandb_compat/__init__.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/wandb_compat/_shim.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/openrunner/wer.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/scripts/gen_betterleaks_rules.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/__init__.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/conftest.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/fixtures/wandb-export-mini/run-aaa/config.json +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/fixtures/wandb-export-mini/run-aaa/metadata.json +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/fixtures/wandb-export-mini/run-aaa/metrics.jsonl +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/fixtures/wandb-export-mini/run-aaa/summary.json +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/model-checkpoint/manifest.json +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/model-checkpoint/weights.bin +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/sample-image/manifest.json +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/sample-image/preview.png +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/fixtures/wandb-export-mini/run-bbb/config.json +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/fixtures/wandb-export-mini/run-bbb/metadata.json +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/fixtures/wandb-export-mini/run-bbb/metrics.jsonl +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/fixtures/wandb-export-mini/run-bbb/summary.json +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/integration/__init__.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/integration/conftest.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_alert.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_aliases.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_api_client.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_api_client_methods.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_api_client_transport.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_buffer.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_cache.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_class_scorers.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_cli.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_cli_artifact_sweep.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_cli_datasets.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_cli_gpu_registry_deploy.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_cli_infra.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_cli_remainder.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_cli_topup.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_config.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_evaluation.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_finish.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_git_info.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu/__init__.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu/conftest.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu/test_cost_guard.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu/test_forge.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu/test_governed_jobs.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu/test_gpu_cluster.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu/test_gpu_types.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu/test_lambda.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu/test_modal.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu/test_ovh_training.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu/test_ovhcloud.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu/test_price_compare.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu/test_provider_base.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu/test_runner.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu/test_runpod.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu/test_sdk_gpu_api.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu/test_vastai.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu/test_workspace_sync.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_gpu_modal_creds.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_init.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_install_commands.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_integration_fastai.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_integration_huggingface.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_integration_keras.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_integration_langchain.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_integration_lightning.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_integration_pytorch.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_integration_setA.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_integration_setB.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_integration_setC.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_integration_sklearn.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_integration_xgboost.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_launch.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_llms.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_log.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_log_code.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_mcp_decisions.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_mcp_monitor.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_mcp_predictions.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_mcp_server.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_media.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_media_extra.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_migrate.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_offline.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_offline_sync.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_patent_scan.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_pii.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_plot.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_plot_media_extra2.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_prompt_module.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_pure_modules.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_query_api.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_redact.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_reload_module.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_research_module.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_resume.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_retry_transport.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_sdk_cluster_j1.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_sdk_cluster_j2.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_sdk_cluster_j4.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_sdk_core_edges.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_sdk_features.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_sender.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_session_module.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_session_sync.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_summary.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_sweep.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_system_metrics.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_topup_hi.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_trace.py +0 -0
- {openrunner_sdk-2.41.7 → openrunner_sdk-2.41.8}/tests/test_wandb_compat.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: openrunner-sdk
|
|
3
|
-
Version: 2.41.
|
|
3
|
+
Version: 2.41.8
|
|
4
4
|
Summary: OpenRunner SDK - W&B-compatible ML experiment tracking client
|
|
5
|
-
Project-URL: Homepage, https://github.com/
|
|
6
|
-
Project-URL: Repository, https://github.com/
|
|
7
|
-
Project-URL: Issues, https://github.com/
|
|
5
|
+
Project-URL: Homepage, https://github.com/gladiaio/openrunner
|
|
6
|
+
Project-URL: Repository, https://github.com/gladiaio/openrunner
|
|
7
|
+
Project-URL: Issues, https://github.com/gladiaio/openrunner/issues
|
|
8
8
|
Author-email: JL Queguiner <jl@gladia.io>
|
|
9
9
|
License: MIT
|
|
10
10
|
Keywords: experiment-tracking,invention-disclosure,machine-learning,ml,mlops,patent,wandb
|
|
@@ -656,7 +656,7 @@ W&B env vars (`WANDB_API_KEY`, `WANDB_BASE_URL`) are also supported as fallback
|
|
|
656
656
|
|
|
657
657
|
## Self-Hosting
|
|
658
658
|
|
|
659
|
-
OpenRunner is designed to be self-hosted. See the [main repo](https://github.com/
|
|
659
|
+
OpenRunner is designed to be self-hosted. See the [main repo](https://github.com/gladiaio/openrunner) for server setup with Docker Compose.
|
|
660
660
|
|
|
661
661
|
## License
|
|
662
662
|
|
|
@@ -541,7 +541,7 @@ W&B env vars (`WANDB_API_KEY`, `WANDB_BASE_URL`) are also supported as fallback
|
|
|
541
541
|
|
|
542
542
|
## Self-Hosting
|
|
543
543
|
|
|
544
|
-
OpenRunner is designed to be self-hosted. See the [main repo](https://github.com/
|
|
544
|
+
OpenRunner is designed to be self-hosted. See the [main repo](https://github.com/gladiaio/openrunner) for server setup with Docker Compose.
|
|
545
545
|
|
|
546
546
|
## License
|
|
547
547
|
|
|
@@ -93,6 +93,7 @@ import openrunner.datasets as datasets
|
|
|
93
93
|
from openrunner.datasets import (
|
|
94
94
|
DatasetInfo,
|
|
95
95
|
DatasetsClient,
|
|
96
|
+
DeriveIncomplete,
|
|
96
97
|
create_dataset,
|
|
97
98
|
dataset_info,
|
|
98
99
|
index_dataset,
|
|
@@ -101,6 +102,7 @@ from openrunner.datasets import (
|
|
|
101
102
|
link_project,
|
|
102
103
|
load_dataset,
|
|
103
104
|
add_derivation,
|
|
105
|
+
derive_by_ids,
|
|
104
106
|
derive_by_query,
|
|
105
107
|
derived_rows,
|
|
106
108
|
keyword_expand,
|
|
@@ -852,11 +854,21 @@ def alert(
|
|
|
852
854
|
return None
|
|
853
855
|
|
|
854
856
|
|
|
855
|
-
def log_artifact(
|
|
857
|
+
def log_artifact(
|
|
858
|
+
artifact: "Artifact | str | Path",
|
|
859
|
+
*,
|
|
860
|
+
name: str | None = None,
|
|
861
|
+
type: str | None = None,
|
|
862
|
+
aliases: list[str] | None = None,
|
|
863
|
+
) -> dict[str, Any] | None:
|
|
856
864
|
"""Upload an artifact to the active run. Never raises.
|
|
857
865
|
|
|
858
866
|
Args:
|
|
859
|
-
artifact:
|
|
867
|
+
artifact: An Artifact with files added via add_file/add_dir, OR a path
|
|
868
|
+
to a file or directory (W&B-style), built into an Artifact for you.
|
|
869
|
+
name: Artifact name when ``artifact`` is a path.
|
|
870
|
+
type: Advisory type when ``artifact`` is a path.
|
|
871
|
+
aliases: Aliases to set on the created version.
|
|
860
872
|
|
|
861
873
|
Returns:
|
|
862
874
|
Server response dict with version info, or None on failure.
|
|
@@ -865,7 +877,9 @@ def log_artifact(artifact: Artifact) -> dict[str, Any] | None:
|
|
|
865
877
|
if _active_run is None:
|
|
866
878
|
logger.warning("openrunner.log_artifact(): no active run")
|
|
867
879
|
return None
|
|
868
|
-
return _active_run.log_artifact(
|
|
880
|
+
return _active_run.log_artifact(
|
|
881
|
+
artifact, name=name, type=type, aliases=aliases
|
|
882
|
+
)
|
|
869
883
|
except Exception as e:
|
|
870
884
|
logger.warning("openrunner.log_artifact() failed: %s", e)
|
|
871
885
|
return None
|
|
@@ -29,6 +29,55 @@ def _sha256_file(path: Path, chunk_size: int = 8192) -> str:
|
|
|
29
29
|
return h.hexdigest()
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
def coerce_artifact(
|
|
33
|
+
source: "Artifact | str | Path",
|
|
34
|
+
*,
|
|
35
|
+
name: str | None = None,
|
|
36
|
+
type: str | None = None,
|
|
37
|
+
description: str | None = None,
|
|
38
|
+
metadata: dict[str, Any] | None = None,
|
|
39
|
+
) -> "Artifact":
|
|
40
|
+
"""Return an Artifact, building one from a path if that's what was given.
|
|
41
|
+
|
|
42
|
+
``log_artifact`` historically took a fully built ``Artifact``. Users coming
|
|
43
|
+
from W&B -- which this repo actively courts, see
|
|
44
|
+
``examples/migrate-from-wandb/`` -- write the path form instead::
|
|
45
|
+
|
|
46
|
+
run.log_artifact("out/final", name="my-model", type="model")
|
|
47
|
+
|
|
48
|
+
and used to get ``TypeError: unexpected keyword argument 'name'``. Accept
|
|
49
|
+
both. A directory becomes ``add_dir``, a file ``add_file``; ``name``
|
|
50
|
+
defaults to the path's own name so the common case needs no kwargs.
|
|
51
|
+
"""
|
|
52
|
+
if isinstance(source, Artifact):
|
|
53
|
+
# Already built: kwargs only fill gaps, they never silently override
|
|
54
|
+
# what the caller put on the object.
|
|
55
|
+
if name and not source.name:
|
|
56
|
+
source.name = name
|
|
57
|
+
if type and source.type is None:
|
|
58
|
+
source.type = type
|
|
59
|
+
return source
|
|
60
|
+
|
|
61
|
+
path = Path(source)
|
|
62
|
+
if not path.exists():
|
|
63
|
+
raise FileNotFoundError(f"artifact path does not exist: {path}")
|
|
64
|
+
art = Artifact(
|
|
65
|
+
name or path.name or path.stem,
|
|
66
|
+
type,
|
|
67
|
+
description,
|
|
68
|
+
metadata,
|
|
69
|
+
)
|
|
70
|
+
if path.is_dir():
|
|
71
|
+
# Existing add_dir semantics: files are prefixed with the directory
|
|
72
|
+
# name. Kept as-is so a coerced artifact lays out identically to a
|
|
73
|
+
# hand-built one -- changing it would silently move every existing
|
|
74
|
+
# caller's files.
|
|
75
|
+
art.add_dir(str(path))
|
|
76
|
+
else:
|
|
77
|
+
art.add_file(str(path))
|
|
78
|
+
return art
|
|
79
|
+
|
|
80
|
+
|
|
32
81
|
class Artifact:
|
|
33
82
|
"""An untyped, versioned bucket of files for experiment tracking.
|
|
34
83
|
|
|
@@ -145,6 +145,45 @@ def _save_settings(settings: dict[str, Any]) -> None:
|
|
|
145
145
|
)
|
|
146
146
|
|
|
147
147
|
|
|
148
|
+
def _resolve_api_key(settings: dict[str, Any] | None = None) -> str:
|
|
149
|
+
"""API key with the same precedence the SDK uses: env, then the
|
|
150
|
+
session-synced ~/.openrunner/session_config.json, then settings.json.
|
|
151
|
+
|
|
152
|
+
Historically the CLI read settings.json FIRST and most subcommands never
|
|
153
|
+
looked at session_config at all — so a stale key in settings.json made
|
|
154
|
+
`openrunner catalog ...` fail with "API key has expired" while
|
|
155
|
+
`openrunner datasets ...` (SDK-backed) worked with the fresh session key.
|
|
156
|
+
"""
|
|
157
|
+
try:
|
|
158
|
+
from openrunner.settings import _global_creds
|
|
159
|
+
session = _global_creds()
|
|
160
|
+
except Exception:
|
|
161
|
+
session = {}
|
|
162
|
+
return (
|
|
163
|
+
os.environ.get("OPENRUNNER_API_KEY")
|
|
164
|
+
or os.environ.get("OPENRUN_API_KEY")
|
|
165
|
+
or session.get("api_key")
|
|
166
|
+
or (settings or _load_settings()).get("api_key")
|
|
167
|
+
or ""
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _resolve_base_url(settings: dict[str, Any] | None = None) -> str:
|
|
172
|
+
"""Base URL with the same precedence chain as ``_resolve_api_key``."""
|
|
173
|
+
try:
|
|
174
|
+
from openrunner.settings import _global_creds
|
|
175
|
+
session = _global_creds()
|
|
176
|
+
except Exception:
|
|
177
|
+
session = {}
|
|
178
|
+
return (
|
|
179
|
+
os.environ.get("OPENRUNNER_BASE_URL")
|
|
180
|
+
or os.environ.get("OPENRUN_BASE_URL")
|
|
181
|
+
or session.get("base_url")
|
|
182
|
+
or (settings or _load_settings()).get("base_url")
|
|
183
|
+
or ""
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
|
|
148
187
|
# ---------------------------------------------------------------------------
|
|
149
188
|
# CLI group
|
|
150
189
|
# ---------------------------------------------------------------------------
|
|
@@ -420,7 +459,7 @@ def sync(
|
|
|
420
459
|
--sync-tensorboard Import TensorBoard event files as metrics
|
|
421
460
|
"""
|
|
422
461
|
settings = _load_settings()
|
|
423
|
-
api_key = settings
|
|
462
|
+
api_key = _resolve_api_key(settings)
|
|
424
463
|
base_url = settings.get("base_url", "http://localhost:8000")
|
|
425
464
|
|
|
426
465
|
if not api_key:
|
|
@@ -545,7 +584,7 @@ def ls(project: str | None) -> None:
|
|
|
545
584
|
in the specified project.
|
|
546
585
|
"""
|
|
547
586
|
settings = _load_settings()
|
|
548
|
-
api_key = settings
|
|
587
|
+
api_key = _resolve_api_key(settings)
|
|
549
588
|
base_url = settings.get("base_url", "http://localhost:8000")
|
|
550
589
|
|
|
551
590
|
if not api_key:
|
|
@@ -607,7 +646,7 @@ def _get_client() -> "APIClient":
|
|
|
607
646
|
import os
|
|
608
647
|
|
|
609
648
|
settings = _load_settings()
|
|
610
|
-
api_key =
|
|
649
|
+
api_key = _resolve_api_key(settings)
|
|
611
650
|
base_url = os.environ.get("OPENRUNNER_BASE_URL") or settings.get(
|
|
612
651
|
"base_url", "http://localhost:8000"
|
|
613
652
|
)
|
|
@@ -1534,7 +1573,7 @@ def launch(
|
|
|
1534
1573
|
from openrunner.launch import launch as launch_fn
|
|
1535
1574
|
|
|
1536
1575
|
settings = _load_settings()
|
|
1537
|
-
api_key = settings
|
|
1576
|
+
api_key = _resolve_api_key(settings)
|
|
1538
1577
|
if not api_key:
|
|
1539
1578
|
click.echo("Error: Not logged in. Run 'openrunner login' first.", err=True)
|
|
1540
1579
|
raise SystemExit(1)
|
|
@@ -1600,7 +1639,7 @@ def launch(
|
|
|
1600
1639
|
def jobs(project: str, state: str | None) -> None:
|
|
1601
1640
|
"""List jobs for a project."""
|
|
1602
1641
|
settings = _load_settings()
|
|
1603
|
-
api_key = settings
|
|
1642
|
+
api_key = _resolve_api_key(settings)
|
|
1604
1643
|
base_url = settings.get("base_url", "http://localhost:8000")
|
|
1605
1644
|
|
|
1606
1645
|
if not api_key:
|
|
@@ -1656,7 +1695,7 @@ def job_logs(job_id: str) -> None:
|
|
|
1656
1695
|
JOB_ID is the UUID of the job.
|
|
1657
1696
|
"""
|
|
1658
1697
|
settings = _load_settings()
|
|
1659
|
-
api_key = settings
|
|
1698
|
+
api_key = _resolve_api_key(settings)
|
|
1660
1699
|
base_url = settings.get("base_url", "http://localhost:8000")
|
|
1661
1700
|
|
|
1662
1701
|
if not api_key:
|
|
@@ -2244,7 +2283,7 @@ def docker(
|
|
|
2244
2283
|
import subprocess
|
|
2245
2284
|
|
|
2246
2285
|
settings = _load_settings()
|
|
2247
|
-
api_key =
|
|
2286
|
+
api_key = _resolve_api_key(settings)
|
|
2248
2287
|
base_url = os.environ.get("OPENRUNNER_BASE_URL") or settings.get(
|
|
2249
2288
|
"base_url", "http://localhost:8000"
|
|
2250
2289
|
)
|
|
@@ -3579,8 +3618,8 @@ def catalog_tables(org: str):
|
|
|
3579
3618
|
"""List catalog tables and their row counts."""
|
|
3580
3619
|
settings = _load_settings()
|
|
3581
3620
|
client = APIClient(
|
|
3582
|
-
base_url=settings
|
|
3583
|
-
api_key=settings
|
|
3621
|
+
base_url=_resolve_base_url(settings),
|
|
3622
|
+
api_key=_resolve_api_key(settings),
|
|
3584
3623
|
)
|
|
3585
3624
|
resp = client._request("get", f"/organizations/{org}/catalog/status")
|
|
3586
3625
|
if resp.status_code != 200:
|
|
@@ -3618,8 +3657,8 @@ def catalog_init(org: str):
|
|
|
3618
3657
|
"""Initialize catalog tables on the configured storage connection."""
|
|
3619
3658
|
settings = _load_settings()
|
|
3620
3659
|
client = APIClient(
|
|
3621
|
-
base_url=settings
|
|
3622
|
-
api_key=settings
|
|
3660
|
+
base_url=_resolve_base_url(settings),
|
|
3661
|
+
api_key=_resolve_api_key(settings),
|
|
3623
3662
|
)
|
|
3624
3663
|
resp = client._request("post", f"/organizations/{org}/catalog/init")
|
|
3625
3664
|
if resp.status_code != 200:
|
|
@@ -3640,8 +3679,8 @@ def catalog_query(org: str, table: str, where: str, limit: int, offset: int, fmt
|
|
|
3640
3679
|
"""Query rows from a catalog table."""
|
|
3641
3680
|
settings = _load_settings()
|
|
3642
3681
|
client = APIClient(
|
|
3643
|
-
base_url=settings
|
|
3644
|
-
api_key=settings
|
|
3682
|
+
base_url=_resolve_base_url(settings),
|
|
3683
|
+
api_key=_resolve_api_key(settings),
|
|
3645
3684
|
)
|
|
3646
3685
|
params = {"limit": limit, "offset": offset}
|
|
3647
3686
|
if where:
|
|
@@ -3687,8 +3726,8 @@ def catalog_export(org: str, table: str, fmt: str, where: str, limit: int, outpu
|
|
|
3687
3726
|
"""Export catalog table data to file or stdout."""
|
|
3688
3727
|
settings = _load_settings()
|
|
3689
3728
|
client = APIClient(
|
|
3690
|
-
base_url=settings
|
|
3691
|
-
api_key=settings
|
|
3729
|
+
base_url=_resolve_base_url(settings),
|
|
3730
|
+
api_key=_resolve_api_key(settings),
|
|
3692
3731
|
)
|
|
3693
3732
|
params: dict[str, Any] = {"format": fmt}
|
|
3694
3733
|
if where:
|
|
@@ -3730,8 +3769,8 @@ def catalog_upload(org: str, table: str, path: str, validate: bool):
|
|
|
3730
3769
|
|
|
3731
3770
|
settings = _load_settings()
|
|
3732
3771
|
client = APIClient(
|
|
3733
|
-
base_url=settings
|
|
3734
|
-
api_key=settings
|
|
3772
|
+
base_url=_resolve_base_url(settings),
|
|
3773
|
+
api_key=_resolve_api_key(settings),
|
|
3735
3774
|
)
|
|
3736
3775
|
|
|
3737
3776
|
# Collect files
|
|
@@ -3792,6 +3831,56 @@ def catalog_upload(org: str, table: str, path: str, validate: bool):
|
|
|
3792
3831
|
click.echo(f"\nDone. {total_rows} total rows uploaded to '{table}'.")
|
|
3793
3832
|
|
|
3794
3833
|
|
|
3834
|
+
@catalog.command("merge")
|
|
3835
|
+
@click.option("--org", required=True, help="Organization ID or slug")
|
|
3836
|
+
@click.option("--table", required=True, help="Table name (e.g. transcriptions)")
|
|
3837
|
+
@click.option("--key", default="request_id", show_default=True,
|
|
3838
|
+
help="Match column for the upsert")
|
|
3839
|
+
@click.option("--insert", is_flag=True, default=False,
|
|
3840
|
+
help="Also insert rows whose key has no match (default: skip them)")
|
|
3841
|
+
@click.option("--batch", default=500, show_default=True, help="Rows per request")
|
|
3842
|
+
@click.argument("path", type=click.Path(exists=True))
|
|
3843
|
+
def catalog_merge(org: str, table: str, key: str, insert: bool, batch: int, path: str):
|
|
3844
|
+
"""Upsert JSONL rows into a catalog table by key (enrichment merge).
|
|
3845
|
+
|
|
3846
|
+
Unlike `catalog upload` (append-only), matched rows get ONLY the uploaded
|
|
3847
|
+
columns updated; new columns are created automatically (nullable, typed
|
|
3848
|
+
from the first non-null value). Nested values must be pre-serialized to
|
|
3849
|
+
JSON strings.
|
|
3850
|
+
|
|
3851
|
+
Example:
|
|
3852
|
+
openrunner catalog merge --org my-org --table transcriptions \\
|
|
3853
|
+
--key request_id enriched.jsonl
|
|
3854
|
+
"""
|
|
3855
|
+
settings = _load_settings()
|
|
3856
|
+
client = APIClient(
|
|
3857
|
+
base_url=_resolve_base_url(settings),
|
|
3858
|
+
api_key=_resolve_api_key(settings),
|
|
3859
|
+
)
|
|
3860
|
+
lines = [ln for ln in Path(path).read_text(encoding="utf-8").splitlines() if ln.strip()]
|
|
3861
|
+
total = 0
|
|
3862
|
+
new_cols: set = set()
|
|
3863
|
+
for start in range(0, len(lines), max(1, batch)):
|
|
3864
|
+
chunk = lines[start:start + max(1, batch)]
|
|
3865
|
+
resp = client._request(
|
|
3866
|
+
"POST",
|
|
3867
|
+
f"/organizations/{org}/catalog/tables/{table}/merge?key={key}"
|
|
3868
|
+
+ ("&insert=true" if insert else ""),
|
|
3869
|
+
content=("\n".join(chunk)).encode("utf-8"),
|
|
3870
|
+
headers={"Content-Type": "application/x-ndjson"},
|
|
3871
|
+
)
|
|
3872
|
+
if resp.status_code not in (200, 201):
|
|
3873
|
+
click.echo(f"Merge failed at row {start + 1}: {resp.text}", err=True)
|
|
3874
|
+
sys.exit(1)
|
|
3875
|
+
result = resp.json()
|
|
3876
|
+
total += result.get("rows_merged", 0)
|
|
3877
|
+
new_cols.update(result.get("new_columns", []))
|
|
3878
|
+
click.echo(f" merged {total}/{len(lines)}")
|
|
3879
|
+
if new_cols:
|
|
3880
|
+
click.echo(f"New columns created: {', '.join(sorted(new_cols))}")
|
|
3881
|
+
click.echo(f"Done. {total} rows merged into '{table}'.")
|
|
3882
|
+
|
|
3883
|
+
|
|
3795
3884
|
# ---------------------------------------------------------------------------
|
|
3796
3885
|
# patent-scan command
|
|
3797
3886
|
# ---------------------------------------------------------------------------
|
|
@@ -3837,7 +3926,7 @@ def patent_scan(
|
|
|
3837
3926
|
from openrunner.patent_scan import scan_commit, _run_git
|
|
3838
3927
|
|
|
3839
3928
|
settings = _load_settings()
|
|
3840
|
-
api_key = settings
|
|
3929
|
+
api_key = _resolve_api_key(settings)
|
|
3841
3930
|
base_url = settings.get("base_url", "https://api.openrun.gladia.io")
|
|
3842
3931
|
project = project or settings.get("project")
|
|
3843
3932
|
|
|
@@ -3954,7 +4043,7 @@ def patent_lab(project: str | None, min_score: int, status: str | None, limit: i
|
|
|
3954
4043
|
openrunner patent-lab --min-score 70 --status detected
|
|
3955
4044
|
"""
|
|
3956
4045
|
settings = _load_settings()
|
|
3957
|
-
api_key = settings
|
|
4046
|
+
api_key = _resolve_api_key(settings)
|
|
3958
4047
|
base_url = settings.get("base_url", "https://api.openrun.gladia.io")
|
|
3959
4048
|
project = project or settings.get("project")
|
|
3960
4049
|
|
|
@@ -4372,7 +4461,7 @@ def gpu_credentials_add(org_id: str, provider: str, provider_key: str | None,
|
|
|
4372
4461
|
from openrunner.gpu.api_integration import GpuAPIClient
|
|
4373
4462
|
|
|
4374
4463
|
settings = _load_settings()
|
|
4375
|
-
api_key = settings
|
|
4464
|
+
api_key = _resolve_api_key(settings)
|
|
4376
4465
|
base_url = settings.get("base_url", "http://localhost:8000")
|
|
4377
4466
|
if not api_key:
|
|
4378
4467
|
click.echo("Error: Not logged in. Run 'openrunner login' first.", err=True)
|
|
@@ -4393,7 +4482,7 @@ def gpu_credentials_ls() -> None:
|
|
|
4393
4482
|
from openrunner.gpu.api_integration import GpuAPIClient
|
|
4394
4483
|
|
|
4395
4484
|
settings = _load_settings()
|
|
4396
|
-
api_key = settings
|
|
4485
|
+
api_key = _resolve_api_key(settings)
|
|
4397
4486
|
base_url = settings.get("base_url", "http://localhost:8000")
|
|
4398
4487
|
if not api_key:
|
|
4399
4488
|
click.echo("Error: Not logged in.", err=True)
|
|
@@ -4430,9 +4519,10 @@ def gpu_run(command, provider, gpu, gpu_count, image, disk_gb, env, ssh_keys, pr
|
|
|
4430
4519
|
|
|
4431
4520
|
Forge is serverless (no pod / no SSH): COMMAND is the App script and it runs
|
|
4432
4521
|
through the forge client, streaming the fleet's logs. The GPU is declared in
|
|
4433
|
-
the script, so --gpu is not used.
|
|
4522
|
+
the script, so --gpu is not used. -e KEY=VALUE reaches the App's
|
|
4523
|
+
local_entrypoint as an environment variable.
|
|
4434
4524
|
|
|
4435
|
-
openrunner gpu run --provider forge get_started.py
|
|
4525
|
+
openrunner gpu run --provider forge -e EPOCHS=3 get_started.py
|
|
4436
4526
|
|
|
4437
4527
|
Example:
|
|
4438
4528
|
|
|
@@ -4447,7 +4537,7 @@ def gpu_run(command, provider, gpu, gpu_count, image, disk_gb, env, ssh_keys, pr
|
|
|
4447
4537
|
raise SystemExit(1)
|
|
4448
4538
|
|
|
4449
4539
|
settings = _load_settings()
|
|
4450
|
-
api_key = settings
|
|
4540
|
+
api_key = _resolve_api_key(settings)
|
|
4451
4541
|
base_url = settings.get("base_url")
|
|
4452
4542
|
if not api_key:
|
|
4453
4543
|
click.echo("Error: Not logged in. Run 'openrunner login' first.", err=True)
|
|
@@ -4527,7 +4617,7 @@ def gpu_run(command, provider, gpu, gpu_count, image, disk_gb, env, ssh_keys, pr
|
|
|
4527
4617
|
from openrunner.gpu.runner import run_engine_serverside_sync
|
|
4528
4618
|
result = run_engine_serverside_sync(
|
|
4529
4619
|
provider=provider, command=command, project=project,
|
|
4530
|
-
emit=lambda m: click.echo(m),
|
|
4620
|
+
env=env_dict, emit=lambda m: click.echo(m),
|
|
4531
4621
|
)
|
|
4532
4622
|
except Exception as exc: # noqa: BLE001
|
|
4533
4623
|
click.echo(f"gpu run failed: {exc}", err=True)
|
|
@@ -4617,7 +4707,7 @@ def _registry_http():
|
|
|
4617
4707
|
import httpx
|
|
4618
4708
|
|
|
4619
4709
|
settings = _load_settings()
|
|
4620
|
-
api_key = settings
|
|
4710
|
+
api_key = _resolve_api_key(settings)
|
|
4621
4711
|
base_url = settings.get("base_url", "http://localhost:8000")
|
|
4622
4712
|
if not api_key:
|
|
4623
4713
|
click.echo("Error: Not logged in. Run 'openrunner login' first.", err=True)
|
|
@@ -4815,7 +4905,7 @@ def registry_logout() -> None:
|
|
|
4815
4905
|
def _drift_client_project(project: str | None):
|
|
4816
4906
|
"""Return (APIClient, project_id) resolving project name/slug -> UUID."""
|
|
4817
4907
|
settings = _load_settings()
|
|
4818
|
-
api_key = settings
|
|
4908
|
+
api_key = _resolve_api_key(settings)
|
|
4819
4909
|
base_url = settings.get("base_url", "http://localhost:8000")
|
|
4820
4910
|
if not api_key:
|
|
4821
4911
|
click.echo("Error: Not logged in. Run 'openrunner login' first.", err=True)
|
|
@@ -5152,7 +5242,7 @@ def deploy_call(deployment_id, path, project, method, data, headers) -> None:
|
|
|
5152
5242
|
openrunner deploy call <id> predict -p ORG/NAME -d '{"text":"hi"}'
|
|
5153
5243
|
"""
|
|
5154
5244
|
settings = _load_settings()
|
|
5155
|
-
api_key = settings
|
|
5245
|
+
api_key = _resolve_api_key(settings)
|
|
5156
5246
|
base_url = settings.get("base_url", "http://localhost:8000").rstrip("/")
|
|
5157
5247
|
if not api_key:
|
|
5158
5248
|
click.echo("Error: Not logged in.", err=True); raise SystemExit(1)
|
|
@@ -5196,7 +5286,7 @@ def deploy_call(deployment_id, path, project, method, data, headers) -> None:
|
|
|
5196
5286
|
def _plain_client():
|
|
5197
5287
|
"""Build an APIClient from settings, or exit if not logged in."""
|
|
5198
5288
|
settings = _load_settings()
|
|
5199
|
-
api_key = settings
|
|
5289
|
+
api_key = _resolve_api_key(settings)
|
|
5200
5290
|
base_url = settings.get("base_url", "http://localhost:8000")
|
|
5201
5291
|
if not api_key:
|
|
5202
5292
|
click.echo("Error: Not logged in. Run 'openrunner login' first.", err=True)
|
|
@@ -5420,7 +5510,7 @@ def runner_start(org, name, labels, gpu, gpu_count, poll_interval, once, token,
|
|
|
5420
5510
|
if not org or not name:
|
|
5421
5511
|
click.echo("Error: pass --org and --name to register, or --token to run with a pre-minted token.", err=True)
|
|
5422
5512
|
raise SystemExit(1)
|
|
5423
|
-
api_key = settings
|
|
5513
|
+
api_key = _resolve_api_key(settings)
|
|
5424
5514
|
if not api_key:
|
|
5425
5515
|
click.echo("Error: Not logged in. Run 'openrunner login' first (or pass --token).", err=True)
|
|
5426
5516
|
raise SystemExit(1)
|
|
@@ -5677,7 +5767,7 @@ def runner_launch(org, name, provider, gpu, gpu_count, labels, image, web, poll_
|
|
|
5677
5767
|
from openrunner.gpu.api_integration import GpuAPIClient
|
|
5678
5768
|
|
|
5679
5769
|
settings = _load_settings()
|
|
5680
|
-
api_key = settings
|
|
5770
|
+
api_key = _resolve_api_key(settings)
|
|
5681
5771
|
base_url = settings.get("base_url", "http://localhost:8000").rstrip("/")
|
|
5682
5772
|
if not api_key:
|
|
5683
5773
|
click.echo("Error: Not logged in. Run 'openrunner login' first.", err=True)
|
|
@@ -31,6 +31,20 @@ import httpx
|
|
|
31
31
|
_UUID_RE = re.compile(r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", re.I)
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
class DeriveIncomplete(RuntimeError):
|
|
35
|
+
"""A chunked ``derive_by_ids`` failed part-way through.
|
|
36
|
+
|
|
37
|
+
The dataset was created and holds ``ids[:resume_from]``. It is a valid
|
|
38
|
+
dataset, not a broken one — it is just short, and nothing about it says so.
|
|
39
|
+
Carries what is needed to append the remainder.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self, msg: str, *, dataset_id: str, resume_from: int):
|
|
43
|
+
super().__init__(msg)
|
|
44
|
+
self.dataset_id = dataset_id
|
|
45
|
+
self.resume_from = resume_from
|
|
46
|
+
|
|
47
|
+
|
|
34
48
|
def _check(r: "httpx.Response") -> "httpx.Response":
|
|
35
49
|
"""Raise with the API's actual error detail (FastAPI 422 lists the bad field)."""
|
|
36
50
|
if r.is_success:
|
|
@@ -741,18 +755,39 @@ class DatasetsClient:
|
|
|
741
755
|
_check(r)
|
|
742
756
|
return r.json()
|
|
743
757
|
|
|
744
|
-
def reconcile_shards(self, repo_id: str, *, timeout: float = 1800.0
|
|
758
|
+
def reconcile_shards(self, repo_id: str, *, timeout: float = 1800.0,
|
|
759
|
+
offset: int = 0, limit: int = 0) -> dict:
|
|
745
760
|
"""Rebuild the tar clip index (index.jsonl) by re-scanning shards on S3.
|
|
746
761
|
|
|
747
762
|
Recovers byte offsets if index.jsonl was lost or an ``upload_sharded`` run
|
|
748
763
|
was interrupted — the shard tars are the source of truth. Re-marks the
|
|
749
764
|
dataset ``sharded`` so ``/clip`` resolves audio by Range GET.
|
|
765
|
+
|
|
766
|
+
``offset``/``limit`` reconcile a slice of the (sorted) shards, MERGING into
|
|
767
|
+
the existing index — for datasets too large to scan in one request without
|
|
768
|
+
hitting the proxy timeout. ``limit=0`` scans everything at once.
|
|
750
769
|
"""
|
|
751
770
|
ds = self._resolve(repo_id)
|
|
752
|
-
r = self._http.post(f"/datasets/{ds.id}/shards/reconcile",
|
|
771
|
+
r = self._http.post(f"/datasets/{ds.id}/shards/reconcile",
|
|
772
|
+
params={"offset": offset, "limit": limit}, timeout=timeout)
|
|
753
773
|
_check(r)
|
|
754
774
|
return r.json()
|
|
755
775
|
|
|
776
|
+
def reconcile_shards_batched(self, repo_id: str, *, batch: int = 500,
|
|
777
|
+
timeout: float = 1800.0, progress=None) -> dict:
|
|
778
|
+
"""Reconcile a large sharded dataset in ``batch``-shard slices so no single
|
|
779
|
+
request exceeds the proxy timeout. Loops until every shard is indexed."""
|
|
780
|
+
off, last = 0, None
|
|
781
|
+
while True:
|
|
782
|
+
last = self.reconcile_shards(repo_id, offset=off, limit=batch, timeout=timeout)
|
|
783
|
+
if progress:
|
|
784
|
+
progress(last)
|
|
785
|
+
nxt = last.get("next_offset")
|
|
786
|
+
if not nxt:
|
|
787
|
+
break
|
|
788
|
+
off = nxt
|
|
789
|
+
return last
|
|
790
|
+
|
|
756
791
|
def set_storage_policy(self, repo_id: str, tier: str) -> dict:
|
|
757
792
|
"""1-click storage tier for a managed dataset's files.
|
|
758
793
|
|
|
@@ -908,6 +943,8 @@ class DatasetsClient:
|
|
|
908
943
|
) -> DatasetInfo:
|
|
909
944
|
"""Create (or append to) a dataset **derived from a query** over ``repo_id``.
|
|
910
945
|
|
|
946
|
+
For an explicit membership list rather than a query, see ``derive_by_ids``.
|
|
947
|
+
|
|
911
948
|
``mode``: "word" (``q`` = term / CSV; pairs with LLM ``keyword_expand``),
|
|
912
949
|
"fts" (BM25), "exact" (phrase), "regex" (``q`` = a Python-``re`` pattern
|
|
913
950
|
matched over the text column, bounded), "semantic" (vector — needs
|
|
@@ -936,6 +973,101 @@ class DatasetsClient:
|
|
|
936
973
|
_check(r)
|
|
937
974
|
return DatasetInfo._from_json(r.json())
|
|
938
975
|
|
|
976
|
+
def derive_by_ids(
|
|
977
|
+
self,
|
|
978
|
+
repo_id: str,
|
|
979
|
+
ids: "list[str]",
|
|
980
|
+
*,
|
|
981
|
+
name: str | None = None,
|
|
982
|
+
columns: "dict[str, list] | None" = None,
|
|
983
|
+
lang: str | None = None,
|
|
984
|
+
validate_ids: bool = True,
|
|
985
|
+
chunk_size: int = 50_000,
|
|
986
|
+
target_repo_id: str | None = None,
|
|
987
|
+
project_id: str | None = None,
|
|
988
|
+
) -> DatasetInfo:
|
|
989
|
+
"""Create (or append to) a dataset from an **explicit list of primary keys**.
|
|
990
|
+
|
|
991
|
+
For selections no query can express: rows picked by a boolean column the
|
|
992
|
+
search modes cannot reach, by an external model's output, or by a human
|
|
993
|
+
review pass. The alternative is reverse-engineering a query that happens
|
|
994
|
+
to return the same rows.
|
|
995
|
+
|
|
996
|
+
``ids`` are the parent's primary keys (``debug_id`` for transcript
|
|
997
|
+
catalogs). Unknown ids are dropped server-side unless
|
|
998
|
+
``validate_ids=False`` — pass that only when the ids were just read out
|
|
999
|
+
of the parent. Long lists are POSTed in ``chunk_size`` batches, the first
|
|
1000
|
+
creating the dataset and the rest appending to it, so the caller does not
|
|
1001
|
+
have to hold a 20 MB request body. Returns the dataset after the last
|
|
1002
|
+
batch; ``num_examples`` reflects the deduplicated total.
|
|
1003
|
+
|
|
1004
|
+
``columns`` attaches per-id metadata as member columns, one list per
|
|
1005
|
+
column positionally aligned to ``ids``::
|
|
1006
|
+
|
|
1007
|
+
ds.derive_by_ids(parent, ids, name='org/medical',
|
|
1008
|
+
columns={'n_health': counts, 'source': sigs})
|
|
1009
|
+
|
|
1010
|
+
Types are preserved, so the ranking signal stays queryable: one dataset
|
|
1011
|
+
plus ``n_health >= 20`` replaces the three nested datasets you would
|
|
1012
|
+
otherwise have to build to express three thresholds over the same rows.
|
|
1013
|
+
Chunking splits the columns alongside the ids, so a batch never carries
|
|
1014
|
+
another batch's values.
|
|
1015
|
+
"""
|
|
1016
|
+
if not ids:
|
|
1017
|
+
raise ValueError("derive_by_ids needs a non-empty ids list")
|
|
1018
|
+
if not (name or target_repo_id):
|
|
1019
|
+
raise ValueError("derive_by_ids needs either name= (create) or target_repo_id= (append)")
|
|
1020
|
+
if chunk_size < 1:
|
|
1021
|
+
raise ValueError("chunk_size must be >= 1")
|
|
1022
|
+
for _n, _v in (columns or {}).items():
|
|
1023
|
+
if len(_v) != len(ids):
|
|
1024
|
+
raise ValueError(
|
|
1025
|
+
f"columns[{_n!r}] has {len(_v)} values but ids has {len(ids)}; "
|
|
1026
|
+
"they are positional")
|
|
1027
|
+
ds = self._resolve(repo_id)
|
|
1028
|
+
nat = self._native_client()
|
|
1029
|
+
info: DatasetInfo | None = None
|
|
1030
|
+
target_id = str(self._resolve(target_repo_id).id) if target_repo_id else None
|
|
1031
|
+
for i in range(0, len(ids), chunk_size):
|
|
1032
|
+
batch = [str(x) for x in ids[i:i + chunk_size]]
|
|
1033
|
+
body: dict[str, Any] = {
|
|
1034
|
+
"mode": "ids", "ids": batch, "limit": len(batch),
|
|
1035
|
+
"validate_ids": validate_ids,
|
|
1036
|
+
}
|
|
1037
|
+
if columns:
|
|
1038
|
+
body["columns"] = {n: list(v[i:i + chunk_size])
|
|
1039
|
+
for n, v in columns.items()}
|
|
1040
|
+
if lang:
|
|
1041
|
+
body["lang"] = lang
|
|
1042
|
+
if target_id: # append into the existing target...
|
|
1043
|
+
body["target_dataset_id"] = target_id
|
|
1044
|
+
else: # ...or create on the first batch
|
|
1045
|
+
body["name"] = name
|
|
1046
|
+
if project_id:
|
|
1047
|
+
body["project_id"] = str(project_id)
|
|
1048
|
+
try:
|
|
1049
|
+
if nat is not None:
|
|
1050
|
+
info = DatasetInfo._from_json(nat.derive_dataset(str(ds.id), body))
|
|
1051
|
+
else:
|
|
1052
|
+
r = self._http.post(f"/datasets/{ds.id}/derive", json=body, timeout=600.0)
|
|
1053
|
+
_check(r)
|
|
1054
|
+
info = DatasetInfo._from_json(r.json())
|
|
1055
|
+
except Exception as e:
|
|
1056
|
+
# A mid-loop failure leaves a dataset that exists and looks
|
|
1057
|
+
# complete. Retrying the whole call re-sends batch 0 with name=
|
|
1058
|
+
# and trips the duplicate-name check, so it can never resume —
|
|
1059
|
+
# hand back what the caller needs to continue instead.
|
|
1060
|
+
if target_id is None:
|
|
1061
|
+
raise
|
|
1062
|
+
raise DeriveIncomplete(
|
|
1063
|
+
f"derive_by_ids failed on the batch starting at index {i} of "
|
|
1064
|
+
f"{len(ids)}: {e}. Dataset {target_id} holds ids[:{i}]; resume "
|
|
1065
|
+
f"with target_repo_id={target_id!r} and ids[{i}:].",
|
|
1066
|
+
dataset_id=target_id, resume_from=i,
|
|
1067
|
+
) from e
|
|
1068
|
+
target_id = target_id or str(info.id) # subsequent batches append
|
|
1069
|
+
return info
|
|
1070
|
+
|
|
939
1071
|
def derived_rows(self, repo_id: str, *, limit: int = 50, offset: int = 0) -> dict:
|
|
940
1072
|
"""Page a query-derived dataset's rows: member keys + inherited parent
|
|
941
1073
|
fields (lineage-namespaced) + extra columns. Native core when available."""
|
|
@@ -1382,6 +1514,11 @@ def derive_by_query(repo_id: str, q: str, **kwargs: Any) -> "DatasetInfo":
|
|
|
1382
1514
|
return _c().derive_by_query(repo_id, q, **kwargs)
|
|
1383
1515
|
|
|
1384
1516
|
|
|
1517
|
+
def derive_by_ids(repo_id: str, ids: "list[str]", **kwargs: Any) -> "DatasetInfo":
|
|
1518
|
+
"""Create/append a dataset from an explicit list of parent primary keys."""
|
|
1519
|
+
return _c().derive_by_ids(repo_id, ids, **kwargs)
|
|
1520
|
+
|
|
1521
|
+
|
|
1385
1522
|
def derived_rows(repo_id: str, **kwargs: Any) -> dict:
|
|
1386
1523
|
"""Page a query-derived dataset's rows (member keys + inherited parent fields)."""
|
|
1387
1524
|
return _c().derived_rows(repo_id, **kwargs)
|