opensportslib 0.3.1.dev19__tar.gz → 0.3.1.dev21__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. {opensportslib-0.3.1.dev19/opensportslib.egg-info → opensportslib-0.3.1.dev21}/PKG-INFO +8 -3
  2. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/README.md +7 -2
  3. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21/opensportslib.egg-info}/PKG-INFO +8 -3
  4. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib.egg-info/SOURCES.txt +3 -0
  5. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/pyproject.toml +1 -1
  6. opensportslib-0.3.1.dev21/scripts/verify_release_manifests.py +32 -0
  7. opensportslib-0.3.1.dev21/scripts/write_release_manifest.py +50 -0
  8. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/conftest.py +1 -0
  9. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/release/_release_common.py +79 -4
  10. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/release/test_00_environment.py +16 -6
  11. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/release/test_classification_release.py +39 -11
  12. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/release/test_localization_release.py +33 -10
  13. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/release/test_vqa_release.py +52 -5
  14. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/contracts/test_runner_contract.py +8 -0
  15. opensportslib-0.3.1.dev21/tests/unit/tools/test_release_manifest.py +25 -0
  16. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/LICENSE +0 -0
  17. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/LICENSE-COMMERCIAL +0 -0
  18. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/MANIFEST.in +0 -0
  19. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/examples/quickstart/basic_classification.py +0 -0
  20. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/examples/quickstart/basic_localization.py +0 -0
  21. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/examples/quickstart/basic_vqa.py +0 -0
  22. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/__init__.py +0 -0
  23. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/adaptation/__init__.py +0 -0
  24. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/adaptation/spotta.py +0 -0
  25. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/apis/__init__.py +0 -0
  26. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/apis/base_task_model.py +0 -0
  27. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/apis/classification.py +0 -0
  28. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/apis/config.py +0 -0
  29. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/apis/configuration.py +0 -0
  30. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/apis/localization.py +0 -0
  31. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/apis/vqa.py +0 -0
  32. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/cli.py +0 -0
  33. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/classification/default.yaml +0 -0
  34. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/classification/sngar_frames.yaml +0 -0
  35. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/classification/sngar_tracking.yaml +0 -0
  36. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/classification/video.yaml +0 -0
  37. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/default.yaml +0 -0
  38. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/localization/calf_resnetpca512.yaml +0 -0
  39. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/localization/default.yaml +0 -0
  40. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/localization/e2e_spotta.yaml +0 -0
  41. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/localization/h5_header_distance.yaml +0 -0
  42. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/localization/h5_header_skeleton.yaml +0 -0
  43. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/localization/netvladpp_resnetpca512.yaml +0 -0
  44. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/localization/tracking_action_spotting.yaml +0 -0
  45. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/localization/video_dali.yaml +0 -0
  46. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/localization/video_ocv.yaml +0 -0
  47. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/vqa/default.yaml +0 -0
  48. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/vqa/qwen.yaml +0 -0
  49. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/vqa/qwen3_vl_native.yaml +0 -0
  50. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/vqa/qwen_lora.yaml +0 -0
  51. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/vqa/qwen_sngar_frames.yaml +0 -0
  52. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/configs/vqa/xvars.yaml +0 -0
  53. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/__init__.py +0 -0
  54. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/config/__init__.py +0 -0
  55. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/config/accessors.py +0 -0
  56. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/config/conflicts.py +0 -0
  57. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/config/editable.py +0 -0
  58. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/config/loader.py +0 -0
  59. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/config/migrate.py +0 -0
  60. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/config/migrations/__init__.py +0 -0
  61. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/config/migrations/legacy_to_canonical.py +0 -0
  62. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/config/rule_variants.py +0 -0
  63. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/config/runtime_adapter.py +0 -0
  64. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/config/schema.py +0 -0
  65. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/config/schemas/__init__.py +0 -0
  66. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/config/schemas/schema_canonical.py +0 -0
  67. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/config/schemas/schema_legacy.py +0 -0
  68. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/config/validate.py +0 -0
  69. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/loss/__init__.py +0 -0
  70. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/loss/builder.py +0 -0
  71. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/loss/calf.py +0 -0
  72. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/loss/ce.py +0 -0
  73. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/loss/combine.py +0 -0
  74. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/loss/nll.py +0 -0
  75. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/optimizer/__init__.py +0 -0
  76. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/optimizer/builder.py +0 -0
  77. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/sampler/weighted_sampler.py +0 -0
  78. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/scheduler/__init__.py +0 -0
  79. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/scheduler/builder.py +0 -0
  80. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/trainer/__init__.py +0 -0
  81. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/trainer/classification_trainer.py +0 -0
  82. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/trainer/localization_trainer.py +0 -0
  83. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/trainer/vqa_trainer.py +0 -0
  84. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/utils/checkpoint.py +0 -0
  85. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/utils/config.py +0 -0
  86. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/utils/config_normalize.py +0 -0
  87. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/utils/data.py +0 -0
  88. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/utils/ddp.py +0 -0
  89. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/utils/default_args.py +0 -0
  90. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/utils/direct_video.py +0 -0
  91. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/utils/hf_runtime.py +0 -0
  92. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/utils/lightning.py +0 -0
  93. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/utils/load_annotations.py +0 -0
  94. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/utils/seed.py +0 -0
  95. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/utils/video_processing.py +0 -0
  96. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/core/utils/wandb.py +0 -0
  97. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/datasets/__init__.py +0 -0
  98. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/datasets/builder.py +0 -0
  99. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/datasets/classification_dataset.py +0 -0
  100. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/datasets/hf_json.py +0 -0
  101. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/datasets/hf_tracking.py +0 -0
  102. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/datasets/localization_dataset.py +0 -0
  103. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/datasets/utils/__init__.py +0 -0
  104. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/datasets/utils/h5_tracking.py +0 -0
  105. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/datasets/utils/tracking.py +0 -0
  106. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/datasets/vqa_dataset.py +0 -0
  107. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/legacy_config/classification.yaml +0 -0
  108. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/legacy_config/localization-e2e-ocv.yaml +0 -0
  109. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/legacy_config/localization-json_calf_resnetpca512.yaml +0 -0
  110. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/legacy_config/localization-json_netvlad++_resnetpca512.yaml +0 -0
  111. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/legacy_config/localization.yaml +0 -0
  112. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/legacy_config/sngar-frames.yaml +0 -0
  113. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/legacy_config/sngar-tracking.yaml +0 -0
  114. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/metrics/classification_metric.py +0 -0
  115. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/metrics/localization_metric.py +0 -0
  116. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/metrics/vqa_metric.py +0 -0
  117. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/__init__.py +0 -0
  118. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/backbones/builder.py +0 -0
  119. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/base/contextaware.py +0 -0
  120. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/base/e2e.py +0 -0
  121. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/base/learnablepooling.py +0 -0
  122. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/base/qwen_vl_native.py +0 -0
  123. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/base/qwen_xvars.py +0 -0
  124. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/base/rule_based.py +0 -0
  125. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/base/tracking.py +0 -0
  126. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/base/vars.py +0 -0
  127. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/base/video.py +0 -0
  128. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/base/video_chatgpt_compat.py +0 -0
  129. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/base/video_mae.py +0 -0
  130. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/base/xvars_videochatgpt.py +0 -0
  131. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/builder.py +0 -0
  132. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/heads/builder.py +0 -0
  133. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/neck/builder.py +0 -0
  134. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/utils/common.py +0 -0
  135. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/utils/impl/__init__.py +0 -0
  136. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/utils/impl/asformer.py +0 -0
  137. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/utils/impl/calf.py +0 -0
  138. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/utils/impl/gsm.py +0 -0
  139. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/utils/impl/gtad.py +0 -0
  140. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/utils/impl/tsm.py +0 -0
  141. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/utils/litebase.py +0 -0
  142. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/utils/modules.py +0 -0
  143. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/utils/shift.py +0 -0
  144. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/utils/utils.py +0 -0
  145. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/utils/vqa_prediction_priors.py +0 -0
  146. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/utils/vqa_prompting.py +0 -0
  147. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/models/utils/xvars_clip_index.py +0 -0
  148. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/remote_registry.py +0 -0
  149. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/setup/setup.py +0 -0
  150. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/tools/__init__.py +0 -0
  151. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/tools/_common.py +0 -0
  152. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/tools/hf_transfer.py +0 -0
  153. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/tools/osl_json_to_parquet.py +0 -0
  154. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib/tools/parquet_to_osl_json.py +0 -0
  155. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib.egg-info/dependency_links.txt +0 -0
  156. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib.egg-info/entry_points.txt +0 -0
  157. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib.egg-info/requires.txt +0 -0
  158. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/opensportslib.egg-info/top_level.txt +0 -0
  159. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/scripts/redact_test_stream.py +0 -0
  160. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/scripts/release_versions.py +0 -0
  161. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/scripts/run_h5_header_rule_inference.py +0 -0
  162. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/scripts/summarize_test_report.py +0 -0
  163. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/setup.cfg +0 -0
  164. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/__init__.py +0 -0
  165. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/helpers/__init__.py +0 -0
  166. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/helpers/configs.py +0 -0
  167. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/helpers/pipeline_stubs.py +0 -0
  168. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/integration/classification/test_pipeline.py +0 -0
  169. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/integration/localization/test_pipeline.py +0 -0
  170. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/integration/localization/test_spotta.py +0 -0
  171. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/integration/vqa/test_pipeline.py +0 -0
  172. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/release/__init__.py +0 -0
  173. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/smoke/test_cli.py +0 -0
  174. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/smoke/test_config_utils.py +0 -0
  175. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/smoke/test_environment.py +0 -0
  176. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/smoke/test_package.py +0 -0
  177. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/smoke/test_public_apis.py +0 -0
  178. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/api/test_remote_registry.py +0 -0
  179. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/api/test_remote_sessions.py +0 -0
  180. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/api/test_server_jobs.py +0 -0
  181. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/api/test_server_registry.py +0 -0
  182. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/api/test_server_startup_scripts.py +0 -0
  183. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/api/test_task_model_contract.py +0 -0
  184. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/api/test_vqa.py +0 -0
  185. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/config/test_architecture.py +0 -0
  186. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/config/test_editable.py +0 -0
  187. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/config/test_optional_hf.py +0 -0
  188. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/config/test_pretrained_merge.py +0 -0
  189. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/config/test_split_overrides.py +0 -0
  190. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/contracts/test_architecture.py +0 -0
  191. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/contracts/test_dependencies.py +0 -0
  192. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/contracts/test_package.py +0 -0
  193. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/contracts/test_release_versions.py +0 -0
  194. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/core/test_classification_trainer.py +0 -0
  195. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/core/test_localization_hf_backend.py +0 -0
  196. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/core/test_training_components.py +0 -0
  197. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/core/test_video_processing.py +0 -0
  198. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/data/test_classification_paths.py +0 -0
  199. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/data/test_h5_tracking.py +0 -0
  200. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/data/test_hf_json_spotting.py +0 -0
  201. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/data/test_hf_json_tracking_spotting.py +0 -0
  202. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/data/test_hf_tracking_dataset.py +0 -0
  203. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/data/test_localization_dali.py +0 -0
  204. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/data/test_localization_intervals.py +0 -0
  205. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/data/test_osl_formats.py +0 -0
  206. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/models/test_classification_metrics.py +0 -0
  207. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/models/test_header_rule_spotter.py +0 -0
  208. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/models/test_header_skeleton_spotter.py +0 -0
  209. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/models/test_localization_metrics.py +0 -0
  210. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/models/test_temporal_shift.py +0 -0
  211. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/models/test_vqa_metrics.py +0 -0
  212. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/models/test_vqa_qwen_xvars.py +0 -0
  213. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/models/test_vqa_training_lora.py +0 -0
  214. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/models/test_vqa_xvars_videochatgpt.py +0 -0
  215. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/tools/test_conversion.py +0 -0
  216. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/tools/test_extract_xvars_features.py +0 -0
  217. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/tools/test_hf_transfer.py +0 -0
  218. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/tools/test_report_summary.py +0 -0
  219. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tests/unit/tools/test_setup.py +0 -0
  220. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/convert/build_sn_vqa_2026_vqa.py +0 -0
  221. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/convert/build_sngar_spotting.py +0 -0
  222. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/convert/build_soccernet_gar.py +0 -0
  223. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/convert/build_soccernet_gar_action_spotting.py +0 -0
  224. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/convert/build_soccernet_gar_vqa.py +0 -0
  225. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/convert/build_xvars_indexes.py +0 -0
  226. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/convert/extract_xvars_clip_features.py +0 -0
  227. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/convert/osl_json_to_parquet_webdataset.py +0 -0
  228. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/convert/parquet_webdataset_to_osl_json.py +0 -0
  229. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/convert/sngar_dataset_card.py +0 -0
  230. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/convert/sngar_events.py +0 -0
  231. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/convert/verify_sngar_spotting.py +0 -0
  232. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/download/download_hf_repo.py +0 -0
  233. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/download/download_osl_hf.py +0 -0
  234. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/download/push_sngar_spotting.py +0 -0
  235. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/download/upload_osl_hf.py +0 -0
  236. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/train/train_config.py +0 -0
  237. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/training/classification.py +0 -0
  238. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/training/localization.py +0 -0
  239. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/tools/training/vqa.py +0 -0
  240. {opensportslib-0.3.1.dev19 → opensportslib-0.3.1.dev21}/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.1.dev19
