Node settings arrive as keyword arguments, not a params dict
A python node's settings are constants of its own function, so they are passed the way its ports are: by name. The controller binds them to the compiled function, the `params` field is gone from the worker and remote protocols, and a setting sharing a port's name is reported as a node error rather than shadowing it. The panel's scaffold follows suit and keeps the header in step with both ports and settings. The demo's `pace` moves from a flow input to a setting of the training node, which is what it always was. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUb8YpL2s3gmN9WTACTt4q
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
"""A node's settings are arguments of its function, like its ports.
|
||||
|
||||
What distinguishes them is where the value comes from: a port carries whatever
|
||||
the graph last published, a setting is a constant stored with the flow. Both
|
||||
arrive by name, so one name cannot mean both.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from app.flow.controller import FlowController
|
||||
from app.flow.messages import DType, MessageSpec
|
||||
from app.flow.pipeline import Pipeline
|
||||
from app.flow.schemas import FlowDef, NodeDef
|
||||
from app.flow.state import MemoryState
|
||||
from app.flow.store import FlowStore
|
||||
|
||||
SOURCE = "def process(reading, factor):\n return {'scaled': reading * factor}\n"
|
||||
|
||||
|
||||
def a_flow(**params: object) -> FlowDef:
|
||||
return FlowDef(
|
||||
name="house",
|
||||
nodes=[
|
||||
NodeDef(
|
||||
id="scale",
|
||||
params=dict(params),
|
||||
requires=[MessageSpec(name="reading", dtype=DType.FLOAT)],
|
||||
provides=[MessageSpec(name="scaled", dtype=DType.FLOAT)],
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def store(tmp_path: Path) -> FlowStore:
|
||||
return FlowStore(tmp_path / "flows")
|
||||
|
||||
|
||||
def test_a_setting_reaches_the_function_as_a_keyword_argument(store: FlowStore):
|
||||
store.write_flow(a_flow(factor=3))
|
||||
store.write_node_source("house", "scale", SOURCE)
|
||||
controller = FlowController(store)
|
||||
|
||||
nodes, _loaded, _initial, _inputs = controller._build_flows(
|
||||
[(store.read_flow("house"), False)]
|
||||
)
|
||||
pipeline = Pipeline(nodes=nodes, state=MemoryState())
|
||||
pipeline.run({"house.reading": 2.0})
|
||||
|
||||
assert pipeline.values()["house.scaled"]["value"] == 6.0
|
||||
|
||||
|
||||
def test_a_setting_named_after_a_port_is_refused(store: FlowStore):
|
||||
store.write_draft(a_flow(reading=3), 0)
|
||||
store.write_node_source("house", "scale", SOURCE, draft=True)
|
||||
controller = FlowController(store)
|
||||
|
||||
preview = controller.preview("house")
|
||||
|
||||
assert [node.status for node in preview.nodes] == ["error"]
|
||||
assert "both an input and a setting" in (preview.nodes[0].error or "")
|
||||
Reference in New Issue
Block a user