> ## Documentation Index
> Fetch the complete documentation index at: https://docs.navtalk.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Maintaining Conversation History

> Learn how to maintain conversation history and context across sessions using NavTalk Real-time Digital Human API

## 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

<Note>
  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.
</Note>

Follow these steps to implement persistent conversation history:

<Steps>
  <Step title="Store Conversation History">
    Save each conversation message to persistent storage as it occurs:

    <CodeGroup>
      ```javascript JavaScript theme={null}
      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);
            }
          }
        }
      };
      ```

      ```python Python theme={null}
      import json
      import os

      NAV_TALK_MESSAGE_TYPE = {
          '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
      }

      def save_conversation_history(role, content, history_file="conversation_history.json"):
          """Save conversation message to file"""
          # Load existing history
          if os.path.exists(history_file):
              with open(history_file, 'r', encoding='utf-8') as f:
                  history = json.load(f)
          else:
              history = []
          
          # Add new message
          history.append({"role": role, "content": content})
          
          # Save back to file
          with open(history_file, 'w', encoding='utf-8') as f:
              json.dump(history, f, ensure_ascii=False, indent=2)

      # Call when receiving messages
      async def handle_messages(websocket):
          async for message in websocket:
              if isinstance(message, str):
                  data = json.loads(message)
                  nav_data = data.get('data')
                  
                  if nav_data and data.get('type') == NAV_TALK_MESSAGE_TYPE['REALTIME_CONVERSATION_ITEM_COMPLETED']:
                      transcript = nav_data.get('content')
                      if transcript:
                          save_conversation_history("user", transcript)
                  
                  elif nav_data and data.get('type') == NAV_TALK_MESSAGE_TYPE['REALTIME_RESPONSE_AUDIO_TRANSCRIPT_DONE']:
                      transcript = nav_data.get('content')
                      if transcript:
                          save_conversation_history("assistant", transcript)
      ```
    </CodeGroup>
  </Step>

  <Step title="Load History on Session Start">
    When establishing a new WebSocket connection, load the stored history and send it to the server:

    <CodeGroup>
      ```javascript JavaScript theme={null}
      async function sendSessionUpdate() {
        // Load conversation history from storage
        const history = localStorage.getItem("realtimeChatHistory");
        const conversationHistory = history ? JSON.parse(history) : [];
        
        // Session configuration (voice, prompt, tools, etc.) is sent via realtime.input_config
        // 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
          }
        }
      };
      ```

      ```python Python theme={null}
      import json

      async def send_session_update(websocket):
          """Send conversation history after session configuration has been sent via realtime.input_config."""
          # Load conversation history
          history_file = "conversation_history.json"
          if os.path.exists(history_file):
              with open(history_file, 'r', encoding='utf-8') as f:
                  conversation_history = json.load(f)
          else:
              conversation_history = []
          
          # Session configuration (voice, prompt, tools, etc.) is sent via realtime.input_config
          # Only send user message history
          for msg in conversation_history:
              if msg["role"] == "user":
                  message_config = {
                      "type": "conversation.item.create",
                      "item": {
                          "type": "message",
                          "role": "user",
                          "content": [
                              {
                                  "type": "input_text",
                                  "text": msg["content"]
                              }
                          ]
                      }
                  }
                  try:
                      await websocket.send(json.dumps(message_config))
                      print(f"Sent historical message: {msg['content']}")
                  except Exception as e:
                      print(f"Error sending historical message: {e}")

      # Handle session creation
      async def handle_messages(websocket):
          async for message in websocket:
              if isinstance(message, str):
                  data = json.loads(message)
                  
                  if data.get('type') == NAV_TALK_MESSAGE_TYPE['REALTIME_SESSION_CREATED']:
                      await send_session_update(websocket)
                  
                  elif data.get('type') == NAV_TALK_MESSAGE_TYPE['REALTIME_SESSION_UPDATED']:
                      print("Session updated. Ready to receive audio.")
      ```
    </CodeGroup>
  </Step>

  <Step title="Restore Conversation Display">
    When the page loads, restore the conversation history display for the user:

    <CodeGroup>
      ```javascript JavaScript theme={null}
      // 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();
      });
      ```

      ```python Python theme={null}
      # For Python web applications, restore history when rendering the page
      def load_conversation_history():
          """Load and return conversation history"""
          history_file = "conversation_history.json"
          if os.path.exists(history_file):
              with open(history_file, 'r', encoding='utf-8') as f:
                  return json.load(f)
          return []

      # In your web framework (Flask/Django/FastAPI), pass history to template
      @app.route('/')
      def index():
          history = load_conversation_history()
          return render_template('index.html', conversation_history=history)
      ```
    </CodeGroup>
  </Step>
</Steps>

## Important Notes

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