biosimulant 0.0.10__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.
Files changed (108) hide show
  1. biosimulant-0.0.10/.gitignore +22 -0
  2. biosimulant-0.0.10/LICENSE.txt +21 -0
  3. biosimulant-0.0.10/PKG-INFO +398 -0
  4. biosimulant-0.0.10/README.md +352 -0
  5. biosimulant-0.0.10/docs/PUBLIC_INTERNAL_BOUNDARY.md +20 -0
  6. biosimulant-0.0.10/docs/README.md +37 -0
  7. biosimulant-0.0.10/docs/biomodule.md +106 -0
  8. biosimulant-0.0.10/docs/bioworld.md +65 -0
  9. biosimulant-0.0.10/docs/book.md +49 -0
  10. biosimulant-0.0.10/docs/brain_pipeline.md +26 -0
  11. biosimulant-0.0.10/docs/cellml.md +123 -0
  12. biosimulant-0.0.10/docs/config.md +54 -0
  13. biosimulant-0.0.10/docs/kernel-1-5.md +68 -0
  14. biosimulant-0.0.10/docs/migration-1-5.md +70 -0
  15. biosimulant-0.0.10/docs/neuro.md +483 -0
  16. biosimulant-0.0.10/docs/overview.md +50 -0
  17. biosimulant-0.0.10/docs/packaging.md +196 -0
  18. biosimulant-0.0.10/docs/plugin-development.md +60 -0
  19. biosimulant-0.0.10/docs/quickstart.md +87 -0
  20. biosimulant-0.0.10/docs/releasing.md +58 -0
  21. biosimulant-0.0.10/docs/wiring.md +41 -0
  22. biosimulant-0.0.10/examples/README.md +43 -0
  23. biosimulant-0.0.10/examples/__init__.py +0 -0
  24. biosimulant-0.0.10/examples/basic_usage.py +33 -0
  25. biosimulant-0.0.10/examples/cellml_runtime_example.py +92 -0
  26. biosimulant-0.0.10/examples/data/small_decay.cellml +35 -0
  27. biosimulant-0.0.10/examples/multi_module_ui_demo.py +154 -0
  28. biosimulant-0.0.10/examples/ui_demo.py +50 -0
  29. biosimulant-0.0.10/examples/visuals_demo.py +55 -0
  30. biosimulant-0.0.10/examples/wiring_builder_demo.py +82 -0
  31. biosimulant-0.0.10/examples/world_simulation.py +124 -0
  32. biosimulant-0.0.10/pyproject.toml +130 -0
  33. biosimulant-0.0.10/scripts/build_simui_frontend.sh +36 -0
  34. biosimulant-0.0.10/scripts/check_public_boundary.sh +19 -0
  35. biosimulant-0.0.10/scripts/release_pypi.sh +93 -0
  36. biosimulant-0.0.10/scripts/snapshot_biomodule_outputs.py +318 -0
  37. biosimulant-0.0.10/src/biosim/__about__.py +4 -0
  38. biosimulant-0.0.10/src/biosim/__init__.py +100 -0
  39. biosimulant-0.0.10/src/biosim/__main__.py +438 -0
  40. biosimulant-0.0.10/src/biosim/contrib/__init__.py +9 -0
  41. biosimulant-0.0.10/src/biosim/contrib/cellml.py +878 -0
  42. biosimulant-0.0.10/src/biosim/contrib/sbml.py +660 -0
  43. biosimulant-0.0.10/src/biosim/modules.py +230 -0
  44. biosimulant-0.0.10/src/biosim/onnx.py +225 -0
  45. biosimulant-0.0.10/src/biosim/pack.py +1413 -0
  46. biosimulant-0.0.10/src/biosim/runtime/__init__.py +41 -0
  47. biosimulant-0.0.10/src/biosim/runtime/coercion.py +339 -0
  48. biosimulant-0.0.10/src/biosim/runtime/entrypoint.py +100 -0
  49. biosimulant-0.0.10/src/biosim/runtime/flatten.py +137 -0
  50. biosimulant-0.0.10/src/biosim/runtime/runtime_config.py +97 -0
  51. biosimulant-0.0.10/src/biosim/runtime/types.py +57 -0
  52. biosimulant-0.0.10/src/biosim/signals.py +940 -0
  53. biosimulant-0.0.10/src/biosim/simui/__init__.py +18 -0
  54. biosimulant-0.0.10/src/biosim/simui/build.py +86 -0
  55. biosimulant-0.0.10/src/biosim/simui/editor_api.py +459 -0
  56. biosimulant-0.0.10/src/biosim/simui/graph.py +442 -0
  57. biosimulant-0.0.10/src/biosim/simui/interface.py +584 -0
  58. biosimulant-0.0.10/src/biosim/simui/registry.py +274 -0
  59. biosimulant-0.0.10/src/biosim/simui/runner.py +186 -0
  60. biosimulant-0.0.10/src/biosim/simui/static/app.css +1 -0
  61. biosimulant-0.0.10/src/biosim/simui/static/app.js +63 -0
  62. biosimulant-0.0.10/src/biosim/visuals.py +72 -0
  63. biosimulant-0.0.10/src/biosim/wiring.py +175 -0
  64. biosimulant-0.0.10/src/biosim/world.py +555 -0
  65. biosimulant-0.0.10/src/biosimulant/__init__.py +52 -0
  66. biosimulant-0.0.10/src/biosimulant/__main__.py +19 -0
  67. biosimulant-0.0.10/src/biosimulant/simui/__init__.py +16 -0
  68. biosimulant-0.0.10/tests/__init__.py +3 -0
  69. biosimulant-0.0.10/tests/conftest.py +22 -0
  70. biosimulant-0.0.10/tests/snapshots/README.md +49 -0
  71. biosimulant-0.0.10/tests/snapshots/biomodule_refactor_fast.json +3146 -0
  72. biosimulant-0.0.10/tests/snapshots/biomodule_refactor_koo2013_integrated_amd64.json +1769 -0
  73. biosimulant-0.0.10/tests/test_api.py +5 -0
  74. biosimulant-0.0.10/tests/test_biomodules.py +68 -0
  75. biosimulant-0.0.10/tests/test_biosignals.py +125 -0
  76. biosimulant-0.0.10/tests/test_biosim_init.py +41 -0
  77. biosimulant-0.0.10/tests/test_biosimulant_namespace.py +70 -0
  78. biosimulant-0.0.10/tests/test_bioworld_flow.py +101 -0
  79. biosimulant-0.0.10/tests/test_cellml_contrib.py +410 -0
  80. biosimulant-0.0.10/tests/test_cli.py +365 -0
  81. biosimulant-0.0.10/tests/test_errors.py +27 -0
  82. biosimulant-0.0.10/tests/test_listeners.py +27 -0
  83. biosimulant-0.0.10/tests/test_modules_coverage.py +95 -0
  84. biosimulant-0.0.10/tests/test_onnx.py +100 -0
  85. biosimulant-0.0.10/tests/test_optional_deps.py +26 -0
  86. biosimulant-0.0.10/tests/test_pack.py +653 -0
  87. biosimulant-0.0.10/tests/test_ports_validation.py +157 -0
  88. biosimulant-0.0.10/tests/test_runtime_coercion.py +46 -0
  89. biosimulant-0.0.10/tests/test_runtime_config.py +55 -0
  90. biosimulant-0.0.10/tests/test_runtime_entrypoint.py +40 -0
  91. biosimulant-0.0.10/tests/test_runtime_flatten.py +61 -0
  92. biosimulant-0.0.10/tests/test_sbml_contrib.py +183 -0
  93. biosimulant-0.0.10/tests/test_signals_coverage.py +220 -0
  94. biosimulant-0.0.10/tests/test_simui_build.py +213 -0
  95. biosimulant-0.0.10/tests/test_simui_editor_api.py +473 -0
  96. biosimulant-0.0.10/tests/test_simui_graph.py +338 -0
  97. biosimulant-0.0.10/tests/test_simui_interface.py +648 -0
  98. biosimulant-0.0.10/tests/test_simui_registry.py +322 -0
  99. biosimulant-0.0.10/tests/test_simui_runner.py +228 -0
  100. biosimulant-0.0.10/tests/test_simui_spec.py +78 -0
  101. biosimulant-0.0.10/tests/test_visuals.py +147 -0
  102. biosimulant-0.0.10/tests/test_visuals_coverage.py +89 -0
  103. biosimulant-0.0.10/tests/test_wiring_builder.py +76 -0
  104. biosimulant-0.0.10/tests/test_wiring_coverage.py +218 -0
  105. biosimulant-0.0.10/tests/test_wiring_loader.py +62 -0
  106. biosimulant-0.0.10/tests/test_world_coverage.py +691 -0
  107. biosimulant-0.0.10/tests/test_world_load_wiring.py +24 -0
  108. biosimulant-0.0.10/tests/test_world_load_wiring_yaml.py +19 -0
