This explanation will trace the data flow and file interactions for two distinct operations: first, when a conversation thread is forked, and second, when a new message is sent to that newly forked branch.
This process initiates a new independent conversation thread, inheriting the history of a parent thread up to a specified point.
1. User Action: Call to POST /commands/fork-thread
- File:
app/api/commands.py - Function:
fork_thread - Description:
- The user sends a
POSTrequest to the/commands/fork-threadendpoint, providing thesource_thread_id(the ID of the thread to fork from) and theevent_number(the point in the parent thread's history where the fork occurs). - A new
thread_idis generated for the branch (e.g.,branch-xxxx). - This function does not interact with LangGraph or directly update projections. It focuses solely on appending events to the event store.
- The user sends a
2. Event Store Interaction: Appending Events
- File:
app/api/commands.py(callsEventStoremethods) ->app/core/event_store.py - Function:
EventStore.append_events - Description:
- The
fork_threadfunction instructs theEventStoreto append two new events for thenew_thread_id:ThreadCreated: Indicates the creation of the new branch. Its payload contains thenew_thread_id.ThreadForked: Crucial for establishing lineage. Its payload containsparent_thread_idandfrom_event_number(the original event number from the parent).
- These events are written to the
eventstable in the PostgreSQL database. This is the immutable source of truth.
- The
3. Projection Worker: Updating branches_projection
- File:
app/projections/worker.py->app/projections/projector.py->app/projections/handlers.py - Functions:
ProjectionWorker.run_once,Projector.project_event,handle_thread_forked - Description:
- The
ProjectionWorker(running continuously in the background) periodically checks for new events in theeventstable by consultingprojection_offsets. - When it finds the
ThreadForkedevent:ProjectionWorker.run_oncefetches the event.Projector.project_eventdispatches the event to the appropriate handler based onEVENT_HANDLER_MAP.handle_thread_forked(inapp/projections/handlers.py) is invoked.- This handler inserts a new record into the
branches_projectiontable. This record contains thethread_idof the new branch, theparent_thread_id, thefrom_event_number, and thecreated_attimestamp.
- The
branches_projectiontable is a read model that allows the API to quickly list branches of a given parent thread without querying the entire event log.
- The
This process involves adding a new user input to the forked conversation and generating an AI response, ensuring the inherited context is maintained.
1. User Action: Call to POST /commands/send-message
- File:
app/api/commands.py - Function:
send_message - Description:
- The user sends a
POSTrequest to the/commands/send-messageendpoint, providing thethread_idof the forked branch (e.g.,branch-xxxx) and thecontentof the new message. - Like
fork_thread, this function does not interact with LangGraph or directly update projections.
- The user sends a
2. Event Store Interaction: Appending UserMessageAdded Event
- File:
app/api/commands.py(callsEventStoremethods) ->app/core/event_store.py - Function:
EventStore.append_event - Description:
- The
send_messagefunction appends aUserMessageAddedevent to the event store for the forked branch'sthread_id. The payload contains the messagecontentandrole("user"). - This event is written to the
eventstable, becoming part of the forked branch's event history.
- The
3. Conversation Worker: Processing the New Message (Crucial for Context)
- File:
app/workers/conversation_worker.py->app/core/langgraph_runner.py - Functions:
ConversationWorker.process_thread,_handle_user_message,run_langgraph_from_events - Description:
- The
ConversationWorker(running continuously in the background) monitors for newUserMessageAddedevents that haven't received an AI response. - When it finds the
UserMessageAddedevent for the forked branch:ConversationWorker.process_threadloads all events for the current forkedthread_id.- Context Reconstruction (inside
_handle_user_message):- It identifies the
ThreadForkedevent within the forked branch's history. - It then loads the events from the
parent_thread_idup to thefrom_event_number(which was9in the user's example). - It combines these parent events with all events from the current forked branch. This combined list represents the full historical context for LangGraph.
- This combined list is then filtered to include only events up to the current
UserMessageAddedevent, creatingprior_events.
- It identifies the
- Checkpoint Resumption (inside
process_thread):- Before processing the
user_event,process_threaddetermines theinitial_resume_checkpoint_id. It first looks for the latestCheckpointCreatedevent within the forked branch's own history. - If no such checkpoint exists in the forked branch (which would be the case for the very first message sent to a fresh fork), it then looks for the
CheckpointCreatedevent in the parent thread at the fork point (from_event_number + 1). This is critical for inheriting the parent's AI state.
- Before processing the
run_langgraph_from_eventsis called, feeding it:- The
prior_events(the full combined historical context). - The
thread_idof the forked branch. - The
resume_checkpoint_id(either from the forked branch's own history or the inherited parent checkpoint).
- The
- LangGraph, seeded with the correct checkpoint and the full historical context, processes the
UserMessageAddedevent and generates an AI response. Because it receives the full history, it remembers information like "I am devansh".
- Emitting AI Events: LangGraph's result (AI response and new checkpoint ID) is then used to append two more events to the event store for the forked branch:
LLMResponseGenerated: Contains the AI's response content andai_message_id.CheckpointCreated: Contains the newcheckpoint_id(representing the state after this AI response) and the associatedai_message_id.
- The
4. Projection Worker: Updating Read Models
- File:
app/projections/worker.py->app/projections/projector.py->app/projections/handlers.py - Functions:
ProjectionWorker.run_once,Projector.project_event,handle_llm_response_generated,handle_checkpoint_created - Description:
- The
ProjectionWorkeridentifies the newly appendedLLMResponseGeneratedandCheckpointCreatedevents. handle_llm_response_generatedinserts the AI message into thethread_timelinetable for the forked branch.handle_checkpoint_createdinserts the mapping intomessage_checkpointsand updates thethread_timelineentry with thecheckpoint_id, and updatesthread_headswith the latest state for the forked branch.
- The
5. User Action: Call to GET /threads/{forked_thread_id}/messages
- File:
app/api/reads.py - Function:
get_messages - Description:
- When the user retrieves messages for the forked branch:
- The
get_messagesfunction identifies theThreadForkedevent in the branch's history. - It loads messages from the
parent_thread_idup to thefrom_event_number. - It then loads all messages from the forked
thread_id. - It combines these two sets of messages.
- Finally, it sorts all combined messages by their
event_number, presenting a single, chronologically ordered conversation history to the user. This is why the user message sent to the forked branch ("what is my name") now appears correctly interspersed with the inherited history.
- The
- When the user retrieves messages for the forked branch:
Summary of Fixes Addressing the User's Issue:
- Context for LangGraph (
app/workers/conversation_worker.py): The modification ensures that_handle_user_messagenow correctly assembles theprior_eventslist for LangGraph, providing the full historical context (parent's history + forked branch's history) up to the current user message. This should allow the AI to "remember" previous interactions like the user's name. - Message Retrieval Order (
app/api/reads.py): The modification toget_messagesensures that messages from the forked branch are properly included in the combined history displayed by the API, and sorted correctly.
By implementing these changes, the system now adheres to the "Git for LLM conversations" model, where forked branches correctly inherit and build upon their parent's context.