3
+ Version: 0.3.1.dev21
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
@@ -114,8 +114,9 @@ It provides a unified framework to **train, evaluate, and run inference** for ke
114
114
  - **Action classification**
115
115
  - **Action localization / spotting**
116
116
  - **Visual Question Answering (VQA)**
117
- - **Action retrieval**
118
- - **Action description / captioning**
117
+
118
+ Retrieval and action description/captioning are roadmap areas. They do not yet
119
+ have first-class task wrappers or training workflows in this package.
119
120
 
120
121
  OpenSportsLib is designed for **researchers, ML engineers, and sports analytics teams** who want reproducible and extensible workflows for sports video AI.
121
122
 
@@ -622,6 +623,10 @@ opensportslib setup --vqa_qwen
622
623
 
623
624
  We welcome contributions to OpenSportsLib.
624
625
 
626
+ All PRs must target `dev`. Before a PR can merge, every GitHub-linked commit
627
+ author must accept the [Individual Contributor License Agreement](.github/CLA.md)
628
+ when prompted by the `CLA check`.
629
+
625
630
  Please check:
626
631
 
627
632
  - [CONTRIBUTING.md](CONTRIBUTING.md)
@@ -75,8 +75,9 @@ It provides a unified framework to **train, evaluate, and run inference** for ke
75
75
  - **Action classification**
