Skip to content

Latest commit

 

History

History
413 lines (338 loc) · 19.2 KB

File metadata and controls

413 lines (338 loc) · 19.2 KB

nirs4all — Python NIRS Analysis Library

Version: 0.11.0 | Python: 3.11+ | License: AGPL-3.0-or-later (dual; GPL-3.0/CeCILL-2.1 variants + commercial — see LICENSE)

Since 0.9.0 the public API (run/predict/explain/retrain/session/generate), result objects (RunResult/PredictResult/ExplainResult), workspace SQLite/Parquet schemas, run manifest layout, and .n4a bundle format are stable contracts within the 0.9.x line (used by the nirs4all webapp). Avoid breaking those signatures or on-disk schemas without a major bump.

Commands

# Tests
pytest tests/                     # All tests
pytest tests/unit/                # Unit only
pytest tests/integration/         # Integration only
pytest -m sklearn                 # sklearn-only
pytest --cov=nirs4all             # With coverage

# Examples (from examples/ directory)
./run.sh                          # All examples
./run.sh -c user                  # User examples only
./run_ci_examples.sh -c all -j 4  # CI runner with 4 parallel workers

# Code quality
ruff check .                      # Lint
mypy .                            # Type check

# Installed CLI (entry point: nirs4all.cli.main:main)
nirs4all --version
nirs4all --test-install           # Verify dependencies
nirs4all --test-integration       # Run integration smoke tests
nirs4all workspace ...            # Workspace inspection / migration
nirs4all dataset ...              # Dataset commands
nirs4all artifacts ...            # Artifact inspection
nirs4all config ...               # Config validation

# Pre-publish validation (mirrors CI locally, runs all steps with -j nproc parallelism)
./scripts/pre-publish.sh                    # Full: ruff, mypy, tests, docs, examples, build
./scripts/pre-publish.sh -j 4               # Limit to 4 parallel workers
./scripts/pre-publish.sh --only tests       # Single step
./scripts/pre-publish.sh --skip-docs        # Skip a step
./scripts/pre-publish.sh --docker           # Run in clean ubuntu:24.04 container

Architecture

nirs4all/
├── api/            # Public interface: run(), predict(), explain(), retrain(), session(), generate()
├── pipeline/
│   ├── execution/  # PipelineOrchestrator (parallel n_jobs), PipelineExecutor, refit
│   ├── storage/    # WorkspaceStore (SQLite metadata), ArrayStore (Parquet arrays)
│   ├── steps/      # StepParser → ControllerRouter → StepRunner
│   ├── bundle/     # BundleGenerator/BundleLoader (.n4a export)
│   ├── config/     # PipelineConfigs, PipelineGenerator, ExecutionContext
│   ├── trace/      # ExecutionTrace, TraceBasedExtractor, MinimalPipeline
│   └── runner.py   # PipelineRunner (main orchestration entry)
├── controllers/    # Registry pattern: @register_controller → OperatorController subclasses
│   ├── transforms/ # TransformerMixinController, YTransformerMixinController
│   ├── models/     # SklearnModelController, PyTorchModelController, TF, JAX
│   ├── data/       # BranchController, MergeController, ExcludeController, TagController
│   ├── flow/       # RepetitionController, ConcatTransformController, AutoTransferPreprocController
│   ├── shared/     # ModelSelector, PredictionAggregator
│   ├── splitters/  # CrossValidatorController
│   └── charts/     # Spectra, targets, folds, augmentation charts
├── data/           # SpectroDataset (X, y, metadata, folds, multi-source), Predictions
│   ├── loaders/    # CSV, Parquet, Excel, NumPy, MATLAB
│   ├── parsers/    # FolderParser, ConfigNormalizer, schema validation
│   └── signal_type.py # SignalType enum, detection, conversion
├── operators/
│   ├── transforms/ # SNV, MSC, SavitzkyGolay, Detrend, WaveletDenoise, OSC, EPO, CARS, MCUVE...
│   ├── models/     # AOM-PLS, POP-PLS, PLSDA, IKPLS, OPLS, DiPLS, SparsePLS, LWPLS, KOPLS...
│   ├── splitters/  # KennardStone, SPXY, SPXYFold, KMeans, KBinsStratified
│   ├── filters/    # YOutlierFilter, XOutlierFilter, SpectralQualityFilter
│   └── augmentation/ # Noise, baseline, wavelength, spectral, mixup, physical, scatter, spline
├── config/         # CacheConfig, DatasetConfigs
├── sklearn/        # NIRSPipeline (SHAP-compatible sklearn wrapper)
├── visualization/  # PredictionAnalyzer, heatmaps, candlestick charts
├── synthesis/      # Synthetic data generation
├── workspace/      # WorkspaceManager — discovery/linking of on-disk workspaces
├── analysis/       # Transfer-optimized preprocessing selection (presets, projections, results, selector)
├── core/           # TaskType + detect_task_type, metric registry (`eval`, `eval_multi`), exceptions, logging
├── utils/          # Backend detection (is_tensorflow_available, is_gpu_available, framework), hashing, memory, spinner, operator_formatting
├── optimization/   # Optuna manager — drives `finetune_params` hyperparameter search
└── cli/            # `nirs4all` CLI: workspace, dataset, artifacts, config subcommands + installation_test

