feat: Steering support for CLI channel#241
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
bub | fc4121e | Commit Preview URL Branch Preview URL |
Jul 01 2026, 08:45 AM |
e7756ad to
01a2c07
Compare
|
@Gezi-lzq I made some changes to the steering control. I removed the steering buffer and expose a new hook This PR also implements a simple steering support for CLI channel, it's single-threaded. Run |
|
Sorry for the late reply. I only just saw this PR. Thanks for explaining the motivation. I understand the direction here: removing the framework-level One concern I have is that So if a plugin replaces The direction makes sense to me. I just wonder whether this contract should be made explicit, e.g. custom model runtimes that support steering should also own |
|
Thinking about this a bit more, I wonder whether the framework should still provide a small abstraction for the queue/mailbox semantics, even if the concrete runtime decides how to consume steering messages. My original I’m not fully convinced yet how much freedom is needed in the stored data structure, since the channel-side input is still an Maybe there is a middle ground: keep steering pluggable, but expose a small framework-level mailbox/buffer abstraction so acknowledgement, fallback, draining, and ownership are explicit. |
…ramework and channel manager Signed-off-by: Frost Ming <me@frostming.com>
…es for steering logic Signed-off-by: Frost Ming <me@frostming.com>
…ents Signed-off-by: Frost Ming <me@frostming.com>
… for steering handling Signed-off-by: Frost Ming <me@frostming.com>
01a2c07 to
11af763
Compare
|
@Gezi-lzq Thank you for your valuable input, it makes sense. How about instead adding a hook |
|
@frostming Thanks, that sounds like a reasonable direction. One thing I’d like to better understand is the intended extension point of Is it mainly meant to let plugins provide different buffer/storage implementations, while the buffer still stores raw If it is only about storing raw |
|
In my imagination, it's probably like this: class SteeringBufferProtocol:
async def enqueue_message(self, message: Envelope, state: State) -> None:
...
async def drain_messages(self, state: State) -> list[Envelope]:
...
def steering_count(self, state: State) -> int: ...We don't create steering buffer on session basis, but rather create a single instance for it through the whole life time. |
|
This makes sense to me. One extra thought, maybe out of scope for this PR: since this is a single buffer instance for the whole runtime lifetime, and the implementation is responsible for routing by session/topic/thread via A rough analogy in my mind is: normal follow-up is like regular chat, steering is like an urgent call that should affect the current loop, and mailbox/inbox is more like email that the agent can check asynchronously. |
…s for message handling Signed-off-by: Frost Ming <me@frostming.com>
|
@Gezi-lzq PR updated, it's similar as before just that steering inbox is defined by plugin but owned by framework |
Signed-off-by: Frost Ming me@frostming.com