Skip to content

Persist stable message IDs from backend through REST + SSE to frontend #12270

@linear

Description

@linear

Problem

The backend ChatMessage Prisma model already has a stable UUID id field, but the Pydantic ChatMessage model strips it in from_db(). This means:

  1. Hydrated messages (from REST API) get synthetic IDs like ${sessionId}-${index}
  2. Streamed messages (from AI SDK via SSE) get their own auto-generated UUIDs
  3. Same message, different ID depending on the source

The frontend currently works around this with a content fingerprint deduplication function (deduplicateMessages in helpers.ts) that compares consecutive assistant messages by text content. This is fragile and adds complexity.

Root Cause

The backend Pydantic model (backend/copilot/model.py:ChatMessage) does not include the id field, even though the Prisma schema already has it as a UUID primary key.

Proposed Solution

Backend

  1. Add id: str | None = None to the Pydantic ChatMessage model
  2. Thread it through from_db(): id=prisma_message.id
  3. Include id in REST responses (session history endpoint)
  4. Include id in SSE stream events (when saving messages during streaming)

Frontend

  1. Update convertChatSessionMessagesToUiMessages to use msg.id from the API response instead of ${sessionId}-${index}
  2. Align AI SDK streaming to use the same IDs when available
  3. Simplify or remove the deduplicateMessages fingerprint logic once IDs are stable

Files to Change

Backend:

  • backend/copilot/model.py - Add id field to ChatMessage, update from_db()
  • Chat REST endpoints - Ensure id is serialized in responses
  • SSE streaming - Include message id in stream events

Frontend:

  • copilot/helpers/convertChatSessionToUiMessages.ts - Use backend id instead of synthetic
  • copilot/helpers.ts - Simplify deduplicateMessages (may become unnecessary)
  • copilot/useCopilotStream.ts - Align hydrated + streamed message IDs

Context

Discussion: https://discord.com/channels/1126875755960336515/1126875756925046928/1478360192495128708

Originated from Zamil's review of Ubbe's SSE proxy removal PR (#12254) - the dedup logic raised questions about why IDs aren't aligned between backend and frontend.

Requested by

@majdyz

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