Record a short value history per message

The panel is going to sparkline how a value moved, and nothing kept more
than the latest one. Emissions now append to a capped list beside the
value — 120 points, enough to fill a sparkline without making Redis a
time-series store — and `/flows/{flow}/history/{message}` hands it back
oldest first.

Only numbers are recorded, bools included as neither, so a string
message costs nothing at all. The response carries `numeric` so an empty
series reads as "not plottable" rather than "nothing yet". The append is
one pipelined round-trip per emission batch and sits outside the lock,
since the list is append-only.

MemoryState keeps the same window in a deque, so the endpoint answers
without Redis too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkmeRiyeYmVZqJVwuyHq9o
This commit is contained in:
Melvin Strobl
2026-08-15 21:39:59 +02:00
co-authored by Claude Opus 5
parent 6156ad1150
commit 3b72d17ca7
5 changed files with 198 additions and 1 deletions
+26
View File
@@ -26,11 +26,14 @@ from app.flow.schemas import (
FlowsPublic,
FlowStatePublic,
FlowSummary,
HistoryPoint,
MessageHistory,
MessageValue,
NodeSource,
NodeStatusPublic,
NodeTypeInfo,
)
from app.flow.state import as_number
from app.flow.store import FlowExists, FlowNotFound
from app.models import Message
@@ -275,6 +278,29 @@ def read_flow_state(name: str, controller: FlowControllerDep) -> Any:
return _flow_state(controller, name)
@router.get("/{name}/history/{message}", response_model=MessageHistory)
def read_message_history(
name: str,
message: str,
controller: FlowControllerDep,
) -> Any:
"""The recent values of one message, for plotting.
``message`` may be given bare or qualified; a message that never carried a
number comes back with an empty series.
"""
_read_flow(controller, name)
key = qualify(name, message)
return MessageHistory(
message=key,
numeric=as_number(controller.state.get(key)) is not None,
points=[
HistoryPoint(ts=ts, value=value)
for ts, value in controller.state.history(key)
],
)
# -----------------------------------------------------------------------------
# Live updates
# -----------------------------------------------------------------------------