76
76
  - **Action localization / spotting**
77
77
  - **Visual Question Answering (VQA)**
78
- - **Action retrieval**
79
- - **Action description / captioning**
78
+
79
+ Retrieval and action description/captioning are roadmap areas. They do not yet
80
+ have first-class task wrappers or training workflows in this package.
80
81
 
81
82
  OpenSportsLib is designed for **researchers, ML engineers, and sports analytics teams** who want reproducible and extensible workflows for sports video AI.
82
83
 
@@ -583,6 +584,10 @@ opensportslib setup --vqa_qwen
583
584
 
584
585
  We welcome contributions to OpenSportsLib.
585
586
 
587
+ All PRs must target `dev`. Before a PR can merge, every GitHub-linked commit
588
+ author must accept the [Individual Contributor License Agreement](.github/CLA.md)
589
+ when prompted by the `CLA check`.
590
+
586
591
  Please check:
587
592
 
588
593
  - [CONTRIBUTING.md](CONTRIBUTING.md)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opensportslib
3
- Version: 0.3.1.dev19
3
+ Version: 0.3.1.dev21
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
@@ -114,8 +114,9 @@ It provides a unified framework to **train, evaluate, and run inference** for ke
114
114
  - **Action classification**
115
115
  - **Action localization / spotting**
116
116
  - **Visual Question Answering (VQA)**
117
- - **Action retrieval**
118
- - **Action description / captioning**
117
+
118
+ Retrieval and action description/captioning are roadmap areas. They do not yet
119
+ have first-class task wrappers or training workflows in this package.
119
120
 
120
121
  OpenSportsLib is designed for **researchers, ML engineers, and sports analytics teams** who want reproducible and extensible workflows for sports video AI.
121
122
 
@@ -622,6 +623,10 @@ opensportslib setup --vqa_qwen
622
623
 
623
624
  We welcome contributions to OpenSportsLib.
624
625
 
626
+ All PRs must target `dev`. Before a PR can merge, every GitHub-linked commit
627
+ author must accept the [Individual Contributor License Agreement](.github/CLA.md)
628
+ when prompted by the `CLA check`.
629
+
625
630
  Please check:
626
631
 
627
632
  - [CONTRIBUTING.md](CONTRIBUTING.md)
@@ -149,6 +149,8 @@ scripts/redact_test_stream.py
149
149
  scripts/release_versions.py
