hyperstudy-perception 0.1.3__tar.gz → 0.1.5__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 (290) hide show
  1. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/PKG-INFO +1 -1
  2. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/pyproject.toml +1 -1
  3. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/RELEASE.json +8 -1
  4. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/cloud/runpod/image/Dockerfile +4 -3
  5. hyperstudy_perception-0.1.3/src/perception/_pipeline/graphs/full.yaml → hyperstudy_perception-0.1.5/src/perception/_pipeline/graphs/full-objects-names.yaml +8 -16
  6. hyperstudy_perception-0.1.5/src/perception/_pipeline/graphs/full.yaml +101 -0
  7. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody-captions/node.py +42 -24
  8. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody-captions/style_captions.py +42 -0
  9. hyperstudy_perception-0.1.5/src/perception/_pipeline/nodes/audio/prosody-captions/test_prosody_captions_node.py +107 -0
  10. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody-captions/test_style_captions.py +46 -0
  11. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/manifest.yaml +5 -3
  12. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/node.py +7 -1
  13. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/haiku.py +10 -8
  14. hyperstudy_perception-0.1.3/src/perception/_pipeline/nodes/vlm/shot-captions/node.py → hyperstudy_perception-0.1.5/src/perception/_pipeline/nodes/vlm/shot-captions/captioning.py +27 -14
  15. hyperstudy_perception-0.1.5/src/perception/_pipeline/nodes/vlm/shot-captions/node.py +15 -0
  16. hyperstudy_perception-0.1.5/src/perception/_pipeline/nodes/vlm/shot-captions/test_captioning.py +38 -0
  17. hyperstudy_perception-0.1.5/src/perception/_pipeline/nodes/vlm/shot-captions-objects/manifest.yaml +35 -0
  18. hyperstudy_perception-0.1.5/src/perception/_pipeline/nodes/vlm/shot-captions-objects/node.py +57 -0
  19. hyperstudy_perception-0.1.5/src/perception/_pipeline/nodes/vlm/shot-captions-objects/test_shot_captions_objects.py +154 -0
  20. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/backends/base.py +3 -3
  21. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/backends/cloud.py +22 -2
  22. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/backends/native.py +31 -10
  23. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/backends/remote.py +9 -4
  24. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/cli.py +350 -19
  25. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/hosts.py +11 -0
  26. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/manifests.py +13 -1
  27. hyperstudy_perception-0.1.5/src/perception/pod_run.py +238 -0
  28. hyperstudy_perception-0.1.5/src/perception/pod_session.py +272 -0
  29. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/remote_coordinator.py +45 -7
  30. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/remote_driver.py +20 -195
  31. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/remote_run.py +61 -32
  32. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/LICENSE +0 -0
  33. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/README.md +0 -0
  34. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/__init__.py +0 -0
  35. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/cloud/runpod/guard.sh +0 -0
  36. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/cloud/runpod/image/start.sh +0 -0
  37. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/cloud/runpod/image/tag.py +0 -0
  38. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/cloud/skypilot/gpu-faces.yaml +0 -0
  39. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/cloud/skypilot/gpu-tune.yaml +0 -0
  40. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/cloud/skypilot/guard.sh +0 -0
  41. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/cloud/skypilot/iam-perception-node.json +0 -0
  42. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/cloud/skypilot/launch.sh +0 -0
  43. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/cloud/skypilot/node-gpu.yaml +0 -0
  44. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/cloud/skypilot/node-setup.sh +0 -0
  45. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/cloud/skypilot/sky.yaml +0 -0
  46. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/cloud/skypilot/smoke.yaml +0 -0
  47. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/envs/api/pyproject.toml +0 -0
  48. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/envs/api/uv.lock +0 -0
  49. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/envs/asr-cuda/pyproject.toml +0 -0
  50. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/envs/asr-cuda/uv.lock +0 -0
  51. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/envs/audio/pyproject.toml +0 -0
  52. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/envs/audio/uv.lock +0 -0
  53. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/envs/audio-events/pyproject.toml +0 -0
  54. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/envs/audio-events/uv.lock +0 -0
  55. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/envs/core/pyproject.toml +0 -0
  56. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/envs/core/uv.lock +0 -0
  57. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/envs/faces/pyproject.toml +0 -0
  58. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/envs/faces/uv.lock +0 -0
  59. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/envs/gaze/pyproject.toml +0 -0
  60. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/envs/gaze/uv.lock +0 -0
  61. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/envs/prosody/pyproject.toml +0 -0
  62. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/envs/prosody/uv.lock +0 -0
  63. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/envs/vision/pyproject.toml +0 -0
  64. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/envs/vision/uv.lock +0 -0
  65. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/graphs/decode.yaml +0 -0
  66. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/graphs/faces.yaml +0 -0
  67. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/graphs/mvp-captions.yaml +0 -0
  68. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/graphs/mvp.yaml +0 -0
  69. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/asr/manifest.yaml +0 -0
  70. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/asr/nemo_model.py +0 -0
  71. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/asr/nemo_tokens.py +0 -0
  72. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/asr/node.py +0 -0
  73. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/asr/node_nemo.py +0 -0
  74. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/asr/prepare.py +0 -0
  75. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/asr/raw_tokens.py +0 -0
  76. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/asr/test_nemo_model.py +0 -0
  77. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/asr/test_nemo_tokens.py +0 -0
  78. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/asr/test_raw_tokens.py +0 -0
  79. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/asr/test_wav.py +0 -0
  80. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/asr/utterances.py +0 -0
  81. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/asr/wav.py +0 -0
  82. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/diarize/diarization_raw.py +0 -0
  83. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/diarize/diarizer.py +0 -0
  84. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/diarize/manifest.yaml +0 -0
  85. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/diarize/node.py +0 -0
  86. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/diarize/pcm16.py +0 -0
  87. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/diarize/prepare.py +0 -0
  88. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/diarize/test_diarization_raw.py +0 -0
  89. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/diarize/test_diarizer.py +0 -0
  90. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/diarize/test_pcm16.py +0 -0
  91. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/diarize/test_voices.py +0 -0
  92. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/diarize/voices.py +0 -0
  93. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody/manifest.yaml +0 -0
  94. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody/node.py +0 -0
  95. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody/odyssey.py +0 -0
  96. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody/praat.py +0 -0
  97. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody/prepare.py +0 -0
  98. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody/prosody_layer.py +0 -0
  99. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody/prosody_measure.py +0 -0
  100. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody/prosody_tracks.py +0 -0
  101. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody/prosody_units.py +0 -0
  102. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody/test_prosody_layer.py +0 -0
  103. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody/test_prosody_measure.py +0 -0
  104. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody/test_prosody_tracks.py +0 -0
  105. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody/test_prosody_units.py +0 -0
  106. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody/third_party/README.md +0 -0
  107. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody/third_party/odyssey/LICENSE +0 -0
  108. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody/third_party/odyssey/ser_model.py +0 -0
  109. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody-captions/captions_raw.py +0 -0
  110. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/prosody-captions/manifest.yaml +0 -0
  111. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/audioset_labels.csv +0 -0
  112. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/efficientat/LICENSE +0 -0
  113. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/efficientat/UPSTREAM.md +0 -0
  114. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/efficientat/__init__.py +0 -0
  115. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/efficientat/mn/__init__.py +0 -0
  116. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/efficientat/mn/attention_pooling.py +0 -0
  117. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/efficientat/mn/block_types.py +0 -0
  118. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/efficientat/mn/model.py +0 -0
  119. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/efficientat/mn/utils.py +0 -0
  120. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/efficientat/preprocess.py +0 -0
  121. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/models.py +0 -0
  122. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/panns/LICENSE.MIT +0 -0
  123. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/panns/UPSTREAM.md +0 -0
  124. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/panns/__init__.py +0 -0
  125. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/panns/cnn14.py +0 -0
  126. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/panns/pytorch_utils.py +0 -0
  127. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/prepare.py +0 -0
  128. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/sound_events.py +0 -0
  129. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/sound_scores.py +0 -0
  130. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/soundtrack.py +0 -0
  131. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/test_sound_events.py +0 -0
  132. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/test_sound_scores.py +0 -0
  133. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/sounds/test_soundtrack.py +0 -0
  134. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/waveform/manifest.yaml +0 -0
  135. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/waveform/node.py +0 -0
  136. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/waveform/test_waveform_peaks.py +0 -0
  137. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/audio/waveform/waveform_peaks.py +0 -0
  138. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/conftest.py +0 -0
  139. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/core/decode/manifest.yaml +0 -0
  140. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/core/decode/media.py +0 -0
  141. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/core/decode/node.py +0 -0
  142. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/core/decode/shot_scores.py +0 -0
  143. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/core/decode/test_media.py +0 -0
  144. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/core/decode/test_shot_scores.py +0 -0
  145. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/testdata/README.md +0 -0
  146. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/testdata/rotated-90.mp4 +0 -0
  147. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/active-speaker/asd_crops.py +0 -0
  148. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/active-speaker/asd_raw.py +0 -0
  149. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/active-speaker/manifest.yaml +0 -0
  150. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/active-speaker/models.py +0 -0
  151. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/active-speaker/node.py +0 -0
  152. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/active-speaker/prepare.py +0 -0
  153. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/active-speaker/speaker_links.py +0 -0
  154. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/active-speaker/test_asd_crops.py +0 -0
  155. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/active-speaker/test_asd_raw.py +0 -0
  156. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/active-speaker/test_speaker_links.py +0 -0
  157. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/active-speaker/third_party/README.md +0 -0
  158. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/active-speaker/third_party/lr_asd/LICENSE +0 -0
  159. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/active-speaker/third_party/lr_asd/loss.py +0 -0
  160. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/active-speaker/third_party/lr_asd/model/Classifier.py +0 -0
  161. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/active-speaker/third_party/lr_asd/model/Encoder.py +0 -0
  162. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/active-speaker/third_party/lr_asd/model/Model.py +0 -0
  163. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/active-speaker/video25.py +0 -0
  164. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/body-pose/bodies.py +0 -0
  165. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/body-pose/detections.py +0 -0
  166. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/body-pose/keypoints.json +0 -0
  167. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/body-pose/manifest.yaml +0 -0
  168. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/body-pose/models.py +0 -0
  169. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/body-pose/node.py +0 -0
  170. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/body-pose/prepare.py +0 -0
  171. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/body-pose/test_bodies.py +0 -0
  172. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/body-pose/test_detections.py +0 -0
  173. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/face-track/face_detail.py +0 -0
  174. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/face-track/frames.py +0 -0
  175. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/face-track/loader.py +0 -0
  176. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/face-track/manifest.yaml +0 -0
  177. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/face-track/node.py +0 -0
  178. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/face-track/prepare.py +0 -0
  179. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/face-track/pyfeat_raw.py +0 -0
  180. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/face-track/test_face_detail.py +0 -0
  181. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/face-track/test_frames.py +0 -0
  182. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/face-track/test_loader.py +0 -0
  183. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/face-track/test_pyfeat_raw.py +0 -0
  184. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/face-track/test_thumbnails.py +0 -0
  185. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/face-track/thumbnails.py +0 -0
  186. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/face-track/tracking.py +0 -0
  187. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/facing/facing.py +0 -0
  188. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/facing/facing_raw.py +0 -0
  189. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/facing/manifest.yaml +0 -0
  190. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/facing/models.py +0 -0
  191. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/facing/node.py +0 -0
  192. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/facing/prepare.py +0 -0
  193. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/facing/test_facing.py +0 -0
  194. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/facing/test_facing_raw.py +0 -0
  195. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/gaze_polygons.py +0 -0
  196. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/gaze_targets.py +0 -0
  197. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/heatmaps.py +0 -0
  198. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/manifest.yaml +0 -0
  199. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/models.py +0 -0
  200. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/node.py +0 -0
  201. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/prepare.py +0 -0
  202. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/test_gaze_polygons.py +0 -0
  203. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/test_gaze_targets.py +0 -0
  204. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/test_heatmaps.py +0 -0
  205. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/third_party/README.md +0 -0
  206. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/third_party/dinov2/LICENSE +0 -0
  207. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/third_party/dinov2/dinov2/__init__.py +0 -0
  208. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/third_party/dinov2/dinov2/layers/__init__.py +0 -0
  209. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/third_party/dinov2/dinov2/layers/attention.py +0 -0
  210. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/third_party/dinov2/dinov2/layers/block.py +0 -0
  211. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/third_party/dinov2/dinov2/layers/dino_head.py +0 -0
  212. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/third_party/dinov2/dinov2/layers/drop_path.py +0 -0
  213. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/third_party/dinov2/dinov2/layers/layer_scale.py +0 -0
  214. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/third_party/dinov2/dinov2/layers/mlp.py +0 -0
  215. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/third_party/dinov2/dinov2/layers/patch_embed.py +0 -0
  216. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/third_party/dinov2/dinov2/layers/swiglu_ffn.py +0 -0
  217. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/third_party/dinov2/dinov2/models/__init__.py +0 -0
  218. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/third_party/dinov2/dinov2/models/vision_transformer.py +0 -0
  219. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/third_party/gazelle/LICENSE +0 -0
  220. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/third_party/gazelle/gazelle/backbone.py +0 -0
  221. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/third_party/gazelle/gazelle/model.py +0 -0
  222. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gaze/third_party/gazelle/gazelle/utils.py +0 -0
  223. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gestures/gesture_evidence.py +0 -0
  224. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gestures/gesture_inputs.py +0 -0
  225. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gestures/gesture_rules.py +0 -0
  226. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gestures/manifest.yaml +0 -0
  227. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gestures/node.py +0 -0
  228. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gestures/test_gesture_evidence.py +0 -0
  229. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/gestures/test_gesture_rules.py +0 -0
  230. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/hands/hand_detections.py +0 -0
  231. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/hands/hand_tracks.py +0 -0
  232. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/hands/manifest.yaml +0 -0
  233. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/hands/models.py +0 -0
  234. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/hands/node.py +0 -0
  235. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/hands/prepare.py +0 -0
  236. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/hands/scipy_lsap/LICENSE +0 -0
  237. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/hands/scipy_lsap/UPSTREAM.md +0 -0
  238. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/hands/scipy_lsap/__init__.py +0 -0
  239. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/hands/scipy_lsap/rectangular_lsap.py +0 -0
  240. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/hands/test_hand_detections.py +0 -0
  241. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/hands/test_hand_tracks.py +0 -0
  242. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/hands/test_node.py +0 -0
  243. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/hands/test_rectangular_lsap.py +0 -0
  244. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/budget.py +0 -0
  245. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/garments.py +0 -0
  246. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/geometry.py +0 -0
  247. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/link.py +0 -0
  248. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/linking.py +0 -0
  249. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/models.py +0 -0
  250. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/names.py +0 -0
  251. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/object_raw.py +0 -0
  252. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/object_tracks.py +0 -0
  253. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/person_masks.py +0 -0
  254. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/prepare.py +0 -0
  255. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/shot_frames.py +0 -0
  256. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/test_garments.py +0 -0
  257. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/test_models_hub.py +0 -0
  258. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/test_object_budget.py +0 -0
  259. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/test_object_links.py +0 -0
  260. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/test_object_raw.py +0 -0
  261. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/test_person_masks.py +0 -0
  262. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/test_shot_frames.py +0 -0
  263. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/test_split.py +0 -0
  264. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/testdata/node_1_1_0.py +0 -0
  265. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/track.py +0 -0
  266. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects/vocabulary.py +0 -0
  267. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects-link/manifest.yaml +0 -0
  268. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects-names/manifest.yaml +0 -0
  269. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vision/objects-track/manifest.yaml +0 -0
  270. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/nodes/vlm/shot-captions/manifest.yaml +0 -0
  271. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/schema/README.md +0 -0
  272. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/schema/bundle-0.1.schema.json +0 -0
  273. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/sdk/pyproject.toml +0 -0
  274. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/sdk/src/perception_node/__init__.py +0 -0
  275. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/_pipeline/sdk/src/perception_node/weights.py +0 -0
  276. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/artifacts.py +0 -0
  277. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/backends/__init__.py +0 -0
  278. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/backends/probe.py +0 -0
  279. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/estimates.py +0 -0
  280. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/export.py +0 -0
  281. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/ir.py +0 -0
  282. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/keycheck.py +0 -0
  283. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/keys.py +0 -0
  284. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/plan.py +0 -0
  285. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/prepare_first.py +0 -0
  286. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/release.py +0 -0
  287. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/runpod.py +0 -0
  288. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/scheduler.py +0 -0
  289. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/setup.py +0 -0
  290. {hyperstudy_perception-0.1.3 → hyperstudy_perception-0.1.5}/src/perception/store.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: hyperstudy-perception
