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