Skip to content

feat: add message branching for regenerated responses#9

Merged
kowyo merged 3 commits into
mainfrom
feat/message-branching
Feb 2, 2026
Merged

feat: add message branching for regenerated responses#9
kowyo merged 3 commits into
mainfrom
feat/message-branching

Conversation

@kowyo

@kowyo kowyo commented Feb 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add message branching support allowing users to navigate between different AI response versions
  • Show regenerate button only on the latest assistant message to prevent mid-conversation regeneration issues
  • Implement branch navigation UI with < 1 of N > controls for messages with multiple versions

Changes

Database

  • Add parent_id column to chat_messages table to create a tree structure for conversation history

Backend (app/api/chat/route.ts)

  • Prevent duplicate user messages when regenerating by checking for existing messages by ID or content
  • Set parent_id on new messages to maintain proper tree structure

Frontend

  • Update ChatMessageList to display branch selector when multiple versions exist
  • Add onBranchChange handler to switch between message versions
  • Sync frontend state with database after response completion
  • Fix MessageBranchContent className override bug that caused all branches to display simultaneously

Test plan

  • Send a message and verify response is saved correctly
  • Click regenerate on latest message and verify new branch is created
  • Verify branch selector shows 1 of 2 after regeneration
  • Click branch navigation arrows to switch between responses
  • Verify only latest message shows regenerate button
  • Reload page and verify branches are preserved

Made with Cursor

- Add parent_id column to chat_messages table for tree structure
- Show regenerate button only on the latest assistant message
- Implement branch navigation UI (< 1 of N >) for messages with siblings
- Prevent duplicate user messages when regenerating responses
- Sync frontend state with database after response completion
- Fix MessageBranchContent className override bug that showed all branches

Co-authored-by: Cursor <cursoragent@cursor.com>
@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @kowyo, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant feature that allows users to explore and manage different versions of AI responses within a single chat conversation. By implementing a message branching system, users can regenerate responses and then navigate between these alternative outputs, providing greater flexibility and control over their interactions. The changes span database schema, backend message persistence, and frontend UI, ensuring a seamless and intuitive user experience for managing branched conversations.

Highlights

  • Message Branching Implementation: Introduced a new parent_id column in the chat_messages table to support a tree-like structure for conversation history, enabling multiple AI response versions.
  • Frontend Branch Navigation: Added UI controls (< 1 of N >) to allow users to navigate between different versions of AI responses for a given message.
  • Regenerate Button Logic: The "Regenerate" button is now only displayed on the latest assistant message to prevent issues with mid-conversation regeneration and maintain a clear conversation flow.
  • Robust User Message Handling: Enhanced backend logic to prevent duplicate user messages during regeneration by checking for existing messages by ID or content before insertion.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request successfully implements message branching for AI responses, allowing users to navigate between different versions of an AI's reply. The changes involve adding a parent_id column to the chat_messages table in the database, updating the backend to correctly persist messages with this new relationship, and enhancing the frontend to display and manage these branched conversations. The logic for preventing duplicate user messages and showing the regenerate button only on the latest assistant message aligns well with the feature's goals. Overall, the implementation provides a robust foundation for conversational branching.

Comment thread app/api/chat/route.ts Outdated
Comment on lines +119 to +125
let parentId = lastMessageId;
if (!parentId) {
const lastInDb = await pool.query(
`select id from chat_messages where chat_id = $1 order by created_at desc limit 1`,
[chatId]
);
parentId = lastInDb.rows[0]?.id;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The fallback logic for determining parentId in the onFinish callback is problematic. The lastMessageId variable is intended to hold the ID of the user message that initiated the current AI response. If lastMessageId is null at this point, it indicates an issue with the preceding user message's persistence or identification (lines 33-84). Relying on a query for the absolute latest message in the database (select id from chat_messages where chat_id = $1 order by created_at desc limit 1) can lead to an incorrect conversation tree, as the latest message might be another assistant message or an unrelated user message. This creates a logically flawed parent-child relationship. It is more robust to ensure lastMessageId is always correctly populated. If it is unexpectedly null, the assistant message should be inserted with a null parent_id (creating an orphaned message) to highlight the underlying issue, rather than creating an incorrect link. The database schema allows parent_id to be null, so this change will result in an orphaned message if lastMessageId is indeed null, which is preferable to an incorrect link.

Suggested change
let parentId = lastMessageId;
if (!parentId) {
const lastInDb = await pool.query(
`select id from chat_messages where chat_id = $1 order by created_at desc limit 1`,
[chatId]
);
parentId = lastInDb.rows[0]?.id;
const parentId = lastMessageId;

kowyo and others added 2 commits February 2, 2026 19:41
The client now sends parentMessageId (the last message in the current branch)
to the server when creating new messages. This ensures new messages are linked
to the correct parent based on the user's selected branch, rather than always
using the absolute latest message in the database.

Co-authored-by: Cursor <cursoragent@cursor.com>
@kowyo kowyo merged commit 6bd2ef9 into main Feb 2, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant