classifyre-cli 0.4.52__tar.gz → 0.4.54__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.54/.turbo/turbo-build.log +3 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/PKG-INFO +1 -1
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/package.json +1 -1
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/pyproject.toml +13 -1
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/custom/runners/_base.py +15 -9
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/custom/runners/_gliner2.py +81 -15
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/pii/detector.py +38 -4
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/models/generated_detectors.py +4 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/models/generated_input.py +0 -12
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/models/generated_single_asset_scan_results.py +65 -25
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/pipeline/detector_pipeline.py +116 -17
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/base.py +0 -16
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/object_storage/base.py +5 -10
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/youtube/source.py +131 -75
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/utils/dependency_groups.py +0 -7
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/utils/file_parser.py +59 -41
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/utils/transcription.py +161 -58
- classifyre_cli-0.4.54/src/utils/video_processing.py +299 -0
- classifyre_cli-0.4.54/tests/detectors/custom/test_gliner2_classification.py +247 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/custom/test_invoice_extraction.py +3 -1
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/custom/test_pipeline_integration.py +8 -4
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/custom/test_runner_hardening.py +12 -9
- classifyre_cli-0.4.54/tests/detectors/pii/test_pii_chunking.py +121 -0
- classifyre_cli-0.4.54/tests/detectors/pii/test_pii_severity.py +96 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/test_custom_detector_examples_runtime.py +3 -3
- classifyre_cli-0.4.54/tests/pipeline/test_detector_outcomes.py +353 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/pipeline/test_detector_pipeline.py +12 -2
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_dependency_groups.py +7 -13
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_jira_source.py +7 -7
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_s3_compatible_storage_source.py +2 -10
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_youtube_source.py +69 -13
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/utils/test_file_parser.py +39 -64
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/utils/test_transcription.py +26 -1
- classifyre_cli-0.4.54/tests/utils/test_video_processing.py +141 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/uv.lock +172 -176
- classifyre_cli-0.4.52/.turbo/turbo-build.log +0 -3
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/.gitignore +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/.python-version +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/README.md +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/main.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/scripts/generate_models.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/config.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/base.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/broken_links/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/broken_links/detector.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/config.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/custom/detector.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/custom/extractor.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/custom/runners/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/custom/runners/_factory.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/custom/runners/_feature_extraction.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/custom/runners/_image_classification.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/custom/runners/_llm.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/custom/runners/_object_detection.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/custom/runners/_regex.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/custom/runners/_text_classification.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/custom/trainer.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/dependencies.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/secrets/detector.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/threat/code_security_detector.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/detectors/threat/yara_detector.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/main.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/outputs/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/outputs/base.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/outputs/console.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/outputs/factory.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/outputs/file.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/outputs/rest.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/pipeline/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/pipeline/content_provider.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/pipeline/parsed_content_provider.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/pipeline/worker_pool.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sandbox/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sandbox/runner.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/asset_metadata.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/atlassian_common.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/azure_blob_storage/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/azure_blob_storage/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/confluence/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/confluence/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/databricks/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/databricks/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/delta_lake/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/delta_lake/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/dependencies.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/elasticsearch/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/elasticsearch/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/email/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/email/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/google_cloud_storage/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/google_cloud_storage/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/google_workspace/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/google_workspace/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/hive/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/hive/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/iceberg/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/iceberg/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/jira/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/jira/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/kafka/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/kafka/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/lakehouse_base.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/local_folder/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/local_folder/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/meilisearch/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/meilisearch/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/microsoft_365/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/microsoft_365/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/mongodb/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/mongodb/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/mssql/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/mssql/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/mysql/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/mysql/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/neo4j/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/neo4j/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/notion/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/notion/client.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/notion/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/opensearch/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/opensearch/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/oracle/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/oracle/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/postgresql/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/postgresql/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/powerbi/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/powerbi/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/recipe_normalizer.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/s3_client.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/s3_compatible_storage/README.md +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/s3_compatible_storage/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/s3_compatible_storage/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/search_engine_base.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/servicedesk/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/servicedesk/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/slack/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/slack/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/snowflake/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/snowflake/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/sqlite/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/sqlite/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/tableau/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/tableau/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/tabular_base.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/tabular_utils.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/wordpress/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/wordpress/source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/sources/youtube/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/telemetry.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/utils/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/utils/content_extraction.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/utils/embedded_images.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/utils/file_metadata.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/utils/file_to_images.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/utils/hashing.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/utils/resources.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/utils/uv_sync.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/utils/validation.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/_lakehouse_fakes.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/conftest.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/broken_links/test_broken_links_detector.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/conftest.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/custom/conftest.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/custom/test_llm_runner.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/custom/test_regex_runner.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/custom/test_transformer_runners.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/pii/conftest.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/pii/sample_invoice.pdf +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/pii/test_pii_detector.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/pii/test_pii_detector_extended.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/secrets/test_secrets_detector.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/secrets/test_secrets_detector_extended.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/test_base_detector.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/test_detector_catalog_commercial.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/test_detector_pipeline_types.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/test_detector_schema_examples.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/test_detector_types.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/test_phase2_detectors.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/test_registry.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/threat/test_code_security_detector.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/detectors/threat/test_yara_detector.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/integration/test_wordpress_broken_links_detector.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/integration/test_wordpress_links_assets.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/pipeline/test_worker_pool.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_assets_metadata_catalog.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_azure_blob_storage_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_base_source_attachment.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_base_source_sampling.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_config.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_confluence_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_custom_extractor.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_databricks_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_delta_lake_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_elasticsearch_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_email_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_google_cloud_storage_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_google_workspace_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_hashing.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_hive_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_iceberg_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_kafka_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_local_folder_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_meilisearch_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_microsoft_365_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_mongodb_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_mssql_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_mysql_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_neo4j_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_notion_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_opensearch_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_oracle_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_outputs.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_postgresql_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_powerbi_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_recipe_normalizer.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_sampling_automatic.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_sandbox_runner.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_servicedesk_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_slack_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_snowflake_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_source_dependency_groups.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_source_strategy_conformance.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_sqlite_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_tableau_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_tabular_automatic_sampling.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_tabular_utils.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_uv_sync.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_wordpress_source.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/test_youtube_source_integration.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/utils/test_content_extraction.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/utils/test_embedded_images.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/utils/test_file_metadata.py +0 -0
- {classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/tests/utils/test_file_to_images.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "classifyre-cli"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.54"
|
|
4
4
|
description = "Classifyre CLI — scan and classify unstructured data sources"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -128,6 +128,7 @@ detectors = [
|
|
|
128
128
|
{ include-group = "regex" },
|
|
129
129
|
{ include-group = "llm" },
|
|
130
130
|
{ include-group = "transcription" },
|
|
131
|
+
{ include-group = "video" },
|
|
131
132
|
]
|
|
132
133
|
file-processing = [
|
|
133
134
|
"filetype>=1.2.0",
|
|
@@ -145,9 +146,16 @@ transcription = [
|
|
|
145
146
|
# PyAV (no system ffmpeg required) and runs ctranslate2 for inference.
|
|
146
147
|
"faster-whisper>=1.1.0",
|
|
147
148
|
]
|
|
149
|
+
video = [
|
|
150
|
+
# Use the server-safe OpenCV 5 wheel. RapidOCR's desktop OpenCV dependency
|
|
151
|
+
# is excluded below because both distributions install the same cv2 module.
|
|
152
|
+
"opencv-python-headless>=5.0.0.93,<6.0.0",
|
|
153
|
+
"rapidocr-onnxruntime>=1.4.0",
|
|
154
|
+
]
|
|
148
155
|
ocr = [
|
|
149
156
|
{ include-group = "file-processing" },
|
|
150
157
|
"docling>=2.94.0",
|
|
158
|
+
"opencv-python-headless>=5.0.0.93,<6.0.0",
|
|
151
159
|
"rapidocr-onnxruntime>=1.4.0",
|
|
152
160
|
# torchvision is a transitive dep of docling-ibm-models, but [tool.uv.sources]
|
|
153
161
|
# index pins only apply to dependencies declared directly in this project.
|
|
@@ -407,6 +415,10 @@ markers = [
|
|
|
407
415
|
|
|
408
416
|
[tool.uv]
|
|
409
417
|
package = true
|
|
418
|
+
# RapidOCR is the sole requester of opencv-python and works with the headless
|
|
419
|
+
# distribution declared by our video and OCR groups. Installing both would
|
|
420
|
+
# overwrite the same cv2 namespace and add GUI libraries to CPU-only containers.
|
|
421
|
+
exclude-dependencies = ["opencv-python"]
|
|
410
422
|
# Every platform we ship (desktop bundles + CI) must resolve to versions with
|
|
411
423
|
# usable wheels. Without this, uv happily locks versions that are missing a
|
|
412
424
|
# platform — e.g. deltalake 1.6.1 published no win_amd64 wheel, which broke the
|
|
@@ -47,10 +47,10 @@ _IMAGE_INPUT_CONTENT_TYPES = [*_IMAGE_CONTENT_TYPES, "application/pdf"]
|
|
|
47
47
|
|
|
48
48
|
logger = logging.getLogger(__name__)
|
|
49
49
|
|
|
50
|
-
# Cap per-label entity spans
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
50
|
+
# Cap per-label entity spans carried through the dedicated extracted_data field.
|
|
51
|
+
# The API moves this into CustomDetectorExtraction and does not persist it in
|
|
52
|
+
# Finding.metadata. It is still repeated in the wire payload until extraction
|
|
53
|
+
# becomes page-scoped, so the cap prevents quadratic request growth.
|
|
54
54
|
_MAX_EMBEDDED_SPANS_PER_LABEL = 25
|
|
55
55
|
|
|
56
56
|
|
|
@@ -139,6 +139,7 @@ class BaseRunner(ABC):
|
|
|
139
139
|
matched_content: str,
|
|
140
140
|
location: Location | None,
|
|
141
141
|
metadata: dict[str, Any],
|
|
142
|
+
extracted_data: dict[str, Any] | None = None,
|
|
142
143
|
) -> DetectionResult:
|
|
143
144
|
return DetectionResult(
|
|
144
145
|
detector_type=DetectorType.CUSTOM,
|
|
@@ -152,14 +153,19 @@ class BaseRunner(ABC):
|
|
|
152
153
|
custom_detector_name=self._detector_name,
|
|
153
154
|
detected_at=datetime.now(UTC),
|
|
154
155
|
metadata=metadata,
|
|
156
|
+
extracted_data=extracted_data,
|
|
155
157
|
)
|
|
156
158
|
|
|
157
159
|
def _result_to_findings(self, text: str, result: PipelineResult) -> list[DetectionResult]:
|
|
158
160
|
findings: list[DetectionResult] = []
|
|
159
|
-
|
|
161
|
+
# Every field on PipelineResult is optional, and runners return a bare
|
|
162
|
+
# PipelineResult() when a model fails to load — so none of these may be
|
|
163
|
+
# dereferenced directly. Doing so turned a handled model-load failure
|
|
164
|
+
# into an AttributeError from deep inside finding construction.
|
|
165
|
+
runner_type = (result.metadata or {}).get("runner", "GLINER2")
|
|
160
166
|
pipeline_result_dump = _slim_pipeline_result(result)
|
|
161
167
|
|
|
162
|
-
for label, spans in result.entities.items():
|
|
168
|
+
for label, spans in (result.entities or {}).items():
|
|
163
169
|
for span in spans:
|
|
164
170
|
confidence = float(span.get("confidence", 0.0))
|
|
165
171
|
value = str(span.get("value", ""))
|
|
@@ -181,7 +187,6 @@ class BaseRunner(ABC):
|
|
|
181
187
|
meta: dict[str, Any] = {
|
|
182
188
|
"runner": runner_type,
|
|
183
189
|
"entity_label": label,
|
|
184
|
-
"pipeline_result": pipeline_result_dump,
|
|
185
190
|
}
|
|
186
191
|
if "groups" in span:
|
|
187
192
|
meta["capture_groups"] = span["groups"]
|
|
@@ -195,10 +200,11 @@ class BaseRunner(ABC):
|
|
|
195
200
|
matched_content=value,
|
|
196
201
|
location=loc,
|
|
197
202
|
metadata=meta,
|
|
203
|
+
extracted_data=pipeline_result_dump,
|
|
198
204
|
)
|
|
199
205
|
)
|
|
200
206
|
|
|
201
|
-
for task, outcome in result.classification.items():
|
|
207
|
+
for task, outcome in (result.classification or {}).items():
|
|
202
208
|
label = str(outcome.get("label", ""))
|
|
203
209
|
confidence = float(outcome.get("confidence", 0.0))
|
|
204
210
|
if not label:
|
|
@@ -216,8 +222,8 @@ class BaseRunner(ABC):
|
|
|
216
222
|
"runner": runner_type,
|
|
217
223
|
"task": task,
|
|
218
224
|
"label": label,
|
|
219
|
-
"pipeline_result": pipeline_result_dump,
|
|
220
225
|
},
|
|
226
|
+
extracted_data=pipeline_result_dump,
|
|
221
227
|
)
|
|
222
228
|
)
|
|
223
229
|
|
|
@@ -80,23 +80,41 @@ class GLiNER2Runner(BaseRunner):
|
|
|
80
80
|
raw_entities: dict[str, list[dict[str, object]]] = {}
|
|
81
81
|
raw_classification: dict[str, dict[str, object]] = {}
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
# Entity extraction and each classification task are isolated. They used
|
|
84
|
+
# to share one try/except, so a classification failure discarded the
|
|
85
|
+
# entities already extracted in the same run and returned an empty
|
|
86
|
+
# result — the whole detector went silent because of one broken task.
|
|
87
|
+
if entity_schema:
|
|
88
|
+
try:
|
|
85
89
|
raw = self._extract_entities(model, text, entity_schema)
|
|
86
90
|
raw_entities = _normalise_entity_output(raw, text)
|
|
91
|
+
except Exception as exc: # pragma: no cover - runtime specific
|
|
92
|
+
logger.error(
|
|
93
|
+
"GLiNER2 entity extraction failed for detector '%s': %s",
|
|
94
|
+
self._detector_key,
|
|
95
|
+
exc,
|
|
96
|
+
)
|
|
87
97
|
|
|
88
|
-
|
|
98
|
+
for task_name, labels in classification_tasks.items():
|
|
99
|
+
try:
|
|
89
100
|
setfit = self._get_setfit_model(task_name)
|
|
90
101
|
if setfit is not None:
|
|
91
102
|
raw_cls: Any = self._run_setfit(setfit, task_name, text)
|
|
92
103
|
else:
|
|
93
|
-
raw_cls = self._classify_chunked(model, text, labels)
|
|
104
|
+
raw_cls = self._classify_chunked(model, text, task_name, labels)
|
|
94
105
|
raw_classification[task_name] = _normalise_classification_output(raw_cls)
|
|
106
|
+
except Exception as exc: # pragma: no cover - runtime specific
|
|
107
|
+
logger.error(
|
|
108
|
+
"GLiNER2 classification task '%s' failed for detector '%s': %s",
|
|
109
|
+
task_name,
|
|
110
|
+
self._detector_key,
|
|
111
|
+
exc,
|
|
112
|
+
)
|
|
95
113
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
114
|
+
# No early return for an empty result: "ran and found nothing" is a
|
|
115
|
+
# legitimate outcome and must still produce a well-formed result with
|
|
116
|
+
# metadata. Failures are reported by the logs above and by the runner's
|
|
117
|
+
# detector outcome, not by an anonymous empty return.
|
|
100
118
|
validation = self._schema.validation or PipelineValidationConfig()
|
|
101
119
|
filtered_entities = _apply_entity_validation(raw_entities, validation, self._schema)
|
|
102
120
|
filtered_classification = _apply_classification_validation(raw_classification, validation)
|
|
@@ -138,15 +156,46 @@ class GLiNER2Runner(BaseRunner):
|
|
|
138
156
|
)
|
|
139
157
|
return model.extract_entities(text, entity_schema, **kwargs)
|
|
140
158
|
|
|
141
|
-
def
|
|
159
|
+
def _classify_once(self, model: Any, text: str, task_name: str, labels: list[str]) -> Any:
|
|
160
|
+
"""Run one classification task against whichever API the runtime exposes.
|
|
161
|
+
|
|
162
|
+
gliner2>=1.3 exposes `classify_text(text, tasks: dict, ...)`, where tasks
|
|
163
|
+
maps a task name to its labels and the result is keyed by that name.
|
|
164
|
+
Mirrors the defensive probing in _extract_entities rather than assuming
|
|
165
|
+
a method exists.
|
|
166
|
+
"""
|
|
167
|
+
classify_text = getattr(model, "classify_text", None)
|
|
168
|
+
if classify_text is not None:
|
|
169
|
+
raw = classify_text(
|
|
170
|
+
text,
|
|
171
|
+
{task_name: list(labels)},
|
|
172
|
+
threshold=0.0,
|
|
173
|
+
include_confidence=True,
|
|
174
|
+
)
|
|
175
|
+
# classify_text returns {task_name: <result>}; unwrap to the result.
|
|
176
|
+
if isinstance(raw, dict) and task_name in raw:
|
|
177
|
+
return raw[task_name]
|
|
178
|
+
return raw
|
|
179
|
+
|
|
180
|
+
legacy_classify = getattr(model, "classify", None)
|
|
181
|
+
if legacy_classify is None:
|
|
182
|
+
raise AttributeError(
|
|
183
|
+
f"GLiNER2 model exposes neither 'classify_text' nor 'classify'; "
|
|
184
|
+
f"cannot run classification task '{task_name}'"
|
|
185
|
+
)
|
|
186
|
+
return legacy_classify(text, labels, threshold=0.0)
|
|
187
|
+
|
|
188
|
+
def _classify_chunked(
|
|
189
|
+
self, model: Any, text: str, task_name: str, labels: list[str]
|
|
190
|
+
) -> dict[str, object]:
|
|
142
191
|
"""Classify text, chunking long inputs and keeping the max-confidence label."""
|
|
143
192
|
chunks = _chunk_text(text, _CLS_CHUNK_SIZE, _CLS_CHUNK_OVERLAP)[:_MAX_CLS_CHUNKS]
|
|
144
|
-
if len(chunks) == 1:
|
|
145
|
-
return _normalise_classification_output(model.classify(text, labels, threshold=0.0))
|
|
146
193
|
|
|
147
194
|
best: dict[str, object] = {}
|
|
148
195
|
for chunk in chunks:
|
|
149
|
-
outcome = _normalise_classification_output(
|
|
196
|
+
outcome = _normalise_classification_output(
|
|
197
|
+
self._classify_once(model, chunk, task_name, labels)
|
|
198
|
+
)
|
|
150
199
|
if outcome and float(outcome.get("confidence", 0.0)) > float(
|
|
151
200
|
best.get("confidence", -1.0)
|
|
152
201
|
):
|
|
@@ -312,14 +361,31 @@ def _normalise_span(span: Any, text: str) -> dict[str, object] | None:
|
|
|
312
361
|
return {"value": value, "confidence": round(confidence, 4), "start": start, "end": end}
|
|
313
362
|
|
|
314
363
|
|
|
364
|
+
def _confidence_of(item: Any) -> float:
|
|
365
|
+
"""Read a confidence from either key the runtime may use.
|
|
366
|
+
|
|
367
|
+
gliner2's formatter emits {"label", "confidence"} for multi-label results.
|
|
368
|
+
Reading only "score" silently yields 0.0 for every label, which the
|
|
369
|
+
validation threshold then filters away — classification would appear to run
|
|
370
|
+
cleanly and produce nothing.
|
|
371
|
+
"""
|
|
372
|
+
if not isinstance(item, dict):
|
|
373
|
+
return 0.0
|
|
374
|
+
value = item.get("confidence", item.get("score", 0.0))
|
|
375
|
+
try:
|
|
376
|
+
return float(value) # type: ignore[arg-type]
|
|
377
|
+
except (TypeError, ValueError):
|
|
378
|
+
return 0.0
|
|
379
|
+
|
|
380
|
+
|
|
315
381
|
def _normalise_classification_output(raw: Any) -> dict[str, object]:
|
|
316
382
|
if isinstance(raw, dict):
|
|
317
383
|
label = raw.get("label", "")
|
|
318
|
-
confidence =
|
|
384
|
+
confidence = _confidence_of(raw)
|
|
319
385
|
elif isinstance(raw, (list, tuple)) and raw:
|
|
320
|
-
best = max(raw, key=
|
|
386
|
+
best = max(raw, key=_confidence_of)
|
|
321
387
|
label = best.get("label", "") if isinstance(best, dict) else str(best)
|
|
322
|
-
confidence =
|
|
388
|
+
confidence = _confidence_of(best) if isinstance(best, dict) else 1.0
|
|
323
389
|
else:
|
|
324
390
|
return {}
|
|
325
391
|
|
|
@@ -20,6 +20,19 @@ logger = logging.getLogger(__name__)
|
|
|
20
20
|
_PRESIDIO_LOG_FILTER_INSTALLED = False
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
def _unwrap_int(value: Any) -> int | None:
|
|
24
|
+
"""Unwrap a generated RootModel[int] config field to a plain int.
|
|
25
|
+
|
|
26
|
+
Numeric fields with constraints (chunk_size, chunk_overlap, max_length) are
|
|
27
|
+
generated as RootModel[int] wrappers. A wrapper instance is truthy and is
|
|
28
|
+
not an int, so `if not value` never short-circuits and any arithmetic or
|
|
29
|
+
comparison against it raises TypeError. Both failure modes are silent: the
|
|
30
|
+
detector fails per page while the run still reports success.
|
|
31
|
+
"""
|
|
32
|
+
unwrapped = getattr(value, "root", value)
|
|
33
|
+
return unwrapped if isinstance(unwrapped, int) else None
|
|
34
|
+
|
|
35
|
+
|
|
23
36
|
class _PresidioNoiseFilter(logging.Filter):
|
|
24
37
|
"""Suppresses noisy but harmless Presidio initialization warnings."""
|
|
25
38
|
|
|
@@ -342,7 +355,7 @@ class PIIDetector(BaseDetector):
|
|
|
342
355
|
logger.warning("spaCy model '%s' not found; using default NLP engine", cfg_model)
|
|
343
356
|
return None
|
|
344
357
|
|
|
345
|
-
spacy_max_length
|
|
358
|
+
spacy_max_length = _unwrap_int(getattr(self._cfg, "max_length", None))
|
|
346
359
|
if spacy_max_length is not None:
|
|
347
360
|
nlp.max_length = spacy_max_length
|
|
348
361
|
logger.debug("Set spaCy nlp.max_length = %d", spacy_max_length)
|
|
@@ -757,6 +770,22 @@ class PIIDetector(BaseDetector):
|
|
|
757
770
|
def _get_severity_for_entity(self, entity_type: str) -> Severity:
|
|
758
771
|
e = entity_type.upper()
|
|
759
772
|
|
|
773
|
+
# An explicit override always wins: a recognizer's severity depends on
|
|
774
|
+
# the corpus, not on its label. A CREDIT_CARD hit is critical in a
|
|
775
|
+
# payments export and noise in an OCR'd court transcript.
|
|
776
|
+
overrides = getattr(self._cfg, "severity_overrides", None) or {}
|
|
777
|
+
if isinstance(overrides, dict):
|
|
778
|
+
override = overrides.get(e) or overrides.get(entity_type)
|
|
779
|
+
if override is not None:
|
|
780
|
+
try:
|
|
781
|
+
return Severity(str(override).lower())
|
|
782
|
+
except ValueError:
|
|
783
|
+
logger.warning(
|
|
784
|
+
"Ignoring invalid severity override '%s' for entity %s",
|
|
785
|
+
override,
|
|
786
|
+
e,
|
|
787
|
+
)
|
|
788
|
+
|
|
760
789
|
# Critical — government IDs, financial account numbers, biometric IDs
|
|
761
790
|
if e in {
|
|
762
791
|
"CREDIT_CARD",
|
|
@@ -807,7 +836,12 @@ class PIIDetector(BaseDetector):
|
|
|
807
836
|
if e in {"PERSON", "LOCATION", "DATE_TIME", "NRP", "URL"}:
|
|
808
837
|
return Severity.medium
|
|
809
838
|
|
|
810
|
-
|
|
839
|
+
# Unknown entity types — including every custom recognizer and anything
|
|
840
|
+
# Presidio adds upstream — default to medium rather than high. Severity
|
|
841
|
+
# is a claim about the label, not about evidence quality, and defaulting
|
|
842
|
+
# unrecognised labels to HIGH inflated the corpus's severity histogram
|
|
843
|
+
# without any review having happened.
|
|
844
|
+
return Severity.medium
|
|
811
845
|
|
|
812
846
|
# ------------------------------------------------------------------
|
|
813
847
|
# Public API
|
|
@@ -827,10 +861,10 @@ class PIIDetector(BaseDetector):
|
|
|
827
861
|
|
|
828
862
|
def _chunk_text(self, text: str) -> list[tuple[str, int]]:
|
|
829
863
|
"""Return (chunk, offset) pairs. When chunk_size is null returns the full text at offset 0."""
|
|
830
|
-
chunk_size
|
|
864
|
+
chunk_size = _unwrap_int(getattr(self._cfg, "chunk_size", None))
|
|
831
865
|
if not chunk_size:
|
|
832
866
|
return [(text, 0)]
|
|
833
|
-
overlap
|
|
867
|
+
overlap = _unwrap_int(getattr(self._cfg, "chunk_overlap", None)) or 0
|
|
834
868
|
step = max(1, chunk_size - overlap)
|
|
835
869
|
return [(text[i : i + chunk_size], i) for i in range(0, len(text), step)]
|
|
836
870
|
|
|
@@ -460,6 +460,10 @@ class PIIDetectorConfig(DetectorConfig):
|
|
|
460
460
|
None,
|
|
461
461
|
description='Presidio entity types to detect. When null, all supported entities are enabled. Use PIIEnabledPattern values (e.g. EMAIL_ADDRESS, US_SSN, CREDIT_CARD).',
|
|
462
462
|
)
|
|
463
|
+
severity_overrides: dict[str, Severity] | None = Field(
|
|
464
|
+
None,
|
|
465
|
+
description='Per-entity severity overrides, keyed by Presidio entity type (e.g. {"CREDIT_CARD": "medium"}). Severity describes the label, not evidence quality: a recognizer that fires on OCR artifacts in one corpus may be genuinely critical in another. Overrides win over the built-in defaults.',
|
|
466
|
+
)
|
|
463
467
|
language: str | None = Field(
|
|
464
468
|
'en', description='BCP-47 language code for NER models (e.g. en, de, es)'
|
|
465
469
|
)
|
|
@@ -112,14 +112,6 @@ class SamplingConfig(BaseModel):
|
|
|
112
112
|
extra='forbid',
|
|
113
113
|
)
|
|
114
114
|
strategy: SamplingStrategy
|
|
115
|
-
enable_ocr: bool | None = Field(
|
|
116
|
-
False,
|
|
117
|
-
description='When true, enable OCR/text extraction for supported binary documents and images before routing text-capable detectors.',
|
|
118
|
-
)
|
|
119
|
-
enable_transcription: bool | None = Field(
|
|
120
|
-
False,
|
|
121
|
-
description='When true, transcribe audio and video files to text (via faster-whisper) before routing text-capable detectors. Slower and requires the transcription dependency.',
|
|
122
|
-
)
|
|
123
115
|
order_by_column: str | None = Field(
|
|
124
116
|
None,
|
|
125
117
|
description='Column to use for LATEST sampling mode in tabular sources (usually created_at/updated_at). Auto-detected when not set.',
|
|
@@ -407,10 +399,6 @@ class YouTubeOptionalTranscript(BaseModel):
|
|
|
407
399
|
None,
|
|
408
400
|
description='Preferred caption language codes in priority order (e.g. ["en"]). Empty means accept any available language.',
|
|
409
401
|
)
|
|
410
|
-
skip_transcript: bool | None = Field(
|
|
411
|
-
False,
|
|
412
|
-
description='When true, skip transcript fetching entirely (metadata-only assets, no detector content).',
|
|
413
|
-
)
|
|
414
402
|
|
|
415
403
|
|
|
416
404
|
class YouTubeOptionalConnection(BaseModel):
|
{classifyre_cli-0.4.52 → classifyre_cli-0.4.54}/src/models/generated_single_asset_scan_results.py
RENAMED
|
@@ -87,31 +87,6 @@ class Location(BaseModel):
|
|
|
87
87
|
end: int | None = Field(None, description='End offset (0-indexed)')
|
|
88
88
|
|
|
89
89
|
|
|
90
|
-
class ScanStats(BaseModel):
|
|
91
|
-
"""
|
|
92
|
-
Statistics about detector scan for an asset
|
|
93
|
-
"""
|
|
94
|
-
|
|
95
|
-
scanned_at: AwareDatetime = Field(
|
|
96
|
-
..., description='Timestamp when the scan started'
|
|
97
|
-
)
|
|
98
|
-
duration_ms: int = Field(..., description='Duration of the scan in milliseconds')
|
|
99
|
-
detectors_run: list[DetectorType] = Field(
|
|
100
|
-
..., description='List of detector types that were run'
|
|
101
|
-
)
|
|
102
|
-
content_size_bytes: int | None = Field(
|
|
103
|
-
None, description='Size of the content that was scanned'
|
|
104
|
-
)
|
|
105
|
-
findings_count: int | None = Field(
|
|
106
|
-
None, description='Total number of findings detected'
|
|
107
|
-
)
|
|
108
|
-
warnings: list[str] | None = Field(
|
|
109
|
-
None,
|
|
110
|
-
description='Non-fatal issues during scan (e.g. content truncation, empty content)',
|
|
111
|
-
)
|
|
112
|
-
errors: list[str] | None = Field(None, description='Detector errors during scan')
|
|
113
|
-
|
|
114
|
-
|
|
115
90
|
class DetectionResult(BaseModel):
|
|
116
91
|
"""
|
|
117
92
|
Result from detector scan
|
|
@@ -191,6 +166,71 @@ class DetectionResult(BaseModel):
|
|
|
191
166
|
)
|
|
192
167
|
|
|
193
168
|
|
|
169
|
+
class Status(StrEnum):
|
|
170
|
+
"""
|
|
171
|
+
OK means the detector completed; it may still have found nothing. ERROR means it raised and its result is unknown.
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
OK = 'OK'
|
|
175
|
+
ERROR = 'ERROR'
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class DetectorOutcome(BaseModel):
|
|
179
|
+
"""
|
|
180
|
+
Outcome of a single detector on a single asset. A detector that raised is reported with status ERROR: it produced no findings, but that absence is a failure rather than a clean result. Consumers must not treat an ERROR detector's silence as evidence that its previous findings are gone.
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
detector_type: DetectorType = Field(
|
|
184
|
+
..., description='Detector family that was attempted'
|
|
185
|
+
)
|
|
186
|
+
custom_detector_key: str | None = Field(
|
|
187
|
+
None,
|
|
188
|
+
description='Stable key of the custom detector when detector_type is CUSTOM. Required to tell two custom detectors apart, since both report detector_type CUSTOM.',
|
|
189
|
+
title='Custom Detector Key',
|
|
190
|
+
)
|
|
191
|
+
status: Status = Field(
|
|
192
|
+
...,
|
|
193
|
+
description='OK means the detector completed; it may still have found nothing. ERROR means it raised and its result is unknown.',
|
|
194
|
+
title='Status',
|
|
195
|
+
)
|
|
196
|
+
error: str | None = Field(
|
|
197
|
+
None, description='Failure detail when status is ERROR', title='Error'
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class ScanStats(BaseModel):
|
|
202
|
+
"""
|
|
203
|
+
Statistics about detector scan for an asset
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
scanned_at: AwareDatetime = Field(
|
|
207
|
+
..., description='Timestamp when the scan started'
|
|
208
|
+
)
|
|
209
|
+
duration_ms: int = Field(..., description='Duration of the scan in milliseconds')
|
|
210
|
+
detectors_run: list[DetectorType] = Field(
|
|
211
|
+
..., description='List of detector types that were run'
|
|
212
|
+
)
|
|
213
|
+
detector_outcomes: list[DetectorOutcome] | None = Field(
|
|
214
|
+
None,
|
|
215
|
+
description='Per-detector outcome for this asset. detectors_run lists what was attempted and cannot distinguish a detector that completed cleanly from one that crashed; this does.',
|
|
216
|
+
)
|
|
217
|
+
content_size_bytes: int | None = Field(
|
|
218
|
+
None, description='Size of the content that was scanned'
|
|
219
|
+
)
|
|
220
|
+
empty_text: bool | None = Field(
|
|
221
|
+
None,
|
|
222
|
+
description="True when an asset that should carry text yielded none. Most significant for assets whose text is derived rather than native (OCR'd images and scanned PDFs, transcribed audio/video): empty derived text means the content may have been missed entirely, not that the document is blank. Distinct from an error — nothing failed — so it is reported as coverage, not as a run failure.",
|
|
223
|
+
)
|
|
224
|
+
findings_count: int | None = Field(
|
|
225
|
+
None, description='Total number of findings detected'
|
|
226
|
+
)
|
|
227
|
+
warnings: list[str] | None = Field(
|
|
228
|
+
None,
|
|
229
|
+
description='Non-fatal issues during scan (e.g. content truncation, empty content)',
|
|
230
|
+
)
|
|
231
|
+
errors: list[str] | None = Field(None, description='Detector errors during scan')
|
|
232
|
+
|
|
233
|
+
|
|
194
234
|
class SingleAssetScanResults(BaseModel):
|
|
195
235
|
"""
|
|
196
236
|
Single asset scan results with detector findings
|