Conversation
…stence - Add CoreMessageRepository implementation with simplified callback API - Add automatic timestamp-based sorting for InMemoryMessageRepository - Add onInitial callback for loading messages from persistence - Add onPut callback for persisting insert/update operations (upsert) - Add optional onRemove callback for persisting deletions - Add session caching and initialization state tracking - Add comprehensive test coverage for both
refactor(kai_engine): add KaiChatController and improve code formatting - Add KaiChatController as ready-to-use chat controller implementation - Add comprehensive documentation with usage examples for basic, persistent storage, and custom context scenarios - Add static create() factory method with async initialization support - Add optional callbacks for onPromptsReady and onStateChange - Add systemPrompt parameter to SimpleContextEngine for customization - Update CoreMessageRepository callback
refactor(chat_ui): migrate KaiChatView to controller-based architecture with enhanced customization - Replace direct message/state props with ChatControllerBase for reactive state management - Add StreamBuilder integration for messages and generation state streams - Add onMessageSent and onError callbacks for better event handling - Add messageItemBuilder, transientItemBuilder, and functionCallBuilder for granular customization - Add showTimeLabel parameter to display message timestamps - Rename
refactor(adapters): improve code formatting and add timestamp preservation in FirebaseAiContentAdapter - Format multi-line getters and factory methods to single lines in CoreMessage - Format long class declarations and method calls to single lines - Add timestamp preservation logic when converting Content to CoreMessage - Extract contentJson variable for better readability - Add timestamp parsing from stored content with fallback handling - Store timestamp in extensions for future round-trip conversions
- Change _formatTime to display HH:mm format instead of relative time - Use _setState instead of direct stream add in error handling for consistency
…ty state support - Reduce default bubble widths (mobile: 320→280, desktop: 480→400) - Reduce bubble and list padding for tighter layout - Change bottom item spacing from theme.itemSpacing to 4px for first item - Adjust bubble corner radius from 6 to 4 for sharper corners - Add inline send/stop button option to KaiComposer with customization (icon, size, color) - Add avatarBuilder support for custom message avatars - Add em
There was a problem hiding this comment.
Code Review
This pull request upgrades the kai_engine suite to version 0.2.0, introducing significant architectural improvements and UI enhancements. Key changes include the addition of KaiChatController for simplified initialization, a new callback-based CoreMessageRepository for persistence, and a major refactor of KaiChatView to support extensive customization via new builders for avatars, function calls, and empty states. The UI now integrates gpt_markdown for message rendering and features an improved composer with an inline button mode. My feedback focuses on addressing state management bugs in the new repository implementation, ensuring correct timestamp preservation during message conversion, and preventing potential data loss in the composer if asynchronous send operations fail.
| if (_cachedSession != null && _cachedSession != session) { | ||
| _cachedSession = session; | ||
| _messages.clear(); | ||
| } else { | ||
| _cachedSession = session; | ||
| } | ||
| _isInitialized = true; | ||
| _messages.addAll(messages); | ||
| await onPut(session, messages); |
There was a problem hiding this comment.
The current implementation of saveMessages has a bug in state management. By manually setting _isInitialized = true and clearing/updating _messages without calling onInitial, it prevents getMessages from ever loading existing persisted messages if a save occurs before the first load. Additionally, it doesn't ensure the internal list remains sorted. Calling getMessages(session) at the start of saveMessages would correctly handle initialization, session switching, and loading existing data.
await getMessages(session);
_messages.addAll(messages);
_messages.sort((a, b) => a.timestamp.compareTo(b.timestamp));
await onPut(session, messages);| extensions['_timestamp'] = timestamp.toIso8601String(); | ||
| } | ||
|
|
||
| return CoreMessage.create(type: messageType, content: combinedText, extensions: extensions); |
There was a problem hiding this comment.
The timestamp preservation logic is currently broken because CoreMessage.create ignores any external timestamp and always uses DateTime.now(). This will result in incorrect message ordering when reloading history from Firebase. You should apply the parsed timestamp to the created message using copyWith.
| return CoreMessage.create(type: messageType, content: combinedText, extensions: extensions); | |
| return CoreMessage.create( | |
| type: messageType, | |
| content: combinedText, | |
| extensions: extensions, | |
| ).copyWith(timestamp: timestamp ?? DateTime.now()); |
| for (var message in messages) { | ||
| final index = _messages.indexWhere((m) => m.messageId == message.messageId); | ||
| if (index != -1) { | ||
| _messages[index] = message; | ||
| } | ||
| } |
There was a problem hiding this comment.
The updateMessages method only updates existing messages in the local state but passes all messages to onPut. Since onPut is intended for upsert operations, the local state should also reflect this by adding messages that aren't already present. Also, the list should be re-sorted to maintain the timestamp order invariant.
for (var message in messages) {
final index = _messages.indexWhere((m) => m.messageId == message.messageId);
if (index != -1) {
_messages[index] = message;
} else {
_messages.add(message);
}
}
_messages.sort((a, b) => a.timestamp.compareTo(b.timestamp));| // Clear text immediately for responsive UX (like Resonate pattern) | ||
| _controller.clear(); | ||
| setState(() => _sending = true); | ||
|
|
||
| try { | ||
| await widget.onSend(text); | ||
| if (mounted) _controller.clear(); | ||
| } catch (e, s) { | ||
| widget.onError?.call(e, s); |
There was a problem hiding this comment.
Clearing the text controller immediately before the asynchronous onSend call can lead to data loss if the operation fails. If a network error occurs, the user's input is gone and they must re-type it. It's better to store the original text and restore it in the catch block to provide a better user experience.
| // Clear text immediately for responsive UX (like Resonate pattern) | |
| _controller.clear(); | |
| setState(() => _sending = true); | |
| try { | |
| await widget.onSend(text); | |
| if (mounted) _controller.clear(); | |
| } catch (e, s) { | |
| widget.onError?.call(e, s); | |
| final originalText = _controller.text; | |
| _controller.clear(); | |
| setState(() => _sending = true); | |
| try { | |
| await widget.onSend(text); | |
| } catch (e, s) { | |
| _controller.text = originalText; | |
| widget.onError?.call(e, s); |
This pull request introduces a new, ready-to-use AI chat controller (
KaiChatController) for easier integration of AI-powered conversations, and adds a flexible, callback-based message repository (CoreMessageRepository). It also includes various code style cleanups for improved readability and consistency.Major new features:
AI Chat Controller:
KaiChatController, a concrete implementation ofChatControllerBase, providing a simple factory for initializing AI chat functionality with sensible defaults, support for persistent/in-memory storage, and extensible context and processing pipelines. Comprehensive documentation and usage examples are included.Message Repository Enhancements:
CoreMessageRepository, a callback-based message repository that allows easy integration with custom persistence backends (such as Firebase or SQLite) by providing callbacks for loading, saving, updating, and removing messages. It maintains an internal state and ensures messages are sorted by timestamp.InMemoryMessageRepositoryto automatically sort messages by timestamp on initialization for consistent ordering.Code style and consistency improvements:
Formatting and Readability:
chat_controller_base.dart,conversation_manager.dart, andmessage_repository_base.dartto use single-line expressions and more concise formatting, improving code readability and reducing unnecessary line breaks. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16]Minor logic improvements:
ChatControllerBaseto use the_setStatemethod for consistency.These changes make it much easier to integrate and extend AI chat functionality in applications, and provide a flexible foundation for message persistence and state management.