weightslab 1.2.6__tar.gz → 1.3.1__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 (372) hide show
  1. {weightslab-1.2.6 → weightslab-1.3.1}/.github/workflows/ci.yml +10 -29
  2. {weightslab-1.2.6 → weightslab-1.3.1}/.github/workflows/release.yml +297 -49
  3. {weightslab-1.2.6 → weightslab-1.3.1}/AGENTS.md +12 -5
  4. weightslab-1.3.1/CHANGELOG.md +1 -0
  5. weightslab-1.3.1/PKG-INFO +390 -0
  6. weightslab-1.3.1/README.md +331 -0
  7. {weightslab-1.2.6 → weightslab-1.3.1}/agent_config.yaml +7 -1
  8. weightslab-1.3.1/docs/_static/custom.css +584 -0
  9. weightslab-1.3.1/docs/_static/examples-gallery.js +208 -0
  10. weightslab-1.3.1/docs/_static/github-stars.js +17 -0
  11. {weightslab-1.2.6 → weightslab-1.3.1}/docs/_static/version-switcher.js +10 -11
  12. weightslab-1.3.1/docs/_static/wl-ribbon.js +49 -0
  13. weightslab-1.3.1/docs/_static/wl-topnav.js +89 -0
  14. weightslab-1.3.1/docs/_templates/sidebar/brand.html +21 -0
  15. weightslab-1.3.1/docs/agent.rst +650 -0
  16. {weightslab-1.2.6 → weightslab-1.3.1}/docs/conf.py +15 -5
  17. weightslab-1.3.1/docs/configuration.rst +1082 -0
  18. {weightslab-1.2.6 → weightslab-1.3.1}/docs/data_exploration.rst +6 -0
  19. weightslab-1.3.1/docs/examples/index.rst +21 -0
  20. weightslab-1.3.1/docs/examples/lightning/classification.rst +119 -0
  21. weightslab-1.3.1/docs/examples/lightning/index.rst +19 -0
  22. weightslab-1.3.1/docs/examples/pytorch/classification.rst +161 -0
  23. weightslab-1.3.1/docs/examples/pytorch/clustering.rst +97 -0
  24. weightslab-1.3.1/docs/examples/pytorch/detection.rst +129 -0
  25. weightslab-1.3.1/docs/examples/pytorch/generation.rst +103 -0
  26. weightslab-1.3.1/docs/examples/pytorch/index.rst +23 -0
  27. weightslab-1.3.1/docs/examples/pytorch/segmentation.rst +116 -0
  28. weightslab-1.3.1/docs/examples/ultralytics/detection.rst +47 -0
  29. weightslab-1.3.1/docs/examples/ultralytics/index.rst +19 -0
  30. weightslab-1.3.1/docs/examples/usecases/index.rst +20 -0
  31. weightslab-1.3.1/docs/examples/usecases/lidar_detection.rst +126 -0
  32. weightslab-1.3.1/docs/examples/usecases/loss_shape_classification.rst +210 -0
  33. weightslab-1.3.1/docs/four_way_approach.rst +58 -0
  34. {weightslab-1.2.6 → weightslab-1.3.1}/docs/grpc/audit_logger.rst +4 -0
  35. {weightslab-1.2.6 → weightslab-1.3.1}/docs/grpc/grpc_functions.rst +0 -1
  36. {weightslab-1.2.6 → weightslab-1.3.1}/docs/index.rst +71 -16
  37. {weightslab-1.2.6 → weightslab-1.3.1}/docs/logger.rst +31 -0
  38. weightslab-1.3.1/docs/quickstart.rst +193 -0
  39. {weightslab-1.2.6 → weightslab-1.3.1}/docs/segmentation_usecase.rst +1 -1
  40. weightslab-1.3.1/docs/ultralytics.rst +224 -0
  41. weightslab-1.3.1/docs/usage/docker.rst +350 -0
  42. weightslab-1.3.1/docs/usage/good_practice.rst +211 -0
  43. weightslab-1.3.1/docs/usage/parameters.rst +528 -0
  44. weightslab-1.3.1/docs/usage.rst +18 -0
  45. weightslab-1.3.1/docs/user_commands.rst +440 -0
  46. {weightslab-1.2.6 → weightslab-1.3.1}/docs/user_functions.rst +437 -15
  47. {weightslab-1.2.6 → weightslab-1.3.1}/docs/weights_studio.rst +26 -42
  48. {weightslab-1.2.6 → weightslab-1.3.1}/pyproject.toml +26 -4
  49. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/__init__.py +1 -1
  50. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/backend/test_compare_dataloaders.py +65 -65
  51. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/backend/test_data_loader_interface.py +65 -65
  52. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/backend/test_instance_signal_logger.py +28 -37
  53. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/backend/test_ledgers.py +57 -3
  54. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/backend/test_logger_core.py +162 -54
  55. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/backend/test_ui_docker_bridge.py +15 -15
  56. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/backend/test_write_dataframe.py +3 -3
  57. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/backend/test_write_history.py +5 -5
  58. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/chaos_monkeys_utests/test_grpc_chaos_monkey_robustness.py +1 -1
  59. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/components/test_checkpoint_workflow.py +68 -64
  60. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/components/test_global_monitoring_unit.py +21 -21
  61. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/gRPC/test_get_point_cloud.py +39 -1
  62. weightslab-1.3.1/tests/gRPC/test_grpc_histogram_categorical.py +205 -0
  63. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/gRPC/test_grpc_tag_operations.py +26 -26
  64. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/gRPC/test_grpc_user_actions.py +53 -11
  65. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/general/test_cli.py +181 -87
  66. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/general/test_signals.py +10 -10
  67. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/general/test_signals_wrapping.py +4 -4
  68. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/integrations/test_pytorch_lightning_integration.py +112 -112
  69. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/integrations/ultralytics/ddp/ddp_ablation.py +13 -13
  70. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/integrations/ultralytics/ddp/ddp_test_suite.py +108 -108
  71. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/model/test_constraint_generation.py +4 -4
  72. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/model/test_dependency_patterns.py +6 -6
  73. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/model/test_model_with_ops.py +2 -2
  74. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/model/test_tracking.py +1 -1
  75. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/modules/test_modules_with_ops.py +18 -18
  76. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/test_secure_docker.py +1 -1
  77. weightslab-1.3.1/tests/trainer/services/test_agent_live_prompt_evaluation.py +1120 -0
  78. weightslab-1.3.1/tests/trainer/services/test_agent_model_and_safety_unit.py +1677 -0
  79. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/trainer/services/test_agent_prompt_unit.py +30 -2
  80. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/trainer/services/test_agent_service_unit.py +2 -2
  81. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/trainer/services/test_trainer_services_server.py +1 -1
  82. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/trainer/services/test_trainer_services_unit.py +118 -16
  83. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/watchdog/test_lock_monitor.py +1 -1
  84. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/watchdog/test_watchdog.py +14 -14
  85. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/__init__.py +71 -8
  86. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/_version.py +3 -3
  87. weightslab-1.3.1/weightslab/art.py +164 -0
  88. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/backend/audit_logger.py +2 -2
  89. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/backend/cli.py +132 -8
  90. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/backend/dataloader_interface.py +39 -39
  91. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/backend/ledgers.py +113 -19
  92. weightslab-1.3.1/weightslab/backend/logger.py +1168 -0
  93. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/backend/model_interface.py +25 -16
  94. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/backend/optimizer_interface.py +1 -1
  95. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/components/__init__.py +9 -9
  96. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/components/checkpoint_manager.py +102 -102
  97. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/components/experiment_hash.py +9 -9
  98. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/components/global_monitoring.py +17 -10
  99. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/components/parallel_primitives.py +15 -15
  100. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/components/tracking.py +10 -10
  101. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/data/array_proxy.py +3 -3
  102. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/data/data_samples_with_ops.py +20 -20
  103. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/data/data_utils.py +40 -33
  104. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/data/dataframe_manager.py +48 -44
  105. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/data/h5_array_store.py +7 -7
  106. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/data/h5_dataframe_store.py +44 -25
  107. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/data/point_cloud_utils.py +21 -21
  108. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/data/sample_stats.py +6 -6
  109. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/docker/docker-compose.yml +16 -1
  110. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/docker/nginx-entrypoint.sh +0 -0
  111. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/docker/test-deployment.sh +0 -0
  112. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/docker/utils/build-and-deploy.sh +57 -6
  113. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/docker/utils/generate-certs-auth-token.ps1 +0 -0
  114. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/docker/utils/generate-certs-auth-token.sh +0 -0
  115. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/docker/utils/test-deployment.sh +0 -0
  116. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Docker_training/docker_in_docker/Dockerfile +1 -2
  117. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Docker_training/siblings_self_contained/Dockerfile +1 -2
  118. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Lightning/ws-classification/main.py +11 -11
  119. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-classification/config.yaml +4 -4
  120. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-classification/main.py +28 -21
  121. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-clustering/face/data.py +47 -47
  122. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-clustering/face/model.py +22 -22
  123. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-clustering/face/signals.py +2 -2
  124. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-clustering/face/utils.py +7 -7
  125. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-clustering/main.py +23 -23
  126. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-detection/main.py +16 -16
  127. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-detection/utils/criterions.py +18 -18
  128. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-detection/utils/data.py +15 -15
  129. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-detection/utils/model.py +11 -11
  130. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-generation/main.py +23 -23
  131. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/config.yaml +2 -1
  132. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/main.py +51 -31
  133. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/utils/criterions.py +9 -9
  134. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/utils/data.py +19 -8
  135. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/utils/model.py +2 -2
  136. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Ultralytics/ws-detection/config.yaml +3 -3
  137. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Ultralytics/ws-detection/main.py +13 -9
  138. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Usecases/ws-2d-lidar-detection/config.yaml +1 -0
  139. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Usecases/ws-2d-lidar-detection/main.py +6 -6
  140. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Usecases/ws-2d-lidar-detection/utils/criterions.py +4 -4
  141. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Usecases/ws-2d-lidar-detection/utils/data.py +8 -8
  142. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Usecases/ws-2d-lidar-detection/utils/model.py +12 -12
  143. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Usecases/ws-3d-lidar-detection/main.py +34 -34
  144. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Usecases/ws-3d-lidar-detection/utils/criterions.py +25 -25
  145. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Usecases/ws-3d-lidar-detection/utils/data.py +47 -47
  146. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Usecases/ws-3d-lidar-detection/utils/kitti_download.py +5 -5
  147. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Usecases/ws-3d-lidar-detection/utils/model.py +28 -28
  148. weightslab-1.3.1/weightslab/examples/Usecases/ws-loss_shapes_classification_per_sample/README.md +102 -0
  149. weightslab-1.3.1/weightslab/examples/Usecases/ws-loss_shapes_classification_per_sample/config.yaml +50 -0
  150. weightslab-1.3.1/weightslab/examples/Usecases/ws-loss_shapes_classification_per_sample/main.py +340 -0
  151. weightslab-1.3.1/weightslab/examples/Usecases/ws-loss_shapes_classification_per_sample/utils/criterions.py +361 -0
  152. weightslab-1.3.1/weightslab/examples/Usecases/ws-loss_shapes_classification_per_sample/utils/data.py +226 -0
  153. weightslab-1.3.1/weightslab/examples/Usecases/ws-loss_shapes_classification_per_sample/utils/model.py +134 -0
  154. weightslab-1.3.1/weightslab/examples/__init__.py +6 -0
  155. weightslab-1.3.1/weightslab/examples/utils/__init__.py +1 -0
  156. {weightslab-1.2.6/weightslab → weightslab-1.3.1/weightslab/examples/utils}/baseline_models/pytorch/models.py +65 -65
  157. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/integrations/ultralytics/__init__.py +2 -2
  158. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/integrations/ultralytics/dataset.py +2 -2
  159. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/integrations/ultralytics/signals.py +12 -12
  160. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/integrations/ultralytics/trainer.py +10 -10
  161. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/models/model_with_ops.py +4 -4
  162. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/models/monkey_patcher.py +1 -1
  163. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/modules/modules_with_ops.py +27 -27
  164. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/proto/experiment_service.proto +71 -0
  165. weightslab-1.3.1/weightslab/proto/experiment_service_pb2.py +196 -0
  166. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/proto/experiment_service_pb2_grpc.py +91 -0
  167. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/security/cert_auth_manager.py +2 -2
  168. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/src.py +173 -158
  169. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/trainer/experiment_context.py +8 -4
  170. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/trainer/services/agent/agent.py +836 -39
  171. weightslab-1.3.1/weightslab/trainer/services/agent/intent_prompt.py +843 -0
  172. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/trainer/services/agent_service.py +5 -5
  173. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/trainer/services/data_image_utils.py +3 -3
  174. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/trainer/services/data_service.py +1300 -261
  175. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/trainer/services/experiment_service.py +249 -14
  176. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/trainer/services/instance_merger.py +6 -6
  177. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/trainer/services/model_service.py +12 -2
  178. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/trainer/services/utils/tools.py +1 -1
  179. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/trainer/trainer_services.py +19 -12
  180. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/trainer/trainer_tools.py +21 -19
  181. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/ui_docker_bridge.py +171 -69
  182. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/utils/computational_graph.py +92 -92
  183. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/utils/logs.py +1 -1
  184. weightslab-1.3.1/weightslab/utils/telemetry.py +152 -0
  185. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/utils/tools.py +18 -16
  186. weightslab-1.3.1/weightslab/watchdog/__init__.py +30 -0
  187. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/watchdog/grpc_watchdog.py +2 -2
  188. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/watchdog/lock_monitor.py +7 -7
  189. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/watchdog/log_level.py +1 -1
  190. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/watchdog/watchdog.py +20 -20
  191. weightslab-1.3.1/weightslab.egg-info/PKG-INFO +390 -0
  192. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab.egg-info/SOURCES.txt +127 -97
  193. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab.egg-info/requires.txt +2 -2
  194. weightslab-1.3.1/weightslab.egg-info/scm_file_list.json +342 -0
  195. weightslab-1.3.1/weightslab.egg-info/scm_version.json +8 -0
  196. weightslab-1.2.6/CHANGELOG.md +0 -1
  197. weightslab-1.2.6/PKG-INFO +0 -481
  198. weightslab-1.2.6/README.md +0 -422
  199. weightslab-1.2.6/docs/_static/custom.css +0 -143
  200. weightslab-1.2.6/docs/configuration.rst +0 -549
  201. weightslab-1.2.6/docs/four_way_approach.rst +0 -29
  202. weightslab-1.2.6/docs/quickstart.rst +0 -191
  203. weightslab-1.2.6/weightslab/art.py +0 -58
  204. weightslab-1.2.6/weightslab/backend/logger.py +0 -1058
  205. weightslab-1.2.6/weightslab/examples/Ultralytics/ws-detection/requirements.txt +0 -1
  206. weightslab-1.2.6/weightslab/proto/experiment_service_pb2.py +0 -180
  207. weightslab-1.2.6/weightslab/tests/data/test_data_samples_with_ops.py +0 -783
  208. weightslab-1.2.6/weightslab/tests/data/test_data_service_metadata_copy.py +0 -97
  209. weightslab-1.2.6/weightslab/tests/data/test_data_utils_unit.py +0 -117
  210. weightslab-1.2.6/weightslab/tests/data/test_dataframe_manager_unit.py +0 -428
  211. weightslab-1.2.6/weightslab/tests/data/test_flush_pipeline.py +0 -277
  212. weightslab-1.2.6/weightslab/tests/data/test_h5_array_store.py +0 -217
  213. weightslab-1.2.6/weightslab/tests/data/test_h5_dataframe_store.py +0 -219
  214. weightslab-1.2.6/weightslab/tests/data/test_point_cloud_utils.py +0 -330
  215. weightslab-1.2.6/weightslab/trainer/services/agent/intent_prompt.py +0 -426
  216. weightslab-1.2.6/weightslab/trainer/services/utils/__init__.py +0 -0
  217. weightslab-1.2.6/weightslab/watchdog/__init__.py +0 -30
  218. weightslab-1.2.6/weightslab.egg-info/PKG-INFO +0 -481
  219. {weightslab-1.2.6 → weightslab-1.3.1}/.gitattributes +0 -0
  220. {weightslab-1.2.6 → weightslab-1.3.1}/.github/workflows/docs-pages.yml +0 -0
  221. {weightslab-1.2.6 → weightslab-1.3.1}/.gitignore +0 -0
  222. {weightslab-1.2.6 → weightslab-1.3.1}/LICENSE +0 -0
  223. {weightslab-1.2.6 → weightslab-1.3.1}/docs/_static/.gitkeep +0 -0
  224. {weightslab-1.2.6 → weightslab-1.3.1}/docs/_static/favicon.png +0 -0
  225. {weightslab-1.2.6 → weightslab-1.3.1}/docs/_static/logo-dark.png +0 -0
  226. {weightslab-1.2.6 → weightslab-1.3.1}/docs/_static/logo-light.png +0 -0
  227. {weightslab-1.2.6 → weightslab-1.3.1}/docs/_static/weights_studio_architecture.png +0 -0
  228. {weightslab-1.2.6 → weightslab-1.3.1}/docs/_templates/.gitkeep +0 -0
  229. {weightslab-1.2.6 → weightslab-1.3.1}/docs/grpc/index.rst +0 -0
  230. {weightslab-1.2.6 → weightslab-1.3.1}/docs/hyperparameters.rst +0 -0
  231. {weightslab-1.2.6 → weightslab-1.3.1}/docs/model_interaction.rst +0 -0
  232. {weightslab-1.2.6 → weightslab-1.3.1}/docs/pytorch_lightning.rst +0 -0
  233. {weightslab-1.2.6 → weightslab-1.3.1}/docs/requirements.txt +0 -0
  234. {weightslab-1.2.6 → weightslab-1.3.1}/docs/usecases.rst +0 -0
  235. {weightslab-1.2.6 → weightslab-1.3.1}/setup.cfg +0 -0
  236. {weightslab-1.2.6/weightslab → weightslab-1.3.1/tests}/backend/__init__.py +0 -0
  237. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/backend/test_audit_logger.py +0 -0
  238. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/backend/test_cli_additional_unit.py +0 -0
  239. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/backend/test_model_interface_unit.py +0 -0
  240. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/backend/test_optimizer_interface.py +0 -0
  241. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/backend/test_optimizer_interface_additional_unit.py +0 -0
  242. {weightslab-1.2.6/weightslab/baseline_models → weightslab-1.3.1/tests/chaos_monkeys_utests}/__init__.py +0 -0
  243. {weightslab-1.2.6/weightslab/baseline_models/pytorch → weightslab-1.3.1/tests/components}/__init__.py +0 -0
  244. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/components/test_experiment_hash_and_art.py +0 -0
  245. {weightslab-1.2.6/weightslab/data → weightslab-1.3.1/tests/gRPC}/__init__.py +0 -0
  246. {weightslab-1.2.6/weightslab/examples/PyTorch/ws-clustering/face → weightslab-1.3.1/tests/general}/__init__.py +0 -0
  247. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/general/test_auditor_mode.py +0 -0
  248. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/general/test_hyperparams.py +0 -0
  249. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/general/test_logger_snapshot_rotation.py +0 -0
  250. {weightslab-1.2.6/weightslab/models → weightslab-1.3.1/tests/integrations}/__init__.py +0 -0
  251. {weightslab-1.2.6/weightslab/modules → weightslab-1.3.1/tests/model}/__init__.py +0 -0
  252. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/model/test_logger.py +0 -0
  253. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/model/test_model_with_ops_unit.py +0 -0
  254. {weightslab-1.2.6/weightslab/proto → weightslab-1.3.1/tests/modules}/__init__.py +0 -0
  255. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/test_src_functions.py +0 -0
  256. {weightslab-1.2.6/weightslab/tests/backend → weightslab-1.3.1/tests/trainer}/__init__.py +0 -0
  257. {weightslab-1.2.6/weightslab/tests/chaos_monkeys_utests → weightslab-1.3.1/tests/trainer/services}/__init__.py +0 -0
  258. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/trainer/test_trainer_tools.py +0 -0
  259. {weightslab-1.2.6/weightslab/tests/components → weightslab-1.3.1/tests/utils}/__init__.py +0 -0
  260. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/utils/test_computational_graph_utils_unit.py +0 -0
  261. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/utils/test_logs_unit.py +0 -0
  262. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/utils/test_modules_dependencies_unit.py +0 -0
  263. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/utils/test_plot_graph_render_unit.py +0 -0
  264. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/utils/test_plot_graph_unit.py +0 -0
  265. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/utils/test_shape_prop_unit.py +0 -0
  266. {weightslab-1.2.6/weightslab → weightslab-1.3.1}/tests/utils/test_utils_tools_unit.py +0 -0
  267. {weightslab-1.2.6/weightslab/tests/data → weightslab-1.3.1/tests/watchdog}/__init__.py +0 -0
  268. {weightslab-1.2.6/weightslab/tests/gRPC → weightslab-1.3.1/weightslab/backend}/__init__.py +0 -0
  269. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/components/evaluation_controller.py +0 -0
  270. {weightslab-1.2.6/weightslab/tests/general → weightslab-1.3.1/weightslab/data}/__init__.py +0 -0
  271. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/Dockerfile +0 -0
  272. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/docker/.dockerignore +0 -0
  273. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/docker/.env.example +0 -0
  274. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/docker/DEPLOYMENT.md +0 -0
  275. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/docker/nginx.base-path.conf copy.template +0 -0
  276. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/docker/nginx.base-path.conf.template +0 -0
  277. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/docker/nginx.conf +0 -0
  278. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/envoy/envoy.downstream_plaintext.yaml +0 -0
  279. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/envoy/envoy.downstream_upstream_plaintext.yaml +0 -0
  280. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/envoy/envoy.upstream_plaintext.yaml +0 -0
  281. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/envoy/envoy.yaml +0 -0
  282. {weightslab-1.2.6/weightslab/ui → weightslab-1.3.1/weightslab/docker}/nginx.conf +0 -0
  283. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Docker_training/README.md +0 -0
  284. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Docker_training/docker_in_docker/README.md +0 -0
  285. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Docker_training/docker_in_docker/docker-compose.yml +0 -0
  286. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Docker_training/docker_in_docker/entrypoint.sh +0 -0
  287. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Docker_training/siblings_self_contained/README.md +0 -0
  288. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Docker_training/siblings_self_contained/docker-compose.yml +0 -0
  289. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Docker_training/siblings_self_contained/entrypoint.sh +0 -0
  290. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Docker_training/siblings_self_contained/ui-compose.yml +0 -0
  291. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Lightning/ws-classification/config.yaml +0 -0
  292. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-clustering/config.yaml +0 -0
  293. {weightslab-1.2.6/weightslab/tests/integrations → weightslab-1.3.1/weightslab/examples/PyTorch/ws-clustering/face}/__init__.py +0 -0
  294. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-detection/README.md +0 -0
  295. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-detection/config.yaml +0 -0
  296. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-generation/config.yaml +0 -0
  297. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/train/0000f77c-6257be58.jpg +0 -0
  298. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/train/000f8d37-d4c09a0f.jpg +0 -0
  299. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/train/00a0f008-a315437f.jpg +0 -0
  300. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/train/00c12bd0-bb46e479.jpg +0 -0
  301. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/train/00c29c52-f9524f1e.jpg +0 -0
  302. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/train/00ce6f6d-50bbee62.jpg +0 -0
  303. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/train/00d1bafa-1b47b41c.jpg +0 -0
  304. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/train/00d7268f-fd4487be.jpg +0 -0
  305. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/train/00f0dd0f-5e9c9557.jpg +0 -0
  306. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/train/0a0d7f4c-ac5c3c8f.jpg +0 -0
  307. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/train/0a1f4fce-f9cac880.jpg +0 -0
  308. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/train/0a3bb2d8-c195d91e.jpg +0 -0
  309. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/val/b1cac6a7-04e33135.jpg +0 -0
  310. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/val/b1ceb32e-3f481b43.jpg +0 -0
  311. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/val/b1d10d08-5b108225.jpg +0 -0
  312. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/val/b1d22449-15fb948f.jpg +0 -0
  313. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/val/b1d7b3ac-5af8623b.jpg +0 -0
  314. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/val/b1dce572-c6a8cb5e.jpg +0 -0
  315. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/val/b1e1a7b8-0aec80e8.jpg +0 -0
  316. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/val/b1e8ad72-c3c79240.jpg +0 -0
  317. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/val/b1ee702d-0ae1fc10.jpg +0 -0
  318. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/val/b1f0efd9-37a14dda.jpg +0 -0
  319. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/val/b2a0648b-d8e126bc.jpg +0 -0
  320. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/images/val/b2b70230-bad4ff6e.jpg +0 -0
  321. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/train/0000f77c-6257be58.png +0 -0
  322. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/train/000f8d37-d4c09a0f.png +0 -0
  323. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/train/00a0f008-a315437f.png +0 -0
  324. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/train/00c12bd0-bb46e479.png +0 -0
  325. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/train/00c29c52-f9524f1e.png +0 -0
  326. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/train/00ce6f6d-50bbee62.png +0 -0
  327. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/train/00d1bafa-1b47b41c.png +0 -0
  328. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/train/00d7268f-fd4487be.png +0 -0
  329. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/train/00f0dd0f-5e9c9557.png +0 -0
  330. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/train/0a0d7f4c-ac5c3c8f.png +0 -0
  331. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/train/0a1f4fce-f9cac880.png +0 -0
  332. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/train/0a3bb2d8-c195d91e.png +0 -0
  333. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/val/b1cac6a7-04e33135.png +0 -0
  334. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/val/b1ceb32e-3f481b43.png +0 -0
  335. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/val/b1d10d08-5b108225.png +0 -0
  336. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/val/b1d22449-15fb948f.png +0 -0
  337. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/val/b1d7b3ac-5af8623b.png +0 -0
  338. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/val/b1dce572-c6a8cb5e.png +0 -0
  339. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/val/b1e1a7b8-0aec80e8.png +0 -0
  340. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/val/b1e8ad72-c3c79240.png +0 -0
  341. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/val/b1ee702d-0ae1fc10.png +0 -0
  342. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/val/b1f0efd9-37a14dda.png +0 -0
  343. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/val/b2a0648b-d8e126bc.png +0 -0
  344. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/PyTorch/ws-segmentation/BDD_subset/labels/val/b2b70230-bad4ff6e.png +0 -0
  345. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Usecases/ws-2d-lidar-detection/README.md +0 -0
  346. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Usecases/ws-3d-lidar-detection/README.md +0 -0
  347. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/examples/Usecases/ws-3d-lidar-detection/config.yaml +0 -0
  348. {weightslab-1.2.6/weightslab/tests/model → weightslab-1.3.1/weightslab/examples/utils/baseline_models}/__init__.py +0 -0
  349. {weightslab-1.2.6/weightslab/tests/modules → weightslab-1.3.1/weightslab/examples/utils/baseline_models/pytorch}/__init__.py +0 -0
  350. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/integrations/__init__.py +0 -0
  351. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/integrations/ultralytics/README.md +0 -0
  352. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/integrations/ultralytics/_utils.py +0 -0
  353. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/integrations/ultralytics/collate.py +0 -0
  354. {weightslab-1.2.6/weightslab/tests/trainer → weightslab-1.3.1/weightslab/models}/__init__.py +0 -0
  355. {weightslab-1.2.6/weightslab/tests/trainer/services → weightslab-1.3.1/weightslab/modules}/__init__.py +0 -0
  356. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/modules/neuron_ops.py +0 -0
  357. {weightslab-1.2.6/weightslab/tests/utils → weightslab-1.3.1/weightslab/proto}/__init__.py +0 -0
  358. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/security/__init__.py +0 -0
  359. {weightslab-1.2.6/weightslab/tests/watchdog → weightslab-1.3.1/weightslab/trainer}/__init__.py +0 -0
  360. {weightslab-1.2.6/weightslab/trainer → weightslab-1.3.1/weightslab/trainer/services}/__init__.py +0 -0
  361. {weightslab-1.2.6/weightslab/trainer/services → weightslab-1.3.1/weightslab/trainer/services/agent}/__init__.py +0 -0
  362. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/trainer/services/agent/agent_overview/agent.png +0 -0
  363. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/trainer/services/agent/agent_overview/query_example.png +0 -0
  364. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/trainer/services/agent/agent_overview/schema.png +0 -0
  365. {weightslab-1.2.6/weightslab/trainer/services/agent → weightslab-1.3.1/weightslab/trainer/services/utils}/__init__.py +0 -0
  366. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/utils/__init__.py +0 -0
  367. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/utils/modules_dependencies.py +0 -0
  368. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/utils/plot_graph.py +0 -0
  369. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab/utils/shape_prop.py +0 -0
  370. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab.egg-info/dependency_links.txt +0 -0
  371. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab.egg-info/entry_points.txt +0 -0
  372. {weightslab-1.2.6 → weightslab-1.3.1}/weightslab.egg-info/top_level.txt +0 -0
