flowgraph 0.1.2__tar.gz → 0.1.7__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.
- {flowgraph-0.1.2/src/flowgraph.egg-info → flowgraph-0.1.7}/PKG-INFO +98 -6
- flowgraph-0.1.7/README.md +190 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/pyproject.toml +21 -1
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/__init__.py +45 -14
- flowgraph-0.1.7/src/flowgraph/adapters/calculator.py +120 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/controls.py +19 -10
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/cosapp.py +13 -4
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/data_types.py +22 -4
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/documentation.py +4 -2
- flowgraph-0.1.7/src/flowgraph/adapters/field_gradient.py +136 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/field_ops.py +79 -8
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/file_conversion.py +6 -2
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/files.py +74 -60
- flowgraph-0.1.7/src/flowgraph/adapters/filters.py +349 -0
- flowgraph-0.1.7/src/flowgraph/adapters/image_tools.py +741 -0
- flowgraph-0.1.7/src/flowgraph/adapters/imagej.py +232 -0
- flowgraph-0.1.7/src/flowgraph/adapters/mesh_analysis.py +282 -0
- flowgraph-0.1.7/src/flowgraph/adapters/mesh_composition.py +481 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/mesh_creation_tools.py +67 -13
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/mesh_generations.py +5 -1
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/mesh_ops.py +150 -6
- flowgraph-0.1.7/src/flowgraph/adapters/paraview.py +107 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/plaid_bridge.py +39 -15
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/readers.py +94 -11
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/remote_files.py +7 -1
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/simple_sources.py +154 -17
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/sinks.py +35 -4
- flowgraph-0.1.7/src/flowgraph/adapters/table_tools.py +128 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/user_code.py +24 -3
- flowgraph-0.1.7/src/flowgraph/adapters/vector_tools.py +168 -0
- flowgraph-0.1.7/src/flowgraph/adapters/workflow.py +306 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/workflow_interfaces.py +11 -2
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/adapters/writers.py +93 -4
- flowgraph-0.1.7/src/flowgraph/adapters/zones.py +311 -0
- flowgraph-0.1.7/src/flowgraph/application/file_dependencies.py +136 -0
- flowgraph-0.1.7/src/flowgraph/application/port_conversions.py +235 -0
- flowgraph-0.1.7/src/flowgraph/application/workflow_cli.py +572 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/application/workflow_core.py +269 -13
- flowgraph-0.1.7/src/flowgraph/application/workflow_defaults.py +90 -0
- flowgraph-0.1.7/src/flowgraph/application/workflow_execution.py +358 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/application/workflow_io.py +79 -11
- flowgraph-0.1.7/src/flowgraph/application/workflow_session.py +436 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/application/workflow_web_catalog.py +26 -5
- flowgraph-0.1.7/src/flowgraph/core_cli.py +141 -0
- flowgraph-0.1.7/src/flowgraph/domain/filepath.py +77 -0
- flowgraph-0.1.7/src/flowgraph/paraview_gateway.py +872 -0
- flowgraph-0.1.7/src/flowgraph/server/__init__.py +1 -0
- flowgraph-0.1.7/src/flowgraph/server/http.py +315 -0
- flowgraph-0.1.7/src/flowgraph/server/jobs.py +223 -0
- flowgraph-0.1.7/src/flowgraph/server/worker.py +36 -0
- flowgraph-0.1.7/src/flowgraph/server/workspace.py +136 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/testdata/AllFilters.json +2 -2
- flowgraph-0.1.7/src/flowgraph/testdata/ColorCardboard.jpg +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/version.py +1 -1
- {flowgraph-0.1.2 → flowgraph-0.1.7/src/flowgraph.egg-info}/PKG-INFO +98 -6
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph.egg-info/SOURCES.txt +53 -3
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph.egg-info/requires.txt +7 -0
- flowgraph-0.1.7/tests/test_array_nodes.py +226 -0
- flowgraph-0.1.7/tests/test_array_types.py +177 -0
- flowgraph-0.1.7/tests/test_cache_policy.py +191 -0
- flowgraph-0.1.7/tests/test_calculator.py +242 -0
- flowgraph-0.1.7/tests/test_conditional_workflow.py +243 -0
- flowgraph-0.1.7/tests/test_controls.py +123 -0
- flowgraph-0.1.7/tests/test_directory_dependencies.py +200 -0
- flowgraph-0.1.7/tests/test_extract_field.py +104 -0
- flowgraph-0.1.7/tests/test_extract_vector_value.py +109 -0
- flowgraph-0.1.7/tests/test_field_gradient.py +231 -0
- flowgraph-0.1.7/tests/test_filepath.py +47 -0
- flowgraph-0.1.7/tests/test_fluency_optimization.py +119 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/tests/test_image_tools.py +217 -5
- flowgraph-0.1.7/tests/test_imagej.py +267 -0
- flowgraph-0.1.7/tests/test_local_server.py +536 -0
- flowgraph-0.1.7/tests/test_mesh_analysis.py +340 -0
- flowgraph-0.1.7/tests/test_mesh_composition.py +492 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/tests/test_mesh_creation_tools.py +10 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/tests/test_mesh_ops.py +56 -0
- flowgraph-0.1.7/tests/test_node_selector.py +216 -0
- flowgraph-0.1.7/tests/test_packaging.py +180 -0
- flowgraph-0.1.7/tests/test_paraview.py +1252 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/tests/test_plaid_bridge.py +108 -6
- {flowgraph-0.1.2 → flowgraph-0.1.7}/tests/test_readers.py +71 -11
- flowgraph-0.1.7/tests/test_release.py +235 -0
- flowgraph-0.1.7/tests/test_remove_lonely_nodes.py +112 -0
- flowgraph-0.1.7/tests/test_scalars_to_array_1d.py +104 -0
- flowgraph-0.1.7/tests/test_simple_sources.py +107 -0
- flowgraph-0.1.7/tests/test_sinks.py +12 -0
- flowgraph-0.1.7/tests/test_table_tools.py +176 -0
- flowgraph-0.1.7/tests/test_transfer_fields.py +122 -0
- flowgraph-0.1.7/tests/test_user_code_mesh.py +38 -0
- flowgraph-0.1.7/tests/test_vector_field_gradient.py +184 -0
- flowgraph-0.1.7/tests/test_vector_tools.py +184 -0
- flowgraph-0.1.7/tests/test_workflow_cli.py +976 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/tests/test_workflow_core.py +340 -1
- flowgraph-0.1.7/tests/test_workflow_execution.py +538 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/tests/test_workflow_io.py +85 -1
- flowgraph-0.1.7/tests/test_workflow_session.py +634 -0
- flowgraph-0.1.7/tests/test_workflow_web_catalog.py +307 -0
- flowgraph-0.1.7/tests/test_workspace_directories.py +54 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/tests/test_writers.py +56 -0
- flowgraph-0.1.7/tests/test_zones.py +214 -0
- flowgraph-0.1.2/README.md +0 -104
- flowgraph-0.1.2/src/flowgraph/adapters/filters.py +0 -173
- flowgraph-0.1.2/src/flowgraph/adapters/image_tools.py +0 -394
- flowgraph-0.1.2/src/flowgraph/adapters/paraview.py +0 -39
- flowgraph-0.1.2/src/flowgraph/adapters/workflow.py +0 -176
- flowgraph-0.1.2/src/flowgraph/application/port_conversions.py +0 -63
- flowgraph-0.1.2/src/flowgraph/application/wasm_execution.py +0 -132
- flowgraph-0.1.2/src/flowgraph/application/workflow_cli.py +0 -417
- flowgraph-0.1.2/src/flowgraph/core_cli.py +0 -81
- flowgraph-0.1.2/tests/test_packaging.py +0 -53
- flowgraph-0.1.2/tests/test_wasm_execution.py +0 -119
- flowgraph-0.1.2/tests/test_workflow_cli.py +0 -308
- flowgraph-0.1.2/tests/test_workflow_web_catalog.py +0 -50
- {flowgraph-0.1.2 → flowgraph-0.1.7}/LICENSE +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/setup.cfg +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/__init__.py +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/__main__.py +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/application/__init__.py +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/application/node_registry.py +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/application/workflow_history.py +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/application/workflow_layout.py +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/application/workflows.py +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/domain/__init__.py +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/domain/mesh_document.py +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/examples/AllFilters.py +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/examples/convert_format.py +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/resources.py +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/testdata/FlowGraph.h5 +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/testdata/FlowGraph.stl +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/testdata/FlowGraph.xdmf +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/testdata/SimpleReadMesh.json +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/testdata/SpherePoints.csv +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph/testdata/muscat-muscat-mesh-conversion.json +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph.egg-info/dependency_links.txt +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph.egg-info/entry_points.txt +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/src/flowgraph.egg-info/top_level.txt +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/tests/test_core_gui_boundary.py +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/tests/test_cosapp.py +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/tests/test_file_conversion.py +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/tests/test_headless_boundary.py +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/tests/test_muscat_vtk.py +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/tests/test_remote_files.py +0 -0
- {flowgraph-0.1.2 → flowgraph-0.1.7}/tests/test_workflow_history.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flowgraph
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
4
4
|
Summary: Headless typed workflow engine and execution runtime
|
|
5
5
|
Author: Felipe Bordeu
|
|
6
6
|
Maintainer: Felipe Bordeu
|
|
@@ -31,10 +31,16 @@ Requires-Dist: scipy>=1.10
|
|
|
31
31
|
Requires-Dist: pandas
|
|
32
32
|
Requires-Dist: h5py
|
|
33
33
|
Requires-Dist: pillow>=10.0
|
|
34
|
+
Requires-Dist: numpy>=1.24
|
|
35
|
+
Requires-Dist: openpyxl>=3.1.5
|
|
36
|
+
Provides-Extra: imagej
|
|
37
|
+
Requires-Dist: pyimagej; extra == "imagej"
|
|
38
|
+
Requires-Dist: scyjava; extra == "imagej"
|
|
34
39
|
Provides-Extra: dev
|
|
35
40
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
36
41
|
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
37
42
|
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
43
|
+
Requires-Dist: build; extra == "dev"
|
|
38
44
|
Provides-Extra: ml
|
|
39
45
|
Requires-Dist: pyplaid; extra == "ml"
|
|
40
46
|
Provides-Extra: cosapp
|
|
@@ -55,7 +61,14 @@ for compatible external clients.
|
|
|
55
61
|
> development, experimentation, and early adopter feedback. APIs and workflow
|
|
56
62
|
> behavior may change before `1.0.0`. See [`Release Policy`](docs/policies/release-policy.md).
|
|
57
63
|
|
|
58
|
-
##
|
|
64
|
+
## Incremental workflow execution
|
|
65
|
+
|
|
66
|
+
`WorkflowExecutor` remains stateless. Use `WorkflowSession` for selected targets,
|
|
67
|
+
dependency-aware cache reuse, forced runs and session lifecycle. See
|
|
68
|
+
[`Incremental execution`](doc/reference/incremental-execution.md) for the Python
|
|
69
|
+
API, node safety contracts, transport options and resource limits.
|
|
70
|
+
|
|
71
|
+
## Development setup
|
|
59
72
|
|
|
60
73
|
```bash
|
|
61
74
|
uv sync --extra dev
|
|
@@ -72,14 +85,81 @@ The `flowgraph` command is headless and has no GUI runtime dependency:
|
|
|
72
85
|
|
|
73
86
|
```bash
|
|
74
87
|
uv run flowgraph inspect workflow.json
|
|
88
|
+
uv run flowgraph inspect workflow.json --full
|
|
89
|
+
uv run flowgraph inspect workflow.json -i name=value --override node-id.parameter=value
|
|
75
90
|
uv run flowgraph run workflow.json --input name=value
|
|
91
|
+
uv run flowgraph run workflow.json -i name=value
|
|
76
92
|
uv run flowgraph run workflow.json --override node-id.parameter=value
|
|
77
93
|
uv run flowgraph run workflow.json --json-output results.json
|
|
78
94
|
```
|
|
79
95
|
|
|
96
|
+
Use `--input` or its short form `-i` for each published workflow input; repeat
|
|
97
|
+
the option to provide multiple inputs.
|
|
98
|
+
`flowgraph inspect` displays the resolved default to the right of each published
|
|
99
|
+
input, or marks it as required or having conflicting saved values.
|
|
100
|
+
Inspection is compact by default; add `--full` (or `-f`) to include the workflow
|
|
101
|
+
graph. Arbitrary-code warnings remain visible in both modes.
|
|
102
|
+
Inspection accepts the same `--input` / `-i` and `--override` assignments as
|
|
103
|
+
execution: overrides update the displayed defaults, and explicit inputs take
|
|
104
|
+
precedence and are marked `(--input)`. It never executes nodes or saves changes.
|
|
105
|
+
When an input is omitted, `flowgraph run` uses its downstream node's saved
|
|
106
|
+
parameter value and prints a warning to stderr identifying the input, value,
|
|
107
|
+
and parameter. This also works for directly exported parameters and nested
|
|
108
|
+
workflow inputs. Explicit inputs take precedence; parameter overrides are applied
|
|
109
|
+
before resolving defaults. Inputs without usable saved values remain required,
|
|
110
|
+
and conflicting saved values require an explicit `--input`.
|
|
111
|
+
For examples and warning output, see
|
|
112
|
+
[Omitted inputs and saved parameter values](doc/user-guide/workflows.md#omitted-inputs-and-saved-parameter-values).
|
|
113
|
+
|
|
80
114
|
Workflows use the versioned `flowgraph-workflow` JSON format. Core validation and
|
|
81
115
|
execution are UI-independent.
|
|
82
116
|
|
|
117
|
+
### JSON execution boundary
|
|
118
|
+
|
|
119
|
+
Python clients and browser-hosted Python workers use the same entry point:
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from flowgraph.application.workflow_execution import execute_workflow_json
|
|
123
|
+
|
|
124
|
+
result_json = execute_workflow_json(workflow_json)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The response includes execution order, public outputs, retained node values,
|
|
128
|
+
display previews, warnings, and structured errors. A process-local node registry
|
|
129
|
+
is reused, but each call reconstructs its workflow. This API does not provide a
|
|
130
|
+
server, authentication, cancellation, or filesystem isolation.
|
|
131
|
+
|
|
132
|
+
Mesh display previews retain scalar values or vector magnitudes in `fields` and
|
|
133
|
+
`cell_fields`. Multi-component fields additionally expose their original tuples
|
|
134
|
+
in `field_components` and `cell_field_components`, indexed by vertex or surface
|
|
135
|
+
triangle respectively, so viewers can select an individual component without
|
|
136
|
+
rerunning the workflow. These preview projections do not modify the input mesh.
|
|
137
|
+
|
|
138
|
+
The former WASM-specific execution module has been removed without a compatibility
|
|
139
|
+
alias. Browser clients must update their import and rebuild/stage their core wheel
|
|
140
|
+
together; the workflow file and execution response formats are unchanged.
|
|
141
|
+
|
|
142
|
+
## Experimental local server
|
|
143
|
+
|
|
144
|
+
The prototype branch includes an authenticated, loopback-only execution API:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
uv run flowgraph serve --workspace /absolute/path/to/existing/workspace
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Port 8765 and the allowed editor origin `https://flowgraph-web.fbw-es.com` are
|
|
151
|
+
defaults; explicit `--allow-origin` values replace that origin. The editor still
|
|
152
|
+
requires bearer authentication and browser local-network permission. Click the
|
|
153
|
+
printed **Open editor** link to pass the token and port automatically; keep the
|
|
154
|
+
link private.
|
|
155
|
+
|
|
156
|
+
It runs one workflow at a time in a cancellable native subprocess and provides
|
|
157
|
+
runtime metadata, catalog, and workspace file endpoints. Core is API-only and never
|
|
158
|
+
serves editor assets. Web and desktop clients own their GUI hosting and explicitly
|
|
159
|
+
allow their origin with `--allow-origin`. Native workflow code is **not sandboxed**.
|
|
160
|
+
See [Local server API and limitations](docs/local-server.md) for authentication,
|
|
161
|
+
request/response formats, examples, limits, and shutdown behavior.
|
|
162
|
+
|
|
83
163
|
## Package artifacts
|
|
84
164
|
|
|
85
165
|
Build the portable, pure-Python source distribution and wheel with:
|
|
@@ -91,12 +171,18 @@ uv build --out-dir dist/
|
|
|
91
171
|
The resulting wheel is compatible with all supported Python platforms. See
|
|
92
172
|
`packaging/README.md` for release details.
|
|
93
173
|
|
|
94
|
-
The
|
|
95
|
-
|
|
96
|
-
|
|
174
|
+
The repository also includes a Conda recipe under `packaging/conda/`. FlowGraph
|
|
175
|
+
is distributed through the [`flowgraph` Anaconda channel](https://anaconda.org/channels/flowgraph):
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
conda install -c flowgraph flowgraph
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Docker, operating-system package managers, and third-party mirrors are not
|
|
182
|
+
supported release channels at this time. See
|
|
97
183
|
[`Release Channels`](docs/policies/release-channels.md) for the release policy.
|
|
98
184
|
|
|
99
|
-
FlowGraph Core declares Python 3.11 or newer. CI covers Python 3.11
|
|
185
|
+
FlowGraph Core declares Python 3.11 or newer. CI covers Python 3.11, 3.12, 3.13, and 3.14 on
|
|
100
186
|
GitHub-hosted Ubuntu, Windows, and macOS runners; a platform/version combination
|
|
101
187
|
is supported only after its release-candidate job passes. See
|
|
102
188
|
[`Support Policy`](docs/policies/support-policy.md) for compatibility, deprecation,
|
|
@@ -116,6 +202,12 @@ behavior.
|
|
|
116
202
|
|
|
117
203
|
## Documentation and support
|
|
118
204
|
|
|
205
|
+
- [FlowGraph documentation](https://flowgraph.readthedocs.io/en/latest/)
|
|
206
|
+
- [Built-in node catalog](doc/reference/adapters/index.md) and
|
|
207
|
+
[Python custom-node guide](doc/user-guide/custom-nodes.md).
|
|
208
|
+
- The website documentation lives in `doc/` (outside the Python package).
|
|
209
|
+
To build the Read the Docs site locally, run
|
|
210
|
+
`uv run --with zensical zensical build -f zensical.toml` from this repository.
|
|
119
211
|
- [`Architecture`](docs/architecture.md) describes the core boundaries and workflow
|
|
120
212
|
model.
|
|
121
213
|
- [`Support Policy`](docs/policies/support-policy.md) describes supported environments and
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# FlowGraph Core
|
|
2
|
+
|
|
3
|
+
`FlowGraph` is the GUI-free Python workflow engine. It provides typed workflow
|
|
4
|
+
composition, validation, persistence, execution, adapters, mesh and image data
|
|
5
|
+
operations, browser-WASM execution support, and metadata-only catalog export
|
|
6
|
+
for compatible external clients.
|
|
7
|
+
|
|
8
|
+
> **Alpha status:** FlowGraph Core `0.1.x` is an alpha series for evaluation,
|
|
9
|
+
> development, experimentation, and early adopter feedback. APIs and workflow
|
|
10
|
+
> behavior may change before `1.0.0`. See [`Release Policy`](docs/policies/release-policy.md).
|
|
11
|
+
|
|
12
|
+
## Incremental workflow execution
|
|
13
|
+
|
|
14
|
+
`WorkflowExecutor` remains stateless. Use `WorkflowSession` for selected targets,
|
|
15
|
+
dependency-aware cache reuse, forced runs and session lifecycle. See
|
|
16
|
+
[`Incremental execution`](doc/reference/incremental-execution.md) for the Python
|
|
17
|
+
API, node safety contracts, transport options and resource limits.
|
|
18
|
+
|
|
19
|
+
## Development setup
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv sync --extra dev
|
|
23
|
+
uv run pytest
|
|
24
|
+
uv run ruff check .
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
See [`Contributing`](.github/CONTRIBUTING.md) for prerequisites, development
|
|
28
|
+
workflow, testing, documentation, and pull-request expectations.
|
|
29
|
+
|
|
30
|
+
## Headless workflows
|
|
31
|
+
|
|
32
|
+
The `flowgraph` command is headless and has no GUI runtime dependency:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uv run flowgraph inspect workflow.json
|
|
36
|
+
uv run flowgraph inspect workflow.json --full
|
|
37
|
+
uv run flowgraph inspect workflow.json -i name=value --override node-id.parameter=value
|
|
38
|
+
uv run flowgraph run workflow.json --input name=value
|
|
39
|
+
uv run flowgraph run workflow.json -i name=value
|
|
40
|
+
uv run flowgraph run workflow.json --override node-id.parameter=value
|
|
41
|
+
uv run flowgraph run workflow.json --json-output results.json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Use `--input` or its short form `-i` for each published workflow input; repeat
|
|
45
|
+
the option to provide multiple inputs.
|
|
46
|
+
`flowgraph inspect` displays the resolved default to the right of each published
|
|
47
|
+
input, or marks it as required or having conflicting saved values.
|
|
48
|
+
Inspection is compact by default; add `--full` (or `-f`) to include the workflow
|
|
49
|
+
graph. Arbitrary-code warnings remain visible in both modes.
|
|
50
|
+
Inspection accepts the same `--input` / `-i` and `--override` assignments as
|
|
51
|
+
execution: overrides update the displayed defaults, and explicit inputs take
|
|
52
|
+
precedence and are marked `(--input)`. It never executes nodes or saves changes.
|
|
53
|
+
When an input is omitted, `flowgraph run` uses its downstream node's saved
|
|
54
|
+
parameter value and prints a warning to stderr identifying the input, value,
|
|
55
|
+
and parameter. This also works for directly exported parameters and nested
|
|
56
|
+
workflow inputs. Explicit inputs take precedence; parameter overrides are applied
|
|
57
|
+
before resolving defaults. Inputs without usable saved values remain required,
|
|
58
|
+
and conflicting saved values require an explicit `--input`.
|
|
59
|
+
For examples and warning output, see
|
|
60
|
+
[Omitted inputs and saved parameter values](doc/user-guide/workflows.md#omitted-inputs-and-saved-parameter-values).
|
|
61
|
+
|
|
62
|
+
Workflows use the versioned `flowgraph-workflow` JSON format. Core validation and
|
|
63
|
+
execution are UI-independent.
|
|
64
|
+
|
|
65
|
+
### JSON execution boundary
|
|
66
|
+
|
|
67
|
+
Python clients and browser-hosted Python workers use the same entry point:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from flowgraph.application.workflow_execution import execute_workflow_json
|
|
71
|
+
|
|
72
|
+
result_json = execute_workflow_json(workflow_json)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The response includes execution order, public outputs, retained node values,
|
|
76
|
+
display previews, warnings, and structured errors. A process-local node registry
|
|
77
|
+
is reused, but each call reconstructs its workflow. This API does not provide a
|
|
78
|
+
server, authentication, cancellation, or filesystem isolation.
|
|
79
|
+
|
|
80
|
+
Mesh display previews retain scalar values or vector magnitudes in `fields` and
|
|
81
|
+
`cell_fields`. Multi-component fields additionally expose their original tuples
|
|
82
|
+
in `field_components` and `cell_field_components`, indexed by vertex or surface
|
|
83
|
+
triangle respectively, so viewers can select an individual component without
|
|
84
|
+
rerunning the workflow. These preview projections do not modify the input mesh.
|
|
85
|
+
|
|
86
|
+
The former WASM-specific execution module has been removed without a compatibility
|
|
87
|
+
alias. Browser clients must update their import and rebuild/stage their core wheel
|
|
88
|
+
together; the workflow file and execution response formats are unchanged.
|
|
89
|
+
|
|
90
|
+
## Experimental local server
|
|
91
|
+
|
|
92
|
+
The prototype branch includes an authenticated, loopback-only execution API:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
uv run flowgraph serve --workspace /absolute/path/to/existing/workspace
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Port 8765 and the allowed editor origin `https://flowgraph-web.fbw-es.com` are
|
|
99
|
+
defaults; explicit `--allow-origin` values replace that origin. The editor still
|
|
100
|
+
requires bearer authentication and browser local-network permission. Click the
|
|
101
|
+
printed **Open editor** link to pass the token and port automatically; keep the
|
|
102
|
+
link private.
|
|
103
|
+
|
|
104
|
+
It runs one workflow at a time in a cancellable native subprocess and provides
|
|
105
|
+
runtime metadata, catalog, and workspace file endpoints. Core is API-only and never
|
|
106
|
+
serves editor assets. Web and desktop clients own their GUI hosting and explicitly
|
|
107
|
+
allow their origin with `--allow-origin`. Native workflow code is **not sandboxed**.
|
|
108
|
+
See [Local server API and limitations](docs/local-server.md) for authentication,
|
|
109
|
+
request/response formats, examples, limits, and shutdown behavior.
|
|
110
|
+
|
|
111
|
+
## Package artifacts
|
|
112
|
+
|
|
113
|
+
Build the portable, pure-Python source distribution and wheel with:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
uv build --out-dir dist/
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The resulting wheel is compatible with all supported Python platforms. See
|
|
120
|
+
`packaging/README.md` for release details.
|
|
121
|
+
|
|
122
|
+
The repository also includes a Conda recipe under `packaging/conda/`. FlowGraph
|
|
123
|
+
is distributed through the [`flowgraph` Anaconda channel](https://anaconda.org/channels/flowgraph):
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
conda install -c flowgraph flowgraph
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Docker, operating-system package managers, and third-party mirrors are not
|
|
130
|
+
supported release channels at this time. See
|
|
131
|
+
[`Release Channels`](docs/policies/release-channels.md) for the release policy.
|
|
132
|
+
|
|
133
|
+
FlowGraph Core declares Python 3.11 or newer. CI covers Python 3.11, 3.12, 3.13, and 3.14 on
|
|
134
|
+
GitHub-hosted Ubuntu, Windows, and macOS runners; a platform/version combination
|
|
135
|
+
is supported only after its release-candidate job passes. See
|
|
136
|
+
[`Support Policy`](docs/policies/support-policy.md) for compatibility, deprecation,
|
|
137
|
+
maintenance, and unsupported-use policies.
|
|
138
|
+
|
|
139
|
+
## Catalog export
|
|
140
|
+
|
|
141
|
+
FlowGraph can export non-executable node metadata for tools that need to author
|
|
142
|
+
or structurally validate workflow JSON:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
uv run python -m flowgraph.application.workflow_web_catalog node-catalog.json
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The catalog intentionally excludes node executors and other callable runtime
|
|
149
|
+
behavior.
|
|
150
|
+
|
|
151
|
+
## Documentation and support
|
|
152
|
+
|
|
153
|
+
- [FlowGraph documentation](https://flowgraph.readthedocs.io/en/latest/)
|
|
154
|
+
- [Built-in node catalog](doc/reference/adapters/index.md) and
|
|
155
|
+
[Python custom-node guide](doc/user-guide/custom-nodes.md).
|
|
156
|
+
- The website documentation lives in `doc/` (outside the Python package).
|
|
157
|
+
To build the Read the Docs site locally, run
|
|
158
|
+
`uv run --with zensical zensical build -f zensical.toml` from this repository.
|
|
159
|
+
- [`Architecture`](docs/architecture.md) describes the core boundaries and workflow
|
|
160
|
+
model.
|
|
161
|
+
- [`Support Policy`](docs/policies/support-policy.md) describes supported environments and
|
|
162
|
+
compatibility expectations.
|
|
163
|
+
- [`SECURITY.md`](SECURITY.md) explains private vulnerability reporting and the
|
|
164
|
+
security boundaries of workflows and adapters.
|
|
165
|
+
- [`Changelog`](docs/changelog.md) records release changes and known limitations.
|
|
166
|
+
- [`docs/workflow-format.md`](docs/workflow-format.md) documents persistence,
|
|
167
|
+
compatibility, and capability boundaries.
|
|
168
|
+
|
|
169
|
+
## Optional integrations
|
|
170
|
+
|
|
171
|
+
The `ml` extra enables the PyPlaid adapter and the `cosapp` extra enables the
|
|
172
|
+
CoSApp adapter. Neither extra is part of the minimum core support guarantee.
|
|
173
|
+
Install an extra only when its upstream dependency, license terms, platform
|
|
174
|
+
requirements, and integration behavior have been reviewed for the target
|
|
175
|
+
release:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
uv sync --extra ml
|
|
179
|
+
uv sync --extra cosapp
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Optional adapters may execute external tools or user-provided models. Treat
|
|
183
|
+
their workflows as trusted input and test the exact dependency versions before
|
|
184
|
+
production use.
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
FlowGraph Core is licensed under the [BSD 3-Clause License](LICENSE). Release
|
|
189
|
+
compliance requires review of the exact dependency lockfile, build artifacts,
|
|
190
|
+
third-party notices, native dependencies, and applicable license obligations.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "flowgraph"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.7"
|
|
8
8
|
description = "Headless typed workflow engine and execution runtime"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -35,13 +35,20 @@ dependencies = [
|
|
|
35
35
|
"pandas",
|
|
36
36
|
"h5py",
|
|
37
37
|
"pillow>=10.0",
|
|
38
|
+
"numpy>=1.24",
|
|
39
|
+
"openpyxl>=3.1.5",
|
|
38
40
|
]
|
|
39
41
|
|
|
40
42
|
[project.optional-dependencies]
|
|
43
|
+
imagej = [
|
|
44
|
+
"pyimagej",
|
|
45
|
+
"scyjava",
|
|
46
|
+
]
|
|
41
47
|
dev = [
|
|
42
48
|
"pytest>=8.0",
|
|
43
49
|
"pytest-cov>=5.0",
|
|
44
50
|
"ruff>=0.6",
|
|
51
|
+
"build",
|
|
45
52
|
]
|
|
46
53
|
ml = [
|
|
47
54
|
"pyplaid",
|
|
@@ -81,3 +88,16 @@ pythonpath = [".", "src"]
|
|
|
81
88
|
target-version = "py311"
|
|
82
89
|
line-length = 100
|
|
83
90
|
|
|
91
|
+
#[tool.uv.sources]
|
|
92
|
+
#muscat = { git = "https://gitlab.com/drti/muscat.git", rev = "ParMmg" }
|
|
93
|
+
##micromamba install -n uv_muscat_build_packages scotch mmgsuite=*=h* kokkos=*=h*
|
|
94
|
+
## uv pip install ipywidgets
|
|
95
|
+
## uv pip install jupyterlab
|
|
96
|
+
##export OMP_PROC_BIND=spread
|
|
97
|
+
##export OMP_PLACES=threads
|
|
98
|
+
##LD_LIBRARY_PATH=/home/fbw/.local/share/mamba/envs/uv_muscat_build_packages/lib/ uv run jupyter lab
|
|
99
|
+
|
|
100
|
+
#[tool.uv]
|
|
101
|
+
#config-settings-package = {muscat = {"cmake.args" = ["-DCMAKE_PREFIX_PATH=/home/fbw/.local/share/mamba/envs/uv_muscat_build_packages/lib/cmake/",
|
|
102
|
+
#"-DSCOTCH_ROOT=/home/fbw/.local/share/mamba/envs/uv_muscat_build_packages/"
|
|
103
|
+
#]}}
|
|
@@ -5,6 +5,7 @@ from dataclasses import dataclass
|
|
|
5
5
|
from flowgraph.adapters.controls import AVAILABLE_NODES as CONTROL_AVAILABLE_NODES
|
|
6
6
|
from flowgraph.adapters.cosapp import AVAILABLE_NODES as COSAPP_AVAILABLE_NODES
|
|
7
7
|
from flowgraph.adapters.documentation import AVAILABLE_NODES as DOCUMENTATION_AVAILABLE_NODES
|
|
8
|
+
from flowgraph.adapters.field_gradient import SCALAR_FIELD_GRADIENT, VECTOR_FIELD_GRADIENT
|
|
8
9
|
from flowgraph.adapters.field_ops import AVAILABLE_NODES as FIELD_OPS_AVAILABLE_NODES
|
|
9
10
|
from flowgraph.adapters.file_conversion import MESH_FILE_CONVERSION
|
|
10
11
|
from flowgraph.adapters.files import (
|
|
@@ -14,36 +15,49 @@ from flowgraph.adapters.files import (
|
|
|
14
15
|
)
|
|
15
16
|
from flowgraph.adapters.filters import (
|
|
16
17
|
CREATE_MUSCAT_ELEMENT_FILTER,
|
|
18
|
+
CREATE_MUSCAT_NODE_FILTER,
|
|
17
19
|
FILTER_MESH_WITH_MUSCAT,
|
|
20
|
+
SELECTOR_OPERATORS,
|
|
18
21
|
)
|
|
19
22
|
from flowgraph.adapters.image_tools import AVAILABLE_NODES as IMAGE_TOOLS_AVAILABLE_NODES
|
|
23
|
+
from flowgraph.adapters.imagej import AVAILABLE_NODES as IMAGEJ_AVAILABLE_NODES
|
|
24
|
+
from flowgraph.adapters.mesh_analysis import MESH_NODES, SET_MESH_FIELD
|
|
25
|
+
from flowgraph.adapters.mesh_composition import FIELD_COMPOSITION_NODES, MESH_COMPOSITION_NODES
|
|
20
26
|
from flowgraph.adapters.mesh_creation_tools import AVAILABLE_NODES as MESH_CREATION_AVAILABLE_NODES
|
|
21
27
|
from flowgraph.adapters.mesh_generations import CSV_TO_MESH
|
|
22
28
|
from flowgraph.adapters.mesh_ops import AVAILABLE_NODES as MESH_OPS_AVAILABLE_NODES
|
|
29
|
+
from flowgraph.adapters.paraview import PARAVIEW_BRIDGE
|
|
23
30
|
from flowgraph.adapters.plaid_bridge import AVAILABLE_NODES as PLAID_AVAILABLE_NODES
|
|
24
31
|
from flowgraph.adapters.readers import (
|
|
25
32
|
LOAD_CSV,
|
|
26
33
|
LOAD_MESHIO,
|
|
27
34
|
LOAD_MESHLANE,
|
|
28
35
|
LOAD_MUSCAT,
|
|
36
|
+
READ_TABLE,
|
|
29
37
|
)
|
|
30
38
|
from flowgraph.adapters.remote_files import DOWNLOAD_URL
|
|
31
39
|
from flowgraph.adapters.simple_sources import (
|
|
40
|
+
SET_ARRAY_1D,
|
|
41
|
+
SET_BOOLEAN,
|
|
32
42
|
SET_FLOAT,
|
|
33
43
|
SET_INT,
|
|
34
|
-
SET_LIST_FLOAT,
|
|
35
44
|
SET_LIST_INT,
|
|
36
45
|
SET_LIST_STR,
|
|
37
46
|
SET_STRING,
|
|
47
|
+
SET_VEC2D,
|
|
48
|
+
SET_VEC2DI,
|
|
38
49
|
SET_VEC3D,
|
|
39
50
|
)
|
|
40
51
|
from flowgraph.adapters.sinks import AVAILABLE_NODES as SINKS_AVAILABLE_NODES
|
|
52
|
+
from flowgraph.adapters.table_tools import AVAILABLE_NODES as TABLE_TOOLS_AVAILABLE_NODES
|
|
41
53
|
from flowgraph.adapters.user_code import AVAILABLE_NODES as USER_CODE_AVAILABLE_NODES
|
|
54
|
+
from flowgraph.adapters.vector_tools import AVAILABLE_NODES as VECTOR_TOOLS_AVAILABLE_NODES
|
|
42
55
|
from flowgraph.adapters.workflow import AVAILABLE_NODES as WORKFLOW_AVAILABLE_NODES
|
|
43
56
|
from flowgraph.adapters.workflow_interfaces import (
|
|
44
57
|
AVAILABLE_NODES as WORKFLOW_INTERFACE_AVAILABLE_NODES,
|
|
45
58
|
)
|
|
46
|
-
from flowgraph.adapters.writers import WRITE_MESHIO, WRITE_MESHLANE, WRITE_MUSCAT
|
|
59
|
+
from flowgraph.adapters.writers import WRITE_MESHIO, WRITE_MESHLANE, WRITE_MUSCAT, WRITE_TABLE
|
|
60
|
+
from flowgraph.adapters.zones import AVAILABLE_NODES as ZONE_AVAILABLE_NODES
|
|
47
61
|
from flowgraph.application.workflow_core import NodeDefinition
|
|
48
62
|
|
|
49
63
|
|
|
@@ -93,9 +107,13 @@ ADAPTERS = AdapterCatalog(
|
|
|
93
107
|
AdapterGroup(
|
|
94
108
|
"Inputs",
|
|
95
109
|
subgroups=(
|
|
96
|
-
AdapterGroup("Scalars", (SET_STRING, SET_INT, SET_FLOAT)),
|
|
97
110
|
AdapterGroup(
|
|
98
|
-
"
|
|
111
|
+
"Scalars",
|
|
112
|
+
(SET_STRING, SET_BOOLEAN, SET_INT, SET_FLOAT) + CONTROL_AVAILABLE_NODES,
|
|
113
|
+
),
|
|
114
|
+
AdapterGroup(
|
|
115
|
+
"Lists/Vectors",
|
|
116
|
+
(SET_LIST_STR, SET_LIST_INT, SET_ARRAY_1D, SET_VEC2D, SET_VEC2DI, SET_VEC3D),
|
|
99
117
|
),
|
|
100
118
|
AdapterGroup(
|
|
101
119
|
"Files",
|
|
@@ -108,25 +126,38 @@ ADAPTERS = AdapterCatalog(
|
|
|
108
126
|
subgroups=(
|
|
109
127
|
AdapterGroup(
|
|
110
128
|
"Readers",
|
|
111
|
-
(LOAD_MUSCAT, LOAD_MESHIO, LOAD_MESHLANE, LOAD_CSV),
|
|
129
|
+
(LOAD_MUSCAT, LOAD_MESHIO, LOAD_MESHLANE, LOAD_CSV, READ_TABLE),
|
|
112
130
|
),
|
|
113
|
-
AdapterGroup("Mesh
|
|
114
|
-
AdapterGroup("Mesh Creation", MESH_CREATION_AVAILABLE_NODES),
|
|
131
|
+
AdapterGroup("Mesh Creation", (CSV_TO_MESH,) + MESH_CREATION_AVAILABLE_NODES),
|
|
115
132
|
AdapterGroup(
|
|
116
133
|
"Filters",
|
|
117
|
-
(
|
|
134
|
+
(
|
|
135
|
+
CREATE_MUSCAT_ELEMENT_FILTER,
|
|
136
|
+
CREATE_MUSCAT_NODE_FILTER,
|
|
137
|
+
FILTER_MESH_WITH_MUSCAT,
|
|
138
|
+
)
|
|
139
|
+
+ SELECTOR_OPERATORS,
|
|
140
|
+
subgroups=(AdapterGroup("Zones", ZONE_AVAILABLE_NODES),),
|
|
141
|
+
),
|
|
142
|
+
AdapterGroup(
|
|
143
|
+
"Mesh Ops", MESH_OPS_AVAILABLE_NODES + MESH_NODES + MESH_COMPOSITION_NODES
|
|
144
|
+
),
|
|
145
|
+
AdapterGroup(
|
|
146
|
+
"Field Ops",
|
|
147
|
+
FIELD_OPS_AVAILABLE_NODES
|
|
148
|
+
+ (SET_MESH_FIELD, SCALAR_FIELD_GRADIENT, VECTOR_FIELD_GRADIENT)
|
|
149
|
+
+ FIELD_COMPOSITION_NODES,
|
|
118
150
|
),
|
|
119
|
-
AdapterGroup("Mesh Ops", MESH_OPS_AVAILABLE_NODES),
|
|
120
|
-
AdapterGroup("Field Ops", FIELD_OPS_AVAILABLE_NODES),
|
|
121
151
|
AdapterGroup("File Conversion", (MESH_FILE_CONVERSION,)),
|
|
122
|
-
AdapterGroup("Writers", (WRITE_MUSCAT, WRITE_MESHLANE, WRITE_MESHIO)),
|
|
152
|
+
AdapterGroup("Writers", (WRITE_MUSCAT, WRITE_MESHLANE, WRITE_MESHIO, WRITE_TABLE)),
|
|
123
153
|
),
|
|
124
154
|
),
|
|
125
|
-
AdapterGroup("Controls", CONTROL_AVAILABLE_NODES),
|
|
126
155
|
AdapterGroup("Image Tools", IMAGE_TOOLS_AVAILABLE_NODES),
|
|
127
|
-
AdapterGroup("
|
|
156
|
+
AdapterGroup("Table Tools", TABLE_TOOLS_AVAILABLE_NODES),
|
|
157
|
+
AdapterGroup("Vector Tools", VECTOR_TOOLS_AVAILABLE_NODES),
|
|
158
|
+
AdapterGroup("Code", USER_CODE_AVAILABLE_NODES + IMAGEJ_AVAILABLE_NODES),
|
|
128
159
|
AdapterGroup("Workflow", WORKFLOW_INTERFACE_AVAILABLE_NODES + WORKFLOW_AVAILABLE_NODES),
|
|
129
|
-
AdapterGroup("Sinks", SINKS_AVAILABLE_NODES),
|
|
160
|
+
AdapterGroup("Sinks", SINKS_AVAILABLE_NODES + (PARAVIEW_BRIDGE,)),
|
|
130
161
|
AdapterGroup("Doc", DOCUMENTATION_AVAILABLE_NODES),
|
|
131
162
|
AdapterGroup(
|
|
132
163
|
"External Tools",
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Trusted Python-expression calculator with positional and upstream-name inputs."""
|
|
2
|
+
|
|
3
|
+
import builtins
|
|
4
|
+
import keyword
|
|
5
|
+
import re
|
|
6
|
+
from collections import Counter
|
|
7
|
+
from collections.abc import Mapping
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
from flowgraph.application.workflow_core import (
|
|
13
|
+
ExecContext,
|
|
14
|
+
NodeDefinition,
|
|
15
|
+
NodeInstance,
|
|
16
|
+
NodeRegistry,
|
|
17
|
+
ParameterDefinition,
|
|
18
|
+
PortDefinition,
|
|
19
|
+
PortDirection,
|
|
20
|
+
WorkflowEdge,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
from .data_types import ANY, ParameterKind
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _numeric_value(value: Any, label: str) -> Any:
|
|
27
|
+
"""Normalize lists and expose numeric array storage through a read-only view."""
|
|
28
|
+
if isinstance(value, list):
|
|
29
|
+
value = np.asarray(value)
|
|
30
|
+
if isinstance(value, np.ndarray):
|
|
31
|
+
if value.dtype.kind not in "buifc":
|
|
32
|
+
raise TypeError(f"Calculator {label} must contain numeric values")
|
|
33
|
+
view = value.view(np.ndarray)
|
|
34
|
+
view.flags.writeable = False
|
|
35
|
+
return view
|
|
36
|
+
if isinstance(value, (bool, int, float, complex, np.number, np.bool_)):
|
|
37
|
+
if np.asarray(value).dtype.kind not in "buifc":
|
|
38
|
+
raise TypeError(f"Calculator {label} must be numeric")
|
|
39
|
+
return value.item() if isinstance(value, np.generic) else value
|
|
40
|
+
raise TypeError(f"Calculator {label} must be a numeric scalar or NumPy array")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _calculate(
|
|
44
|
+
inputs: Mapping[str, Any],
|
|
45
|
+
parameters: Mapping[str, Any],
|
|
46
|
+
input_edges: Mapping[str, WorkflowEdge],
|
|
47
|
+
) -> Mapping[str, Any]:
|
|
48
|
+
"""Evaluate one trusted expression; this is intentionally not a sandbox."""
|
|
49
|
+
formula = inputs.get("formula", parameters.get("formula", "0"))
|
|
50
|
+
if not isinstance(formula, str) or not formula.strip():
|
|
51
|
+
raise ValueError("Calculator formula must be a non-empty Python expression")
|
|
52
|
+
|
|
53
|
+
namespace = {
|
|
54
|
+
name: _numeric_value(inputs[name], f"input {name!r}")
|
|
55
|
+
for index in range(1, 126)
|
|
56
|
+
if (name := f"input_{index}") in inputs
|
|
57
|
+
}
|
|
58
|
+
data_names = frozenset(namespace)
|
|
59
|
+
names = Counter(edge.source_port for name, edge in input_edges.items() if name in data_names)
|
|
60
|
+
for name, edge in input_edges.items():
|
|
61
|
+
alias = edge.source_port
|
|
62
|
+
if (
|
|
63
|
+
name in data_names
|
|
64
|
+
and names[alias] == 1
|
|
65
|
+
and alias.isidentifier()
|
|
66
|
+
and not keyword.iskeyword(alias)
|
|
67
|
+
and not re.fullmatch(r"input_[0-9]+", alias)
|
|
68
|
+
and alias not in {"np", "__builtins__"}
|
|
69
|
+
and not hasattr(builtins, alias)
|
|
70
|
+
):
|
|
71
|
+
namespace[alias] = namespace[name]
|
|
72
|
+
namespace.update(np=np, __builtins__=builtins.__dict__)
|
|
73
|
+
try:
|
|
74
|
+
# One shared namespace also makes aliases available inside comprehensions/lambdas.
|
|
75
|
+
result = eval(compile(formula.strip(), "<calculator formula>", "eval"), namespace)
|
|
76
|
+
except Exception as error:
|
|
77
|
+
raise ValueError(f"Calculator formula failed ({type(error).__name__}): {error}") from error
|
|
78
|
+
if isinstance(result, list):
|
|
79
|
+
raise TypeError("Calculator result must be a numeric scalar or NumPy array; use np.array")
|
|
80
|
+
return {"result": _numeric_value(result, "result")}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _calculator(
|
|
84
|
+
inputs: Mapping[str, Any],
|
|
85
|
+
parameters: Mapping[str, Any],
|
|
86
|
+
_exec_context: ExecContext | None = None,
|
|
87
|
+
) -> Mapping[str, Any]:
|
|
88
|
+
"""Evaluate using numbered names when called without workflow provenance."""
|
|
89
|
+
return _calculate(inputs, parameters, {})
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _calculator_instance(
|
|
93
|
+
_instance: NodeInstance,
|
|
94
|
+
inputs: Mapping[str, Any],
|
|
95
|
+
parameters: Mapping[str, Any],
|
|
96
|
+
_registry: NodeRegistry,
|
|
97
|
+
input_edges: Mapping[str, WorkflowEdge],
|
|
98
|
+
_exec_context: ExecContext,
|
|
99
|
+
) -> Mapping[str, Any]:
|
|
100
|
+
"""Resolve optional aliases from actual upstream output port names."""
|
|
101
|
+
return _calculate(inputs, parameters, input_edges)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
CALCULATOR = NodeDefinition(
|
|
105
|
+
id="calculator",
|
|
106
|
+
label="Calculator",
|
|
107
|
+
icon="mdi-calculator",
|
|
108
|
+
description="Evaluate a trusted Python expression with np, numbered inputs and unique aliases.",
|
|
109
|
+
requirements=("py:numpy",),
|
|
110
|
+
ports=(
|
|
111
|
+
PortDefinition("input", PortDirection.INPUT, ANY, "Input", required=False, expandable=125),
|
|
112
|
+
PortDefinition("result", PortDirection.OUTPUT, ANY, "Result"),
|
|
113
|
+
),
|
|
114
|
+
parameters=(ParameterDefinition("formula", ParameterKind.TEXT, "Formula", "0", "input_1 * 2"),),
|
|
115
|
+
executor=_calculator,
|
|
116
|
+
edge_aware_instance_executor=_calculator_instance,
|
|
117
|
+
cache_policy="always",
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
__all__ = ["CALCULATOR"]
|