slide2vec 7.1.2__tar.gz → 7.1.4__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 (189) hide show
  1. {slide2vec-7.1.2 → slide2vec-7.1.4}/PKG-INFO +1 -1
  2. {slide2vec-7.1.2 → slide2vec-7.1.4}/pyproject.toml +3 -3
  3. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/__init__.py +1 -1
  4. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/api.py +44 -0
  5. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/__init__.py +2 -0
  6. slide2vec-7.1.4/slide2vec/encoders/code.py +102 -0
  7. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/inference.py +7 -2
  8. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/dense_image_recipe.py +58 -2
  9. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/dense_image_stage.py +3 -28
  10. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/feature_identity.py +81 -10
  11. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec.egg-info/PKG-INFO +1 -1
  12. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec.egg-info/SOURCES.txt +3 -0
  13. slide2vec-7.1.4/tests/test_dense_identity_api.py +430 -0
  14. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_dense_image_resume.py +5 -0
  15. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_dense_image_shard.py +2 -0
  16. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_dense_stage.py +16 -0
  17. slide2vec-7.1.4/tests/test_encoder_code.py +241 -0
  18. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_encoder_plugins.py +9 -1
  19. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_feature_identity.py +106 -13
  20. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_output_consistency.py +3 -0
  21. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_pooled_identity_api.py +12 -1
  22. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_soma_migration.py +1 -0
  23. {slide2vec-7.1.2 → slide2vec-7.1.4}/LICENSE +0 -0
  24. {slide2vec-7.1.2 → slide2vec-7.1.4}/README.md +0 -0
  25. {slide2vec-7.1.2 → slide2vec-7.1.4}/setup.cfg +0 -0
  26. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/__main__.py +0 -0
  27. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/_cufile.py +0 -0
  28. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/artifacts.py +0 -0
  29. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/cli.py +0 -0
  30. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/configs/__init__.py +0 -0
  31. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/configs/default.yaml +0 -0
  32. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/configs/resources.py +0 -0
  33. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/cufile.json +0 -0
  34. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/data/__init__.py +0 -0
  35. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/data/dataset.py +0 -0
  36. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/data/tile_reader.py +0 -0
  37. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/data/tile_store.py +0 -0
  38. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/distributed/__init__.py +0 -0
  39. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/distributed/dense_image_worker.py +0 -0
  40. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/distributed/dense_worker.py +0 -0
  41. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/distributed/direct_embed_worker.py +0 -0
  42. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/distributed/image_worker.py +0 -0
  43. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/distributed/pin_gpu.py +0 -0
  44. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/distributed/pipeline_worker.py +0 -0
  45. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/distributed/worker_entry.py +0 -0
  46. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/base.py +0 -0
  47. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/__init__.py +0 -0
  48. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/conch.py +0 -0
  49. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/dinov2.py +0 -0
  50. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/dinov3.py +0 -0
  51. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/genbio.py +0 -0
  52. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/gigapath.py +0 -0
  53. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/gpfm.py +0 -0
  54. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/hibou.py +0 -0
  55. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/hoptimus.py +0 -0
  56. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/isight.py +0 -0
  57. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/lunit.py +0 -0
  58. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/mettle.py +0 -0
  59. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/midnight.py +0 -0
  60. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/moozy/__init__.py +0 -0
  61. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/moozy/blocks.py +0 -0
  62. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/moozy/case.py +0 -0
  63. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/moozy/loading.py +0 -0
  64. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/moozy/slide.py +0 -0
  65. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/moozy/types.py +0 -0
  66. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/mstar.py +0 -0
  67. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/musk.py +0 -0
  68. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/phikon.py +0 -0
  69. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/prism.py +0 -0
  70. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/prism2.py +0 -0
  71. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/prost40m.py +0 -0
  72. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/rudolfv2.py +0 -0
  73. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/titan.py +0 -0
  74. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/uni.py +0 -0
  75. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/virchow.py +0 -0
  76. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/models/waiv.py +0 -0
  77. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/registry.py +0 -0
  78. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/encoders/validation.py +0 -0
  79. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/progress.py +0 -0
  80. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/__init__.py +0 -0
  81. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/artifacts_collect.py +0 -0
  82. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/batching.py +0 -0
  83. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/cpu_budget.py +0 -0
  84. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/dense_encode.py +0 -0
  85. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/dense_encoder_input.py +0 -0
  86. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/dense_image_reading.py +0 -0
  87. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/dense_image_shard.py +0 -0
  88. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/dense_regions.py +0 -0
  89. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/dense_shard.py +0 -0
  90. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/dense_sliding.py +0 -0
  91. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/dense_stage.py +0 -0
  92. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/distributed.py +0 -0
  93. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/distributed_stage.py +0 -0
  94. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/effective_encoder_input.py +0 -0
  95. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/embedding.py +0 -0
  96. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/embedding_persist.py +0 -0
  97. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/embedding_pipeline.py +0 -0
  98. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/encoder_input_contract.py +0 -0
  99. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/hierarchical.py +0 -0
  100. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/image_shard.py +0 -0
  101. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/image_specs.py +0 -0
  102. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/image_stage.py +0 -0
  103. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/manifest.py +0 -0
  104. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/model_settings.py +0 -0
  105. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/patient_pipeline.py +0 -0
  106. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/persist_callbacks.py +0 -0
  107. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/persistence.py +0 -0
  108. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/pooled_encoder_input.py +0 -0
  109. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/preprocessing.py +0 -0
  110. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/process_list.py +0 -0
  111. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/progress_bridge.py +0 -0
  112. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/registry.py +0 -0
  113. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/serialization.py +0 -0
  114. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/sharding.py +0 -0
  115. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/slide_encode.py +0 -0
  116. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/tiling.py +0 -0
  117. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/tiling_pipeline.py +0 -0
  118. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/types.py +0 -0
  119. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/runtime/worker_io.py +0 -0
  120. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/utils/__init__.py +0 -0
  121. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/utils/config.py +0 -0
  122. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/utils/coordinates.py +0 -0
  123. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/utils/log_utils.py +0 -0
  124. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/utils/tiling_io.py +0 -0
  125. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec/utils/utils.py +0 -0
  126. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec.egg-info/dependency_links.txt +0 -0
  127. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec.egg-info/entry_points.txt +0 -0
  128. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec.egg-info/not-zip-safe +0 -0
  129. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec.egg-info/requires.txt +0 -0
  130. {slide2vec-7.1.2 → slide2vec-7.1.4}/slide2vec.egg-info/top_level.txt +0 -0
  131. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_attention_extraction.py +0 -0
  132. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_benchmark_tooling.py +0 -0
  133. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_cli_noninteractive.py +0 -0
  134. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_cufile_compat_mode.py +0 -0
  135. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_dense_encode_kit.py +0 -0
  136. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_dense_encoder_input.py +0 -0
  137. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_dense_extraction.py +0 -0
  138. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_dense_image_reading.py +0 -0
  139. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_dense_image_stage.py +0 -0
  140. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_dense_regions.py +0 -0
  141. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_dense_shard.py +0 -0
  142. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_dense_sliding.py +0 -0
  143. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_dense_source_spacing.py +0 -0
  144. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_dense_worker.py +0 -0
  145. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_dinov2_natimage.py +0 -0
  146. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_dinov3_natimage.py +0 -0
  147. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_drop_in_encoders.py +0 -0
  148. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_encoder_capabilities.py +0 -0
  149. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_encoder_input_contract.py +0 -0
  150. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_encoder_preprocessing.py +0 -0
  151. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_encoder_provider_failures.py +0 -0
  152. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_encoder_registry.py +0 -0
  153. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_execution_options.py +0 -0
  154. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_gigapath.py +0 -0
  155. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_gpfm_genbio_heavy.py +0 -0
  156. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_hierarchical_encoding.py +0 -0
  157. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_hs2p5_integration.py +0 -0
  158. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_hs2p_package_cutover.py +0 -0
  159. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_image_shard.py +0 -0
  160. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_image_stage.py +0 -0
  161. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_inference_benchmark.py +0 -0
  162. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_isight.py +0 -0
  163. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_load_model_hf_auth.py +0 -0
  164. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_mascaret.py +0 -0
  165. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_mettle.py +0 -0
  166. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_on_slide_persisted.py +0 -0
  167. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_patch_size_metadata.py +0 -0
  168. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_patient_annotation_sampling.py +0 -0
  169. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_patient_manifest.py +0 -0
  170. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_patient_tiling_only.py +0 -0
  171. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_phaet.py +0 -0
  172. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_pooled_encoder_input.py +0 -0
  173. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_pooled_geometry.py +0 -0
  174. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_prism.py +0 -0
  175. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_prism2.py +0 -0
  176. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_process_list_performance.py +0 -0
  177. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_progress.py +0 -0
  178. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_regression_core.py +0 -0
  179. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_regression_inference.py +0 -0
  180. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_regression_models.py +0 -0
  181. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_rudolfv2.py +0 -0
  182. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_runtime_batching.py +0 -0
  183. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_sharding.py +0 -0
  184. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_slide_coordinate_preparation.py +0 -0
  185. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_slide_latents.py +0 -0
  186. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_tile_store.py +0 -0
  187. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_tiling_pipeline.py +0 -0
  188. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_titan.py +0 -0
  189. {slide2vec-7.1.2 → slide2vec-7.1.4}/tests/test_worker_gpu_pinning.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slide2vec
