spectro-kernel 0.6.0__tar.gz → 0.6.2__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.
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/.gitignore +8 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/CHANGELOG.md +394 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/PKG-INFO +37 -5
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/README.md +30 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/concepts/algorithms.md +1 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/contributing.md +3 -3
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/getting-started.md +1 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/index.md +1 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/reference.md +1 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/tutorials/discover-the-catalogue.md +1 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/tutorials/first-spectrum.md +1 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/tutorials/long-slit-reduction.md +1 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/usage/mcp.md +49 -3
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/why.md +1 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/pyproject.toml +10 -4
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/advanced/disentangle_sb2.py +63 -13
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/advanced/doppler_tomogram.py +32 -9
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/classification/classify_template_chi2.py +74 -14
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/classification/pickles_atlas.py +9 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/compare_normalisations.py +7 -4
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_edges.py +32 -4
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_max.py +13 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_percentile.py +11 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/subtract_continuum.py +12 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/air_vacuum.py +34 -9
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/extinction_correct_easyspec.py +31 -34
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/fit_telluric_scaling.py +9 -3
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/flux_calibrate_easyspec.py +59 -60
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/remove_telluric.py +45 -17
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/response_from_standard.py +36 -20
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/synth_telluric.py +17 -9
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_band_power.py +22 -14
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_continuum_subtracted.py +13 -2
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_lick_indices.py +16 -2
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_log_lambda.py +23 -12
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_pretrained.py +11 -2
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_remote.py +31 -3
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_spectrum.py +16 -5
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_wavelets.py +12 -2
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_hdf5.py +9 -2
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/boxcar.py +92 -31
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/detect_trace.py +9 -4
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/easyspec_extract.py +64 -52
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_fits.py +14 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/kinematics/rotation_curve.py +10 -2
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/_line_flux.py +11 -6
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/detect.py +21 -3
- spectro_kernel-0.6.2/src/spectro_kernel/algorithms/lines/equivalent_width.py +190 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/vr_ratio.py +11 -4
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/nebular/bpt_line_ratios.py +26 -6
- spectro_kernel-0.6.2/src/spectro_kernel/algorithms/nebular/oiii_electron_temperature.py +324 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/nebular/sii_electron_density.py +27 -5
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/compare_snr_methods.py +6 -3
- spectro_kernel-0.6.2/src/spectro_kernel/algorithms/quality/snr_edge.py +102 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/snr_linear_fit.py +38 -5
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/_easyspec_apply.py +16 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/_easyspec_helpers.py +67 -12
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/dark_combine.py +32 -6
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/denoise_2d.py +51 -18
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_bias.py +1 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_cosmic_ray.py +4 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_dark.py +1 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_flat.py +1 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_flat_normalize.py +4 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_bias.py +4 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_dark.py +4 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/extract_spectrum_sum.py +13 -3
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/flat_combine.py +16 -3
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/flat_normalize.py +24 -7
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/subtract_sky_2d.py +37 -19
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/wavelength_calibrate.py +24 -4
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/cross_correlate.py +131 -17
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/fit_keplerian_orbit.py +64 -18
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/redshift_lines.py +28 -7
- spectro_kernel-0.6.2/src/spectro_kernel/algorithms/smoothing/_nan_bridge.py +32 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/smoothing/compare_smoothings.py +6 -4
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/smoothing/smooth_gaussian.py +25 -3
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/smoothing/smooth_savgol.py +33 -3
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/stacking/merge_echelle_orders.py +6 -3
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/stacking/stack.py +40 -8
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/timeseries/lomb_scargle.py +20 -4
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/timeseries/phase_fold.py +12 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/clip_sigma.py +8 -3
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/combine_arithmetic.py +7 -2
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/resample.py +4 -2
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/resample_flux_conserving.py +29 -5
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/arc_geometry.py +28 -7
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/easyspec_wavelength.py +20 -25
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/match_lamp.py +2 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/solar.py +18 -8
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/cli.py +8 -3
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/embeddings.py +13 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/io/fits.py +27 -5
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/pipeline.py +20 -3
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/registry.py +61 -17
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/types/context.py +53 -1
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/types/image.py +18 -2
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/types/spectrum.py +46 -4
- spectro_kernel-0.6.2/src/spectro_kernel/url_safety.py +301 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/version.py +1 -1
- spectro_kernel-0.6.2/src/spectro_mcp/__main__.py +211 -0
- spectro_kernel-0.6.2/src/spectro_mcp/auth.py +172 -0
- spectro_kernel-0.6.2/src/spectro_mcp/auto_tools.py +888 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_mcp/server.py +21 -3
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_mcp/session.py +77 -22
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_mcp/url_safety.py +11 -5
- spectro_kernel-0.6.2/tests/unit/test_aperture_photometry.py +70 -0
- spectro_kernel-0.6.2/tests/unit/test_catalog_queries_mocked.py +208 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_classify_template_chi2.py +85 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_cli.py +41 -0
- spectro_kernel-0.6.2/tests/unit/test_combine.py +118 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_combine_arithmetic.py +23 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_continuum.py +46 -0
- spectro_kernel-0.6.2/tests/unit/test_corrections.py +183 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_cross_correlate_extension.py +110 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_dark_flat_combine.py +50 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_denoise_2d.py +60 -0
- spectro_kernel-0.6.2/tests/unit/test_disentangle_sb2.py +91 -0
- spectro_kernel-0.6.2/tests/unit/test_doppler_tomogram.py +145 -0
- spectro_kernel-0.6.2/tests/unit/test_easyspec_sessions.py +132 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_embedding.py +20 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_embedding_tier1.py +62 -0
- spectro_kernel-0.6.2/tests/unit/test_export_hdf5.py +90 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_extract_boxcar.py +97 -0
- spectro_kernel-0.6.2/tests/unit/test_extract_spectrum_sum.py +67 -0
- spectro_kernel-0.6.2/tests/unit/test_flux_calibrate_easyspec.py +88 -0
- spectro_kernel-0.6.2/tests/unit/test_io.py +140 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_line_profiles.py +56 -0
- spectro_kernel-0.6.2/tests/unit/test_lines.py +151 -0
- spectro_kernel-0.6.2/tests/unit/test_mcp_http_entry.py +204 -0
- spectro_kernel-0.6.2/tests/unit/test_mcp_param_guard.py +204 -0
- spectro_kernel-0.6.2/tests/unit/test_mcp_production.py +255 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_mcp_security.py +105 -3
- spectro_kernel-0.6.2/tests/unit/test_mcp_tools_extra.py +304 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_measure_arc_geometry.py +86 -2
- spectro_kernel-0.6.2/tests/unit/test_misc_algorithms.py +245 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_nebular_diagnostics.py +115 -8
- spectro_kernel-0.6.2/tests/unit/test_output_produces_contract.py +179 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_pipeline.py +38 -0
- spectro_kernel-0.6.2/tests/unit/test_quality.py +96 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_redshift_lines.py +20 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_registry.py +76 -1
- spectro_kernel-0.6.2/tests/unit/test_resample_flux_conserving.py +87 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_response_from_standard.py +24 -0
- spectro_kernel-0.6.2/tests/unit/test_rv_precision_bouchy.py +94 -0
- spectro_kernel-0.6.2/tests/unit/test_smoothing.py +125 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_timeseries.py +42 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_transforms.py +18 -0
- spectro_kernel-0.6.2/tests/unit/test_types.py +173 -0
- spectro_kernel-0.6.2/tests/unit/test_viz.py +106 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_wavelength_calibration_solar.py +37 -0
- spectro_kernel-0.6.2/tools/docs/__init__.py +6 -0
- spectro_kernel-0.6.2/tools/docs/build.py +82 -0
- spectro_kernel-0.6.2/tools/docs/generate.py +25 -0
- spectro_kernel-0.6.2/tools/docs/generate_catalogue.py +482 -0
- spectro_kernel-0.6.2/tools/docs/generate_llms.py +134 -0
- spectro_kernel-0.6.2/tools/docs/netlify.toml +20 -0
- spectro_kernel-0.6.0/docs/gen_catalogue.py +0 -124
- spectro_kernel-0.6.0/playground/README.md +0 -99
- spectro_kernel-0.6.0/src/spectro_kernel/algorithms/lines/equivalent_width.py +0 -84
- spectro_kernel-0.6.0/src/spectro_kernel/algorithms/nebular/oiii_electron_temperature.py +0 -218
- spectro_kernel-0.6.0/src/spectro_kernel/algorithms/quality/snr_edge.py +0 -65
- spectro_kernel-0.6.0/src/spectro_kernel/url_safety.py +0 -142
- spectro_kernel-0.6.0/src/spectro_mcp/__main__.py +0 -127
- spectro_kernel-0.6.0/src/spectro_mcp/auth.py +0 -88
- spectro_kernel-0.6.0/src/spectro_mcp/auto_tools.py +0 -390
- spectro_kernel-0.6.0/tests/reference/data/.gitkeep +0 -1
- spectro_kernel-0.6.0/tests/reference/data/README.md +0 -57
- spectro_kernel-0.6.0/tests/reference/data/sun/sun_reference_stis_002.fits +0 -3
- spectro_kernel-0.6.0/tests/reference/data/vega/alpha_lyr_stis_011.fits +0 -3
- spectro_kernel-0.6.0/tests/unit/test_combine.py +0 -61
- spectro_kernel-0.6.0/tests/unit/test_corrections.py +0 -60
- spectro_kernel-0.6.0/tests/unit/test_doppler_tomogram.py +0 -79
- spectro_kernel-0.6.0/tests/unit/test_io.py +0 -54
- spectro_kernel-0.6.0/tests/unit/test_lines.py +0 -50
- spectro_kernel-0.6.0/tests/unit/test_mcp_production.py +0 -140
- spectro_kernel-0.6.0/tests/unit/test_misc_algorithms.py +0 -124
- spectro_kernel-0.6.0/tests/unit/test_quality.py +0 -38
- spectro_kernel-0.6.0/tests/unit/test_smoothing.py +0 -41
- spectro_kernel-0.6.0/tests/unit/test_types.py +0 -64
- spectro_kernel-0.6.0/tests/unit/test_viz.py +0 -36
- spectro_kernel-0.6.0/website/README.md +0 -71
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/LICENSE +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/concepts/architecture.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/concepts/data-types.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/concepts/pipelines.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/cookbook/bess-dashboard.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/cookbook/multi-star-viewer.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/cookbook/web-playground.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_ew_timeseries.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_halpha_phase_stack.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_halpha_timeseries.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_overlay.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_pc1_vs_phase.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_pca_2d.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_pca_3d.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_periodogram.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_phase_folded.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_rv_timeseries.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_similarity.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_similarity_date.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_halpha_phase_stack.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_pc1_vs_phase.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_pca_2d.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_pca_3d.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_periodogram.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_phase_folded.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_rv_timeseries.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_similarity_date.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_similarity_phase.png +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/notebooks/alpha-cyg-time-series.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/notebooks/alpha-dra-binary-period.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/notebooks/aurora-line-monitor.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/notebooks/be-star-variability.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/notebooks/claude-mcp-end-to-end.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/notebooks/exoplanet-transit-rv.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/notebooks/full-reduction-walkthrough.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/notebooks/native-vs-easyspec-showdown.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/notebooks/your-first-sb2.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/tutorials/add-an-algorithm.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/tutorials/analyse-a-spectrum.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/usage/cli.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/usage/library.md +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/adapters/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/adapters/easyspec.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/_common.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/advanced/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/advanced/aperture_photometry.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/catalogs/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/catalogs/gaia.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/catalogs/simbad.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/catalogs/vizier.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/classification/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_polynomial.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_region.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/barycentric.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/doppler_shift.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/_bess_contract.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_csv.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_fits.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_fits_bess.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_votable.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/optimal.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/sky_lateral_bands.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_ascii.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_echelle.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_sdss.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_votable.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/kinematics/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/_profiles.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/catalogs.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/compare_line_fits.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/fit_gaussian.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/fit_lorentzian.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/fit_voigt.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/nebular/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/snr_der.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/validate_bess_header.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/bias_combine.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/clip_cosmic_rays.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/dark_subtract.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/geometry.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/measure.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/precision_bouchy.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/smoothing/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/stacking/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/timeseries/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/extract_region.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/mask_range.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/plot_3d_surface.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/plot_animation.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/plot_dynamic_spectrum.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/plot_plotly.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/in_situ.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/lamp_atlas.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/reidentify.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/base.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/errors.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/io/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/io/ascii.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/io/votable.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/balmer_quick.yaml +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/embed_quick.yaml +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/quality_report.yaml +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/rv_quick.yaml +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/snr_check.yaml +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/time_series_overview.yaml +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/reduction/full_reduction_easyspec.yaml +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/loader.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/py.typed +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/types/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/types/catalog.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/types/enums.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/types/history.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/types/line.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/types/timeseries.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_mcp/__init__.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_mcp/observability.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_mcp/py.typed +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/conftest.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/reference/conftest.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/reference/test_known_answers.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/reference/test_sun.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/reference/test_vega.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_barycentric_bjd.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_base.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_detect_lines_blind.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_detect_trace.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_easyspec_apply_staging.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_easyspec_wrappers.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_embedding_pretrained.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_embedding_remote_lick.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_export_fits_bess.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_extract_optimal.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_geometry_corrections.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_lamp_atlas_match.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_mcp.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_no_circular_imports.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_normalize_to_region.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_read_sdss.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_reidentify.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_rotation_curve.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_sky_lateral_bands.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_url_safety_kernel.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_validate_bess_header.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_vr_ratio.py +0 -0
- {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_wavelength_calibration_in_situ.py +0 -0
|
@@ -164,6 +164,11 @@ venv.bak/
|
|
|
164
164
|
# Rope project settings
|
|
165
165
|
.ropeproject
|
|
166
166
|
|
|
167
|
+
# documentation - generated by tools/docs/generate.py
|
|
168
|
+
docs/algorithms/
|
|
169
|
+
docs/ai/
|
|
170
|
+
docs/llms.txt
|
|
171
|
+
|
|
167
172
|
# mkdocs documentation
|
|
168
173
|
/site
|
|
169
174
|
|
|
@@ -222,6 +227,9 @@ __marimo__/
|
|
|
222
227
|
# GitHub, not in the PyPI sdist, not on the docs site.
|
|
223
228
|
stuff/
|
|
224
229
|
|
|
230
|
+
# Node — the website's dependencies (never committed, never in the sdist).
|
|
231
|
+
node_modules/
|
|
232
|
+
|
|
225
233
|
# Playground — personal FITS files, generated plots and the local venv.
|
|
226
234
|
# The example script and README ARE tracked; only user data + outputs are not.
|
|
227
235
|
playground/datas/
|
|
@@ -6,6 +6,400 @@ Until `1.0.0` the public API may change between minor versions.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.6.2] — 2026-09-19
|
|
10
|
+
|
|
11
|
+
A documentation and web release : the docs move to a generated,
|
|
12
|
+
agent-readable site at https://docs.spectrokernel.io/, the landing page is
|
|
13
|
+
rebuilt around the open MCP server, and the server gains CORS so the web
|
|
14
|
+
playground can use it. No algorithm changes.
|
|
15
|
+
|
|
16
|
+
### MCP server
|
|
17
|
+
|
|
18
|
+
- CORS for browser clients in HTTP mode : `SPECTRO_MCP_CORS_ORIGINS`
|
|
19
|
+
(default : the spectrokernel.io sites ; `*` or `none`), with the
|
|
20
|
+
`Mcp-Session-Id` header exposed, so the web playground on the landing
|
|
21
|
+
page can talk to the public server. Pre-flights are answered before the
|
|
22
|
+
API-key check.
|
|
23
|
+
|
|
24
|
+
### Documentation — new build pipeline and hosting (no package change)
|
|
25
|
+
|
|
26
|
+
- The site is now built with **Zensical** (the Material for MkDocs team's
|
|
27
|
+
successor engine, reading the unchanged `mkdocs.yml`) and published at
|
|
28
|
+
**https://docs.spectrokernel.io/** by Netlify, which builds it from the
|
|
29
|
+
repository (base directory `tools/docs`, see `tools/docs/netlify.toml`) ;
|
|
30
|
+
pull requests get a deploy preview and GitHub Actions keeps a strict build
|
|
31
|
+
check. `mkdocs build` keeps working as a fallback.
|
|
32
|
+
- The algorithm catalogue is generated *before* the build by
|
|
33
|
+
`tools/docs/generate_catalogue.py` (Zensical runs no MkDocs plugin such as
|
|
34
|
+
`gen-files`) : one page per algorithm under `/algorithms/<category>/<name>/`
|
|
35
|
+
(summary, backend, version, context reads/writes, parameter table, Python /
|
|
36
|
+
CLI / MCP call examples, references, related algorithms), a category index,
|
|
37
|
+
and a navigation tree consumed by `literate-nav`.
|
|
38
|
+
- Machine-readable layer for agents and tooling : `/ai/algorithms.json`
|
|
39
|
+
(validated at build time against `/ai/schemas/algorithms.schema.json`,
|
|
40
|
+
schema version 1, stamped with the package version), a curated `/llms.txt`
|
|
41
|
+
derived from the navigation, and the Markdown source of every page served
|
|
42
|
+
next to its HTML (`<page>/index.md`). Netlify headers set the content types
|
|
43
|
+
and CORS on `/ai/*`.
|
|
44
|
+
- `tools/docs/build.py` reproduces the CI build locally (generators + strict
|
|
45
|
+
build + post-processing) ; `tools/docs/generate.py && zensical serve` for
|
|
46
|
+
live preview. Generated files (`docs/algorithms/`, `docs/ai/`,
|
|
47
|
+
`docs/llms.txt`) are git-ignored. `.readthedocs.yaml` and
|
|
48
|
+
`docs/gen_catalogue.py` are removed ; the `docs` extra now installs
|
|
49
|
+
`zensical`, `mkdocstrings[python]`, `mkdocs-material`,
|
|
50
|
+
`mkdocs-literate-nav`, `jsonschema`.
|
|
51
|
+
|
|
52
|
+
## [0.6.1] — 2026-09-19
|
|
53
|
+
|
|
54
|
+
A correctness and hardening release driven by a full audit of the
|
|
55
|
+
library (`stuff/spectro_kernel_audit_19_09_26.md`, local) : one
|
|
56
|
+
critical hole in the public MCP server, ten numerical bugs in the
|
|
57
|
+
catalogue, and a long tail of robustness, reference and test fixes.
|
|
58
|
+
No algorithm, parameter, context key or metric key is renamed or
|
|
59
|
+
removed. Bricks whose numerical output changed carry a MAJOR bump of
|
|
60
|
+
their own `version` attribute and the measured impact is listed below,
|
|
61
|
+
so downstream pipelines can decide when to re-run. Catalogue unchanged
|
|
62
|
+
at 115 algorithms ; 399 → 620+ tests.
|
|
63
|
+
|
|
64
|
+
### Security — MCP server (`spectro-mcp --http`)
|
|
65
|
+
|
|
66
|
+
- **Critical fix : every algorithm tool now goes through the path /
|
|
67
|
+
URL guards.** Until v0.6.0 only `load_spectrum` was guarded ; the
|
|
68
|
+
auto-generated tools ran `run_algorithm` bare, so in HTTP mode
|
|
69
|
+
`read_ascii_spectrum` / `read_fits` / `read_votable_spectrum` /
|
|
70
|
+
`read_echelle_fits` / `read_sdss_spectrum` could read any server
|
|
71
|
+
file, the `export_*` bricks could write anywhere, the EasySpec
|
|
72
|
+
wrappers could stage any directory, and `read_fits` /
|
|
73
|
+
`embed_remote` could reach loopback and metadata addresses.
|
|
74
|
+
`make_tool` now validates every parameter named `path`, `*_path`,
|
|
75
|
+
`*_paths`, `*_dir`, `model_path`, `target_path`, `endpoint`,
|
|
76
|
+
`atlas_dir` : local paths are refused under
|
|
77
|
+
`SPECTRO_MCP_DENY_LOCAL_PATHS` (set by `--http`), URLs go through the
|
|
78
|
+
SSRF guard, and URLs handed to a `read_*` brick are downloaded by the
|
|
79
|
+
MCP layer (no redirects, 256 MiB cap, wall-clock budget, concurrency
|
|
80
|
+
cap, pixel-count check) before the brick sees a temp file. `run_preset`
|
|
81
|
+
only accepts bundled preset names in HTTP mode. Regression tests drive
|
|
82
|
+
the real tool registry with the deny flag set.
|
|
83
|
+
- `embed_remote` v1.0.1 : the server-side `SPECTRO_EMBED_API_KEY` is
|
|
84
|
+
no longer attached to an arbitrary caller-supplied endpoint ; in HTTP
|
|
85
|
+
mode it is sent only to hosts listed in `SPECTRO_EMBED_ENDPOINT_ALLOW`.
|
|
86
|
+
- **HTTP mode now requires an explicit posture** : the server exits
|
|
87
|
+
(code 2) unless `SPECTRO_MCP_API_KEY` is set (closed) or
|
|
88
|
+
`SPECTRO_MCP_ALLOW_ANONYMOUS=1` is set on purpose (open). The public
|
|
89
|
+
demo server is open : `.do/app.yaml` declares
|
|
90
|
+
`SPECTRO_MCP_ALLOW_ANONYMOUS=1`, `SPECTRO_MCP_RATE_PER_MINUTE=60` and
|
|
91
|
+
`SPECTRO_MCP_TRUSTED_PROXY=1` (the closed variant is documented next to
|
|
92
|
+
it). README and `docs/usage/mcp.md` gain the full environment-variable
|
|
93
|
+
table. **Add `SPECTRO_MCP_ALLOW_ANONYMOUS=1` in the DigitalOcean console
|
|
94
|
+
before deploying this version**, otherwise the container refuses to
|
|
95
|
+
start.
|
|
96
|
+
- Key carriers : besides `X-API-Key`, the middleware accepts
|
|
97
|
+
`Authorization: Bearer <key>` and, when `SPECTRO_MCP_ALLOW_KEY_IN_URL=1`,
|
|
98
|
+
`?api_key=…` in the URL, which is what claude.ai / Claude Desktop
|
|
99
|
+
custom connectors need (they cannot send headers). The parameter is
|
|
100
|
+
removed from the request scope after validation so uvicorn's access log
|
|
101
|
+
never records it.
|
|
102
|
+
- Rate limiting : the API-key middleware is now outermost (401 before
|
|
103
|
+
any bucket is allocated), buckets are keyed on the validated key (or
|
|
104
|
+
the client IP when auth is off), idle buckets are evicted and the
|
|
105
|
+
table is capped ; `SPECTRO_MCP_TRUSTED_PROXY=1` makes uvicorn honour
|
|
106
|
+
`X-Forwarded-For` behind a load balancer. Key comparison is
|
|
107
|
+
constant-time (`hmac.compare_digest`).
|
|
108
|
+
- Memory : live sessions are capped (`SPECTRO_MCP_MAX_SESSIONS`, default
|
|
109
|
+
200, LRU eviction, expired sessions purged on every `create`) ;
|
|
110
|
+
downloaded FITS files are refused above `SPECTRO_MCP_MAX_PIXELS`
|
|
111
|
+
(default 32 M elements, i.e. 256 MB as float64) before any conversion ;
|
|
112
|
+
`get_spectrum_data` returns at most `max_points` samples (default
|
|
113
|
+
20 000, stride-downsampled, with new fields `downsampled`,
|
|
114
|
+
`npix_total`, `npix_returned`, `stride` ; `max_points=0` returns
|
|
115
|
+
everything).
|
|
116
|
+
- Downloads : per-download wall-clock budget
|
|
117
|
+
(`SPECTRO_MCP_DOWNLOAD_BUDGET_S`, 120 s, enforced inside the read loop
|
|
118
|
+
so a drip-feeding server cannot pin a worker thread) and a concurrency
|
|
119
|
+
cap (`SPECTRO_MCP_MAX_CONCURRENT_DOWNLOADS`, 4).
|
|
120
|
+
- Information disclosure : `FastMCP(mask_error_details=True)` in HTTP
|
|
121
|
+
mode ; `get_algorithm_source` withholds the absolute install path
|
|
122
|
+
(`path: null`, repo-relative path and GitHub URL kept).
|
|
123
|
+
- Every tool response is now strict JSON : `NaN` / `±inf` metrics are
|
|
124
|
+
serialised as `null`, numpy scalars as Python numbers (both channels).
|
|
125
|
+
- `spectro_kernel.url_safety` (library guard) : refuses every
|
|
126
|
+
non-global address (adds CGNAT 100.64/10 and friends), judges NAT64
|
|
127
|
+
(`64:ff9b::/96`) and IPv4-mapped addresses on the embedded IPv4 (a
|
|
128
|
+
public host resolved through a DNS64 resolver was wrongly refused —
|
|
129
|
+
this was the one failing test), and `open_safe_url` pins the
|
|
130
|
+
connection to the validated address (one DNS lookup, closes the
|
|
131
|
+
rebinding window). The public-URL test no longer touches the network.
|
|
132
|
+
- Dockerfile : the dependency layer is built from `pyproject.toml`
|
|
133
|
+
before `COPY src`, so source-only changes no longer reinstall the
|
|
134
|
+
scientific stack. Same specifiers, no lockfile.
|
|
135
|
+
|
|
136
|
+
### Core
|
|
137
|
+
|
|
138
|
+
- Registry discovery is thread-safe : `ensure_discovered()` runs under a
|
|
139
|
+
lock and flags completion only after the import walk. A thread pool
|
|
140
|
+
hitting the registry cold used to see `AlgorithmNotFoundError` on 5 of
|
|
141
|
+
6 threads. `clear_registry()` followed by any registry call now
|
|
142
|
+
restores the full catalogue.
|
|
143
|
+
- Provenance hashing is memoised on array identity : a 4096² frame
|
|
144
|
+
hashes in 4 µs after the first 390 ms, so a six-step reduction on ten
|
|
145
|
+
raw frames no longer spends ~20 s re-hashing untouched inputs.
|
|
146
|
+
Digests are byte-identical to v0.6.0 for the same content. In-place
|
|
147
|
+
mutation of `Spectrum1D` / `ImageFrame` arrays after hashing is
|
|
148
|
+
unsupported (documented ; no brick does it).
|
|
149
|
+
- Provenance correctness : `Spectrum1D.content_hash` now covers `mask`
|
|
150
|
+
and `WorkContext.hash_state` covers non-empty `extras`, `periodograms`,
|
|
151
|
+
`light_curves` and `exports`, so `mask_range`, `extract_sky_lateral_bands`,
|
|
152
|
+
`response_from_standard`, `lomb_scargle`, `synth_telluric`, `export_*`
|
|
153
|
+
no longer record `output_hash == input_hash`. Digests change only for
|
|
154
|
+
contexts that carry those slots. The MCP `load_spectrum*` tools append
|
|
155
|
+
a `load_spectrum` step when they replace the spectrum of a session
|
|
156
|
+
that already has history, so the input-hash chain stays valid.
|
|
157
|
+
- `PipelineBuilder.from_config` : `params: null` is accepted ; a string
|
|
158
|
+
`depends_on` or a non-mapping `params` raise `PipelineError` instead of
|
|
159
|
+
a `TypeError` / silent character split. The CLI `pipeline` report is
|
|
160
|
+
built from the audit trail, so after an early stop in DAG mode it no
|
|
161
|
+
longer blames a step that never ran.
|
|
162
|
+
- `Spectrum1D.wavelength_min/max` return `nan` on empty arrays ;
|
|
163
|
+
`preview()` never raises ; the MCP loaders return `{"error": …}` for
|
|
164
|
+
unreadable or empty inputs.
|
|
165
|
+
- Packaging : the sdist no longer ships `website/` (153
|
|
166
|
+
`node_modules` files had leaked in), `playground/` or the Git-LFS
|
|
167
|
+
pointer stubs of `tests/reference/data` ; `node_modules/` is
|
|
168
|
+
git-ignored. CI gains a `test-extras` job installing `[all,dev]`, so
|
|
169
|
+
the EasySpec, plotly, photutils, h5py, astroquery and pywavelets tests
|
|
170
|
+
actually run on GitHub (they were always skipped there).
|
|
171
|
+
|
|
172
|
+
### Fixed — numerical (MAJOR bump of the brick)
|
|
173
|
+
|
|
174
|
+
- **`oiii_electron_temperature` 1.0.0 → 2.0.0.** The 7.90 prefactor of
|
|
175
|
+
Osterbrock & Ferland 2006 eq. 5.4 applies to
|
|
176
|
+
`[F(4959)+F(5007)]/F(4363)` ; the brick applied it to the 5007-only
|
|
177
|
+
ratio, biasing Te high by 9–16 % across the validity range. λ4959 is
|
|
178
|
+
now fitted too (new optional `lambda_4959`, default 4958.911 Å) with a
|
|
179
|
+
fallback to `F(5007)·(1+1/2.98)` (Storey & Zeippen 2000) when it
|
|
180
|
+
cannot be fitted or its measured 5007/4959 falls outside [2.0, 4.5].
|
|
181
|
+
Measured : spectrum with 5007/4363 = 100 → doublet ratio 133.6 →
|
|
182
|
+
**Te 12 961 K → 11 635 K** ; 5007/4363 = 50 → 17 830 → 15 413 K ;
|
|
183
|
+
200 → 10 181 → 9 344 K. `metrics.ratio` is now the doublet ratio ;
|
|
184
|
+
additive `oiii_ratio`, `oiii_ratio_doublet`, `ratio_5007_4363`,
|
|
185
|
+
`flux_4959`, `ratio_5007_4959`, `oiii_doublet_from_fit`,
|
|
186
|
+
`extras.oiii_doublet_source`. The acceptance test carries the
|
|
187
|
+
Osterbrock values.
|
|
188
|
+
- **`doppler_tomogram` 1.0.0 → 2.0.0.** Back-projection used
|
|
189
|
+
`v = γ − Vx sin 2πφ + Vy cos 2πφ` ; Marsh & Horne 1988 eq. 1 is
|
|
190
|
+
`v = γ − Vx cos 2πφ + Vy sin 2πφ`. A spot with RV curve γ + K sin 2πφ
|
|
191
|
+
(the donor, MH position (0, +K)) landed at (−K, 0) : v1 maps are the
|
|
192
|
+
v2 maps rotated `(Vx, Vy) → (Vy, −Vx)`. Test generator switched to the
|
|
193
|
+
MH convention with an explicit (0, +K) assertion.
|
|
194
|
+
|
|
195
|
+
- **`measure_arc_geometry` 1.0.0 → 2.0.0.** The returned `slant_deg`
|
|
196
|
+
had the opposite sign of what `correct_slant_affine` expects, so the
|
|
197
|
+
documented measure → correct chain doubled the slant instead of
|
|
198
|
+
removing it (synthetic +0.05 px/row arc : residual +0.10 px/row after
|
|
199
|
+
correction, now 0.0001). The sign is negated in the measuring brick
|
|
200
|
+
only ; `correct_slant_affine` is untouched, so hand-tuned preset angles
|
|
201
|
+
keep their meaning. Round-trip test added (the smile half of the chain
|
|
202
|
+
was already exact).
|
|
203
|
+
- **`extract_spectrum_easyspec` 1.0.0 → 2.0.0.** The uint16
|
|
204
|
+
`BZERO`/`BSCALE` leak fixed in v0.2.1 for the `_easyspec_apply`
|
|
205
|
+
wrappers was still present in the extraction wrapper's own staging :
|
|
206
|
+
a uint16 science frame came back +32768. Structural and scaling
|
|
207
|
+
keywords are now filtered with the same `_filter_structural` helper.
|
|
208
|
+
- **`extract_spectrum_sum` 1.0.0 → 2.0.0.** Its pixel axis was 1-based
|
|
209
|
+
(`arange(n) + 1`) while every other extractor, the lamp identifiers
|
|
210
|
+
and `wavelength_calibrate_polynomial` are 0-based : a sum-extracted
|
|
211
|
+
lamp calibrated through `match_lamp_lines` was off by one pixel
|
|
212
|
+
(+0.9 Å at 0.9 Å/px) with a clean RMS. Now 0-based ; the convention is
|
|
213
|
+
stated in both bricks.
|
|
214
|
+
- **`extract_spectrum_boxcar` 1.0.0 → 2.0.0.** The sky estimate was
|
|
215
|
+
the mean of the two flanking windows with an empty window counted as
|
|
216
|
+
zero, and the windows are bounded by the *median* trace, so a tilted
|
|
217
|
+
trace halved the sky on many columns (audit scenario : 163 of 400
|
|
218
|
+
columns at sky = 50 instead of 100 ; now 0). Only non-empty windows
|
|
219
|
+
are averaged ; `metrics.boxcar_columns_without_sky` counts the columns
|
|
220
|
+
with neither. `shift_y_pixels` must now exceed `trace_half_width`
|
|
221
|
+
(it silently produced a zero sky otherwise) and negative values are
|
|
222
|
+
refused ; the two contradictory docstrings agree. Untilted traces are
|
|
223
|
+
bit-identical to v1.
|
|
224
|
+
- **`stack_spectra` 1.0.0 → 2.0.0.** `np.interp` extrapolated each
|
|
225
|
+
spectrum's edge value across the whole reference grid : two spectra
|
|
226
|
+
covering 4000–5000 Å (flux 1) and 4500–5500 Å (flux 3) averaged to 2.0
|
|
227
|
+
at 4000 Å. Samples outside a spectrum's coverage are now NaN and
|
|
228
|
+
ignored by the combiners (also for `sum`) ; `metrics.n_nan_samples`
|
|
229
|
+
reports how many reference samples had no contributor. The brick also
|
|
230
|
+
gains a `backend` and a reference (Tody 1993, IRAF `scombine`).
|
|
231
|
+
- **`dark_combine` 1.0.0 → 2.0.0, `flat_combine` 1.0.0 → 2.0.0.** The
|
|
232
|
+
sigma-clipped mean floored the MAD scale at machine epsilon, so any
|
|
233
|
+
stack with MAD = 0 (integer-ADU darks with ~1 ADU read noise) rejected
|
|
234
|
+
every value that differed from the median and degenerated to it
|
|
235
|
+
(`[10,10,10,11,12]` → 10.0). When MAD is zero the scale now falls back
|
|
236
|
+
to the mean absolute deviation about the median (Gaussian-scaled),
|
|
237
|
+
which still rejects a cosmic ray in that regime (`[10,10,10,11,1000]`
|
|
238
|
+
→ 10.25, whereas a `nanstd` fallback would have kept it). Result :
|
|
239
|
+
10.6. Stacks with MAD > 0 are unchanged.
|
|
240
|
+
- **`wavelength_calibration_solar` 1.0.0 → 2.0.0.** When several peaks
|
|
241
|
+
matched the same Fraunhofer line, the tie-break compared the catalogue
|
|
242
|
+
wavelength with itself and always kept the first peak ; it now keeps
|
|
243
|
+
the closest (a spurious dip 6 Å blueward of Hα no longer steals the
|
|
244
|
+
identification). New artifact `matched_pixels`.
|
|
245
|
+
- **`read_fits` 1.0.0 → 2.0.0 (`io/fits.py`).** A `CTYPE1` ending in
|
|
246
|
+
`-LOG` (`WAVE-LOG`, `AWAV-LOG`) was decoded as IRAF log10, giving
|
|
247
|
+
`inf` everywhere ; it now follows WCS Paper III (Greisen et al. 2006)
|
|
248
|
+
λ = CRVAL1·exp((p − CRPIX1)·CDELT1/CRVAL1), verified against
|
|
249
|
+
`astropy.wcs` to 1e-9. `10**` applies only to `DC-FLAG = 1`. A `-LOG`
|
|
250
|
+
axis with `CRVAL1 = 0` raises `IOReadError`.
|
|
251
|
+
|
|
252
|
+
### Changed — analysis-side bricks (additive, no numerical change for valid input)
|
|
253
|
+
|
|
254
|
+
- `cross_correlate_rv` 2.0.0 → 2.1.0 : `n_grid=None` derives the
|
|
255
|
+
log-λ grid from the finest native sampling of the two spectra (capped
|
|
256
|
+
at 2^18). The default stays 4096, which is badly under-sampled for
|
|
257
|
+
high-resolution input : on a 30 000-pixel synthetic (σ_line 0.15 Å,
|
|
258
|
+
S/N 100) an injected +10 km/s comes back as +0.9 ± 0.15 km/s at the
|
|
259
|
+
default 41 km/s step and +9.97 ± 0.02 km/s with `n_grid=None`. σ_v
|
|
260
|
+
(Tonry & Davis) is documented as a lower bound at coarse grids. New
|
|
261
|
+
`metrics.n_grid_used`, `metrics.velocity_step_kms`,
|
|
262
|
+
`extras.template_source` ; the `ctx.spectra[0]` template fallback is
|
|
263
|
+
deprecated (`DeprecationWarning` + message) because it contradicts the
|
|
264
|
+
"second spectrum lives in `ctx.extras`" rule.
|
|
265
|
+
- `equivalent_width` 1.0.0 → 1.1.0 : σ(EW) per Vollmann & Eversberg
|
|
266
|
+
2006 (AN 327, 862) eq. 7 as `metrics.equivalent_width_error_angstrom`
|
|
267
|
+
when the spectrum carries uncertainties ; new optional `label` with
|
|
268
|
+
per-label keys next to the flat ones ; references added.
|
|
269
|
+
- `fit_keplerian_orbit` 1.0.0 → 1.1.0 : an explicit `0.0` initial guess
|
|
270
|
+
is honoured (`or` → `is None`) ; additive `reduced_chi_squared` when
|
|
271
|
+
RV uncertainties are present ; ω bounds and the unweighted
|
|
272
|
+
`chi_squared` documented as implemented.
|
|
273
|
+
- `disentangle_sb2` 1.0.0 → 1.1.0 : cites Bagnuolo & Gies 1991 (ApJ
|
|
274
|
+
376, 266) as the implemented scheme, documents the additive continuum
|
|
275
|
+
degeneracy, adds `metrics.disentangle_residual_rms` ; first numerical
|
|
276
|
+
test.
|
|
277
|
+
- `classify_template_chi2` 1.0.0 → 1.1.0 : `atlas_dir` (path) as an
|
|
278
|
+
alternative to the Python `atlas` object, so the brick is CLI/MCP
|
|
279
|
+
drivable ; `required_params` is now empty (one of the two is validated
|
|
280
|
+
at run time) ; per-template normalisation cached on the atlas
|
|
281
|
+
(identical numbers) ; `grid_lo/grid_hi/grid_step` documented as
|
|
282
|
+
reserved.
|
|
283
|
+
- `sii_electron_density` 1.0.0 → 1.1.0, `bpt_line_ratios` 1.0.0 →
|
|
284
|
+
1.1.0 : prefixed keys (`sii_ratio`, `sii_regime`, `bpt_method`, …)
|
|
285
|
+
next to the generic `ratio` / `regime` / `method`, which the three
|
|
286
|
+
nebular bricks used to overwrite in a pipeline.
|
|
287
|
+
- `lomb_scargle` 1.1.0 → 1.1.1 : the false-alarm probability is
|
|
288
|
+
evaluated on the frequency range actually searched (unchanged with
|
|
289
|
+
default bounds ; with `maximum_frequency=1.0` on the test series
|
|
290
|
+
1.85e-210 → 2.95e-211).
|
|
291
|
+
- `redshift_lines` 1.0.0 → 1.0.1 : `z_error` documented as the
|
|
292
|
+
population std of the per-line z ; descending axes and non-finite
|
|
293
|
+
samples handled (v1 answered "0 lines matched") ; needs ≥ 6 finite
|
|
294
|
+
samples.
|
|
295
|
+
|
|
296
|
+
### Changed — reduction-side bricks (additive / robustness)
|
|
297
|
+
|
|
298
|
+
- `resample_flux_conserving` 1.0.0 → 1.0.1 : failed for the kernel's
|
|
299
|
+
own default flux unit (`ADU`) and for `counts` / `transmission`
|
|
300
|
+
because `astropy.units.Unit(..., parse_strict="silent")` returns an
|
|
301
|
+
`UnrecognizedUnit` instead of raising ; units are now mapped
|
|
302
|
+
(`adu`, `count`, else dimensionless). First tests for the brick,
|
|
303
|
+
including flux conservation on a top-hat to 1e-6.
|
|
304
|
+
- `remove_telluric_division` 1.0.0 → 1.1.0 : when the default
|
|
305
|
+
`telluric_spectrum` key is absent it falls back to
|
|
306
|
+
`telluric_template_scaled` then `telluric_template`, so the documented
|
|
307
|
+
`synth_telluric → fit_telluric_scaling → remove_telluric_division`
|
|
308
|
+
chain works with defaults (in-band residual 0.40 → 0.03 on the test
|
|
309
|
+
case). Records `meta["telluric_template_key"]`.
|
|
310
|
+
- EasySpec wrappers (all PATCH) : sessions are serialised behind one
|
|
311
|
+
lock, user paths are resolved to absolute before the working
|
|
312
|
+
directory switch, `stage_frames` tolerates duplicate basenames, and
|
|
313
|
+
the four wrappers that did their own `os.chdir` now use the shared
|
|
314
|
+
context manager. Cheap parameter validation in
|
|
315
|
+
`flux_calibrate_easyspec` / `extinction_correct_easyspec` /
|
|
316
|
+
`extract_spectrum_boxcar` runs before the EasySpec import, so
|
|
317
|
+
refusals are reachable without it.
|
|
318
|
+
- `subtract_sky_2d` 1.0.0 → 1.0.1 : one 2-D `polyfit` instead of a
|
|
319
|
+
per-column loop (200×2048 : 0.21 s → 0.007 s, max |Δ| 6e-14, tested
|
|
320
|
+
against the verbatim v1 loop for orders 0–3).
|
|
321
|
+
- `outlier_rejection_mad_adaptive` 1.0.0 → 1.0.1 : rows are processed
|
|
322
|
+
in chunks sized from a 64 MB budget (a 2048², k = 5 frame needed
|
|
323
|
+
~0.8 GB transient) ; bit-identical output, tested.
|
|
324
|
+
- `export_hdf5` 1.0.1 (file-descriptor leak), `resample_linear` 1.0.1
|
|
325
|
+
(own header dict), `response_from_standard` / `flat_normalize` /
|
|
326
|
+
`flat_combine` docstrings aligned with the code (no masking, global
|
|
327
|
+
median normalisation imprints the lamp continuum, `spline_knots` is a
|
|
328
|
+
smoothing factor, exposure-time normalisation is the caller's job).
|
|
329
|
+
|
|
330
|
+
### Fixed — robustness (PATCH bumps)
|
|
331
|
+
|
|
332
|
+
- NaN no longer yields `success=True` with garbage : `snr_edge`,
|
|
333
|
+
`snr_linear_fit` (SNR = inf) and `normalize_edges` (all-NaN output)
|
|
334
|
+
mask non-finite samples and fail when too few remain ; the
|
|
335
|
+
`embed_*` family fails cleanly (or drops non-finite samples where the
|
|
336
|
+
recipe is sampling-independent : `embed_band_power`,
|
|
337
|
+
`embed_lick_indices`, `embed_log_lambda`) instead of returning a zero
|
|
338
|
+
vector ; `smooth_gaussian` / `smooth_savgol` bridge NaN before
|
|
339
|
+
filtering and restore it afterwards (one NaN used to spread over 34
|
|
340
|
+
pixels, Savitzky-Golay used to raise). `embed_flux` raises
|
|
341
|
+
`ValueError` on non-finite input.
|
|
342
|
+
- Descending wavelength axes (negative `CDELT1`, reversed ASCII tables)
|
|
343
|
+
are sorted locally in `equivalent_width` (returned −0.005 instead of
|
|
344
|
+
+1.88 Å), `redshift_lines`, `embed_band_power`, `embed_lick_indices`,
|
|
345
|
+
`embed_log_lambda`, `cross_correlate_rv`, `disentangle_sb2`,
|
|
346
|
+
`doppler_tomogram`, `classify_template_chi2`
|
|
347
|
+
and, on the reduction side, in the reference read by
|
|
348
|
+
`combine_spectra_arithmetic` (which silently returned the science flux
|
|
349
|
+
unchanged with a reversed reference), `remove_telluric_division` and
|
|
350
|
+
`response_from_standard`. Ascending input is unchanged.
|
|
351
|
+
- `compare_normalisations` / `compare_smoothings` / `compare_snr_methods`
|
|
352
|
+
1.0.1 : work on a slim copy of the spectrum instead of deep-copying
|
|
353
|
+
the whole context (gigabytes in a reduction session).
|
|
354
|
+
- `output_produces` declarations completed for `detect_lines`,
|
|
355
|
+
`vr_ratio`, `rotation_curve`, `redshift_lines`, `doppler_tomogram`,
|
|
356
|
+
`fit_keplerian_orbit`, `cross_correlate_rv`, `lomb_scargle`,
|
|
357
|
+
`disentangle_sb2`, the nebular trio and, on the reduction side, `match_lamp_lines`,
|
|
358
|
+
`wavelength_calibration_solar`, `fit_telluric_scaling`, `detect_trace`,
|
|
359
|
+
`extract_spectrum_boxcar`, `stack_spectra` ; a contract
|
|
360
|
+
test now checks that every written key is declared.
|
|
361
|
+
|
|
362
|
+
### References
|
|
363
|
+
|
|
364
|
+
- Corrected : `vr_ratio` Okazaki 1991 is PASJ 43, 75 (not A&A 246, 415) ;
|
|
365
|
+
`embed_spectrum` no longer credits Soubiran et al. 2016 (the PASTEL
|
|
366
|
+
catalogue) for the DCT ; `detect_lines` cites Press et al. 2007 §10.2
|
|
367
|
+
for three-point parabolic centroiding instead of "standard technique"
|
|
368
|
+
; the air → vacuum
|
|
369
|
+
coefficients of `air_to_vacuum` are attributed to the Piskunov / VALD3
|
|
370
|
+
inverse fit (vacuum → air stays Morton 2000, with the Hα 6562.79 →
|
|
371
|
+
6564.603 Å acceptance value now tested) ; `detect_trace` cites
|
|
372
|
+
`scipy.signal.find_peaks`, which it actually calls, instead of
|
|
373
|
+
`center_of_mass` ; "standard technique" hand-waving replaced by Tody
|
|
374
|
+
1986 / 1993 in `wavelength_calibrate_polynomial`, Horne 1986 §2 in
|
|
375
|
+
`subtract_sky_2d`, Hinkle, Wallace & Livingston 2003 + Hardie 1962 in
|
|
376
|
+
`synth_telluric`, Bevington & Robinson 2003 in `clip_sigma`, Tody 1993 +
|
|
377
|
+
Horne 1986 in `merge_echelle_orders` ; `response_from_standard` drops
|
|
378
|
+
the unused Hayes & Latham citation and its docstring no longer promises
|
|
379
|
+
an airmass step that does not exist.
|
|
380
|
+
- Added to bricks that had none : `equivalent_width`, `normalize_edges`,
|
|
381
|
+
`normalize_max`, `normalize_percentile`, `subtract_continuum`,
|
|
382
|
+
`snr_edge`, `snr_linear_fit`, `phase_fold`.
|
|
383
|
+
- Still to verify (left in place, flagged in the audit) : Baldry 1999
|
|
384
|
+
(`embed_log_lambda`), Bu 2014 and Sánchez-Sáez 2021 (`embed_spectrum`),
|
|
385
|
+
Brault 1987 (`rv_precision_bouchy`), Teyssier 2015
|
|
386
|
+
(`validate_bess_header`).
|
|
387
|
+
|
|
388
|
+
### Tests
|
|
389
|
+
|
|
390
|
+
- First tests for the ten orphan bricks : `aperture_photometry`,
|
|
391
|
+
`simbad_query` / `gaia_query` / `vizier_query` (astroquery mocked),
|
|
392
|
+
`plot_dynamic_spectrum` / `plot_3d_surface_plotly` /
|
|
393
|
+
`plot_animation_plotly`, `export_hdf5`, `flux_calibrate_easyspec`,
|
|
394
|
+
`resample_flux_conserving`.
|
|
395
|
+
- Literature acceptance values added : Bouchy 2001 eq. 6 closed form
|
|
396
|
+
(89.18 m/s), Lorentzian γ / FWHM / area, Voigt Olivero-Longbothum
|
|
397
|
+
FWHM, Lomb-Scargle FAP vs astropy, Morton 2000 Hα 6562.79 → 6564.603 Å,
|
|
398
|
+
Osterbrock [O III] table, Marsh-Horne spot position, slant round trip,
|
|
399
|
+
partial-overlap stacking, WCS Paper III log axis.
|
|
400
|
+
- New MCP suites : parameter guard (12), tool extras (17), HTTP entry
|
|
401
|
+
point (6), rebinding / NAT64 / CGNAT cases, session cap and LRU.
|
|
402
|
+
|
|
9
403
|
## [0.6.0] — 2026-07-30
|
|
10
404
|
|
|
11
405
|
Contributions from the SpecRelay / LineMill ecosystem audit
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: spectro-kernel
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: A shared catalogue of astronomical spectroscopy algorithms, composable into reproducible pipelines, usable as a Python library, a CLI, or an MCP server.
|
|
5
5
|
Project-URL: Homepage, https://github.com/matthieulel/spectro-kernel
|
|
6
|
-
Project-URL: Documentation, https://
|
|
6
|
+
Project-URL: Documentation, https://docs.spectrokernel.io/
|
|
7
7
|
Project-URL: Source, https://github.com/matthieulel/spectro-kernel
|
|
8
8
|
Project-URL: Issues, https://github.com/matthieulel/spectro-kernel/issues
|
|
9
9
|
Project-URL: Changelog, https://github.com/matthieulel/spectro-kernel/blob/main/CHANGELOG.md
|
|
@@ -54,9 +54,12 @@ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
|
54
54
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
55
55
|
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
56
56
|
Provides-Extra: docs
|
|
57
|
-
Requires-Dist:
|
|
57
|
+
Requires-Dist: jsonschema>=4.0; extra == 'docs'
|
|
58
|
+
Requires-Dist: mkdocs-literate-nav>=0.6; extra == 'docs'
|
|
58
59
|
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
59
60
|
Requires-Dist: mkdocstrings[python]>=0.26; extra == 'docs'
|
|
61
|
+
Requires-Dist: pyyaml>=6.0; extra == 'docs'
|
|
62
|
+
Requires-Dist: zensical>=0.0.62; extra == 'docs'
|
|
60
63
|
Provides-Extra: embedding-ml
|
|
61
64
|
Requires-Dist: torch>=2.2; extra == 'embedding-ml'
|
|
62
65
|
Provides-Extra: embedding-remote
|
|
@@ -178,6 +181,35 @@ transverse ones (`list_algorithms`, `describe_algorithm`, `get_algorithm_source`
|
|
|
178
181
|
deploy the same `spectro-mcp` in HTTP mode on any container host (DigitalOcean
|
|
179
182
|
App Platform, Fly.io, etc.). Local-stdio remains the recommended default.
|
|
180
183
|
|
|
184
|
+
In `--http` mode the server refuses local filesystem paths, fetches URLs
|
|
185
|
+
through an SSRF guard (no private addresses, no redirects, size and time
|
|
186
|
+
caps), caps sessions and memory, and rate-limits clients. Two postures :
|
|
187
|
+
|
|
188
|
+
- **Open** (the public demo server) : `SPECTRO_MCP_ALLOW_ANONYMOUS=1`. No
|
|
189
|
+
accounts, no key ; anyone adds `https://<host>/mcp` as a connector.
|
|
190
|
+
- **Closed** : `SPECTRO_MCP_API_KEY=<secret>` ; clients send it in the
|
|
191
|
+
`X-API-Key` header (or `?api_key=` with `SPECTRO_MCP_ALLOW_KEY_IN_URL=1`).
|
|
192
|
+
|
|
193
|
+
The server refuses to start in `--http` mode with neither. Configuration is
|
|
194
|
+
by environment variable (none of these affect stdio mode) :
|
|
195
|
+
|
|
196
|
+
| Variable | Default | Meaning |
|
|
197
|
+
|----------|---------|---------|
|
|
198
|
+
| `SPECTRO_MCP_API_KEY` | unset | Closed posture : shared secret clients send in the `X-API-Key` header. |
|
|
199
|
+
| `SPECTRO_MCP_ALLOW_ANONYMOUS` | unset | Open posture : `1` to start without a key (explicit opt-in). |
|
|
200
|
+
| `SPECTRO_MCP_ALLOW_KEY_IN_URL` | unset | `1` also accepts the key as `?api_key=…` in the URL (for claude.ai / Claude Desktop connectors, which cannot set headers); the parameter is scrubbed before access logging. `Authorization: Bearer <key>` is always accepted. |
|
|
201
|
+
| `SPECTRO_MCP_RATE_PER_MINUTE` | `0` (off) | Sliding-window request limit per API key (per client IP when auth is off). |
|
|
202
|
+
| `SPECTRO_MCP_TRUSTED_PROXY` | unset | `1` behind a load balancer: trust `X-Forwarded-For` for the client IP. |
|
|
203
|
+
| `SPECTRO_MCP_CORS_ORIGINS` | spectrokernel.io sites | Comma-separated browser origins allowed to call the server (`*` for any, `none` to disable). The `Mcp-Session-Id` header is exposed to browsers. |
|
|
204
|
+
| `SPECTRO_MCP_MAX_SESSIONS` | `200` | Cap on live in-memory sessions; the least recently used one is evicted. |
|
|
205
|
+
| `SPECTRO_MCP_MAX_PIXELS` | `32000000` | Refuse downloaded FITS files with more data elements than this (256 MB as float64); `0` disables. |
|
|
206
|
+
| `SPECTRO_MCP_DOWNLOAD_BUDGET_S` | `120` | Wall-clock budget for one URL download (on top of the 256 MiB cap). |
|
|
207
|
+
| `SPECTRO_MCP_MAX_CONCURRENT_DOWNLOADS` | `4` | Downloads allowed in flight at once. |
|
|
208
|
+
| `SPECTRO_EMBED_ENDPOINT_ALLOW` | unset | Comma-separated hosts `embed_remote` may call with the server-side `SPECTRO_EMBED_API_KEY` (and bypass the SSRF guard for, e.g. an internal inference service). |
|
|
209
|
+
| `SPECTRO_MCP_REDIS_URL`, `SPECTRO_MCP_SESSION_SECRET` | unset | Redis-backed sessions for multi-instance deployments (payloads are HMAC-signed with the secret). |
|
|
210
|
+
| `SPECTRO_MCP_S3_BUCKET`, `SPECTRO_MCP_S3_ENDPOINT`, … | unset | Presigned-URL uploads (`request_upload_url`). |
|
|
211
|
+
| `SENTRY_DSN` | unset | Error reporting. |
|
|
212
|
+
|
|
181
213
|
## Architecture
|
|
182
214
|
|
|
183
215
|
```
|
|
@@ -206,7 +238,7 @@ from the registry. Build it locally with:
|
|
|
206
238
|
|
|
207
239
|
```bash
|
|
208
240
|
uv pip install -e ".[docs,all]"
|
|
209
|
-
|
|
241
|
+
python tools/docs/generate.py && zensical serve # live preview at http://127.0.0.1:8000
|
|
210
242
|
```
|
|
211
243
|
|
|
212
244
|
Every algorithm declares its **provenance** — a `backend` (the library it leans on) and
|
|
@@ -92,6 +92,35 @@ transverse ones (`list_algorithms`, `describe_algorithm`, `get_algorithm_source`
|
|
|
92
92
|
deploy the same `spectro-mcp` in HTTP mode on any container host (DigitalOcean
|
|
93
93
|
App Platform, Fly.io, etc.). Local-stdio remains the recommended default.
|
|
94
94
|
|
|
95
|
+
In `--http` mode the server refuses local filesystem paths, fetches URLs
|
|
96
|
+
through an SSRF guard (no private addresses, no redirects, size and time
|
|
97
|
+
caps), caps sessions and memory, and rate-limits clients. Two postures :
|
|
98
|
+
|
|
99
|
+
- **Open** (the public demo server) : `SPECTRO_MCP_ALLOW_ANONYMOUS=1`. No
|
|
100
|
+
accounts, no key ; anyone adds `https://<host>/mcp` as a connector.
|
|
101
|
+
- **Closed** : `SPECTRO_MCP_API_KEY=<secret>` ; clients send it in the
|
|
102
|
+
`X-API-Key` header (or `?api_key=` with `SPECTRO_MCP_ALLOW_KEY_IN_URL=1`).
|
|
103
|
+
|
|
104
|
+
The server refuses to start in `--http` mode with neither. Configuration is
|
|
105
|
+
by environment variable (none of these affect stdio mode) :
|
|
106
|
+
|
|
107
|
+
| Variable | Default | Meaning |
|
|
108
|
+
|----------|---------|---------|
|
|
109
|
+
| `SPECTRO_MCP_API_KEY` | unset | Closed posture : shared secret clients send in the `X-API-Key` header. |
|
|
110
|
+
| `SPECTRO_MCP_ALLOW_ANONYMOUS` | unset | Open posture : `1` to start without a key (explicit opt-in). |
|
|
111
|
+
| `SPECTRO_MCP_ALLOW_KEY_IN_URL` | unset | `1` also accepts the key as `?api_key=…` in the URL (for claude.ai / Claude Desktop connectors, which cannot set headers); the parameter is scrubbed before access logging. `Authorization: Bearer <key>` is always accepted. |
|
|
112
|
+
| `SPECTRO_MCP_RATE_PER_MINUTE` | `0` (off) | Sliding-window request limit per API key (per client IP when auth is off). |
|
|
113
|
+
| `SPECTRO_MCP_TRUSTED_PROXY` | unset | `1` behind a load balancer: trust `X-Forwarded-For` for the client IP. |
|
|
114
|
+
| `SPECTRO_MCP_CORS_ORIGINS` | spectrokernel.io sites | Comma-separated browser origins allowed to call the server (`*` for any, `none` to disable). The `Mcp-Session-Id` header is exposed to browsers. |
|
|
115
|
+
| `SPECTRO_MCP_MAX_SESSIONS` | `200` | Cap on live in-memory sessions; the least recently used one is evicted. |
|
|
116
|
+
| `SPECTRO_MCP_MAX_PIXELS` | `32000000` | Refuse downloaded FITS files with more data elements than this (256 MB as float64); `0` disables. |
|
|
117
|
+
| `SPECTRO_MCP_DOWNLOAD_BUDGET_S` | `120` | Wall-clock budget for one URL download (on top of the 256 MiB cap). |
|
|
118
|
+
| `SPECTRO_MCP_MAX_CONCURRENT_DOWNLOADS` | `4` | Downloads allowed in flight at once. |
|
|
119
|
+
| `SPECTRO_EMBED_ENDPOINT_ALLOW` | unset | Comma-separated hosts `embed_remote` may call with the server-side `SPECTRO_EMBED_API_KEY` (and bypass the SSRF guard for, e.g. an internal inference service). |
|
|
120
|
+
| `SPECTRO_MCP_REDIS_URL`, `SPECTRO_MCP_SESSION_SECRET` | unset | Redis-backed sessions for multi-instance deployments (payloads are HMAC-signed with the secret). |
|
|
121
|
+
| `SPECTRO_MCP_S3_BUCKET`, `SPECTRO_MCP_S3_ENDPOINT`, … | unset | Presigned-URL uploads (`request_upload_url`). |
|
|
122
|
+
| `SENTRY_DSN` | unset | Error reporting. |
|
|
123
|
+
|
|
95
124
|
## Architecture
|
|
96
125
|
|
|
97
126
|
```
|
|
@@ -120,7 +149,7 @@ from the registry. Build it locally with:
|
|
|
120
149
|
|
|
121
150
|
```bash
|
|
122
151
|
uv pip install -e ".[docs,all]"
|
|
123
|
-
|
|
152
|
+
python tools/docs/generate.py && zensical serve # live preview at http://127.0.0.1:8000
|
|
124
153
|
```
|
|
125
154
|
|
|
126
155
|
Every algorithm declares its **provenance** — a `backend` (the library it leans on) and
|
|
@@ -103,7 +103,7 @@ principle](../why.md#the-principle-we-do-not-reinvent). Every algorithm declares
|
|
|
103
103
|
- its **`references`** - the literature for the method, or the wrapped library.
|
|
104
104
|
|
|
105
105
|
Both are visible in `spectro describe <name>`, in the
|
|
106
|
-
[catalogue](../
|
|
106
|
+
[catalogue](../algorithms/index.md), and in every MCP tool description. Nothing is a black box:
|
|
107
107
|
you can always see what an algorithm stands on and where the method comes from.
|
|
108
108
|
|
|
109
109
|
## Naming conventions
|
|
@@ -57,10 +57,10 @@ CI runs the same checks on every pull request; keep `main` green.
|
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
59
|
uv pip install -e ".[docs,all]"
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
python tools/docs/generate.py && zensical serve # live preview at http://127.0.0.1:8000
|
|
61
|
+
python tools/docs/build.py # what CI runs: generators + strict build into site/
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
The [algorithm catalogue](
|
|
64
|
+
The [algorithm catalogue](algorithms/index.md) page is *generated* from the registry at build
|
|
65
65
|
time - you never edit it by hand. Add a docstring and metadata to your algorithm and the
|
|
66
66
|
catalogue page documents it automatically.
|
|
@@ -114,4 +114,4 @@ The agent now sees every algorithm as a tool. See [As an MCP server](usage/mcp.m
|
|
|
114
114
|
catalogue](tutorials/discover-the-catalogue.md).
|
|
115
115
|
- Understand the moving parts → [Concepts: architecture](concepts/architecture.md).
|
|
116
116
|
- A guided, end-to-end analysis → [Tutorial: analyse a spectrum](tutorials/analyse-a-spectrum.md).
|
|
117
|
-
- Browse everything available → [Algorithm catalogue](
|
|
117
|
+
- Browse everything available → [Algorithm catalogue](algorithms/index.md).
|
|
@@ -49,7 +49,7 @@ through MCP, you can do from a terminal, and vice-versa.
|
|
|
49
49
|
your first analysis in five minutes.
|
|
50
50
|
- :material-sitemap: **[Concepts](concepts/architecture.md)** - the architecture,
|
|
51
51
|
the data types, the registry, the pipelines.
|
|
52
|
-
- :material-book-open-variant: **[Algorithm catalogue](
|
|
52
|
+
- :material-book-open-variant: **[Algorithm catalogue](algorithms/index.md)** - every
|
|
53
53
|
algorithm, auto-generated from the code.
|
|
54
54
|
- :material-school: **[Tutorials](tutorials/first-spectrum.md)** - from *"I captured a
|
|
55
55
|
spectrum of a star, now what?"* all the way to writing your own algorithm.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This page is generated from the source docstrings by
|
|
4
4
|
[mkdocstrings](https://mkdocstrings.github.io/). It documents the public Python API; for
|
|
5
|
-
the *algorithms* themselves see the [catalogue](
|
|
5
|
+
the *algorithms* themselves see the [catalogue](algorithms/index.md).
|
|
6
6
|
|
|
7
7
|
## Data types
|
|
8
8
|
|
|
@@ -106,7 +106,7 @@ Over [MCP](../usage/mcp.md), the discovery tools are `list_algorithms` and
|
|
|
106
106
|
|
|
107
107
|
## The full reference
|
|
108
108
|
|
|
109
|
-
The [Algorithm catalogue](../
|
|
109
|
+
The [Algorithm catalogue](../algorithms/index.md) page lists **every** algorithm with its
|
|
110
110
|
parameters, provenance and references, grouped by category. It is generated directly
|
|
111
111
|
from the registry, so it is always in sync with the version you have installed.
|
|
112
112
|
|
|
@@ -149,4 +149,4 @@ checked, continuum normalised, lines identified. That is a genuine scientific re
|
|
|
149
149
|
operation available, and how to inspect each one.
|
|
150
150
|
- **A fuller, guided analysis** → [Analyse a spectrum](analyse-a-spectrum.md).
|
|
151
151
|
- **Periodic targets** (Be stars, eclipsing binaries) → the `lomb_scargle` and
|
|
152
|
-
`phase_fold` algorithms in the [catalogue](../
|
|
152
|
+
`phase_fold` algorithms in the [catalogue](../algorithms/index.md).
|
|
@@ -167,5 +167,5 @@ Every algorithm above follows the same kernel contract:
|
|
|
167
167
|
`ctx.image.meta` (for 2-D) or `ctx.spectrum.meta` (for 1-D), and the
|
|
168
168
|
per-step `metrics` are JSON-serialisable for later auditing.
|
|
169
169
|
|
|
170
|
-
See [Algorithm catalogue](../
|
|
170
|
+
See [Algorithm catalogue](../algorithms/index.md) for the full parameter and
|
|
171
171
|
reference list of each brick.
|