150
150
  scripts/run_h5_header_rule_inference.py
151
151
  scripts/summarize_test_report.py
152
+ scripts/verify_release_manifests.py
153
+ scripts/write_release_manifest.py
152
154
  tests/__init__.py
153
155
  tests/conftest.py
154
156
  tests/helpers/__init__.py
@@ -210,6 +212,7 @@ tests/unit/models/test_vqa_xvars_videochatgpt.py
210
212
  tests/unit/tools/test_conversion.py
211
213
  tests/unit/tools/test_extract_xvars_features.py
212
214
  tests/unit/tools/test_hf_transfer.py
215
+ tests/unit/tools/test_release_manifest.py
213
216
  tests/unit/tools/test_report_summary.py
214
217
  tests/unit/tools/test_setup.py
215
218
  tools/convert/build_sn_vqa_2026_vqa.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "opensportslib"
7
- version = "0.3.1.dev19"
7
+ version = "0.3.1.dev21"
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"
@@ -0,0 +1,32 @@
1
+ """Verify the Qwen, X-VARS, and GAR release manifests describe one passing commit."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ from pathlib import Path
8
+
9
+
10
+ REQUIRED_PROFILES = {"qwen", "xvars", "gar"}
11
+
12
+
13
+ def main() -> int:
14
+ parser = argparse.ArgumentParser()
15
+ parser.add_argument("manifests", nargs="+", help="release-manifest.json paths copied from release servers")
16
+ args = parser.parse_args()
17
+ records = [json.loads(Path(path).read_text(encoding="utf-8")) for path in args.manifests]
18
+ profiles = {record.get("profile") for record in records}
19
+ commits = {record.get("commit") for record in records}
20
+ failed = [record.get("profile") for record in records if record.get("result") != "passed"]
21
+ if profiles != REQUIRED_PROFILES:
22
+ raise SystemExit(f"Expected manifests for {sorted(REQUIRED_PROFILES)}, got {sorted(profiles)}.")
23
+ if len(commits) != 1 or "unknown" in commits:
24
+ raise SystemExit(f"Release manifests must identify one known commit, got {sorted(commits)}.")
25
+ if failed:
26
+ raise SystemExit(f"Release profiles failed: {failed}.")
27
+ print(f"Release manifests verified for commit {next(iter(commits))}: {', '.join(sorted(profiles))}")
28
+ return 0
29
+
30
+
31
+ if __name__ == "__main__":
32
+ raise SystemExit(main())
@@ -0,0 +1,50 @@
1
+ """Write one non-secret, commit-bound release profile manifest."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import subprocess
8
+ from datetime import datetime, timezone
9
+ from pathlib import Path
10
+
11
+
12
+ def main() -> int:
13
+ parser = argparse.ArgumentParser()
14
+ parser.add_argument("--report-dir", required=True)
15
+ parser.add_argument("--profile", required=True, choices=("qwen", "xvars", "gar"))
16
+ parser.add_argument("--scale", required=True, choices=("full", "bounded"))
17
+ parser.add_argument("--exit-code", required=True, type=int)
18
+ args = parser.parse_args()
19
+
20
+ try:
21
+ commit = subprocess.check_output(
22
+ ["git", "rev-parse", "HEAD"], text=True, stderr=subprocess.DEVNULL
23
+ ).strip()
24
+ except subprocess.CalledProcessError:
25
+ commit = "unknown"
26
+ report_dir = Path(args.report_dir)
27
+ metadata = report_dir / "release-metadata.jsonl"
28
+ records = []
29
+ if metadata.exists():
30
+ for line in metadata.read_text(encoding="utf-8").splitlines():
31
+ if line.strip():
32
+ records.append(json.loads(line))
33
+ manifest = {
34
+ "created_at": datetime.now(timezone.utc).isoformat(),
35
+ "commit": commit,
36
+ "profile": args.profile,
37
+ "scale": args.scale,
38
+ "result": "passed" if args.exit_code == 0 else "failed",
39
+ "exit_code": args.exit_code,
40
+ "metadata_records": records,
41
+ }
42
+ (report_dir / "release-manifest.json").write_text(
43
+ json.dumps(manifest, indent=2, sort_keys=True, default=str) + "\n",
44
+ encoding="utf-8",
45
+ )
46
+ return 0
47
+
48
+
49
+ if __name__ == "__main__":
50
+ raise SystemExit(main())
@@ -28,6 +28,7 @@ MARKERS = {
28
28
  "vqa": "Visual-question-answering task coverage.",
29
29
  "vqa_qwen": "Requires the Qwen VQA dependency profile.",
30
30
  "vqa_xvars": "Requires the X-VARS VQA dependency profile.",
31
+ "release_gar": "SoccerNet-GAR/PyG release profile.",
31
32
  }
32
33
 
33
34
 
@@ -15,6 +15,8 @@ from __future__ import annotations
15
15
  import importlib.util
16
16
  import json
17
17
  import os
18
+ import subprocess
19
+ import sys
18
20
  from copy import deepcopy
19
21
  from pathlib import Path
20
22
  from typing import Any
@@ -24,6 +26,9 @@ import pytest
24
26
  REPO_ROOT = Path(__file__).resolve().parents[2]
25
27
 
26
28
  RELEASE_ENV_FLAG = "RUN_OSL_RELEASE_TESTS"
29
+ RELEASE_SCALE_ENV = "OSL_RELEASE_SCALE"
30
+ RELEASE_PROFILE_ENV = "OSL_RELEASE_PROFILE"
31
+ RELEASE_PROFILES = {"qwen", "xvars", "gar"}
27
32
 
28
33
  # Where materialized configs / run outputs (checkpoints, logs, predictions)
29
34
  # are cached. Override with OSL_RELEASE_CACHE_DIR to point at a disk with