@@ -9,7 +9,7 @@ on:
9
9
  branches: [ '**' ]
10
10
  tags: [ 'v*' ]
11
11
  pull_request:
12
- types: [ opened, reopened, ready_for_review ]
12
+ types: [ opened, reopened, ready_for_review, synchronize ]
13
13
  workflow_dispatch:
14
14
 
15
15
  jobs:
@@ -32,8 +32,8 @@ jobs:
32
32
  run_ci=true # PRs and manual dispatch always run
33
33
  elif [[ "${{ github.ref }}" == refs/tags/* ]]; then
34
34
  run_ci=false # tag pushes are handled by the release workflow
35
- elif [ "${{ github.ref }}" = "refs/heads/main" ] || [ "${{ github.ref }}" = "refs/heads/dev" ]; then
36
- run_ci=true # default branches always run
35
+ elif [ "${{ github.ref }}" = "refs/heads/main" ] || [ "${{ github.ref }}" = "refs/heads/dev" ] || [[ "${{ github.ref }}" == refs/heads/v* ]]; then
36
+ run_ci=true # default branches and release branches (v*) always run
37
37
  elif [ "${FORCE_CI}" = "true" ]; then
38
38
  run_ci=true # custom branch opted in via [force ci]
39
39
  else
@@ -57,7 +57,7 @@ jobs:
57
57
 
58
58
  - name: Set up Python
59
59
  id: setup-python
60
- uses: actions/setup-python@v5
60
+ uses: actions/setup-python@v6
61
61
  with:
62
62
  python-version: '3.11'
63
63
 
@@ -141,7 +141,7 @@ jobs:
141
141
  uses: actions/checkout@v4
142
142
 
143
143
  - name: Set up Python 3.11
144
- uses: actions/setup-python@v5
144
+ uses: actions/setup-python@v6
145
145
  with:
146
146
  python-version: '3.11'
147
147
 
@@ -179,7 +179,7 @@ jobs:
179
179
  uses: actions/checkout@v4
180
180
 
181
181
  - name: Set up Python ${{ matrix.python-version }}
182
- uses: actions/setup-python@v5
182
+ uses: actions/setup-python@v6
183
183
  with:
184
184
  python-version: ${{ matrix.python-version }}
185
185
  allow-prereleases: ${{ matrix.allow-prereleases || false }}
@@ -204,7 +204,7 @@ jobs:
204
204
  uses: actions/checkout@v4
205
205
 
206
206
  - name: Set up Python
207
- uses: actions/setup-python@v5
207
+ uses: actions/setup-python@v6
208
208
  with:
209
209
  python-version: '3.11'
210
210
 
@@ -221,26 +221,7 @@ jobs:
221
221
  run: |
222
222
  export WEIGHTSLAB_LOG_LEVEL="DEBUG"
223
223
  # A per-test timeout guards against any regression that hangs a test.
224
- python -m pytest ./weightslab/tests -v --timeout=300
225
-
226
- # TODO (GP): WL CI do not find WS CI for now; token or visibility problem ??
227
- # - name: Trigger WeightsStudio CI
228
- # env:
229
- # WS_TOKEN: ${{ secrets.WEIGHTS_STUDIO_API_TOKEN }}
230
- # run: |
231
- # if [ -z "${WS_TOKEN}" ]; then
232
- # echo "WEIGHTS_STUDIO_API_TOKEN not set; skipping WeightsStudio trigger."
233
- # exit 0
234
- # fi
235
-
236
- # # Trigger the ws-ci workflow in the weights_studio repository on main.
237
- # curl -fSs -X POST "https://api.github.com/repos/GrayboxTech/weights_studio/actions/workflows/ws-ci.yml/dispatches" \
238
- # -H "Authorization: Bearer ${WS_TOKEN}" \
239
- # -H "Accept: application/vnd.github+json" \
240
- # -H "Content-Type: application/json" \
241
- # -d '{"ref":"main"}'
242
-
243
- # echo "WeightsStudio workflow dispatch requested successfully."
224
+ python -m pytest ./tests -v --timeout=300
244
225
 
245
226
  build-and-publish-dev:
246
227
  # Only publish to TestPyPI when pushing to main (not on PRs or dev branch pushes).
@@ -259,7 +240,7 @@ jobs:
259
240
  fetch-depth: 0
260
241
 
261
242
  - name: Set up Python
262
- uses: actions/setup-python@v5
243
+ uses: actions/setup-python@v6
263
244
  with:
264
245
  python-version: '3.11'
265
246
 
@@ -351,7 +332,7 @@ jobs:
351
332
  contents: read
352
333
  steps:
353
334
  - name: Set up Python ${{ matrix.python-version }}
354
- uses: actions/setup-python@v5
335
+ uses: actions/setup-python@v6
355
336
  with:
356
337
  python-version: ${{ matrix.python-version }}
357
338
  allow-prereleases: ${{ matrix.allow-prereleases || false }}
@@ -16,6 +16,8 @@ jobs:
16
16
  if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
17
17
  outputs:
18
18
  is_main: ${{ steps.check.outputs.is_main }}
19
+ is_dev_release: ${{ steps.check.outputs.is_dev_release }}
20
+ should_run: ${{ steps.check.outputs.should_run }}
19
21
  steps:
20
22
  - uses: actions/checkout@v4
21
23
  with:
@@ -29,31 +31,53 @@ jobs:
29
31
  IS_ON_MAIN=$(git merge-base --is-ancestor "${TAG_COMMIT}" origin/main \
30
32
  && echo "true" || echo "false")
31
33
  echo "is_main=${IS_ON_MAIN}" >> $GITHUB_OUTPUT
32
- echo "Tag: ${GITHUB_REF_NAME} commit: ${TAG_COMMIT} on_main: ${IS_ON_MAIN}"
33
34
 
34
- # ── Shared: run tests before main publish only ───────────────────────────
35
+ # Dev pre-release: a tag NOT on main whose name contains 'dev'
36
+ # (e.g. v1.3.1-dev0). Runs the full pipeline up to the TestPyPI install
37
+ # tests + a GitHub pre-release, then STOPS before the production PyPI
38
+ # publish. A tag off main WITHOUT 'dev' triggers nothing (should_run=false),
39
+ # so accidental/non-dev tags on dev never release.
40
+ IS_DEV_RELEASE=false
41
+ if [ "${IS_ON_MAIN}" != "true" ] && [[ "${GITHUB_REF_NAME}" == *dev* ]]; then
42
+ IS_DEV_RELEASE=true
43
+ fi
44
+ echo "is_dev_release=${IS_DEV_RELEASE}" >> $GITHUB_OUTPUT
45
+
46
+ if [ "${IS_ON_MAIN}" = "true" ] || [ "${IS_DEV_RELEASE}" = "true" ]; then
47
+ SHOULD_RUN=true
48
+ else
49
+ SHOULD_RUN=false
50
+ fi
51
+ echo "should_run=${SHOULD_RUN}" >> $GITHUB_OUTPUT
52
+ echo "Tag: ${GITHUB_REF_NAME} commit: ${TAG_COMMIT} on_main: ${IS_ON_MAIN} dev_release: ${IS_DEV_RELEASE} should_run: ${SHOULD_RUN}"
53
+
54
+ # ── Shared: run tests before any publish (main or dev pre-release) ────────
35
55
  test:
36
56
  name: Run Tests Before Release
37
57
  needs: detect-target
38
58
  runs-on: ubuntu-latest
39
- if: ${{ needs.detect-target.outputs.is_main == 'true' }}
59
+ if: ${{ needs.detect-target.outputs.should_run == 'true' }}
40
60
  steps:
41
61
  - uses: actions/checkout@v4
42
62
 
43
- - uses: actions/setup-python@v5
63
+ - uses: actions/setup-python@v6
44
64
  with:
45
65
  python-version: '3.11'
46
66
 
47
67
  - name: Install dependencies
48
68
  run: |
49
69
  python -m pip install --upgrade pip
50
- python -m pip install . --extra-index-url https://download.pytorch.org/whl/cpu
70
+ # Install the test extra so pytest, graphviz, torchmetrics,
71
+ # pytorch-lightning and tensorboard are available (several test modules
72
+ # import pytest / use pytest fixtures and cannot run under bare unittest).
73
+ python -m pip install .[utest] --extra-index-url https://download.pytorch.org/whl/cpu
74
+ python -m pip install pytest-timeout
51
75
 
52
76
  - name: Run tests
53
77
  run: |
54
78
  export WEIGHTSLAB_LOG_LEVEL="DEBUG"
55
79
  python -m pip install pytorch_lightning
56
- python -m unittest discover -s ./weightslab/tests -p "test_*.py" -t . -v
80
+ python -m unittest discover -s ./tests -p "test_*.py" -t . -v
57
81
 
58
82
  # ═══════════════════════════════════════════════════════════════════════════
59
83
  # DEV RELEASE — tag on dev branch, not yet merged to main
@@ -62,9 +86,9 @@ jobs:
62
86
 
63
87
  build-and-publish-dev-release:
64
88
  name: Build & Publish Dev Release (TestPyPI)
65
- needs: detect-target
89
+ needs: [detect-target,test]
66
90
  runs-on: ubuntu-latest
67
- if: ${{ needs.detect-target.outputs.is_main != 'true' }}
91
+ if: ${{ needs.detect-target.outputs.should_run == 'true' }}
68
92
  permissions:
69
93
  contents: read
70
94
  outputs:
@@ -74,7 +98,7 @@ jobs:
74
98
  with:
75
99
  fetch-depth: 0
76
100
 
77
- - uses: actions/setup-python@v5
101
+ - uses: actions/setup-python@v6
78
102
  with:
79
103
  python-version: '3.11'
80
104
 
@@ -108,7 +132,7 @@ jobs:
108
132
  python -m twine upload --repository-url https://test.pypi.org/legacy/ --verbose dist/*
109
133
 
110
134
  - name: Upload build artifacts for release
111
- uses: actions/upload-artifact@v4
135
+ uses: actions/upload-artifact@v6
112
136
  with:
113
137
  name: dev-release-dist
114
138
  path: dist/
@@ -133,7 +157,7 @@ jobs:
133
157
  permissions:
134
158
  contents: read
135
159
  steps:
136
- - uses: actions/setup-python@v5
160
+ - uses: actions/setup-python@v6
137
161
  with:
138
162
  python-version: ${{ matrix.python-version }}
139
163
  allow-prereleases: ${{ matrix.allow-prereleases || false }}
@@ -183,9 +207,11 @@ jobs:
183
207
  print('installed version:', m.version('weightslab'))
184
208
  "
185
209
 
210
+ # Only for a dev pre-release tag (off main, name contains 'dev').
186
211
  create-dev-release:
187
212
  name: Create GitHub Pre-Release
188
- needs: [build-and-publish-dev-release, test-install-from-pip-dev-release]
213
+ needs: [detect-target, build-and-publish-dev-release, test-install-from-pip-dev-release]
214
+ if: ${{ needs.detect-target.outputs.is_dev_release == 'true' }}
189
215
  runs-on: ubuntu-latest
190
216
  permissions:
191
217
  contents: write
@@ -195,7 +221,7 @@ jobs:
195
221
  fetch-depth: 0
196
222
 
197
223
  - name: Download build artifacts
198
- uses: actions/download-artifact@v4
224
+ uses: actions/download-artifact@v7
199
225
  with:
200
226
  name: dev-release-dist
201
227
  path: dist/
@@ -208,47 +234,142 @@ jobs:
208
234
  export VERSION_NO_V="${VERSION#v}"
209
235
  export PYPI_URL="https://test.pypi.org/project/weightslab/${VERSION_NO_V}/"
210
236
 
211
- PREV_TAG=$(git tag --sort=-version:refname | grep -v "^${VERSION}$" | head -n 1 || true)
237
+ # Walk the actual commit graph for the nearest ancestor tag, rather
238
+ # than a lexicographic/semver sort across ALL tags. The old sort
239
+ # could pick an unrelated tag from a diverging branch — e.g. a
240
+ # "v1.3.0-dev0" tag cut on dev after "v1.3.0" was tagged on main
241
+ # sorted ABOVE it, making every real PR since v1.3.0 look "older"
242
+ # than that bogus previous-tag date and get silently filtered out
243
+ # (this is why past releases showed "no pull requests found").
244
+ PREV_TAG=$(git describe --tags --abbrev=0 "${VERSION}^" 2>/dev/null || true)
212
245
  if [ -n "$PREV_TAG" ]; then
213
- export PREV_TAG_DATE=$(git log -1 --format="%aI" "${PREV_TAG}" 2>/dev/null || echo "2020-01-01T00:00:00Z")
246
+ RAW_DATE=$(git log -1 --format="%aI" "${PREV_TAG}" 2>/dev/null || true)
247
+ # Normalize to UTC "Z" form so string comparison against GitHub's
248
+ # mergedAt (also UTC "Z") is valid regardless of the committer's
249
+ # local timezone offset.
250
+ export PREV_TAG_DATE=$(date -u -d "${RAW_DATE:-2020-01-01}" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || echo "2020-01-01T00:00:00Z")
214
251
  export GIT_RANGE="${PREV_TAG}..HEAD"
215
252
  else
216
253
  export PREV_TAG_DATE="2020-01-01T00:00:00Z"
217
254
  export GIT_RANGE="HEAD"
218
255
  fi
219
256
 
220
- export COMMITS
221
- COMMITS=$(git log "${GIT_RANGE}" --no-merges --format="- %h %s" 2>/dev/null | head -25 || true)
222
- [ -z "$COMMITS" ] && COMMITS="_(no commits found)_"
257
+ export COMMITS_RAW
258
+ COMMITS_RAW=$(git log "${GIT_RANGE}" --no-merges --format="%h%x09%s" 2>/dev/null | head -25 || true)
223
259
 
224
260
  export GIT_AUTHORS
225
261
  GIT_AUTHORS=$(git log "${GIT_RANGE}" --format="%an" 2>/dev/null | sort -u | sed 's/^/- /' | head -30 || true)
226
262
 
263
+ # NOTE: deliberately NOT requesting --json commits here. With
264
+ # --limit 100 that expands into a GraphQL query requesting 100 PRs'
265
+ # worth of commit nodes, which reliably exceeds GitHub's 500,000
266
+ # node budget and makes `gh` print a plain-text GraphQL error
267
+ # instead of JSON — json.loads() below then fails, prs_data
268
+ # silently becomes [], and every release note says "no pull
269
+ # requests found" regardless of the date filter. Commits are
270
+ # fetched below, one lightweight call per PR, only for the small
271
+ # number of PRs that actually match this release.
227
272
  export PRS_JSON
228
273
  PRS_JSON=$(gh pr list --state merged --base "dev" --limit 100 \
229
274
  --json number,title,mergedAt,url,author,body \
230
275
  2>/dev/null || echo "[]")
231
276
 
232
277
  python3 << 'PYEOF'
233
- import os, json
278
+ import os, json, re, importlib.util, subprocess
234
279
  version = os.environ["VERSION"]
235
280
  pypi_url = os.environ["PYPI_URL"]
236
281
  prev_date = os.environ.get("PREV_TAG_DATE", "2020-01-01")
237
- commits = os.environ.get("COMMITS", "_(no commits found)_")
282
+ commits_raw = os.environ.get("COMMITS_RAW", "")
238
283
  git_authors = os.environ.get("GIT_AUTHORS", "")
239
284
  try:
240
285
  prs_data = json.loads(os.environ.get("PRS_JSON", "[]"))
241
286
  except Exception:
242
287
  prs_data = []
288
+
289
+ _README_RE = re.compile(r'^update\s+readme(\.md)?\s*$', re.IGNORECASE)
290
+
291
+ def _strip_pr_suffix(head):
292
+ return re.sub(r'\s*\(#\d+\)\s*$', '', head).strip()
293
+
294
+ def _collapse_readme(entries):
295
+ """entries: list of (oid_or_None, head) tuples, in original order.
296
+ Collapse every bare "Update README[.md]" commit into a single
297
+ "Update README (n)" line at the position of its first occurrence —
298
+ these are near-duplicate housekeeping commits with no release signal."""
299
+ out, count, placeholder_idx = [], 0, None
300
+ for oid, head in entries:
301
+ if _README_RE.match(_strip_pr_suffix(head)):
302
+ count += 1
303
+ if placeholder_idx is None:
304
+ placeholder_idx = len(out)
305
+ out.append(None)
306
+ continue
307
+ out.append((oid, head))
308
+ if placeholder_idx is not None:
309
+ label = "Update README" if count == 1 else f"Update README ({count})"
310
+ out[placeholder_idx] = (None, label)
311
+ return out
312
+
313
+ def _format_entries(entries, indent=""):
314
+ return [f"{indent}- `{oid}` {head}" if oid else f"{indent}- {head}" for oid, head in entries]
315
+
243
316
  def _pr_entry(pr):
244
- line = f"- [#{pr['number']}]({pr['url']}) {pr['title']} — {pr['mergedAt'][:10]}"
245
317
  desc = " ".join((pr.get("body") or "").split()) # collapse newlines/whitespace
246
- if desc:
247
- if len(desc) > 512:
248
- desc = desc[:509].rstrip() + "..."
249
- line += f"\n\n > {desc}"
250
- return line
318
+ if not desc:
319
+ desc = pr["title"]
320
+ if len(desc) > 512:
321
+ desc = desc[:509].rstrip() + "..."
322
+ return f"- [PR #{pr['number']}]({pr['url']}): {desc}"
323
+
324
+ def _fetch_pr_commits(number):
325
+ # One lightweight call per matched PR (see the note above the
326
+ # PRS_JSON fetch for why this isn't batched into the list query).
327
+ try:
328
+ out = subprocess.run(
329
+ ["gh", "pr", "view", str(number), "--json", "commits"],
330
+ capture_output=True, text=True, timeout=30,
331
+ )
332
+ if out.returncode != 0:
333
+ return []
334
+ return json.loads(out.stdout).get("commits") or []
335
+ except Exception:
336
+ return []
337
+
251
338
  filtered = [pr for pr in prs_data if pr.get("mergedAt", "") > prev_date]
339
+ # "What's Changed precisely": list each merged PR's own developer commits,
340
+ # NOT the squashed/merge/chore commits that land on the release branch
341
+ # (those are redundant with the PR list above and not useful).
342
+ _seen_commits = set()
343
+ _commit_blocks = []
344
+ for pr in filtered:
345
+ raw_entries = []
346
+ for c in _fetch_pr_commits(pr["number"]):
347
+ oid = (c.get("oid") or "")[:7]
348
+ head = (c.get("messageHeadline") or "").strip()
349
+ if not head or head.startswith("Merge "):
350
+ continue
351
+ key = (oid, head)
352
+ if key in _seen_commits:
353
+ continue
354
+ _seen_commits.add(key)
355
+ raw_entries.append((oid, head))
356
+ commit_lines = _format_entries(_collapse_readme(raw_entries), indent=" ")
357
+ if commit_lines:
358
+ _commit_blocks.append(
359
+ f"**[#{pr['number']}]({pr['url']}) {pr['title']}**\n" + "\n".join(commit_lines)
360
+ )
361
+ # Fall back to the raw git log only when no PR commits are available.
362
+ if _commit_blocks:
363
+ commits_section = "\n\n".join(_commit_blocks)
364
+ else:
365
+ fallback_entries = []
366
+ for line in commits_raw.splitlines():
367
+ if "\t" not in line:
368
+ continue
369
+ oid, head = line.split("\t", 1)
370
+ fallback_entries.append((oid, head))
371
+ fallback_entries = _collapse_readme(fallback_entries)
372
+ commits_section = "\n".join(_format_entries(fallback_entries)) if fallback_entries else "_(no commits found)_"
252
373
  if filtered:
253
374
  prs_lines = "\n".join(_pr_entry(pr) for pr in filtered)
254
375
  seen, clabels = set(), []
@@ -261,7 +382,23 @@ jobs:
261
382
  else:
262
383
  prs_lines = "_(no pull requests found since last release)_"
263
384
  contributors = git_authors or "_(no contributors found)_"
385
+
386
+ # CLI banner (weightslab/art.py), loaded as a standalone file so we
387
+ # don't pull in the full package (and its heavy optional deps) just
388
+ # to print a logo. ANSI color codes are stripped (Markdown can't render
389
+ # them); the box-drawing/ASCII art itself renders fine in a code fence.
390
+ banner_block = ""
391
+ try:
392
+ spec = importlib.util.spec_from_file_location("wl_art", "weightslab/art.py")
393
+ wl_art = importlib.util.module_from_spec(spec)
394
+ spec.loader.exec_module(wl_art)
395
+ plain_banner = re.sub(r'\x1b\[[0-9;]*m', '', wl_art._BANNER)
396
+ banner_block = f"```text\n{plain_banner.strip(chr(10))}\n```\n\n"
397
+ except Exception:
398
+ pass
399
+
264
400
  body = (
401
+ banner_block +
265
402
  "## **Weightslab**\n\n"
266
403
  "Follow us on [LinkedIn](https://www.linkedin.com/company/graybx-com/)"
267
404
  " | [Graybx](https://www.graybx.com/)\n\n"
@@ -276,12 +413,12 @@ jobs:
276
413
  "Happy Training!\n\n"
277
414
  "---\n\n"
278
415
  "### What's Changed precisely:\n\n"
279
- f"{commits}\n\n"
416
+ f"{commits_section}\n\n"
280
417
  "---\n\n"
281
418
  "### Thank you!\n\n"
282
419
  f"{contributors}\n"
283
420
  )
284
- with open("RELEASE_NOTES.md", "w") as f:
421
+ with open("RELEASE_NOTES.md", "w", encoding="utf-8") as f:
285
422
  f.write(body)
286
423
  PYEOF
287
424
 
@@ -303,7 +440,7 @@ jobs:
303
440
 
304
441
  build-and-publish-main:
305
442
  name: Build & Publish Main (PyPI)
306
- needs: [detect-target]
443
+ needs: [test-install-from-pip-dev-release, detect-target]
307
444
  runs-on: ubuntu-latest
308
445
  if: ${{ needs.detect-target.outputs.is_main == 'true' }}
309
446
  permissions:
@@ -315,7 +452,7 @@ jobs:
315
452
  with:
316
453
  fetch-depth: 0
317
454
 
318
- - uses: actions/setup-python@v5
455
+ - uses: actions/setup-python@v6
319
456
  with:
320
457
  python-version: '3.11'
321
458
 
@@ -349,7 +486,7 @@ jobs:
349
486
  python -m twine upload dist/* --non-interactive --verbose
350
487
 
351
488
  - name: Upload build artifacts for release
352
- uses: actions/upload-artifact@v4
489
+ uses: actions/upload-artifact@v6
353
490
  with:
354
491
  name: main-release-dist
355
492
  path: dist/
@@ -367,7 +504,7 @@ jobs:
367
504
  permissions:
368
505
  contents: read
369
506
  steps:
370
- - uses: actions/setup-python@v5
507
+ - uses: actions/setup-python@v6
371
508
  with:
372
509
  python-version: ${{ matrix.python-version }}
373
510
 
@@ -417,7 +554,7 @@ jobs:
417
554
 
418
555
  create-main-release:
419
556
  name: Create GitHub Release
420
- needs: [build-and-publish-main, test-install-from-pip-main]
557
+ needs: [test-install-from-pip-main]
421
558
  runs-on: ubuntu-latest
422
559
  permissions:
423
560
  contents: write
@@ -427,7 +564,7 @@ jobs:
427
564
  fetch-depth: 0
428
565
 
429
566
  - name: Download build artifacts
430
- uses: actions/download-artifact@v4
567
+ uses: actions/download-artifact@v7
431
568
  with:
432
569
  name: main-release-dist
433
570
  path: dist/
@@ -440,47 +577,142 @@ jobs:
440
577
  export VERSION_NO_V="${VERSION#v}"
441
578
  export PYPI_URL="https://pypi.org/project/weightslab/${VERSION_NO_V}/"
442
579
 
443
- PREV_TAG=$(git tag --sort=-version:refname | grep -v "^${VERSION}$" | head -n 1 || true)
580
+ # Walk the actual commit graph for the nearest ancestor tag, rather
581
+ # than a lexicographic/semver sort across ALL tags. The old sort
582
+ # could pick an unrelated tag from a diverging branch — e.g. a
583
+ # "v1.3.0-dev0" tag cut on dev after "v1.3.0" was tagged on main
584
+ # sorted ABOVE it, making every real PR since v1.3.0 look "older"
585
+ # than that bogus previous-tag date and get silently filtered out
586
+ # (this is why past releases showed "no pull requests found").
587
+ PREV_TAG=$(git describe --tags --abbrev=0 "${VERSION}^" 2>/dev/null || true)
444
588
  if [ -n "$PREV_TAG" ]; then
445
- export PREV_TAG_DATE=$(git log -1 --format="%aI" "${PREV_TAG}" 2>/dev/null || echo "2020-01-01T00:00:00Z")
589
+ RAW_DATE=$(git log -1 --format="%aI" "${PREV_TAG}" 2>/dev/null || true)
590
+ # Normalize to UTC "Z" form so string comparison against GitHub's
591
+ # mergedAt (also UTC "Z") is valid regardless of the committer's
592
+ # local timezone offset.
593
+ export PREV_TAG_DATE=$(date -u -d "${RAW_DATE:-2020-01-01}" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || echo "2020-01-01T00:00:00Z")
446
594
  export GIT_RANGE="${PREV_TAG}..HEAD"
447
595
  else
448
596
  export PREV_TAG_DATE="2020-01-01T00:00:00Z"
449
597
  export GIT_RANGE="HEAD"
450
598
  fi
451
599
 
452
- export COMMITS
453
- COMMITS=$(git log "${GIT_RANGE}" --no-merges --format="- %h %s" 2>/dev/null | head -25 || true)
454
- [ -z "$COMMITS" ] && COMMITS="_(no commits found)_"
600
+ export COMMITS_RAW
601
+ COMMITS_RAW=$(git log "${GIT_RANGE}" --no-merges --format="%h%x09%s" 2>/dev/null | head -25 || true)
455
602
 
456
603
  export GIT_AUTHORS
457
604
  GIT_AUTHORS=$(git log "${GIT_RANGE}" --format="%an" 2>/dev/null | sort -u | sed 's/^/- /' | head -30 || true)
458
605
 
606
+ # NOTE: deliberately NOT requesting --json commits here. With
607
+ # --limit 100 that expands into a GraphQL query requesting 100 PRs'
608
+ # worth of commit nodes, which reliably exceeds GitHub's 500,000
609
+ # node budget and makes `gh` print a plain-text GraphQL error
610
+ # instead of JSON — json.loads() below then fails, prs_data
611
+ # silently becomes [], and every release note says "no pull
612
+ # requests found" regardless of the date filter. Commits are
613
+ # fetched below, one lightweight call per PR, only for the small
614
+ # number of PRs that actually match this release.
459
615
  export PRS_JSON
460
616
  PRS_JSON=$(gh pr list --state merged --base "main" --limit 100 \
461
617
  --json number,title,mergedAt,url,author,body \
462
618
  2>/dev/null || echo "[]")
463
619
 
464
620
  python3 << 'PYEOF'
465
- import os, json
621
+ import os, json, re, importlib.util, subprocess
466
622
  version = os.environ["VERSION"]
467
623
  pypi_url = os.environ["PYPI_URL"]
468
624
  prev_date = os.environ.get("PREV_TAG_DATE", "2020-01-01")
469
- commits = os.environ.get("COMMITS", "_(no commits found)_")
625
+ commits_raw = os.environ.get("COMMITS_RAW", "")
470
626
  git_authors = os.environ.get("GIT_AUTHORS", "")
471
627
  try:
472
628
  prs_data = json.loads(os.environ.get("PRS_JSON", "[]"))
473
629
  except Exception:
474
630
  prs_data = []
631
+
632
+ _README_RE = re.compile(r'^update\s+readme(\.md)?\s*$', re.IGNORECASE)
633
+
634
+ def _strip_pr_suffix(head):
635
+ return re.sub(r'\s*\(#\d+\)\s*$', '', head).strip()
636
+
637
+ def _collapse_readme(entries):
638
+ """entries: list of (oid_or_None, head) tuples, in original order.
639
+ Collapse every bare "Update README[.md]" commit into a single
640
+ "Update README (n)" line at the position of its first occurrence —
641
+ these are near-duplicate housekeeping commits with no release signal."""
642
+ out, count, placeholder_idx = [], 0, None
643
+ for oid, head in entries:
644
+ if _README_RE.match(_strip_pr_suffix(head)):
645
+ count += 1
646
+ if placeholder_idx is None:
647
+ placeholder_idx = len(out)
648
+ out.append(None)
649
+ continue
650
+ out.append((oid, head))
651
+ if placeholder_idx is not None:
652
+ label = "Update README" if count == 1 else f"Update README ({count})"
653
+ out[placeholder_idx] = (None, label)
654
+ return out
655
+
656
+ def _format_entries(entries, indent=""):
657
+ return [f"{indent}- `{oid}` {head}" if oid else f"{indent}- {head}" for oid, head in entries]
658
+
475
659
  def _pr_entry(pr):
476
- line = f"- [#{pr['number']}]({pr['url']}) {pr['title']} — {pr['mergedAt'][:10]}"
477
660
  desc = " ".join((pr.get("body") or "").split()) # collapse newlines/whitespace
478
- if desc:
479
- if len(desc) > 512:
480
- desc = desc[:509].rstrip() + "..."
481
- line += f"\n\n > {desc}"
482
- return line
661
+ if not desc:
662
+ desc = pr["title"]
663
+ if len(desc) > 512:
664
+ desc = desc[:509].rstrip() + "..."
665
+ return f"- [PR #{pr['number']}]({pr['url']}): {desc}"
666
+
667
+ def _fetch_pr_commits(number):
668
+ # One lightweight call per matched PR (see the note above the
669
+ # PRS_JSON fetch for why this isn't batched into the list query).
670
+ try:
671
+ out = subprocess.run(
672
+ ["gh", "pr", "view", str(number), "--json", "commits"],
673
+ capture_output=True, text=True, timeout=30,
674
+ )
675
+ if out.returncode != 0:
676
+ return []
677
+ return json.loads(out.stdout).get("commits") or []
678
+ except Exception:
679
+ return []
680
+
483
681
  filtered = [pr for pr in prs_data if pr.get("mergedAt", "") > prev_date]
682
+ # "What's Changed precisely": list each merged PR's own developer commits,
683
+ # NOT the squashed/merge/chore commits that land on the release branch
684
+ # (those are redundant with the PR list above and not useful).
685
+ _seen_commits = set()
686
+ _commit_blocks = []
687
+ for pr in filtered:
688
+ raw_entries = []
689
+ for c in _fetch_pr_commits(pr["number"]):
690
+ oid = (c.get("oid") or "")[:7]
691
+ head = (c.get("messageHeadline") or "").strip()
692
+ if not head or head.startswith("Merge "):
693
+ continue
694
+ key = (oid, head)
695
+ if key in _seen_commits:
696
+ continue
697
+ _seen_commits.add(key)
698
+ raw_entries.append((oid, head))
699
+ commit_lines = _format_entries(_collapse_readme(raw_entries), indent=" ")
700
+ if commit_lines:
701
+ _commit_blocks.append(
702
+ f"**[#{pr['number']}]({pr['url']}) {pr['title']}**\n" + "\n".join(commit_lines)
703
+ )
704
+ # Fall back to the raw git log only when no PR commits are available.
705
+ if _commit_blocks:
706
+ commits_section = "\n\n".join(_commit_blocks)
707
+ else:
708
+ fallback_entries = []
709
+ for line in commits_raw.splitlines():
710
+ if "\t" not in line:
711
+ continue
712
+ oid, head = line.split("\t", 1)
713
+ fallback_entries.append((oid, head))
714
+ fallback_entries = _collapse_readme(fallback_entries)
715
+ commits_section = "\n".join(_format_entries(fallback_entries)) if fallback_entries else "_(no commits found)_"
484
716
  if filtered:
485
717
  prs_lines = "\n".join(_pr_entry(pr) for pr in filtered)
486
718
  seen, clabels = set(), []
@@ -493,7 +725,23 @@ jobs:
493
725
  else:
494
726
  prs_lines = "_(no pull requests found since last release)_"
495
727
  contributors = git_authors or "_(no contributors found)_"
728
+
729
+ # CLI banner (weightslab/art.py), loaded as a standalone file so we
730
+ # don't pull in the full package (and its heavy optional deps) just
731
+ # to print a logo. ANSI color codes are stripped (Markdown can't render
732
+ # them); the box-drawing/ASCII art itself renders fine in a code fence.
733
+ banner_block = ""
734
+ try:
735
+ spec = importlib.util.spec_from_file_location("wl_art", "weightslab/art.py")
736
+ wl_art = importlib.util.module_from_spec(spec)
737
+ spec.loader.exec_module(wl_art)
738
+ plain_banner = re.sub(r'\x1b\[[0-9;]*m', '', wl_art._BANNER)
739
+ banner_block = f"```text\n{plain_banner.strip(chr(10))}\n```\n\n"
740
+ except Exception:
741
+ pass
742
+
496
743
  body = (
744
+ banner_block +
497
745
  "## **Weightslab**\n\n"
498
746
  "Follow us on [LinkedIn](https://www.linkedin.com/company/graybx-com/)"
499
747
  " | [Graybx](https://www.graybx.com/)\n\n"
@@ -508,12 +756,12 @@ jobs:
508
756
  "Happy Training!\n\n"
509
757
  "---\n\n"
510
758
  "### What's Changed precisely:\n\n"
511
- f"{commits}\n\n"
759
+ f"{commits_section}\n\n"
512
760
  "---\n\n"
513
761
  "### Thank you!\n\n"
514
762
  f"{contributors}\n"
515
763
  )
516
- with open("RELEASE_NOTES.md", "w") as f:
764
+ with open("RELEASE_NOTES.md", "w", encoding="utf-8") as f:
517
765
  f.write(body)
518
766
  PYEOF
519
767