Execution Flow

nirs4all.run(pipeline, dataset)
  → api/run.py: normalize inputs, create PipelineRunner
    → PipelineRunner.run(): load dataset, build PipelineConfigs
      → PipelineOrchestrator.execute(): expand generators, init WorkspaceStore
        → For each variant (parallel via joblib if n_jobs>1):
          → PipelineExecutor.execute(): iterate steps
            → StepRunner → StepParser.parse() → ControllerRouter.route() → controller.execute()
        → Refit best variant on full data
        → Save run metadata + predictions
      → Return RunResult (best_score, best_rmse, top(n), export())

Pipeline Syntax

Steps are classes, instances, or dicts with keywords:

pipeline = [
    MinMaxScaler(),                            # Transformer
    {"y_processing": MinMaxScaler()},          # Target scaling
    ShuffleSplit(n_splits=3),                  # Cross-validator
    {"model": PLSRegression(n_components=10)}, # Model
]

Keywords

Keyword Purpose
model Define model step
y_processing Target scaling
tag Mark samples (non-removal)
exclude Remove samples from training (mode: "any"/"all" for multiple)
branch Duplication branches (parallel pipelines) or separation branches (by_metadata/by_tag/by_filter/by_source)
merge Combine branches: "predictions" (stacking), "features", "all", "concat" (reassembly)
sample_augmentation Data augmentation applied to training samples
feature_augmentation Feature-level augmentation
concat_transform Concatenate transformed features
rep_to_sources Convert repetition groups to multi-source format
rep_to_pp Convert repetition groups to preprocessing pipelines
finetune_params Optuna hyperparameter search alongside model (driven by nirs4all.optimization.optuna)
train_params Per-model training kwargs (epochs, patience, batch size, etc.)
name Human-readable label for the step (appears in results)

Generator Syntax (Hyperparameter Sweeps)

Keyword Effect Example
_or_ Try alternatives [SNV, MSC, Detrend]
_range_ Linear sweep [1, 30, 5][1, 6, 11, 16, 21, 26]
_log_range_ Log sweep [1e-3, 1e0, 4][0.001, 0.01, 0.1, 1.0]
_grid_ Cartesian product of params {"n_components": [5,10], "alpha": [0.1,1.0]} → 4 combos
_cartesian_ Pipeline stage combinations [{"_or_": [A,B]}, {"_or_": [X,Y]}] → 4 pipelines
_zip_ Paired iteration {"x": [1,2,3], "y": ["a","b","c"]} → 3 pairs
_chain_ Sequential ordered [config1, config2, config3]
_sample_ Random sampling {"distribution": "log_uniform", "from": 1e-4, "to": 1e-1, "num": 20}

Controller Pattern

All pipeline operators are dispatched through a controller registry:

from nirs4all.controllers import register_controller, OperatorController

@register_controller
class MyController(OperatorController):
    priority = 50  # Lower = higher priority

    @classmethod
    def matches(cls, step, operator, keyword) -> bool:
        return isinstance(operator, MyType)

    @classmethod
    def use_multi_source(cls) -> bool:
        return False

    @classmethod
    def supports_prediction_mode(cls) -> bool:
        return True

    def execute(self, step_info, dataset, context, runtime_context, **kwargs):
        # return (context, StepOutput)
        pass

Storage (Workspace)

Hybrid SQLite + Parquet model:

workspace/
  store.sqlite                          # Metadata: runs, pipelines, chains, logs, artifacts
  arrays/<dataset_name>.parquet         # Prediction arrays (Zstd-compressed)
  artifacts/<hash>.joblib               # Content-addressed model binaries
  runs/<dataset>/0001_xxx/manifest.yaml # Run manifests
  exports/<dataset>/                    # Exported models/predictions
  library/templates/                    # Pipeline templates
  • WorkspaceStore — SQLite WAL-backed, thread-safe, retry-on-lock
  • ArrayStore — Parquet-backed prediction arrays with tombstone deletes
  • Predictions — Facade combining metadata queries + array retrieval