@@ -111,15 +116,42 @@ def _env_int(name: str, default: int) -> int:
111
116
  return int(raw)
112
117
 
113
118
 
114
- def epochs_for(default: int) -> int:
115
- """TRAIN.epochs override. OSL_RELEASE_EPOCHS=0 means 'keep config default'."""
116
- value = _env_int("OSL_RELEASE_EPOCHS", default)
117
- return default if value == 0 else value
119
+ def release_scale() -> str:
120
+ """Return ``full`` (the release default) or explicit bounded debug mode."""
121
+ value = os.environ.get(RELEASE_SCALE_ENV, "full").strip().lower()
122
+ if value not in {"full", "bounded"}:
123
+ raise ValueError(f"{RELEASE_SCALE_ENV} must be 'full' or 'bounded', got {value!r}.")
124
+ return value
125
+
126
+
127
+ def release_profile() -> str:
128
+ value = os.environ.get(RELEASE_PROFILE_ENV, "qwen").strip().lower()
129
+ if value not in RELEASE_PROFILES:
130
+ raise ValueError(f"{RELEASE_PROFILE_ENV} must be one of {sorted(RELEASE_PROFILES)}, got {value!r}.")
131
+ return value
132
+
133
+
134
+ def training_overrides(bounded_default: int) -> dict[str, int]:
135
+ """Return an epoch override only when the caller explicitly requests one.
136
+
137
+ Full release runs preserve the canonical preset schedule. ``0`` has the
138
+ same preserve-default meaning; bounded mode retains the previous small
139
+ release schedule unless an explicit positive override is supplied.
140
+ """
141
+ raw = os.environ.get("OSL_RELEASE_EPOCHS")
142
+ if raw is not None and raw != "":
143
+ value = int(raw)
144
+ if value < 0:
145
+ raise ValueError("OSL_RELEASE_EPOCHS must be zero or a positive integer.")
146
+ return {} if value == 0 else {"epochs": value}
147
+ return {"epochs": bounded_default} if release_scale() == "bounded" else {}
118
148
 
119
149
 
120
150
  def max_items_for(env_name: str, default: int | None) -> int | None:
121
151
  """Generic dataset-subset-size override. 0 (or 'all') means 'download everything'."""
122
152
  raw = os.environ.get(env_name)
153
+ if (raw is None or raw == "") and release_scale() == "full":
154
+ return None
123
155
  if raw is None or raw == "":
124
156
  return default
125
157
  if raw.strip().lower() == "all":
@@ -289,6 +321,7 @@ def snapshot_dataset(
289
321
  local_dir: Path,
290
322
  *,
291
323
  allow_patterns: list[str] | None = None,
324
+ revision: str = "main",
292
325
  ) -> Path:
293
326
  """Download (or update) a full dataset repo, or a pattern-restricted
294
327
  subset of it, into local_dir. Safe to call repeatedly (resumable)."""
@@ -301,10 +334,52 @@ def snapshot_dataset(
301
334
  local_dir=str(local_dir),
302
335
  token=hf_token(),
303
336
  allow_patterns=allow_patterns,
337
+ revision=revision,
304
338
  )
305
339
  return local_dir
306
340
 
307
341
 
342
+ def require_model_repo(repo_id: str) -> None:
343
+ """Fail with release remediation when a published model cannot be read."""
344
+ require_repo_access(repo_id, repo_type="model")
345
+
346
+
347
+ def download_model_snapshot(repo_id: str, local_dir: Path, *, revision: str = "main") -> Path:
348
+ """Download/cache a model repository and write non-secret provenance."""
349
+ from huggingface_hub import snapshot_download
350
+
351
+ local_dir.mkdir(parents=True, exist_ok=True)
352
+ path = Path(snapshot_download(
353
+ repo_id=repo_id, repo_type="model", revision=revision,
354
+ local_dir=str(local_dir), token=hf_token(),
355
+ ))
356
+ record_release_metadata("model", repo_id=repo_id, revision=revision, local_path=str(path))
357
+ return path
358
+
359
+
360
+ def run_xvars_preprocessing(dataset_root: Path, output_root: Path, *, weights_path: Path) -> None:
361
+ """Run the project-owned X-VARS feature and index builders once per cache."""
362
+ marker = output_root / ".osl_xvars_prepared.json"
363
+ if marker.exists():
364
+ record_release_metadata("xvars_preprocess", status="cached", marker=str(marker))
365
+ return
366
+ output_root.mkdir(parents=True, exist_ok=True)
367
+ extractor = REPO_ROOT / "tools" / "convert" / "extract_xvars_clip_features.py"
368
+ indexer = REPO_ROOT / "tools" / "convert" / "build_xvars_indexes.py"
369
+ command = [
370
+ sys.executable, str(extractor), "--dataset-root", str(dataset_root),
371
+ "--dataset-output-root", str(output_root), "--mode", "strict_xvars",
372
+ "--weights-path", str(weights_path),
373
+ ]
374
+ subprocess.check_call(command)
375
+ subprocess.check_call([
376
+ sys.executable, str(indexer), "--dataset-root", str(dataset_root),
377
+ "--features-root", str(output_root),
378
+ ])
379
+ marker.write_text(json.dumps({"weights_path": str(weights_path)}), encoding="utf-8")
380
+ record_release_metadata("xvars_preprocess", status="complete", output_root=str(output_root))
381
+
382
+
308
383
  def download_files(repo_id: str, filenames: list[str], local_dir: Path) -> list[Path]:
309
384
  from huggingface_hub import hf_hub_download
310
385
 
@@ -6,7 +6,7 @@ import importlib.util
6
6
 
7
7
  import pytest
8
8
 
9
- from tests.release._release_common import require_release_enabled
9
+ from tests.release._release_common import release_profile, require_release_enabled
10
10
 
