Skip to content

Plug spectro-kernel into an existing app - BeSS dashboard

A short walkthrough showing how to migrate an existing spectroscopy app to lean on spectro-kernel. We use the BeSS dashboard as the running example because it is small and representative; the same recipe works for staros-dashboard, aurorex-dashboard, or your own code.

The starting situation

BeSS dashboard currently re-implements two operations the kernel already provides:

  • a FITS parser that rebuilds the wavelength axis from the WCS,
  • a "normalise the continuum at the 95th percentile" step.

Two extra implementations no one wants to maintain. Migrating them to the kernel buys uniform behaviour with every other spectroscopy app and provenance (backend + literature references) for free.

The two-line migration

Replace parse_fits_spectrum and the percentile normalisation with two run_algorithm calls:

# bess-dashboard, backend/app/utils/fits_parser.py
def parse_fits_spectrum(path):
    # … 80 lines: open with astropy, rebuild WCS, normalise at p95 …
    return {"wavelength": wave, "flux": flux_normalised}
from spectro_kernel import WorkContext, run_algorithm
from spectro_kernel.io import read_fits

def parse_fits_spectrum(path):
    ctx = WorkContext(spectrum=read_fits(path))
    run_algorithm("normalize_percentile", ctx, {"percentile": 95.0})
    spec = ctx.spectrum
    return {"wavelength": spec.wavelength.tolist(),
            "flux": spec.flux.tolist()}

That is the entire change. The dashboard's React side, its API routes, its database, none of it changes. Only the FITS-and-normalise plumbing is rerouted through the kernel.

What you gain immediately

  • One canonical FITS parser - same WCS reconstruction conventions as every other spectro-kernel-based app.
  • SNR, line detection, line fitting for free - same ctx, add three lines:
    run_algorithm("snr_der", ctx)
    run_algorithm("detect_lines", ctx, {"catalog": "balmer"})
    run_algorithm("fit_gaussian_line", ctx, {"line_center_angstrom": 6562.79})
    
    Now the dashboard can show quality flags and identified lines without writing more code.
  • Provenance & reproducibility - ctx.history records every step (algorithm, version, parameters, hashes). You can quote "this measurement was produced with spectro-kernel normalize_percentile v1.0.0" in a paper.
  • MCP-ready - the same dashboard can offer the same analysis to an agent later, no extra work.

Two integration shapes

flowchart LR
    subgraph LIB["A · Library mode - same process"]
        D1["BeSS backend (FastAPI)"] -->|import spectro_kernel| K1["spectro-kernel<br/>(in-process)"]
    end
    subgraph REMOTE["B · MCP mode - kernel hosted separately"]
        D2["BeSS backend"] -->|HTTPS / MCP| K2["spectro-mcp on DigitalOcean<br/>(one shared kernel)"]
    end

Mode A - import the library (the snippet above). Simplest, fastest, no network. The kernel runs in the dashboard's own Python process. Pick this when the dashboard is already Python and you don't need to share a kernel across services.

Mode B - talk to the remote MCP server. The dashboard calls a cloud-hosted spectro-kernel via the MCP protocol. Useful when the dashboard is not Python (Node/Go/Rust), or when several services should share one hardened kernel instance.

# Mode B sketch - Python backend, kernel lives on DigitalOcean
from fastmcp import Client

KERNEL = "https://spectro-mcp.example/mcp"

async def parse_fits_spectrum(url):
    async with Client(KERNEL) as c:
        sid = (await c.call_tool("create_session", {})).data["session_id"]
        await c.call_tool("load_spectrum", {"session_id": sid, "path": url})
        await c.call_tool("normalize_percentile",
                          {"session_id": sid, "params": {"percentile": 95.0}})
        preview = (await c.call_tool("get_spectrum_preview", {"session_id": sid})).data
        await c.call_tool("end_session", {"session_id": sid})
        return preview

The result is identical to Mode A - same algorithm, same parameters, same numbers. That parity is the whole point.

  1. Replace the FITS parser only (1 file).
  2. Replace the normalisation (1 line).
  3. Add snr_der and surface the value in the UI.
  4. Add detect_lines + an overlay of identified lines.
  5. Optionally pivot to Mode B once the cloud kernel is up.

Each step is independent, reversible, and visible in the UI within minutes.