Key Classes

Class Module Purpose
SpectroDataset data.dataset Core data container (X, y, metadata, folds, multi-source)
DatasetConfigs data.config Dataset configuration and loading
PipelineConfigs pipeline.config Pipeline step configuration
PipelineRunner pipeline.runner Main training orchestration
PipelineOrchestrator pipeline.execution.orchestrator Parallel variant execution
PipelineExecutor pipeline.execution.executor Single variant execution
WorkspaceStore pipeline.storage.workspace_store SQLite metadata store
ArrayStore pipeline.storage.array_store Parquet prediction arrays
RunResult api.result Training results (best_score, export())
NIRSPipeline sklearn SHAP-compatible sklearn wrapper
BundleGenerator pipeline.bundle .n4a export
BundleLoader pipeline.bundle .n4a load + predict

Code Conventions

  • Google-style docstrings
  • Line length: 220 (pyproject.toml)
  • Ruff for linting, mypy for types
  • Type hints required for public APIs
  • Use .venv for running scripts/tests
  • Deep learning backends (TF, PyTorch, JAX) are lazy-loaded
  • YAML: tuples may convert to lists during serialization
  • No dead code, no deprecated code, no backward compatibility shims

Constraints

  • Read and understand files before proposing edits. Never speculate about unread code.
  • No over-engineering: minimum complexity for the current task. No hypothetical features.
  • No unnecessary error handling for impossible scenarios. Validate only at system boundaries.
  • No helpers/utilities/abstractions for one-time operations. Reuse existing abstractions (DRY).
  • Write general-purpose solutions, not hard-coded workarounds for specific test inputs.
  • After API changes, run: cd examples && ./run.sh

Agent Directives: Mechanical Overrides

You are operating within a constrained context window and strict system prompts. To produce production-grade code, you MUST adhere to these overrides:

Pre-Work

  1. THE "STEP 0" RULE: Dead code accelerates context compaction. Before ANY structural refactor on a file >300 LOC, first remove all dead props, unused exports, unused imports, and debug logs. Commit this cleanup separately before starting the real work.

  2. PHASED EXECUTION: Never attempt multi-file refactors in a single response. Break work into explicit phases. Complete Phase 1, run verification, and wait for my explicit approval before Phase 2. Each phase must touch no more than 5 files.

