Skip to content

Performance: updateFromUIMessageChunks assert eagerly JSON.stringify’s large messages #187

@KevinGrajeda

Description

@KevinGrajeda

I’ve been debugging some pretty bad frontend performance in an app using useThreadMessages with streaming enabled. In some cases the UI was dropping to 2 FPS 😭

I know we have a fair amount of re‑rendering going on, but even accounting for that, things felt way slower than expected. So I recorded a performance trace in Chrome and found this:

  • updateFromUIMessageChunks in deltas.ts is taking a big chunk of the main thread time
Image
  • Inside that function, this assert line seems to be the hotspot :
Image

agent/src/deltas.ts

Lines 80 to 83 in b48f51c

assert(
messagePart.id === message.id,
`Expecting to only make one UIMessage in a stream, but have ${JSON.stringify(message)} and created ${JSON.stringify(messagePart)}`,
);

it is surprising, because this is just an equality check. But my theory is that, regardless of whether the equality check passes, the JSON.stringify calls in the template string are always evaluated eagerly.

Since updateFromUIMessageChunks is called for every delta while streaming, this means we’re doing a full JSON.stringify of the whole message on every new delta.

useThreadMessages({ stream: true })
  ↓
useStreamingThreadMessages
  ↓
useStreamingUIMessages
  ↓
useDeltaStreams (fetches stream deltas)
  ↓
useEffect in useStreamingUIMessages (on new deltas)
  ↓
updateFromUIMessageChunks

this happens with both useThreadMessages and useUIMessages

Possible fix

If my understanding is correct, this should be a relatively easy performance win by making the error message lazy, so JSON.stringify only runs on failure.

Happy to provide more details if helpful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions