opensportslib 0.3.1.dev27__tar.gz → 0.3.2__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.1.dev27/opensportslib.egg-info → opensportslib-0.3.2}/PKG-INFO +2 -8
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/README.md +1 -7
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2/opensportslib.egg-info}/PKG-INFO +2 -8
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib.egg-info/SOURCES.txt +1 -8
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/pyproject.toml +2 -2
- opensportslib-0.3.2/tests/unit/api/test_server_jobs.py +63 -0
- opensportslib-0.3.2/tests/unit/api/test_server_registry.py +131 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/contracts/test_package.py +2 -3
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/core/test_classification_trainer.py +0 -90
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/data/test_localization_intervals.py +1 -45
- opensportslib-0.3.2/tests/unit/models/test_localization_metrics.py +25 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/models/test_vqa_qwen_xvars.py +103 -1
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/tools/test_hf_transfer.py +0 -178
- opensportslib-0.3.1.dev27/tests/helpers/causal_lm.py +0 -110
- opensportslib-0.3.1.dev27/tests/unit/api/test_server_jobs.py +0 -121
- opensportslib-0.3.1.dev27/tests/unit/api/test_server_registry.py +0 -314
- opensportslib-0.3.1.dev27/tests/unit/core/test_hf_runtime.py +0 -159
- opensportslib-0.3.1.dev27/tests/unit/models/test_localization_metrics.py +0 -129
- opensportslib-0.3.1.dev27/tests/unit/tools/test_spotting_classification.py +0 -489
- opensportslib-0.3.1.dev27/tools/convert/spotting_to_classification.py +0 -902
- opensportslib-0.3.1.dev27/tools/convert/spotting_tracking_to_classification.py +0 -414
- opensportslib-0.3.1.dev27/tools/upload/upload_sngar_tracking_full.py +0 -107
- opensportslib-0.3.1.dev27/tools/upload/upload_sngar_videos.py +0 -130
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/LICENSE +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/LICENSE-COMMERCIAL +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/MANIFEST.in +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/examples/quickstart/basic_classification.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/examples/quickstart/basic_localization.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/examples/quickstart/basic_vqa.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/adaptation/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/adaptation/spotta.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/apis/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/apis/base_task_model.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/apis/classification.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/apis/config.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/apis/configuration.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/apis/localization.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/apis/vqa.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/cli.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/classification/default.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/classification/sngar_frames.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/classification/sngar_tracking.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/classification/video.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/default.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/localization/calf_resnetpca512.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/localization/default.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/localization/e2e_spotta.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/localization/h5_header_distance.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/localization/h5_header_skeleton.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/localization/netvladpp_resnetpca512.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/localization/tracking_action_spotting.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/localization/video_dali.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/localization/video_ocv.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/vqa/default.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/vqa/qwen.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/vqa/qwen3_vl_native.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/vqa/qwen_lora.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/vqa/qwen_sngar_frames.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/vqa/xvars.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/accessors.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/conflicts.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/editable.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/loader.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/migrate.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/migrations/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/migrations/legacy_to_canonical.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/rule_variants.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/runtime_adapter.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/schema.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/schemas/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/schemas/schema_canonical.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/schemas/schema_legacy.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/validate.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/loss/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/loss/builder.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/loss/calf.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/loss/ce.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/loss/combine.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/loss/nll.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/optimizer/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/optimizer/builder.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/sampler/weighted_sampler.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/scheduler/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/scheduler/builder.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/trainer/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/trainer/classification_trainer.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/trainer/localization_trainer.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/trainer/vqa_trainer.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/checkpoint.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/config.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/config_normalize.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/data.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/ddp.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/default_args.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/direct_video.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/hf_runtime.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/lightning.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/load_annotations.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/seed.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/video_processing.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/wandb.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/builder.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/classification_dataset.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/hf_json.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/hf_tracking.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/localization_dataset.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/utils/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/utils/h5_tracking.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/utils/tracking.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/vqa_dataset.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/legacy_config/classification.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/legacy_config/localization-e2e-ocv.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/legacy_config/localization-json_calf_resnetpca512.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/legacy_config/localization-json_netvlad++_resnetpca512.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/legacy_config/localization.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/legacy_config/sngar-frames.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/legacy_config/sngar-tracking.yaml +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/metrics/classification_metric.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/metrics/localization_metric.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/metrics/vqa_metric.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/backbones/builder.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/contextaware.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/e2e.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/learnablepooling.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/qwen_vl_native.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/qwen_xvars.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/rule_based.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/tracking.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/vars.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/video.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/video_chatgpt_compat.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/video_mae.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/xvars_videochatgpt.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/builder.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/heads/builder.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/neck/builder.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/common.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/impl/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/impl/asformer.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/impl/calf.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/impl/gsm.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/impl/gtad.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/impl/tsm.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/litebase.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/modules.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/shift.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/utils.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/vqa_prediction_priors.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/vqa_prompting.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/xvars_clip_index.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/remote_registry.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/setup/setup.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/tools/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/tools/_common.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/tools/hf_transfer.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/tools/osl_json_to_parquet.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/tools/parquet_to_osl_json.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib.egg-info/dependency_links.txt +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib.egg-info/entry_points.txt +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib.egg-info/requires.txt +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib.egg-info/top_level.txt +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/scripts/redact_test_stream.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/scripts/release_versions.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/scripts/run_h5_header_rule_inference.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/scripts/summarize_test_report.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/scripts/verify_release_manifests.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/scripts/write_release_manifest.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/setup.cfg +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/conftest.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/helpers/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/helpers/configs.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/helpers/pipeline_stubs.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/integration/classification/test_pipeline.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/integration/localization/test_pipeline.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/integration/localization/test_spotta.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/integration/vqa/test_pipeline.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/release/__init__.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/release/_release_common.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/release/test_00_environment.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/release/test_classification_release.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/release/test_localization_release.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/release/test_vqa_release.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/smoke/test_cli.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/smoke/test_config_utils.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/smoke/test_environment.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/smoke/test_package.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/smoke/test_public_apis.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/api/test_remote_registry.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/api/test_remote_sessions.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/api/test_server_startup_scripts.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/api/test_task_model_contract.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/api/test_vqa.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/config/test_architecture.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/config/test_editable.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/config/test_optional_hf.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/config/test_pretrained_merge.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/config/test_split_overrides.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/contracts/test_architecture.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/contracts/test_dependencies.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/contracts/test_release_versions.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/contracts/test_runner_contract.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/core/test_localization_hf_backend.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/core/test_training_components.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/core/test_video_processing.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/data/test_classification_paths.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/data/test_h5_tracking.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/data/test_hf_json_spotting.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/data/test_hf_json_tracking_spotting.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/data/test_hf_tracking_dataset.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/data/test_localization_dali.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/data/test_osl_formats.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/models/test_classification_metrics.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/models/test_header_rule_spotter.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/models/test_header_skeleton_spotter.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/models/test_temporal_shift.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/models/test_vqa_metrics.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/models/test_vqa_training_lora.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/models/test_vqa_xvars_videochatgpt.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/tools/test_conversion.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/tools/test_extract_xvars_features.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/tools/test_release_manifest.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/tools/test_report_summary.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/tools/test_setup.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/build_sn_vqa_2026_vqa.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/build_sngar_spotting.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/build_soccernet_gar.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/build_soccernet_gar_action_spotting.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/build_soccernet_gar_vqa.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/build_xvars_indexes.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/extract_xvars_clip_features.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/osl_json_to_parquet_webdataset.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/parquet_webdataset_to_osl_json.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/sngar_dataset_card.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/sngar_events.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/verify_sngar_spotting.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/download/download_hf_repo.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/download/download_osl_hf.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/download/push_sngar_spotting.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/download/upload_osl_hf.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/train/train_config.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/training/classification.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/training/localization.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/training/vqa.py +0 -0
- {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/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.
|
|
3
|
+
Version: 0.3.2
|
|
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
|
|
@@ -67,7 +67,7 @@ OpenSportsLib is designed for **researchers, ML engineers, and sports analytics
|
|
|
67
67
|
|
|
68
68
|
- **Documentation:** https://opensportslab.github.io/opensportslib/
|
|
69
69
|
- **OSL JSON format:** https://opensportslab.github.io/opensportslib/data/osl-json-format/
|
|
70
|
-
- **Inference server:** [server/README.md](server/README.md)
|
|
70
|
+
- **Inference server:** [server/README.md](server/README.md)
|
|
71
71
|
- **PyPI:** https://pypi.org/project/opensportslib/
|
|
72
72
|
- **Issues:** https://github.com/OpenSportsLab/opensportslib/issues
|
|
73
73
|
|
|
@@ -324,9 +324,3 @@ video understanding. Core contributors affiliated with KAUST include:
|
|
|
324
324
|
- [Jeet Vora](https://jeetv.github.io/) — Remote Research Engineer
|
|
325
325
|
- [Dr. Merey Ramazanova](https://meryusha.github.io/) — Post-Doc
|
|
326
326
|
- [Dr. Silvio Giancola](https://www.silviogiancola.com/) — Research Scientist
|
|
327
|
-
|
|
328
|
-
For synchronized native-FPS GAR action clips and optional exact frame reproduction, see the
|
|
329
|
-
[spotting-to-classification converter](docs/tools/spotting-to-classification.md).
|
|
330
|
-
|
|
331
|
-
For full-rate tracking clips with the same classification sample IDs, see the
|
|
332
|
-
[tracking conversion script](docs/tools/spotting-tracking-to-classification.md).
|
|
@@ -28,7 +28,7 @@ OpenSportsLib is designed for **researchers, ML engineers, and sports analytics
|
|
|
28
28
|
|
|
29
29
|
- **Documentation:** https://opensportslab.github.io/opensportslib/
|
|
30
30
|
- **OSL JSON format:** https://opensportslab.github.io/opensportslib/data/osl-json-format/
|
|
31
|
-
- **Inference server:** [server/README.md](server/README.md)
|
|
31
|
+
- **Inference server:** [server/README.md](server/README.md)
|
|
32
32
|
- **PyPI:** https://pypi.org/project/opensportslib/
|
|
33
33
|
- **Issues:** https://github.com/OpenSportsLab/opensportslib/issues
|
|
34
34
|
|
|
@@ -285,9 +285,3 @@ video understanding. Core contributors affiliated with KAUST include:
|
|
|
285
285
|
- [Jeet Vora](https://jeetv.github.io/) — Remote Research Engineer
|
|
286
286
|
- [Dr. Merey Ramazanova](https://meryusha.github.io/) — Post-Doc
|
|
287
287
|
- [Dr. Silvio Giancola](https://www.silviogiancola.com/) — Research Scientist
|
|
288
|
-
|
|
289
|
-
For synchronized native-FPS GAR action clips and optional exact frame reproduction, see the
|
|
290
|
-
[spotting-to-classification converter](docs/tools/spotting-to-classification.md).
|
|
291
|
-
|
|
292
|
-
For full-rate tracking clips with the same classification sample IDs, see the
|
|
293
|
-
[tracking conversion script](docs/tools/spotting-tracking-to-classification.md).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opensportslib
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
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
|
|
@@ -67,7 +67,7 @@ OpenSportsLib is designed for **researchers, ML engineers, and sports analytics
|
|
|
67
67
|
|
|
68
68
|
- **Documentation:** https://opensportslab.github.io/opensportslib/
|
|
69
69
|
- **OSL JSON format:** https://opensportslab.github.io/opensportslib/data/osl-json-format/
|
|
70
|
-
- **Inference server:** [server/README.md](server/README.md)
|
|
70
|
+
- **Inference server:** [server/README.md](server/README.md)
|
|
71
71
|
- **PyPI:** https://pypi.org/project/opensportslib/
|
|
72
72
|
- **Issues:** https://github.com/OpenSportsLab/opensportslib/issues
|
|
73
73
|
|
|
@@ -324,9 +324,3 @@ video understanding. Core contributors affiliated with KAUST include:
|
|
|
324
324
|
- [Jeet Vora](https://jeetv.github.io/) — Remote Research Engineer
|
|
325
325
|
- [Dr. Merey Ramazanova](https://meryusha.github.io/) — Post-Doc
|
|
326
326
|
- [Dr. Silvio Giancola](https://www.silviogiancola.com/) — Research Scientist
|
|
327
|
-
|
|
328
|
-
For synchronized native-FPS GAR action clips and optional exact frame reproduction, see the
|
|
329
|
-
[spotting-to-classification converter](docs/tools/spotting-to-classification.md).
|
|
330
|
-
|
|
331
|
-
For full-rate tracking clips with the same classification sample IDs, see the
|
|
332
|
-
[tracking conversion script](docs/tools/spotting-tracking-to-classification.md).
|
|
@@ -154,7 +154,6 @@ scripts/write_release_manifest.py
|
|
|
154
154
|
tests/__init__.py
|
|
155
155
|
tests/conftest.py
|
|
156
156
|
tests/helpers/__init__.py
|
|
157
|
-
tests/helpers/causal_lm.py
|
|
158
157
|
tests/helpers/configs.py
|
|
159
158
|
tests/helpers/pipeline_stubs.py
|
|
160
159
|
tests/integration/classification/test_pipeline.py
|
|
@@ -190,7 +189,6 @@ tests/unit/contracts/test_package.py
|
|
|
190
189
|
tests/unit/contracts/test_release_versions.py
|
|
191
190
|
tests/unit/contracts/test_runner_contract.py
|
|
192
191
|
tests/unit/core/test_classification_trainer.py
|
|
193
|
-
tests/unit/core/test_hf_runtime.py
|
|
194
192
|
tests/unit/core/test_localization_hf_backend.py
|
|
195
193
|
tests/unit/core/test_training_components.py
|
|
196
194
|
tests/unit/core/test_video_processing.py
|
|
@@ -217,7 +215,6 @@ tests/unit/tools/test_hf_transfer.py
|
|
|
217
215
|
tests/unit/tools/test_release_manifest.py
|
|
218
216
|
tests/unit/tools/test_report_summary.py
|
|
219
217
|
tests/unit/tools/test_setup.py
|
|
220
|
-
tests/unit/tools/test_spotting_classification.py
|
|
221
218
|
tools/convert/build_sn_vqa_2026_vqa.py
|
|
222
219
|
tools/convert/build_sngar_spotting.py
|
|
223
220
|
tools/convert/build_soccernet_gar.py
|
|
@@ -229,8 +226,6 @@ tools/convert/osl_json_to_parquet_webdataset.py
|
|
|
229
226
|
tools/convert/parquet_webdataset_to_osl_json.py
|
|
230
227
|
tools/convert/sngar_dataset_card.py
|
|
231
228
|
tools/convert/sngar_events.py
|
|
232
|
-
tools/convert/spotting_to_classification.py
|
|
233
|
-
tools/convert/spotting_tracking_to_classification.py
|
|
234
229
|
tools/convert/verify_sngar_spotting.py
|
|
235
230
|
tools/download/download_hf_repo.py
|
|
236
231
|
tools/download/download_osl_hf.py
|
|
@@ -240,6 +235,4 @@ tools/train/train_config.py
|
|
|
240
235
|
tools/training/classification.py
|
|
241
236
|
tools/training/localization.py
|
|
242
237
|
tools/training/vqa.py
|
|
243
|
-
tools/upload/upload_model_hf.py
|
|
244
|
-
tools/upload/upload_sngar_tracking_full.py
|
|
245
|
-
tools/upload/upload_sngar_videos.py
|
|
238
|
+
tools/upload/upload_model_hf.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "opensportslib"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.2"
|
|
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"
|
|
@@ -22,7 +22,7 @@ opensportslib = "opensportslib.cli:main"
|
|
|
22
22
|
opensportslib = [ "legacy_config/*.yaml", "configs/**/*.yaml",]
|
|
23
23
|
|
|
24
24
|
[tool.coverage.run]
|
|
25
|
-
source = [ "opensportslib",
|
|
25
|
+
source = [ "opensportslib",]
|
|
26
26
|
branch = true
|
|
27
27
|
omit = [ "*/.ipynb_checkpoints/*",]
|
|
28
28
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from types import SimpleNamespace
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
SERVER_ROOT = Path(__file__).resolve().parents[3] / "server"
|
|
9
|
+
sys.path.insert(0, str(SERVER_ROOT))
|
|
10
|
+
|
|
11
|
+
from app.schemas import JobStatus, PredictRequest # noqa: E402
|
|
12
|
+
from storage.jobs import JobStore # noqa: E402
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def store(tmp_path):
|
|
16
|
+
runtime = tmp_path / "runtime"
|
|
17
|
+
settings = SimpleNamespace(
|
|
18
|
+
runtime_dir=runtime,
|
|
19
|
+
tmp_dir=runtime / "tmp",
|
|
20
|
+
jobs_dir=runtime / "jobs",
|
|
21
|
+
results_dir=runtime / "results",
|
|
22
|
+
)
|
|
23
|
+
settings.jobs_dir.mkdir(parents=True)
|
|
24
|
+
settings.tmp_dir.mkdir(parents=True)
|
|
25
|
+
settings.results_dir.mkdir(parents=True)
|
|
26
|
+
return JobStore(settings)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def request():
|
|
30
|
+
return PredictRequest(task_type="classification", video_path="/tmp/clip.mp4")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_metadata_is_valid_json_after_create(tmp_path):
|
|
34
|
+
jobs = store(tmp_path)
|
|
35
|
+
metadata = jobs.create_job(request(), "model")
|
|
36
|
+
|
|
37
|
+
loaded = jobs.load_metadata(metadata.job_id)
|
|
38
|
+
assert loaded is not None
|
|
39
|
+
assert loaded.status is JobStatus.QUEUED
|
|
40
|
+
assert jobs.metadata_path(metadata.job_id).read_text(encoding="utf-8").strip()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_metadata_replacement_never_leaves_partial_file(tmp_path):
|
|
44
|
+
jobs = store(tmp_path)
|
|
45
|
+
metadata = jobs.create_job(request(), "model")
|
|
46
|
+
|
|
47
|
+
for status in (JobStatus.RUNNING, JobStatus.SUCCEEDED, JobStatus.FAILED):
|
|
48
|
+
metadata.status = status
|
|
49
|
+
jobs.save_metadata(metadata)
|
|
50
|
+
loaded = jobs.load_metadata(metadata.job_id)
|
|
51
|
+
assert loaded is not None
|
|
52
|
+
assert loaded.status is status
|
|
53
|
+
assert not list(jobs.settings.jobs_dir.glob(f".{metadata.job_id}.*.tmp"))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_invalid_metadata_is_treated_as_missing(tmp_path):
|
|
57
|
+
jobs = store(tmp_path)
|
|
58
|
+
path = jobs.metadata_path("broken")
|
|
59
|
+
path.write_text("", encoding="utf-8")
|
|
60
|
+
|
|
61
|
+
assert jobs.load_metadata("broken") is None
|
|
62
|
+
with pytest.raises(FileNotFoundError, match="broken"):
|
|
63
|
+
jobs.mark_running("broken")
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from types import SimpleNamespace
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
SERVER_ROOT = Path(__file__).parents[3] / "server"
|
|
9
|
+
sys.path.insert(0, str(SERVER_ROOT))
|
|
10
|
+
|
|
11
|
+
from config.model_registry import ( # noqa: E402
|
|
12
|
+
ModelRegistry,
|
|
13
|
+
generated_local_model_id,
|
|
14
|
+
public_record,
|
|
15
|
+
resolve_local_source,
|
|
16
|
+
infer_source_mode,
|
|
17
|
+
validate_local_model_id,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class FakeRedis:
|
|
22
|
+
def __init__(self):
|
|
23
|
+
self.hashes = {}
|
|
24
|
+
|
|
25
|
+
def hget(self, key, field):
|
|
26
|
+
return self.hashes.get(key, {}).get(field)
|
|
27
|
+
|
|
28
|
+
def hvals(self, key):
|
|
29
|
+
return list(self.hashes.get(key, {}).values())
|
|
30
|
+
|
|
31
|
+
def hset(self, key, field, value):
|
|
32
|
+
self.hashes.setdefault(key, {})[field] = value
|
|
33
|
+
|
|
34
|
+
def hsetnx(self, key, field, value):
|
|
35
|
+
values = self.hashes.setdefault(key, {})
|
|
36
|
+
if field in values:
|
|
37
|
+
return 0
|
|
38
|
+
values[field] = value
|
|
39
|
+
return 1
|
|
40
|
+
|
|
41
|
+
def hdel(self, key, field):
|
|
42
|
+
self.hashes.get(key, {}).pop(field, None)
|
|
43
|
+
|
|
44
|
+
def hgetall(self, key):
|
|
45
|
+
return dict(self.hashes.get(key, {}))
|
|
46
|
+
|
|
47
|
+
def setex(self, key, ttl, value):
|
|
48
|
+
self.hashes.setdefault("strings", {})[key] = value
|
|
49
|
+
|
|
50
|
+
def getdel(self, key):
|
|
51
|
+
return self.hashes.setdefault("strings", {}).pop(key, None)
|
|
52
|
+
|
|
53
|
+
def delete(self, key):
|
|
54
|
+
self.hashes.setdefault("strings", {}).pop(key, None)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def record(model_id="model", source="org/model"):
|
|
58
|
+
return {
|
|
59
|
+
"model_id": model_id,
|
|
60
|
+
"task_type": "classification",
|
|
61
|
+
"source_type": "huggingface",
|
|
62
|
+
"source": source,
|
|
63
|
+
"config_path": None,
|
|
64
|
+
"status": "ready",
|
|
65
|
+
"generation": 1,
|
|
66
|
+
"created_at": "now",
|
|
67
|
+
"updated_at": "now",
|
|
68
|
+
"error": None,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_registry_reserves_idempotently_and_rejects_conflicts():
|
|
73
|
+
registry = ModelRegistry(SimpleNamespace(redis_url="redis://unused"), FakeRedis())
|
|
74
|
+
_, created = registry.reserve(record())
|
|
75
|
+
assert created
|
|
76
|
+
existing, created = registry.reserve(record())
|
|
77
|
+
assert not created
|
|
78
|
+
assert existing["source"] == "org/model"
|
|
79
|
+
with pytest.raises(FileExistsError):
|
|
80
|
+
registry.reserve(record(source="other/model"))
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_local_source_stays_inside_model_root(tmp_path):
|
|
84
|
+
root = tmp_path / "models"
|
|
85
|
+
folder = root / "custom"
|
|
86
|
+
folder.mkdir(parents=True)
|
|
87
|
+
(folder / "config.yaml").write_text("MODEL: {}")
|
|
88
|
+
settings = SimpleNamespace(model_root=root)
|
|
89
|
+
|
|
90
|
+
weights, config = resolve_local_source(settings, str(folder), None)
|
|
91
|
+
assert weights == folder
|
|
92
|
+
assert config == folder / "config.yaml"
|
|
93
|
+
assert generated_local_model_id(folder).startswith("local:custom-")
|
|
94
|
+
|
|
95
|
+
outside = tmp_path / "outside"
|
|
96
|
+
outside.mkdir()
|
|
97
|
+
with pytest.raises(ValueError, match="OSL_MODEL_ROOT"):
|
|
98
|
+
resolve_local_source(settings, str(outside), None)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_custom_id_and_public_local_record_validation():
|
|
102
|
+
assert validate_local_model_id("football-model:v2") == "football-model:v2"
|
|
103
|
+
with pytest.raises(ValueError):
|
|
104
|
+
validate_local_model_id("folder/model")
|
|
105
|
+
local = record()
|
|
106
|
+
local.update({"source_type": "local", "source": "/secret/model", "config_path": "/secret/config", "error": "at /secret/model"})
|
|
107
|
+
visible = public_record(local)
|
|
108
|
+
assert "source" not in visible
|
|
109
|
+
assert "config_path" not in visible
|
|
110
|
+
assert "/secret" not in visible["error"]
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def test_config_driven_runner_is_weightless(tmp_path):
|
|
114
|
+
config = tmp_path / "config.yaml"
|
|
115
|
+
config.write_text("TRAIN:\n runner:\n type: runner_h5_header_rule\n")
|
|
116
|
+
assert infer_source_mode(str(config), "org/config-only") == "config_only"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_ordinary_config_requires_checkpoint(tmp_path):
|
|
120
|
+
config = tmp_path / "config.yaml"
|
|
121
|
+
config.write_text("TRAIN:\n runner:\n type: default\n")
|
|
122
|
+
assert infer_source_mode(str(config), "org/model") == "checkpoint"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def test_operation_secret_is_consumed_and_not_part_of_records():
|
|
126
|
+
settings = SimpleNamespace(redis_url="redis://unused", model_operation_timeout_seconds=60)
|
|
127
|
+
registry = ModelRegistry(settings, FakeRedis())
|
|
128
|
+
registry.store_operation_secret("operation", "hf_secret")
|
|
129
|
+
assert registry.consume_operation_secret("operation") == "hf_secret"
|
|
130
|
+
assert registry.consume_operation_secret("operation") is None
|
|
131
|
+
assert registry.list() == []
|
|
@@ -47,9 +47,8 @@ def test_packaging_includes_all_yaml_presets():
|
|
|
47
47
|
assert experiment_config_paths(), f"No task presets found under {CONFIG_ROOT}"
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
raw = (REPOSITORY_ROOT / "scripts" / filename).read_text(encoding="utf-8").strip()
|
|
50
|
+
def test_coverage_baseline_is_a_valid_committed_percentage():
|
|
51
|
+
raw = (REPOSITORY_ROOT / "scripts" / "coverage-baseline.txt").read_text(encoding="utf-8").strip()
|
|
53
52
|
assert raw.isdigit(), "coverage-baseline.txt must contain one integer percentage"
|
|
54
53
|
assert 1 <= int(raw) <= 100
|
|
55
54
|
|
{opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/core/test_classification_trainer.py
RENAMED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from types import SimpleNamespace
|
|
2
2
|
|
|
3
|
-
import pytest
|
|
4
3
|
import torch
|
|
5
4
|
from omegaconf import OmegaConf
|
|
6
5
|
|
|
@@ -162,92 +161,3 @@ def test_video_train_loader_respects_explicit_context(monkeypatch):
|
|
|
162
161
|
assert len(dataloader_calls) == 2
|
|
163
162
|
assert (dataloader_calls[0]["multiprocessing_context"].get_start_method() == "forkserver")
|
|
164
163
|
assert (dataloader_calls[1]["multiprocessing_context"].get_start_method() == "forkserver")
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
@pytest.fixture
|
|
168
|
-
def cpu_trainer(tmp_path, monkeypatch):
|
|
169
|
-
from opensportslib.core.loss.builder import build_criterion
|
|
170
|
-
from torch.utils.data import DataLoader
|
|
171
|
-
|
|
172
|
-
monkeypatch.setattr(classification_trainer.wandb, "watch", lambda *args, **kwargs: None)
|
|
173
|
-
monkeypatch.setattr(classification_trainer.wandb, "run", None)
|
|
174
|
-
monkeypatch.setattr(classification_trainer, "log_confusion_matrix_wandb", lambda **kwargs: None)
|
|
175
|
-
data = [{"id": f"clip-{i}", "pixel_values": torch.eye(3)[i % 3], "labels": i % 3} for i in range(6)]
|
|
176
|
-
loader = DataLoader(data, batch_size=3, shuffle=False)
|
|
177
|
-
model = torch.nn.Linear(3, 3)
|
|
178
|
-
with torch.no_grad():
|
|
179
|
-
model.weight.copy_(torch.eye(3))
|
|
180
|
-
model.bias.zero_()
|
|
181
|
-
optimizer = torch.optim.SGD(model.parameters(), lr=.1, momentum=.9)
|
|
182
|
-
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=1, gamma=.5)
|
|
183
|
-
return classification_trainer.MVTrainerClassification(
|
|
184
|
-
train_loader=loader, val_loader=loader, test_loader=loader,
|
|
185
|
-
model=model, optimizer=optimizer, scheduler=scheduler,
|
|
186
|
-
criterion=build_criterion({"type": "CrossEntropyLoss"}),
|
|
187
|
-
class_weights=None, class_names={0: "PASS", 1: "SHOT", 2: "HEADER"},
|
|
188
|
-
save_dir=str(tmp_path), model_name="tiny", max_epochs=2, device="cpu",
|
|
189
|
-
monitor="loss", mode="min",
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
@pytest.mark.parametrize("weighted", [False, True])
|
|
194
|
-
def test_cpu_training_checkpoint_resumes_identical_next_update(cpu_trainer, weighted):
|
|
195
|
-
from opensportslib.core.utils.checkpoint import load_checkpoint
|
|
196
|
-
|
|
197
|
-
trainer = cpu_trainer
|
|
198
|
-
if weighted:
|
|
199
|
-
trainer.class_weights = torch.tensor([1., 2., 3.])
|
|
200
|
-
before = trainer.model.weight.detach().clone()
|
|
201
|
-
trainer.train()
|
|
202
|
-
assert not torch.equal(before, trainer.model.weight)
|
|
203
|
-
assert trainer.best_checkpoint_path is not None
|
|
204
|
-
restored = torch.nn.Linear(3, 3)
|
|
205
|
-
optimizer = torch.optim.SGD(restored.parameters(), lr=.9, momentum=.9)
|
|
206
|
-
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=1, gamma=.5)
|
|
207
|
-
_, _, _, _, epoch, checkpoint = load_checkpoint(
|
|
208
|
-
restored, trainer.best_checkpoint_path, optimizer=optimizer,
|
|
209
|
-
scheduler=scheduler, device=torch.device("cpu"),
|
|
210
|
-
)
|
|
211
|
-
assert epoch == 2
|
|
212
|
-
assert checkpoint["best_metric"] == pytest.approx(trainer.best_metric)
|
|
213
|
-
assert optimizer.param_groups[0]["lr"] == pytest.approx(.025)
|
|
214
|
-
assert scheduler.state_dict() == trainer.scheduler.state_dict()
|
|
215
|
-
assert optimizer.state # Momentum must be populated, not an empty-state round trip.
|
|
216
|
-
batch = next(iter(trainer.train_loader))
|
|
217
|
-
expected_before = trainer.model(batch["pixel_values"]).detach()
|
|
218
|
-
torch.testing.assert_close(restored(batch["pixel_values"]), expected_before)
|
|
219
|
-
trainer._process_batch(batch, train=True)
|
|
220
|
-
optimizer.zero_grad()
|
|
221
|
-
loss = trainer.criterion(output=restored(batch["pixel_values"]), labels=batch["labels"], weight=trainer.class_weights)
|
|
222
|
-
loss.backward()
|
|
223
|
-
torch.nn.utils.clip_grad_norm_(restored.parameters(), 1.)
|
|
224
|
-
optimizer.step()
|
|
225
|
-
for actual, expected in zip(restored.parameters(), trainer.model.parameters()):
|
|
226
|
-
torch.testing.assert_close(actual, expected)
|
|
227
|
-
test_loss, scores = trainer.test()
|
|
228
|
-
assert test_loss > 0 and scores["accuracy"] == 1.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
@pytest.mark.parametrize("labelled", [False, True])
|
|
232
|
-
def test_evaluation_keeps_weights_and_exports_sample_predictions(cpu_trainer, labelled):
|
|
233
|
-
trainer = cpu_trainer
|
|
234
|
-
batch = next(iter(trainer.test_loader))
|
|
235
|
-
if not labelled:
|
|
236
|
-
batch.pop("labels")
|
|
237
|
-
original = {name: value.clone() for name, value in trainer.model.state_dict().items()}
|
|
238
|
-
logits, labels, loss, scores = trainer._run_epoch([batch], epoch="check", set_name="test")
|
|
239
|
-
assert logits.shape == (3, 3)
|
|
240
|
-
assert len(labels) == (3 if labelled else 0)
|
|
241
|
-
assert loss > 0 if labelled else loss == 0
|
|
242
|
-
assert scores["accuracy"] == 1. if labelled else scores == {}
|
|
243
|
-
assert [item["id"] for item in trainer.predictions_payload["data"]] == batch["id"]
|
|
244
|
-
assert [item["labels"]["action"]["label"] for item in trainer.predictions_payload["data"]] == ["PASS", "SHOT", "HEADER"]
|
|
245
|
-
for name, value in trainer.model.state_dict().items():
|
|
246
|
-
torch.testing.assert_close(value, original[name])
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
def test_empty_evaluation_returns_empty_predictions(cpu_trainer):
|
|
250
|
-
logits, labels, loss, scores = cpu_trainer._run_epoch([], epoch="empty", set_name="test")
|
|
251
|
-
assert len(logits) == len(labels) == 0
|
|
252
|
-
assert loss == 0 and scores == {}
|
|
253
|
-
assert cpu_trainer.predictions_payload["data"] == []
|
{opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/data/test_localization_intervals.py
RENAMED
|
@@ -154,7 +154,6 @@ class _FrameCapture:
|
|
|
154
154
|
|
|
155
155
|
def __init__(self, *_):
|
|
156
156
|
self.position = 0
|
|
157
|
-
self.released = False
|
|
158
157
|
self.seek_positions = []
|
|
159
158
|
self.read_positions = []
|
|
160
159
|
self.grab_positions = []
|
|
@@ -187,7 +186,7 @@ class _FrameCapture:
|
|
|
187
186
|
return True
|
|
188
187
|
|
|
189
188
|
def release(self):
|
|
190
|
-
|
|
189
|
+
pass
|
|
191
190
|
|
|
192
191
|
|
|
193
192
|
def test_frame_reader_seeks_and_stops_inside_interval(monkeypatch):
|
|
@@ -405,46 +404,3 @@ def test_v2_evaluator_counts_missing_verified_interval_as_empty(tmp_path):
|
|
|
405
404
|
|
|
406
405
|
assert "Average mAP" in result
|
|
407
406
|
assert "Average mAP 54.55" in result
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
@pytest.fixture
|
|
411
|
-
def boundary_reader(monkeypatch):
|
|
412
|
-
class BrightCapture(_FrameCapture):
|
|
413
|
-
def read(self):
|
|
414
|
-
ok, frame = super().read()
|
|
415
|
-
if ok:
|
|
416
|
-
frame.fill(127)
|
|
417
|
-
return ok, frame
|
|
418
|
-
|
|
419
|
-
_FrameCapture.instances.clear()
|
|
420
|
-
monkeypatch.setattr(cv2, "VideoCapture", BrightCapture)
|
|
421
|
-
return FrameReader("rgb", crop_transform=None, img_transform=lambda frame: frame,
|
|
422
|
-
same_transform=False, sample_fps=2, TARGET_HEIGHT=2, TARGET_WIDTH=2)
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
@pytest.mark.parametrize("start,end,bounds,message", [
|
|
426
|
-
(2, 2, {}, "Invalid requested frame range"),
|
|
427
|
-
(3, 2, {}, "Invalid requested frame range"),
|
|
428
|
-
(0, 2, {"source_start_frame": 0}, "Both source"),
|
|
429
|
-
(0, 2, {"source_end_frame": 10}, "Both source"),
|
|
430
|
-
(0, 2, {"source_start_frame": -1, "source_end_frame": 10}, "Invalid source interval"),
|
|
431
|
-
(0, 2, {"source_start_frame": 10, "source_end_frame": 10}, "Invalid source interval"),
|
|
432
|
-
(0, 2, {"source_start_frame": 0, "source_end_frame": 101}, "Invalid source interval"),
|
|
433
|
-
(25, 27, {}, "No frames read"),
|
|
434
|
-
])
|
|
435
|
-
def test_frame_reader_rejects_invalid_or_empty_ranges_and_releases_capture(boundary_reader, start, end, bounds, message):
|
|
436
|
-
with pytest.raises(ValueError, match=message):
|
|
437
|
-
boundary_reader.load_frames_ocv("unused.mp4", start, end, **bounds)
|
|
438
|
-
assert _FrameCapture.instances[-1].released
|
|
439
|
-
assert not _FrameCapture.instances[-1].read_positions
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
@pytest.mark.parametrize("pad", [False, True])
|
|
443
|
-
def test_frame_reader_end_padding_preserves_real_samples(boundary_reader, pad):
|
|
444
|
-
frames = boundary_reader.load_frames_ocv("unused.mp4", 18, 23, pad=pad)
|
|
445
|
-
assert frames.shape == ((5 if pad else 2), 3, 2, 2)
|
|
446
|
-
assert frames[:2].count_nonzero() > 0
|
|
447
|
-
if pad:
|
|
448
|
-
assert frames[2:].count_nonzero() == 0
|
|
449
|
-
assert _FrameCapture.instances[-1].read_positions == [90, 95]
|
|
450
|
-
assert _FrameCapture.instances[-1].released
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Prediction-format contracts for localization metrics."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from opensportslib.metrics.localization_metric import build_snpro_prediction_json
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_localization_prediction_builder_emits_osl_v2_shape():
|
|
9
|
+
payload = build_snpro_prediction_json([
|
|
10
|
+
{
|
|
11
|
+
"video": "clips/match.mp4",
|
|
12
|
+
"fps": 25,
|
|
13
|
+
"events": [{
|
|
14
|
+
"label": "GOAL",
|
|
15
|
+
"gameTime": "1 - 00:10",
|
|
16
|
+
"frame": 250,
|
|
17
|
+
"position": 10_000,
|
|
18
|
+
"confidence": 0.9,
|
|
19
|
+
}],
|
|
20
|
+
}
|
|
21
|
+
], split="test", created_by="test")
|
|
22
|
+
assert payload["version"] == "2.0"
|
|
23
|
+
assert payload["task"] == "localization"
|
|
24
|
+
assert payload["metadata"] == {"type": "predictions", "created_by": "test", "split": "test"}
|
|
25
|
+
assert payload["data"][0]["events"][0]["position_ms"] == 10_000
|
|
@@ -6,7 +6,109 @@ from opensportslib.models.base.qwen_xvars import QWEN_MM_PROJECTOR_FILE, QwenXVa
|
|
|
6
6
|
from opensportslib.models.builder import build_model
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
class TinyTokenizer:
|
|
10
|
+
pad_token_id = 0
|
|
11
|
+
eos_token_id = 1
|
|
12
|
+
pad_token = "<pad>"
|
|
13
|
+
eos_token = "</s>"
|
|
14
|
+
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.vocab = {"<pad>": 0, "</s>": 1, "<vid_patch>": 2, "<vid_start>": 3, "<vid_end>": 4}
|
|
17
|
+
|
|
18
|
+
def get_vocab(self):
|
|
19
|
+
return dict(self.vocab)
|
|
20
|
+
|
|
21
|
+
def add_special_tokens(self, payload):
|
|
22
|
+
added = 0
|
|
23
|
+
for token in payload.get("additional_special_tokens", []):
|
|
24
|
+
if token not in self.vocab:
|
|
25
|
+
self.vocab[token] = len(self.vocab)
|
|
26
|
+
added += 1
|
|
27
|
+
return added
|
|
28
|
+
|
|
29
|
+
def convert_tokens_to_ids(self, token):
|
|
30
|
+
return self.vocab[token]
|
|
31
|
+
|
|
32
|
+
def __len__(self):
|
|
33
|
+
return len(self.vocab)
|
|
34
|
+
|
|
35
|
+
def __call__(self, text, return_tensors=None, **kwargs):
|
|
36
|
+
del kwargs
|
|
37
|
+
if isinstance(text, list):
|
|
38
|
+
text = text[0]
|
|
39
|
+
tokens = []
|
|
40
|
+
i = 0
|
|
41
|
+
while i < len(text):
|
|
42
|
+
matched = False
|
|
43
|
+
for token in ("<vid_start>", "<vid_patch>", "<vid_end>", "</s>"):
|
|
44
|
+
if text.startswith(token, i):
|
|
45
|
+
tokens.append(token)
|
|
46
|
+
i += len(token)
|
|
47
|
+
matched = True
|
|
48
|
+
break
|
|
49
|
+
if matched:
|
|
50
|
+
continue
|
|
51
|
+
if text[i].isspace():
|
|
52
|
+
i += 1
|
|
53
|
+
continue
|
|
54
|
+
j = i
|
|
55
|
+
while j < len(text) and not text[j].isspace() and not any(
|
|
56
|
+
text.startswith(token, j) for token in ("<vid_start>", "<vid_patch>", "<vid_end>", "</s>")
|
|
57
|
+
):
|
|
58
|
+
j += 1
|
|
59
|
+
tokens.append(text[i:j])
|
|
60
|
+
i = j
|
|
61
|
+
ids = []
|
|
62
|
+
for token in tokens:
|
|
63
|
+
if token not in self.vocab:
|
|
64
|
+
self.vocab[token] = len(self.vocab)
|
|
65
|
+
ids.append(self.vocab[token])
|
|
66
|
+
attn = [1] * len(ids)
|
|
67
|
+
if return_tensors == "pt":
|
|
68
|
+
return {"input_ids": torch.tensor([ids]), "attention_mask": torch.tensor([attn])}
|
|
69
|
+
return SimpleNamespace(input_ids=ids, attention_mask=attn)
|
|
70
|
+
|
|
71
|
+
def batch_decode(self, sequences, skip_special_tokens=True):
|
|
72
|
+
del sequences, skip_special_tokens
|
|
73
|
+
return ["qwen answer </s>"]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class TinyGenerateLM(torch.nn.Module):
|
|
77
|
+
def __init__(self):
|
|
78
|
+
super().__init__()
|
|
79
|
+
self.config = SimpleNamespace(hidden_size=4, vocab_size=64, use_cache=True)
|
|
80
|
+
self.generation_config = SimpleNamespace(use_cache=False)
|
|
81
|
+
self.emb = torch.nn.Embedding(128, 4)
|
|
82
|
+
self.lm = torch.nn.Linear(4, 64)
|
|
83
|
+
self.seen_inputs_embeds = None
|
|
84
|
+
self.generate_kwargs = None
|
|
85
|
+
|
|
86
|
+
def get_input_embeddings(self):
|
|
87
|
+
return self.emb
|
|
88
|
+
|
|
89
|
+
def resize_token_embeddings(self, size):
|
|
90
|
+
return self.emb
|
|
91
|
+
|
|
92
|
+
def save_pretrained(self, output_dir):
|
|
93
|
+
del output_dir
|
|
94
|
+
|
|
95
|
+
def forward(self, input_ids=None, inputs_embeds=None, attention_mask=None, labels=None, **kwargs):
|
|
96
|
+
del input_ids, attention_mask, kwargs
|
|
97
|
+
self.seen_inputs_embeds = inputs_embeds.detach().clone()
|
|
98
|
+
logits = self.lm(inputs_embeds)
|
|
99
|
+
loss = logits.sum() * 0
|
|
100
|
+
if labels is not None:
|
|
101
|
+
loss = loss + 0.123
|
|
102
|
+
return SimpleNamespace(loss=loss, logits=logits)
|
|
103
|
+
|
|
104
|
+
def generate(self, input_ids=None, inputs_embeds=None, attention_mask=None, **kwargs):
|
|
105
|
+
del attention_mask
|
|
106
|
+
self.generate_kwargs = {"input_ids": input_ids, "inputs_embeds": inputs_embeds, **kwargs}
|
|
107
|
+
extra = torch.tensor([[9, 1]], device=input_ids.device if input_ids is not None else inputs_embeds.device)
|
|
108
|
+
if input_ids is None:
|
|
109
|
+
prefix = torch.zeros((1, inputs_embeds.shape[1]), dtype=torch.long, device=extra.device)
|
|
110
|
+
return torch.cat((prefix, extra), dim=1)
|
|
111
|
+
return torch.cat((input_ids, extra), dim=1)
|
|
10
112
|
|
|
11
113
|
|
|
12
114
|
def _cfg(tmp_path):
|