Code Quality

  1. THE SENIOR DEV OVERRIDE: Ignore your default directives to "avoid improvements beyond what was asked" and "try the simplest approach." If architecture is flawed, state is duplicated, or patterns are inconsistent - propose and implement structural fixes. Ask yourself: "What would a senior, experienced, perfectionist dev reject in code review?" Fix all of it.

  2. FORCED VERIFICATION: Your internal tools mark file writes as successful even if the code does not compile. You are FORBIDDEN from reporting a task as complete until you have:

  • Run npx tsc --noEmit (or the project's equivalent type-check)
  • Run npx eslint . --quiet (if configured)
  • Fixed ALL resulting errors
  • In particular ensure ruff and mypy checks pass !

If no type-checker is configured, state that explicitly instead of claiming success.

Context Management

  1. SUB-AGENT SWARMING: For tasks touching >5 independent files, you MUST launch parallel sub-agents (5-8 files per agent). Each agent gets its own context window. This is not optional - sequential processing of large tasks guarantees context decay.

  2. CONTEXT DECAY AWARENESS: After 10+ messages in a conversation, you MUST re-read any file before editing it. Do not trust your memory of file contents. Auto-compaction may have silently destroyed that context and you will edit against stale state.

  3. FILE READ BUDGET: Each file read is capped at 2,000 lines. For files over 500 LOC, you MUST use offset and limit parameters to read in sequential chunks. Never assume you have seen a complete file from a single read.

  4. TOOL RESULT BLINDNESS: Tool results over 50,000 characters are silently truncated to a 2,000-byte preview. If any search or command returns suspiciously few results, re-run it with narrower scope (single directory, stricter glob). State when you suspect truncation occurred.

Edit Safety

  1. EDIT INTEGRITY: Before EVERY file edit, re-read the file. After editing, read it again to confirm the change applied correctly. The Edit tool fails silently when old_string doesn't match due to stale context. Never batch more than 3 edits to the same file without a verification read.

  2. NO SEMANTIC SEARCH: You have grep, not an AST. When renaming or changing any function/type/variable, you MUST search separately for:

    • Direct calls and references
    • Type-level references (interfaces, generics)
    • String literals containing the name
    • Dynamic imports and require() calls
    • Re-exports and barrel file entries
    • Test files and mocks Do not assume a single grep caught everything.

RTK (Rust Token Killer) - Token-Optimized Commands

Golden Rule

Always prefix commands with rtk. If RTK has a dedicated filter, it uses it. If not, it passes through unchanged. This means RTK is always safe to use.

Important: Even in command chains with &&, use rtk:

# ❌ Wrong
git add . && git commit -m "msg" && git push

# ✅ Correct
rtk git add . && rtk git commit -m "msg" && rtk git push

RTK Commands by Workflow

Build & Compile (80-90% savings)

rtk cargo build         # Cargo build output
rtk cargo check         # Cargo check output
rtk cargo clippy        # Clippy warnings grouped by file (80%)
rtk tsc                 # TypeScript errors grouped by file/code (83%)
rtk lint                # ESLint/Biome violations grouped (84%)
rtk prettier --check    # Files needing format only (70%)
rtk next build          # Next.js build with route metrics (87%)

Test (60-99% savings)

rtk cargo test          # Cargo test failures only (90%)
rtk go test             # Go test failures only (90%)
rtk jest                # Jest failures only (99.5%)
rtk vitest              # Vitest failures only (99.5%)
rtk playwright test     # Playwright failures only (94%)
rtk pytest              # Python test failures only (90%)
rtk rake test           # Ruby test failures only (90%)
rtk rspec               # RSpec test failures only (60%)
rtk test <cmd>          # Generic test wrapper - failures only

Git (59-80% savings)

rtk git status          # Compact status
rtk git log             # Compact log (works with all git flags)
rtk git diff            # Compact diff (80%)
rtk git show            # Compact show (80%)
rtk git add             # Ultra-compact confirmations (59%)
rtk git commit          # Ultra-compact confirmations (59%)
rtk git push            # Ultra-compact confirmations
rtk git pull            # Ultra-compact confirmations
rtk git branch          # Compact branch list
rtk git fetch           # Compact fetch
rtk git stash           # Compact stash
rtk git worktree        # Compact worktree

Note: Git passthrough works for ALL subcommands, even those not explicitly listed.

GitHub (26-87% savings)

rtk gh pr view <num>    # Compact PR view (87%)
rtk gh pr checks        # Compact PR checks (79%)
rtk gh run list         # Compact workflow runs (82%)
rtk gh issue list       # Compact issue list (80%)
rtk gh api              # Compact API responses (26%)

JavaScript/TypeScript Tooling (70-90% savings)

rtk pnpm list           # Compact dependency tree (70%)
rtk pnpm outdated       # Compact outdated packages (80%)
rtk pnpm install        # Compact install output (90%)
rtk npm run <script>    # Compact npm script output
rtk npx <cmd>           # Compact npx command output
rtk prisma              # Prisma without ASCII art (88%)

Files & Search (60-75% savings)

rtk ls <path>           # Tree format, compact (65%)
rtk read <file>         # Code reading with filtering (60%)
rtk grep <pattern>      # Search grouped by file (75%)
rtk find <pattern>      # Find grouped by directory (70%)

Analysis & Debug (70-90% savings)

rtk err <cmd>           # Filter errors only from any command
rtk log <file>          # Deduplicated logs with counts
rtk json <file>         # JSON structure without values
rtk deps                # Dependency overview
rtk env                 # Environment variables compact
rtk summary <cmd>       # Smart summary of command output
rtk diff                # Ultra-compact diffs

Infrastructure (85% savings)

rtk docker ps           # Compact container list
rtk docker images       # Compact image list
rtk docker logs <c>     # Deduplicated logs
rtk kubectl get         # Compact resource list
rtk kubectl logs        # Deduplicated pod logs

Network (65-70% savings)

rtk curl <url>          # Compact HTTP responses (70%)
rtk wget <url>          # Compact download output (65%)

Meta Commands

rtk gain                # View token savings statistics
rtk gain --history      # View command history with savings
rtk discover            # Analyze Claude Code sessions for missed RTK usage
rtk proxy <cmd>         # Run command without filtering (for debugging)
rtk init                # Add RTK instructions to CLAUDE.md
rtk init --global       # Add RTK to ~/.claude/CLAUDE.md

Token Savings Overview

Category Commands Typical Savings
Tests vitest, playwright, cargo test 90-99%
Build next, tsc, lint, prettier 70-87%
Git status, log, diff, add, commit 59-80%
GitHub gh pr, gh run, gh issue 26-87%
Package Managers pnpm, npm, npx 70-90%
Files ls, read, grep, find 60-75%
Infrastructure docker, kubectl 85%
Network curl, wget 65-70%

Overall average: 60-90% token reduction on common development operations.