Flow inputs carry an origin hint, run or panel
The Inputs editor and the canvas label show it, and the run forms leave panel inputs out.
This commit is contained in:
@@ -267,6 +267,15 @@ class FlowInput(BaseModel):
|
||||
|
||||
spec: MessageSpec
|
||||
initial: Any | None = None
|
||||
origin: Literal["run", "panel"] = Field(
|
||||
default="run",
|
||||
description=(
|
||||
"What writes it. A run input is a parameter a run passes in, a "
|
||||
"panel input is written by a dashboard or panel control. A hint "
|
||||
"only: run forms leave panel inputs out, the engine treats both "
|
||||
"the same."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class FlowDef(BaseModel):
|
||||
|
||||
@@ -96,13 +96,18 @@ Declare messages supplied by dashboard controls, the API or run parameters as
|
||||
the flow's **Inputs**. Give live inputs a starting value:
|
||||
|
||||
```json
|
||||
{"inputs": [{"spec": {"name": "setpoint", "dtype": "float"}, "initial": 21.0}]}
|
||||
{"inputs": [{"spec": {"name": "setpoint", "dtype": "float"}, "initial": 21.0, "origin": "panel"}]}
|
||||
```
|
||||
|
||||
This seeds `house.setpoint` in flow `house` if it has no value yet. Later
|
||||
publications replace it. Batch inputs are run parameters, and batch outputs
|
||||
select the run's result. See [Where a node's values come from](values.md).
|
||||
|
||||
`origin` says what writes an input: `run` (the default) for a parameter a run
|
||||
passes in, `panel` for a dashboard or panel control. The canvas labels each
|
||||
input with it, and the run form leaves panel inputs out, so a run keeps their
|
||||
starting value. The engine treats both the same.
|
||||
|
||||
## Drafts and publishing
|
||||
|
||||
- **Saving** stores a draft; the engine keeps running the published version.
|
||||
|
||||
@@ -47,8 +47,10 @@ The initial value seeds an unset live message. Later publications take over;
|
||||
changing the starting value does not overwrite a value already in state.
|
||||
Use **Publish this value now** in the input row to send it immediately.
|
||||
|
||||
The canvas draws flow inputs as labelled endpoints. Without a producer or
|
||||
declared input, a consuming node reports `unconnected_input`.
|
||||
The canvas draws flow inputs as labelled endpoints, marked `run` or `panel`
|
||||
by what writes them. A panel input is one a dashboard or panel control writes,
|
||||
and the run form does not ask for it. Without a producer or declared input, a
|
||||
consuming node reports `unconnected_input`.
|
||||
|
||||
A batch flow's inputs are run parameters. Its **Result** section selects the
|
||||
messages returned when the run finishes. See [Runs](runs.md).
|
||||
|
||||
@@ -80,7 +80,7 @@ Open **Flow settings** from the pencil button in the dock.
|
||||
|---|---|
|
||||
| **Running** | starts or stops the published live flow's subscriptions, schedules and webhooks |
|
||||
| **Mode** | chooses continuous `live` operation or requested `batch` runs |
|
||||
| **Inputs** | declares external messages and starting values; batch inputs become run parameters |
|
||||
| **Inputs** | declares external messages, their starting values and whether a run or a panel writes them; run inputs become run parameters |
|
||||
| **Result** | selects batch output messages |
|
||||
| **Contents** | shows the node count and message namespace |
|
||||
|
||||
|
||||
@@ -1355,6 +1355,13 @@ export const FlowInput_InputSchema = {
|
||||
}
|
||||
],
|
||||
title: 'Initial'
|
||||
},
|
||||
origin: {
|
||||
type: 'string',
|
||||
enum: ['run', 'panel'],
|
||||
title: 'Origin',
|
||||
description: 'What writes it. A run input is a parameter a run passes in, a panel input is written by a dashboard or panel control. A hint only: run forms leave panel inputs out, the engine treats both the same.',
|
||||
default: 'run'
|
||||
}
|
||||
},
|
||||
type: 'object',
|
||||
@@ -1376,6 +1383,13 @@ export const FlowInput_OutputSchema = {
|
||||
}
|
||||
],
|
||||
title: 'Initial'
|
||||
},
|
||||
origin: {
|
||||
type: 'string',
|
||||
enum: ['run', 'panel'],
|
||||
title: 'Origin',
|
||||
description: 'What writes it. A run input is a parameter a run passes in, a panel input is written by a dashboard or panel control. A hint only: run forms leave panel inputs out, the engine treats both the same.',
|
||||
default: 'run'
|
||||
}
|
||||
},
|
||||
type: 'object',
|
||||
|
||||
@@ -391,14 +391,27 @@ export type FlowDetail = {
|
||||
export type FlowInput_Input = {
|
||||
spec: MessageSpec;
|
||||
initial?: (unknown | null);
|
||||
/**
|
||||
* What writes it. A run input is a parameter a run passes in, a panel input is written by a dashboard or panel control. A hint only: run forms leave panel inputs out, the engine treats both the same.
|
||||
*/
|
||||
origin?: 'run' | 'panel';
|
||||
};
|
||||
|
||||
/**
|
||||
* What writes it. A run input is a parameter a run passes in, a panel input is written by a dashboard or panel control. A hint only: run forms leave panel inputs out, the engine treats both the same.
|
||||
*/
|
||||
export type origin = 'run' | 'panel';
|
||||
|
||||
/**
|
||||
* A message the flow starts with rather than computes.
|
||||
*/
|
||||
export type FlowInput_Output = {
|
||||
spec: MessageSpec;
|
||||
initial?: (unknown | null);
|
||||
/**
|
||||
* What writes it. A run input is a parameter a run passes in, a panel input is written by a dashboard or panel control. A hint only: run forms leave panel inputs out, the engine treats both the same.
|
||||
*/
|
||||
origin?: 'run' | 'panel';
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,7 @@ import { MessagesService } from "@/client"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Segmented } from "@/components/ui/segmented"
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -160,6 +161,12 @@ function usePublishInput() {
|
||||
})
|
||||
}
|
||||
|
||||
/** What writes an input: a run passing it in, or a control on a panel. */
|
||||
const ORIGINS = [
|
||||
["run", "run"],
|
||||
["panel", "panel"],
|
||||
] as const
|
||||
|
||||
function InputRow({
|
||||
flow,
|
||||
declared,
|
||||
@@ -231,34 +238,44 @@ function InputRow({
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{live && name ? (
|
||||
<div className="flex items-center gap-2 pl-1 text-xs text-muted-foreground">
|
||||
<span className="shrink-0">now</span>
|
||||
{current === undefined ? (
|
||||
<span className="flex-1">—</span>
|
||||
) : (
|
||||
<span className="min-w-0 flex-1">
|
||||
<ValuePreview value={current.value} dtype={spec.dtype} />
|
||||
</span>
|
||||
)}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
aria-label="Publish this value now"
|
||||
disabled={publish.isPending || declared.initial === null}
|
||||
onClick={() =>
|
||||
publish.mutate({ message, value: declared.initial })
|
||||
}
|
||||
>
|
||||
<ArrowUpFromLine />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Put this value into the flow now</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex items-center gap-2 pl-1 text-xs text-muted-foreground">
|
||||
<Segmented
|
||||
label="Written by"
|
||||
value={declared.origin ?? "run"}
|
||||
options={ORIGINS}
|
||||
onChange={(origin) =>
|
||||
onChange({ ...declared, origin: origin as typeof declared.origin })
|
||||
}
|
||||
/>
|
||||
{live && name ? (
|
||||
<>
|
||||
<span className="shrink-0">now</span>
|
||||
{current === undefined ? (
|
||||
<span className="flex-1">—</span>
|
||||
) : (
|
||||
<span className="min-w-0 flex-1">
|
||||
<ValuePreview value={current.value} dtype={spec.dtype} />
|
||||
</span>
|
||||
)}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
aria-label="Publish this value now"
|
||||
disabled={publish.isPending || declared.initial === null}
|
||||
onClick={() =>
|
||||
publish.mutate({ message, value: declared.initial })
|
||||
}
|
||||
>
|
||||
<ArrowUpFromLine />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Put this value into the flow now</TooltipContent>
|
||||
</Tooltip>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -338,8 +355,9 @@ export function BoundarySections({
|
||||
<div className="flex items-center justify-between">
|
||||
<span className={PANEL_SECTION}>Inputs</span>
|
||||
<InfoTip label="Inputs">
|
||||
Messages that arrive from outside, such as a dashboard control, a
|
||||
run or the API, and the value the flow starts from.
|
||||
Messages that arrive from outside, and the value the flow starts
|
||||
from. A run passes a run input in, a dashboard or panel control
|
||||
writes a panel input.
|
||||
</InfoTip>
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
||||
@@ -94,9 +94,16 @@ export function initialText(inputs: FlowInput_Input[]): Record<string, string> {
|
||||
)
|
||||
}
|
||||
|
||||
/** The declared inputs worth drawing a field for. */
|
||||
/**
|
||||
* The declared inputs worth drawing a field for.
|
||||
*
|
||||
* A panel input is a control's to write, so a run keeps its starting value
|
||||
* and the form does not offer it as a parameter.
|
||||
*/
|
||||
export function declaredInputs(definition: FlowDef_Input): FlowInput_Input[] {
|
||||
return (definition.inputs ?? []).filter((one) => one.spec?.name)
|
||||
return (definition.inputs ?? []).filter(
|
||||
(one) => one.spec?.name && one.origin !== "panel",
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -91,11 +91,14 @@ export function deriveEdges(nodes: NodeDef_Input[], flow: string): Edge[] {
|
||||
*/
|
||||
export function boundaryKey(doc: {
|
||||
mode?: string | null
|
||||
inputs?: { spec?: MessageSpec | null }[] | null
|
||||
inputs?: { spec?: MessageSpec | null; origin?: string }[] | null
|
||||
outputs?: string[] | null
|
||||
}): string {
|
||||
const inputs = (doc.inputs ?? [])
|
||||
.map((one) => `${one.spec?.name ?? ""}:${one.spec?.dtype ?? ""}`)
|
||||
.map(
|
||||
(one) =>
|
||||
`${one.spec?.name ?? ""}:${one.spec?.dtype ?? ""}:${one.origin ?? ""}`,
|
||||
)
|
||||
.join(",")
|
||||
return `${doc.mode ?? "live"}|${inputs}|${(doc.outputs ?? []).join(",")}`
|
||||
}
|
||||
|
||||
@@ -62,7 +62,8 @@ function boundaryOf(
|
||||
kind: "input",
|
||||
id: `input:${message}`,
|
||||
label: displayName(flow, message),
|
||||
detail: declared.spec?.dtype ?? "float",
|
||||
// What writes it, so a control's input does not read as a run's.
|
||||
detail: `${declared.origin ?? "run"} · ${declared.spec?.dtype ?? "float"}`,
|
||||
provides: [message],
|
||||
requires: [],
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user