ai icon indicating copy to clipboard operation
ai copied to clipboard

Ending `textStream.done()` in `streamUI()` causes another render in component after version `>=3.2.6`

Open Theonlyhamstertoh opened this issue 1 year ago • 0 comments

Description

Building on top of https://github.com/vercel/ai/issues/2183. It appears that any versions after [email protected] will cause an extra component rerender once textStream.done() is ran/when the stream ends after text generation. This bug is noticeable in [email protected]

Video demo with [email protected]

https://github.com/vercel/ai/assets/75579372/50d0b2b5-e930-4a7e-bd45-83bbadf86912

Video demo with [email protected]

https://github.com/vercel/ai/assets/75579372/b47108d1-769a-4aab-bb67-47f039ea66fa

Code example

Example code using streamUI() and textStream.done():

async function submitUserMessage(content: string) {
  "use server"

  const aiState = getMutableAIState<typeof AI>()

  aiState.update({
    ...aiState.get(),
    messages: [
      ...aiState.get().messages,
      {
        id: nanoid(),
        role: "user",
        content,
      },
    ],
  })

  let textStream: undefined | ReturnType<typeof createStreamableValue<string>>
  let textNode: undefined | React.ReactNode

  const result = await streamUI({
    model: openai("gpt-4o"),
    initial: <SpinnerMessage />,
    system: system_instructions,
    messages: [
      ...aiState.get().messages.map((message: any) => ({
        role: message.role,
        content: message.content,
        name: message.name,
      })),
    ],
    text: ({ content, done, delta }) => {
      if (!textStream) {
        textStream = createStreamableValue("")
        textNode = <BotMessage content={textStream.value} />
      }

      if (done) {
        textStream.done()
        aiState.done({
          ...aiState.get(),
          messages: [
            ...aiState.get().messages,
            {
              id: nanoid(),
              role: "assistant",
              content,
            },
          ],
        })
      } else {
        textStream.update(delta)
      }
      return textNode
    },
    onFinish: result => {
      console.log(result.finishReason)
    },
  })

  return {
    id: nanoid(),
    display: result.value,
  }
}

Additional context

No response

Theonlyhamstertoh avatar Jul 10 '24 20:07 Theonlyhamstertoh