classifyre-cli 0.5.3__tar.gz → 0.5.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- classifyre_cli-0.5.5/.turbo/turbo-build.log +3 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/PKG-INFO +2 -2
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/package.json +1 -1
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/pyproject.toml +7 -2
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/custom/runners/_llm.py +143 -4
- classifyre_cli-0.5.5/src/detectors/errors.py +19 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/main.py +20 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/models/generated_detectors.py +59 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/models/generated_input.py +85 -37
- classifyre_cli-0.5.5/src/notebook/cohort.py +194 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/notebook/execute.py +2 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/notebook/sdk.py +267 -1
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/outputs/rest.py +10 -2
- classifyre_cli-0.5.5/src/pipeline/detector_breaker.py +207 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/pipeline/detector_pipeline.py +127 -12
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/pipeline/parsed_content_provider.py +3 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/pipeline/scan_cache.py +15 -1
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/base.py +10 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/custom/runner.py +46 -1
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/custom/source.py +190 -16
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/custom/test_llm_runner.py +213 -1
- classifyre_cli-0.5.5/tests/pipeline/test_detector_breaker.py +342 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/pipeline/test_detector_pipeline.py +36 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_custom_source.py +336 -1
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_dependency_groups.py +17 -0
- classifyre_cli-0.5.5/tests/test_notebook_asset_extract.py +46 -0
- classifyre_cli-0.5.5/tests/test_notebook_cohort.py +125 -0
- classifyre_cli-0.5.5/tests/test_notebook_query_assets.py +105 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_scan_cache.py +32 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/uv.lock +293 -827
- classifyre_cli-0.5.3/.turbo/turbo-build.log +0 -3
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/.gitignore +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/.python-version +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/README.md +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/main.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/scripts/generate_models.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/scripts/generate_runtime_packages.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/scripts/generate_sdk_completions.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/augmentation/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/augmentation/contract.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/augmentation/preview.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/augmentation/runner.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/augmentation/sdk.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/augmentation/session.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/config.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/base.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/broken_links/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/broken_links/detector.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/config.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/custom/detector.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/custom/extractor.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/custom/runners/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/custom/runners/_base.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/custom/runners/_factory.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/custom/runners/_gliner2.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/custom/runners/_image_classification.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/custom/runners/_object_detection.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/custom/runners/_regex.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/custom/runners/_tag.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/custom/runners/_text_classification.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/custom/trainer.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/dependencies.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/engine_version.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/pii/detector.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/secrets/detector.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/threat/code_security_detector.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/detectors/threat/yara_detector.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/file_evaluation/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/file_evaluation/runner.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/graph/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/graph/edges.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/models/generated_single_asset_scan_results.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/notebook/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/notebook/child.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/notebook/cli.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/notebook/contract.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/notebook/files.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/notebook/groups.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/notebook/outputs.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/notebook/packages.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/notebook/protocol.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/notebook/redact.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/notebook/scaffold.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/notebook/serialize.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/outputs/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/outputs/base.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/outputs/console.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/outputs/factory.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/outputs/file.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/pipeline/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/pipeline/content_provider.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/pipeline/payload_window.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/pipeline/text_artifact.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/pipeline/worker_pool.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/asset_metadata.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/atlassian_common.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/azure_blob_storage/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/azure_blob_storage/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/confluence/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/confluence/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/custom/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/custom/env.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/databricks/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/databricks/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/delta_lake/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/delta_lake/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/dependencies.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/dropbox/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/dropbox/auth.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/dropbox/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/elasticsearch/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/elasticsearch/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/email/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/email/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/git/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/git/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/google_cloud_storage/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/google_cloud_storage/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/google_workspace/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/google_workspace/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/hive/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/hive/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/hugging_face/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/hugging_face/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/iceberg/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/iceberg/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/jira/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/jira/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/kafka/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/kafka/rest.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/kafka/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/lakehouse_base.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/local_folder/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/local_folder/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/meilisearch/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/meilisearch/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/microsoft_365/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/microsoft_365/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/mongodb/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/mongodb/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/mssql/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/mssql/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/mysql/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/mysql/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/neo4j/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/neo4j/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/notion/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/notion/client.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/notion/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/object_storage/base.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/opensearch/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/opensearch/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/oracle/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/oracle/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/postgresql/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/postgresql/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/powerbi/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/powerbi/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/recipe_normalizer.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/reddit/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/reddit/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/s3_client.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/s3_compatible_storage/README.md +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/s3_compatible_storage/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/s3_compatible_storage/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/sandbox/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/sandbox/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/search_engine_base.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/servicedesk/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/servicedesk/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/slack/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/slack/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/snowflake/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/snowflake/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/sqlite/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/sqlite/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/tableau/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/tableau/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/tabular_base.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/tabular_utils.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/wordpress/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/wordpress/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/youtube/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/sources/youtube/source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/telemetry.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/archive_extraction.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/content_extraction.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/dependency_groups.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/embedded_files.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/external_video.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/file_metadata.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/file_parser.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/file_to_images.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/hashing.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/legacy_office.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/payload.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/range_reader.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/resources.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/source_files.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/sql_lineage.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/torch_runtime.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/transcription.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/urn.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/uv_sync.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/validation.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/src/utils/video_processing.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/_lakehouse_fakes.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/conftest.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/broken_links/test_broken_links_detector.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/conftest.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/custom/conftest.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/custom/test_gliner2_classification.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/custom/test_invoice_extraction.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/custom/test_pipeline_integration.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/custom/test_regex_runner.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/custom/test_runner_hardening.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/custom/test_transformer_runners.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/pii/conftest.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/pii/sample_invoice.pdf +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/pii/test_pii_chunking.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/pii/test_pii_detector.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/pii/test_pii_detector_extended.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/pii/test_pii_regional_entities.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/pii/test_pii_severity.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/secrets/test_secrets_detector.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/secrets/test_secrets_detector_extended.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/test_base_detector.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/test_custom_detector_examples_runtime.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/test_detector_catalog_commercial.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/test_detector_pipeline_types.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/test_detector_schema_examples.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/test_detector_types.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/test_phase2_detectors.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/test_registry.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/test_tag_runner.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/threat/test_code_security_detector.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/detectors/threat/test_yara_detector.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/integration/test_wordpress_broken_links_detector.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/integration/test_wordpress_links_assets.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/pipeline/test_detector_outcomes.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/pipeline/test_worker_pool.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_arrow_ipc_payloads.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_assets_metadata_catalog.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_augmentation_contract.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_augmentation_edges.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_augmentation_patch.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_augmentation_scan_cache.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_augmentation_sdk.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_augmentation_session.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_augmentation_tags_end_to_end.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_azure_blob_storage_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_base_source_attachment.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_base_source_sampling.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_byte_valued_fields.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_config.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_confluence_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_custom_extractor.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_custom_source_relationships.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_custom_source_tags_end_to_end.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_databricks_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_delta_lake_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_detector_scope.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_dropbox_auth_command.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_dropbox_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_elasticsearch_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_email_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_file_evaluation_runner.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_git_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_google_cloud_storage_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_google_workspace_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_graph_edges.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_hashing.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_hive_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_hugging_face_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_iceberg_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_jira_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_kafka_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_lineage_loss_reporting.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_local_folder_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_main.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_meilisearch_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_microsoft_365_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_mongodb_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_mssql_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_mysql_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_neo4j_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_notebook_asset_tags.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_notebook_execute.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_notebook_files.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_notebook_groups.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_notebook_packages.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_notebook_serialize.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_notebook_templates.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_notion_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_object_storage_download_efficiency.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_opensearch_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_oracle_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_outputs.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_payload_window.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_payload_window_parquet.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_postgresql_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_powerbi_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_range_reading.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_recipe_normalizer.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_reddit_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_rest_backpressure.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_runtime_packages.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_s3_compatible_storage_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_sampling_automatic.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_sandbox_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_sdk_completions.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_servicedesk_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_slack_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_snowflake_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_source_dependency_groups.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_source_strategy_conformance.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_sqlite_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_tableau_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_tabular_automatic_sampling.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_tabular_lineage.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_tabular_utils.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_torch_runtime.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_urn.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_uv_sync.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_wordpress_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_youtube_source.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/test_youtube_source_integration.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/utils/test_archive_extraction.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/utils/test_content_extraction.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/utils/test_embedded_files.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/utils/test_file_metadata.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/utils/test_file_parser.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/utils/test_file_to_images.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/utils/test_legacy_office.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/utils/test_payload.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/utils/test_transcription.py +0 -0
- {classifyre_cli-0.5.3 → classifyre_cli-0.5.5}/tests/utils/test_video_processing.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: classifyre-cli
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.5
|
|
4
4
|
Summary: Classifyre CLI — scan and classify unstructured data sources
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: data,ingestion,metadata,pii,secrets,unstructured
|
|
7
|
-
Requires-Python:
|
|
7
|
+
Requires-Python: <3.15,>=3.12
|
|
8
8
|
Requires-Dist: beautifulsoup4>=4.12.0
|
|
9
9
|
Requires-Dist: classifyre-schemas
|
|
10
10
|
Requires-Dist: email-validator>=2.3.0
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "classifyre-cli"
|
|
3
|
-
version = "0.5.
|
|
3
|
+
version = "0.5.5"
|
|
4
4
|
description = "Classifyre CLI — scan and classify unstructured data sources"
|
|
5
5
|
readme = "README.md"
|
|
6
|
-
|
|
6
|
+
# Upper bound is load-bearing: onnxruntime publishes no cp315 wheels (notably
|
|
7
|
+
# linux/aarch64, which required-environments forces uv to resolve), so an
|
|
8
|
+
# unbounded range makes `uv lock` fail for the python>=3.15 split and breaks
|
|
9
|
+
# the release workflow and Dependabot's uv updates. Lift to <3.16 once
|
|
10
|
+
# onnxruntime ships cp315 wheels for all required-environments platforms.
|
|
11
|
+
requires-python = ">=3.12,<3.15"
|
|
7
12
|
license = { text = "MIT" }
|
|
8
13
|
keywords = ["data", "metadata", "ingestion", "unstructured", "pii", "secrets"]
|
|
9
14
|
dependencies = [
|
|
@@ -3,12 +3,15 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import base64
|
|
6
|
+
import functools
|
|
6
7
|
import json
|
|
7
8
|
import logging
|
|
8
9
|
import os
|
|
9
10
|
import random
|
|
11
|
+
import re
|
|
10
12
|
import time
|
|
11
13
|
from datetime import UTC, datetime
|
|
14
|
+
from pathlib import Path
|
|
12
15
|
from typing import Any
|
|
13
16
|
|
|
14
17
|
# Quiet litellm's import-time provider preload warnings (bedrock/sagemaker need
|
|
@@ -22,6 +25,7 @@ from ....models.generated_single_asset_scan_results import (
|
|
|
22
25
|
)
|
|
23
26
|
from ....utils.file_to_images import render_to_images, supported_mime_type
|
|
24
27
|
from ...dependencies import require_module
|
|
28
|
+
from ...errors import ProviderRefusedError
|
|
25
29
|
from ._base import _IMAGE_CONTENT_TYPES, _TEXT_CONTENT_TYPES, BaseRunner, _resolve_pipeline_severity
|
|
26
30
|
|
|
27
31
|
logger = logging.getLogger(__name__)
|
|
@@ -48,11 +52,61 @@ _COMPLETION_TIMEOUT_SECONDS = float(os.environ.get("CLASSIFYRE_LLM_TIMEOUT_SECON
|
|
|
48
52
|
# Retry budget for transient provider failures (429 rate limits, 5xx, timeouts).
|
|
49
53
|
# Exponential backoff with jitter, honouring Retry-After when the provider
|
|
50
54
|
# sends one, so a saturated endpoint is backed off from instead of hammered.
|
|
51
|
-
|
|
55
|
+
# The attempt count is read per call (see `_max_attempts`), so a detector's own
|
|
56
|
+
# `budget.max_attempts` can override it.
|
|
57
|
+
_DEFAULT_MAX_COMPLETION_ATTEMPTS = 4
|
|
52
58
|
_RETRY_BACKOFF_BASE_SECONDS = float(os.environ.get("CLASSIFYRE_LLM_RETRY_BASE_SECONDS", "2"))
|
|
53
59
|
_RETRY_BACKOFF_MAX_SECONDS = 60.0
|
|
54
60
|
_RETRYABLE_STATUS_CODES = {408, 429, 500, 502, 503, 504}
|
|
55
61
|
|
|
62
|
+
# Used only if the shared marker list cannot be read, so a packaging mistake
|
|
63
|
+
# degrades to "the known worst offender still fails fast" rather than to "every
|
|
64
|
+
# quota refusal is retried on every asset again".
|
|
65
|
+
_FALLBACK_QUOTA_MARKERS = (
|
|
66
|
+
"insufficient_credit",
|
|
67
|
+
"insufficient balance",
|
|
68
|
+
"out of credits",
|
|
69
|
+
"resource_exhausted",
|
|
70
|
+
"resource exhausted",
|
|
71
|
+
"quota exceeded",
|
|
72
|
+
"free-models-per-day",
|
|
73
|
+
"insufficient_quota",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Billing-exhaustion signals checked before the generic substring scan, with
|
|
77
|
+
# word-boundary anchors so "billing" inside an unrelated word cannot trip them
|
|
78
|
+
# while "you have insufficient_credit" always does. Mirrors the head of the
|
|
79
|
+
# shared quota list in provider_refusal_markers.json.
|
|
80
|
+
_ANCHORED_BILLING_EXHAUSTION_MARKERS = (
|
|
81
|
+
"insufficient_credit",
|
|
82
|
+
"insufficient balance",
|
|
83
|
+
"out of credits",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _billing_exhaustion_match(text: str) -> str | None:
|
|
88
|
+
"""The anchored billing marker in *text*, or None when absent."""
|
|
89
|
+
for marker in _ANCHORED_BILLING_EXHAUSTION_MARKERS:
|
|
90
|
+
if re.search(rf"(?<![a-z0-9_]){re.escape(marker)}(?![a-z0-9_])", text):
|
|
91
|
+
return marker
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@functools.lru_cache(maxsize=1)
|
|
96
|
+
def _quota_markers() -> tuple[str, ...]:
|
|
97
|
+
"""Substrings that make a refusal non-retryable; shared with the API."""
|
|
98
|
+
try:
|
|
99
|
+
import schemas
|
|
100
|
+
|
|
101
|
+
path = Path(schemas.__file__).parent / "provider_refusal_markers.json"
|
|
102
|
+
markers = json.loads(path.read_text(encoding="utf-8")).get("quota", [])
|
|
103
|
+
loaded = tuple(str(m).strip().lower() for m in markers if str(m).strip())
|
|
104
|
+
if loaded:
|
|
105
|
+
return loaded
|
|
106
|
+
except Exception as exc: # pragma: no cover - packaging failure
|
|
107
|
+
logger.warning("provider_refusal_markers.json unavailable (%s); using fallback", exc)
|
|
108
|
+
return _FALLBACK_QUOTA_MARKERS
|
|
109
|
+
|
|
56
110
|
|
|
57
111
|
class LLMCompletionError(RuntimeError):
|
|
58
112
|
"""Provider call failed after retries.
|
|
@@ -63,6 +117,14 @@ class LLMCompletionError(RuntimeError):
|
|
|
63
117
|
"""
|
|
64
118
|
|
|
65
119
|
|
|
120
|
+
class LLMProviderRefusedError(LLMCompletionError, ProviderRefusedError):
|
|
121
|
+
"""The provider refused in a way no retry can fix (quota, auth, billing).
|
|
122
|
+
|
|
123
|
+
Raised on the first such response, never after retries. The pipeline
|
|
124
|
+
disables the detector for the rest of the run when it sees one.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
|
|
66
128
|
class LLMRunner(BaseRunner):
|
|
67
129
|
"""AI detector — sends content to a configured LLM provider for classification + extraction."""
|
|
68
130
|
|
|
@@ -153,6 +215,10 @@ class LLMRunner(BaseRunner):
|
|
|
153
215
|
response = self._complete_with_backoff(messages)
|
|
154
216
|
raw = response.choices[0].message.content or "{}"
|
|
155
217
|
parsed = self._parse_json(raw)
|
|
218
|
+
except LLMProviderRefusedError:
|
|
219
|
+
# Already names the detector, the model and the refusal. No stack
|
|
220
|
+
# trace: it is expected, and it will be the same on every asset.
|
|
221
|
+
raise
|
|
156
222
|
except Exception as exc:
|
|
157
223
|
logger.error(
|
|
158
224
|
"llm detector error (detector=%s, model=%s): %s",
|
|
@@ -173,7 +239,8 @@ class LLMRunner(BaseRunner):
|
|
|
173
239
|
|
|
174
240
|
def _complete_with_backoff(self, messages: list[dict[str, Any]]) -> Any:
|
|
175
241
|
schema = self._schema
|
|
176
|
-
|
|
242
|
+
max_attempts = self._max_attempts()
|
|
243
|
+
for attempt in range(1, max_attempts + 1):
|
|
177
244
|
try:
|
|
178
245
|
return self._litellm.completion(
|
|
179
246
|
model=self._model_string(),
|
|
@@ -184,9 +251,26 @@ class LLMRunner(BaseRunner):
|
|
|
184
251
|
messages=messages,
|
|
185
252
|
response_format={"type": "json_object"},
|
|
186
253
|
timeout=_COMPLETION_TIMEOUT_SECONDS,
|
|
254
|
+
# One retry layer, and it is this loop. litellm hands the
|
|
255
|
+
# OpenAI client its own default of 2 retries, which hid
|
|
256
|
+
# ~30 s of backoff inside every single attempt: a daily-quota
|
|
257
|
+
# refusal measured 30.2 s before this loop ever saw it.
|
|
258
|
+
max_retries=0,
|
|
187
259
|
)
|
|
188
260
|
except Exception as exc:
|
|
189
|
-
|
|
261
|
+
refusal = self._refusal_reason(exc)
|
|
262
|
+
if refusal is not None:
|
|
263
|
+
logger.warning(
|
|
264
|
+
"llm provider refused (detector=%s, model=%s): %s — not retrying",
|
|
265
|
+
self._detector_key,
|
|
266
|
+
self._runtime.model,
|
|
267
|
+
refusal,
|
|
268
|
+
)
|
|
269
|
+
raise LLMProviderRefusedError(
|
|
270
|
+
f"LLM provider refused detector '{self._detector_key}' "
|
|
271
|
+
f"(model={self._runtime.model}, {refusal}): {exc}"
|
|
272
|
+
) from None
|
|
273
|
+
if attempt >= max_attempts or not self._is_retryable(exc):
|
|
190
274
|
raise
|
|
191
275
|
delay = min(
|
|
192
276
|
_RETRY_BACKOFF_MAX_SECONDS,
|
|
@@ -202,13 +286,55 @@ class LLMRunner(BaseRunner):
|
|
|
202
286
|
self._detector_key,
|
|
203
287
|
self._runtime.model,
|
|
204
288
|
attempt,
|
|
205
|
-
|
|
289
|
+
max_attempts,
|
|
206
290
|
delay,
|
|
207
291
|
exc,
|
|
208
292
|
)
|
|
209
293
|
time.sleep(delay)
|
|
210
294
|
raise LLMCompletionError("unreachable") # pragma: no cover
|
|
211
295
|
|
|
296
|
+
def _max_attempts(self) -> int:
|
|
297
|
+
"""The detector's own budget first, then the deployment default."""
|
|
298
|
+
budget = getattr(self._schema, "budget", None)
|
|
299
|
+
configured = getattr(budget, "max_attempts", None)
|
|
300
|
+
if isinstance(configured, int) and configured >= 1:
|
|
301
|
+
return configured
|
|
302
|
+
raw = os.environ.get("CLASSIFYRE_LLM_MAX_ATTEMPTS", "")
|
|
303
|
+
try:
|
|
304
|
+
return max(1, int(raw)) if raw.strip() else _DEFAULT_MAX_COMPLETION_ATTEMPTS
|
|
305
|
+
except ValueError:
|
|
306
|
+
return _DEFAULT_MAX_COMPLETION_ATTEMPTS
|
|
307
|
+
|
|
308
|
+
def _refusal_reason(self, exc: Exception) -> str | None:
|
|
309
|
+
"""Why retrying this error cannot help, or None when it might.
|
|
310
|
+
|
|
311
|
+
A 429 is usually "slow down" and is retried. It is a refusal only when the
|
|
312
|
+
provider says the allowance itself is gone — a daily or credit quota — and
|
|
313
|
+
then every further call until the reset is the same answer, paid for again.
|
|
314
|
+
"""
|
|
315
|
+
status = getattr(exc, "status_code", None)
|
|
316
|
+
if status in (401, 403):
|
|
317
|
+
return "authentication refused"
|
|
318
|
+
if status == 402:
|
|
319
|
+
return "payment required"
|
|
320
|
+
for type_name, reason in (
|
|
321
|
+
("AuthenticationError", "authentication refused"),
|
|
322
|
+
("PermissionDeniedError", "permission denied"),
|
|
323
|
+
("BudgetExceededError", "budget exceeded"),
|
|
324
|
+
):
|
|
325
|
+
error_type = getattr(self._litellm, type_name, None)
|
|
326
|
+
if isinstance(error_type, type) and isinstance(exc, error_type):
|
|
327
|
+
return reason
|
|
328
|
+
|
|
329
|
+
text = _error_text(exc).lower()
|
|
330
|
+
# Anchored billing-exhaustion markers first: a spent balance is the
|
|
331
|
+
# most specific "retrying cannot help" signal in the shared list.
|
|
332
|
+
if _billing_exhaustion_match(text) is not None:
|
|
333
|
+
return "quota exhausted"
|
|
334
|
+
if any(marker in text for marker in _quota_markers()):
|
|
335
|
+
return "quota exhausted"
|
|
336
|
+
return None
|
|
337
|
+
|
|
212
338
|
def _is_retryable(self, exc: Exception) -> bool:
|
|
213
339
|
retryable_types = tuple(
|
|
214
340
|
t
|
|
@@ -223,6 +349,9 @@ class LLMRunner(BaseRunner):
|
|
|
223
349
|
)
|
|
224
350
|
if retryable_types and isinstance(exc, retryable_types):
|
|
225
351
|
return True
|
|
352
|
+
# Only the listed statuses are retried: an unknown 4xx (e.g. a Gemini
|
|
353
|
+
# safety-block 400) is a final answer for this input, not a busy
|
|
354
|
+
# provider. Retry stays for 5xx, plain 429s and network errors.
|
|
226
355
|
return getattr(exc, "status_code", None) in _RETRYABLE_STATUS_CODES
|
|
227
356
|
|
|
228
357
|
def _vision_enabled(self) -> bool:
|
|
@@ -391,6 +520,16 @@ class LLMRunner(BaseRunner):
|
|
|
391
520
|
return {str(k): v for k, v in raw.items()} if isinstance(raw, dict) else {}
|
|
392
521
|
|
|
393
522
|
|
|
523
|
+
def _error_text(exc: Exception) -> str:
|
|
524
|
+
"""Everything a provider exception says about itself, for marker matching."""
|
|
525
|
+
parts = [str(exc), str(getattr(exc, "message", "") or "")]
|
|
526
|
+
response = getattr(exc, "response", None)
|
|
527
|
+
body = getattr(response, "text", None)
|
|
528
|
+
if isinstance(body, str):
|
|
529
|
+
parts.append(body)
|
|
530
|
+
return " ".join(parts)
|
|
531
|
+
|
|
532
|
+
|
|
394
533
|
def _retry_after_seconds(exc: Exception) -> float | None:
|
|
395
534
|
"""Best-effort extraction of a Retry-After hint from a provider exception."""
|
|
396
535
|
response = getattr(exc, "response", None)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Detector errors whose *type* the pipeline acts on.
|
|
2
|
+
|
|
3
|
+
Kept free of heavy imports so the pipeline can recognise them without loading
|
|
4
|
+
any engine, and kept to a single string argument so they survive pickling
|
|
5
|
+
across the detector worker-pool process boundary with their class intact.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ProviderRefusedError(RuntimeError):
|
|
12
|
+
"""A provider refused the request in a way retrying cannot fix.
|
|
13
|
+
|
|
14
|
+
Quota exhausted for the day, a rejected key, billing. The next asset will be
|
|
15
|
+
refused the same way, so the pipeline disables the detector for the rest of
|
|
16
|
+
the run on the first one instead of paying for the same answer on every
|
|
17
|
+
asset — which is what held the only runner slot for 10 hours a day when a
|
|
18
|
+
free-tier LLM provider started refusing its key.
|
|
19
|
+
"""
|
|
@@ -768,6 +768,22 @@ async def run_command_async(args: argparse.Namespace, recipe: dict[str, Any]) ->
|
|
|
768
768
|
f" asset(s) after retries: {preview}"
|
|
769
769
|
)
|
|
770
770
|
|
|
771
|
+
# A detector the breaker disabled is not a crash and not a
|
|
772
|
+
# clean run; the API reports it from the per-asset
|
|
773
|
+
# `breaker_open[...]` outcomes, and this is the log line an
|
|
774
|
+
# operator reads first.
|
|
775
|
+
for degraded in pipeline.breaker_summary():
|
|
776
|
+
logger.warning(
|
|
777
|
+
"Detector %s was disabled mid-run (%s): %s. %d payload(s) "
|
|
778
|
+
"attempted, %d skipped; the skipped assets keep their "
|
|
779
|
+
"findings and are retried on the next run.",
|
|
780
|
+
degraded["label"],
|
|
781
|
+
degraded["cause"],
|
|
782
|
+
degraded["reason"],
|
|
783
|
+
degraded["attempted_payloads"],
|
|
784
|
+
degraded["skipped_payloads"],
|
|
785
|
+
)
|
|
786
|
+
|
|
771
787
|
# Persist the advanced AUTOMATIC sampling cursor (no-op for
|
|
772
788
|
# other strategies, which return None). Only on the normal
|
|
773
789
|
# completion path — a timed-out run must not advance it.
|
|
@@ -777,6 +793,10 @@ async def run_command_async(args: argparse.Namespace, recipe: dict[str, Any]) ->
|
|
|
777
793
|
# A connector that covered a slice must say so before
|
|
778
794
|
# finish(), which is what decides whether absence retires
|
|
779
795
|
# an asset.
|
|
796
|
+
cohort_stats = getattr(source, "cohort_stats", None)
|
|
797
|
+
if cohort_stats and hasattr(sink, "set_cohort_stats"):
|
|
798
|
+
sink.set_cohort_stats(cohort_stats)
|
|
799
|
+
|
|
780
800
|
if getattr(source, "partial_coverage", False) and hasattr(
|
|
781
801
|
sink, "set_partial_coverage"
|
|
782
802
|
):
|
|
@@ -966,6 +966,37 @@ class DetectorScope(BaseModel):
|
|
|
966
966
|
)
|
|
967
967
|
|
|
968
968
|
|
|
969
|
+
class DetectorBudget(BaseModel):
|
|
970
|
+
"""
|
|
971
|
+
How much of a run this detector may spend failing.
|
|
972
|
+
|
|
973
|
+
Without a budget a detector keeps being called after it has stopped working: an LLM detector on a provider that refused its key retried every asset for 15-40 seconds and held the only runner slot for 10 hours a day, failing on 451 of 451 assets. Every run already disables a detector after a non-retryable provider refusal (quota, auth, billing) and after 10 consecutive failures; these fields tighten or loosen that per detector.
|
|
974
|
+
|
|
975
|
+
A disabled detector records an ERROR outcome on the assets it skipped, so their existing findings are kept and the assets are retried on the next run. Changing the budget never invalidates the scan cache.
|
|
976
|
+
"""
|
|
977
|
+
|
|
978
|
+
model_config = ConfigDict(
|
|
979
|
+
extra='forbid',
|
|
980
|
+
)
|
|
981
|
+
max_attempts: int | None = Field(
|
|
982
|
+
None,
|
|
983
|
+
description='LLM detectors only: provider calls per asset, including the first. Overrides CLASSIFYRE_LLM_MAX_ATTEMPTS. A non-retryable refusal is never retried regardless.',
|
|
984
|
+
ge=1,
|
|
985
|
+
le=10,
|
|
986
|
+
)
|
|
987
|
+
max_consecutive_failures: int | None = Field(
|
|
988
|
+
None,
|
|
989
|
+
description='Disable the detector for the rest of the run after this many consecutive failed payloads. Defaults to 10.',
|
|
990
|
+
ge=1,
|
|
991
|
+
le=10000,
|
|
992
|
+
)
|
|
993
|
+
max_wall_clock_seconds: int | None = Field(
|
|
994
|
+
None,
|
|
995
|
+
description='Disable the detector for the rest of the run once it has spent this long, summed over every payload it ran on. Unset means no limit.',
|
|
996
|
+
ge=1,
|
|
997
|
+
)
|
|
998
|
+
|
|
999
|
+
|
|
969
1000
|
class Type1(StrEnum):
|
|
970
1001
|
GLINER2 = 'GLINER2'
|
|
971
1002
|
|
|
@@ -978,6 +1009,10 @@ class GLiNER2PipelineSchema(BaseModel):
|
|
|
978
1009
|
None,
|
|
979
1010
|
description='Restrict this detector to part of a source (asset kind, content type, or an asset-metadata predicate). Null runs it on everything the source produces.',
|
|
980
1011
|
)
|
|
1012
|
+
budget: DetectorBudget | None = Field(
|
|
1013
|
+
None,
|
|
1014
|
+
description='Failure and time budget for this detector within one run. Null uses the defaults: stop after a non-retryable provider refusal or 10 consecutive failures.',
|
|
1015
|
+
)
|
|
981
1016
|
type: Literal['GLINER2'] = 'GLINER2'
|
|
982
1017
|
entities: dict[str, PipelineEntityDefinition] | None = None
|
|
983
1018
|
classification: dict[str, PipelineClassificationDefinition] | None = None
|
|
@@ -997,6 +1032,10 @@ class RegexPipelineSchema(BaseModel):
|
|
|
997
1032
|
None,
|
|
998
1033
|
description='Restrict this detector to part of a source (asset kind, content type, or an asset-metadata predicate). Null runs it on everything the source produces.',
|
|
999
1034
|
)
|
|
1035
|
+
budget: DetectorBudget | None = Field(
|
|
1036
|
+
None,
|
|
1037
|
+
description='Failure and time budget for this detector within one run. Null uses the defaults: stop after a non-retryable provider refusal or 10 consecutive failures.',
|
|
1038
|
+
)
|
|
1000
1039
|
type: Literal['REGEX'] = 'REGEX'
|
|
1001
1040
|
patterns: dict[str, RegexPatternDefinition] | None = None
|
|
1002
1041
|
validation: PipelineValidationConfig | None = None
|
|
@@ -1105,6 +1144,10 @@ class LLMPipelineSchema(BaseModel):
|
|
|
1105
1144
|
None,
|
|
1106
1145
|
description='Restrict this detector to part of a source (asset kind, content type, or an asset-metadata predicate). Null runs it on everything the source produces.',
|
|
1107
1146
|
)
|
|
1147
|
+
budget: DetectorBudget | None = Field(
|
|
1148
|
+
None,
|
|
1149
|
+
description='Failure and time budget for this detector within one run. Null uses the defaults: stop after a non-retryable provider refusal or 10 consecutive failures.',
|
|
1150
|
+
)
|
|
1108
1151
|
type: Literal['LLM'] = 'LLM'
|
|
1109
1152
|
system_prompt: str = Field(
|
|
1110
1153
|
...,
|
|
@@ -1205,6 +1248,10 @@ class TextClassificationPipelineSchema(BaseModel):
|
|
|
1205
1248
|
None,
|
|
1206
1249
|
description='Restrict this detector to part of a source (asset kind, content type, or an asset-metadata predicate). Null runs it on everything the source produces.',
|
|
1207
1250
|
)
|
|
1251
|
+
budget: DetectorBudget | None = Field(
|
|
1252
|
+
None,
|
|
1253
|
+
description='Failure and time budget for this detector within one run. Null uses the defaults: stop after a non-retryable provider refusal or 10 consecutive failures.',
|
|
1254
|
+
)
|
|
1208
1255
|
type: Literal['TEXT_CLASSIFICATION']
|
|
1209
1256
|
model: str = Field(
|
|
1210
1257
|
...,
|
|
@@ -1279,6 +1326,10 @@ class ImageClassificationPipelineSchema(BaseModel):
|
|
|
1279
1326
|
None,
|
|
1280
1327
|
description='Restrict this detector to part of a source (asset kind, content type, or an asset-metadata predicate). Null runs it on everything the source produces.',
|
|
1281
1328
|
)
|
|
1329
|
+
budget: DetectorBudget | None = Field(
|
|
1330
|
+
None,
|
|
1331
|
+
description='Failure and time budget for this detector within one run. Null uses the defaults: stop after a non-retryable provider refusal or 10 consecutive failures.',
|
|
1332
|
+
)
|
|
1282
1333
|
type: Literal['IMAGE_CLASSIFICATION']
|
|
1283
1334
|
model: str | None = Field(
|
|
1284
1335
|
None,
|
|
@@ -1335,6 +1386,10 @@ class ObjectDetectionPipelineSchema(BaseModel):
|
|
|
1335
1386
|
None,
|
|
1336
1387
|
description='Restrict this detector to part of a source (asset kind, content type, or an asset-metadata predicate). Null runs it on everything the source produces.',
|
|
1337
1388
|
)
|
|
1389
|
+
budget: DetectorBudget | None = Field(
|
|
1390
|
+
None,
|
|
1391
|
+
description='Failure and time budget for this detector within one run. Null uses the defaults: stop after a non-retryable provider refusal or 10 consecutive failures.',
|
|
1392
|
+
)
|
|
1338
1393
|
type: Literal['OBJECT_DETECTION']
|
|
1339
1394
|
model: str = Field(
|
|
1340
1395
|
...,
|
|
@@ -1399,6 +1454,10 @@ class TagPipelineSchema(BaseModel):
|
|
|
1399
1454
|
None,
|
|
1400
1455
|
description='Restrict this detector to part of a source (asset kind, content type, or an asset-metadata predicate). Null runs it on everything the source produces.',
|
|
1401
1456
|
)
|
|
1457
|
+
budget: DetectorBudget | None = Field(
|
|
1458
|
+
None,
|
|
1459
|
+
description='Failure and time budget for this detector within one run. Null uses the defaults: stop after a non-retryable provider refusal or 10 consecutive failures.',
|
|
1460
|
+
)
|
|
1402
1461
|
type: Literal['TAG'] = 'TAG'
|
|
1403
1462
|
label: str | None = Field(
|
|
1404
1463
|
None,
|
|
@@ -4631,6 +4631,24 @@ class Variables(RootModel[str]):
|
|
|
4631
4631
|
root: str = Field(..., max_length=8192)
|
|
4632
4632
|
|
|
4633
4633
|
|
|
4634
|
+
class CohortWeightsMode(StrEnum):
|
|
4635
|
+
"""
|
|
4636
|
+
auto: measured from what each band yields. fixed: the declared or fixed split, always.
|
|
4637
|
+
"""
|
|
4638
|
+
|
|
4639
|
+
auto = 'auto'
|
|
4640
|
+
fixed = 'fixed'
|
|
4641
|
+
|
|
4642
|
+
|
|
4643
|
+
class CustomCohortBandWeights(BaseModel):
|
|
4644
|
+
model_config = ConfigDict(
|
|
4645
|
+
extra='forbid',
|
|
4646
|
+
)
|
|
4647
|
+
newest: float | None = Field(None, ge=0.0)
|
|
4648
|
+
oldest: float | None = Field(None, ge=0.0)
|
|
4649
|
+
random: float | None = Field(None, ge=0.0)
|
|
4650
|
+
|
|
4651
|
+
|
|
4634
4652
|
class NotebookPackage(BaseModel):
|
|
4635
4653
|
"""
|
|
4636
4654
|
A Python distribution installed before the notebook runs.
|
|
@@ -5688,38 +5706,40 @@ class GitInput(CoreInput):
|
|
|
5688
5706
|
augmentation: AugmentationConfig | None = None
|
|
5689
5707
|
|
|
5690
5708
|
|
|
5691
|
-
class
|
|
5709
|
+
class CustomCohortWeights(BaseModel):
|
|
5710
|
+
"""
|
|
5711
|
+
How ctx.cohort() splits each run's budget across its bands. auto shifts the split toward the bands whose keys produce HIGH/CRITICAL findings, within guards: the declared split stands until every band has 200 visits, no band drops below the floor, and no band moves more than 15 points per run. fixed always uses the bands the notebook declares, or the ones given here.
|
|
5712
|
+
"""
|
|
5713
|
+
|
|
5692
5714
|
model_config = ConfigDict(
|
|
5693
5715
|
extra='forbid',
|
|
5694
5716
|
)
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
description='Non-secret values the notebook reads with ctx.var("name"), such as a base URL or an account id. Stored in clear text. Keys must be valid Python identifiers.',
|
|
5700
|
-
max_length=64,
|
|
5701
|
-
validate_default=True,
|
|
5717
|
+
mode: CohortWeightsMode | None = Field(
|
|
5718
|
+
'auto',
|
|
5719
|
+
description='auto: measured from what each band yields. fixed: the declared or fixed split, always.',
|
|
5720
|
+
title='CohortWeightsMode',
|
|
5702
5721
|
)
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
validate_default=True,
|
|
5722
|
+
floor: float | None = Field(
|
|
5723
|
+
None,
|
|
5724
|
+
description='The least share any band keeps under auto, as a fraction. Never below 0.1.',
|
|
5725
|
+
ge=0.1,
|
|
5726
|
+
le=0.5,
|
|
5709
5727
|
)
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
description='
|
|
5713
|
-
|
|
5714
|
-
|
|
5728
|
+
fixed: dict[str, CustomCohortBandWeights] | None = Field(
|
|
5729
|
+
None,
|
|
5730
|
+
description='Per cohort name, the band split to use under fixed, e.g. {"register": {"newest": 60, "oldest": 30, "random": 10}}.',
|
|
5731
|
+
)
|
|
5732
|
+
effective: dict[str, CustomCohortBandWeights] | None = Field(
|
|
5733
|
+
None,
|
|
5734
|
+
description="Set by the platform on each run's recipe: the split each cohort runs with. Not stored on the source.",
|
|
5715
5735
|
)
|
|
5716
5736
|
|
|
5717
5737
|
|
|
5718
|
-
class
|
|
5719
|
-
type: Literal['
|
|
5720
|
-
required:
|
|
5721
|
-
masked:
|
|
5722
|
-
optional:
|
|
5738
|
+
class YouTubeInput(CoreInput):
|
|
5739
|
+
type: Literal['YOUTUBE'] = Field(..., description='Type of the asset or source')
|
|
5740
|
+
required: YouTubeRequired
|
|
5741
|
+
masked: YouTubeMasked | None = None
|
|
5742
|
+
optional: YouTubeOptional | None = None
|
|
5723
5743
|
detectors: list[Detector] | None = Field(
|
|
5724
5744
|
None, description='Detectors to run on ingested content'
|
|
5725
5745
|
)
|
|
@@ -5737,11 +5757,15 @@ class CustomInput(CoreInput):
|
|
|
5737
5757
|
augmentation: AugmentationConfig | None = None
|
|
5738
5758
|
|
|
5739
5759
|
|
|
5740
|
-
class
|
|
5741
|
-
type: Literal['
|
|
5742
|
-
required:
|
|
5743
|
-
|
|
5744
|
-
|
|
5760
|
+
class RedditInput(CoreInput):
|
|
5761
|
+
type: Literal['REDDIT'] = Field(..., description='Type of the asset or source')
|
|
5762
|
+
required: (
|
|
5763
|
+
RedditRequiredReadOnly | RedditRequiredScript | RedditRequiredRefreshToken
|
|
5764
|
+
) = Field(..., title='RedditRequired')
|
|
5765
|
+
masked: RedditMaskedReadOnly | RedditMaskedScript | RedditMaskedRefreshToken = (
|
|
5766
|
+
Field(..., title='RedditMasked')
|
|
5767
|
+
)
|
|
5768
|
+
optional: RedditOptional | None = None
|
|
5745
5769
|
detectors: list[Detector] | None = Field(
|
|
5746
5770
|
None, description='Detectors to run on ingested content'
|
|
5747
5771
|
)
|
|
@@ -5759,15 +5783,39 @@ class YouTubeInput(CoreInput):
|
|
|
5759
5783
|
augmentation: AugmentationConfig | None = None
|
|
5760
5784
|
|
|
5761
5785
|
|
|
5762
|
-
class
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
RedditRequiredReadOnly | RedditRequiredScript | RedditRequiredRefreshToken
|
|
5766
|
-
) = Field(..., title='RedditRequired')
|
|
5767
|
-
masked: RedditMaskedReadOnly | RedditMaskedScript | RedditMaskedRefreshToken = (
|
|
5768
|
-
Field(..., title='RedditMasked')
|
|
5786
|
+
class CustomOptional(BaseModel):
|
|
5787
|
+
model_config = ConfigDict(
|
|
5788
|
+
extra='forbid',
|
|
5769
5789
|
)
|
|
5770
|
-
|
|
5790
|
+
variables: (
|
|
5791
|
+
dict[constr(pattern=r'^[A-Za-z_][A-Za-z0-9_]{0,62}$'), Variables] | None
|
|
5792
|
+
) = Field(
|
|
5793
|
+
{},
|
|
5794
|
+
description='Non-secret values the notebook reads with ctx.var("name"), such as a base URL or an account id. Stored in clear text. Keys must be valid Python identifiers.',
|
|
5795
|
+
max_length=64,
|
|
5796
|
+
validate_default=True,
|
|
5797
|
+
)
|
|
5798
|
+
limits: CustomOptionalLimits | None = None
|
|
5799
|
+
packages: list[NotebookPackage] | None = Field(
|
|
5800
|
+
[],
|
|
5801
|
+
description="Python packages installed into the run environment before any cell executes. Installed with uv; the base image's own dependencies are always present and do not need listing.",
|
|
5802
|
+
max_length=50,
|
|
5803
|
+
validate_default=True,
|
|
5804
|
+
)
|
|
5805
|
+
local_folders: list[NotebookLocalFolder] | None = Field(
|
|
5806
|
+
[],
|
|
5807
|
+
description='Folders the notebook reads with ctx.folder("name"). Not available in Kubernetes deployments, where files are uploaded to the source instead. This is a convenience, not a sandbox: the notebook process runs as you and can open any path you can.',
|
|
5808
|
+
max_length=10,
|
|
5809
|
+
validate_default=True,
|
|
5810
|
+
)
|
|
5811
|
+
cohort_weights: CustomCohortWeights | None = None
|
|
5812
|
+
|
|
5813
|
+
|
|
5814
|
+
class CustomInput(CoreInput):
|
|
5815
|
+
type: Literal['CUSTOM'] = Field(..., description='Type of the asset or source')
|
|
5816
|
+
required: CustomRequired
|
|
5817
|
+
masked: CustomMasked | None = None
|
|
5818
|
+
optional: CustomOptional | None = None
|
|
5771
5819
|
detectors: list[Detector] | None = Field(
|
|
5772
5820
|
None, description='Detectors to run on ingested content'
|
|
5773
5821
|
)
|