opensportslib 0.3.0.dev11__tar.gz → 0.3.0.dev13__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.
- {opensportslib-0.3.0.dev11/opensportslib.egg-info → opensportslib-0.3.0.dev13}/PKG-INFO +8 -2
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/README.md +7 -1
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/optimizer/builder.py +10 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/backbones/builder.py +1 -1
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13/opensportslib.egg-info}/PKG-INFO +8 -2
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib.egg-info/SOURCES.txt +5 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/pyproject.toml +1 -1
- opensportslib-0.3.0.dev13/tests/release/__init__.py +0 -0
- opensportslib-0.3.0.dev13/tests/release/_release_common.py +372 -0
- opensportslib-0.3.0.dev13/tests/release/test_classification_release.py +413 -0
- opensportslib-0.3.0.dev13/tests/release/test_localization_release.py +386 -0
- opensportslib-0.3.0.dev13/tests/release/test_vqa_release.py +188 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/LICENSE +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/LICENSE-COMMERCIAL +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/MANIFEST.in +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/examples/quickstart/basic_classification.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/examples/quickstart/basic_localization.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/examples/quickstart/basic_vqa.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/__init__.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/adaptation/__init__.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/adaptation/spotta.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/apis/__init__.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/apis/base_task_model.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/apis/classification.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/apis/localization.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/apis/vqa.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/cli.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/classification/default.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/classification/sngar_frames.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/classification/sngar_tracking.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/classification/video.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/default.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/localization/calf_resnetpca512.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/localization/default.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/localization/e2e_spotta.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/localization/h5_header_distance.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/localization/h5_header_skeleton.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/localization/netvladpp_resnetpca512.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/localization/tracking_action_spotting.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/localization/video_dali.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/localization/video_ocv.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/vqa/default.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/vqa/qwen.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/vqa/qwen3_vl_native.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/vqa/qwen_lora.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/vqa/qwen_sngar_frames.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/configs/vqa/xvars.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/__init__.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/config/__init__.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/config/accessors.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/config/conflicts.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/config/loader.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/config/migrate.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/config/migrations/__init__.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/config/migrations/legacy_to_canonical.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/config/runtime_adapter.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/config/schema.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/config/schemas/__init__.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/config/schemas/schema_canonical.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/config/schemas/schema_legacy.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/config/validate.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/loss/__init__.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/loss/builder.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/loss/calf.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/loss/ce.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/loss/combine.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/loss/nll.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/optimizer/__init__.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/sampler/weighted_sampler.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/scheduler/__init__.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/scheduler/builder.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/trainer/__init__.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/trainer/classification_trainer.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/trainer/localization_trainer.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/trainer/vqa_trainer.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/utils/checkpoint.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/utils/config.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/utils/config_normalize.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/utils/data.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/utils/ddp.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/utils/default_args.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/utils/hf_runtime.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/utils/lightning.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/utils/load_annotations.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/utils/seed.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/utils/video_processing.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/utils/wandb.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/datasets/__init__.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/datasets/builder.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/datasets/classification_dataset.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/datasets/localization_dataset.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/datasets/utils/__init__.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/datasets/utils/h5_tracking.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/datasets/utils/tracking.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/datasets/vqa_dataset.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/legacy_config/classification.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/legacy_config/localization-e2e-ocv.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/legacy_config/localization-json_calf_resnetpca512.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/legacy_config/localization-json_netvlad++_resnetpca512.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/legacy_config/localization.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/legacy_config/sngar-frames.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/legacy_config/sngar-tracking.yaml +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/metrics/classification_metric.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/metrics/localization_metric.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/metrics/vqa_metric.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/__init__.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/base/contextaware.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/base/e2e.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/base/learnablepooling.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/base/qwen_vl_native.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/base/qwen_xvars.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/base/rule_based.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/base/tracking.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/base/vars.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/base/video.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/base/video_chatgpt_compat.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/base/video_mae.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/base/xvars_videochatgpt.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/builder.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/heads/builder.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/neck/builder.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/utils/common.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/utils/impl/__init__.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/utils/impl/asformer.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/utils/impl/calf.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/utils/impl/gsm.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/utils/impl/gtad.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/utils/impl/tsm.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/utils/litebase.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/utils/modules.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/utils/shift.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/utils/utils.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/utils/vqa_prediction_priors.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/utils/vqa_prompting.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/utils/xvars_clip_index.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/setup/setup.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/tools/__init__.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/tools/_common.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/tools/hf_transfer.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/tools/osl_json_to_parquet.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/tools/parquet_to_osl_json.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib.egg-info/dependency_links.txt +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib.egg-info/entry_points.txt +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib.egg-info/requires.txt +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib.egg-info/top_level.txt +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/scripts/run_h5_header_rule_inference.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/setup.cfg +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/conftest.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_classification_dataset_paths.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_classification_trainer_dataloader.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_config_architecture.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_config_split_override_sync.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_config_utils_smoke.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_conversion_tools.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_extract_xvars_features.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_h5_header_rule_spotter.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_h5_header_skeleton_spotter.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_h5_tracking_dataset.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_hf_transfer_tools.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_localization_dali_filenames.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_localization_hf_backend_override.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_localization_intervals.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_package_smoke.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_pretrained_config_merge_policy.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_public_apis_smoke.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_setup_cli.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_spotta_e2e.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_subset_train_infer_integration.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_task_model_api_contract.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_vqa_api.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_vqa_metrics_semantic.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_vqa_qwen_xvars.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_vqa_training_lora.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tests/test_vqa_xvars_videochatgpt.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tools/convert/build_sn_vqa_2026_vqa.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tools/convert/build_soccernet_gar.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tools/convert/build_soccernet_gar_action_spotting.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tools/convert/build_soccernet_gar_vqa.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tools/convert/build_xvars_indexes.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tools/convert/extract_xvars_clip_features.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tools/convert/osl_json_to_parquet_webdataset.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tools/convert/parquet_webdataset_to_osl_json.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tools/download/download_hf_repo.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tools/download/download_osl_hf.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tools/download/upload_osl_hf.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tools/training/classification.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tools/training/localization.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tools/training/vqa.py +0 -0
- {opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/tools/upload/upload_model_hf.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opensportslib
|
|
3
|
-
Version: 0.3.0.
|
|
3
|
+
Version: 0.3.0.dev13
|
|
4
4
|
Summary: OpenSportsLib is the professional library, designed for advanced video understanding in sports. It provides state-of-the-art tools for action recognition, spotting, retrieval, and captioning, making it ideal for researchers, analysts, and developers working with sports video data.
|
|
5
5
|
Author: Jeet Vora
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -76,7 +76,7 @@ OpenSportsLib is designed for **researchers, ML engineers, and sports analytics
|
|
|
76
76
|
### Create conda env
|
|
77
77
|
|
|
78
78
|
```bash
|
|
79
|
-
conda create -n osl python=3.12 pip
|
|
79
|
+
conda create -n osl python=3.12 pip -y
|
|
80
80
|
conda activate osl
|
|
81
81
|
```
|
|
82
82
|
|
|
@@ -92,6 +92,12 @@ pip install opensportslib
|
|
|
92
92
|
pip install --pre opensportslib
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
+
### Source development version
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pip install -e .
|
|
99
|
+
```
|
|
100
|
+
|
|
95
101
|
### Setup Environment (PyTorch, CUDA aware & Optional Dependencies)
|
|
96
102
|
```bash
|
|
97
103
|
# Install PyTorch (CPU/GPU auto-detected)
|
|
@@ -40,7 +40,7 @@ OpenSportsLib is designed for **researchers, ML engineers, and sports analytics
|
|
|
40
40
|
### Create conda env
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
conda create -n osl python=3.12 pip
|
|
43
|
+
conda create -n osl python=3.12 pip -y
|
|
44
44
|
conda activate osl
|
|
45
45
|
```
|
|
46
46
|
|
|
@@ -56,6 +56,12 @@ pip install opensportslib
|
|
|
56
56
|
pip install --pre opensportslib
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
+
### Source development version
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install -e .
|
|
63
|
+
```
|
|
64
|
+
|
|
59
65
|
### Setup Environment (PyTorch, CUDA aware & Optional Dependencies)
|
|
60
66
|
```bash
|
|
61
67
|
# Install PyTorch (CPU/GPU auto-detected)
|
{opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/core/optimizer/builder.py
RENAMED
|
@@ -39,4 +39,14 @@ def build_optimizer(parameters, cfg, default_args=None):
|
|
|
39
39
|
weight_decay=cfg.weight_decay,
|
|
40
40
|
amsgrad=cfg.amsgrad,
|
|
41
41
|
)
|
|
42
|
+
elif cfg.type == "SGD":
|
|
43
|
+
optimizer = torch.optim.SGD(
|
|
44
|
+
parameters,
|
|
45
|
+
lr=cfg.lr,
|
|
46
|
+
momentum=getattr(cfg, "momentum", 0.0),
|
|
47
|
+
weight_decay=getattr(cfg, "weight_decay", 0.0),
|
|
48
|
+
nesterov=getattr(cfg, "nesterov", False),
|
|
49
|
+
)
|
|
50
|
+
else:
|
|
51
|
+
raise ValueError(f"Unsupported optimizer type: {cfg.type}")
|
|
42
52
|
return optimizer
|
{opensportslib-0.3.0.dev11 → opensportslib-0.3.0.dev13}/opensportslib/models/backbones/builder.py
RENAMED
|
@@ -253,7 +253,7 @@ class RegnetyExtractFeatures(BaseExtractFeatures):
|
|
|
253
253
|
self._feat_dim = feat_dim
|
|
254
254
|
|
|
255
255
|
|
|
256
|
-
class ResnetExtractFeatures(
|
|
256
|
+
class ResnetExtractFeatures(BaseExtractFeatures):
|
|
257
257
|
"""Feature extractor which is based on the "resnet" models of the torchvision models.
|
|
258
258
|
The model is adapted for this task by adding temporal shift modules.
|
|
259
259
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opensportslib
|
|
3
|
-
Version: 0.3.0.
|
|
3
|
+
Version: 0.3.0.dev13
|
|
4
4
|
Summary: OpenSportsLib is the professional library, designed for advanced video understanding in sports. It provides state-of-the-art tools for action recognition, spotting, retrieval, and captioning, making it ideal for researchers, analysts, and developers working with sports video data.
|
|
5
5
|
Author: Jeet Vora
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -76,7 +76,7 @@ OpenSportsLib is designed for **researchers, ML engineers, and sports analytics
|
|
|
76
76
|
### Create conda env
|
|
77
77
|
|
|
78
78
|
```bash
|
|
79
|
-
conda create -n osl python=3.12 pip
|
|
79
|
+
conda create -n osl python=3.12 pip -y
|
|
80
80
|
conda activate osl
|
|
81
81
|
```
|
|
82
82
|
|
|
@@ -92,6 +92,12 @@ pip install opensportslib
|
|
|
92
92
|
pip install --pre opensportslib
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
+
### Source development version
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pip install -e .
|
|
99
|
+
```
|
|
100
|
+
|
|
95
101
|
### Setup Environment (PyTorch, CUDA aware & Optional Dependencies)
|
|
96
102
|
```bash
|
|
97
103
|
# Install PyTorch (CPU/GPU auto-detected)
|
|
@@ -165,6 +165,11 @@ tests/test_vqa_metrics_semantic.py
|
|
|
165
165
|
tests/test_vqa_qwen_xvars.py
|
|
166
166
|
tests/test_vqa_training_lora.py
|
|
167
167
|
tests/test_vqa_xvars_videochatgpt.py
|
|
168
|
+
tests/release/__init__.py
|
|
169
|
+
tests/release/_release_common.py
|
|
170
|
+
tests/release/test_classification_release.py
|
|
171
|
+
tests/release/test_localization_release.py
|
|
172
|
+
tests/release/test_vqa_release.py
|
|
168
173
|
tools/convert/build_sn_vqa_2026_vqa.py
|
|
169
174
|
tools/convert/build_soccernet_gar.py
|
|
170
175
|
tools/convert/build_soccernet_gar_action_spotting.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "opensportslib"
|
|
7
|
-
version = "0.3.0.
|
|
7
|
+
version = "0.3.0.dev13"
|
|
8
8
|
description = "OpenSportsLib is the professional library, designed for advanced video understanding in sports. It provides state-of-the-art tools for action recognition, spotting, retrieval, and captioning, making it ideal for researchers, analysts, and developers working with sports video data."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
File without changes
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
"""Shared helpers for the release-verification test suite (tests/release/).
|
|
2
|
+
|
|
3
|
+
These tests are NOT part of the regular `pytest tests/test_*.py` contract.
|
|
4
|
+
They download real datasets from the OpenSportsLab Hugging Face org
|
|
5
|
+
(some of them large) and run real training/inference/evaluation on GPU.
|
|
6
|
+
They exist to be run manually after a big release to confirm that training
|
|
7
|
+
still works end-to-end for every model family the library ships.
|
|
8
|
+
|
|
9
|
+
See tests/release/README.md for the full contract, prerequisites, and
|
|
10
|
+
invocation examples.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import importlib.util
|
|
16
|
+
import os
|
|
17
|
+
from copy import deepcopy
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
import pytest
|
|
22
|
+
|
|
23
|
+
REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
24
|
+
|
|
25
|
+
RELEASE_ENV_FLAG = "RUN_OSL_RELEASE_TESTS"
|
|
26
|
+
|
|
27
|
+
# Where materialized configs / run outputs (checkpoints, logs, predictions)
|
|
28
|
+
# are cached. Override with OSL_RELEASE_CACHE_DIR to point at a disk with
|
|
29
|
+
# more room.
|
|
30
|
+
CACHE_ROOT = Path(
|
|
31
|
+
os.environ.get("OSL_RELEASE_CACHE_DIR", str(REPO_ROOT / ".release_test_cache"))
|
|
32
|
+
).expanduser()
|
|
33
|
+
CONFIG_DIR = CACHE_ROOT / "configs"
|
|
34
|
+
OUTPUT_DIR = CACHE_ROOT / "outputs"
|
|
35
|
+
|
|
36
|
+
# Where datasets are downloaded to / read from. Deliberately independent of
|
|
37
|
+
# CACHE_ROOT: point OSL_RELEASE_DATA_DIR at a folder that already hosts these
|
|
38
|
+
# datasets (a shared drive, a previous run's download, ...) and nothing gets
|
|
39
|
+
# re-downloaded or overwritten -- snapshot_dataset()/download_files() below
|
|
40
|
+
# pass this straight through as `local_dir` to huggingface_hub, which only
|
|
41
|
+
# fetches files that are missing or whose content has changed (standard HF
|
|
42
|
+
# Hub behavior; see https://huggingface.co/docs/huggingface_hub/guides/download).
|
|
43
|
+
# Defaults to a subdirectory of CACHE_ROOT when unset.
|
|
44
|
+
DATA_DIR = Path(
|
|
45
|
+
os.environ.get("OSL_RELEASE_DATA_DIR", str(CACHE_ROOT / "data"))
|
|
46
|
+
).expanduser()
|
|
47
|
+
|
|
48
|
+
for _d in (DATA_DIR, CONFIG_DIR, OUTPUT_DIR):
|
|
49
|
+
_d.mkdir(parents=True, exist_ok=True)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# --------------------------------------------------------------------------
|
|
53
|
+
# Opt-in gate
|
|
54
|
+
# --------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def release_tests_enabled() -> bool:
|
|
58
|
+
return os.environ.get(RELEASE_ENV_FLAG, "") == "1"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def require_release_enabled() -> None:
|
|
62
|
+
"""Call at the top of every release test / fixture.
|
|
63
|
+
|
|
64
|
+
Keeps these tests from ever running by accident (plain `pytest tests/`,
|
|
65
|
+
an IDE "run all tests" button, a CI job someone forgot to scope) even
|
|
66
|
+
though pytest can discover them. The flat `pytest tests/test_*.py`
|
|
67
|
+
command from AGENTS.md never reaches this directory in the first place
|
|
68
|
+
since it's a shell glob, not a recursive pattern — this is the second,
|
|
69
|
+
explicit line of defense for anyone running `pytest tests/` directly.
|
|
70
|
+
"""
|
|
71
|
+
if not release_tests_enabled():
|
|
72
|
+
pytest.skip(
|
|
73
|
+
f"Release verification tests are opt-in. "
|
|
74
|
+
f"Set {RELEASE_ENV_FLAG}=1 to run them (see tests/release/README.md). "
|
|
75
|
+
f"They download real datasets and run real training; do not enable "
|
|
76
|
+
f"them in routine CI."
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# --------------------------------------------------------------------------
|
|
81
|
+
# Tunables (env-overridable so a maintainer can scale a run up or down)
|
|
82
|
+
# --------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _env_int(name: str, default: int) -> int:
|
|
86
|
+
raw = os.environ.get(name)
|
|
87
|
+
if raw is None or raw == "":
|
|
88
|
+
return default
|
|
89
|
+
return int(raw)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def epochs_for(default: int) -> int:
|
|
93
|
+
"""TRAIN.epochs override. OSL_RELEASE_EPOCHS=0 means 'keep config default'."""
|
|
94
|
+
value = _env_int("OSL_RELEASE_EPOCHS", default)
|
|
95
|
+
return default if value == 0 else value
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def max_items_for(env_name: str, default: int | None) -> int | None:
|
|
99
|
+
"""Generic dataset-subset-size override. 0 (or 'all') means 'download everything'."""
|
|
100
|
+
raw = os.environ.get(env_name)
|
|
101
|
+
if raw is None or raw == "":
|
|
102
|
+
return default
|
|
103
|
+
if raw.strip().lower() == "all":
|
|
104
|
+
return None
|
|
105
|
+
value = int(raw)
|
|
106
|
+
return None if value == 0 else value
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def hf_token() -> str | None:
|
|
110
|
+
return os.environ.get("HF_TOKEN") or os.environ.get("HUGGINGFACE_TOKEN")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def optional_module_available(module_name: str) -> bool:
|
|
114
|
+
try:
|
|
115
|
+
return importlib.util.find_spec(module_name) is not None
|
|
116
|
+
except (ImportError, ValueError):
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# --------------------------------------------------------------------------
|
|
121
|
+
# Hugging Face dataset access helpers
|
|
122
|
+
# --------------------------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _hf_api():
|
|
126
|
+
from huggingface_hub import HfApi
|
|
127
|
+
|
|
128
|
+
return HfApi(token=hf_token())
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def repo_accessible(repo_id: str, repo_type: str = "dataset") -> bool:
|
|
132
|
+
"""True if we can read repo metadata (i.e. it's public or we're authorized).
|
|
133
|
+
|
|
134
|
+
Distinguishes "gated/private, no access" (returns False -> caller should
|
|
135
|
+
skip) from genuine infrastructure problems (network down, HF outage),
|
|
136
|
+
which are re-raised so the test fails loudly instead of silently skipping.
|
|
137
|
+
"""
|
|
138
|
+
from huggingface_hub.utils import GatedRepoError, RepositoryNotFoundError
|
|
139
|
+
from requests.exceptions import ConnectionError as RequestsConnectionError
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
_hf_api().repo_info(repo_id, repo_type=repo_type)
|
|
143
|
+
return True
|
|
144
|
+
except (GatedRepoError, RepositoryNotFoundError):
|
|
145
|
+
return False
|
|
146
|
+
except RequestsConnectionError:
|
|
147
|
+
raise
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def require_repo_access(repo_id: str, repo_type: str = "dataset") -> None:
|
|
151
|
+
if not repo_accessible(repo_id, repo_type=repo_type):
|
|
152
|
+
pytest.skip(
|
|
153
|
+
f"No access to {repo_id!r} on Hugging Face. It may be gated — "
|
|
154
|
+
f"request access at https://huggingface.co/datasets/{repo_id} and "
|
|
155
|
+
f"export HF_TOKEN (or HUGGINGFACE_TOKEN) for an account that has "
|
|
156
|
+
f"been granted access, then re-run."
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def list_repo_files(repo_id: str, repo_type: str = "dataset") -> list[str]:
|
|
161
|
+
"""List a repo's files with a single cheap API call -- use this to find
|
|
162
|
+
what you actually need (e.g. small annotation JSONs) before downloading
|
|
163
|
+
anything, rather than snapshot-downloading an entire many-file repo.
|
|
164
|
+
"""
|
|
165
|
+
return list(_hf_api().list_repo_files(repo_id, repo_type=repo_type))
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def repo_is_populated(repo_id: str, repo_type: str = "dataset", min_files: int = 2, revision: str = "main") -> bool:
|
|
169
|
+
"""Some OpenSportsLab dataset repos have nothing but a placeholder (just
|
|
170
|
+
a .gitattributes / README) on their default branch while the real data
|
|
171
|
+
lives on a named branch (see prefer_osl_ready_dataset() below) -- always
|
|
172
|
+
pass the branch you actually intend to read from. Treat a repo/revision
|
|
173
|
+
with no real files as "not yet available" rather than crashing on an
|
|
174
|
+
empty split.
|
|
175
|
+
"""
|
|
176
|
+
files = _hf_api().list_repo_files(repo_id, repo_type=repo_type, revision=revision)
|
|
177
|
+
real_files = [f for f in files if f not in (".gitattributes", "README.md")]
|
|
178
|
+
return len(real_files) >= min_files
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def require_repo_populated(repo_id: str, repo_type: str = "dataset", min_files: int = 2, revision: str = "main") -> None:
|
|
182
|
+
if not repo_is_populated(repo_id, repo_type=repo_type, min_files=min_files, revision=revision):
|
|
183
|
+
pytest.skip(
|
|
184
|
+
f"{repo_id!r}@{revision} does not have data uploaded yet. This "
|
|
185
|
+
f"test is ready to run as soon as the dataset/branch is "
|
|
186
|
+
f"published — re-run once it is."
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
# --------------------------------------------------------------------------
|
|
191
|
+
# OSL-ready (sharded) datasets
|
|
192
|
+
#
|
|
193
|
+
# https://huggingface.co/collections/OpenSportsLab/osl-ready-datasets pins
|
|
194
|
+
# the datasets the org publishes as parquet + webdataset shards (a handful
|
|
195
|
+
# of large files per split) rather than one file per clip/game -- always
|
|
196
|
+
# prefer these when they're populated. Use prefer_osl_ready_dataset() to try
|
|
197
|
+
# a collection dataset first and fall back to a known-good loose-file
|
|
198
|
+
# dataset only while the sharded one isn't published yet.
|
|
199
|
+
#
|
|
200
|
+
# Important: for every OSL-ready repo observed so far, the *default* ("main")
|
|
201
|
+
# branch is an empty placeholder -- the actual shards live on named branches
|
|
202
|
+
# (e.g. "224p", "720p", "ResNET_PCA512", "224p-2024"). Always pass the
|
|
203
|
+
# specific revision you want; there is no sensible repo-wide default.
|
|
204
|
+
# --------------------------------------------------------------------------
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def download_shard_split(repo_id: str, split: str, output_dir: Path, *, revision: str) -> Path:
|
|
208
|
+
"""Download one split of a parquet/webdataset-shard OSL dataset and
|
|
209
|
+
convert it to a local OSL v2 JSON with media extracted alongside it.
|
|
210
|
+
|
|
211
|
+
Thin wrapper around opensportslib.tools.hf_transfer.
|
|
212
|
+
download_dataset_split_from_hf(..., download_format="parquet"), which
|
|
213
|
+
itself does snapshot_download(allow_patterns=[f"{split}/*"]) -- for a
|
|
214
|
+
sharded dataset that's a handful of `metadata.parquet` /
|
|
215
|
+
`shard_manifest.parquet` / `shards/shard-*.tar` files (see
|
|
216
|
+
opensportslib/tools/parquet_to_osl_json.py for the exact expected
|
|
217
|
+
layout), not one request per sample, regardless of how many samples the
|
|
218
|
+
split has.
|
|
219
|
+
"""
|
|
220
|
+
from opensportslib.tools.hf_transfer import download_dataset_split_from_hf
|
|
221
|
+
|
|
222
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
223
|
+
result = download_dataset_split_from_hf(
|
|
224
|
+
repo_id,
|
|
225
|
+
revision,
|
|
226
|
+
split,
|
|
227
|
+
str(output_dir),
|
|
228
|
+
download_format="parquet",
|
|
229
|
+
token=hf_token(),
|
|
230
|
+
progress_cb=lambda msg: report_step(f"[{repo_id}@{revision}:{split}] {msg}"),
|
|
231
|
+
)
|
|
232
|
+
return Path(result["json_path"])
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def prefer_osl_ready_dataset(
|
|
236
|
+
primary: str, fallback: str | None, *, primary_revision: str
|
|
237
|
+
) -> tuple[str, str, bool]:
|
|
238
|
+
"""Prefer `primary`@`primary_revision` (an OSL-ready/sharded dataset
|
|
239
|
+
branch) if it's populated; otherwise fall back to `fallback` (a
|
|
240
|
+
known-good, currently-populated but non-sharded dataset on its default
|
|
241
|
+
branch) and say why. Returns (repo_id, revision, is_sharded).
|
|
242
|
+
|
|
243
|
+
If `fallback` is None and `primary` isn't populated, skips the test --
|
|
244
|
+
use this when there's no non-sharded alternative worth falling back to.
|
|
245
|
+
"""
|
|
246
|
+
if repo_is_populated(primary, revision=primary_revision):
|
|
247
|
+
return primary, primary_revision, True
|
|
248
|
+
if fallback is None:
|
|
249
|
+
require_repo_populated(primary, revision=primary_revision) # raises pytest.skip
|
|
250
|
+
report_step(
|
|
251
|
+
f"{primary!r}@{primary_revision} (OSL-ready/sharded) is not populated "
|
|
252
|
+
f"yet -- falling back to {fallback!r}. Re-run once {primary!r}@"
|
|
253
|
+
f"{primary_revision} is published to automatically switch to the "
|
|
254
|
+
f"sharded version."
|
|
255
|
+
)
|
|
256
|
+
return fallback, "main", False
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def snapshot_dataset(
|
|
260
|
+
repo_id: str,
|
|
261
|
+
local_dir: Path,
|
|
262
|
+
*,
|
|
263
|
+
allow_patterns: list[str] | None = None,
|
|
264
|
+
) -> Path:
|
|
265
|
+
"""Download (or update) a full dataset repo, or a pattern-restricted
|
|
266
|
+
subset of it, into local_dir. Safe to call repeatedly (resumable)."""
|
|
267
|
+
from huggingface_hub import snapshot_download
|
|
268
|
+
|
|
269
|
+
local_dir.mkdir(parents=True, exist_ok=True)
|
|
270
|
+
snapshot_download(
|
|
271
|
+
repo_id=repo_id,
|
|
272
|
+
repo_type="dataset",
|
|
273
|
+
local_dir=str(local_dir),
|
|
274
|
+
token=hf_token(),
|
|
275
|
+
allow_patterns=allow_patterns,
|
|
276
|
+
)
|
|
277
|
+
return local_dir
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def download_files(repo_id: str, filenames: list[str], local_dir: Path) -> list[Path]:
|
|
281
|
+
from huggingface_hub import hf_hub_download
|
|
282
|
+
|
|
283
|
+
local_dir.mkdir(parents=True, exist_ok=True)
|
|
284
|
+
out = []
|
|
285
|
+
for filename in filenames:
|
|
286
|
+
path = hf_hub_download(
|
|
287
|
+
repo_id=repo_id,
|
|
288
|
+
repo_type="dataset",
|
|
289
|
+
filename=filename,
|
|
290
|
+
local_dir=str(local_dir),
|
|
291
|
+
token=hf_token(),
|
|
292
|
+
)
|
|
293
|
+
out.append(Path(path))
|
|
294
|
+
return out
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
# --------------------------------------------------------------------------
|
|
298
|
+
# Canonical config materialization
|
|
299
|
+
#
|
|
300
|
+
# opensportslib/configs/<task>/<name>.yaml files are NOT self-contained: the
|
|
301
|
+
# library layers them on top of opensportslib/configs/default.yaml and
|
|
302
|
+
# opensportslib/configs/<task>/default.yaml at load time (see
|
|
303
|
+
# opensportslib/core/config/loader.py::_compose_yaml_layers), but only when
|
|
304
|
+
# the path is physically inside opensportslib/configs/<task>/. We reuse the
|
|
305
|
+
# library's own composer to get the real, maintained defaults, apply our
|
|
306
|
+
# dataset/run overrides on top in Python, then write the fully-resolved
|
|
307
|
+
# result out to CONFIG_DIR (outside opensportslib/configs/) so it loads as a
|
|
308
|
+
# plain standalone config.
|
|
309
|
+
# --------------------------------------------------------------------------
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _deep_merge(base: Any, override: Any) -> Any:
|
|
313
|
+
if not isinstance(base, dict) or not isinstance(override, dict):
|
|
314
|
+
return deepcopy(override)
|
|
315
|
+
merged = deepcopy(base)
|
|
316
|
+
for key, value in override.items():
|
|
317
|
+
merged[key] = _deep_merge(merged.get(key), value) if key in merged else deepcopy(value)
|
|
318
|
+
return merged
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def materialize_config(task: str, name: str, overrides: dict, *, out_name: str | None = None) -> str:
|
|
322
|
+
"""Load opensportslib/configs/<task>/<name>.yaml with its real defaults
|
|
323
|
+
applied, deep-merge `overrides` on top, write the result under
|
|
324
|
+
CONFIG_DIR, and return the path to the materialized file.
|
|
325
|
+
"""
|
|
326
|
+
from opensportslib.core.config import load_config, save_config
|
|
327
|
+
|
|
328
|
+
canonical_path = REPO_ROOT / "opensportslib" / "configs" / task / f"{name}.yaml"
|
|
329
|
+
if not canonical_path.is_file():
|
|
330
|
+
raise FileNotFoundError(f"No such canonical config: {canonical_path}")
|
|
331
|
+
|
|
332
|
+
base = load_config(str(canonical_path), as_namespace=False, validate=False)
|
|
333
|
+
merged = _deep_merge(base, overrides)
|
|
334
|
+
|
|
335
|
+
out_path = CONFIG_DIR / (out_name or f"{task}_{name}.yaml")
|
|
336
|
+
save_config(merged, out_path)
|
|
337
|
+
return str(out_path)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def system_block(run_name: str, *, gpu_count: int = 1) -> dict:
|
|
341
|
+
"""Standard SYSTEM override pointing checkpoints/logs at OUTPUT_DIR."""
|
|
342
|
+
run_dir = OUTPUT_DIR / run_name
|
|
343
|
+
return {
|
|
344
|
+
"SYSTEM": {
|
|
345
|
+
"paths": {
|
|
346
|
+
"save_dir": str(run_dir / "checkpoints"),
|
|
347
|
+
"work_dir": str(run_dir),
|
|
348
|
+
"log_dir": str(run_dir / "logs"),
|
|
349
|
+
},
|
|
350
|
+
"device": "auto",
|
|
351
|
+
"gpu": {"count": gpu_count, "id": 0},
|
|
352
|
+
"reproducibility": {"use_seed": True, "seed": 0},
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def report_step(label: str) -> None:
|
|
358
|
+
print(f"\n=== {label} ===", flush=True)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def classes_from_osl_json(payload: dict, *, head: str | None = None) -> list[str]:
|
|
362
|
+
"""Extract a single_label head's class list from an OSL v2 JSON payload.
|
|
363
|
+
Defaults to the first label head found (fine for datasets with exactly
|
|
364
|
+
one classification/event head, which is the common case here); pass
|
|
365
|
+
`head` explicitly for multi-head datasets (e.g. OSL-XFoul's `action` +
|
|
366
|
+
`offence`).
|
|
367
|
+
"""
|
|
368
|
+
labels = payload.get("labels") or {}
|
|
369
|
+
if not labels:
|
|
370
|
+
raise ValueError("No 'labels' block in this OSL v2 payload.")
|
|
371
|
+
key = head or next(iter(labels))
|
|
372
|
+
return list(labels[key]["labels"])
|