Skip to content

Conversation History

Learn how to persist and manage conversation history across sessions using the Cyborg SDK.

Overview

The Cyborg SDK can automatically save and restore chat conversations, allowing users to continue where they left off. This is enabled through the uniqueIdentifier configuration option.

Enabling Conversation History

To enable conversation persistence, provide a uniqueIdentifier in your CyborgProvider config:

tsx
import { CyborgProvider } from '@cyborg-sdk/react'

function App() {
  return (
    <CyborgProvider
      config={{
        publishableKey: process.env.CYBORG_PUBLISHABLE_KEY,
        uniqueIdentifier: 'user_123' // Unique per user
      }}
    >
      <YourApp />
    </CyborgProvider>
  )
}

How It Works

When uniqueIdentifier is set:

  1. Automatic Saving: Each message exchange is automatically saved to the backend
  2. Session Restoration: Previous conversations can be loaded on subsequent visits
  3. Cross-Device Sync: Users can continue conversations from any device

When uniqueIdentifier is not set:

  • Conversations are ephemeral (in-memory only)
  • History is lost on page refresh
  • No backend persistence

Managing Conversations

The useChat hook provides methods for managing conversation history:

List All Conversations

tsx
import { useChat } from '@cyborg-sdk/react'

function ConversationList() {
  const { listConversations } = useChat()

  async function showConversations() {
    const conversations = await listConversations()
    console.log(conversations)
    // [
    //   { id: 'conv_abc', title: 'Product questions', updatedAt: '...' },
    //   { id: 'conv_def', title: 'Technical support', updatedAt: '...' }
    // ]
  }

  return <button onClick={showConversations}>View History</button>
}

Load a Conversation

tsx
import { useChat } from '@cyborg-sdk/react'

function ConversationList() {
  const { listConversations, loadConversation } = useChat()
  const [conversations, setConversations] = useState([])

  useEffect(() => {
    listConversations().then(setConversations)
  }, [])

  return (
    <ul>
      {conversations.map(conv => (
        <li key={conv.id}>
          <button onClick={() => loadConversation(conv.id)}>
            {conv.title}
          </button>
        </li>
      ))}
    </ul>
  )
}

Clear Current Conversation

tsx
import { useChat } from '@cyborg-sdk/react'

function ChatControls() {
  const { clearMessages } = useChat()

  return (
    <button onClick={clearMessages}>
      Start New Conversation
    </button>
  )
}

User Identification Strategies

The uniqueIdentifier should be unique per user. Common strategies:

Authenticated Users

Use the user's ID from your authentication system:

tsx
import { useAuth } from './auth'

function App() {
  const { user } = useAuth()

  return (
    <CyborgProvider
      config={{
        publishableKey: process.env.CYBORG_PUBLISHABLE_KEY,
        uniqueIdentifier: user?.id // Only set when logged in
      }}
    >
      <YourApp />
    </CyborgProvider>
  )
}

Anonymous Users

For anonymous users, generate and persist a unique ID:

tsx
function getAnonymousId() {
  let id = localStorage.getItem('cyborg_anonymous_id')
  if (!id) {
    id = crypto.randomUUID()
    localStorage.setItem('cyborg_anonymous_id', id)
  }
  return id
}

function App() {
  return (
    <CyborgProvider
      config={{
        publishableKey: process.env.CYBORG_PUBLISHABLE_KEY,
        uniqueIdentifier: getAnonymousId()
      }}
    >
      <YourApp />
    </CyborgProvider>
  )
}

Hybrid Approach

Combine authenticated and anonymous IDs:

tsx
function getUserIdentifier(user) {
  if (user?.id) {
    return `user_${user.id}`
  }
  return `anon_${getAnonymousId()}`
}

Conversation Metadata

Each conversation includes metadata:

typescript
interface Conversation {
  id: string              // Unique conversation ID
  title: string           // Auto-generated from first message
  createdAt: string       // ISO 8601 timestamp
  updatedAt: string       // ISO 8601 timestamp
  messageCount: number    // Total messages in conversation
}

Best Practices

1. Consistent Identifiers

Always use the same identifier format for the same user:

tsx
// Good - consistent format
uniqueIdentifier: `user_${user.id}`

// Bad - inconsistent format
uniqueIdentifier: user.id // Sometimes string, sometimes number

2. Handle Missing Identifiers

Gracefully handle cases where the identifier isn't available:

tsx
<CyborgProvider
  config={{
    publishableKey: process.env.CYBORG_PUBLISHABLE_KEY,
    uniqueIdentifier: user?.id || undefined // Undefined = no persistence
  }}
>

3. Clear on Logout

Clear conversation state when users log out:

tsx
function LogoutButton() {
  const { clearMessages } = useChat()
  const { logout } = useAuth()

  async function handleLogout() {
    clearMessages() // Clear local state
    await logout()
  }

  return <button onClick={handleLogout}>Logout</button>
}

4. Privacy Considerations

  • Inform users that conversations are saved
  • Provide a way to delete conversation history
  • Consider data retention policies

Example: Full Implementation

tsx
import { CyborgProvider, useChat } from '@cyborg-sdk/react'
import { useAuth } from './auth'

function ChatHistory() {
  const { listConversations, loadConversation, clearMessages } = useChat()
  const [conversations, setConversations] = useState([])
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    listConversations()
      .then(setConversations)
      .finally(() => setLoading(false))
  }, [])

  if (loading) return <div>Loading history...</div>

  return (
    <div>
      <button onClick={clearMessages}>New Conversation</button>
      <h3>Previous Conversations</h3>
      <ul>
        {conversations.map(conv => (
          <li key={conv.id}>
            <button onClick={() => loadConversation(conv.id)}>
              {conv.title}
              <small>{new Date(conv.updatedAt).toLocaleDateString()}</small>
            </button>
          </li>
        ))}
      </ul>
    </div>
  )
}

function App() {
  const { user } = useAuth()

  return (
    <CyborgProvider
      config={{
        publishableKey: process.env.CYBORG_PUBLISHABLE_KEY,
        uniqueIdentifier: user?.id
      }}
    >
      <YourApp />
      <ChatHistory />
    </CyborgProvider>
  )
}

Next Steps

Built with VitePress