useChat에서 어시스턴트 메시지가 반복돼요

useChat에서 어시스턴트 메시지가 반복돼요

서버에서 streamText와 함께 useChat을 쓸 때 어시스턴트 메시지가 UI에 중복으로 보이는 문제와 해결 방법을 알려드려요.

출처: 문서

본문

문제 (Issue)

서버에서 useChat을 streamText와 함께 사용할 때, 어시스턴트의 메시지가 UI에 중복으로 보여요 — 이전 메시지와 새 메시지가 모두 보이거나, 같은 메시지가 여러 번 보여요. 이는 도구 호출이나 복잡한 메시지 흐름을 사용할 때 발생할 수 있어요.

// 클라이언트에서 어시스턴트 메시지 중복이 발생할 수 있는 서버 측 코드
export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: 'openai/gpt-6-luna',
    messages: await convertToModelMessages(messages),
    tools: {
      weather: {
        description: 'Get the weather for a location',
        inputSchema: z.object({
          location: z.string(),
        }),
        execute: async ({ location }) => {
          return { temperature: 72, condition: 'sunny' };
        },
      },
    },
  });

  return createUIMessageStreamResponse({
    stream: toUIMessageStream({ stream: result.stream }),
  });
}

배경 (Background)

중복은 UI 메시지 스트림이 각 새 메시지에 대해 새 메시지 ID를 생성하기 때문에 발생해요.

해결 방법 (Solution)

originalMessages 옵션을 사용해 원본 메시지 배열을 toUIMessageStream에 전달하세요. originalMessages를 전달하면 헬퍼가 새 ID를 생성하는 대신 기존 메시지 ID를 재사용해, 클라이언트가 중복을 만들지 않고 기존 메시지를 올바르게 업데이트하게 해요.

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: 'openai/gpt-6-luna',
    messages: await convertToModelMessages(messages),
    tools: {
      weather: {
        description: 'Get the weather for a location',
        inputSchema: z.object({
          location: z.string(),
        }),
        execute: async ({ location }) => {
          return { temperature: 72, condition: 'sunny' };
        },
      },
    },
  });

  return createUIMessageStreamResponse({
    stream: toUIMessageStream({
      stream: result.stream,
      originalMessages: messages, // 여기에 원본 메시지 전달
      generateMessageId: generateId,
      onEnd: ({ messages }) => {
        saveChat({ id, messages });
      },
    }),
  });
}

더 알아보기 (Learn more)