3
- Version: 7.1.2
3
+ Version: 7.1.4
4
4
  Summary: Embedding of whole slide images with Foundation Models
5
5
  Author-email: Clément Grisi <clement.grisi@radboudumc.nl>
6
6
  License-Expression: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "slide2vec"
7
- version = "7.1.2"
7
+ version = "7.1.4"
8
8
  description = "Embedding of whole slide images with Foundation Models"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -218,7 +218,7 @@ testpaths = [
218
218
  "tests",
219
219
  ]
220
220
  markers = [
221
- "heavy: real-weight foundation-model inference on CPU; minutes per test. Included in the PR suite and available separately through the manual heavy workflow (.github/workflows/nightly-heavy.yaml).",
221
+ "heavy: real-weight foundation-model inference on CPU; minutes per test. Excluded from the PR suite except the PRISM consistency test; run all of them through the manual heavy workflow (.github/workflows/nightly-heavy.yaml).",
222
222
  "gpu_integration: real one-GPU versus multi-GPU parity; requires at least two visible CUDA devices. Run explicitly with `CUDA_VISIBLE_DEVICES=0,1 python -m pytest -m gpu_integration --no-cov`.",
223
223
  ]
224
224
 
@@ -240,7 +240,7 @@ no_implicit_reexport = true
240
240
  max-line-length = 160
