Skip to content
Merged
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/simple_chat/lib/chat_session.dart
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ sealed class ChatSession extends ChangeNotifier {
_currentAiMessage = Message(isUser: false, text: '');
_messages.add(_currentAiMessage!);
}
_currentAiMessage!.text = (_currentAiMessage!.text ?? '') + chunk;
_currentAiMessage!.text = '${_currentAiMessage!.text ?? ''}\n$chunk';
Comment thread
polina-c marked this conversation as resolved.
Outdated

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure this works as expected?

My understanding is that the LLM emits a sequence of chunks structured however it wants, and there are no guarantees that the chunk boundary coincides with a newline, so therefore we should not inject additional newlines automatically, because they might be in the wrong place.

I don't fully understand the problem you're fixing, but if it's a lack of whitespace in the layout, perhaps this could be accomplished by post-processing the messages at render time to non-destructively add a newline at the end, rather than destructively adding it here in the chunk processing logic.

@polina-c polina-c Aug 11, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check screensots and comments on the issue to better understand both issue and fix: a2ui-project/a2ui#1891

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new line in most cases is noop for markdown, that makes me think the fix is right

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey Polina! Sorry I should have looked at the linked issue first to get more context - I'll do that next time.

I do think that it could cause problems to add a newline here because it's not always a no-op in markdown and could accidentally break words.

Analysis from Gemini

It turns out the issue isn't that the LLM is omitting spaces between chunks, but rather that A2uiTransportAdapter.incomingText is explicitly stripping them.

Specifically, in packages/genui/lib/src/transport/a2ui_transport_adapter.dart#L68:

  @override
  Stream<String> get incomingText => _pipeline
      .where((e) => e is TextEvent)
      .cast<TextEvent>()
      .map((e) => e.text.trim())  // <-- Strips leading/trailing whitespace from every chunk!
      .where((text) => text.isNotEmpty);

Because .trim() is called on every TextEvent chunk before it reaches _updateAiMessage, any trailing or leading whitespace in streamed chunks (e.g. "ty of friendly ") gets stripped. When _updateAiMessage concatenates these trimmed chunks, words smash together across boundaries (e.g. "friendlyspots").Adding \n before every chunk in chat_session.dart forces literal line breaks inside sentences when chunks split mid-sentence or mid-word, leading to broken streaming rendering and unexpected formatting in Markdown (such as inside code blocks, tables, or lists).

Suggested Fix

Instead of appending in chat_session.dart, we should remove .trim() from A2uiTransportAdapter.incomingText so that original chunk whitespace is preserved as emitted.

@polina-c polina-c Aug 11, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's cool! I am impressed.

which model did you use?

applied the fix

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that was just flash actually! I'll send you a screenshot of my workflow :-D

notifyListeners();
}

Expand Down
Loading