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.
- classifyre_cli-0.4.136/.turbo/turbo-build.log +3 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/PKG-INFO +1 -1
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/package.json +1 -1
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/pyproject.toml +1 -1
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/scripts/generate_sdk_completions.py +33 -2
- classifyre_cli-0.4.136/src/augmentation/__init__.py +5 -0
- classifyre_cli-0.4.136/src/augmentation/contract.py +70 -0
- classifyre_cli-0.4.136/src/augmentation/preview.py +280 -0
- classifyre_cli-0.4.136/src/augmentation/runner.py +323 -0
- classifyre_cli-0.4.136/src/augmentation/sdk.py +489 -0
- classifyre_cli-0.4.136/src/augmentation/session.py +730 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/main.py +143 -4
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/models/generated_input.py +2116 -1966
- classifyre_cli-0.4.136/src/notebook/child.py +228 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/cli.py +113 -7
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/execute.py +26 -6
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/protocol.py +31 -1
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/redact.py +26 -2
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/sdk.py +39 -1
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/outputs/rest.py +31 -5
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/pipeline/parsed_content_provider.py +11 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/pipeline/scan_cache.py +21 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/asset_metadata.py +7 -1
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/base.py +85 -5
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/custom/runner.py +4 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/custom/source.py +58 -6
- classifyre_cli-0.4.136/tests/test_augmentation_contract.py +82 -0
- classifyre_cli-0.4.136/tests/test_augmentation_edges.py +107 -0
- classifyre_cli-0.4.136/tests/test_augmentation_patch.py +166 -0
- classifyre_cli-0.4.136/tests/test_augmentation_scan_cache.py +60 -0
- classifyre_cli-0.4.136/tests/test_augmentation_sdk.py +144 -0
- classifyre_cli-0.4.136/tests/test_augmentation_session.py +264 -0
- classifyre_cli-0.4.136/tests/test_augmentation_tags_end_to_end.py +148 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_custom_source.py +72 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_lineage_loss_reporting.py +31 -2
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_source_strategy_conformance.py +5 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/uv.lock +841 -891
- classifyre_cli-0.4.135/.turbo/turbo-build.log +0 -3
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/.gitignore +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/.python-version +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/README.md +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/main.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/scripts/generate_models.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/scripts/generate_runtime_packages.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/config.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/base.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/broken_links/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/broken_links/detector.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/config.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/detector.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/extractor.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/_base.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/_factory.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/_gliner2.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/_image_classification.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/_llm.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/_object_detection.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/_regex.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/_tag.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/runners/_text_classification.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/custom/trainer.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/dependencies.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/engine_version.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/pii/detector.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/secrets/detector.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/threat/code_security_detector.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/detectors/threat/yara_detector.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/file_evaluation/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/file_evaluation/runner.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/graph/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/graph/edges.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/models/generated_detectors.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/models/generated_single_asset_scan_results.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/contract.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/files.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/groups.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/outputs.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/packages.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/scaffold.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/notebook/serialize.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/outputs/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/outputs/base.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/outputs/console.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/outputs/factory.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/outputs/file.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/pipeline/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/pipeline/content_provider.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/pipeline/detector_pipeline.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/pipeline/payload_window.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/pipeline/text_artifact.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/pipeline/worker_pool.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/atlassian_common.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/azure_blob_storage/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/azure_blob_storage/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/confluence/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/confluence/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/custom/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/custom/env.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/databricks/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/databricks/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/delta_lake/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/delta_lake/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/dependencies.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/dropbox/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/dropbox/auth.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/dropbox/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/elasticsearch/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/elasticsearch/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/email/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/email/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/git/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/git/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/google_cloud_storage/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/google_cloud_storage/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/google_workspace/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/google_workspace/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/hive/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/hive/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/hugging_face/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/hugging_face/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/iceberg/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/iceberg/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/jira/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/jira/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/kafka/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/kafka/rest.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/kafka/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/lakehouse_base.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/local_folder/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/local_folder/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/meilisearch/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/meilisearch/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/microsoft_365/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/microsoft_365/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/mongodb/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/mongodb/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/mssql/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/mssql/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/mysql/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/mysql/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/neo4j/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/neo4j/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/notion/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/notion/client.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/notion/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/object_storage/base.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/opensearch/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/opensearch/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/oracle/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/oracle/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/postgresql/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/postgresql/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/powerbi/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/powerbi/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/recipe_normalizer.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/reddit/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/reddit/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/s3_client.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/s3_compatible_storage/README.md +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/s3_compatible_storage/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/s3_compatible_storage/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/sandbox/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/sandbox/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/search_engine_base.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/servicedesk/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/servicedesk/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/slack/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/slack/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/snowflake/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/snowflake/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/sqlite/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/sqlite/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/tableau/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/tableau/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/tabular_base.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/tabular_utils.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/wordpress/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/wordpress/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/youtube/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/sources/youtube/source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/telemetry.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/archive_extraction.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/content_extraction.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/dependency_groups.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/embedded_files.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/external_video.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/file_metadata.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/file_parser.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/file_to_images.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/hashing.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/legacy_office.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/payload.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/range_reader.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/resources.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/source_files.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/sql_lineage.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/torch_runtime.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/transcription.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/urn.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/uv_sync.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/validation.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/src/utils/video_processing.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/_lakehouse_fakes.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/conftest.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/broken_links/test_broken_links_detector.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/conftest.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/custom/conftest.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/custom/test_gliner2_classification.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/custom/test_invoice_extraction.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/custom/test_llm_runner.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/custom/test_pipeline_integration.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/custom/test_regex_runner.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/custom/test_runner_hardening.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/custom/test_transformer_runners.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/pii/conftest.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/pii/sample_invoice.pdf +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/pii/test_pii_chunking.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/pii/test_pii_detector.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/pii/test_pii_detector_extended.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/pii/test_pii_regional_entities.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/pii/test_pii_severity.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/secrets/test_secrets_detector.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/secrets/test_secrets_detector_extended.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/test_base_detector.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/test_custom_detector_examples_runtime.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/test_detector_catalog_commercial.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/test_detector_pipeline_types.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/test_detector_schema_examples.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/test_detector_types.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/test_phase2_detectors.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/test_registry.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/test_tag_runner.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/threat/test_code_security_detector.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/detectors/threat/test_yara_detector.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/integration/test_wordpress_broken_links_detector.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/integration/test_wordpress_links_assets.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/pipeline/test_detector_outcomes.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/pipeline/test_detector_pipeline.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/pipeline/test_worker_pool.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_arrow_ipc_payloads.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_assets_metadata_catalog.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_azure_blob_storage_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_base_source_attachment.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_base_source_sampling.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_byte_valued_fields.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_config.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_confluence_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_custom_extractor.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_custom_source_relationships.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_custom_source_tags_end_to_end.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_databricks_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_delta_lake_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_dependency_groups.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_detector_scope.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_dropbox_auth_command.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_dropbox_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_elasticsearch_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_email_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_file_evaluation_runner.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_git_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_google_cloud_storage_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_google_workspace_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_graph_edges.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_hashing.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_hive_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_hugging_face_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_iceberg_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_jira_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_kafka_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_local_folder_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_main.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_meilisearch_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_microsoft_365_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_mongodb_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_mssql_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_mysql_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_neo4j_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_notebook_asset_tags.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_notebook_execute.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_notebook_files.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_notebook_groups.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_notebook_packages.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_notebook_serialize.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_notebook_templates.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_notion_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_object_storage_download_efficiency.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_opensearch_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_oracle_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_outputs.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_payload_window.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_payload_window_parquet.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_postgresql_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_powerbi_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_range_reading.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_recipe_normalizer.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_reddit_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_rest_backpressure.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_runtime_packages.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_s3_compatible_storage_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_sampling_automatic.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_sandbox_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_scan_cache.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_sdk_completions.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_servicedesk_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_slack_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_snowflake_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_source_dependency_groups.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_sqlite_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_tableau_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_tabular_automatic_sampling.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_tabular_lineage.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_tabular_utils.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_torch_runtime.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_urn.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_uv_sync.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_wordpress_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_youtube_source.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/test_youtube_source_integration.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_archive_extraction.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_content_extraction.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_embedded_files.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_file_metadata.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_file_parser.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_file_to_images.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_legacy_office.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_payload.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_transcription.py +0 -0
- {classifyre_cli-0.4.135 → classifyre_cli-0.4.136}/tests/utils/test_video_processing.py +0 -0
|
@@ -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(
|
|
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
|
|
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,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"]
|