241
241
 
242
242
  [tool.bumpver]
243
- current_version = "7.1.2"
243
+ current_version = "7.1.4"
244
244
  version_pattern = "MAJOR.MINOR.PATCH"
245
245
  commit = false
246
246
  tag = false
@@ -36,7 +36,7 @@ from slide2vec.encoders import (
36
36
  )
37
37
 
38
38
 
39
- __version__ = "7.1.2"
39
+ __version__ = "7.1.4"
40
40
 
41
41
  __all__ = [
42
42
  "Model",
@@ -708,6 +708,45 @@ class Model:
708
708
  encoded_pixels=encoded_pixels,
709
709
  )
710
710
 
711
+ def dense_identity_differences(
712
+ self,
713
+ recorded: Mapping[str, Any],
714
+ *,
715
+ dense: "DenseOptions | DenseImageOptions",
716
+ execution: ExecutionOptions | None = None,
717
+ ) -> dict[str, tuple[Any, Any]]:
718
+ """Compare a dense sidecar's ``compatibility`` block with the current request.
719
+
720
+ The dense counterpart of :meth:`pooled_identity_differences`. Pass the
721
+ ``dense`` options and ``execution`` of :meth:`embed_regions_dense` (a
722
+ :class:`DenseOptions`) or :meth:`embed_images_dense` (a
723
+ :class:`DenseImageOptions`). Returns ``{field: (recorded, current)}`` for every
724
+ request-wide field that the record holds with a different value or does not
725
+ hold. A field the record does not hold is reported as
726
+ :data:`slide2vec.MISSING_FIELD`. Per-sample fields (the sample, its path and its
727
+ read plan) are not compared. An empty result means the record proves the
728
+ current request-wide identity. The caller owns the policy for a difference.
729
+
730
+ An invalid request raises as it does in the embed methods. ``execution``
731
+ defaults to the model's recommended precision and needs no output directory.
732
+ Comparing the ``transform`` loads weights, on an isolated CPU model. This method
733
+ does not change this model's input contract or loaded backend, encode pixels,
734
+ or write files.
735
+ """
736
+ from slide2vec.runtime.feature_identity import dense_identity_differences
737
+
738
+ if not isinstance(dense, (DenseOptions, DenseImageOptions)):
739
+ raise TypeError(
740
+ "Model.dense_identity_differences(dense=...) expects DenseOptions "
741
+ f"or DenseImageOptions, got {type(dense).__name__}"
742
+ )
743
+ return dense_identity_differences(
744
+ self,
745
+ dict(recorded),
746
+ dense=dense,
747
+ execution=_coerce_execution_options(execution, model=self),
748
+ )
749
+
711
750
  def prepare_dense_encoder(
712
751
  self,
713
752
  *,
@@ -1085,6 +1124,11 @@ class Model:
1085
1124
  non-timm backbone, say) fails before any backend is loaded and before resume
1086
1125
  inspects or invalidates an existing artifact.
