feat: add ConversationTurn enum and ConversationReady event (#847)#855
feat: add ConversationTurn enum and ConversationReady event (#847)#855omarfarouk228 wants to merge 2 commits intoflutter:mainfrom
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a ConversationTurn enum and a turn getter in ConversationState to track whether it is the user's or the agent's turn. It also adds a ConversationReady event, emitted when the agent finishes responding. Feedback focuses on potential state inconsistencies during concurrent requests, the need for safety checks when adding events to a potentially closed controller during disposal, and an opportunity to improve test assertions by utilizing the new ConversationReady event.
| ConversationTurn get turn => | ||
| isWaiting ? ConversationTurn.agent : ConversationTurn.user; |
There was a problem hiding this comment.
The turn property is derived from isWaiting, which currently does not account for concurrent calls to sendRequest. If multiple requests are initiated, the first one to complete will set isWaiting to false, incorrectly signaling that it is the user's turn even if other agent responses are still pending. Consider implementing a request counter or a concurrency guard in sendRequest to ensure this state remains reliable.
| case ConversationReady(): | ||
| // No-op for now | ||
| break; |
There was a problem hiding this comment.
With the introduction of the ConversationReady event, we can now verify the turn's state immediately after the agent finishes responding. This provides a more precise point for assertions than waiting for the next ConversationWaiting event.
| case ConversationReady(): | |
| // No-op for now | |
| break; | |
| case ConversationReady(): | |
| verifyTurn(); | |
| break; |
| @@ -190,6 +213,7 @@ interface class Conversation { | |||
| _eventController.add(ConversationError(exception, stackTrace)); | |||
There was a problem hiding this comment.
Since sendRequest is an asynchronous method, there is a risk that dispose() is called while a request is in flight. If the _eventController is closed during the await, calling add() will throw an exception. It is safer to check _eventController.isClosed before adding the event.
| _eventController.add(ConversationError(exception, stackTrace)); | |
| if (!_eventController.isClosed) _eventController.add(ConversationError(exception, stackTrace)); |
| _eventController.add(ConversationError(exception, stackTrace)); | ||
| } finally { | ||
| _updateState((s) => s.copyWith(isWaiting: false)); | ||
| _eventController.add(ConversationReady()); |
There was a problem hiding this comment.
Similar to the error event above, adding the ConversationReady event after an await should be guarded against a closed controller to prevent exceptions during disposal.
| _eventController.add(ConversationReady()); | |
| if (!_eventController.isClosed) _eventController.add(ConversationReady()); |
Description
Fixes #847
Adds an explicit, observable API to detect whose turn it is in a conversation.
Changes
ConversationTurnenum withuserandagentvalues.turngetter onConversationState, derived fromisWaiting.ConversationReadyevent, emitted when the agent finishes responding — the complement of the existingConversationWaitingevent.chat_session.dartandsimple_chat_test.dartto handle the newConversationReadycase in their exhaustive switches.Event sequence for a single turn
ConversationWaiting— agent's turn beginsConversationSurfaceAdded/ConversationComponentsUpdated— AI streams UIConversationReady— agent's turn ends, user's turn beginsPre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-devrel channel on Discord.