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:
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:
- Automatic Saving: Each message exchange is automatically saved to the backend
- Session Restoration: Previous conversations can be loaded on subsequent visits
- 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
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
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
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:
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:
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:
function getUserIdentifier(user) {
if (user?.id) {
return `user_${user.id}`
}
return `anon_${getAnonymousId()}`
}Conversation Metadata
Each conversation includes metadata:
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:
// Good - consistent format
uniqueIdentifier: `user_${user.id}`
// Bad - inconsistent format
uniqueIdentifier: user.id // Sometimes string, sometimes number2. Handle Missing Identifiers
Gracefully handle cases where the identifier isn't available:
<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:
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
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
- useChat Hook - Full API reference
- Tool Calling - Add custom actions
- Context-Aware Chat - Dynamic context