@@ -0,0 +1,22 @@
1
+ # Byte-compiled / cache
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+ .ruff_cache/
8
+ .coverage
9
+ coverage.xml
10
+ htmlcov/
11
+
12
+ node_modules/
13
+ .venv/
14
+ dist/
15
+ build/
16
+ src/biosim/simui/_frontend/dist/
17
+
18
+ .DS_Store
19
+
20
+ # SimUI UI build outputs (built automatically via prepare script)
21
+ packages/simui-ui/dist/
22
+ packages/simui-ui/dist-static/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 BioSimulant
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,398 @@
1
+ Metadata-Version: 2.4
2
+ Name: biosimulant
3
+ Version: 0.0.10
4
+ Summary: Open-source local simulation runtime and CLI for Biosimulant
5
+ Project-URL: Documentation, https://github.com/Biosimulant/biosim#readme
6
+ Project-URL: Issues, https://github.com/Biosimulant/biosim/issues
7
+ Project-URL: Source, https://github.com/Biosimulant/biosim
8
+ Author-email: Demi <bjaiye1@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE.txt
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Programming Language :: Python
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: Implementation :: CPython
17
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
18
+ Requires-Python: >=3.10
19
+ Requires-Dist: numpy>=1.26
20
+ Provides-Extra: all
21
+ Requires-Dist: fastapi<1,>=0.110; extra == 'all'
22
+ Requires-Dist: httpx>=0.26; extra == 'all'
23
+ Requires-Dist: libcellml<0.7,>=0.6.3; extra == 'all'
24
+ Requires-Dist: onnxruntime>=1.18; extra == 'all'
25
+ Requires-Dist: pytest-cov>=4; extra == 'all'
26
+ Requires-Dist: pytest>=7; extra == 'all'
27
+ Requires-Dist: pyyaml>=6; extra == 'all'
28
+ Requires-Dist: scipy<2,>=1.11; extra == 'all'
29
+ Requires-Dist: tomli>=2; (python_version < '3.11') and extra == 'all'
30
+ Requires-Dist: uvicorn>=0.23; extra == 'all'
31
+ Provides-Extra: cellml
32
+ Requires-Dist: libcellml<0.7,>=0.6.3; extra == 'cellml'
33
+ Requires-Dist: scipy<2,>=1.11; extra == 'cellml'
34
+ Provides-Extra: dev
35
+ Requires-Dist: httpx>=0.26; extra == 'dev'
36
+ Requires-Dist: pytest-cov>=4; extra == 'dev'
37
+ Requires-Dist: pytest>=7; extra == 'dev'
38
+ Requires-Dist: pyyaml>=6; extra == 'dev'
39
+ Requires-Dist: tomli>=2; (python_version < '3.11') and extra == 'dev'
40
+ Provides-Extra: ml
41
+ Requires-Dist: onnxruntime>=1.18; extra == 'ml'
42
+ Provides-Extra: ui
43
+ Requires-Dist: fastapi<1,>=0.110; extra == 'ui'
44
+ Requires-Dist: uvicorn>=0.23; extra == 'ui'
45
+ Description-Content-Type: text/markdown
46
+
47
+ # biosimulant
48
+
49
+ [![PyPI - Version](https://img.shields.io/pypi/v/biosimulant.svg)](https://pypi.org/project/biosimulant)
50
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/biosimulant.svg)](https://pypi.org/project/biosimulant)
51
+
52
+ Composable simulation runtime + UI layer for orchestrating runnable biomodules.
53
+
54
+ `biosimulant` is the primary package and CLI name. The existing `biosim`
55
+ Python import path and `python -m biosim` command remain supported for existing
56
+ model packages during the migration.
57
+
58
+ ---
59
+
60
+ ## Executive Summary & System Goals
61
+
62
+ ### Vision
63
+
64
+ Provide a small, stable composition layer for simulations: wire reusable components ("biomodules") into a `BioWorld`, run them with a single orchestration contract, and visualize/debug runs via a lightweight web UI (SimUI). Biomodules are self-contained Python packages that can wrap external simulators internally (SBML/NeuroML/CellML/etc.) without a separate adapter layer.
65
+
66
+ ### Core Mission
67
+
68
+ - Compose simulations from reusable, interoperable biomodules.
69
+ - Make "run + visualize + share a config" the default workflow (local-first; hosted later).
70
+ - Keep the runtime small and predictable while letting biomodules embed their own simulator/tooling.
71
+
72
+ ### Primary Users
73
+
74
+ - Developers and researchers who need composable simulation workflows and fast iteration.
75
+ - Near-term beachhead: neuroscience demos (single neuron + small E/I microcircuits) with strong visuals and reproducible configs.
76
+
77
+ ---
78
+
79
+ ## Installation
80
+
81
+ Preferred (pinned GitHub ref):
82
+
83
+ ```console
84
+ pip install "biosimulant @ git+https://github.com/<org>/biosim.git@<ref>"
85
+ ```
86
+
87
+ Alternative (package index):
88
+
89
+ ```console
90
+ pip install biosimulant
91
+ ```
92
+
93
+ For the shared ONNX biomodule helpers:
94
+
95
+ ```console
96
+ pip install "biosimulant[ml]"
97
+ ```
98
+
99
+ ### Compatibility and command ownership
100
+
101
+ The `biosimulant` package still ships the `biosim` Python import path so existing
102
+ model packages keep working:
103
+
104
+ ```python
105
+ import biosim
106
+ ```
107
+
108
+ Use `biosimulant` for new CLI examples:
109
+
110
+ ```bash
111
+ biosimulant --help
112
+ python -m biosimulant --help
113
+ ```
114
+
115
+ `python -m biosim` remains available as a compatibility command. If a machine
116
+ also has the Desktop/product CLI installed, `PATH` decides which `biosimulant`
117
+ binary runs. Use `python -m biosimulant ...` to force the Python package CLI.
118
+ The Python package owns local open-source workflows; Desktop/product extensions
119
+ own Hub, auth, cloud, app state, and managed-service workflows.
120
+
121
+ ## Publishing to PyPI
122
+
123
+ See the release guide: [`docs/releasing.md`](docs/releasing.md).
124
+
125
+ ## Packaging Models And Labs
126
+
127
+ `biosim` can package one model or one lab into a single archive for portability, upload, caching, and validation.
128
+
129
+ Common commands:
130
+
131
+ ```bash
132
+ # Build a package from a directory that contains model.yaml or lab.yaml
133
+ biosimulant pack build path/to/model-or-lab
134
+
135
+ # Validate an existing package file
136
+ biosimulant pack validate dist/local__counter-1.0.0.bsimodel
137
+
138
+ # Build a self-contained lab package (.bsilab)
139
+ biosimulant pack build path/to/lab
140
+ ```
141
+
142
+ Notes:
143
+ - `build` prefers `package:` and `version:` from `model.yaml` or `lab.yaml` when present.
144
+ - model dependencies in manifests must use exact `==` pins.
145
+ - lab builds are always self-contained and preserve the full runnable source tree inside the `.bsilab`.
146
+ - nested lab dependencies must use relative `path` refs and must already exist inside the packaged lab directory.
147
+ - `validate` prints human-readable success or failure output by default; add `--json` for machine-readable output.
148
+
149
+ See [`docs/packaging.md`](docs/packaging.md) for the full package layout, recommended authoring flow, and CLI examples.
150
+
151
+ ## Provisional Runtime Helpers
152
+
153
+ `biosim.runtime` is the provisional public home for package interpretation helpers shared by the open-source CLI and Biosimulant platform executors. It owns entrypoint loading, typed `runtime.initial_inputs` coercion, communication-step resolution, and source-neutral lab flattening. Import these helpers from `biosim.runtime`; they are not exported from top-level `biosim` while the API settles.
154
+
155
+ ## BioModule Convenience Layers
156
+
157
+ `BioModule` remains the minimal full-control runtime contract. For common model
158
+ adapters, `biosim` also exports opt-in helpers:
159
+
160
+ - `SignalEmitterBioModule`: output storage, source-name resolution, and raw
161
+ value to typed `BioSignal` wrapping.
162
+ - `StatefulBioModule`: fixed-step window advancement, input override storage,
163
+ bounded history, and output publishing hooks.
164
+
165
+ Signal helper functions are available from `biosim.signals` and top-level
166
+ `biosim`: `unwrap_payload`, `coerce_float`, `scalar_or_record_input`, and
167
+ `make_signal`.
168
+
169
+ ## Examples
170
+
171
+ - See `examples/` for quick-start scripts. Try:
172
+
173
+ ```bash
174
+ pip install -e .
175
+ python examples/basic_usage.py
176
+ ```
177
+
178
+ For advanced curated demos (neuro/ecology), wiring configs, and model-pack templates, see the companion repo:
179
+
180
+ - https://github.com/Biosimulant/models
181
+
182
+ ### Quick Start: BioWorld
183
+
184
+ Minimal usage:
185
+
186
+ ```python
187
+ import biosim
188
+ from biosim import ScalarSignal, SignalSpec
189
+
190
+
191
+ class Counter(biosim.BioModule):
192
+ def __init__(self):
193
+ self.value = 0
194
+ self._t = 0.0
195
+
196
+ def outputs(self):
197
+ return {"count": SignalSpec.scalar(dtype="int64", emitted_unit="1")}
198
+
199
+ def advance_window(self, start: float, end: float) -> None:
200
+ _ = start
201
+ self.value += 1
202
+ self._t = end
203
+
204
+ def get_outputs(self):
205
+ return {
206
+ "count": ScalarSignal(
207
+ source="counter",
208
+ name="count",
209
+ value=self.value,
210
+ emitted_at=self._t,
211
+ spec=self.outputs()["count"],
212
+ )
213
+ }
214
+
215
+ def snapshot(self) -> dict:
216
+ return {"value": self.value, "t": self._t}
217
+
218
+ def restore(self, snapshot: dict) -> None:
219
+ self.value = int(snapshot.get("value", 0))
220
+ self._t = float(snapshot.get("t", 0.0))
221
+
222
+
223
+ world = biosim.BioWorld(communication_step=0.1)
224
+ world.add_biomodule("counter", Counter())
225
+ world.run(duration=1.0)
226
+ ```
227
+
228
+ Outputs produced during a communication window are committed at the end of that
229
+ window and become visible to downstream modules on a later communication turn.
230
+ For final report, export, or visualisation modules in workflow-style graphs, call
231
+ `world.settle(steps=1)` after `world.run(...)` to propagate final outputs without
232
+ advancing simulated time.
233
+
234
+ ### Visuals from Modules
235
+
236
+ Modules may optionally expose visuals via `visualize()`, returning a dict or list of dicts with keys `render` and `data`. The world can collect them without any transport layer:
237
+
238
+ ```python
239
+ class MyModule(biosim.BioModule):
240
+ def advance_window(self, start: float, end: float) -> None:
241
+ _ = start, end
242
+
243
+ def get_outputs(self):
244
+ return {}
245
+
246
+ def snapshot(self) -> dict:
247
+ return {}
248
+
249
+ def restore(self, snapshot: dict) -> None:
250
+ _ = snapshot
251
+
252
+ def visualize(self):
253
+ return {
254
+ "render": "timeseries",
255
+ "data": {"series": [{"name": "s", "points": [[0.0, 1.0]]}]},
256
+ }
257
+
258
+ world = biosim.BioWorld(communication_step=0.1)
259
+ world.add_biomodule("module", MyModule())
260
+ world.run(duration=0.1)
261
+ print(world.collect_visuals()) # [{"module": "module", "visuals": [...]}]
262
+ ```
263
+
264
+ If visuals are generated by a separate downstream module wired to another
265
+ producer's final outputs, run one or more settle turns before collecting visuals:
266
+ `world.run(duration=...); world.settle(1); world.collect_visuals()`.
267
+
268
+ See `examples/visuals_demo.py` for a minimal end-to-end example.
269
+
270
+ ### ONNX Modules
271
+
272
+ `biosim` can host ONNX-backed modules without changing the core runtime. Install
273
+ the ML extras and wrap the ONNX model behind the standard `BioModule`
274
+ interface:
275
+
276
+ ```python
277
+ from biosim import OnnxClassifierModule, ScalarSignal, SignalSpec
278
+
279
+ classifier = OnnxClassifierModule(
280
+ model_path="artifacts/model.onnx",
281
+ class_labels=["quiescent", "subthreshold", "spiking"],
282
+ input_port="state_vector",
283
+ probabilities_port="state_probabilities",
284
+ predicted_port="predicted_state",
285
+ input_vector_length=4,
286
+ )
287
+
288
+ classifier.set_inputs(
289
+ {
290
+ "state_vector": ScalarSignal(
291
+ source="adapter",
292
+ name="state_vector",
293
+ value=-64.0,
294
+ emitted_at=0.0,
295
+ spec=SignalSpec.scalar(dtype="float64"),
296
+ )
297
+ }
298
+ )
299
+ classifier.advance_window(0.0, 0.001)
300
+ print(classifier.get_outputs()["predicted_state"].value)
301
+ ```
302
+
303
+ Model packs can subclass `OnnxClassifierModule` to set model-relative
304
+ `model_path`, port names, and label sets while keeping the inference logic in
305
+ the shared library.
306
+
307
+ ## SimUI (Python-Declared UI)
308
+
309
+ SimUI lets you build and launch a small web UI entirely from Python (similar to Gradio's ergonomics), backed by FastAPI and a prebuilt React SPA that renders visuals from JSON. The frontend uses Server-Sent Events (SSE) for real-time updates.
310
+
311
+ - User usage (no Node/npm required):
312
+ - Install UI extras: `pip install 'biosimulant[ui]'`
313
+ - Try the demo: `python examples/ui_demo.py` then open `http://127.0.0.1:7860/ui/`.
314
+ - From your own code:
315
+
316
+ ```python
317
+ from biosim.simui import Interface, Number, Button, EventLog, VisualsPanel
318
+ world = biosim.BioWorld(communication_step=0.1)
319
+ ui = Interface(
320
+ world,
321
+ controls=[Number("duration", 10), Button("Run")],
322
+ outputs=[EventLog(), VisualsPanel()],
323
+ )
324
+ ui.launch()
325
+ ```
326
+
327
+ - The UI provides endpoints under `/ui/api/...`:
328
+ - `GET /api/spec` – UI layout (controls, outputs, modules)
329
+ - `POST /api/run` – Start a simulation run
330
+ - `GET /api/status` – Runner status (running/paused/error + optional progress fields)
331
+ - `GET /api/state` – Full state (status + last step + modules)
332
+ - `GET /api/events` – Buffered world events (`?since_id=&limit=`)
333
+ - `GET /api/visuals` – Collected module visuals
334
+ - `GET /api/snapshot` – Full snapshot (status + visuals + events)
335
+ - `GET /api/stream` – SSE endpoint for real-time event streaming
336
+ - `POST /api/pause` – Pause running simulation
337
+ - `POST /api/resume` – Resume paused simulation
338
+ - `POST /api/reset` – Stop, reset, and clear buffers
339
+ - **Editor sub-API** (`/api/editor/...`): visual config editor for loading, saving, validating, and applying YAML wiring configs as node graphs. Endpoints include `modules`, `current`, `config`, `apply`, `validate`, `layout`, `to-yaml`, `from-yaml`, and `files`.
340
+
341
+ Per-run resets for clean visuals
342
+ - On each `Run`, the backend clears its event buffer and calls `reset()` on modules if they implement it.
343
+ - The frontend clears visuals/events before posting `/api/run`.
344
+ - To avoid overlapping charts across runs, add `reset()` to modules that accumulate history (e.g., time series points).
345
+
346
+ - Maintainer flow (building the frontend SPA):
347
+ - Edit the React/Vite app under `src/biosim/simui/_frontend/`.
348
+ - Build via Python: `python -m biosim.simui.build` (requires Node/npm). This writes `src/biosim/simui/static/app.js`.
349
+ - Alternatively: `bash scripts/build_simui_frontend.sh`.
350
+ - Packaging includes `src/biosim/simui/static/**`, so end users never need npm.
351
+
352
+ - CI packaging (recommended): run the frontend build before `python -m build` so wheels/sdists ship the bundled assets.
353
+
354
+ Troubleshooting:
355
+ - If you see `SimUI static bundle missing at .../static/app.js`, build the frontend with `python -m biosim.simui.build` (requires Node/npm) before launching. End users installing a release wheel won't see this.
356
+
357
+ ### SimUI Design Notes
358
+ - Transport: SSE (Server-Sent Events). The SPA connects to `/api/stream` for real-time updates. Polling endpoints (`/api/status`, `/api/visuals`, `/api/events`) remain available for fallback/debugging.
359
+ - Objective progress fields are based on simulation-time progress (`(sim_time - sim_start) / duration`), not wall-clock time.
360
+ - `/api/status` may include: `sim_time`, `sim_start`, `sim_end`, `sim_remaining`, `progress`, `progress_pct` (all optional/additive).
361
+ - Events API: `/api/events?since_id=<int>&limit=<int>` returns `{ events, next_since_id }` where `events` are appended world events and `next_since_id` is the cursor for subsequent calls.
362
+ - VisualSpec types supported now:
363
+ - `timeseries`: `data = { "series": [{ "name": str, "points": [[x, y], ...] }, ...] }`
364
+ - `bar`: `data = { "items": [{ "label": str, "value": number }, ...] }`
365
+ - `scatter`: `data = { "points": [{ "x": number, "y": number, "label"?: str, "series"?: str }, ...] }`
366
+ - `heatmap`: `data = { "values": [[number, ...], ...], "x_labels"?: [str, ...], "y_labels"?: [str, ...] }`
367
+ - `table`: `data = { "columns": [..], "rows": [[..], ...] }` or `data = { "items": [{...}, ...] }`
368
+ - `image`: `data = { "src": str, "alt"?: str, "width"?: number, "height"?: number }`
369
+ - `graph`: simple node-edge graph renderer
370
+ - `structure3d`: `data = { "title"?: str, "source": { "kind": "url", "url": str } | { "kind": "artifact", "artifact_id": str }, "format": "mmcif" | "pdb", "annotations"?: [{ "label": str, "value": str|number|bool }], "initial_view"?: {...} }`
371
+ - VisualSpec may also include an optional `description` (string) for hover text or captions.
372
+ - SimUI serves artifact-backed `structure3d` files through `/api/artifacts/{artifact_id}` so browser clients do not receive raw local filesystem paths.
373
+
374
+ ## Terminology
375
+
376
+ Understanding the core concepts is essential for working with biosim effectively.
377
+
378
+ | Term | Description |
379
+ |------|-------------|
380
+ | **BioWorld** | Runtime container that orchestrates multi-rate biomodules, routes signals, and publishes lifecycle events. |
381
+ | **BioModule** | Pluggable unit of behavior with local state. Implements the runnable contract (`setup/reset/advance_to/...`). |
382
+ | **BioSignal** | Typed, versioned data payload exchanged between modules via named ports. |
383
+ | **WorldEvent** | Runtime events emitted by the BioWorld (`STARTED`, `TICK`, `FINISHED`, etc.). |
384
+ | **Wiring** | Module connection graph. Defined programmatically, via `WiringBuilder`, or loaded from YAML/TOML configs. |
385
+ | **VisualSpec** | JSON structure returned by `module.visualize()` with `render` type and `data` payload. |
386
+
387
+ ### Event Lifecycle
388
+
389
+ Every simulation follows this sequence:
390
+ ```
391
+ STARTED -> TICK (xN) -> FINISHED
392
+ ```
393
+
394
+ `PAUSED`, `RESUMED`, `STOPPED`, and `ERROR` may also be emitted depending on runtime control flow.
395
+
396
+ ## License
397
+
398
+ MIT. See `LICENSE.txt`.