3
- Version: 0.1.3
3
+ Version: 0.1.5
4
4
  Summary: Video perception pipeline: shots, speech, faces and captions from a video, as a versioned track bundle, run locally, on cloud GPUs or lab servers
5
5
  Project-URL: Homepage, https://github.com/hyperstudyio/hyperstudy-perception
6
6
  Project-URL: Changelog, https://github.com/hyperstudyio/hyperstudy-perception/releases
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "hyperstudy-perception"
3
- version = "0.1.3"
3
+ version = "0.1.5"
4
4
  description = "Video perception pipeline: shots, speech, faces and captions from a video, as a versioned track bundle, run locally, on cloud GPUs or lab servers"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -1,5 +1,5 @@
1
1
  {
2
- "commit": "ba5bb86926a9a7218d908ad5f7dc0371cf87a0c3",
2
+ "commit": "b174b9b1d8d56b59dd69d36db67f93bcf0537899",
3
3
  "files": [
4
4
  "cloud/runpod/guard.sh",
5
5
  "cloud/runpod/image/Dockerfile",
@@ -34,6 +34,7 @@
34
34
  "envs/vision/uv.lock",
35
35
  "graphs/decode.yaml",
36
36
  "graphs/faces.yaml",
37
+ "graphs/full-objects-names.yaml",
37
38
  "graphs/full.yaml",
38
39
  "graphs/mvp-captions.yaml",
39
40
  "graphs/mvp.yaml",
@@ -65,6 +66,7 @@
65
66
  "nodes/audio/prosody-captions/manifest.yaml",
66
67
  "nodes/audio/prosody-captions/node.py",
67
68
  "nodes/audio/prosody-captions/style_captions.py",
69
+ "nodes/audio/prosody-captions/test_prosody_captions_node.py",
68
70
  "nodes/audio/prosody-captions/test_style_captions.py",
69
71
  "nodes/audio/prosody/manifest.yaml",
70
72
  "nodes/audio/prosody/node.py",
@@ -244,8 +246,13 @@
244
246
  "nodes/vision/objects/testdata/node_1_1_0.py",
245
247
  "nodes/vision/objects/track.py",
246
248
  "nodes/vision/objects/vocabulary.py",
249
+ "nodes/vlm/shot-captions-objects/manifest.yaml",
250
+ "nodes/vlm/shot-captions-objects/node.py",
251
+ "nodes/vlm/shot-captions-objects/test_shot_captions_objects.py",
252
+ "nodes/vlm/shot-captions/captioning.py",
247
253
  "nodes/vlm/shot-captions/manifest.yaml",
248
254
  "nodes/vlm/shot-captions/node.py",
255
+ "nodes/vlm/shot-captions/test_captioning.py",
249
256
  "schema/README.md",
250
257
  "schema/bundle-0.1.schema.json",
251
258
  "sdk/pyproject.toml",
@@ -8,10 +8,11 @@
8
8
  FROM runpod/pytorch:2.1.0-py3.10-cuda11.8.0-devel-ubuntu22.04@sha256:2594f25beeef751aeebcc207ba4c45a382ba174655f01e15cbbfad0e2bdb5ef8
9
9
  ENV DEBIAN_FRONTEND=noninteractive
10
10
 
11
- # what nodes and the driver use (setup then installs nothing); sshd takes keys only. Each pod makes its own host keys
12
- # at start (start.sh): the ones in the base image are public, and are never used
11
+ # what nodes and the driver use (setup then installs nothing): ffmpeg, rsync, and the EGL and GLES libraries MediaPipe
12
+ # loads even on the CPU (vision.facing failed for want of libEGL.so.1 on a pod, 2026-10-31). sshd takes keys only.
13
+ # Each pod makes its own host keys at start (start.sh): the ones in the base image are public, and are never used
13
14
  RUN apt-get update \
14
- && apt-get install -y --no-install-recommends ffmpeg rsync \
15
+ && apt-get install -y --no-install-recommends ffmpeg rsync libegl1 libgles2 \
15
16
  && rm -rf /var/lib/apt/lists/* /etc/ssh/ssh_host_* \
16
17
  && mkdir -p /run/sshd /etc/ssh/sshd_config.d \
17
18
  && printf 'PermitRootLogin prohibit-password\nPasswordAuthentication no\nKbdInteractiveAuthentication no\n' \
@@ -1,18 +1,11 @@
1
- # Everything the pipeline can do: the graph the annotator app runs. The app's feature list picks what runs (--only, with
2
- # what it needs), settings (--set) and places (--place). Nothing leaves the machine without --allow-uploads, and then
3
- # only the kinds allow_upload names: frames, faces and transcript for the captions API (frames and faces also for the
4
- # objects steps' naming and crop check), audio and transcript for the delivery captions (per-utterance snippets to
5
- # OpenAI), and video and audio for a step placed on another machine (a lab server, a cloud GPU), which receives its
6
- # inputs. Embeddings too: the faces layer holds ArcFace identity distances (person_pairs), and
7
- # every step that reads it (bodies, gaze, hands...) receives it when placed elsewhere; the run's consent still names it
8
- # per step. max_cost_usd caps the spend.
9
- # perception plan graphs/full.yaml --input video=PATH/TO/VIDEO.mp4
10
- # perception run graphs/full.yaml --input video=PATH --only asr,faces # the local, free features
11
- # perception run graphs/full.yaml --input video=PATH --only gestures # gestures without objects (its optional
12
- # input): in a run of everything, a refused or failed objects cancels it
13
- # perception run graphs/full.yaml --input video=PATH --allow-uploads # everything, captions too
1
+ # The full graph (full.yaml) with the objects named in a step of their own (run-anywhere 1b, A): objects_names names
2
+ # them with Claude Haiku 5.5 from decode's keyframes, a second pass over the keyframes the captions step reads.
3
+ # full.yaml has the captions call name them instead (B), chosen 2026-11-01: on held-out OOO clips 3 and 4 the
4
+ # captions read the same blind, clothing was the same, and A's extra objects were mostly stage lights, which the
5
+ # naming rules exclude (RUN_ANYWHERE_PLAN.md 1b). Kept to compare the two on more clips.
6
+ # perception remote run HOST graphs/full-objects-names.yaml --input video=PATH --allow-uploads
14
7
  schema_version: 1
15
- pipeline: perception-full
8
+ pipeline: perception-full-objects-names
16
9
  inputs:
17
10
  video: {type: video.v1, uri: '../data/video.mp4'}
18
11
  policy:
@@ -63,8 +56,7 @@ nodes:
63
56
  # to the API: ANTHROPIC_API_KEY), as soon as decode is done; objects_track outlines and tracks them with SAM 3 and
64
57
  # embeds them with DINOv3 (both gated: HF_TOKEN), on the GPU with no API; objects checks each track's crops with Haiku
65
58
  # (frames and faces again), finds who wears what (faces, bodies) and links them across shots: the objects layer. The
66
- # naming duplicates the captions' pass over the same keyframes; whether the captions call should name them instead
67
- # is measured (RUN_ANYWHERE_PLAN.md 1b)
59
+ # naming duplicates the captions' pass over the same keyframes; full.yaml has the captions call name them
68
60
  - id: objects_names
69
61
  uses: vision.objects-names@1.0.0
70
62
  inputs: {shots: $nodes.decode.shots, keyframes: $nodes.decode.keyframes, media: $nodes.decode.media}
@@ -0,0 +1,101 @@
1
+ # Everything the pipeline can do: the graph the annotator app runs. The app's feature list picks what runs (--only, with
2
+ # what it needs), settings (--set) and places (--place). Nothing leaves the machine without --allow-uploads, and then
3
+ # only the kinds allow_upload names: frames, faces and transcript for the captions API (whose call also names the
4
+ # objects; frames and faces also for the objects' crop check), audio and transcript for the delivery captions
5
+ # (per-utterance snippets to OpenAI), and video and audio for a step placed on another machine (a lab server, a cloud
6
+ # GPU), which receives its inputs. Embeddings too: the faces layer holds ArcFace identity distances (person_pairs), and
7
+ # every step that reads it (bodies, gaze, hands...) receives it when placed elsewhere; the run's consent still names it
8
+ # per step. max_cost_usd caps the spend.
9
+ # perception plan graphs/full.yaml --input video=PATH/TO/VIDEO.mp4
10
+ # perception run graphs/full.yaml --input video=PATH --only asr,faces # the local, free features
11
+ # perception run graphs/full.yaml --input video=PATH --only gestures # gestures without objects (its optional
12
+ # input): in a run of everything, a refused or failed objects cancels it
13
+ # perception run graphs/full.yaml --input video=PATH --allow-uploads # everything, captions too
14
+ schema_version: 1
15
+ pipeline: perception-full
16
+ inputs:
17
+ video: {type: video.v1, uri: '../data/video.mp4'}
18
+ policy:
19
+ placement: hybrid
20
+ privacy: local-preferred
21
+ allow_upload: [video, audio, frames, faces, transcript, embeddings]
22
+ # Measured on held-out OOO clips 3 and 4 (28 and 39 shots; liquidswords2, 2026-10-30): the captions with the names
23
+ # $0.029 and $0.037 (about $0.001 a shot, the class call included; A's captions alone $0.023 and $0.033), the crop
24
+ # check $0.022 and $0.021, whole runs $0.32 and $0.37 (A's $0.31 and $0.38)
25
+ max_cost_usd: 1.50
26
+ retries: 1
27
+ nodes:
28
+ - id: decode
29
+ uses: core.decode@1.1.0
30
+ inputs: {video: $inputs.video}
31
+ - id: asr
32
+ uses: audio.asr@1.1.0
33
+ inputs: {audio: $nodes.decode.audio, shots: $nodes.decode.shots}
34
+ # refines asr's transcript: the same words and utterances, with their voices (pyannote; gated, HF_TOKEN)
35
+ - id: diarize
36
+ uses: audio.diarize@1.1.0
37
+ inputs: {audio: $nodes.decode.audio, transcript: $nodes.asr.transcript}
38
+ - id: sounds
39
+ uses: audio.sounds@1.1.0
40
+ inputs: {video: $inputs.video}
41
+ - id: waveform
42
+ uses: audio.waveform@1.0.0
43
+ inputs: {audio: $nodes.decode.audio}
44
+ - id: faces
45
+ uses: vision.face-track@1.4.0
46
+ inputs: {video: $inputs.video, media: $nodes.decode.media, shots: $nodes.decode.shots}
47
+ # who on screen speaks each utterance (LR-ASD over the face tracks and the audio), and which person each voice is
48
+ - id: speakers
49
+ uses: vision.active-speaker@1.1.0
50
+ inputs: {video: $inputs.video, audio: $nodes.decode.audio, shots: $nodes.decode.shots, faces: $nodes.faces.faces,
51
+ transcript: $nodes.diarize.transcript}
52
+ - id: bodies
53
+ uses: vision.body-pose@1.1.0
54
+ inputs: {video: $inputs.video, media: $nodes.decode.media, shots: $nodes.decode.shots, faces: $nodes.faces.faces}
55
+ # who faces whom: MediaPipe 3D torso direction for the bodies' own person boxes (no second detector pass)
56
+ - id: facing
57
+ uses: vision.facing@1.1.0
58
+ inputs: {video: $inputs.video, media: $nodes.decode.media, bodies: $nodes.bodies.bodies}
59
+ # hand boxes from the bodies' hand keypoints and a hand detector; each hand's body and face come with the bodies
60
+ - id: hands
61
+ uses: vision.hands@1.1.0
62
+ inputs: {video: $inputs.video, media: $nodes.decode.media, shots: $nodes.decode.shots, bodies: $nodes.bodies.bodies}
63
+ # objects in two steps: objects_track outlines and tracks the things the captions call names (its vocabulary; how
64
+ # this was chosen over a names step of its own is in graphs/full-objects-names.yaml)
65
+ # with SAM 3 and embeds them with DINOv3 (both gated: HF_TOKEN), on the GPU with no API; objects checks each track's
66
+ # crops with Haiku (frames and faces), finds who wears what (faces, bodies) and links them across shots
67
+ - id: objects_track
68
+ uses: vision.objects-track@1.0.0
69
+ inputs: {vocabulary: $nodes.captions.vocabulary, video: $inputs.video, media: $nodes.decode.media,
70
+ shots: $nodes.decode.shots, faces: $nodes.faces.faces, bodies: $nodes.bodies.bodies}
71
+ - id: objects
72
+ uses: vision.objects@2.0.0
73
+ inputs: {tracks: $nodes.objects_track.tracks, shots: $nodes.decode.shots, faces: $nodes.faces.faces,
74
+ bodies: $nodes.bodies.bodies, media: $nodes.decode.media}
75
+ # hand over mouth, self-touch, pointing and raised hands from the hands' keypoints, py-feat's landmarks and the bodies'
76
+ # shoulders. objects is optional: with it, an object can be what a hand points at; leaving objects out (--only)
77
+ # still runs gestures, pointing at faces only
78
+ - id: gestures
79
+ uses: vision.gestures@1.0.0
80
+ inputs: {video: $inputs.video, hands: $nodes.hands.hands, bodies: $nodes.bodies.bodies, faces: $nodes.faces.faces,
81
+ detail: $nodes.faces.detail, objects: $nodes.objects.objects}
82
+ # objects is optional for gaze: when the objects step runs, its tracks are gaze targets too (gaze waits for it); when
83
+ # --only leaves it out, gaze runs on the faces alone
84
+ - id: gaze
85
+ uses: vision.gaze@1.2.0
86
+ inputs: {video: $inputs.video, media: $nodes.decode.media, faces: $nodes.faces.faces, objects: $nodes.objects.objects}
87
+ # the speech it reads is diarized, as the bake-off's captions read it: lines by voice (SPEAKER_00, ...), not one SPEAKER
88
+ - id: captions
89
+ uses: vlm.shot-captions-objects@1.0.0
90
+ inputs: {shots: $nodes.decode.shots, keyframes: $nodes.decode.keyframes, transcript: $nodes.diarize.transcript,
91
+ faces: $nodes.faces.faces, media: $nodes.decode.media}
92
+ # how each utterance is said: Praat's measures against its speaker's pitch range (the speakers layer names the speaker)
93
+ # and the Odyssey baseline's arousal, dominance and valence, all on this machine
94
+ - id: prosody
95
+ uses: audio.prosody@1.0.0
96
+ inputs: {audio: $nodes.decode.audio, transcript: $nodes.diarize.transcript, speakers: $nodes.speakers.speakers}
97
+ # the delivery in words and tags, by gpt-audio-1.5 (each utterance's audio with 1 s of context, and its transcript
98
+ # lines, go to OpenAI: OPENAI_API_KEY), written into the prosody layer. Its own node, so prosody runs without uploads
99
+ - id: prosody_captions
100
+ uses: audio.prosody-captions@1.0.0
101
+ inputs: {audio: $nodes.decode.audio, transcript: $nodes.diarize.transcript, prosody: $nodes.prosody.prosody}
@@ -80,28 +80,25 @@ with Node() as node:
80
80
  u = units[k]
81
81
  i, j = u["samples"]
82
82
  text = sc.prompt(units, k)
83
- r = call(lambda: client.chat.completions.create(
84
- model=sc.GPT_MODEL, modalities=["text"],
85
- tools=[{"type": "function", "function": {"name": "record_style", "description": "Record how the target is said",
86
- "parameters": sc.CAPTION_SCHEMA, "strict": True}}],
87
- tool_choice={"type": "function", "function": {"name": "record_style"}},
88
- messages=[{"role": "system", "content": sc.GPT_INSTRUCTIONS},
89
- {"role": "user", "content": [{"type": "text", "text": text},
90
- {"type": "input_audio", "input_audio": {"data": wav_b64(pcm[2 * i:2 * j]), "format": "wav"}}]}]))
91
- d = r.usage.prompt_tokens_details
92
- usage = sc.cost(sc.GPT_MODEL, r.usage.prompt_tokens, d.audio_tokens if d else 0, r.usage.completion_tokens)
93
- spend(usage)
94
- choice = r.choices[0]
95
- if not choice.message.tool_calls:
96
- raise NodeError("internal", f"{u['id']}: no function call in the reply (finish_reason {choice.finish_reason})")
97
- arguments = choice.message.tool_calls[0].function.arguments
98
- try:
99
- parsed = sc.parse_caption(arguments)
100
- except ValueError as e:
101
- raise NodeError("internal", f"{u['id']}: {e}") from None
102
- return {"parsed": parsed, "usage": usage, "prompt": text, "outside": sc.outside_tags(arguments),
103
- "raw": {"model": r.model, "finish_reason": choice.finish_reason, "arguments": arguments,
104
- "usage": r.usage.model_dump(mode="json")}}
83
+
84
+ def ask():
85
+ r = call(lambda: client.chat.completions.create(
86
+ model=sc.GPT_MODEL, modalities=["text"],
87
+ tools=[{"type": "function", "function": {"name": "record_style", "description": "Record how the target is said",
88
+ "parameters": sc.CAPTION_SCHEMA, "strict": True}}],
89
+ tool_choice={"type": "function", "function": {"name": "record_style"}},
90
+ messages=[{"role": "system", "content": sc.GPT_INSTRUCTIONS},
91
+ {"role": "user", "content": [{"type": "text", "text": text},
92
+ {"type": "input_audio", "input_audio": {"data": wav_b64(pcm[2 * i:2 * j]), "format": "wav"}}]}]))
93
+ d = r.usage.prompt_tokens_details
94
+ usage = sc.cost(sc.GPT_MODEL, r.usage.prompt_tokens, d.audio_tokens if d else 0, r.usage.completion_tokens)
95
+ spend(usage)
96
+ choice = r.choices[0]
97
+ arguments = choice.message.tool_calls[0].function.arguments if choice.message.tool_calls else None
98
+ return arguments, usage, {"model": r.model, "finish_reason": choice.finish_reason, "arguments": arguments,
99
+ "usage": r.usage.model_dump(mode="json")}
100
+ got = sc.caption_attempt(ask)
101
+ return {**got, "prompt": text, "ask": ask, "asked_again": []}
105
102
 
106
103
  def tag(description: str) -> dict:
107
104
  r = call(lambda: client.chat.completions.create(
@@ -125,8 +122,26 @@ with Node() as node:
125
122
  captions = sc.fan_out(caption, range(len(units))) # on a failure, no further call starts
126
123
  caption_s = round(time.time() - t0, 1)
127
124
  t0 = time.time()
128
- tagged = sc.fan_out(lambda c: tag(c["parsed"]["description"]), captions)
125
+ good = [k for k, c in enumerate(captions) if "rejected" not in c]
126
+ tagged = dict(zip(good, sc.fan_out(lambda k: tag(captions[k]["parsed"]["description"]), good)))
129
127
  tagger_s = round(time.time() - t0, 1)
128
+ # a reply that was not the schema's, asked again once every other call is made: then what the step spent is all but
129
+ # this second call and its tag, which must stay within its reservation (style_captions.may_ask_again)
130
+ for k, c in enumerate(captions):
131
+ if "rejected" not in c:
132
+ continue
133
+ tag_usd = max((t["usage"]["usd"] for t in tagged.values()), default=c["usage"]["usd"])
134
+ if not sc.may_ask_again(totals["usd"], c["usage"]["usd"], tag_usd, len(units)):
135
+ raise NodeError("internal", f"{units[k]['id']}: not asked again (the step's reservation would not cover it): "
136
+ f"{sc.rejected_text(c['rejected'])}")
137
+ try:
138
+ captions[k] = {**c, **sc.asked_again(c, sc.caption_attempt(c["ask"]))}
139
+ except ValueError as e:
140
+ raise NodeError("internal", f"{units[k]['id']}: {e}") from None
141
+ tagged[k] = tag(captions[k]["parsed"]["description"])
142
+ tagged = [tagged[k] for k in range(len(units))]
143
+ for c in captions:
144
+ c["outside"] = sc.outside_tags(c["arguments"])
130
145
 
131
146
  for m in prosody["producer"]["models"]: # the measures' and emotion's models, as the prosody node recorded them
132
147
  node.model(m["id"], licence_mode=m["licence_mode"], source=m.get("source"), revision=m.get("revision"), licence=m.get("licence"))
@@ -143,6 +158,9 @@ with Node() as node:
143
158
  {"file": captions_raw.FILE, "format": "parquet", "description": captions_raw.DESCRIPTION}))
144
159
  # tags the audio model gave outside the list, dropped from their captions: utterance -> tags
145
160
  outside = {u["id"]: c["outside"] for u, c in zip(units, captions) if c["outside"]}
146
- node.usage(utterances=len(units), workers=sc.WORKERS, tags_outside_list=outside,
161
+ # replies that were not the schema's JSON, asked again once (style_captions.caption_attempt, asked_again): utterance -> the first
162
+ # reply as it came (its problem, finish_reason and arguments), to find out why
163
+ asked_again = {u["id"]: c["asked_again"][0] for u, c in zip(units, captions) if c["asked_again"]}
164
+ node.usage(utterances=len(units), workers=sc.WORKERS, tags_outside_list=outside, asked_again=asked_again,
147
165
  usd_by_model={sc.GPT_MODEL: round(sum(c["usage"]["usd"] for c in captions), 4),
148
166
  sc.TAGGER: round(sum(t["usage"]["usd"] for t in tagged), 4)})
@@ -136,6 +136,48 @@ def cost(model: str, prompt_tokens: int, audio_tokens: int | None, completion_to
136
136
  return {"text_in": text_in, "audio_in": audio_in, "out": completion_tokens, "usd": usd}
137
137
 
138
138
 
139
+ def caption_attempt(ask) -> dict:
140
+ """One call for one target's caption: `ask()` makes it and gives (its function call's arguments, or None when it
141
+ made none; its usage, as cost() gives it; its raw reply). {parsed, arguments, raw, usage}, or {rejected: {problem,
142
+ finish_reason, arguments}, usage} when the reply is not the schema's. Why one can fail the schema is known: the
143
+ audio models do not constrain their function call to it (strict or not; the same reason off-list tags are dropped,
144
+ parse_caption), so a sample can be malformed (one was cut short of a comma on a pod's run, 2026-10-31)."""
145
+ arguments, usage, raw = ask()
146
+ try:
147
+ if arguments is None:
148
+ raise ValueError(f"no function call in the reply (finish_reason {raw.get('finish_reason')})")
149
+ return {"parsed": parse_caption(arguments), "arguments": arguments, "raw": raw, "usage": usage}
150
+ except ValueError as e:
151
+ return {"rejected": {"problem": str(e), "finish_reason": raw.get("finish_reason"), "arguments": arguments},
152
+ "usage": usage}
153
+
154
+
155
+ def rejected_text(rejected: dict) -> str:
156
+ return (f"{rejected['problem']} (finish_reason {rejected['finish_reason']}, "
157
+ f"arguments {str(rejected['arguments'])[:300]!r})")
158
+
159
+
160
+ def may_ask_again(spent_usd: float, caption_usd: float, tag_usd: float, utterances: int) -> bool:
161
+ """Whether a rejected caption may be asked again: once every other call of the step has been made (spent_usd is
162
+ then all but this), only if this caption's second call and its tag stay within what the step reserved (the
163
+ manifest's cost, an utterance: review of #53)."""
164
+ return spent_usd + caption_usd + tag_usd <= utterances * COST_PER_UTTERANCE
165
+
166
+
167
+ def asked_again(first: dict, second: dict) -> dict:
168
+ """A caption asked a second time: the second's, with both calls' usage and the first's rejected reply
169
+ (asked_again). ValueError when the second is not the schema's either."""
170
+ if "rejected" in second:
171
+ raise ValueError(f"asked twice, and neither reply was the schema's: {rejected_text(first['rejected'])}; "
172
+ f"{rejected_text(second['rejected'])}")
173
+ usage = {k: first["usage"][k] + second["usage"][k] for k in first["usage"]}
174
+ return {**second, "usage": usage, "asked_again": [first["rejected"]]}
175
+
176
+
177
+ # what the step reserves an utterance (manifest.yaml's cost; a test checks they agree): one caption call and its tags
178
+ COST_PER_UTTERANCE = 0.008
179
+
180
+
139
181
  def parse_caption(arguments: str) -> dict:
140
182
  """The function call's arguments -> {description, tags, emphasized_words}; ValueError when they are not that. Its tags
141
183
  are those of the list only: gpt-audio-1.5 does not hold a function call to the schema's enum (strict or not), so a tag
@@ -0,0 +1,107 @@
1
+ """audio.prosody-captions run as the coordinator runs it, with OpenAI stood in for: every caption made once, then the
2
+ tags, then a reply that was not the schema's asked again once, only within what the step reserved (review of #53).
3
+ The stand-in answers by what it is asked (the target's line, from the prompt), never by when.
4
+ Run: uv run --no-project --with pytest --with numpy --with-editable ./sdk pytest -q nodes/audio/prosody-captions"""
5
+
6
+ import json
7
+ import runpy
8
+ import sys
9
+ import threading
10
+ import types
11
+ import wave
12
+ from pathlib import Path
13
+ from types import SimpleNamespace
14
+
15
+ import pytest
16
+
17
+ import style_captions as sc
18
+ from test_style_captions import N, PROSODY, TRANSCRIPT
19
+
20
+ HERE = Path(__file__).resolve().parent
21
+ GOOD = {"description": "Calm and even.", "tags": ["calm"], "emphasized_words": []}
22
+
23
+
24
+ class OpenAI:
25
+ """The client: a caption (the first one for `bad`'s line cut short, as gpt-audio's was on a pod), or tags. calls:
26
+ every request, in order; usd: what each call is billed (prompt tokens, at the models' prices)."""
27
+ calls: list = []
28
+ bad: set = set()
29
+ prompt_tokens = 100
30
+ lock = threading.Lock()
31
+
32
+ def __init__(self, api_key):
33
+ assert api_key == "key"
34
+ self.chat = SimpleNamespace(completions=self)
35
+
36
+ def create(self, **request):
37
+ usage = SimpleNamespace(prompt_tokens=self.prompt_tokens, completion_tokens=20,
38
+ prompt_tokens_details=SimpleNamespace(audio_tokens=0), model_dump=lambda mode: {})
39
+ if "tools" in request:
40
+ text = request["messages"][1]["content"][0]["text"]
41
+ target = text.split('Target transcript (automatic, may contain errors): "')[1].split('"')[0]
42
+ with self.lock:
43
+ self.calls.append(request)
44
+ asked = sum(1 for c in self.calls if "tools" in c and c["messages"][1]["content"][0]["text"] == text)
45
+ arguments = '{"description": "Calm" "tags": []}' if target in self.bad and asked == 1 else json.dumps(GOOD)
46
+ message = SimpleNamespace(tool_calls=[SimpleNamespace(function=SimpleNamespace(arguments=arguments))])
47
+ return SimpleNamespace(model=request["model"], usage=usage,
48
+ choices=[SimpleNamespace(message=message, finish_reason="tool_calls")])
49
+ with self.lock:
50
+ self.calls.append(request)
51
+ return SimpleNamespace(model=request["model"], usage=usage,
52
+ choices=[SimpleNamespace(message=SimpleNamespace(content=json.dumps({"tags": ["calm"]})),
53
+ finish_reason="stop")])
54
+
55
+
56
+ @pytest.fixture
57
+ def openai(monkeypatch):
58
+ module = types.ModuleType("openai")
59
+ module.OpenAI = OpenAI
60
+ for name in ("RateLimitError", "InternalServerError", "APIConnectionError", "AuthenticationError"):
61
+ setattr(module, name, type(name, (Exception,), {}))
62
+ monkeypatch.setitem(sys.modules, "openai", module)
63
+ monkeypatch.setenv("OPENAI_API_KEY", "key")
64
+ OpenAI.calls, OpenAI.bad, OpenAI.prompt_tokens = [], set(), 100
65
+ return OpenAI
66
+
67
+
68
+ def run(tmp_path, monkeypatch) -> dict:
69
+ audio = tmp_path / "audio.wav"
70
+ with wave.open(str(audio), "wb") as w:
71
+ w.setnchannels(1)
72
+ w.setsampwidth(2)
73
+ w.setframerate(sc.RATE)
74
+ w.writeframes(b"\0\0" * N)
75
+ (tmp_path / "transcript.json").write_text(json.dumps(TRANSCRIPT))
76
+ (tmp_path / "prosody.json").write_text(json.dumps(PROSODY))
77
+ request = tmp_path / "request.json"
78
+ request.write_text(json.dumps({"protocol": 1, "out_dir": str(tmp_path / "out"), "accelerator": "cpu", "config": {},
79
+ "secrets": ["OPENAI_API_KEY"], "producer": {"stage": "prosody", "stage_version": "1.0.0"},
80
+ "inputs": {"audio": {"path": str(audio)}, "transcript": {"path": str(tmp_path / "transcript.json")},
81
+ "prosody": {"path": str(tmp_path / "prosody.json")}}}))
82
+ monkeypatch.setattr(sys, "argv", ["node.py", "--request", str(request)])
83
+ try:
84
+ runpy.run_path(str(HERE / "node.py"), run_name="__main__")
85
+ except SystemExit:
86
+ pass
87
+ return json.loads((tmp_path / "response.json").read_text())
88
+
89
+
90
+ def test_a_reply_that_is_not_the_schemas_is_asked_again_after_every_other_call(tmp_path, monkeypatch, openai):
91
+ openai.bad = {"orange lamp."} # utt:3's line
92
+ got = run(tmp_path, monkeypatch)
93
+ assert got["ok"], got.get("error")
94
+ kinds = ["caption" if "tools" in c else "tag" for c in openai.calls]
95
+ # three captions, two tags (the good ones), then utt:3 asked again and tagged
96
+ assert kinds == ["caption"] * 3 + ["tag"] * 2 + ["caption", "tag"]
97
+ first = got["usage"]["asked_again"]["utt:3"]
98
+ assert "delimiter" in first["problem"] and first["arguments"] == '{"description": "Calm" "tags": []}'
99
+ assert got["usage"]["calls"] == 7
100
+
101
+
102
+ def test_a_reply_is_not_asked_again_past_the_steps_reservation(tmp_path, monkeypatch, openai):
103
+ openai.bad = {"orange lamp."}
104
+ openai.prompt_tokens = 4000 # each call near the utterance's reservation: a second caption would pass it
105
+ got = run(tmp_path, monkeypatch)
106
+ assert not got["ok"] and "not asked again" in got["error"]["message"] and "utt:3" in got["error"]["message"]
107
+ assert ["caption" if "tools" in c else "tag" for c in openai.calls] == ["caption"] * 3 + ["tag"] * 2
@@ -162,3 +162,49 @@ def test_the_fan_out_keeps_the_items_order():
162
162
  client = FailingClient(-1)
163
163
  got = sc.fan_out(lambda i: client.chat.completions.create(utterance=i)["utterance"], range(50), workers=4)
164
164
  assert got == list(range(50)) and client.calls == 50
165
+
166
+
167
+ GOOD = json.dumps({"description": "Calm.", "tags": ["calm"], "emphasized_words": []})
168
+
169
+
170
+ def asker(*replies):
171
+ """One call per reply given, in order: (arguments or None, usage, raw), as node.py's ask makes them."""
172
+ calls = iter(replies)
173
+
174
+ def ask():
175
+ arguments = next(calls)
176
+ return arguments, {"text_in": 10, "audio_in": 5, "out": 3, "usd": 0.01}, {"finish_reason": "tool_calls", "arguments": arguments}
177
+ return ask
178
+
179
+
180
+ def test_a_caption_reply_that_is_not_the_schemas_json_is_rejected_whole_and_a_second_one_taken():
181
+ """gpt-audio does not constrain its function call to the schema: a reply that is not its JSON (a pod's run,
182
+ 2026-10-31: "Expecting ',' delimiter") is kept as it came, and a second reply, if asked, is the caption, both calls
183
+ billed."""
184
+ calls = asker('{"description": "Calm" "tags": []}', GOOD)
185
+ first = sc.caption_attempt(calls)
186
+ assert "delimiter" in first["rejected"]["problem"] and first["rejected"]["arguments"] == '{"description": "Calm" "tags": []}'
187
+ assert first["rejected"]["finish_reason"] == "tool_calls"
188
+ got = sc.asked_again(first, sc.caption_attempt(calls))
189
+ assert got["parsed"]["description"] == "Calm." and got["arguments"] == GOOD and got["asked_again"] == [first["rejected"]]
190
+ assert got["usage"] == {"text_in": 20, "audio_in": 10, "out": 6, "usd": 0.02}
191
+ assert "rejected" not in sc.caption_attempt(asker(GOOD))
192
+
193
+
194
+ def test_a_second_reply_that_is_not_the_schemas_fails_the_caption():
195
+ calls = asker("{", None)
196
+ first = sc.caption_attempt(calls)
197
+ with pytest.raises(ValueError, match="asked twice"):
198
+ sc.asked_again(first, sc.caption_attempt(calls))
199
+
200
+
201
+ def test_a_reply_is_asked_again_only_within_what_the_step_reserved():
202
+ """Review of #53: asked once every other call is made, so what the step has spent is all but this caption's second
203
+ call and its tag, which must stay within the step's reservation (COST_PER_UTTERANCE an utterance)."""
204
+ assert sc.may_ask_again(0.07, 0.005, 0.001, 10) # 0.076 <= 0.08
205
+ assert not sc.may_ask_again(0.076, 0.005, 0.001, 10) # 0.082 > 0.08
206
+
207
+
208
+ def test_what_an_utterance_is_reserved_is_the_manifests():
209
+ lines = (Path(sc.__file__).parent / "manifest.yaml").read_text().splitlines()
210
+ assert f"cost: {{usd: {sc.COST_PER_UTTERANCE}, per: utterance, input: transcript}}" in lines
@@ -16,9 +16,11 @@ config:
16
16
  runners:
17
17
  - {kind: native, env: envs/audio-events, entry: node.py, accelerator: mps, platforms: [darwin/arm64]}
18
18
  # PyPI's torch 2.8 wheels for Linux carry CUDA 12.8 (envs/audio-events); the machine needs an NVIDIA driver >= 570.
19
- # PE-A-Frame large is 5.8 GB of float32 weights, run on up to 140 s of audio at once: 17 GB allocated on the Apple GPU
20
- # (not yet measured on CUDA). 20, not 24: a "24 GB" card reports 23.99, an L4 22.5, and the cloud places 22 and 20.
21
- - {kind: native, env: envs/audio-events, entry: node.py, accelerator: cuda, platforms: [linux/amd64], minimum_vram_gb: 20}
19
+ # PE-A-Frame large is 5.8 GB of float32 weights, run on up to 140 s of audio at once: 17 GB allocated on the Apple GPU.
20
+ # On CUDA (an RTX 3090, 2026-10-31), with expandable segments (node.py), the whole step peaks at 17.1 GB for a 92 s
21
+ # soundtrack and 22.1 GB for 131.8 s, about 0.13 GB a second more: 23.1 GB for a 140 s piece. 23, not 24: a "24 GB"
22
+ # card reports 24 or 23.99; an L4 (22.5) and a 20 GB card are too small
23
+ - {kind: native, env: envs/audio-events, entry: node.py, accelerator: cuda, platforms: [linux/amd64], minimum_vram_gb: 23}
22
24
  - {kind: native, env: envs/audio-events, entry: node.py, accelerator: cpu}
23
25
  # on the CPU, 28 GB resident at most (PE-A-Frame on nes-biking's 137.6 s soundtrack, one piece)
24
26
  resources: {cpu_cores: 4, memory_gb: 28, gpu: exclusive}
@@ -18,9 +18,15 @@ Licences: PANNs code MIT and weights CC-BY-4.0, EfficientAT code and weights MIT
18
18
  are CC-BY-4.0), PE-A-Frame Apache-2.0: commercial-ok, as PLAN.md §4 lists them.
19
19
  """
20
20
 
21
+ import os
21
22
  import time
22
23
 
23
- import numpy as np
24
+ # PE-A-Frame's largest allocation (7.38 GiB on a 92 s soundtrack) failed on a 24 GB card with 6.7 GiB free and 6.7 GiB
25
+ # more reserved by PyTorch but unused: the caching allocator's blocks, split by the pass's own earlier tensors, could not
26
+ # hold it (a RunPod pod, 2026-10-31). Expandable segments map that memory as one; set before torch is first imported
27
+ os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
28
+
29
+ import numpy as np # noqa: E402
24
30
 
25
31
  from perception_node import Node, NodeError
26
32
  from models import PEAFRAME, PEAFRAME_SR, TAGGER_SR, WEIGHTS, device, load_mn40, load_panns, load_peaframe, run_mn40, run_panns, run_peaframe, sync
@@ -44,12 +44,7 @@ VOCAB_SCHEMA = {
44
44
  "additionalProperties": False,
45
45
  }
46
46
 
47
- VOCAB_INSTRUCTIONS = f"""These are three frames from one shot of a video. List up to {MAX_OBJECTS} concrete \
48
- physical objects that are clearly visible in the frames and could be outlined and tracked with a \
49
- segmentation mask.
50
-
51
- Rules:
52
- - Generic nouns a detector would understand: 1-2 words, singular, lower case, the plain name of \
47
+ VOCAB_RULES = """- Generic nouns a detector would understand: 1-2 words, singular, lower case, the plain name of \
53
48
  the kind of thing (e.g. "bicycle", "ramp", "mug", "car", "helmet", "chair"), not a description of \
54
49
  this one ("helmet", not "full-face helmet"; "cup", not "white paper cup").
55
50
  - Never people, faces, hands or other body parts, and never hair, wigs or beards.
@@ -61,7 +56,14 @@ separately. Other things worn or held are objects (e.g. "helmet", "glove", "earr
61
56
  "watch", "bag", "microphone").
62
57
  - On-screen text and logos are not objects.
63
58
  - If an object of the same kind was named in an earlier shot, reuse exactly that phrase.
64
- - Most prominent first. Return an empty list if there is nothing that fits."""
59
+ - Most prominent first. Return an empty list if there is nothing that fits.""" # also given to the captions call that names the objects (vlm.shot-captions-objects)
60
+
61
+ VOCAB_INSTRUCTIONS = f"""These are three frames from one shot of a video. List up to {MAX_OBJECTS} concrete \
62
+ physical objects that are clearly visible in the frames and could be outlined and tracked with a \
63
+ segmentation mask.
64
+
65
+ Rules:
66
+ {VOCAB_RULES}"""
65
67
 
66
68
  NAMES_SCHEMA = {
67
69
  "type": "object",
@@ -156,7 +158,7 @@ def reply_json(response):
156
158
 
157
159
  def fan_out(fn, items, workers: int | None = None) -> list:
158
160
  """[fn(x) for x in items] on `workers` threads (WORKERS when not given), in order, raising the first failure:
159
- prosody-captions' style_captions.fan_out (a node's code is its own folder's, so it is not imported from there). At
161
+ prosody-captions' style_captions.fan_out (a copy: that node's runner does not share this folder, Runner.shares). At
160
162
  most `workers` calls are submitted at a time, the next only once one has finished, so after a failure no call
161
163
  starts: those running finish (each billed call reports its spend itself) and the failure is raised. The results are
162
164
  in the items' order whatever order the calls finish in."""