Message Save Flow Analysis - User Messages Saved Twice
Overview
When using assistants, user messages are being saved twice to Firestore, while assistant messages are saved only once. This document traces the complete flow to understand why this duplication occurs.
The Complete Flow
1. Frontend Initiates Request
When a user sends a message through the chat interface:
- Frontend saves user message immediately (
StreamingContext.tsx:100):const messageData = EnhancedFirebaseService.createUserMessagePayload(text, currentUser, traceId); const savePromise = EnhancedFirebaseService.saveMessage(assistantConfig.assistantId, messageData); - Frontend calls the streaming API (
chatStreaming.ts:436):await vacChat({ // ... apiEndpoint: `/vac/streaming/aitana3`, save_to_history: true, // This flag is passed to backend // ... });
2. Request Routes Through Backend
The request follows this path:
- Frontend → Proxy (
/api/proxy):- The proxy forwards the request to the backend with the endpoint
/vac/streaming/aitana3
- The proxy forwards the request to the backend with the endpoint
- Backend Router (
app.py:104):VACRoutescreates the/vac/streaming/<vector_name>endpoint- Uses
vac_stream_with_assistant_supportas the stream interpreter
- Wrapper Function (
vac_service_wrapper.py):- Detects
assistantIdinemissaryConfig - Routes to assistant logic instead of regular vac_stream
- Calls original
vac_streamwith assistant configuration
- Detects
- Backend saves messages (
vac_service_wrapper.py:122-143):# Check if we need to save messages to history save_to_history = emissary_config.get('save_to_history', kwargs.get('save_to_history', False)) if save_to_history and response and "metadata" in response: # Calls save_messages_after_streaming which saves BOTH user and assistant messages await save_messages_after_streaming(...)
3. Why User Messages Are Saved Twice
- First Save: Frontend saves user message immediately when sending (
StreamingContext.tsx:100) - Second Save: Backend saves user message in
save_messages_after_streaming(assistant_config.py:198)
4. Why Assistant Messages Are Only Saved Once
- Frontend no longer saves assistant messages (comment at
chatStreaming.ts:475-479) - Only the backend saves assistant messages in
save_messages_after_streaming - When the wrapper save logic was removed, assistant messages were lost because nothing else saves them
The Root Cause
The duplication occurs because:
- Frontend proactively saves user messages for immediate UI feedback
- Backend also saves user messages as part of the complete conversation flow
- Both saves happen because
save_to_history: trueis passed from frontend
Alternative Flows
Direct Assistant Endpoint (/vac/assistant/<assistant_id>)
When calling the assistant endpoint directly:
assistant_streaminassistant_config.pyhandles the request- Also calls
save_messages_after_streamingwhen streaming completes - Would still result in duplicate user messages if frontend saves first
Email Integration Flow
- Uses
process_assistant_requestfromassistant_utils.py - This function also saves both user and assistant messages when
save_to_history=True - No duplication here because email integration doesn’t pre-save messages
Solutions
Option 1: Frontend Only Saves User Messages
- Keep frontend saving user messages for immediate UI feedback
- Modify backend to only save assistant messages
- Pros: No changes to frontend, maintains immediate UI feedback
- Cons: Split responsibility for message saving
Option 2: Backend Saves Everything
- Remove frontend message saving
- Let backend handle all message persistence
- Pros: Single source of truth, consistent save logic
- Cons: Slight delay before user message appears in Firestore
Option 3: Conditional Backend Save
- Backend checks if user message already exists before saving
- Only saves if not already present
- Pros: Handles both flows gracefully
- Cons: Additional Firestore read operations
Option 4: Pass Flag from Frontend
- Frontend passes
user_message_already_saved: trueflag - Backend skips user message save when flag is true
- Pros: Explicit control, no extra reads
- Cons: Requires frontend changes
Recommendation
Option 4 (Pass Flag from Frontend) is recommended because:
- Maintains immediate UI feedback
- Avoids duplicate saves
- No extra Firestore reads
- Clear and explicit control flow
- Minimal code changes required
Implementation Plan
- Modify frontend to pass
user_message_already_saved: truein the request - Update backend save logic to check this flag
- Test all flows (streaming, direct assistant, email integration)
- Ensure backward compatibility for API clients