SageScan
Production-grade, CLI-first data quality validation for modern data pipelines.
SageScan combines a Go CLI for fast, scriptable orchestration with a Python engine for rich statistical validation — connected via a clean JSON bridge over stdin/stdout.
Table of Contents
- Why SageScan?
- Architecture
- Installation
- Quick Start
- CLI Reference
- Configuration Reference
- Validator Types
- Example Dataset & Output
- Big-Data Readiness
- AI Layer (Optional)
- Testing
- Roadmap
- Contributing
Why SageScan?
Data pipelines fail silently more often than they break loudly.
SageScan helps you catch those failures before they reach production.
| Feature | SageScan |
|---|---|
| CLI-first design | ✅ — pipe-friendly, CI-ready |
| Declarative rules | ✅ — YAML-based, version-controlled |
| Statistical checks | ✅ — z-score, mean, std, PSI, KS |
| Drift detection | ✅ — KS test + Population Stability Index |
| AI rule generation | ✅ — optional, GPT-4 compatible |
| Big-data support | ✅ — CSV chunked reads, Parquet |
| Zero external DB | ✅ — runs on local files |
Architecture
┌─────────────────────────────────────────────────────┐
│ Go CLI (Cobra) │
│ validate │ profile │ report │ generate-rules │ init│
└─────────────────────┬───────────────────────────────┘
│ JSON via stdin/stdout
┌─────────────────────▼───────────────────────────────┐
│ Python Engine (main.py) │
│ Command router → runner → pipeline → validators │
└──────────────────────────────────────────────────────┘
│ │
Pydantic v2 Validator Registry
(strict schema) (not_null │ regex │ z_score
min_length │ allowed_values …)
Key Design Decisions:
| Layer | Technology | Rationale |
|---|---|---|
| CLI | Go + Cobra + Viper | Fast startup, single binary, no runtime needed |
| Validation Engine | Python + pandas | Rich ecosystem for data science |
| Schema Validation | Pydantic v2 | Strict, fast, Pythonic |
| Communication | JSON stdin/stdout | Simple, debuggable, language-agnostic |
| Config | YAML | Human-readable, version-control friendly |
Installation
Install SageScan effortlessly via PyPI. This will automatically bundle the compiled Go CLI alongside the Python statistical validations!
pip install sagescan-data[all]
(You must have a valid Python 3.9+ runtime to execute the pipeline).
Verify your installation globally:
sagescan --version
Advanced Setup (From Source)
For contributors or teams who want to build the Go binary and harness the raw codebase directly instead of using pip:
- Clone the repo
git clone https://github.com/sagescan/sagescan.git cd sagescan
- Setup the local Python Engine
make setup-python - Compile the Go CLI natively
make build - Use your local binary instead of the global pip command:
./sagescan --help
Quick Start
# 1. Create a sample data file
cat > data/sample.csv << 'EOF'
user_id,age,email,status
1,25,alice@example.com,active
2,17,bob@example.com,inactive
3,30,charlie@example.com,active
EOF
# 2. Initialise a config
sagescan init --output rules.yaml
# 3. Edit rules.yaml to match your columns, then run:
sagescan validate rules.yaml
# 4. Get JSON output (great for CI / downstream processing)
sagescan validate rules.yaml --output json
# 5. Fail CI pipeline on any violation
sagescan validate rules.yaml --fail-fast
CLI Reference
Usage:
sagescan [command]
Available Commands:
validate Run data quality validation on a config file
profile Generate a statistical profile of a dataset
report Generate a formatted validation report
generate-rules Auto-generate validation rules using AI (requires API key)
init Create a new SageScan config file with defaults
Flags:
--timeout duration Max wait time for engine (default: 5m)
--context string Validation context tag (e.g. production, staging)
--baseline string Path to baseline config for comparison
-h, --help Help for any command
-v, --version Show version
validate
sagescan validate <config.yaml> [flags]
Flags:
-o, --output string Output format: text (default) or json
--fail-fast Exit 1 on any validation failure (for CI)
--timeout 10m Override engine timeout
--context string Tag the run (e.g. production)
profile
sagescan profile <config.yaml> [flags]
Profiles the dataset: row count, column types, null %, min/max/mean/std for numeric columns.
report
sagescan report <config.yaml> [flags]
Runs validation and outputs a rich formatted report.
generate-rules
sagescan generate-rules -i data.csv -o rules.yaml [flags]
Flags:
-i, --input string Input CSV file (required)
-o, --output string Output YAML rules file (required)
--llm-model string LLM model (default: gpt-4o)
--llm-api-key string OpenAI API key (or set OPENAI_API_KEY env var)
--llm-base-url string Custom API base URL for self-hosted or local LLMs
--llm-temperature float32 Temperature setting for LLM generation (default: 0.3)
init
sagescan init --output my_rules.yaml
Generates a ready-to-use config file with sensible defaults.
Configuration Reference
version: "1.0"
source:
type: csv # csv | parquet
path: data/users.csv # relative to the config file, or absolute
rules:
- column: user_id
checks:
- type: not_null
- type: unique
- column: age
checks:
- type: range
min: 18
max: 120
- type: z_score
value: 3.0 # flag |z| > 3 as outlier
- column: email
checks:
- type: not_null
- type: regex
value: "^[^@]+@[^@]+\\.[^@]+$"
- type: max_length
value: 254
- column: status
checks:
- type: allowed_values
values: [active, inactive, pending, banned]
# Optional — needed only for generate-rules and explain features
# llm_api_key: sk-... (prefer OPENAI_API_KEY env var)
# llm_model: gpt-4o
# llm_max_tokens: 2000
# llm_base_url: http://localhost:11434/v1
# llm_temperature: 0.3
Validator Types
| Check type | Config keys | Description |
|---|---|---|
not_null |
— | No null values allowed |
unique |
— | All values must be distinct |
min_value |
value |
All values ≥ value |
max_value |
value |
All values ≤ value |
range |
min, max |
All values in [min, max] |
regex |
value |
Values match regex pattern |
pattern |
value |
Alias for regex |
null_percentage |
value |
% of nulls ≤ value (0–100) |
min_length |
value |
String length ≥ value |
max_length |
value |
String length ≤ value |
allowed_values |
values: [...] |
Value must be in the list |
mean_check |
min, max |
Column mean in [min, max] |
std_check |
min, max |
Column std in [min, max] |
z_score |
value |
Flag rows where |z| > value |
z_score_outlier |
threshold, upper_threshold, lower_threshold |
Separate upper/lower bounds |
ks_test |
reference_path, alpha |
Kolmogorov–Smirnov distribution test |
psi |
reference_path, threshold, bins |
Population Stability Index |
Example Dataset & Output
examples/sample_data.csv
user_id,age,email,status
1,25,alice@example.com,active
2,17,bob@example.com,inactive
3,30,charlie@example.com,active
3,40,duplicate@example.com,pending
5,,missing_age@example.com,active
Run:
sagescan validate examples/basic_rules.yaml
Output:
📊 Validating data quality rules from: examples/basic_rules.yaml
────────────────────────────────────────────────────────────
Status: FAIL
Pass Rate: 60.0%
Checks: 5 total | 3 passed | 2 failed
✅ user_id not_null All values are not null
❌ user_id unique Found 1 duplicate values
❌ age not_null Null values found in 1 rows
✅ age range All values are within range
✅ email regex All values match the specified pattern
JSON output mode:
sagescan validate examples/basic_rules.yaml --output json
{
"status": "FAIL",
"summary": {
"total": 5,
"passed": 3,
"failed": 2,
"pass_rate": 60.0
},
"results": [
{
"column": "user_id",
"check_type": "not_null",
"passed": true,
"message": "All values are not null"
},
...
]
}
Environment Variables
| Variable | Default | Description |
|---|---|---|
SAGESCAN_ENGINE_PATH |
<binary_dir>/engine/main.py |
Override Python engine path |
SAGESCAN_VERBOSE |
false |
Set true for debug timing info |
SAGESCAN_LOG_LEVEL |
WARNING |
Python log level (DEBUG/INFO/WARNING/ERROR) |
OPENAI_API_KEY |
— | API key for AI features |
Big-Data Readiness
| Scenario | Current Behaviour |
|---|---|
| CSV < 50 MB | Full in-memory load |
| CSV 50 MB – 2 GB | Chunked pd.read_csv(chunksize=100_000) |
| CSV > 2 GB | Rejected with clear error message |
| Parquet | pd.read_parquet via pyarrow (up to 4 GB) |
| Failed rows | Capped at 1,000 per check to prevent OOM |
Planned improvements:
- Polars backend (10–50× faster than pandas for large files)
- Streaming validation (per-chunk without materialising full DataFrame)
- DuckDB connector for in-process SQL-based validation
- S3 / GCS URI support for cloud-native use
AI Layer (Optional)
Generate rules from a dataset
export OPENAI_API_KEY=sk-...
sagescan generate-rules -i data/users.csv -o rules/users.yaml --context "user registration data"
To use a custom local model like Ollama instead of OpenAI, provide your custom base URL and temperature:
sagescan generate-rules -i data/users.csv -o rules/users.yaml --llm-model="llama3" --llm-base-url="http://localhost:11434/v1" --llm-temperature=0.8 --llm-api-key="dummy"
SageScan sends column statistics (not raw data) to the LLM to generate rules — no PII leaves your machine.
Explain validation failures
Add llm_api_key to your config. On the next validate run, failed checks automatically get plain-English explanations appended to the JSON output.
AI is always optional. Core validation is fully deterministic and works without any API key.
Testing
# Run all tests
make test
# Go tests only
make test-go
# Python tests only
make test-python
# With coverage
cd engine && python -m pytest --cov=sagescan_engine --cov-report=term-missing
Test coverage:
engine/sagescan_engine/validators/test_implementations.py— all 17 validator typesengine/sagescan_engine/rules/test_models.py— Pydantic schema validationinternal/cli/base_test.go— CLI config loadinginternal/python/engine_test.go— subprocess communication
Project Structure
sagescan/
├── cmd/sagescan/
│ └── main.go # CLI entrypoint
├── internal/
│ ├── cli/ # Cobra command definitions
│ │ ├── cli.go # Root command + init
│ │ ├── validate.go # sagescan validate
│ │ ├── profile.go # sagescan profile
│ │ ├── report.go # sagescan report
│ │ ├── generate_rules.go # sagescan generate-rules
│ │ ├── init.go # sagescan init
│ │ ├── base.go # Shared flags/helpers
│ │ └── logger.go # Structured logging setup
│ ├── python/
│ │ └── engine.go # Go ↔ Python subprocess bridge
│ └── config/
│ └── config.go # Config loading utilities
├── engine/
│ ├── main.py # Python engine entrypoint
│ ├── requirements.txt
│ ├── pyproject.toml
│ └── sagescan_engine/
│ ├── core/
│ │ ├── pipeline.py # Data loading + validator orchestration
│ │ ├── runner.py # Command dispatch (validate/profile/…)
│ │ └── report.py # Report formatting (CLI / JSON / HTML)
│ ├── rules/
│ │ ├── models.py # Pydantic config models
│ │ └── schema.py # JSON schema
│ ├── validators/
│ │ ├── base.py # BaseValidator + ValidationResult
│ │ ├── implementations.py # All 17 validator types
│ │ ├── distribution.py # KS test, PSI, z-score outlier
│ │ └── registry.py # Validator factory
│ └── llm/
│ ├── rule_generator.py # AI rule generation
│ └── explanation_generator.py # AI failure explanation
├── examples/
│ ├── sample_data.csv
│ ├── basic_rules.yaml
│ └── comprehensive_rules.yaml
├── Makefile
├── go.mod
└── README.md
Roadmap
v1.1 (Next)
- Polars backend (opt-in via
--engine polars) - DuckDB connector for SQL-based validation
- HTML report generation
-
--watchmode for streaming pipelines - GitHub Actions example workflow
v1.2
- PostgreSQL connector
- Snowflake connector
- S3/GCS URI support
- Webhook notifications (Slack, PagerDuty)
- Rule inheritance / rule templates
v2.0
- Web UI dashboard
- Drift alerting + historical trending
- dbt integration
- Great Expectations compatibility layer
Contributing
Contributions are welcome!
# Fork the repo, then:
git checkout -b feat/my-improvement
# Make your changes, then test:
make test
# Open a pull request
Guidelines:
- All new validators must have tests in
test_implementations.py - Go changes must pass
go vet ./... - Python changes must pass
python -m py_compile - Keep
engine/main.pystdout clean — only JSON, nothing else - Document new check types in this README
Metadata
Release files for sagescan-data 1.0.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| sagescan_data-1.0.6.tar.gz | 67.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sagescan_data-1.0.6-cp312-cp312-win_amd64.whl | CPython 3.12 | CPython 3.12 | Windows x86-64 | Details |
Total release size: 6.3 MB
Release files / sagescan_data-1.0.6.tar.gz
| Download URL | sagescan_data-1.0.6.tar.gz |
|---|---|
| Size | 67.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
91093fa504b0dd4cf29c2a854492222b56cfb822d33dc65ab86946b73cb4c6d4
|
|
BLAKE2b-256 checksum How to use checksums |
85d519dea2267fa4890c1dae87f4d5d4ca2b5e48c95a01d985c7ffec662bf5ce
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.4
|
Release files / sagescan_data-1.0.6-cp312-cp312-win_amd64.whl
| Download URL | sagescan_data-1.0.6-cp312-cp312-win_amd64.whl |
|---|---|
| Size | 6.3 MB |
| Tags | CPython 3.12 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
3857251cddbc98cbd0e7602a19e1680380f719138a014d1dc0823040aae21875
|
|
BLAKE2b-256 checksum How to use checksums |
0967586e0025d3f47cf2ff2033f6616cb2d6f54c34b8d1bd68239a0182ab5b61
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.4
|
Security for sagescan-data 1.0.6
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.