Display Conversation Messages
Display and manage conversation messages between the user and AI assistant during voice calls.
Accessing Messages
The conversation is available through the Micdrop state and contains all messages exchanged during the current session.
When the server runs a speech to text and nothing else, each sentence the user says arrives in the conversation as a user message. A dictation page builds its text from those messages.
Basic Usage
Access conversation messages using the state:
import { Micdrop } from '@micdrop/web'
// Get current conversationconst conversation = Micdrop.conversation
console.log('Total messages:', conversation.length)conversation.forEach((message, index) => { console.log(`${message.role}: ${message.content}`)})Message Structure
Each item in the conversation can be a regular message, tool call, or tool result:
type MicdropConversationItem = | MicdropConversationMessage | MicdropConversationToolCall | MicdropConversationToolResult
interface MicdropConversationMessage { role: 'user' | 'assistant' | 'system' content: string metadata?: Record<string, any>}
interface MicdropConversationToolCall { role: 'tool_call' toolName: string parameters: string // JSON string}
interface MicdropConversationToolResult { role: 'tool_result' toolName: string output: string // JSON string}Message Types:
MicdropConversationMessage: Regular conversation messages'user': Messages from the user (transcribed speech)'assistant': Messages from the AI assistant'system': System messages (rare, used for debugging)
MicdropConversationToolCall: When the AI agent calls a tool/functionMicdropConversationToolResult: The result/output from a tool execution
Message Events
The Message event fires once per message added to the conversation, which
saves comparing the array against the one you saw last time:
import { Micdrop } from '@micdrop/web'
Micdrop.on('Message', (message) => { if (message.role === 'user') { console.log('The user said:', message.content) }})StateChange carries the whole conversation instead, for a view that redraws
from the current state rather than reacting to what changed:
Micdrop.on('StateChange', (state) => { console.log('Messages:', state.conversation)})The Answer Being Written
A server configured with partialMessages also
sends the answer before it is finished. The client keeps it next to the
conversation:
Micdrop.on('StateChange', (state) => { console.log('Being written:', state.partialAssistantMessage)})It is an empty string the rest of the time, and empties as soon as the settled
message lands in conversation. The PartialAssistantMessage event carries the
same text.
React Integration
Use the React hook to automatically update your UI when new messages arrive:
import { useMicdropState } from '@micdrop/react'
function ConversationDisplay() { const { conversation } = useMicdropState()
return ( <div className="conversation"> {conversation.map((message, index) => ( <div key={index} className={`message ${message.role}`}> <strong>{message.role}:</strong> {message.content} </div> ))} </div> )}Complete React Component Example
Hereβs a full-featured conversation component with styling and auto-scroll that show tool calls:
import { useMicdropState } from '@micdrop/react'import { useEffect, useRef } from 'react'
interface Props { className?: string}
export default function Conversation({ className }: Props) { const { conversation } = useMicdropState() const bottomRef = useRef<HTMLDivElement>(null)
// Auto-scroll to bottom when new messages arrive useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: 'smooth' }) }, [conversation.length])
const renderMessage = (item: (typeof conversation)[0], index: number) => { switch (item.role) { case 'user': case 'assistant': case 'system': return ( <div key={index} className={`max-w-[80%] p-3 rounded-xl ${ item.role === 'assistant' ? 'bg-green-100 ml-0 mr-auto rounded-bl-none' : item.role === 'user' ? 'bg-white ml-auto mr-0 rounded-br-none' : 'bg-gray-50 mx-auto' }`} > {item.content} </div> )
case 'tool_call': return ( <div key={index} className="max-w-[80%] p-3 rounded-xl bg-blue-50 ml-0 mr-auto border-l-4 border-blue-300" > <div className="text-sm text-blue-600 font-medium mb-1"> π§ Tool Call: {item.toolName} </div> <div className="text-xs text-blue-500 font-mono bg-blue-100 p-2 rounded"> {JSON.stringify(JSON.parse(item.parameters), null, 2)} </div> </div> )
case 'tool_result': return ( <div key={index} className="max-w-[80%] p-3 rounded-xl bg-purple-50 ml-0 mr-auto border-l-4 border-purple-300" > <div className="text-sm text-purple-600 font-medium mb-1"> π Tool Result: {item.toolName} </div> <div className="text-xs text-purple-500 font-mono bg-purple-100 p-2 rounded"> {JSON.stringify(JSON.parse(item.output), null, 2)} </div> </div> )
default: return null } }
return ( <div className={`flex flex-col overflow-y-auto p-6 ${className}`}> <div className="flex flex-col gap-4"> {conversation.map((item, index) => renderMessage(item, index))} <div ref={bottomRef} /> </div> </div> )}Clearing Conversation
Clear the conversation history when needed (doesnβt apply on server):
import { Micdrop } from '@micdrop/web'
// Clear all messagesMicdrop.conversation = []The conversation is automatically cleared when the call starts, not when it ends.