11
11
 
12
12
  pytestmark = [pytest.mark.release, pytest.mark.gpu]
@@ -19,10 +19,20 @@ def test_release_environment_has_cuda_and_required_training_packages():
19
19
  assert torch.cuda.is_available(), "Release verification requires an accessible CUDA GPU."
20
20
  assert torch.cuda.device_count() >= 1, "CUDA reported available but exposed no devices."
21
21
 
22
- required = {
23
- "nvidia.dali": "run `opensportslib setup --dali`",
24
- "peft": "run `opensportslib setup --vqa_qwen`",
25
- "trl": "run `opensportslib setup --vqa_qwen`",
26
- }
22
+ profile = release_profile()
23
+ required = {}
24
+ if profile == "qwen":
25
+ required.update({
26
+ "peft": "run `opensportslib setup --vqa_qwen`",
27
+ "nvidia.dali": "run `opensportslib setup --dali`",
28
+ "trl": "run `opensportslib setup --vqa_qwen`",
29
+ })
30
+ elif profile == "xvars":
31
+ required.update({
32
+ "peft": "run `opensportslib setup --vqa_xvars`",
33
+ "trl": "run `opensportslib setup --vqa_xvars`",
34
+ })
35
+ elif profile == "gar":
36
+ required["torch_geometric"] = "run `opensportslib setup --pyg` on a supported platform"
27
37
  missing = [f"{name} ({hint})" for name, hint in required.items() if importlib.util.find_spec(name) is None]
28
38
  assert not missing, "Release environment is missing required packages: " + ", ".join(missing)
@@ -66,7 +66,7 @@ from ._release_common import (
66
66
  DATA_DIR,
67
67
  classes_from_osl_json,
68
68
  download_shard_split,
69
- epochs_for,
69
+ training_overrides,
70
70
  materialize_config,
71
71
  max_items_for,
72
72
  prefer_osl_ready_dataset,
@@ -93,6 +93,8 @@ CLS_PRIMARY_REPO = os.environ.get("OSL_RELEASE_CLS_REPO", "OpenSportsLab/OSL-XFo
93
93
  CLS_PRIMARY_REVISION = os.environ.get("OSL_RELEASE_CLS_REVISION", "224p")
94
94
  CLS_FALLBACK_REPO = os.environ.get("OSL_RELEASE_CLS_FALLBACK_REPO", "OpenSportsLab/OSL-cls-UEFA-fouls")
95
95
  TRACKING_CLS_REPO = os.environ.get("OSL_RELEASE_TRACKING_CLS_REPO", "OpenSportsLab/SoccerNet-GAR")
96
+ TRACKING_CLS_REVISION = os.environ.get("OSL_RELEASE_TRACKING_CLS_REVISION", "tracking")
97
+ PUBLISHED_CLASSIFICATION_MODEL = "OpenSportsLab/OSL-cls-action-mvitv2"
96
98
 
97
99
  os.environ.setdefault("WANDB_MODE", "disabled")
98
100
  os.environ.setdefault("OSL_PRETRAINED_WEIGHTS", "0")
@@ -321,8 +323,9 @@ def _run_classification_pipeline(config_path: str, dataset: dict, run_name: str)
321
323
  config_path=config_path, checkpoint_path=str(checkpoint),
322
324
  )
323
325
 
324
- report_step(f"[{run_name}] infer()")
325
- predictions = model.infer(test_set=str(split_paths["test"]), weights=checkpoint, use_wandb=False)
326
+ report_step(f"[{run_name}] fresh checkpoint load + infer()")
327
+ restored_model = ClassificationModel(config=config_path, weights=checkpoint)
328
+ predictions = restored_model.infer(test_set=str(split_paths["test"]), use_wandb=False)
326
329
  assert isinstance(predictions, dict) and predictions.get("data"), f"[{run_name}] empty predictions"
327
330
 
328
331
  report_step(f"[{run_name}] save_predictions()")
@@ -332,7 +335,7 @@ def _run_classification_pipeline(config_path: str, dataset: dict, run_name: str)
332
335
  record_release_metadata("prediction", task="classification", model_family=run_name, prediction_path=str(pred_path))
333
336
 
334
337
  report_step(f"[{run_name}] evaluate()")
335
- metrics = model.evaluate(test_set=str(split_paths["test"]), use_wandb=False)
338
+ metrics = restored_model.evaluate(test_set=str(split_paths["test"]), predictions=predictions, use_wandb=False)
336
339
  assert isinstance(metrics, dict), f"[{run_name}] evaluate() did not return metrics"
337
340
  record_release_metadata(
338
341
  "result", task="classification", model_family=run_name,
@@ -341,6 +344,30 @@ def _run_classification_pipeline(config_path: str, dataset: dict, run_name: str)
341
344
  print(f"[{run_name}] metrics: {metrics}")
342
345
 
343
346
 
347
+ @pytest.mark.release
348
+ def test_published_classification_model_inference(classification_dataset):
349
+ """Published model artifact must load with its canonical inference path."""
350
+ require_release_enabled()
351
+ from ._release_common import require_model_repo
352
+
353
+ require_model_repo(PUBLISHED_CLASSIFICATION_MODEL)
354
+ dataset = classification_dataset
355
+ overrides = system_block("published_classification_mvitv2")
356
+ overrides["DATA"] = {
357
+ "common": {
358
+ "data_root": str(dataset["data_root"]), "classes": dataset["classes"],
359
+ "splits": {split: {"annotation_path": str(path), "source_path": str(dataset["source_paths"][split])}
360
+ for split, path in dataset["split_paths"].items()},
361
+ }
362
+ }
363
+ config_path = materialize_config("classification", "video", overrides, out_name="published_cls_mvitv2.yaml")
364
+ model = ClassificationModel(config=config_path, weights=PUBLISHED_CLASSIFICATION_MODEL)
365
+ predictions = model.infer(test_set=str(dataset["split_paths"]["test"]), use_wandb=False)
366
+ assert isinstance(predictions, dict) and predictions.get("data")
367
+ metrics = model.evaluate(test_set=str(dataset["split_paths"]["test"]), predictions=predictions, use_wandb=False)
368
+ record_release_metadata("published_model_result", task="classification", model_id=PUBLISHED_CLASSIFICATION_MODEL, metrics=metrics)
369
+
370
+
344
371
  @pytest.mark.release
345
372
  @pytest.mark.parametrize("backbone", MVNETWORK_BACKBONES)
346
373
  def test_classification_mvnetwork_backbone(classification_dataset, backbone):
@@ -375,7 +402,7 @@ def test_classification_mvnetwork_backbone(classification_dataset, backbone):
375
402
  "task_head": {"params": {"num_classes": len(dataset["classes"])}},
376
403
  }
377
404
  }
