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.
Files changed (250) hide show
  1. {opensportslib-0.3.1.dev27/opensportslib.egg-info → opensportslib-0.3.2}/PKG-INFO +2 -8
  2. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/README.md +1 -7
  3. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2/opensportslib.egg-info}/PKG-INFO +2 -8
  4. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib.egg-info/SOURCES.txt +1 -8
  5. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/pyproject.toml +2 -2
  6. opensportslib-0.3.2/tests/unit/api/test_server_jobs.py +63 -0
  7. opensportslib-0.3.2/tests/unit/api/test_server_registry.py +131 -0
  8. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/contracts/test_package.py +2 -3
  9. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/core/test_classification_trainer.py +0 -90
  10. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/data/test_localization_intervals.py +1 -45
  11. opensportslib-0.3.2/tests/unit/models/test_localization_metrics.py +25 -0
  12. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/models/test_vqa_qwen_xvars.py +103 -1
  13. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/tools/test_hf_transfer.py +0 -178
  14. opensportslib-0.3.1.dev27/tests/helpers/causal_lm.py +0 -110
  15. opensportslib-0.3.1.dev27/tests/unit/api/test_server_jobs.py +0 -121
  16. opensportslib-0.3.1.dev27/tests/unit/api/test_server_registry.py +0 -314
  17. opensportslib-0.3.1.dev27/tests/unit/core/test_hf_runtime.py +0 -159
  18. opensportslib-0.3.1.dev27/tests/unit/models/test_localization_metrics.py +0 -129
  19. opensportslib-0.3.1.dev27/tests/unit/tools/test_spotting_classification.py +0 -489
  20. opensportslib-0.3.1.dev27/tools/convert/spotting_to_classification.py +0 -902
  21. opensportslib-0.3.1.dev27/tools/convert/spotting_tracking_to_classification.py +0 -414
  22. opensportslib-0.3.1.dev27/tools/upload/upload_sngar_tracking_full.py +0 -107
  23. opensportslib-0.3.1.dev27/tools/upload/upload_sngar_videos.py +0 -130
  24. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/LICENSE +0 -0
  25. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/LICENSE-COMMERCIAL +0 -0
  26. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/MANIFEST.in +0 -0
  27. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/examples/quickstart/basic_classification.py +0 -0
  28. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/examples/quickstart/basic_localization.py +0 -0
  29. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/examples/quickstart/basic_vqa.py +0 -0
  30. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/__init__.py +0 -0
  31. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/adaptation/__init__.py +0 -0
  32. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/adaptation/spotta.py +0 -0
  33. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/apis/__init__.py +0 -0
  34. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/apis/base_task_model.py +0 -0
  35. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/apis/classification.py +0 -0
  36. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/apis/config.py +0 -0
  37. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/apis/configuration.py +0 -0
  38. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/apis/localization.py +0 -0
  39. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/apis/vqa.py +0 -0
  40. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/cli.py +0 -0
  41. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/classification/default.yaml +0 -0
  42. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/classification/sngar_frames.yaml +0 -0
  43. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/classification/sngar_tracking.yaml +0 -0
  44. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/classification/video.yaml +0 -0
  45. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/default.yaml +0 -0
  46. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/localization/calf_resnetpca512.yaml +0 -0
  47. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/localization/default.yaml +0 -0
  48. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/localization/e2e_spotta.yaml +0 -0
  49. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/localization/h5_header_distance.yaml +0 -0
  50. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/localization/h5_header_skeleton.yaml +0 -0
  51. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/localization/netvladpp_resnetpca512.yaml +0 -0
  52. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/localization/tracking_action_spotting.yaml +0 -0
  53. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/localization/video_dali.yaml +0 -0
  54. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/localization/video_ocv.yaml +0 -0
  55. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/vqa/default.yaml +0 -0
  56. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/vqa/qwen.yaml +0 -0
  57. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/vqa/qwen3_vl_native.yaml +0 -0
  58. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/vqa/qwen_lora.yaml +0 -0
  59. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/vqa/qwen_sngar_frames.yaml +0 -0
  60. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/configs/vqa/xvars.yaml +0 -0
  61. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/__init__.py +0 -0
  62. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/__init__.py +0 -0
  63. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/accessors.py +0 -0
  64. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/conflicts.py +0 -0
  65. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/editable.py +0 -0
  66. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/loader.py +0 -0
  67. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/migrate.py +0 -0
  68. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/migrations/__init__.py +0 -0
  69. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/migrations/legacy_to_canonical.py +0 -0
  70. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/rule_variants.py +0 -0
  71. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/runtime_adapter.py +0 -0
  72. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/schema.py +0 -0
  73. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/schemas/__init__.py +0 -0
  74. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/schemas/schema_canonical.py +0 -0
  75. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/schemas/schema_legacy.py +0 -0
  76. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/config/validate.py +0 -0
  77. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/loss/__init__.py +0 -0
  78. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/loss/builder.py +0 -0
  79. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/loss/calf.py +0 -0
  80. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/loss/ce.py +0 -0
  81. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/loss/combine.py +0 -0
  82. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/loss/nll.py +0 -0
  83. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/optimizer/__init__.py +0 -0
  84. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/optimizer/builder.py +0 -0
  85. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/sampler/weighted_sampler.py +0 -0
  86. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/scheduler/__init__.py +0 -0
  87. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/scheduler/builder.py +0 -0
  88. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/trainer/__init__.py +0 -0
  89. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/trainer/classification_trainer.py +0 -0
  90. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/trainer/localization_trainer.py +0 -0
  91. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/trainer/vqa_trainer.py +0 -0
  92. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/checkpoint.py +0 -0
  93. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/config.py +0 -0
  94. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/config_normalize.py +0 -0
  95. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/data.py +0 -0
  96. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/ddp.py +0 -0
  97. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/default_args.py +0 -0
  98. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/direct_video.py +0 -0
  99. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/hf_runtime.py +0 -0
  100. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/lightning.py +0 -0
  101. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/load_annotations.py +0 -0
  102. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/seed.py +0 -0
  103. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/video_processing.py +0 -0
  104. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/core/utils/wandb.py +0 -0
  105. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/__init__.py +0 -0
  106. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/builder.py +0 -0
  107. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/classification_dataset.py +0 -0
  108. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/hf_json.py +0 -0
  109. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/hf_tracking.py +0 -0
  110. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/localization_dataset.py +0 -0
  111. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/utils/__init__.py +0 -0
  112. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/utils/h5_tracking.py +0 -0
  113. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/utils/tracking.py +0 -0
  114. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/datasets/vqa_dataset.py +0 -0
  115. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/legacy_config/classification.yaml +0 -0
  116. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/legacy_config/localization-e2e-ocv.yaml +0 -0
  117. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/legacy_config/localization-json_calf_resnetpca512.yaml +0 -0
  118. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/legacy_config/localization-json_netvlad++_resnetpca512.yaml +0 -0
  119. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/legacy_config/localization.yaml +0 -0
  120. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/legacy_config/sngar-frames.yaml +0 -0
  121. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/legacy_config/sngar-tracking.yaml +0 -0
  122. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/metrics/classification_metric.py +0 -0
  123. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/metrics/localization_metric.py +0 -0
  124. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/metrics/vqa_metric.py +0 -0
  125. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/__init__.py +0 -0
  126. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/backbones/builder.py +0 -0
  127. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/contextaware.py +0 -0
  128. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/e2e.py +0 -0
  129. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/learnablepooling.py +0 -0
  130. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/qwen_vl_native.py +0 -0
  131. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/qwen_xvars.py +0 -0
  132. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/rule_based.py +0 -0
  133. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/tracking.py +0 -0
  134. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/vars.py +0 -0
  135. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/video.py +0 -0
  136. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/video_chatgpt_compat.py +0 -0
  137. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/video_mae.py +0 -0
  138. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/base/xvars_videochatgpt.py +0 -0
  139. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/builder.py +0 -0
  140. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/heads/builder.py +0 -0
  141. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/neck/builder.py +0 -0
  142. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/common.py +0 -0
  143. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/impl/__init__.py +0 -0
  144. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/impl/asformer.py +0 -0
  145. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/impl/calf.py +0 -0
  146. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/impl/gsm.py +0 -0
  147. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/impl/gtad.py +0 -0
  148. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/impl/tsm.py +0 -0
  149. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/litebase.py +0 -0
  150. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/modules.py +0 -0
  151. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/shift.py +0 -0
  152. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/utils.py +0 -0
  153. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/vqa_prediction_priors.py +0 -0
  154. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/vqa_prompting.py +0 -0
  155. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/models/utils/xvars_clip_index.py +0 -0
  156. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/remote_registry.py +0 -0
  157. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/setup/setup.py +0 -0
  158. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/tools/__init__.py +0 -0
  159. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/tools/_common.py +0 -0
  160. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/tools/hf_transfer.py +0 -0
  161. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/tools/osl_json_to_parquet.py +0 -0
  162. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib/tools/parquet_to_osl_json.py +0 -0
  163. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib.egg-info/dependency_links.txt +0 -0
  164. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib.egg-info/entry_points.txt +0 -0
  165. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib.egg-info/requires.txt +0 -0
  166. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/opensportslib.egg-info/top_level.txt +0 -0
  167. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/scripts/redact_test_stream.py +0 -0
  168. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/scripts/release_versions.py +0 -0
  169. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/scripts/run_h5_header_rule_inference.py +0 -0
  170. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/scripts/summarize_test_report.py +0 -0
  171. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/scripts/verify_release_manifests.py +0 -0
  172. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/scripts/write_release_manifest.py +0 -0
  173. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/setup.cfg +0 -0
  174. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/__init__.py +0 -0
  175. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/conftest.py +0 -0
  176. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/helpers/__init__.py +0 -0
  177. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/helpers/configs.py +0 -0
  178. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/helpers/pipeline_stubs.py +0 -0
  179. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/integration/classification/test_pipeline.py +0 -0
  180. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/integration/localization/test_pipeline.py +0 -0
  181. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/integration/localization/test_spotta.py +0 -0
  182. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/integration/vqa/test_pipeline.py +0 -0
  183. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/release/__init__.py +0 -0
  184. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/release/_release_common.py +0 -0
  185. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/release/test_00_environment.py +0 -0
  186. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/release/test_classification_release.py +0 -0
  187. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/release/test_localization_release.py +0 -0
  188. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/release/test_vqa_release.py +0 -0
  189. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/smoke/test_cli.py +0 -0
  190. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/smoke/test_config_utils.py +0 -0
  191. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/smoke/test_environment.py +0 -0
  192. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/smoke/test_package.py +0 -0
  193. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/smoke/test_public_apis.py +0 -0
  194. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/api/test_remote_registry.py +0 -0
  195. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/api/test_remote_sessions.py +0 -0
  196. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/api/test_server_startup_scripts.py +0 -0
  197. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/api/test_task_model_contract.py +0 -0
  198. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/api/test_vqa.py +0 -0
  199. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/config/test_architecture.py +0 -0
  200. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/config/test_editable.py +0 -0
  201. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/config/test_optional_hf.py +0 -0
  202. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/config/test_pretrained_merge.py +0 -0
  203. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/config/test_split_overrides.py +0 -0
  204. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/contracts/test_architecture.py +0 -0
  205. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/contracts/test_dependencies.py +0 -0
  206. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/contracts/test_release_versions.py +0 -0
  207. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/contracts/test_runner_contract.py +0 -0
  208. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/core/test_localization_hf_backend.py +0 -0
  209. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/core/test_training_components.py +0 -0
  210. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/core/test_video_processing.py +0 -0
  211. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/data/test_classification_paths.py +0 -0
  212. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/data/test_h5_tracking.py +0 -0
  213. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/data/test_hf_json_spotting.py +0 -0
  214. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/data/test_hf_json_tracking_spotting.py +0 -0
  215. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/data/test_hf_tracking_dataset.py +0 -0
  216. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/data/test_localization_dali.py +0 -0
  217. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/data/test_osl_formats.py +0 -0
  218. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/models/test_classification_metrics.py +0 -0
  219. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/models/test_header_rule_spotter.py +0 -0
  220. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/models/test_header_skeleton_spotter.py +0 -0
  221. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/models/test_temporal_shift.py +0 -0
  222. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/models/test_vqa_metrics.py +0 -0
  223. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/models/test_vqa_training_lora.py +0 -0
  224. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/models/test_vqa_xvars_videochatgpt.py +0 -0
  225. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/tools/test_conversion.py +0 -0
  226. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/tools/test_extract_xvars_features.py +0 -0
  227. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/tools/test_release_manifest.py +0 -0
  228. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/tools/test_report_summary.py +0 -0
  229. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tests/unit/tools/test_setup.py +0 -0
  230. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/build_sn_vqa_2026_vqa.py +0 -0
  231. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/build_sngar_spotting.py +0 -0
  232. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/build_soccernet_gar.py +0 -0
  233. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/build_soccernet_gar_action_spotting.py +0 -0
  234. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/build_soccernet_gar_vqa.py +0 -0
  235. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/build_xvars_indexes.py +0 -0
  236. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/extract_xvars_clip_features.py +0 -0
  237. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/osl_json_to_parquet_webdataset.py +0 -0
  238. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/parquet_webdataset_to_osl_json.py +0 -0
  239. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/sngar_dataset_card.py +0 -0
  240. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/sngar_events.py +0 -0
  241. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/convert/verify_sngar_spotting.py +0 -0
  242. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/download/download_hf_repo.py +0 -0
  243. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/download/download_osl_hf.py +0 -0
  244. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/download/push_sngar_spotting.py +0 -0
  245. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/download/upload_osl_hf.py +0 -0
  246. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/train/train_config.py +0 -0
  247. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/training/classification.py +0 -0
  248. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/training/localization.py +0 -0
  249. {opensportslib-0.3.1.dev27 → opensportslib-0.3.2}/tools/training/vqa.py +0 -0
  250. {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.1.dev27
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), including checkpoint and config-only rule-model registration
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), including checkpoint and config-only rule-model registration
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.1.dev27
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), including checkpoint and config-only rule-model registration
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.1.dev27"
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", "server",]
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
- @pytest.mark.parametrize("filename", ["coverage-baseline.txt", "server-coverage-baseline.txt"])
51
- def test_coverage_baseline_is_a_valid_committed_percentage(filename):
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
 
@@ -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"] == []
@@ -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
- self.released = True
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
- from tests.helpers.causal_lm import TinyGenerateLM, TinyTokenizer
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):