Skip to main content

Maintaining Conversation History Across Sessions

NavTalk supports maintaining conversation history across multiple sessions, allowing the digital human to remember previous conversations and maintain context continuity.

Workflow Overview

The conversation history workflow follows this sequence:
  1. Store messages: Save user and assistant messages as they occur during the conversation
  2. Send configuration: When WebSocket connection opens, send realtime.input_config message with voice, prompt, and optionally tools (for OpenAI models only)
  3. Load on connection: Wait for realtime.session.created event
  4. Send history: Send stored user messages using conversation.item.create messages
  5. Wait for ready: After sending history, wait for realtime.session.updated event before starting audio input
Session configuration (voice, prompt, tools) is sent via realtime.input_config message immediately after the WebSocket connection opens. The conversation.item.create messages are only used to send conversation history after receiving realtime.session.created.The tools parameter is optional and only applicable when using OpenAI models (gpt-realtime, gpt-realtime-mini). Function calling is not supported by other AI models.
Follow these steps to implement persistent conversation history:
1

Store Conversation History

Save each conversation message to persistent storage as it occurs:
const NavTalkMessageType = Object.freeze({
    REALTIME_SESSION_CREATED: "realtime.session.created",
    REALTIME_SESSION_UPDATED: "realtime.session.updated",
    REALTIME_CONVERSATION_ITEM_COMPLETED: "realtime.conversation.item.input_audio_transcription.completed",
    REALTIME_RESPONSE_AUDIO_TRANSCRIPT_DONE: "realtime.response.audio_transcript.done",
    // ... other event types
});

// Save conversation history to localStorage
async function appendRealtimeChatHistory(role, content) {
  // Get existing history from storage
  let history = localStorage.getItem("realtimeChatHistory");
  let realtimeChatHistory = history ? JSON.parse(history) : [];
  
  // Add new message
  realtimeChatHistory.push({ role, content });
  
  // Save back to storage
  localStorage.setItem("realtimeChatHistory", JSON.stringify(realtimeChatHistory));
}

// Call this function when receiving user transcription
socket.onmessage = async (event) => {
  if (typeof event.data === 'string') {
    const data = JSON.parse(event.data);
    const nav_data = data.data;
    
    // Save user message when transcription completes
    if (nav_data && data.type === NavTalkMessageType.REALTIME_CONVERSATION_ITEM_COMPLETED) {
      const transcript = nav_data.content;
      if (transcript) {
        await appendRealtimeChatHistory("user", transcript);
      }
    }
    
    // Save AI response when complete
    if (nav_data && data.type === NavTalkMessageType.REALTIME_RESPONSE_AUDIO_TRANSCRIPT_DONE) {
      const transcript = nav_data.content;
      if (transcript) {
        await appendRealtimeChatHistory("assistant", transcript);
      }
    }
  }
};
2

Load History on Session Start

When establishing a new WebSocket connection, load the stored history and send it to the server:
async function sendSessionUpdate() {
  // Load conversation history from storage
  const history = localStorage.getItem("realtimeChatHistory");
  const conversationHistory = history ? JSON.parse(history) : [];
  
  // Session configuration (voice, model, instructions, etc.) is passed through URL parameters
  // Only send user message history
  conversationHistory.forEach((msg) => {
    if (msg.role === "user") {
      const messageConfig = {
        type: "conversation.item.create",
        item: {
          type: "message",
          role: "user",
          content: [
            {
              type: "input_text",
              text: msg.content
            }
          ]
        }
      };
      
      try {
        // Send user messages from history
        socket.send(JSON.stringify(messageConfig));
        console.log("Sent historical message:", msg.content);
      } catch (e) {
        console.error("Error sending historical message:", e);
      }
    }
  });
}

// Call this after receiving session.created
socket.onmessage = async (event) => {
  if (typeof event.data === 'string') {
    const data = JSON.parse(event.data);
    
    if (data.type === NavTalkMessageType.REALTIME_SESSION_CREATED) {
      console.log("Session created, sending history.");
      await sendSessionUpdate();
    }
    
    if (data.type === NavTalkMessageType.REALTIME_SESSION_UPDATED) {
      console.log("Session updated. Ready to receive audio.");
      // Start recording audio here
    }
  }
};
3

Restore Conversation Display

When the page loads, restore the conversation history display for the user:
// Restore conversation history display on page load
async function initConversationHistory() {
  // Load history from storage
  const historyStr = localStorage.getItem("realtimeChatHistory");
  const realtimeChatHistory = historyStr ? JSON.parse(historyStr) : [];
  
  // Render each historical message in the UI
  if (realtimeChatHistory && realtimeChatHistory.length > 0) {
    realtimeChatHistory.forEach(item => {
      appendMessageToUI(item.role, item.content);
    });
    
    // Scroll to bottom to show latest messages
    const chatContainer = document.querySelector('.chat-container');
    if (chatContainer) {
      chatContainer.scrollTop = chatContainer.scrollHeight;
    }
  }
}

function appendMessageToUI(role, content) {
  const container = document.createElement('div');
  container.classList.add('message', role === 'user' ? 'message-user' : 'message-assistant');
  
  const contentDiv = document.createElement('div');
  contentDiv.classList.add('message-content');
  contentDiv.textContent = content;
  
  container.appendChild(contentDiv);
  
  const chatContainer = document.querySelector('.chat-container');
  if (chatContainer) {
    chatContainer.appendChild(container);
  }
}

// Initialize on page load
document.addEventListener('DOMContentLoaded', () => {
  initConversationHistory();
});

Important Notes

When sending historical messages via conversation.item.create, only user messages can be sent. AI-generated historical messages cannot be embedded this way.