Invocation timing
AreaInvokeTiming | Meaning |
|---|---|
immediate | Request model invocation immediately after this tick. |
deferrable | Allow model invocation to be deferred while other Areas advance. |
An Area may change its invocation timing as its state changes.
immediate: completing a prerequisite
A discovers that it needs task E to finish before it can continue. E uses immediate timing to work on that task.
Start with retained, deferrable Areas A, B, C and D:
from hydrangea.context.coop_context import AreaLane, AreaLayout, CoopContext
coop_context = CoopContext(context)
layout = AreaLayout([
area_a,
AreaLane(area_b, area_c),
area_d,
])
coop_context.compose(layout)
Before overlay, the expected tick() order is area_a -> area_b -> area_c -> area_d.
After A identifies the prerequisite, overlay area_e onto A’s lane between unfold() calls. E is retained and its invoke_timing is AreaInvokeTiming.immediate:
handle = coop_context.fetch_handle(area_a)
assert handle is not None
coop_context.overlay(area_e, handle)
After overlay, only area_e ticks before the next model invocation. This continues while E remains retained and immediate, allowing E to complete its task before A ticks again.
When E finishes, it retires and the expected tick() order returns to area_a -> area_b -> area_c -> area_d. E should return its result from promote() so that the result remains in Context after collection.
deferrable: supplying incoming information
E listens on a port and forwards incoming messages into Context. Its tick() returns the pending messages, or None if nothing has arrived. Using deferrable lets E supply that information alongside A’s output in the same model invocation.
Start with retained, deferrable Areas A, B, C and D:
from hydrangea.context.coop_context import AreaLane, AreaLayout, CoopContext
coop_context = CoopContext(context)
layout = AreaLayout([
area_a,
AreaLane(area_b, area_c),
area_d,
])
coop_context.compose(layout)
Before overlay, the expected tick() order is area_a -> area_b -> area_c -> area_d.
Between unfold() calls, overlay area_e onto A’s lane. E is retained and its invoke_timing is AreaInvokeTiming.deferrable:
handle = coop_context.fetch_handle(area_a)
assert handle is not None
coop_context.overlay(area_e, handle)
After overlay, the expected tick() order is area_e -> area_a -> area_b -> area_c -> area_d.
In that turn, E’s messages are appended before A’s output. The LLM can therefore use E’s information when processing A’s output in the same invocation.