classifyre-cli 0.4.135__tar.gz → 0.4.136__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 (346) hide show
  1. classifyre_cli-0.4.136/.turbo/turbo-build.log +3 -0
  2. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/PKG-INFO +1 -1
  3. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/package.json +1 -1
  4. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/pyproject.toml +1 -1
  5. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/scripts/generate_sdk_completions.py +33 -2
  6. classifyre_cli-0.4.136/src/augmentation/__init__.py +5 -0
  7. classifyre_cli-0.4.136/src/augmentation/contract.py +70 -0
  8. classifyre_cli-0.4.136/src/augmentation/preview.py +280 -0
  9. classifyre_cli-0.4.136/src/augmentation/runner.py +323 -0
  10. classifyre_cli-0.4.136/src/augmentation/sdk.py +489 -0
  11. classifyre_cli-0.4.136/src/augmentation/session.py +730 -0
  12. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/main.py +143 -4
  13. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/models/generated_input.py +2116 -1966
  14. classifyre_cli-0.4.136/src/notebook/child.py +228 -0
  15. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/cli.py +113 -7
  16. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/execute.py +26 -6
  17. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/protocol.py +31 -1
  18. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/redact.py +26 -2
  19. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/sdk.py +39 -1
  20. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/outputs/rest.py +31 -5
  21. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/pipeline/parsed_content_provider.py +11 -0
  22. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/pipeline/scan_cache.py +21 -0
  23. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/asset_metadata.py +7 -1
  24. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/base.py +85 -5
  25. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/custom/runner.py +4 -0
  26. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/custom/source.py +58 -6
  27. classifyre_cli-0.4.136/tests/test_augmentation_contract.py +82 -0
  28. classifyre_cli-0.4.136/tests/test_augmentation_edges.py +107 -0
  29. classifyre_cli-0.4.136/tests/test_augmentation_patch.py +166 -0
  30. classifyre_cli-0.4.136/tests/test_augmentation_scan_cache.py +60 -0
  31. classifyre_cli-0.4.136/tests/test_augmentation_sdk.py +144 -0
  32. classifyre_cli-0.4.136/tests/test_augmentation_session.py +264 -0
  33. classifyre_cli-0.4.136/tests/test_augmentation_tags_end_to_end.py +148 -0
  34. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_custom_source.py +72 -0
  35. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_lineage_loss_reporting.py +31 -2
  36. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_source_strategy_conformance.py +5 -0
  37. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/uv.lock +841 -891
  38. classifyre_cli-0.4.135/.turbo/turbo-build.log +0 -3
  39. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/.gitignore +0 -0
  40. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/.python-version +0 -0
  41. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/README.md +0 -0
  42. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/main.py +0 -0
  43. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/scripts/generate_models.py +0 -0
  44. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/scripts/generate_runtime_packages.py +0 -0
  45. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/__init__.py +0 -0
  46. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/config.py +0 -0
  47. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/__init__.py +0 -0
  48. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/base.py +0 -0
  49. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/broken_links/__init__.py +0 -0
  50. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/broken_links/detector.py +0 -0
  51. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/config.py +0 -0
  52. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/content/__init__.py +0 -0
  53. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/__init__.py +0 -0
  54. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/detector.py +0 -0
  55. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/extractor.py +0 -0
  56. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/__init__.py +0 -0
  57. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/_base.py +0 -0
  58. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/_factory.py +0 -0
  59. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/_gliner2.py +0 -0
  60. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/_image_classification.py +0 -0
  61. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/_llm.py +0 -0
  62. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/_object_detection.py +0 -0
  63. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/_regex.py +0 -0
  64. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/_tag.py +0 -0
  65. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/_text_classification.py +0 -0
  66. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/trainer.py +0 -0
  67. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/dependencies.py +0 -0
  68. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/engine_version.py +0 -0
  69. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/pii/__init__.py +0 -0
  70. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/pii/detector.py +0 -0
  71. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/secrets/__init__.py +0 -0
  72. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/secrets/detector.py +0 -0
  73. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/threat/__init__.py +0 -0
  74. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/threat/code_security_detector.py +0 -0
  75. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/threat/yara_detector.py +0 -0
  76. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/file_evaluation/__init__.py +0 -0
  77. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/file_evaluation/runner.py +0 -0
  78. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/graph/__init__.py +0 -0
  79. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/graph/edges.py +0 -0
  80. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/models/generated_detectors.py +0 -0
  81. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/models/generated_single_asset_scan_results.py +0 -0
  82. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/__init__.py +0 -0
  83. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/contract.py +0 -0
  84. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/files.py +0 -0
  85. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/groups.py +0 -0
  86. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/outputs.py +0 -0
  87. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/packages.py +0 -0
  88. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/scaffold.py +0 -0
  89. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/serialize.py +0 -0
  90. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/outputs/__init__.py +0 -0
  91. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/outputs/base.py +0 -0
  92. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/outputs/console.py +0 -0
  93. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/outputs/factory.py +0 -0
  94. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/outputs/file.py +0 -0
  95. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/pipeline/__init__.py +0 -0
  96. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/pipeline/content_provider.py +0 -0
  97. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/pipeline/detector_pipeline.py +0 -0
  98. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/pipeline/payload_window.py +0 -0
  99. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/pipeline/text_artifact.py +0 -0
  100. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/pipeline/worker_pool.py +0 -0
  101. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/__init__.py +0 -0
  102. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/atlassian_common.py +0 -0
  103. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/azure_blob_storage/__init__.py +0 -0
  104. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/azure_blob_storage/source.py +0 -0
  105. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/confluence/__init__.py +0 -0
  106. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/confluence/source.py +0 -0
  107. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/custom/__init__.py +0 -0
  108. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/custom/env.py +0 -0
  109. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/databricks/__init__.py +0 -0
  110. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/databricks/source.py +0 -0
  111. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/delta_lake/__init__.py +0 -0
  112. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/delta_lake/source.py +0 -0
  113. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/dependencies.py +0 -0
  114. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/dropbox/__init__.py +0 -0
  115. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/dropbox/auth.py +0 -0
  116. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/dropbox/source.py +0 -0
  117. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/elasticsearch/__init__.py +0 -0
  118. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/elasticsearch/source.py +0 -0
  119. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/email/__init__.py +0 -0
  120. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/email/source.py +0 -0
  121. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/git/__init__.py +0 -0
  122. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/git/source.py +0 -0
  123. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/google_cloud_storage/__init__.py +0 -0
  124. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/google_cloud_storage/source.py +0 -0
  125. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/google_workspace/__init__.py +0 -0
  126. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/google_workspace/source.py +0 -0
  127. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/hive/__init__.py +0 -0
  128. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/hive/source.py +0 -0
  129. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/hugging_face/__init__.py +0 -0
  130. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/hugging_face/source.py +0 -0
  131. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/iceberg/__init__.py +0 -0
  132. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/iceberg/source.py +0 -0
  133. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/jira/__init__.py +0 -0
  134. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/jira/source.py +0 -0
  135. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/kafka/__init__.py +0 -0
  136. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/kafka/rest.py +0 -0
  137. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/kafka/source.py +0 -0
  138. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/lakehouse_base.py +0 -0
  139. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/local_folder/__init__.py +0 -0
  140. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/local_folder/source.py +0 -0
  141. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/meilisearch/__init__.py +0 -0
  142. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/meilisearch/source.py +0 -0
  143. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/microsoft_365/__init__.py +0 -0
  144. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/microsoft_365/source.py +0 -0
  145. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/mongodb/__init__.py +0 -0
  146. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/mongodb/source.py +0 -0
  147. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/mssql/__init__.py +0 -0
  148. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/mssql/source.py +0 -0
  149. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/mysql/__init__.py +0 -0
  150. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/mysql/source.py +0 -0
  151. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/neo4j/__init__.py +0 -0
  152. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/neo4j/source.py +0 -0
  153. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/notion/__init__.py +0 -0
  154. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/notion/client.py +0 -0
  155. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/notion/source.py +0 -0
  156. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/object_storage/base.py +0 -0
  157. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/opensearch/__init__.py +0 -0
  158. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/opensearch/source.py +0 -0
  159. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/oracle/__init__.py +0 -0
  160. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/oracle/source.py +0 -0
  161. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/postgresql/__init__.py +0 -0
  162. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/postgresql/source.py +0 -0
  163. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/powerbi/__init__.py +0 -0
  164. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/powerbi/source.py +0 -0
  165. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/recipe_normalizer.py +0 -0
  166. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/reddit/__init__.py +0 -0
  167. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/reddit/source.py +0 -0
  168. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/s3_client.py +0 -0
  169. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/s3_compatible_storage/README.md +0 -0
  170. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/s3_compatible_storage/__init__.py +0 -0
  171. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/s3_compatible_storage/source.py +0 -0
  172. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/sandbox/__init__.py +0 -0
  173. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/sandbox/source.py +0 -0
  174. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/search_engine_base.py +0 -0
  175. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/servicedesk/__init__.py +0 -0
  176. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/servicedesk/source.py +0 -0
  177. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/slack/__init__.py +0 -0
  178. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/slack/source.py +0 -0
  179. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/snowflake/__init__.py +0 -0
  180. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/snowflake/source.py +0 -0
  181. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/sqlite/__init__.py +0 -0
  182. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/sqlite/source.py +0 -0
  183. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/tableau/__init__.py +0 -0
  184. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/tableau/source.py +0 -0
  185. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/tabular_base.py +0 -0
  186. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/tabular_utils.py +0 -0
  187. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/wordpress/__init__.py +0 -0
  188. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/wordpress/source.py +0 -0
  189. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/youtube/__init__.py +0 -0
  190. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/youtube/source.py +0 -0
  191. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/telemetry.py +0 -0
  192. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/__init__.py +0 -0
  193. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/archive_extraction.py +0 -0
  194. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/content_extraction.py +0 -0
  195. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/dependency_groups.py +0 -0
  196. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/embedded_files.py +0 -0
  197. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/external_video.py +0 -0
  198. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/file_metadata.py +0 -0
  199. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/file_parser.py +0 -0
  200. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/file_to_images.py +0 -0
  201. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/hashing.py +0 -0
  202. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/legacy_office.py +0 -0
  203. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/payload.py +0 -0
  204. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/range_reader.py +0 -0
  205. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/resources.py +0 -0
  206. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/source_files.py +0 -0
  207. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/sql_lineage.py +0 -0
  208. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/torch_runtime.py +0 -0
  209. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/transcription.py +0 -0
  210. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/urn.py +0 -0
  211. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/uv_sync.py +0 -0
  212. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/validation.py +0 -0
  213. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/video_processing.py +0 -0
  214. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/__init__.py +0 -0
  215. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/_lakehouse_fakes.py +0 -0
  216. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/conftest.py +0 -0
  217. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/__init__.py +0 -0
  218. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/broken_links/test_broken_links_detector.py +0 -0
  219. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/conftest.py +0 -0
  220. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/content/__init__.py +0 -0
  221. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/custom/__init__.py +0 -0
  222. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/custom/conftest.py +0 -0
  223. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/custom/test_gliner2_classification.py +0 -0
  224. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/custom/test_invoice_extraction.py +0 -0
  225. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/custom/test_llm_runner.py +0 -0
  226. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/custom/test_pipeline_integration.py +0 -0
  227. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/custom/test_regex_runner.py +0 -0
  228. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/custom/test_runner_hardening.py +0 -0
  229. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/custom/test_transformer_runners.py +0 -0
  230. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/pii/__init__.py +0 -0
  231. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/pii/conftest.py +0 -0
  232. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/pii/sample_invoice.pdf +0 -0
  233. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/pii/test_pii_chunking.py +0 -0
  234. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/pii/test_pii_detector.py +0 -0
  235. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/pii/test_pii_detector_extended.py +0 -0
  236. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/pii/test_pii_regional_entities.py +0 -0
  237. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/pii/test_pii_severity.py +0 -0
  238. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/secrets/__init__.py +0 -0
  239. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/secrets/test_secrets_detector.py +0 -0
  240. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/secrets/test_secrets_detector_extended.py +0 -0
  241. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/test_base_detector.py +0 -0
  242. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/test_custom_detector_examples_runtime.py +0 -0
  243. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/test_detector_catalog_commercial.py +0 -0
  244. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/test_detector_pipeline_types.py +0 -0
  245. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/test_detector_schema_examples.py +0 -0
  246. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/test_detector_types.py +0 -0
  247. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/test_phase2_detectors.py +0 -0
  248. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/test_registry.py +0 -0
  249. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/test_tag_runner.py +0 -0
  250. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/threat/__init__.py +0 -0
  251. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/threat/test_code_security_detector.py +0 -0
  252. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/threat/test_yara_detector.py +0 -0
  253. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/integration/test_wordpress_broken_links_detector.py +0 -0
  254. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/integration/test_wordpress_links_assets.py +0 -0
  255. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/pipeline/test_detector_outcomes.py +0 -0
  256. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/pipeline/test_detector_pipeline.py +0 -0
  257. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/pipeline/test_worker_pool.py +0 -0
  258. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_arrow_ipc_payloads.py +0 -0
  259. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_assets_metadata_catalog.py +0 -0
  260. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_azure_blob_storage_source.py +0 -0
  261. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_base_source_attachment.py +0 -0
  262. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_base_source_sampling.py +0 -0
  263. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_byte_valued_fields.py +0 -0
  264. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_config.py +0 -0
  265. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_confluence_source.py +0 -0
  266. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_custom_extractor.py +0 -0
  267. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_custom_source_relationships.py +0 -0
  268. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_custom_source_tags_end_to_end.py +0 -0
  269. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_databricks_source.py +0 -0
  270. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_delta_lake_source.py +0 -0
  271. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_dependency_groups.py +0 -0
  272. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_detector_scope.py +0 -0
  273. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_dropbox_auth_command.py +0 -0
  274. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_dropbox_source.py +0 -0
  275. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_elasticsearch_source.py +0 -0
  276. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_email_source.py +0 -0
  277. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_file_evaluation_runner.py +0 -0
  278. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_git_source.py +0 -0
  279. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_google_cloud_storage_source.py +0 -0
  280. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_google_workspace_source.py +0 -0
  281. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_graph_edges.py +0 -0
  282. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_hashing.py +0 -0
  283. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_hive_source.py +0 -0
  284. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_hugging_face_source.py +0 -0
  285. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_iceberg_source.py +0 -0
  286. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_jira_source.py +0 -0
  287. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_kafka_source.py +0 -0
  288. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_local_folder_source.py +0 -0
  289. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_main.py +0 -0
  290. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_meilisearch_source.py +0 -0
  291. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_microsoft_365_source.py +0 -0
  292. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_mongodb_source.py +0 -0
  293. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_mssql_source.py +0 -0
  294. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_mysql_source.py +0 -0
  295. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_neo4j_source.py +0 -0
  296. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_notebook_asset_tags.py +0 -0
  297. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_notebook_execute.py +0 -0
  298. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_notebook_files.py +0 -0
  299. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_notebook_groups.py +0 -0
  300. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_notebook_packages.py +0 -0
  301. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_notebook_serialize.py +0 -0
  302. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_notebook_templates.py +0 -0
  303. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_notion_source.py +0 -0
  304. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_object_storage_download_efficiency.py +0 -0
  305. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_opensearch_source.py +0 -0
  306. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_oracle_source.py +0 -0
  307. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_outputs.py +0 -0
  308. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_payload_window.py +0 -0
  309. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_payload_window_parquet.py +0 -0
  310. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_postgresql_source.py +0 -0
  311. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_powerbi_source.py +0 -0
  312. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_range_reading.py +0 -0
  313. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_recipe_normalizer.py +0 -0
  314. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_reddit_source.py +0 -0
  315. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_rest_backpressure.py +0 -0
  316. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_runtime_packages.py +0 -0
  317. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_s3_compatible_storage_source.py +0 -0
  318. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_sampling_automatic.py +0 -0
  319. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_sandbox_source.py +0 -0
  320. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_scan_cache.py +0 -0
  321. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_sdk_completions.py +0 -0
  322. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_servicedesk_source.py +0 -0
  323. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_slack_source.py +0 -0
  324. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_snowflake_source.py +0 -0
  325. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_source_dependency_groups.py +0 -0
  326. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_sqlite_source.py +0 -0
  327. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_tableau_source.py +0 -0
  328. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_tabular_automatic_sampling.py +0 -0
  329. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_tabular_lineage.py +0 -0
  330. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_tabular_utils.py +0 -0
  331. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_torch_runtime.py +0 -0
  332. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_urn.py +0 -0
  333. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_uv_sync.py +0 -0
  334. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_wordpress_source.py +0 -0
  335. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_youtube_source.py +0 -0
  336. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_youtube_source_integration.py +0 -0
  337. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_archive_extraction.py +0 -0
  338. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_content_extraction.py +0 -0
  339. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_embedded_files.py +0 -0
  340. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_file_metadata.py +0 -0
  341. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_file_parser.py +0 -0
  342. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_file_to_images.py +0 -0
  343. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_legacy_office.py +0 -0
  344. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_payload.py +0 -0
  345. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_transcription.py +0 -0
  346. {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_video_processing.py +0 -0
@@ -0,0 +1,3 @@
1
+ $ uv sync
2
+ Resolved 322 packages in 257ms
3
+ Checked 51 packages in 0.67ms
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: classifyre-cli
3
- Version: 0.4.135
3
+ Version: 0.4.136
4
4
  Summary: Classifyre CLI — scan and classify unstructured data sources
5
5
  License: MIT
6
6
  Keywords: data,ingestion,metadata,pii,secrets,unstructured
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classifyre/cli",
3
- "version": "0.4.135",
3
+ "version": "0.4.136",
4
4
  "private": true,
5
5
  "scripts": {
6
6
  "build": "uv sync",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "classifyre-cli"
3
- version = "0.4.135"
3
+ version = "0.4.136"
4
4
  description = "Classifyre CLI — scan and classify unstructured data sources"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -26,6 +26,8 @@ from typing import Any
26
26
  CLI_ROOT = Path(__file__).resolve().parents[1]
27
27
  sys.path.insert(0, str(CLI_ROOT))
28
28
 
29
+ from src.augmentation import contract as augmentation_contract # noqa: E402
30
+ from src.augmentation import sdk as augmentation_sdk # noqa: E402
29
31
  from src.notebook.contract import ( # noqa: E402
30
32
  OPTIONAL_FUNCTIONS,
31
33
  REQUIRED_FUNCTIONS,
@@ -126,7 +128,17 @@ def _snippet(name: str, func: Any) -> str:
126
128
  #: Public on Context because the runtime reads them, but not part of what a
127
129
  #: notebook author writes -- offering them would be noise at best and a
128
130
  #: misleading suggestion at worst.
129
- RUNTIME_ONLY_MEMBERS = frozenset({"set_offset", "offset_consumed", "next_cursor"})
131
+ RUNTIME_ONLY_MEMBERS = frozenset(
132
+ {
133
+ "set_offset",
134
+ "offset_consumed",
135
+ "next_cursor",
136
+ # The adapter reads these back off the run; the author only ever calls
137
+ # set_partial_coverage().
138
+ "partial_coverage",
139
+ "partial_coverage_reason",
140
+ }
141
+ )
130
142
 
131
143
 
132
144
  def _members(cls: type) -> list[dict[str, Any]]:
@@ -220,7 +232,8 @@ def build() -> dict[str, Any]:
220
232
  return {
221
233
  "$comment": (
222
234
  "Generated by apps/cli/scripts/generate_sdk_completions.py from "
223
- "apps/cli/src/notebook/sdk.py. Do not edit by hand."
235
+ "apps/cli/src/notebook/sdk.py and apps/cli/src/augmentation/sdk.py. "
236
+ "Do not edit by hand."
224
237
  ),
225
238
  "module": "classifyre",
226
239
  "globals": [
@@ -263,12 +276,26 @@ def build() -> dict[str, Any]:
263
276
  ],
264
277
  "objects": {
265
278
  "ctx": {"type": "Context", "members": _members(Context)},
279
+ "augmentation": {
280
+ "ctx": {
281
+ "type": "AugmentContext",
282
+ "members": _members(augmentation_sdk.AugmentContext),
283
+ },
284
+ "asset": {
285
+ "type": "AugmentedAsset",
286
+ "members": _members(augmentation_sdk.AugmentedAsset),
287
+ },
288
+ },
266
289
  },
267
290
  "classes": {
268
291
  "Asset": {
269
292
  "documentation": _summary(Asset),
270
293
  "fields": _fields(Asset),
271
294
  },
295
+ "AugmentedAsset": {
296
+ "documentation": _summary(augmentation_sdk.AugmentedAsset),
297
+ "members": _members(augmentation_sdk.AugmentedAsset),
298
+ },
272
299
  # Returned by `parse()` and reached as `ctx.file(...)`, so an author
273
300
  # who never writes their name still needs their members offered.
274
301
  "ParsedContent": {
@@ -288,6 +315,10 @@ def build() -> dict[str, Any]:
288
315
  "contract": {
289
316
  "required": list(REQUIRED_FUNCTIONS),
290
317
  "optional": list(OPTIONAL_FUNCTIONS),
318
+ "augmentation": {
319
+ "required": list(augmentation_contract.REQUIRED_FUNCTIONS),
320
+ "optional": list(augmentation_contract.OPTIONAL_FUNCTIONS),
321
+ },
291
322
  },
292
323
  }
293
324
 
@@ -0,0 +1,5 @@
1
+ """Source augmentation: per-asset enrichment for any known source."""
2
+
3
+ from .session import AugmentationSession
4
+
5
+ __all__ = ["AugmentationSession"]
@@ -0,0 +1,70 @@
1
+ """The interface an augmentation notebook must cover.
2
+
3
+ An augmentation is not a connector: it never answers "can you connect?" or
4
+ "what is there?", so the connector contract's ``test_connection``/``extract``
5
+ would be the wrong shape to check. What it must answer is "what do you add to
6
+ this asset?", which is ``augment(asset)`` — required — plus the two lifecycle
7
+ hooks ``setup()`` (once, before the first asset) and ``finalize()`` (once, at
8
+ the end, when both sides of a computed relationship are finally known).
9
+
10
+ The check itself is the connector one, parametrised: ``validate_module`` and
11
+ ``validate_notebook`` already take a ``required`` iterable rather than
12
+ hardcoding the connector names, so this module passes its own names instead of
13
+ copying the parser. Only the *shape* is checked here as well — isolation is a
14
+ process boundary and a scrubbed environment, not a blocklist.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from collections.abc import Iterable, Mapping
20
+
21
+ from ..notebook.contract import (
22
+ ContractReport,
23
+ NotebookContractError,
24
+ validate_module,
25
+ validate_notebook,
26
+ )
27
+ from ..notebook.serialize import ModuleSource
28
+
29
+ #: Without this the notebook cannot answer "what do you add to this asset?",
30
+ #: which is the whole of the augmentation contract.
31
+ REQUIRED_FUNCTIONS = ("augment",)
32
+
33
+ #: Implemented when the notebook needs them; absence is not an error. ``setup``
34
+ #: builds per-run state (lookup tables in ``ctx.state``) before the first
35
+ #: asset; ``finalize`` runs once at the end and may yield edges that needed
36
+ #: every asset to have been seen first.
37
+ OPTIONAL_FUNCTIONS = ("setup", "finalize")
38
+
39
+
40
+ def validate_augmentation_module(module: ModuleSource) -> ContractReport:
41
+ """Parse the assembled module and check ``augment()`` exists."""
42
+ return validate_module(module, required=REQUIRED_FUNCTIONS)
43
+
44
+
45
+ def validate_augmentation_notebook(
46
+ cells: Iterable[Mapping[str, object] | object],
47
+ *,
48
+ required: Iterable[str] = REQUIRED_FUNCTIONS,
49
+ ) -> ContractReport:
50
+ """Validate a notebook's cells against the augmentation contract."""
51
+ return validate_notebook(cells, required=required)
52
+
53
+
54
+ def defined_functions(cells: Iterable[Mapping[str, object] | object]) -> frozenset[str]:
55
+ """Names the notebook defines, without failing a notebook that parses badly."""
56
+ try:
57
+ return validate_notebook(list(cells), required=()).defined_functions
58
+ except Exception:
59
+ # A notebook that will not parse has already failed louder elsewhere.
60
+ return frozenset()
61
+
62
+
63
+ __all__ = [
64
+ "OPTIONAL_FUNCTIONS",
65
+ "REQUIRED_FUNCTIONS",
66
+ "NotebookContractError",
67
+ "defined_functions",
68
+ "validate_augmentation_module",
69
+ "validate_augmentation_notebook",
70
+ ]
@@ -0,0 +1,280 @@
1
+ """Preview augmentation against real assets: ``preview_augment``.
2
+
3
+ Unlike ``preview_extract`` — which replays cells in-process — augmentation
4
+ needs the **real connector**: the assets it enriches only exist after
5
+ extraction. So this flow builds the source from the recipe with sampling
6
+ forced to RANDOM and a cap of N (default 5, max 25), takes the first N assets
7
+ from ``extract_raw()``, and runs ``setup()`` / ``augment(asset)`` /
8
+ ``finalize()`` over them.
9
+
10
+ No sink, no run record, no ingestion — read-only. The answer is an
11
+ ``ExecutionResponse`` whose ``assets`` carry per-asset **diffs**: metadata
12
+ added, tags asserted (and whether each key matches a live Tag detector), links
13
+ added, URN change, edges yielded, and warnings.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import copy
19
+ import logging
20
+ import time
21
+ from typing import Any
22
+
23
+ from ..notebook.protocol import (
24
+ ExecutionError,
25
+ ExecutionMode,
26
+ ExecutionResponse,
27
+ ExecutionStatus,
28
+ )
29
+ from ..notebook.redact import Redactor
30
+ from .contract import validate_augmentation_notebook
31
+ from .session import AugmentationSession, augmentation_config_of
32
+
33
+ logger = logging.getLogger(__name__)
34
+
35
+ DEFAULT_PREVIEW_ASSETS = 5
36
+ MAX_PREVIEW_ASSETS = 25
37
+
38
+
39
+ def _tag_keys_from_recipe(recipe: dict[str, Any], source: Any) -> set[str]:
40
+ """Keys a live Tag detector would resolve, from this run's recipe.
41
+
42
+ The API hydrates runtime TAG detectors into ``recipe.detectors`` before
43
+ dispatch (see cli-runner), so the dispatched recipe is authoritative. A
44
+ detector entry names its tag key via ``custom_detector_key`` (top level or
45
+ under ``config``); a bare ``TAG`` pipeline entry carries ``key``.
46
+ """
47
+ keys: set[str] = set()
48
+ detectors = recipe.get("detectors")
49
+ if not isinstance(detectors, list):
50
+ return keys
51
+ for entry in detectors:
52
+ if not isinstance(entry, dict):
53
+ continue
54
+ if str(entry.get("type") or "").strip().upper() not in {"TAG", "CUSTOM"}:
55
+ continue
56
+ for holder in (entry, entry.get("config")):
57
+ if not isinstance(holder, dict):
58
+ continue
59
+ for field in ("custom_detector_key", "key"):
60
+ value = holder.get(field)
61
+ if isinstance(value, str) and value.strip():
62
+ keys.add(value.strip())
63
+ if not keys:
64
+ # Fall back to the detectors the pipeline would actually build: exact,
65
+ # at the cost of constructing them.
66
+ try:
67
+ from ..pipeline.detector_pipeline import DetectorPipeline
68
+
69
+ pipeline = DetectorPipeline.from_recipe(recipe, source, "preview")
70
+ runners = DetectorPipeline._tag_runners(pipeline.detectors)
71
+ keys.update(runners)
72
+ except Exception as exc:
73
+ logger.debug("Could not resolve live Tag detector keys: %s", exc)
74
+ return keys
75
+
76
+
77
+ async def preview_augment(
78
+ recipe: dict[str, Any],
79
+ *,
80
+ max_assets: int = DEFAULT_PREVIEW_ASSETS,
81
+ execution_id: str | None = None,
82
+ revision: int | None = None,
83
+ ) -> ExecutionResponse:
84
+ """Run augmentation over a bounded sample of real assets and diff each one."""
85
+ started = time.monotonic()
86
+ mode = ExecutionMode.PREVIEW_AUGMENT
87
+ redactor = Redactor.from_recipe(recipe)
88
+
89
+ section = augmentation_config_of(recipe)
90
+ cells = (
91
+ section.get("notebook", {}).get("cells")
92
+ if isinstance(section.get("notebook"), dict)
93
+ else None
94
+ )
95
+ if not section.get("enabled", False) or not cells:
96
+ return ExecutionResponse(
97
+ status=ExecutionStatus.ERROR,
98
+ mode=mode,
99
+ execution_id=execution_id,
100
+ revision=revision,
101
+ error=_error(
102
+ "AugmentationNotEnabled",
103
+ "This source has no enabled augmentation notebook to preview.",
104
+ ),
105
+ )
106
+
107
+ report = validate_augmentation_notebook(cells)
108
+ if not report.ok:
109
+ violation = report.violations[0]
110
+ return ExecutionResponse(
111
+ status=ExecutionStatus.ERROR,
112
+ mode=mode,
113
+ execution_id=execution_id,
114
+ revision=revision,
115
+ contract=report.to_dict(),
116
+ error=_error("NotebookContractError", violation.message),
117
+ )
118
+
119
+ count = max(1, min(MAX_PREVIEW_ASSETS, int(max_assets or DEFAULT_PREVIEW_ASSETS)))
120
+
121
+ # The real connector, sampled: RANDOM with a cap, so the preview shows real
122
+ # assets without ingesting anything. No sink is ever built.
123
+ sampled = copy.deepcopy(recipe)
124
+ sampled["sampling"] = {"strategy": "RANDOM", "rows_per_page": count}
125
+
126
+ from ..sources import get_source
127
+
128
+ try:
129
+ source = get_source(sampled)
130
+ except Exception as exc:
131
+ return ExecutionResponse(
132
+ status=ExecutionStatus.ERROR,
133
+ mode=mode,
134
+ execution_id=execution_id,
135
+ revision=revision,
136
+ error=_error("SourceBuildFailed", f"Could not build the source: {exc}"),
137
+ )
138
+
139
+ session = AugmentationSession.from_recipe(recipe, source)
140
+ if session is None: # pragma: no cover - guarded above, kept for safety
141
+ return ExecutionResponse(
142
+ status=ExecutionStatus.ERROR,
143
+ mode=mode,
144
+ execution_id=execution_id,
145
+ revision=revision,
146
+ error=_error("AugmentationNotEnabled", "Augmentation is not enabled."),
147
+ )
148
+ source.attach_augmentation(session)
149
+
150
+ samples: list[dict[str, Any]] = []
151
+ tag_keys = _tag_keys_from_recipe(recipe, source)
152
+ try:
153
+ await session.startup()
154
+ if not session.enabled:
155
+ return ExecutionResponse(
156
+ status=ExecutionStatus.ERROR,
157
+ mode=mode,
158
+ execution_id=execution_id,
159
+ revision=revision,
160
+ error=_error(
161
+ "AugmentationSetupFailed",
162
+ session.warnings[-1] if session.warnings else "setup() failed",
163
+ ),
164
+ )
165
+
166
+ seen = 0
167
+ async for batch in source.extract_raw():
168
+ for asset in batch:
169
+ if seen >= count:
170
+ break
171
+ samples.append(await _preview_one(session, source, asset, tag_keys, redactor))
172
+ seen += 1
173
+ if seen >= count:
174
+ break
175
+
176
+ finalize_edges = await _finalize_edges(session, source)
177
+ response = ExecutionResponse(
178
+ status=ExecutionStatus.SUCCESS,
179
+ mode=mode,
180
+ execution_id=execution_id,
181
+ revision=revision or session.revision,
182
+ assets=samples,
183
+ result={
184
+ "sampled": len(samples),
185
+ "finalizeEdges": finalize_edges,
186
+ "session": session.summary(),
187
+ },
188
+ )
189
+ except Exception as exc:
190
+ logger.warning("preview_augment failed: %s", exc)
191
+ response = ExecutionResponse(
192
+ status=ExecutionStatus.ERROR,
193
+ mode=mode,
194
+ execution_id=execution_id,
195
+ revision=revision,
196
+ assets=samples,
197
+ error=_error(type(exc).__name__, str(exc)),
198
+ )
199
+ finally:
200
+ session.close()
201
+ try:
202
+ source.cleanup()
203
+ except Exception:
204
+ pass
205
+
206
+ response.duration_ms = int((time.monotonic() - started) * 1000)
207
+ return response
208
+
209
+
210
+ async def _preview_one(
211
+ session: AugmentationSession,
212
+ source: Any,
213
+ asset: Any,
214
+ tag_keys: set[str],
215
+ redactor: Redactor,
216
+ ) -> dict[str, Any]:
217
+ asset_hash = str(getattr(asset, "hash", "") or "")
218
+ before_links = list(getattr(asset, "links", None) or [])
219
+ before_urn = getattr(asset, "urn", None)
220
+ try:
221
+ before_tags = dict(source.asset_tags(asset_hash))
222
+ except Exception:
223
+ before_tags = {}
224
+ warnings_before = len(session.warnings)
225
+ edges_before = session.stats.edges
226
+
227
+ await session.augment(asset)
228
+
229
+ try:
230
+ after_tags = dict(source.asset_tags(asset_hash))
231
+ except Exception:
232
+ after_tags = {}
233
+ captured_tags = {key: value for key, value in after_tags.items() if key not in before_tags}
234
+
235
+ after = getattr(asset, "metadata", None) or {}
236
+ added_metadata = after.get("augmentation") if isinstance(after, dict) else {}
237
+ if not isinstance(added_metadata, dict):
238
+ added_metadata = {}
239
+ after_links = list(getattr(asset, "links", None) or [])
240
+
241
+ try:
242
+ drained = source.drain_edges()
243
+ except Exception:
244
+ drained = []
245
+
246
+ redacted: dict[str, Any] = redactor.redact_deep(
247
+ {
248
+ "hash": str(getattr(asset, "hash", "") or ""),
249
+ "name": str(getattr(asset, "name", "") or ""),
250
+ "kind": str(getattr(asset, "asset_kind", "") or ""),
251
+ "metadataAdded": dict(added_metadata),
252
+ "tags": {
253
+ key: {"value": value, "matchesTagDetector": key in tag_keys}
254
+ for key, value in captured_tags.items()
255
+ },
256
+ "linksAdded": [link for link in after_links if link not in before_links],
257
+ "urn": {"before": before_urn, "after": getattr(asset, "urn", None)},
258
+ "edges": len(drained),
259
+ "warnings": session.warnings[warnings_before:],
260
+ "augmentedEdges": session.stats.edges - edges_before,
261
+ }
262
+ )
263
+ return redacted
264
+
265
+
266
+ async def _finalize_edges(session: AugmentationSession, source: Any) -> int:
267
+ before = session.stats.edges
268
+ await session.finish()
269
+ try:
270
+ leftover = source.drain_edges()
271
+ except Exception:
272
+ leftover = []
273
+ return (session.stats.edges - before) + len(leftover or [])
274
+
275
+
276
+ def _error(type_: str, message: str) -> ExecutionError:
277
+ return ExecutionError(type=type_, message=message)
278
+
279
+
280
+ __all__ = ["DEFAULT_PREVIEW_ASSETS", "MAX_PREVIEW_ASSETS", "preview_augment"]