Skip to content

Using it as a Python library

The library is the primary door - the CLI and the MCP server are thin shells over it. Anything shown here works in any Python application, notebook or script.

The public API

Everything you need is importable straight from spectro_kernel:

from spectro_kernel import (
    Spectrum1D, WorkContext,             # data types
    list_algorithms, describe_algorithm, # discovery
    run_algorithm,                       # run one algorithm
    PipelineBuilder,                     # compose many
    read_fits, read_ascii_spectrum,      # I/O
)

Loading a spectrum

from spectro_kernel.io import read_fits, read_ascii_spectrum, read_votable_spectrum

spec = read_fits("obs.fits")               # local path …
spec = read_fits("https://example/obs.fits")  # … or an http(s) URL
spec = read_ascii_spectrum("obs.csv")

read_fits handles both common layouts - a 1D flux array with a WCS header, and a binary table with explicit columns.

URLs and the trust boundary

Remote downloads are streamed with a 60 s timeout and a 1 GiB size ceiling (timeout= / max_bytes= to adjust). By default the library applies no SSRF policy — like astropy or requests, it cannot know whether http://10.0.0.5/ is your own MinIO or an attacker's pivot; only the application knows its trust boundary.

If your URLs come from untrusted users (a web backend, a relay service), opt into the kernel's canonical guard:

from spectro_kernel.io import read_fits
from spectro_kernel.url_safety import validate_safe_url

spec = read_fits(user_url, url_validator=validate_safe_url)

With a validator set, private / loopback / link-local targets are refused and so are HTTP redirects (each hop would evade the check). Pass your own callable for a custom policy, or allow_remote=False to guarantee the call never touches the network.

Running a single algorithm

from spectro_kernel import WorkContext, run_algorithm

ctx = WorkContext(spectrum=spec)
output = run_algorithm("normalize_polynomial", ctx, {"order": 3})

output.success        # True / False
output.metrics        # scalar summary, e.g. {"continuum_median": 1.2e4}
ctx.spectrum          # the normalised spectrum (algorithms write back into ctx)

If you prefer a failed run to return an AlgorithmOutput instead of raising:

output = run_algorithm("snr_der", ctx, raise_on_error=False)
if not output.success:
    print(output.error)

Discovering what exists

from spectro_kernel import list_algorithms, describe_algorithm

for meta in list_algorithms("line_fitting"):
    print(meta["name"], "-", meta["description"])

info = describe_algorithm("fit_gaussian_line")
info["default_params"]   # {'line_center_angstrom': None, 'window_angstrom': 20.0, ...}
info["required_params"]  # ['line_center_angstrom']
info["backend"]          # 'scipy'
info["references"]       # ['scipy.optimize.curve_fit - …']

Composing a pipeline

from spectro_kernel import PipelineBuilder

pipeline = (
    PipelineBuilder()
    .add("normalize_polynomial", order=3)
    .add("snr_der")
    .add("fit_gaussian_line", line_center_angstrom=6562.79, window_angstrom=30)
    .build()
)
result = pipeline.execute(ctx)

See Concepts: pipelines for presets and reproducibility.

Reading the results

After a run, everything is on the WorkContext:

ctx.spectrum            # the current (possibly transformed) spectrum
ctx.metrics             # {"snr_der": 142.3, ...}
ctx.line_fits           # {"H-alpha": LineFitResult(...)}
ctx.periodograms        # {"lomb_scargle": Periodogram(...)}
ctx.exports             # {"csv": b"...", "fits": b"..."}
ctx.history             # list[ProcessingStep] - the audit trail
ctx.summary()           # compact JSON-safe overview

Writing your own algorithm

Because the kernel is a normal library, an algorithm you register in your own codebase joins the catalogue just like a built-in one:

from spectro_kernel.base import BaseAlgorithm, AlgorithmOutput
from spectro_kernel.registry import register_algorithm
from spectro_kernel.types import AlgorithmCategory, WorkContext


@register_algorithm("my_metric", category=AlgorithmCategory.QUALITY, version="1.0.0")
class MyMetric(BaseAlgorithm):
    """A project-specific quality metric."""

    input_requirements = ["spectrum"]

    def run(self, ctx: WorkContext, params: dict) -> AlgorithmOutput:
        value = float(ctx.spectrum.flux.mean())
        ctx.metrics["my_metric"] = value
        return AlgorithmOutput.ok(metrics={"my_metric": value})

Once the module is imported, run_algorithm("my_metric", ctx) works - and so does the CLI and the MCP server. The full walkthrough is in Tutorial: add an algorithm.