noodlelab 0.1.0__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.
- noodlelab-0.1.0/.claude/skills/noodlelab/SKILL.md +138 -0
- noodlelab-0.1.0/.devcontainer/devcontainer.json +21 -0
- noodlelab-0.1.0/.dockerignore +15 -0
- noodlelab-0.1.0/.github/workflows/ci.yml +89 -0
- noodlelab-0.1.0/.github/workflows/release.yml +287 -0
- noodlelab-0.1.0/.gitignore +21 -0
- noodlelab-0.1.0/AGENTS.md +83 -0
- noodlelab-0.1.0/CLAUDE.md +1 -0
- noodlelab-0.1.0/Dockerfile +84 -0
- noodlelab-0.1.0/Dockerfile.test +42 -0
- noodlelab-0.1.0/Dockerfile.test.dockerignore +12 -0
- noodlelab-0.1.0/LICENSE +21 -0
- noodlelab-0.1.0/Makefile +38 -0
- noodlelab-0.1.0/PKG-INFO +335 -0
- noodlelab-0.1.0/README.md +217 -0
- noodlelab-0.1.0/docker-compose.yml +19 -0
- noodlelab-0.1.0/docs/agents/adopt-in-existing-project.md +127 -0
- noodlelab-0.1.0/docs/agents/graph.schema.json +432 -0
- noodlelab-0.1.0/docs/agents/graphs-and-reports.md +114 -0
- noodlelab-0.1.0/docs/agents/quickstart.md +78 -0
- noodlelab-0.1.0/docs/editor.md +24 -0
- noodlelab-0.1.0/docs/images/agent.png +0 -0
- noodlelab-0.1.0/docs/images/groundwater.png +0 -0
- noodlelab-0.1.0/docs/images/hero.png +0 -0
- noodlelab-0.1.0/docs/images/report.png +0 -0
- noodlelab-0.1.0/docs/images/reporting.png +0 -0
- noodlelab-0.1.0/docs/images/satellite.png +0 -0
- noodlelab-0.1.0/docs/installation.md +56 -0
- noodlelab-0.1.0/docs/llms-full.txt +1007 -0
- noodlelab-0.1.0/docs/releasing.md +80 -0
- noodlelab-0.1.0/docs/server.md +51 -0
- noodlelab-0.1.0/docs/slurm.md +80 -0
- noodlelab-0.1.0/examples/noodlelab-example-pack/README.md +14 -0
- noodlelab-0.1.0/examples/noodlelab-example-pack/pyproject.toml +18 -0
- noodlelab-0.1.0/examples/noodlelab-example-pack/src/noodlelab_example/__init__.py +151 -0
- noodlelab-0.1.0/frontend/admin.html +29 -0
- noodlelab-0.1.0/frontend/index.html +106 -0
- noodlelab-0.1.0/frontend/package-lock.json +1164 -0
- noodlelab-0.1.0/frontend/package.json +22 -0
- noodlelab-0.1.0/frontend/scripts/vendor-litegraph.mjs +34 -0
- noodlelab-0.1.0/frontend/src/admin.ts +486 -0
- noodlelab-0.1.0/frontend/src/agent.ts +258 -0
- noodlelab-0.1.0/frontend/src/annotation.ts +194 -0
- noodlelab-0.1.0/frontend/src/api.ts +174 -0
- noodlelab-0.1.0/frontend/src/compare.ts +190 -0
- noodlelab-0.1.0/frontend/src/connection.ts +50 -0
- noodlelab-0.1.0/frontend/src/dom.ts +99 -0
- noodlelab-0.1.0/frontend/src/env.d.ts +2 -0
- noodlelab-0.1.0/frontend/src/explorer.ts +217 -0
- noodlelab-0.1.0/frontend/src/filepicker.ts +275 -0
- noodlelab-0.1.0/frontend/src/graph.ts +643 -0
- noodlelab-0.1.0/frontend/src/health.ts +66 -0
- noodlelab-0.1.0/frontend/src/help.ts +408 -0
- noodlelab-0.1.0/frontend/src/history.ts +211 -0
- noodlelab-0.1.0/frontend/src/inspector.ts +306 -0
- noodlelab-0.1.0/frontend/src/litegraph.d.ts +11 -0
- noodlelab-0.1.0/frontend/src/main.ts +1672 -0
- noodlelab-0.1.0/frontend/src/monitor.ts +181 -0
- noodlelab-0.1.0/frontend/src/nodes.ts +650 -0
- noodlelab-0.1.0/frontend/src/outputs.ts +482 -0
- noodlelab-0.1.0/frontend/src/panes.ts +105 -0
- noodlelab-0.1.0/frontend/src/prefs.ts +40 -0
- noodlelab-0.1.0/frontend/src/probe.ts +69 -0
- noodlelab-0.1.0/frontend/src/reports.ts +374 -0
- noodlelab-0.1.0/frontend/src/repro.ts +120 -0
- noodlelab-0.1.0/frontend/src/requirements.ts +166 -0
- noodlelab-0.1.0/frontend/src/runstate.ts +115 -0
- noodlelab-0.1.0/frontend/src/select.ts +39 -0
- noodlelab-0.1.0/frontend/src/sharing.ts +142 -0
- noodlelab-0.1.0/frontend/src/study.ts +352 -0
- noodlelab-0.1.0/frontend/src/style.css +2584 -0
- noodlelab-0.1.0/frontend/src/subgraph.ts +642 -0
- noodlelab-0.1.0/frontend/src/theme.ts +88 -0
- noodlelab-0.1.0/frontend/src/tracked.ts +233 -0
- noodlelab-0.1.0/frontend/src/types.ts +545 -0
- noodlelab-0.1.0/frontend/src/zones.ts +498 -0
- noodlelab-0.1.0/frontend/tsconfig.json +16 -0
- noodlelab-0.1.0/frontend/vite.config.ts +22 -0
- noodlelab-0.1.0/llms.txt +37 -0
- noodlelab-0.1.0/pyproject.toml +180 -0
- noodlelab-0.1.0/render.yaml +22 -0
- noodlelab-0.1.0/scripts/check_tier.py +108 -0
- noodlelab-0.1.0/scripts/gen_llms.py +229 -0
- noodlelab-0.1.0/scripts/make_example_data.py +993 -0
- noodlelab-0.1.0/scripts/make_example_graphs.py +3285 -0
- noodlelab-0.1.0/src/noodlelab/__init__.py +64 -0
- noodlelab-0.1.0/src/noodlelab/__main__.py +3 -0
- noodlelab-0.1.0/src/noodlelab/agent/__init__.py +138 -0
- noodlelab-0.1.0/src/noodlelab/agent/guide.md +129 -0
- noodlelab-0.1.0/src/noodlelab/agent/mcp.py +219 -0
- noodlelab-0.1.0/src/noodlelab/agent/tools.py +613 -0
- noodlelab-0.1.0/src/noodlelab/cli.py +934 -0
- noodlelab-0.1.0/src/noodlelab/config.py +221 -0
- noodlelab-0.1.0/src/noodlelab/core/__init__.py +1 -0
- noodlelab-0.1.0/src/noodlelab/core/about.py +212 -0
- noodlelab-0.1.0/src/noodlelab/core/baseline.py +363 -0
- noodlelab-0.1.0/src/noodlelab/core/bundle.py +148 -0
- noodlelab-0.1.0/src/noodlelab/core/cache.py +66 -0
- noodlelab-0.1.0/src/noodlelab/core/checkpoint.py +230 -0
- noodlelab-0.1.0/src/noodlelab/core/checks.py +92 -0
- noodlelab-0.1.0/src/noodlelab/core/codecs.py +426 -0
- noodlelab-0.1.0/src/noodlelab/core/context.py +113 -0
- noodlelab-0.1.0/src/noodlelab/core/convert.py +224 -0
- noodlelab-0.1.0/src/noodlelab/core/events.py +185 -0
- noodlelab-0.1.0/src/noodlelab/core/executor.py +1693 -0
- noodlelab-0.1.0/src/noodlelab/core/export.py +405 -0
- noodlelab-0.1.0/src/noodlelab/core/figures.py +110 -0
- noodlelab-0.1.0/src/noodlelab/core/files.py +577 -0
- noodlelab-0.1.0/src/noodlelab/core/fold.py +121 -0
- noodlelab-0.1.0/src/noodlelab/core/graph.py +510 -0
- noodlelab-0.1.0/src/noodlelab/core/health.py +462 -0
- noodlelab-0.1.0/src/noodlelab/core/meta.py +164 -0
- noodlelab-0.1.0/src/noodlelab/core/monitor.py +142 -0
- noodlelab-0.1.0/src/noodlelab/core/montecarlo.py +306 -0
- noodlelab-0.1.0/src/noodlelab/core/node.py +795 -0
- noodlelab-0.1.0/src/noodlelab/core/ports.py +461 -0
- noodlelab-0.1.0/src/noodlelab/core/preview.py +201 -0
- noodlelab-0.1.0/src/noodlelab/core/probe.py +989 -0
- noodlelab-0.1.0/src/noodlelab/core/provenance.py +477 -0
- noodlelab-0.1.0/src/noodlelab/core/registry.py +278 -0
- noodlelab-0.1.0/src/noodlelab/core/reqlog.py +138 -0
- noodlelab-0.1.0/src/noodlelab/core/requirements.py +430 -0
- noodlelab-0.1.0/src/noodlelab/core/rundiff.py +168 -0
- noodlelab-0.1.0/src/noodlelab/core/sample.py +144 -0
- noodlelab-0.1.0/src/noodlelab/core/studies.py +309 -0
- noodlelab-0.1.0/src/noodlelab/core/suggest.py +144 -0
- noodlelab-0.1.0/src/noodlelab/core/tracked.py +161 -0
- noodlelab-0.1.0/src/noodlelab/core/types.py +107 -0
- noodlelab-0.1.0/src/noodlelab/core/typesys.py +706 -0
- noodlelab-0.1.0/src/noodlelab/core/uncertainty.py +421 -0
- noodlelab-0.1.0/src/noodlelab/core/units.py +230 -0
- noodlelab-0.1.0/src/noodlelab/examples/01-getting-started.graph.json +71 -0
- noodlelab-0.1.0/src/noodlelab/examples/02-units.graph.json +113 -0
- noodlelab-0.1.0/src/noodlelab/examples/03-plot-a-function.graph.json +77 -0
- noodlelab-0.1.0/src/noodlelab/examples/04-noise-and-smoothing.graph.json +156 -0
- noodlelab-0.1.0/src/noodlelab/examples/05-first-csv.graph.json +87 -0
- noodlelab-0.1.0/src/noodlelab/examples/06-straight-line-fit.graph.json +195 -0
- noodlelab-0.1.0/src/noodlelab/examples/07-filter-and-group.graph.json +133 -0
- noodlelab-0.1.0/src/noodlelab/examples/08-calibration.graph.json +276 -0
- noodlelab-0.1.0/src/noodlelab/examples/09-uncertainty.graph.json +211 -0
- noodlelab-0.1.0/src/noodlelab/examples/10-curve-fit-subgraph.graph.json +559 -0
- noodlelab-0.1.0/src/noodlelab/examples/11-time-series.graph.json +215 -0
- noodlelab-0.1.0/src/noodlelab/examples/12-signals.graph.json +196 -0
- noodlelab-0.1.0/src/noodlelab/examples/13-symbolic.graph.json +242 -0
- noodlelab-0.1.0/src/noodlelab/examples/14-first-report.graph.json +409 -0
- noodlelab-0.1.0/src/noodlelab/examples/15-repeat-zone.graph.json +264 -0
- noodlelab-0.1.0/src/noodlelab/examples/16-until-converged.graph.json +370 -0
- noodlelab-0.1.0/src/noodlelab/examples/17-sweep-and-optimize.graph.json +430 -0
- noodlelab-0.1.0/src/noodlelab/examples/18-gps-track.graph.json +208 -0
- noodlelab-0.1.0/src/noodlelab/examples/19-earthquakes.graph.json +173 -0
- noodlelab-0.1.0/src/noodlelab/examples/20-groundwater-nitrate.graph.json +2197 -0
- noodlelab-0.1.0/src/noodlelab/examples/21-fixed-beam.graph.json +2866 -0
- noodlelab-0.1.0/src/noodlelab/examples/22-watershed.graph.json +3279 -0
- noodlelab-0.1.0/src/noodlelab/examples/23-satellite-link.graph.json +2059 -0
- noodlelab-0.1.0/src/noodlelab/examples/24-cantilever-bracket.graph.json +591 -0
- noodlelab-0.1.0/src/noodlelab/examples/__init__.py +0 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/05-first-csv/README.md +16 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/05-first-csv/pendulum.csv +56 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/06-straight-line-fit/README.md +16 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/06-straight-line-fit/pendulum.csv +56 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/07-filter-and-group/README.md +20 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/07-filter-and-group/field_trial.csv +73 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/08-calibration/README.md +11 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/08-calibration/samples.csv +13 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/08-calibration/standards.csv +25 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/10-curve-fit-subgraph/README.md +17 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/10-curve-fit-subgraph/kinetics.csv +49 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/11-time-series/README.md +16 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/11-time-series/station_daily.csv +5480 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/12-signals/README.md +17 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/12-signals/accelerometer.csv +10001 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/14-first-report/README.md +16 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/14-first-report/pendulum.csv +56 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/18-gps-track/README.md +19 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/18-gps-track/hike.csv +3526 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/18-gps-track/waypoints.csv +6 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/19-earthquakes/README.md +20 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/19-earthquakes/catalog.csv +2012 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/19-earthquakes/regions.geojson +1 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/20-groundwater-nitrate/README.md +22 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/20-groundwater-nitrate/districts.geojson +1 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/20-groundwater-nitrate/river.geojson +1 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/20-groundwater-nitrate/wells.csv +161 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/21-fixed-beam/README.md +18 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/21-fixed-beam/strain_gauges.csv +12 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/22-watershed/README.md +20 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/22-watershed/catchments.geojson +1 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/22-watershed/dem.asc +156 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/22-watershed/dem.prj +1 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/22-watershed/nir.asc +156 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/22-watershed/nir.prj +1 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/22-watershed/rain_gauges.csv +13 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/22-watershed/red.asc +156 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/22-watershed/red.prj +1 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/23-satellite-link/README.md +19 -0
- noodlelab-0.1.0/src/noodlelab/examples/data/23-satellite-link/radios.csv +9 -0
- noodlelab-0.1.0/src/noodlelab/nodes/__init__.py +1 -0
- noodlelab-0.1.0/src/noodlelab/nodes/checks.py +197 -0
- noodlelab-0.1.0/src/noodlelab/nodes/core.py +500 -0
- noodlelab-0.1.0/src/noodlelab/nodes/engineering/__init__.py +33 -0
- noodlelab-0.1.0/src/noodlelab/nodes/engineering/controls.py +181 -0
- noodlelab-0.1.0/src/noodlelab/nodes/engineering/decibels.py +85 -0
- noodlelab-0.1.0/src/noodlelab/nodes/engineering/fluids.py +83 -0
- noodlelab-0.1.0/src/noodlelab/nodes/engineering/materials.py +126 -0
- noodlelab-0.1.0/src/noodlelab/nodes/engineering/sections.py +120 -0
- noodlelab-0.1.0/src/noodlelab/nodes/engineering/structures.py +217 -0
- noodlelab-0.1.0/src/noodlelab/nodes/engineering/thermal.py +80 -0
- noodlelab-0.1.0/src/noodlelab/nodes/engineering/trade.py +88 -0
- noodlelab-0.1.0/src/noodlelab/nodes/geo/__init__.py +31 -0
- noodlelab-0.1.0/src/noodlelab/nodes/geo/plot.py +353 -0
- noodlelab-0.1.0/src/noodlelab/nodes/geo/raster.py +837 -0
- noodlelab-0.1.0/src/noodlelab/nodes/geo/seismology.py +128 -0
- noodlelab-0.1.0/src/noodlelab/nodes/geo/track.py +151 -0
- noodlelab-0.1.0/src/noodlelab/nodes/geo/types.py +378 -0
- noodlelab-0.1.0/src/noodlelab/nodes/geo/vector.py +440 -0
- noodlelab-0.1.0/src/noodlelab/nodes/maths/__init__.py +26 -0
- noodlelab-0.1.0/src/noodlelab/nodes/maths/_common.py +72 -0
- noodlelab-0.1.0/src/noodlelab/nodes/maths/arrays.py +100 -0
- noodlelab-0.1.0/src/noodlelab/nodes/maths/calculus.py +60 -0
- noodlelab-0.1.0/src/noodlelab/nodes/maths/fitting.py +484 -0
- noodlelab-0.1.0/src/noodlelab/nodes/maths/plot.py +169 -0
- noodlelab-0.1.0/src/noodlelab/nodes/report.py +531 -0
- noodlelab-0.1.0/src/noodlelab/nodes/requirements.py +171 -0
- noodlelab-0.1.0/src/noodlelab/nodes/science/__init__.py +27 -0
- noodlelab-0.1.0/src/noodlelab/nodes/science/plot.py +218 -0
- noodlelab-0.1.0/src/noodlelab/nodes/science/signal.py +247 -0
- noodlelab-0.1.0/src/noodlelab/nodes/science/stats.py +495 -0
- noodlelab-0.1.0/src/noodlelab/nodes/science/studies.py +267 -0
- noodlelab-0.1.0/src/noodlelab/nodes/science/tables.py +619 -0
- noodlelab-0.1.0/src/noodlelab/nodes/science/timeseries.py +164 -0
- noodlelab-0.1.0/src/noodlelab/nodes/symbolic/__init__.py +23 -0
- noodlelab-0.1.0/src/noodlelab/nodes/symbolic/nodes.py +634 -0
- noodlelab-0.1.0/src/noodlelab/nodes/symbolic/parse.py +264 -0
- noodlelab-0.1.0/src/noodlelab/nodes/symbolic/types.py +200 -0
- noodlelab-0.1.0/src/noodlelab/nodes/symbolic/typst.py +280 -0
- noodlelab-0.1.0/src/noodlelab/nodes/uncertainty.py +275 -0
- noodlelab-0.1.0/src/noodlelab/nodes/units.py +138 -0
- noodlelab-0.1.0/src/noodlelab/py.typed +0 -0
- noodlelab-0.1.0/src/noodlelab/reports/__init__.py +6 -0
- noodlelab-0.1.0/src/noodlelab/reports/document.py +274 -0
- noodlelab-0.1.0/src/noodlelab/reports/math.py +81 -0
- noodlelab-0.1.0/src/noodlelab/reports/render.py +46 -0
- noodlelab-0.1.0/src/noodlelab/reports/templates/default.typ +160 -0
- noodlelab-0.1.0/src/noodlelab/server/__init__.py +16 -0
- noodlelab-0.1.0/src/noodlelab/server/admin.py +410 -0
- noodlelab-0.1.0/src/noodlelab/server/agents.py +368 -0
- noodlelab-0.1.0/src/noodlelab/server/api.py +879 -0
- noodlelab-0.1.0/src/noodlelab/server/app.py +83 -0
- noodlelab-0.1.0/src/noodlelab/server/auth.py +87 -0
- noodlelab-0.1.0/src/noodlelab/server/cluster.py +406 -0
- noodlelab-0.1.0/src/noodlelab/server/hub.py +117 -0
- noodlelab-0.1.0/src/noodlelab/server/permissions.py +72 -0
- noodlelab-0.1.0/src/noodlelab/server/runs.py +306 -0
- noodlelab-0.1.0/src/noodlelab/server/sharing.py +117 -0
- noodlelab-0.1.0/src/noodlelab/server/state.py +94 -0
- noodlelab-0.1.0/src/noodlelab/server/store.py +328 -0
- noodlelab-0.1.0/src/noodlelab/server/workspaces.py +207 -0
- noodlelab-0.1.0/src/noodlelab/server/ws.py +643 -0
- noodlelab-0.1.0/src/noodlelab/static/admin.html +31 -0
- noodlelab-0.1.0/src/noodlelab/static/assets/admin-CdIecA1j.js +8 -0
- noodlelab-0.1.0/src/noodlelab/static/assets/agent-6GBZ9nXN.css +32 -0
- noodlelab-0.1.0/src/noodlelab/static/assets/agent-BSxoem_1.js +41 -0
- noodlelab-0.1.0/src/noodlelab/static/assets/editor-DWWQ0JL2.js +25 -0
- noodlelab-0.1.0/src/noodlelab/static/assets/sharing-Bnota0Ch.js +1 -0
- noodlelab-0.1.0/src/noodlelab/static/assets/sharing-DgCDYlte.css +1 -0
- noodlelab-0.1.0/src/noodlelab/static/index.html +108 -0
- noodlelab-0.1.0/src/noodlelab/static/vendor/litegraph/litegraph.css +680 -0
- noodlelab-0.1.0/src/noodlelab/static/vendor/litegraph/litegraph.js +14427 -0
- noodlelab-0.1.0/src/noodlelab/tiers.py +201 -0
- noodlelab-0.1.0/src/noodlelab/verify.py +917 -0
- noodlelab-0.1.0/tests/__init__.py +0 -0
- noodlelab-0.1.0/tests/conftest.py +50 -0
- noodlelab-0.1.0/tests/sample_nodes.py +432 -0
- noodlelab-0.1.0/tests/test_about.py +153 -0
- noodlelab-0.1.0/tests/test_agent.py +231 -0
- noodlelab-0.1.0/tests/test_agent_terminal.py +143 -0
- noodlelab-0.1.0/tests/test_api.py +493 -0
- noodlelab-0.1.0/tests/test_checkpoint.py +80 -0
- noodlelab-0.1.0/tests/test_checks.py +179 -0
- noodlelab-0.1.0/tests/test_cli.py +18 -0
- noodlelab-0.1.0/tests/test_codecs.py +246 -0
- noodlelab-0.1.0/tests/test_convert.py +168 -0
- noodlelab-0.1.0/tests/test_engineering.py +182 -0
- noodlelab-0.1.0/tests/test_examples.py +152 -0
- noodlelab-0.1.0/tests/test_executor.py +169 -0
- noodlelab-0.1.0/tests/test_export.py +25 -0
- noodlelab-0.1.0/tests/test_figures.py +131 -0
- noodlelab-0.1.0/tests/test_files.py +147 -0
- noodlelab-0.1.0/tests/test_fold.py +179 -0
- noodlelab-0.1.0/tests/test_generics.py +199 -0
- noodlelab-0.1.0/tests/test_geo_nodes.py +523 -0
- noodlelab-0.1.0/tests/test_health.py +52 -0
- noodlelab-0.1.0/tests/test_llms.py +29 -0
- noodlelab-0.1.0/tests/test_monitor.py +30 -0
- noodlelab-0.1.0/tests/test_montecarlo.py +469 -0
- noodlelab-0.1.0/tests/test_node_spec.py +72 -0
- noodlelab-0.1.0/tests/test_node_views.py +36 -0
- noodlelab-0.1.0/tests/test_permissions.py +223 -0
- noodlelab-0.1.0/tests/test_ports.py +184 -0
- noodlelab-0.1.0/tests/test_probe.py +134 -0
- noodlelab-0.1.0/tests/test_provenance.py +129 -0
- noodlelab-0.1.0/tests/test_report_tab.py +76 -0
- noodlelab-0.1.0/tests/test_reports.py +277 -0
- noodlelab-0.1.0/tests/test_reproducibility.py +29 -0
- noodlelab-0.1.0/tests/test_reqlog.py +94 -0
- noodlelab-0.1.0/tests/test_requirements.py +219 -0
- noodlelab-0.1.0/tests/test_rundiff.py +65 -0
- noodlelab-0.1.0/tests/test_sample.py +110 -0
- noodlelab-0.1.0/tests/test_science_nodes.py +340 -0
- noodlelab-0.1.0/tests/test_slurm.py +274 -0
- noodlelab-0.1.0/tests/test_studies.py +247 -0
- noodlelab-0.1.0/tests/test_subgraphs.py +160 -0
- noodlelab-0.1.0/tests/test_symbolic.py +294 -0
- noodlelab-0.1.0/tests/test_tiers.py +76 -0
- noodlelab-0.1.0/tests/test_tracked.py +135 -0
- noodlelab-0.1.0/tests/test_typesys.py +80 -0
- noodlelab-0.1.0/tests/test_uncertainty.py +221 -0
- noodlelab-0.1.0/tests/test_units.py +288 -0
- noodlelab-0.1.0/tests/test_verify.py +193 -0
- noodlelab-0.1.0/tests/test_workspaces.py +83 -0
- noodlelab-0.1.0/tests/test_zones.py +372 -0
- noodlelab-0.1.0/tests/unit_nodes.py +103 -0
- noodlelab-0.1.0/uv.lock +4300 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noodlelab
|
|
3
|
+
description: Verifiable scientific and engineering calculations with noodlelab - units and
|
|
4
|
+
uncertainty on every number, requirements checked with margins, checks, and a provenance
|
|
5
|
+
record of every run. Use whenever code computes physical quantities, measurement results,
|
|
6
|
+
design margins or other results people rely on, or when asked to build or change a
|
|
7
|
+
noodlelab graph, report or requirement.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# noodlelab for AI agents
|
|
11
|
+
|
|
12
|
+
noodlelab makes scientific and engineering calculations verifiable. Use it
|
|
13
|
+
whenever code computes a physical quantity, a measurement result, a design
|
|
14
|
+
margin or anything a person will rely on. The point is that a reviewer (or
|
|
15
|
+
another agent) can check what you did, not just trust it.
|
|
16
|
+
|
|
17
|
+
## The rules
|
|
18
|
+
|
|
19
|
+
1. **Units on every number.** Use quantities (`q("9.81 m/s^2")`), never bare
|
|
20
|
+
floats with the unit in a variable name or a comment. Mixing dimensions then
|
|
21
|
+
raises instead of silently giving nonsense. Dimensionless results say so
|
|
22
|
+
(`unit="1"`).
|
|
23
|
+
2. **Uncertainty on every measured input, and a source.** Write
|
|
24
|
+
`"9.81 ± 0.02 m/s^2"` and say where it came from (an instrument, datasheet,
|
|
25
|
+
paper or dataset). Uncertainty propagates by itself (GUM, first order, with
|
|
26
|
+
correlations kept). `budget()` shows which input dominates. For strongly
|
|
27
|
+
non-linear models, check with `monte_carlo()`.
|
|
28
|
+
3. **Write the requirements down before checking them**, one per line:
|
|
29
|
+
`COM-001 link_margin >= 3 dB [Analysis] # The link shall close with 3 dB to spare`.
|
|
30
|
+
Verify each one: a check reports its margin, not just pass or fail.
|
|
31
|
+
4. **Leave a record.** Wrap the calculation in `with nv.record(...)`, or build
|
|
32
|
+
it as a graph. Every run then writes a `provenance.json` holding inputs,
|
|
33
|
+
results, checks, the code (hash, git commit), files (SHA-256) and the
|
|
34
|
+
environment.
|
|
35
|
+
5. **Verify before you say you are done.** Run `noodlelab verify <script.py |
|
|
36
|
+
graph.json> --json` and fix what it reports. Exit code 0 means every check
|
|
37
|
+
passed and every requirement was verified. Report the margins and any
|
|
38
|
+
warnings to the user; don't hide failures.
|
|
39
|
+
|
|
40
|
+
Never invent a measured value or its uncertainty. When a number is assumed,
|
|
41
|
+
say so with `rec.note(...)` and `source="assumed: ..."`.
|
|
42
|
+
|
|
43
|
+
## Python: `noodlelab.verify`
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import noodlelab.verify as nv
|
|
47
|
+
|
|
48
|
+
with nv.record("link budget") as rec: # writes runs/<time>-link-budget-<id>/provenance.json
|
|
49
|
+
p_tx = rec.input("p_tx", "10.0 ± 0.3 W", source="PA datasheet rev C")
|
|
50
|
+
d = rec.input("d", nv.q("1200 km"), source="orbit design")
|
|
51
|
+
...
|
|
52
|
+
margin = rec.result("link_margin", computed_margin) # a quantity, e.g. in dB
|
|
53
|
+
rec.require("COM-001 link_margin >= 3 dB [Analysis] # The link shall close")
|
|
54
|
+
rec.verify("COM-001", margin) # Check(passed, margin=+1.2 dB, ...)
|
|
55
|
+
rec.expect(0 < efficiency.m < 1, "efficiency is a fraction")
|
|
56
|
+
rec.close_to("vs. textbook", result, nv.q("2.006 s"), rtol=0.01)
|
|
57
|
+
print(rec.summary())
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- `nv.q(text_or_number, unit)` returns an exact quantity. `nv.measure("x ± u unit")`
|
|
61
|
+
or `nv.measure(x, u, unit, name=...)` returns an uncertain one.
|
|
62
|
+
- `nv.requirements(text)` and `nv.verify(req, value)` work outside a record too.
|
|
63
|
+
- `@nv.traced` records every call of a function (arguments, result, source hash)
|
|
64
|
+
inside a record.
|
|
65
|
+
- `nv.monte_carlo(model, trials)`, where `model` makes its inputs with
|
|
66
|
+
`measure()` and returns the result, checks whether the GUM result can be
|
|
67
|
+
trusted (JCGM 101).
|
|
68
|
+
- `nv.audit(record)` lists findings NL001–NL010: failed checks, unverified
|
|
69
|
+
requirements, results without units or uncertainty, inputs without a source,
|
|
70
|
+
and code that was dirty or has changed since.
|
|
71
|
+
|
|
72
|
+
## Graphs: node pipelines the user can open in the editor
|
|
73
|
+
|
|
74
|
+
A graph is a JSON file `<name>.graph.json` in the workspace. Nodes are typed
|
|
75
|
+
Python functions (`list_nodes`, `describe_node`). Every input is either a
|
|
76
|
+
value or a link to another node's output:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{"nodes": [
|
|
80
|
+
{"id": "1", "type": "core.number", "inputs": {"value": {"value": 3}}},
|
|
81
|
+
{"id": "2", "type": "core.math",
|
|
82
|
+
"inputs": {"operation": {"value": "power"},
|
|
83
|
+
"a": {"link": {"node": "1", "output": "result"}},
|
|
84
|
+
"b": {"value": 2}}}
|
|
85
|
+
],
|
|
86
|
+
"report": [
|
|
87
|
+
{"id": "1", "type": "report.new_report", "inputs": {"title": {"value": "Results"}}},
|
|
88
|
+
{"id": "2", "type": "report.add_value",
|
|
89
|
+
"inputs": {"report": {"link": {"node": "1", "output": "result"}},
|
|
90
|
+
"label": {"value": "3 squared"},
|
|
91
|
+
"value": {"link": {"node": "2", "output": "result", "tab": "processing"}}}},
|
|
92
|
+
{"id": "3", "type": "report.render_report",
|
|
93
|
+
"inputs": {"report": {"link": {"node": "2", "output": "result"}}}}
|
|
94
|
+
]}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
- `nodes` is the processing canvas and `report` is the Reporting canvas. A
|
|
98
|
+
report node reads a processing output with `"tab": "processing"` in its
|
|
99
|
+
link. Data only flows from processing into the report.
|
|
100
|
+
- The usual shape for a verified analysis:
|
|
101
|
+
- **Requirements** (`requirements.requirements`, one per line), then the
|
|
102
|
+
inputs, with units and uncertainty (`units.*`, `uncertainty.*`), then the
|
|
103
|
+
model.
|
|
104
|
+
- **Verify Requirement** for each requirement, chaining its `log` output
|
|
105
|
+
from one to the next.
|
|
106
|
+
- A report: **New Report**, Add Heading / Text / Value / Figure /
|
|
107
|
+
Requirements, then Add Compliance Matrix (from the last `log`), Run
|
|
108
|
+
Details (provenance), and Render Report (the PDF).
|
|
109
|
+
- Start from a similar example (`list_examples`, `get_example`). Example 23
|
|
110
|
+
(satellite link budget) and example 24 (cantilever bracket) are complete
|
|
111
|
+
verified studies with reports.
|
|
112
|
+
|
|
113
|
+
## MCP tools (server `noodlelab mcp`)
|
|
114
|
+
|
|
115
|
+
| Tool | Use |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `guide` | this text |
|
|
118
|
+
| `list_nodes`, `describe_node` | find nodes and their inputs, outputs, units and options |
|
|
119
|
+
| `list_examples`, `get_example` | complete graphs to copy from |
|
|
120
|
+
| `list_graphs`, `get_graph` | the workspace's graphs |
|
|
121
|
+
| `save_graph` | write a whole graph (validated; the open editor reloads it) |
|
|
122
|
+
| `edit_graph` | add, set, remove, retitle or track nodes without resending the graph |
|
|
123
|
+
| `check_graph` | problems before running: types, units, missing inputs |
|
|
124
|
+
| `run_graph` | run it; returns each node's result, checks, requirements, files and provenance |
|
|
125
|
+
| `requirements` | each requirement's latest verdict, margin and history |
|
|
126
|
+
| `verify` | audit a script, graph or records, as `noodlelab verify --json` does |
|
|
127
|
+
|
|
128
|
+
When you are started from the editor's Agent panel, the user is watching the
|
|
129
|
+
canvas: every graph you save opens there, laid out automatically.
|
|
130
|
+
`NOODLELAB_GRAPH` names the graph they had open.
|
|
131
|
+
|
|
132
|
+
The workflow:
|
|
133
|
+
1. `guide`, then `list_examples` / `list_nodes`.
|
|
134
|
+
2. `save_graph`, then `check_graph` until there are no errors.
|
|
135
|
+
3. `run_graph`, then read the checks and margins.
|
|
136
|
+
4. Fix and iterate. Finish with `verify`.
|
|
137
|
+
5. Tell the user what passed, the margins, what was assumed, and where the
|
|
138
|
+
report PDF and provenance are.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// "Open in GitHub Codespaces": a private, throwaway copy of noodlelab for each visitor.
|
|
2
|
+
// A codespace is billed to the account that creates it (GitHub includes free monthly
|
|
3
|
+
// hours), not to this repository's owner.
|
|
4
|
+
{
|
|
5
|
+
"name": "noodlelab",
|
|
6
|
+
"image": "mcr.microsoft.com/devcontainers/python:3.12",
|
|
7
|
+
"features": {
|
|
8
|
+
"ghcr.io/devcontainers/features/node:1": { "version": "22" }
|
|
9
|
+
},
|
|
10
|
+
"postCreateCommand": "pip install --user uv && uv sync --extra full && cd frontend && npm ci && npm run build",
|
|
11
|
+
// A workspace outside the repository, so trying things out leaves git clean.
|
|
12
|
+
"postAttachCommand": "mkdir -p ~/noodlelab-workspace && uv run noodlelab serve ~/noodlelab-workspace --no-browser",
|
|
13
|
+
"forwardPorts": [8000],
|
|
14
|
+
"portsAttributes": {
|
|
15
|
+
"8000": { "label": "noodlelab", "onAutoForward": "openBrowser" }
|
|
16
|
+
},
|
|
17
|
+
"customizations": {
|
|
18
|
+
"vscode": { "extensions": ["ms-python.python", "charliermarsh.ruff"] },
|
|
19
|
+
"codespaces": { "openFiles": ["README.md"] }
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
# the release workflow runs these checks before it builds anything
|
|
8
|
+
workflow_call:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
python:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
strategy:
|
|
17
|
+
matrix:
|
|
18
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
with:
|
|
22
|
+
fetch-depth: 0 # the version comes from the git tags
|
|
23
|
+
- uses: astral-sh/setup-uv@v6
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
enable-cache: true
|
|
27
|
+
- run: uv sync --locked --all-extras
|
|
28
|
+
- run: uv run ruff check .
|
|
29
|
+
- run: uv run ruff format --check .
|
|
30
|
+
- run: uv run pytest -q
|
|
31
|
+
|
|
32
|
+
frontend:
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
defaults:
|
|
35
|
+
run:
|
|
36
|
+
working-directory: frontend
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
|
+
- uses: actions/setup-node@v4
|
|
40
|
+
with:
|
|
41
|
+
node-version: 22
|
|
42
|
+
cache: npm
|
|
43
|
+
cache-dependency-path: frontend/package-lock.json
|
|
44
|
+
- run: npm ci
|
|
45
|
+
- run: npm run typecheck
|
|
46
|
+
- run: npm run build
|
|
47
|
+
|
|
48
|
+
# Each installation tier on its own: exactly its packs load (a bigger tier's
|
|
49
|
+
# dependency leaking into a smaller extra fails here), and every example it
|
|
50
|
+
# covers runs.
|
|
51
|
+
tiers:
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
strategy:
|
|
54
|
+
fail-fast: false
|
|
55
|
+
matrix:
|
|
56
|
+
tier: [library, basic, maths, science, engineering, geo, full]
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
with:
|
|
60
|
+
fetch-depth: 0
|
|
61
|
+
- uses: astral-sh/setup-uv@v6
|
|
62
|
+
with:
|
|
63
|
+
python-version: "3.12"
|
|
64
|
+
enable-cache: true
|
|
65
|
+
- name: Install the ${{ matrix.tier }} tier
|
|
66
|
+
run: |
|
|
67
|
+
uv venv .tier
|
|
68
|
+
case "${{ matrix.tier }}" in
|
|
69
|
+
library) spec="." ;;
|
|
70
|
+
basic) spec=".[app]" ;;
|
|
71
|
+
*) spec=".[${{ matrix.tier }}]" ;;
|
|
72
|
+
esac
|
|
73
|
+
VIRTUAL_ENV=.tier uv pip install "$spec"
|
|
74
|
+
- name: Check its packs and run its examples
|
|
75
|
+
run: .tier/bin/python scripts/check_tier.py ${{ matrix.tier }} --examples
|
|
76
|
+
|
|
77
|
+
docker:
|
|
78
|
+
runs-on: ubuntu-latest
|
|
79
|
+
needs: [python, frontend]
|
|
80
|
+
steps:
|
|
81
|
+
- uses: actions/checkout@v4
|
|
82
|
+
- uses: docker/setup-buildx-action@v3
|
|
83
|
+
- uses: docker/build-push-action@v6
|
|
84
|
+
with:
|
|
85
|
+
context: .
|
|
86
|
+
push: false
|
|
87
|
+
tags: noodlelab:ci
|
|
88
|
+
cache-from: type=gha,scope=full
|
|
89
|
+
cache-to: type=gha,mode=max,scope=full
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Push a version tag to release it:
|
|
4
|
+
#
|
|
5
|
+
# git tag v0.3.0 && git push origin v0.3.0 # PyPI, GitHub Release, Docker images
|
|
6
|
+
# git tag v0.3.0rc1 && git push origin v0.3.0rc1 # a pre-release: TestPyPI instead of PyPI
|
|
7
|
+
#
|
|
8
|
+
# checks -> build -> smoke -> GitHub Release -> PyPI / TestPyPI + Docker (GHCR) -> verify
|
|
9
|
+
#
|
|
10
|
+
# One-time setup (see docs/releasing.md): trusted publishers on PyPI and TestPyPI
|
|
11
|
+
# for this workflow, with the environments "pypi" and "testpypi".
|
|
12
|
+
on:
|
|
13
|
+
push:
|
|
14
|
+
tags: ["v*"]
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
concurrency:
|
|
20
|
+
group: release-${{ github.ref }}
|
|
21
|
+
cancel-in-progress: false
|
|
22
|
+
|
|
23
|
+
env:
|
|
24
|
+
TAG: ${{ github.ref_name }}
|
|
25
|
+
|
|
26
|
+
jobs:
|
|
27
|
+
checks:
|
|
28
|
+
uses: ./.github/workflows/ci.yml
|
|
29
|
+
|
|
30
|
+
build:
|
|
31
|
+
needs: checks
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
outputs:
|
|
34
|
+
version: ${{ steps.version.outputs.version }}
|
|
35
|
+
prerelease: ${{ steps.version.outputs.prerelease }}
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v4
|
|
38
|
+
with:
|
|
39
|
+
fetch-depth: 0 # hatch-vcs reads the version from the tag
|
|
40
|
+
- name: Version from the tag
|
|
41
|
+
id: version
|
|
42
|
+
run: |
|
|
43
|
+
version="${TAG#v}"
|
|
44
|
+
if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+((a|b|rc)[0-9]+)?$ ]]; then
|
|
45
|
+
echo "::error::Tag $TAG is not vX.Y.Z or vX.Y.Z(a|b|rc)N"
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
49
|
+
if [[ "$version" =~ (a|b|rc) ]]; then pre=true; else pre=false; fi
|
|
50
|
+
echo "prerelease=$pre" >> "$GITHUB_OUTPUT"
|
|
51
|
+
echo "Releasing $version (pre-release: $pre)"
|
|
52
|
+
- uses: actions/setup-node@v4
|
|
53
|
+
with:
|
|
54
|
+
node-version: 22
|
|
55
|
+
cache: npm
|
|
56
|
+
cache-dependency-path: frontend/package-lock.json
|
|
57
|
+
- name: Build the editor into the package
|
|
58
|
+
working-directory: frontend
|
|
59
|
+
run: npm ci && npm run build
|
|
60
|
+
- uses: astral-sh/setup-uv@v6
|
|
61
|
+
- run: uv build
|
|
62
|
+
- name: Check the distributions
|
|
63
|
+
env:
|
|
64
|
+
VERSION: ${{ steps.version.outputs.version }}
|
|
65
|
+
run: |
|
|
66
|
+
ls -l dist
|
|
67
|
+
wheel="dist/noodlelab-${VERSION}-py3-none-any.whl"
|
|
68
|
+
test -f "$wheel" || { echo "::error::No $wheel: the built version is not the tag's"; exit 1; }
|
|
69
|
+
test -f "dist/noodlelab-${VERSION}.tar.gz"
|
|
70
|
+
for page in index.html admin.html; do
|
|
71
|
+
unzip -l "$wheel" | grep -q "noodlelab/static/$page" \
|
|
72
|
+
|| { echo "::error::The wheel has no editor ($page)"; exit 1; }
|
|
73
|
+
done
|
|
74
|
+
uvx twine check --strict dist/*
|
|
75
|
+
- uses: actions/upload-artifact@v4
|
|
76
|
+
with:
|
|
77
|
+
name: dist
|
|
78
|
+
path: dist/
|
|
79
|
+
if-no-files-found: error
|
|
80
|
+
|
|
81
|
+
# The wheel installs and serves on every OS, before anything is published.
|
|
82
|
+
smoke:
|
|
83
|
+
needs: build
|
|
84
|
+
strategy:
|
|
85
|
+
fail-fast: false
|
|
86
|
+
matrix:
|
|
87
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
88
|
+
tier: [basic]
|
|
89
|
+
include:
|
|
90
|
+
- os: ubuntu-latest
|
|
91
|
+
tier: full
|
|
92
|
+
runs-on: ${{ matrix.os }}
|
|
93
|
+
defaults:
|
|
94
|
+
run:
|
|
95
|
+
shell: bash
|
|
96
|
+
env:
|
|
97
|
+
VERSION: ${{ needs.build.outputs.version }}
|
|
98
|
+
TIER: ${{ matrix.tier }}
|
|
99
|
+
steps:
|
|
100
|
+
- uses: actions/download-artifact@v4
|
|
101
|
+
with:
|
|
102
|
+
name: dist
|
|
103
|
+
path: dist
|
|
104
|
+
- uses: astral-sh/setup-uv@v6
|
|
105
|
+
with:
|
|
106
|
+
python-version: "3.12"
|
|
107
|
+
- name: Install the wheel as a uv tool
|
|
108
|
+
run: |
|
|
109
|
+
if [ "$TIER" = basic ]; then spec="noodlelab[app]==$VERSION"; else spec="noodlelab[$TIER]==$VERSION"; fi
|
|
110
|
+
uv tool install "$spec" --find-links dist
|
|
111
|
+
uv tool dir --bin >> "$GITHUB_PATH"
|
|
112
|
+
- name: Version and tier
|
|
113
|
+
run: |
|
|
114
|
+
noodlelab --version | tee version.txt
|
|
115
|
+
grep -qF "$VERSION" version.txt
|
|
116
|
+
noodlelab tiers --json > tiers.json
|
|
117
|
+
uv run --no-project python -c "import json; r = json.load(open('tiers.json')); assert r['installed'] == '$TIER', r"
|
|
118
|
+
- name: The server starts
|
|
119
|
+
run: |
|
|
120
|
+
mkdir ws
|
|
121
|
+
noodlelab serve ws --no-browser --port 8123 > serve.log 2>&1 &
|
|
122
|
+
for _ in $(seq 1 60); do
|
|
123
|
+
if curl -fsS http://127.0.0.1:8123/api/health; then exit 0; fi
|
|
124
|
+
sleep 1
|
|
125
|
+
done
|
|
126
|
+
cat serve.log
|
|
127
|
+
exit 1
|
|
128
|
+
|
|
129
|
+
github-release:
|
|
130
|
+
needs: [build, smoke]
|
|
131
|
+
runs-on: ubuntu-latest
|
|
132
|
+
permissions:
|
|
133
|
+
contents: write
|
|
134
|
+
env:
|
|
135
|
+
VERSION: ${{ needs.build.outputs.version }}
|
|
136
|
+
PRERELEASE: ${{ needs.build.outputs.prerelease }}
|
|
137
|
+
GH_TOKEN: ${{ github.token }}
|
|
138
|
+
GH_REPO: ${{ github.repository }}
|
|
139
|
+
steps:
|
|
140
|
+
- uses: actions/download-artifact@v4
|
|
141
|
+
with:
|
|
142
|
+
name: dist
|
|
143
|
+
path: dist
|
|
144
|
+
- name: Create the release (or update its files when re-run)
|
|
145
|
+
run: |
|
|
146
|
+
if gh release view "$TAG" > /dev/null 2>&1; then
|
|
147
|
+
gh release upload "$TAG" dist/* --clobber
|
|
148
|
+
else
|
|
149
|
+
flags=()
|
|
150
|
+
if [ "$PRERELEASE" = true ]; then flags+=(--prerelease); fi
|
|
151
|
+
gh release create "$TAG" dist/* --verify-tag --generate-notes \
|
|
152
|
+
--title "noodlelab $VERSION" "${flags[@]}"
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
publish-pypi:
|
|
156
|
+
needs: [build, github-release]
|
|
157
|
+
if: needs.build.outputs.prerelease == 'false'
|
|
158
|
+
runs-on: ubuntu-latest
|
|
159
|
+
environment:
|
|
160
|
+
name: pypi
|
|
161
|
+
url: https://pypi.org/project/noodlelab/${{ needs.build.outputs.version }}/
|
|
162
|
+
permissions:
|
|
163
|
+
id-token: write # trusted publishing: no API token
|
|
164
|
+
steps:
|
|
165
|
+
- uses: actions/download-artifact@v4
|
|
166
|
+
with:
|
|
167
|
+
name: dist
|
|
168
|
+
path: dist
|
|
169
|
+
- uses: astral-sh/setup-uv@v6
|
|
170
|
+
- run: uv publish --trusted-publishing always --check-url https://pypi.org/simple/
|
|
171
|
+
|
|
172
|
+
publish-testpypi:
|
|
173
|
+
needs: [build, github-release]
|
|
174
|
+
if: needs.build.outputs.prerelease == 'true'
|
|
175
|
+
runs-on: ubuntu-latest
|
|
176
|
+
environment:
|
|
177
|
+
name: testpypi
|
|
178
|
+
url: https://test.pypi.org/project/noodlelab/${{ needs.build.outputs.version }}/
|
|
179
|
+
permissions:
|
|
180
|
+
id-token: write
|
|
181
|
+
steps:
|
|
182
|
+
- uses: actions/download-artifact@v4
|
|
183
|
+
with:
|
|
184
|
+
name: dist
|
|
185
|
+
path: dist
|
|
186
|
+
- uses: astral-sh/setup-uv@v6
|
|
187
|
+
- run: >-
|
|
188
|
+
uv publish --trusted-publishing always
|
|
189
|
+
--publish-url https://test.pypi.org/legacy/
|
|
190
|
+
--check-url https://test.pypi.org/simple/
|
|
191
|
+
|
|
192
|
+
# One image per tier, for amd64 and arm64:
|
|
193
|
+
# ghcr.io/<owner>/noodlelab:0.3.0 the full tier (also :0.3.0-full)
|
|
194
|
+
# ghcr.io/<owner>/noodlelab:0.3.0-maths a smaller tier
|
|
195
|
+
# A final release also moves :0.3(-<tier>), :<tier> and :latest (the full tier).
|
|
196
|
+
docker:
|
|
197
|
+
needs: [build, github-release]
|
|
198
|
+
runs-on: ubuntu-latest
|
|
199
|
+
permissions:
|
|
200
|
+
contents: read
|
|
201
|
+
packages: write
|
|
202
|
+
strategy:
|
|
203
|
+
fail-fast: false
|
|
204
|
+
matrix:
|
|
205
|
+
tier: [basic, maths, science, engineering, geo, full]
|
|
206
|
+
env:
|
|
207
|
+
VERSION: ${{ needs.build.outputs.version }}
|
|
208
|
+
PRERELEASE: ${{ needs.build.outputs.prerelease }}
|
|
209
|
+
TIER: ${{ matrix.tier }}
|
|
210
|
+
steps:
|
|
211
|
+
- uses: actions/checkout@v4
|
|
212
|
+
- name: Image tags
|
|
213
|
+
id: tags
|
|
214
|
+
run: |
|
|
215
|
+
image="ghcr.io/${GITHUB_REPOSITORY,,}"
|
|
216
|
+
minor="$(echo "$VERSION" | cut -d. -f1,2)"
|
|
217
|
+
tags="$image:$VERSION-$TIER"
|
|
218
|
+
if [ "$TIER" = full ]; then tags="$tags,$image:$VERSION"; fi
|
|
219
|
+
if [ "$PRERELEASE" = false ]; then
|
|
220
|
+
tags="$tags,$image:$minor-$TIER,$image:$TIER"
|
|
221
|
+
if [ "$TIER" = full ]; then tags="$tags,$image:$minor,$image:latest"; fi
|
|
222
|
+
fi
|
|
223
|
+
echo "tags=$tags" >> "$GITHUB_OUTPUT"
|
|
224
|
+
echo "$tags" | tr , '\n'
|
|
225
|
+
- uses: docker/setup-qemu-action@v3
|
|
226
|
+
- uses: docker/setup-buildx-action@v3
|
|
227
|
+
- uses: docker/login-action@v3
|
|
228
|
+
with:
|
|
229
|
+
registry: ghcr.io
|
|
230
|
+
username: ${{ github.actor }}
|
|
231
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
232
|
+
- uses: docker/build-push-action@v6
|
|
233
|
+
with:
|
|
234
|
+
context: .
|
|
235
|
+
platforms: linux/amd64,linux/arm64
|
|
236
|
+
push: true
|
|
237
|
+
tags: ${{ steps.tags.outputs.tags }}
|
|
238
|
+
build-args: |
|
|
239
|
+
NOODLELAB_TIER=${{ matrix.tier }}
|
|
240
|
+
NOODLELAB_VERSION=${{ needs.build.outputs.version }}
|
|
241
|
+
labels: |
|
|
242
|
+
org.opencontainers.image.title=noodlelab (${{ matrix.tier }})
|
|
243
|
+
org.opencontainers.image.version=${{ needs.build.outputs.version }}
|
|
244
|
+
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
|
|
245
|
+
org.opencontainers.image.revision=${{ github.sha }}
|
|
246
|
+
cache-from: type=gha,scope=${{ matrix.tier }}
|
|
247
|
+
cache-to: type=gha,mode=max,scope=${{ matrix.tier }}
|
|
248
|
+
|
|
249
|
+
# What was published installs: from the package index with uv, and from GHCR.
|
|
250
|
+
verify:
|
|
251
|
+
needs: [build, publish-pypi, publish-testpypi, docker]
|
|
252
|
+
if: >-
|
|
253
|
+
always() && needs.docker.result == 'success' &&
|
|
254
|
+
(needs.publish-pypi.result == 'success' || needs.publish-testpypi.result == 'success')
|
|
255
|
+
runs-on: ubuntu-latest
|
|
256
|
+
permissions:
|
|
257
|
+
contents: read
|
|
258
|
+
packages: read
|
|
259
|
+
env:
|
|
260
|
+
VERSION: ${{ needs.build.outputs.version }}
|
|
261
|
+
PRERELEASE: ${{ needs.build.outputs.prerelease }}
|
|
262
|
+
steps:
|
|
263
|
+
- uses: astral-sh/setup-uv@v6
|
|
264
|
+
- name: uvx runs the published package
|
|
265
|
+
run: |
|
|
266
|
+
index=()
|
|
267
|
+
if [ "$PRERELEASE" = true ]; then
|
|
268
|
+
index=(--index https://test.pypi.org/simple/ --index-strategy unsafe-best-match)
|
|
269
|
+
fi
|
|
270
|
+
# the index can take a few minutes to list a new release
|
|
271
|
+
for _ in $(seq 1 20); do
|
|
272
|
+
if uvx --refresh "${index[@]}" --from "noodlelab[maths]==$VERSION" noodlelab tiers; then
|
|
273
|
+
exit 0
|
|
274
|
+
fi
|
|
275
|
+
sleep 15
|
|
276
|
+
done
|
|
277
|
+
exit 1
|
|
278
|
+
- uses: docker/login-action@v3
|
|
279
|
+
with:
|
|
280
|
+
registry: ghcr.io
|
|
281
|
+
username: ${{ github.actor }}
|
|
282
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
283
|
+
- name: The images run
|
|
284
|
+
run: |
|
|
285
|
+
image="ghcr.io/${GITHUB_REPOSITORY,,}"
|
|
286
|
+
docker run --rm "$image:$VERSION-basic" noodlelab --version | grep -F "$VERSION"
|
|
287
|
+
docker run --rm "$image:$VERSION" noodlelab tiers
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
.venv/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
*.egg-info/
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
.ruff_cache/
|
|
10
|
+
|
|
11
|
+
# Frontend
|
|
12
|
+
frontend/node_modules/
|
|
13
|
+
frontend/public/vendor/
|
|
14
|
+
src/noodlelab/static/*
|
|
15
|
+
!src/noodlelab/static/.gitkeep
|
|
16
|
+
|
|
17
|
+
# Runtime data (workspace state; run outputs are yours to keep or ignore)
|
|
18
|
+
.noodlelab/
|
|
19
|
+
noodlelab-data/
|
|
20
|
+
noodlelab-output/
|
|
21
|
+
.env
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Notes for AI coding agents (Claude Code, Codex CLI, Cursor, Copilot and
|
|
4
|
+
others). There are two cases: **using** noodlelab in someone's project, and
|
|
5
|
+
**working on** this repository.
|
|
6
|
+
|
|
7
|
+
## Using noodlelab in a project
|
|
8
|
+
|
|
9
|
+
noodlelab makes calculations verifiable. Every number carries its unit and
|
|
10
|
+
uncertainty, requirements are checked with margins, and every run leaves a
|
|
11
|
+
provenance record. When a user asks you to "use noodlelab", "make this
|
|
12
|
+
verifiable", or to build a noodlelab graph or report:
|
|
13
|
+
|
|
14
|
+
1. `pip install noodlelab`, or `uv add noodlelab` (the library: `noodlelab.verify`).
|
|
15
|
+
Add `[app]` for the browser editor, and `[full]` for every node pack.
|
|
16
|
+
2. Run `noodlelab init-agent` in the project. It writes an `AGENTS.md` section,
|
|
17
|
+
a `CLAUDE.md` import, a skill (`.claude/skills/noodlelab/SKILL.md`) and the
|
|
18
|
+
MCP server in `.mcp.json`. For Codex, run
|
|
19
|
+
`codex mcp add noodlelab -- noodlelab mcp`.
|
|
20
|
+
3. Read [`src/noodlelab/agent/guide.md`](src/noodlelab/agent/guide.md) (the MCP
|
|
21
|
+
`guide` tool returns the same text). It has the rules, the Python API, the
|
|
22
|
+
graph format and the workflow.
|
|
23
|
+
4. Before saying you are done, run `noodlelab verify <script.py | graph.json> --json`.
|
|
24
|
+
Exit code 0 means every check passed and every requirement was verified.
|
|
25
|
+
|
|
26
|
+
Recipes:
|
|
27
|
+
- [docs/agents/quickstart.md](docs/agents/quickstart.md): a verified calculation in 10 lines
|
|
28
|
+
- [docs/agents/adopt-in-existing-project.md](docs/agents/adopt-in-existing-project.md):
|
|
29
|
+
the steps for "extend my project to use noodlelab"
|
|
30
|
+
- [docs/agents/graphs-and-reports.md](docs/agents/graphs-and-reports.md): building
|
|
31
|
+
graphs and PDF reports over MCP or as JSON
|
|
32
|
+
- [llms.txt](llms.txt): an index of everything, and
|
|
33
|
+
[docs/llms-full.txt](docs/llms-full.txt), all the API docs and every node in one file
|
|
34
|
+
|
|
35
|
+
## Working on this repository
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
uv sync --all-extras # Python env with every pack and the dev tools
|
|
39
|
+
cd frontend && npm ci && cd .. # editor dependencies
|
|
40
|
+
make test # uv run pytest -q (parallel; -n0 for serial)
|
|
41
|
+
make lint # ruff check + ruff format --check + tsc
|
|
42
|
+
make frontend # build the editor into src/noodlelab/static
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
CI (`.github/workflows/ci.yml`) runs lint and tests on Python 3.11–3.13, the
|
|
46
|
+
frontend build, and each installation tier on its own (`scripts/check_tier.py`).
|
|
47
|
+
After changing a docstring, a node or the guide, run
|
|
48
|
+
`uv run python scripts/gen_llms.py` (a test fails when `llms.txt` is stale).
|
|
49
|
+
|
|
50
|
+
### Map
|
|
51
|
+
|
|
52
|
+
| Path | What |
|
|
53
|
+
|---|---|
|
|
54
|
+
| `src/noodlelab/verify.py` | `noodlelab.verify`: records, measure/q, requirements, audit, Monte Carlo check |
|
|
55
|
+
| `src/noodlelab/core/` | the engine, independent of the editor: graph model (`graph.py`), `executor.py`, `units.py`, `uncertainty.py` (GUM), `montecarlo.py` (JCGM 101), `requirements.py`, `checks.py`, `provenance.py`, `tracked.py` / `reqlog.py` (run histories), `registry.py`, `typesys.py` |
|
|
56
|
+
| `src/noodlelab/nodes/` | the built-in node packs (entry points in `pyproject.toml`), one per tier |
|
|
57
|
+
| `src/noodlelab/agent/` | agent support: `guide.md`, the MCP server (`mcp.py`, `tools.py`), `init-agent` |
|
|
58
|
+
| `src/noodlelab/server/` | FastAPI app (`app.py`), REST API (`api.py`), WebSocket runs (`ws.py`), workspaces, auth, Slurm, agent terminal |
|
|
59
|
+
| `src/noodlelab/reports/` | Typst PDF reports |
|
|
60
|
+
| `src/noodlelab/examples/` | 24 example graphs (`*.graph.json`) with data: the best reference for graph JSON |
|
|
61
|
+
| `src/noodlelab/cli.py` | `noodlelab serve / run / test / verify / mcp / init-agent / nodes / export ...` |
|
|
62
|
+
| `src/noodlelab/tiers.py` | installation tiers (library, basic = `[app]`, maths, science, engineering, geo, full) |
|
|
63
|
+
| `frontend/src/` | the editor: TypeScript + litegraph, no framework (`main.ts` wires everything) |
|
|
64
|
+
| `tests/` | pytest; `conftest.py` has the `registry`, `packs` and `executor` fixtures and the `graph`/`link`/`value` helpers |
|
|
65
|
+
|
|
66
|
+
### Conventions
|
|
67
|
+
|
|
68
|
+
- Docstrings and comments are plain prose saying *why*. Modules open with a
|
|
69
|
+
docstring that explains the concept (read `core/graph.py` and
|
|
70
|
+
`core/uncertainty.py` for the style).
|
|
71
|
+
- A node is a type-hinted function with `@node`. Its hints decide the sockets,
|
|
72
|
+
units (`Quantity["m"]`) and widgets (`Annotated[int, Param(min=1)]`), and its
|
|
73
|
+
docstring is the help text. A node pack checks its dependencies with
|
|
74
|
+
`noodlelab.tiers.require()`.
|
|
75
|
+
- Anything that records history or provenance must never fail a run: catch,
|
|
76
|
+
log, and carry on.
|
|
77
|
+
- The bare library must not import the web server or typst at module level
|
|
78
|
+
(CI's `library` tier checks this).
|
|
79
|
+
- Line length 100, ruff rules E, F, I, UP, B, SIM. TypeScript is checked with
|
|
80
|
+
`tsc --noEmit`.
|
|
81
|
+
- Tests describe behaviour in their names
|
|
82
|
+
(`test_a_run_without_requirements_records_nothing`), and use real graphs
|
|
83
|
+
through `executor.run(graph(...))` rather than mocks.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|