1087
1126
  """
1127
+ if self.level != "tile":
1128
+ raise ValueError(
1129
+ "Dense extraction requires a tile-level foundation encoder; model "
1130
+ f"{self.name!r} is registered at level {self.level!r}."
1131
+ )
1088
1132
  contract = EncoderInputContract.declared_dense(
1089
1133
  self.name,
1090
1134
  target_size_px=dense.target_size,
@@ -16,6 +16,7 @@ from slide2vec.encoders.base import (
16
16
  resolve_recommended_dynamic_img_size,
17
17
  resolve_requested_output_variant,
18
18
  )
19
+ from slide2vec.encoders.code import encoder_code
19
20
  from slide2vec.encoders.registry import (
20
21
  EncoderCapabilities,
21
22
  EncoderProviderDiagnostic,
@@ -42,6 +43,7 @@ __all__ = [
42
43
  "reshape_tokens_to_grid",
43
44
  "resolve_recommended_dynamic_img_size",
44
45
  "resolve_requested_output_variant",
46
+ "encoder_code",
45
47
  "encoder_registry",
46
48
  "list_encoder_provider_diagnostics",
47
49
  "normalize_patch_size",
@@ -0,0 +1,102 @@
1
+ """The code a registered encoder runs, as the feature identity records it."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Mapping
6
+ import functools
7
+ import hashlib
8
+ from importlib import metadata as importlib_metadata
9
+ import inspect
10
+ import json
11
+ from pathlib import Path
12
+ import re
13
+ import sys
14
+ from typing import Any
15
+
16
+ from slide2vec.encoders.registry import encoder_registry
17
+
18
+
19
+ def encoder_code(name: str) -> dict[str, Any]:
20
+ """Return the code block of the registered encoder *name*.
21
+
22
+ ``object`` is the encoder class's ``module.qualname``. ``fingerprint`` is the
23
+ version of the non-editable installed distribution, other than slide2vec, whose
24
+ ``RECORD`` lists the file that defines the class; otherwise the SHA-256 of that
25
+ file, hashed on every call; ``None`` when there is no file (a class defined in a
26
+ notebook cell). ``source`` is the registry's ``source``. Reads the registry entry
27
+ only: the encoder is not instantiated and no weights are loaded.
28
+ """
29
+ encoder_cls = encoder_registry.require(name)
30
+ return {
31
+ "object": f"{encoder_cls.__module__}.{encoder_cls.__qualname__}",
32
+ "fingerprint": _fingerprint(encoder_cls),
33
+ "source": encoder_registry.info(name)["source"],
34
+ }
35
+
36
+
37
+ def _fingerprint(encoder_cls: type) -> str | None:
38
+ path = _source_file(encoder_cls)
39
+ if path is None:
40
+ return None
41
+ distribution = _installed_distribution(encoder_cls.__module__, path)
42
+ return _sha256(path) if distribution is None else distribution.version
43
+
44
+
45
+ def _installed_distribution(module: str, path: Path) -> importlib_metadata.Distribution | None:
46
+ """The non-editable installed distribution, other than slide2vec, whose ``RECORD``
47
+ lists *path*, or ``None``.
48
+
49
+ slide2vec is left out so that a built-in encoder's identity changes only when a
50
+ release changes its file. The distributions of a namespace package share its
51
+ top-level name and its install directory, so the top-level name only narrows the
52
+ candidates.
53
+ """
54
+ top = module.split(".", 1)[0]
55
+ for name in _packages_distributions(tuple(sys.path)).get(top, ()):
56
+ if re.sub(r"[-_.]+", "-", name).lower() == "slide2vec":
57
+ continue
58
+ try:
59
+ distribution = importlib_metadata.distribution(name)
60
+ except importlib_metadata.PackageNotFoundError:
61
+ continue
62
+ if distribution.read_text("RECORD") is None or _is_editable(distribution):
63
+ continue
64
+ try:
65
+ relative = path.relative_to(Path(distribution.locate_file("")).resolve())
66
+ except ValueError:
67
+ continue
68
+ if relative.as_posix() in {file.as_posix() for file in distribution.files or ()}:
69
+ return distribution
70
+ return None
71
+
72
+
73
+ def _is_editable(distribution) -> bool:
74
+ """Whether *distribution* is an editable install, as its ``direct_url.json`` records."""
75
+ try:
76
+ payload = json.loads(distribution.read_text("direct_url.json") or "{}")
77
+ except json.JSONDecodeError:
78
+ return False
79
+ return isinstance(payload, dict) and bool((payload.get("dir_info") or {}).get("editable"))
80
+
81
+
82
+ @functools.lru_cache(maxsize=1)
83
+ def _packages_distributions(path: tuple[str, ...]) -> Mapping[str, list[str]]:
84
+ """Top-level package -> distributions on the import *path*, scanned once per path."""
85
+ return importlib_metadata.packages_distributions()
86
+
87
+
88
+ def _source_file(encoder_cls: type) -> Path | None:
89
+ """The file that defines *encoder_cls*, or ``None`` (a notebook cell)."""
90
+ try:
91
+ path = Path(inspect.getsourcefile(encoder_cls) or inspect.getfile(encoder_cls))
92
+ except (TypeError, OSError):
93
+ return None
94
+ return path.resolve() if path.is_file() else None
95
+
96
+
97
+ def _sha256(path: Path) -> str:
98
+ digest = hashlib.sha256()
99
+ with path.open("rb") as handle:
100
+ for chunk in iter(lambda: handle.read(1024 * 1024), b""):
101
+ digest.update(chunk)
102
+ return digest.hexdigest()
@@ -725,8 +725,8 @@ def aggregate_tiles(
725
725
  execution=execution,
726
726
  )