378
- overrides["TRAIN"] = {"epochs": epochs_for(2)}
405
+ overrides["TRAIN"] = training_overrides(2)
379
406
 
380
407
  config_path = materialize_config("classification", "video", overrides, out_name=f"cls_{backbone}.yaml")
381
408
  _run_classification_pipeline(config_path, dataset, backbone)
@@ -421,7 +448,7 @@ def test_classification_video_mae_huggingface_backend(classification_dataset):
421
448
  "task_head": {"params": {"num_classes": len(dataset["classes"])}},
422
449
  }
423
450
  }
424
- overrides["TRAIN"] = {"epochs": epochs_for(2)}
451
+ overrides["TRAIN"] = training_overrides(2)
425
452
 
426
453
  config_path = materialize_config("classification", "video", overrides, out_name="cls_video_mae.yaml")
427
454
  _run_classification_pipeline(config_path, dataset, "video_mae")
@@ -480,9 +507,9 @@ def tracking_classification_dataset():
480
507
 
481
508
  require_release_enabled()
482
509
  require_repo_access(TRACKING_CLS_REPO)
483
- require_repo_populated(TRACKING_CLS_REPO)
484
- root = DATA_DIR / "classification" / TRACKING_CLS_REPO.split("/")[-1]
485
- snapshot_dataset(TRACKING_CLS_REPO, root)
510
+ require_repo_populated(TRACKING_CLS_REPO, revision=TRACKING_CLS_REVISION)
511
+ root = DATA_DIR / "classification" / f"{TRACKING_CLS_REPO.split('/')[-1]}-{TRACKING_CLS_REVISION}"
512
+ snapshot_dataset(TRACKING_CLS_REPO, root, revision=TRACKING_CLS_REVISION)
486
513
  split_paths = {}
487
514
  for split in ("train", "valid", "test"):
488
515
  candidates = sorted(root.rglob(f"*{split}*.json"))
@@ -500,6 +527,7 @@ def tracking_classification_dataset():
500
527
 
501
528
 
502
529
  @pytest.mark.release
530
+ @pytest.mark.release_gar
503
531
  def test_classification_tracking_graph_conv(tracking_classification_dataset):
