You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
graph: spec v0.6 observer pair model (proposal 0005) (#9)
* graph: spec v0.6 observer pair model (proposal 0005)
Replace single-event-per-attempt observer hooks with started/completed
pairs. Each node attempt now produces two NodeEvents sharing a step:
the started event fires before the wrapped node runs, the completed
event fires after the merge succeeds (with post_state) or after the
node, reducer, or state validation fails (with error).
Adds per-observer phase subscription via a new public SubscribedObserver
dataclass. attach_observer accepts an optional phases= kwarg; invoke
accepts Observer | SubscribedObserver in its observers list. Empty
phase sets raise ValueError at registration time. The delivery worker
filters by event.phase against each observer's subscribed phases.
NodeEvent gains attempt_index (default 0 until retry middleware lands)
and fan_out_index (default None until fan-out runtime lands).
Conformance fixtures 012-016 and 018 now pass; 017 still skips
pending fan-out runtime (proposal 0005 pipeline-utilities side).
* graph: document drain unbounded-wait risk and wait_for workaround
Drain blocks until every queued event has been delivered, so a slow or
hung observer can hold the calling process indefinitely. Note this in
the drain() docstring with the asyncio.wait_for stop-gap pattern. A
spec proposal for a first-class deadline parameter is in flight; this
docstring nudge holds until it lands.
Also bump the docstring's spec version reference from v0.3.0 to v0.6.0
to match the rest of the file.
* graph: clearer Observer Protocol docstring, idiomatic body
Lead with the structural-Protocol idea so users see they don't need
to subclass — any async callable with a matching signature works.
Includes a short usage example.
Simplify __call__ body to `...` (the PEP 544 idiomatic form for
Protocol methods) since the body is never executed.
* test: harness preserves empty phases list as frozenset()
Switching the truthiness check to `is not None` so an explicit
`phases: []` in a fixture produces a `frozenset()` rather than
collapsing to `None`. The engine's empty-set check then fires the
intended ValueError instead of silently defaulting to ALL_PHASES.
No current fixture uses `phases: []` for a regular observer, but the
harness should faithfully translate fixture intent so a future fixture
with that input doesn't silently mask a registration bug.
0 commit comments