Add flow settings, pulse emitting nodes, and simplify node state
- One dot per node now carries the whole story: primary while running, sage after a good run, red when anything is wrong, with the explanation on hover. The corner badge is gone, along with the second way of saying the same thing. - A node that publishes something flashes a ring, so a running flow is legible without reading the edge values. Nodes that consume but publish nothing stay quiet, which is why the event carries an output count. - Flow settings open in the same panel its nodes use, from a pencil in the dock: the title, the name, and deleting the flow. NodePanel and FlowPanel share the panel chrome rather than each drawing their own. - Renaming is a server operation, because a flow's name is the namespace of its messages: the directory moves and every other flow reading `old.message` is repointed, instead of being left pointing at a flow that no longer exists. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WzrvW7rjQbynnhF6pxh6i
This commit is contained in:
co-authored by
Claude Fable 5
parent
c254d487ba
commit
fd666743d2
@@ -21,6 +21,7 @@ from app.flow.events import event_bus
|
||||
from app.flow.messages import qualify
|
||||
from app.flow.pipeline import ValidationIssue
|
||||
from app.flow.schemas import (
|
||||
NAME_PATTERN,
|
||||
FlowDef,
|
||||
FlowsPublic,
|
||||
FlowStatePublic,
|
||||
@@ -30,7 +31,7 @@ from app.flow.schemas import (
|
||||
NodeStatusPublic,
|
||||
NodeTypeInfo,
|
||||
)
|
||||
from app.flow.store import FlowNotFound
|
||||
from app.flow.store import FlowExists, FlowNotFound
|
||||
from app.models import Message
|
||||
|
||||
router = APIRouter(
|
||||
@@ -53,6 +54,10 @@ class ValidationResult(BaseModel):
|
||||
issues: list[ValidationIssue] = []
|
||||
|
||||
|
||||
class RenameRequest(BaseModel):
|
||||
new_name: str
|
||||
|
||||
|
||||
class RunRequest(BaseModel):
|
||||
inputs: dict[str, Any] = {}
|
||||
|
||||
@@ -154,6 +159,33 @@ async def delete_flow(name: str, controller: FlowControllerDep) -> Any:
|
||||
return Message(message=f"Deleted flow '{name}'")
|
||||
|
||||
|
||||
@router.post("/{name}/rename", response_model=FlowDetail)
|
||||
async def rename_flow(
|
||||
name: str,
|
||||
body: RenameRequest,
|
||||
controller: FlowControllerDep,
|
||||
) -> Any:
|
||||
"""Rename a flow, along with every reference to its messages."""
|
||||
if not NAME_PATTERN.match(body.new_name):
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=(
|
||||
"Use lowercase letters, digits and underscores, starting with a letter"
|
||||
),
|
||||
)
|
||||
try:
|
||||
renamed = await run_in_threadpool(
|
||||
controller.store.rename_flow, name, body.new_name
|
||||
)
|
||||
except FlowNotFound:
|
||||
raise HTTPException(status_code=404, detail=f"No flow named '{name}'")
|
||||
except FlowExists as exc:
|
||||
raise HTTPException(status_code=409, detail=str(exc))
|
||||
|
||||
await controller.reload()
|
||||
return _detail(controller, renamed)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Node source
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user