727
727
  latents = None
728
- # Aggregation encodes no tile: the tile geometry and transform are the ones
729
- # recorded with the tile artifact.
728
+ # Aggregation encodes no tile: the tile geometry, transform and tile encoder code
729
+ # are the ones recorded with the tile artifact.
730
730
  tile_identity = metadata.get("compatibility") or {}
731
731
  compatibility = {
732
732
  **feature_identity.pooled_feature_identity(model, execution=execution),
@@ -736,6 +736,11 @@ def aggregate_tiles(
736
736
  if field in tile_identity
737
737
  },
738
738
  }
739
+ # A slide run's tile sidecar records the tile encoder's code under `tile_encoder_code`.
740
+ compatibility.pop("tile_encoder_code", None)
741
+ tile_code = tile_identity.get("tile_encoder_code") or tile_identity.get("encoder_code")
742
+ if tile_code is not None:
743
+ compatibility["tile_encoder_code"] = tile_code
739
744
  slide_artifact = embedding.write_slide_embedding_artifact(
740
745
  artifact.sample_id,
741
746
  slide_embedding,
@@ -2,11 +2,14 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from dataclasses import dataclass
5
+ import math
6
+ from dataclasses import dataclass, fields
6
7
  from pathlib import Path
7
8
  from typing import Any
8
9
 
9
- from slide2vec.encoders.registry import resolve_encoder_output
10
+ from slide2vec.encoders.code import encoder_code
11
+ from slide2vec.encoders.registry import resolve_encoder_output, resolve_preprocessing_defaults
12
+ from slide2vec.encoders.validation import validate_encoder_config
10
13
  from slide2vec.runtime.model_settings import output_dtype_name, resolve_output_precision
11
14
 
12
15
 
@@ -67,6 +70,8 @@ class DenseImageRecipe:
67
70
  """Request-wide extraction facts combined with one parent-resolved image read plan."""
68
71
 
69
72
  encoder_name: str
73
+ #: The encoder's code block (:func:`slide2vec.encoders.encoder_code`).
74
+ encoder_code: dict[str, Any]
70
75
  output_variant: str
71
76
  reader_regime: str
72
77
  spacing_source: str
@@ -99,6 +104,7 @@ class DenseImageRecipe:
99
104
  def to_dict(self) -> dict[str, Any]:
100
105
  payload = {
101
106
  "encoder_name": self.encoder_name,
107
+ "encoder_code": self.encoder_code,
102
108
  "output_variant": self.output_variant,
103
109
  "reader_regime": self.reader_regime,
104
110
  "spacing_source": self.spacing_source,
@@ -130,6 +136,20 @@ class DenseImageRecipe:
130
136
  payload["transform"] = self.transform
131
137
  return payload
132
138
 
139
+ def request_identity(self) -> dict[str, Any]:
140
+ """The request-wide fields of :meth:`for_image`: the recipe without per-image fields.
141
+
142
+ The per-image fields are the read plan's, which :meth:`for_image` replaces, and
143
+ the read sizes.
144
+ """
145
+ from slide2vec.runtime.dense_image_reading import DenseImageReadPlan
146
+
147
+ per_image = {field.name for field in fields(DenseImageReadPlan)} | {
148
+ "read_tile_size_px",
149
+ "requested_tile_size_px",
150
+ }
151
+ return {key: value for key, value in self.to_dict().items() if key not in per_image}
152
+
133
153
  def for_image(self, spec, read_plan=None) -> dict[str, Any]:
134
154
  """Add the normalized source identity to this request-wide recipe."""
135
155
  if read_plan is None:
@@ -142,6 +162,7 @@ class DenseImageRecipe:
142
162
  )
143
163
  recipe = self.to_dict()
144
164
  encoder_name = recipe.pop("encoder_name")
165
+ code = recipe.pop("encoder_code")
145
166
  output_variant = recipe.pop("output_variant")
146
167
  for field in read_plan.to_dict():
147
168
  recipe.pop(field, None)
@@ -149,6 +170,7 @@ class DenseImageRecipe:
149
170
  "sample_id": str(spec.sample_id),
150
171
  "image_path": str(Path(spec.image_path).expanduser().resolve()),
151
172
  "encoder_name": encoder_name,
173
+ "encoder_code": code,
152
174
  "output_variant": output_variant,
153
175
  **read_plan.to_dict(),
154
176
  **recipe,
@@ -158,6 +180,7 @@ class DenseImageRecipe:
158
180
  def from_dict(cls, payload: dict[str, Any]) -> "DenseImageRecipe":
159
181
  return cls(
160
182
  encoder_name=str(payload["encoder_name"]),
183
+ encoder_code=dict(payload["encoder_code"]),
161
184
  output_variant=str(payload["output_variant"]),
162
185
  reader_regime=str(payload["reader_regime"]),
163
186
  spacing_source=str(payload["spacing_source"]),
@@ -218,6 +241,38 @@ class DenseImageRecipe:
218
241
  )
219
242
 
220
243
 
244
+ def resolve_dense_image_spacing(model, dense) -> tuple[float, str]:
245
+ """Resolve and validate the run spacing of *dense*: ``(spacing_um, spacing_source)``.
246
+
247
+ An omitted spacing resolves the encoder's single registry default.
248
+ """
249
+ spacing_source = "explicit"
250
+ if dense.spacing_um is None:
251
+ try:
252
+ spacing_um = float(
253
+ resolve_preprocessing_defaults(model.name)["spacing_um"]
254
+ )
255
+ except (KeyError, ValueError) as exc:
256
+ raise ValueError(
257
+ "DenseImageOptions.spacing_um must be explicit when encoder "
258
+ f"{model.name!r} has no single resolvable model default: {exc}"
259
+ ) from exc
260
+ spacing_source = "model_default"
261
+ else:
262
+ spacing_um = float(dense.spacing_um)
263
+ if not math.isfinite(spacing_um) or spacing_um <= 0:
264
+ raise ValueError(
265
+ "DenseImageOptions.spacing_um must be a positive, finite value or None."
266
+ )
267
+ validate_encoder_config(
268
+ model.name,
269
+ requested_spacing_um=spacing_um,
270
+ allow_non_recommended=bool(model.allow_non_recommended_settings),
271
+ require_known_spacing=True,
272
+ )
273
+ return spacing_um, spacing_source
274
+
275
+
221
276
  def resolve_dense_image_recipe(
222
277
  *,
223
278
  model,
@@ -270,6 +325,7 @@ def resolve_dense_image_recipe(
270
325
  )
271
326
  return DenseImageRecipe(
272
327
  encoder_name=str(model.name),
328
+ encoder_code=encoder_code(model.name),
273
329
  output_variant=str(output["output_variant"]),
274
330
  reader_regime=str(reader_regime),
275
331
  spacing_source=(
@@ -29,7 +29,6 @@ from __future__ import annotations
29
29
 
30
30
  import json
31
31
  import logging
32
- import math
33
32
  from dataclasses import replace
34
33
  from pathlib import Path
35
34
  from subprocess import Popen
@@ -37,8 +36,6 @@ from typing import Callable, Sequence
37
36
 
38
37
  from slide2vec.api import DenseImageOptions, ImageSpec
39
38
  from slide2vec.artifacts import DenseImageArtifact
40
- from slide2vec.encoders.registry import resolve_preprocessing_defaults
41
- from slide2vec.encoders.validation import validate_encoder_config
42
39
  from slide2vec.progress import emit_progress
43
40
  from slide2vec.runtime.dense_image_shard import (
44
41
  dense_image_artifact_from_disk,
@@ -48,6 +45,7 @@ from slide2vec.runtime.dense_image_shard import (
48
45
  from slide2vec.runtime.dense_image_recipe import (
49
46
  DenseImageRecipe,
50
47
  resolve_dense_image_recipe,
48
+ resolve_dense_image_spacing,
51
49
  )
52
50
  from slide2vec.runtime.dense_image_reading import DenseImageReadPlan, resolve_spacing_read_plan
53
51
  from slide2vec.runtime.dense_stage import resolve_output_torch_dtype
@@ -115,31 +113,8 @@ def embed_images_dense(
115
113
  artifact_location="dense_image_embeddings/<sample_id>",
116
114
  )
117
115
  reader_regime, probed_auto_backends = validate_dense_image_reader_regime(specs)
118
- spacing_source = "explicit"
119
- if dense.spacing_um is None:
120
- try:
121
- spacing_um = float(
122
- resolve_preprocessing_defaults(model.name)["spacing_um"]
123
- )
124
- except (KeyError, ValueError) as exc:
125
- raise ValueError(
126
- "DenseImageOptions.spacing_um must be explicit when encoder "
127
- f"{model.name!r} has no single resolvable model default: {exc}"
128
- ) from exc
129
- spacing_source = "model_default"
130
- dense = replace(dense, spacing_um=spacing_um)
131
- else:
132
- spacing_um = float(dense.spacing_um)
133
- if spacing_um is not None and (not math.isfinite(spacing_um) or spacing_um <= 0):
134
- raise ValueError(
135
- "DenseImageOptions.spacing_um must be a positive, finite value or None."
136
- )
137
- validate_encoder_config(
138
- model.name,
139
- requested_spacing_um=spacing_um,
140
- allow_non_recommended=bool(model.allow_non_recommended_settings),
141
- require_known_spacing=True,
142
- )
116
+ spacing_um, spacing_source = resolve_dense_image_spacing(model, dense)
117
+ dense = replace(dense, spacing_um=spacing_um)
143
118
  # Declare the effective encoder input before anything is decoded, sharded or launched: an
144
119
  # image geometry this encoder cannot accept must fail here, not on the first forward pass
145
120
  # of a torchrun rank. Idempotent, so every rank re-declares for itself (dense_image_worker).
@@ -11,6 +11,7 @@ from PIL import Image
11
11
  from transformers.image_processing_utils import BaseImageProcessor
12
12
 
13
13
  from slide2vec.encoders.base import ProcessorTransform
14
+ from slide2vec.encoders.code import encoder_code
14
15
  from slide2vec.encoders.registry import encoder_registry
15
16
  from slide2vec.runtime.model_settings import output_dtype_name, resolve_output_precision
16
17
  from slide2vec.runtime.preprocessing import iter_transform_steps
@@ -35,6 +36,7 @@ def pooled_feature_identity(
35
36
  if model.level != "tile" and model.name in encoder_registry:
36
37
  info = encoder_registry.info(model.name)
37
38
  identity["tile_encoder"] = info["tile_encoder"]
39
+ identity["tile_encoder_code"] = encoder_code(info["tile_encoder"])
38
40
  identity["tile_encoder_output_variant"] = info["tile_encoder_output_variant"]
39
41
  if info.get("aggregation_revision") is not None:
40
42
  identity["aggregation_revision"] = info["aggregation_revision"]
@@ -74,15 +76,16 @@ def dense_feature_identity(model, *, dense, execution) -> dict[str, Any]:
74
76
 
75
77
 
76
78
  def _encoder_identity(model, execution) -> dict[str, Any]:
77
- """The fields every path records: the encoder, its output variant, the precision."""
79
+ """The fields every path records: the encoder, its code, its output variant, the precision."""
78
80
  from slide2vec.runtime.process_list import resolved_process_list_output_variant
79
81
 
80
- return {
81
- "encoder_name": model.name,
82
- "output_variant": resolved_process_list_output_variant(model),
83
- # An unset inference precision runs without autocast, which is fp32.
84
- "precision": execution.precision or "fp32",
85
- }
82
+ identity: dict[str, Any] = {"encoder_name": model.name}
83
+ if model.name in encoder_registry:
84
+ identity["encoder_code"] = encoder_code(model.name)
85
+ identity["output_variant"] = resolved_process_list_output_variant(model)
86
+ # An unset inference precision runs without autocast, which is fp32.
87
+ identity["precision"] = execution.precision or "fp32"
88
+ return identity
86
89
 
87
90
 
88
91
  def deferred_transform_record(
@@ -222,6 +225,69 @@ def pooled_identity_differences(
222
225
  )
223
226
 
224
227
 
228
+ def dense_identity_differences(
229
+ model,
230
+ recorded: dict[str, Any],
231
+ *,
232
+ dense,
233
+ execution,
234
+ ) -> dict[str, tuple[Any, Any]]:
235
+ """Compare a recorded dense identity with the request-wide identity of the current request.
236
+
237
+ A :class:`~slide2vec.api.DenseImageOptions` request is compared with the image recipe,
238
+ any other with the region identity. The request is validated as the embed methods
239
+ validate it, on an isolated CPU copy of *model* that declares the dense encoder-input
240
+ contract, so *model* keeps its contract and backend. The transform is that copy's
241
+ ``get_normalization_transform()``, as the shards record it.
242
+ """
243
+ from slide2vec.api import DenseImageOptions, Model
244
+
245
+ source = Model.from_preset(
246
+ model.name,
247
+ device="cpu",
248
+ output_variant=model._output_variant,
249
+ allow_non_recommended_settings=model.allow_non_recommended_settings,
250
+ )
251
+ if isinstance(dense, DenseImageOptions):
252
+ from slide2vec.runtime.dense_image_recipe import (
253
+ resolve_dense_image_recipe,
254
+ resolve_dense_image_spacing,
255
+ )
256
+
257
+ resolve_dense_image_spacing(source, dense) # validates; the spacing is per-sample
258
+ contract = source._declare_dense_encoder_input(dense, emit_run_info=False)
259
+ requested = resolve_dense_image_recipe(
260
+ model=source, contract=contract, dense=dense, execution=execution
261
+ ).request_identity()
262
+ else:
263
+ from slide2vec.encoders.registry import resolve_patch_size
264
+ from slide2vec.runtime.dense_regions import (
265
+ compute_dense_geometry,
266
+ validate_dense_request_settings,
267
+ )
268
+
269
+ plan = source._declare_dense_encoder_input(dense, emit_run_info=False).plan
270
+ # The region shards validate the same settings when they encode.
271
+ validate_dense_request_settings(
272
+ compute_dense_geometry(
273
+ target_size=plan.target_size_px,
274
+ patch_size=resolve_patch_size(plan.tile_encoder_name),
275
+ ),
276
+ pad_mode=dense.pad_mode,
277
+ window_size=dense.window_size,
278
+ overlap=dense.overlap,
279
+ feature_kind=dense.feature_kind,
280
+ attention_blocks=dense.attention_blocks,
281
+ attention_include_registers=dense.attention_include_registers,
282
+ )
283
+ requested = dense_feature_identity(source, dense=dense, execution=execution)
284
+
285
+ def resolve_transform() -> dict[str, Any]:
286
+ return transform_record(source._load_backend().model.get_normalization_transform())
287
+
288
+ return differing_fields(recorded, requested, resolve_transform=resolve_transform)
289
+
290
+
225
291
  class PooledResumeCheck:
226
292
  """Compare the completed pooled artifacts of one resume with the run's feature identity.
227
293
 
@@ -253,11 +319,16 @@ class PooledResumeCheck:
253
319
  for field, values in differing.items()
254
320
  for leaf, old, new in _leaf_differences(field, *values)
255
321
  )
322
+ # A caller can request a recorded value, but not recorded code.
323
+ remedies = (
324
+ "Re-run into a new output_dir or delete the stale artifacts."
325
+ if differing.keys() & {"encoder_code", "tile_encoder_code"}
326
+ else "Re-run into a new output_dir, delete the stale artifacts, or request "
327
+ "the recorded values."
328
+ )
256
329
  raise ValueError(
257
330
  f"Cannot resume '{sample_id}': the existing {kind} embeddings at {path} "
258
- f"were computed with a different feature identity: {details}. Re-run into "
259
- "a new output_dir, delete the stale artifacts, or request the recorded "
260
- "values."
331
+ f"were computed with a different feature identity: {details}. {remedies}"
261
332
  )
262
333
  return not missing
263
334
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slide2vec
3
- Version: 7.1.2
3
+ Version: 7.1.4
4
4
  Summary: Embedding of whole slide images with Foundation Models
5
5
  Author-email: Clément Grisi <clement.grisi@radboudumc.nl>
6
6
  License-Expression: Apache-2.0
@@ -34,6 +34,7 @@ slide2vec/distributed/pipeline_worker.py
34
34
  slide2vec/distributed/worker_entry.py
35
35
  slide2vec/encoders/__init__.py
36
36
  slide2vec/encoders/base.py
37
+ slide2vec/encoders/code.py
37
38
  slide2vec/encoders/registry.py
38
39
  slide2vec/encoders/validation.py
39
40
  slide2vec/encoders/models/__init__.py
@@ -122,6 +123,7 @@ tests/test_cufile_compat_mode.py
122
123
  tests/test_dense_encode_kit.py
123
124
  tests/test_dense_encoder_input.py
124
125
  tests/test_dense_extraction.py
126
+ tests/test_dense_identity_api.py
125
127
  tests/test_dense_image_reading.py
126
128
  tests/test_dense_image_resume.py
127
129
  tests/test_dense_image_shard.py
@@ -136,6 +138,7 @@ tests/test_dinov2_natimage.py
136
138
  tests/test_dinov3_natimage.py
137
139
  tests/test_drop_in_encoders.py
138
140
  tests/test_encoder_capabilities.py
141
+ tests/test_encoder_code.py
139
142
  tests/test_encoder_input_contract.py
140
143
  tests/test_encoder_plugins.py
141
144
  tests/test_encoder_preprocessing.py