504
532
  """Tracking-modality classification (graph_conv backbone,
505
533
  classification/sngar_tracking.yaml canonical config). Uses
@@ -521,7 +549,7 @@ def test_classification_tracking_graph_conv(tracking_classification_dataset):
521
549
  "inputs": {"video": {"params": {"max_samples": max_items_for("OSL_RELEASE_MAX_CLIPS", 40)}}},
522
550
  }
523
551
  overrides["MODEL"] = {"components": {"task_head": {"params": {"num_classes": len(dataset["classes"])}}}}
524
- overrides["TRAIN"] = {"epochs": epochs_for(1)}
552
+ overrides["TRAIN"] = training_overrides(1)
525
553
  config_path = materialize_config(
526
554
  "classification", "sngar_tracking", overrides, out_name="cls_graph_conv.yaml"
527
555
  )
@@ -555,7 +583,7 @@ def test_classification_frames_npy_backbones(frames_npy_dataset, backbone):
555
583
  "video_adapter": {"params": {"hidden_dim": hidden_dim}},
556
584
  "task_head": {"params": {"hidden_dim": hidden_dim, "num_classes": len(dataset["classes"])}},
557
585
  }}
558
- overrides["TRAIN"] = {"epochs": epochs_for(1)}
586
+ overrides["TRAIN"] = training_overrides(1)
559
587
  config_path = materialize_config(
560
588
  "classification", "sngar_frames", overrides, out_name=f"cls_frames_{backbone}.yaml"
561
589
  )
@@ -55,7 +55,7 @@ from ._release_common import (
55
55
  classes_from_osl_json,
56
56
  download_files,
57
57
  download_shard_split,
58
- epochs_for,
58
+ training_overrides,
59
59
  hf_token,
60
60
  materialize_config,
61
61
  max_items_for,
@@ -63,6 +63,7 @@ from ._release_common import (
63
63
  prefer_osl_ready_dataset,
64
64
  report_step,
65
65
  record_release_metadata,
66
+ release_scale,
66
67
  require_release_enabled,
67
68
  require_repo_access,
68
69
  snapshot_dataset,
@@ -98,6 +99,7 @@ E2E_FALLBACK_REPO = os.environ.get("OSL_RELEASE_LOC_E2E_FALLBACK_REPO", "OpenSpo
98
99
  # the (non-sharded, 2210-file) SoccerNet-ActionSpotting-Features dataset only
99
100
  # if that branch isn't up.
100
101
  FEATURES_PRIMARY_REPO = os.environ.get("OSL_RELEASE_LOC_FEATURES_REPO", "OpenSportsLab/OSL-SoccerNet")
102
+ PUBLISHED_LOCALIZATION_MODEL = "OpenSportsLab/OSL-loc-snbas-2025-e2e"
101
103
  FEATURES_PRIMARY_REVISION = os.environ.get("OSL_RELEASE_LOC_FEATURES_REVISION", "ResNET_PCA512")
102
104
  FEATURES_FALLBACK_REPO = os.environ.get(
103
105
  "OSL_RELEASE_LOC_FEATURES_FALLBACK_REPO", "OpenSportsLab/SoccerNet-ActionSpotting-Features"
@@ -200,10 +202,10 @@ def _e2e_overrides(run_name: str, dataset: dict, backbone: str, head: str, *, lo
200
202
  "task_head": {"source": {"name": head}},
201
203
  }
202
204
  }
203
- overrides["TRAIN"] = {
204
- "epochs": epochs_for(2),
205
- "scheduler": {"num_epochs": epochs_for(2)},
206
- }
205
+ train_overrides = training_overrides(2)
206
+ overrides["TRAIN"] = train_overrides
207
+ if "epochs" in train_overrides:
208
+ overrides["TRAIN"]["scheduler"] = {"num_epochs": train_overrides["epochs"]}
207
209
  return overrides
208
210
 
209
211
 
@@ -226,8 +228,9 @@ def _run_localization_pipeline(config_path: str, dataset: dict, run_name: str) -
226
228
  config_path=config_path, checkpoint_path=str(checkpoint),
227
229
  )
228
230
 
229
- report_step(f"[{run_name}] infer()")
230
- predictions = model.infer(test_set=str(split_paths["test"]), weights=checkpoint, use_wandb=False)
231
+ report_step(f"[{run_name}] fresh checkpoint load + infer()")
232
+ restored_model = LocalizationModel(config=config_path, weights=checkpoint)
233
+ predictions = restored_model.infer(test_set=str(split_paths["test"]), use_wandb=False)
231
234
  assert isinstance(predictions, dict) and predictions.get("data"), f"[{run_name}] empty predictions"
232
235
 
233
236
  report_step(f"[{run_name}] save_predictions()")
@@ -237,7 +240,7 @@ def _run_localization_pipeline(config_path: str, dataset: dict, run_name: str) -
237
240
  record_release_metadata("prediction", task="localization", model_family=run_name, prediction_path=str(pred_path))
238
241
 
239
242
  report_step(f"[{run_name}] evaluate()")
240
- metrics = model.evaluate(test_set=str(split_paths["test"]), use_wandb=False)
243
+ metrics = restored_model.evaluate(test_set=str(split_paths["test"]), predictions=predictions, use_wandb=False)
241
244
  record_release_metadata(
242
245
  "result", task="localization", model_family=run_name,
243
246
  runtime_seconds=round(time.perf_counter() - started, 3),
@@ -245,6 +248,22 @@ def _run_localization_pipeline(config_path: str, dataset: dict, run_name: str) -
245
248
  print(f"[{run_name}] metrics: {metrics}")
246
249
 
247
250
 
251
+ @pytest.mark.release
252
+ def test_published_localization_model_inference(e2e_localization_dataset):
253
+ require_release_enabled()
254
+ from ._release_common import require_model_repo
255
+
256
+ require_model_repo(PUBLISHED_LOCALIZATION_MODEL)
257
+ dataset = e2e_localization_dataset
258
+ overrides = _e2e_overrides("published_loc_e2e", dataset, "rny008_gsm", "gru", loader_backend="dali")
259
+ config_path = materialize_config("localization", "video_dali", overrides, out_name="published_loc_e2e.yaml")
260
+ model = LocalizationModel(config=config_path, weights=PUBLISHED_LOCALIZATION_MODEL)
261
+ predictions = model.infer(test_set=str(dataset["split_paths"]["test"]), use_wandb=False)
262
+ assert isinstance(predictions, dict) and predictions.get("data")
263
+ metrics = model.evaluate(test_set=str(dataset["split_paths"]["test"]), predictions=predictions, use_wandb=False)
264
+ record_release_metadata("published_model_result", task="localization", model_id=PUBLISHED_LOCALIZATION_MODEL, metrics=metrics)
265
+
266
+
248
267
  # Curated backbone x head coverage. Full cross product of every
249
268
  # backbone_type/head_type check_config() allows (see opensportslib/core/utils/
250
269
  # load_annotations.py::check_config) would be 9 backbones x 4 heads = 36 runs;
@@ -253,7 +272,11 @@ def _run_localization_pipeline(config_path: str, dataset: dict, run_name: str) -
253
272
  # coverage on a dedicated release-test machine.
254
273
  E2E_BACKBONES = ["rn18", "rn50", "rny002", "rny008_gsm", "convnextt"]
255
274
  E2E_HEADS = ["gru", "deeper_gru", "mstcn", "asformer"]
256
- E2E_MATRIX = [(b, "gru") for b in E2E_BACKBONES] + [("rn18", h) for h in E2E_HEADS if h != "gru"]
275
+ E2E_MATRIX = (
276
+ [(backbone, head) for backbone in E2E_BACKBONES for head in E2E_HEADS]
277
+ if release_scale() == "full"
278
+ else [(b, "gru") for b in E2E_BACKBONES] + [("rn18", h) for h in E2E_HEADS if h != "gru"]
279
+ )
257
280
 
258
281
 
259
282
  @pytest.mark.release
@@ -364,7 +387,7 @@ def _feature_family_overrides(run_name: str, dataset: dict) -> dict:
364
387
  },
365
388
  },
366
389
  }
367
- overrides["TRAIN"] = {"epochs": epochs_for(3)}
390
+ overrides["TRAIN"] = training_overrides(3)
368
391
  return overrides
369
392
 
370
393