atoms-client-sdk
v1.1.0
Published
A TypeScript/JavaScript SDK for real-time voice and text communication with AI agents using WebRTC technology. Built on Atoms' proprietary real-time communication infrastructure for robust, low-latency audio streaming and real-time data exchange.
Readme
Atoms Client SDK
A TypeScript/JavaScript SDK for real-time voice and text communication with AI agents using WebRTC technology. Built on Atoms' proprietary real-time communication infrastructure for robust, low-latency audio streaming and real-time data exchange.
Table of Contents
- Installation
- Quick Start
- API Reference
- Events
- Configuration
- Error Handling
- Examples
- Browser Support
- Troubleshooting
Installation
npm install atoms-client-sdkQuick Start
Voice Mode
import { AtomsClient } from "atoms-client-sdk";
const client = new AtomsClient();
// Listen for events
client.on("session_started", ({ mode }) => {
console.log(`Session started in ${mode} mode`);
});
client.on("transcript", (data) => {
console.log("Agent said:", data.text);
});
client.on("agent_start_talking", () => {
console.log("Agent started talking");
});
client.on("agent_stop_talking", () => {
console.log("Agent stopped talking");
});
// Start voice session
await client.startSession({
accessToken: "your-access-token",
mode: "voice",
host: "wss://your-atoms-host.com",
sampleRate: 48000,
});
// Don't forget to call startAudioPlayback() after user interaction
await client.startAudioPlayback();Text Mode
import { AtomsClient } from "atoms-client-sdk";
const client = new AtomsClient();
// Listen for transcript responses
client.on("transcript", (data) => {
console.log("Agent response:", data.text);
});
// Start text-only session
await client.startSession({
accessToken: "your-access-token",
mode: "text",
host: "wss://your-atoms-host.com",
});
// Send text messages
client.sendTextMessage("Hello, how can you help me today?");API Reference
Class: AtomsClient
The main client class that extends EventEmitter for real-time communication with AI agents.
Constructor
new AtomsClient();Creates a new instance of the AtomsClient.
Methods
startSession(config: StartSessionConfig): Promise<void>
Initializes and starts a communication session with the AI agent.
Parameters:
config- Session configuration object
Throws:
Error- If mode is not specified, audio context is not supported, or connection fails
Example:
await client.startSession({
accessToken: "jwt-token",
mode: "voice",
host: "wss://your-host.com",
sampleRate: 48000,
captureDeviceId: "default",
playbackDeviceId: "default",
});startAudioPlayback(): Promise<void>
Starts audio playback. Must be called after user interaction due to browser autoplay policies.
Example:
// Call this inside a click/tap event handler
button.addEventListener("click", async () => {
await client.startAudioPlayback();
});stopSession(): void
Stops the current session and cleans up all resources.
Example:
client.stopSession();mute(): void
Mutes the microphone (voice mode only).
Example:
client.mute();unmute(): void
Unmutes the microphone (voice mode only).
Example:
client.unmute();sendTextMessage(message: string): void
Sends a text message to the agent (text mode only).
Parameters:
message- The text message to send
Example:
client.sendTextMessage("What is the weather like today?");checkMicrophonePermissions(): Promise<{granted: boolean, error?: string}>
Manually checks microphone permissions. This is called automatically during startSession() for voice mode.
Returns:
granted- Whether microphone access is grantederror- Error message if permission check failed
Example:
const { granted, error } = await client.checkMicrophonePermissions();
if (!granted) {
console.error("Microphone permission required:", error);
}Properties
isConnected: boolean (read-only)
Returns whether the client is currently connected to the session.
currentMode: "voice" | "text" | undefined (read-only)
Returns the current session mode.
roomName: string | undefined (read-only)
Returns the session room name if connected.
isAgentTalking: boolean (read-only)
Returns whether the AI agent is currently speaking.
audioContext: AudioContext | undefined (read-only)
Returns the Web Audio API context (voice mode only).
sampleRate: number | undefined (read-only)
Returns the audio sample rate being used.
Interface: StartSessionConfig
Configuration object for starting a session.
interface StartSessionConfig {
accessToken: string; // JWT token for authentication
mode: "voice" | "text"; // Communication mode
host?: string; // Atoms server URL
sampleRate?: number; // Audio sample rate (default: 48000)
captureDeviceId?: string; // Microphone device ID
playbackDeviceId?: string; // Speaker device ID
emitRawAudioSamples?: boolean; // Whether to emit raw audio data
}Properties
accessToken(required): JWT token for authenticating with the Atoms servermode(required): Either"voice"for audio communication or"text"for text-onlyhost(optional): WebSocket URL of the Atoms serversampleRate(optional): Audio sample rate in Hz (default: 48000)captureDeviceId(optional): Device ID for microphone inputplaybackDeviceId(optional): Device ID for audio outputemitRawAudioSamples(optional): Enable raw audio data events
Events
The AtomsClient emits various events that you can listen to:
Session Events
session_started
Emitted when the session successfully starts.
client.on("session_started", (data: { mode: string }) => {
console.log(`Session started in ${data.mode} mode`);
});session_ended
Emitted when the session ends.
client.on("session_ended", () => {
console.log("Session ended");
});error
Emitted when an error occurs.
client.on("error", (error: string) => {
console.error("Session error:", error);
});Permission Events
microphone_permission_granted
Emitted when microphone permission is successfully granted.
client.on("microphone_permission_granted", () => {
console.log("Microphone access granted");
});microphone_permission_error
Emitted when there's an issue with microphone permissions.
client.on(
"microphone_permission_error",
(data: { error: string; canRetry: boolean }) => {
console.error("Microphone permission error:", data.error);
if (data.canRetry) {
// Show retry button to user
}
},
);microphone_access_failed
Emitted when microphone access fails during session start.
client.on(
"microphone_access_failed",
(data: { error: string; name: string; constraint?: string }) => {
console.error("Microphone access failed:", data);
},
);Agent Events
agent_connected
Emitted when an AI agent connects to the session.
client.on("agent_connected", (participant) => {
console.log("Agent connected:", participant.identity);
});agent_start_talking
Emitted when the AI agent starts speaking.
client.on("agent_start_talking", () => {
console.log("Agent is now speaking");
// Update UI to show agent is talking
});agent_stop_talking
Emitted when the AI agent stops speaking.
client.on("agent_stop_talking", () => {
console.log("Agent stopped speaking");
// Update UI to show agent is not talking
});Communication Events
transcript
Emitted when a transcript message is received from the agent.
client.on(
"transcript",
(data: { type: string; topic: string; text: string; timestamp: number }) => {
console.log("Agent said:", data.text);
// Display message in chat UI
},
);update
Emitted when the agent sends an update event.
client.on("update", (event) => {
console.log("Agent update:", event);
});metadata
Emitted when the agent sends metadata.
client.on("metadata", (event) => {
console.log("Agent metadata:", event);
});Audio Events
audio_track_subscribed
Emitted when an audio track is subscribed (voice mode only).
client.on(
"audio_track_subscribed",
(data: { track: any; participant: any }) => {
console.log("Audio track subscribed");
},
);audio
Emitted when raw audio samples are available (requires emitRawAudioSamples: true).
client.on("audio", (audioData: Float32Array) => {
// Process raw audio samples
console.log("Raw audio samples:", audioData);
});Configuration
Audio Configuration
For voice mode, you can configure various audio parameters:
await client.startSession({
accessToken: "your-token",
mode: "voice",
host: "wss://your-host.com",
sampleRate: 48000, // Higher quality audio
captureDeviceId: "specific-mic-id", // Use specific microphone
playbackDeviceId: "specific-speaker-id", // Use specific speaker
emitRawAudioSamples: true, // Enable raw audio events
});Device Selection
To get available audio devices:
// Get available devices
const devices = await navigator.mediaDevices.enumerateDevices();
const audioInputs = devices.filter((device) => device.kind === "audioinput");
const audioOutputs = devices.filter((device) => device.kind === "audiooutput");
// Use specific device
await client.startSession({
accessToken: "your-token",
mode: "voice",
host: "wss://your-host.com",
captureDeviceId: audioInputs[0].deviceId,
playbackDeviceId: audioOutputs[0].deviceId,
});Error Handling
Common Errors and Solutions
Microphone Permission Denied
client.on("microphone_permission_error", (data) => {
if (data.error.includes("denied")) {
// Show instructions to enable microphone in browser settings
showMicrophoneInstructions();
}
});Audio Context Issues
client.on("error", (error) => {
if (error.includes("AudioContext")) {
// Try starting audio playback after user interaction
showClickToEnableAudioButton();
}
});Connection Failures
client.on("error", (error) => {
console.error("Connection error:", error);
// Implement retry logic
setTimeout(() => {
client.startSession(config);
}, 5000);
});Best Practices
- Always handle microphone permissions gracefully
- Call
startAudioPlayback()after user interaction - Implement proper cleanup with
stopSession() - Handle network disconnections with retry logic
- Provide user feedback for all states
Examples
Complete Voice Chat Application
import { AtomsClient } from "atoms-client-sdk";
class VoiceChatApp {
private client: AtomsClient;
private isSessionActive = false;
constructor() {
this.client = new AtomsClient();
this.setupEventListeners();
}
private setupEventListeners() {
// Session events
this.client.on("session_started", () => {
this.isSessionActive = true;
this.updateUI("Connected");
});
this.client.on("session_ended", () => {
this.isSessionActive = false;
this.updateUI("Disconnected");
});
// Agent events
this.client.on("agent_start_talking", () => {
this.showAgentSpeaking(true);
});
this.client.on("agent_stop_talking", () => {
this.showAgentSpeaking(false);
});
this.client.on("transcript", (data) => {
this.addMessageToChat("agent", data.text);
});
// Error handling
this.client.on("error", (error) => {
console.error("Client error:", error);
this.showError(error);
});
this.client.on("microphone_permission_error", (data) => {
this.showMicrophoneError(data.error);
});
}
async startSession(accessToken: string, host: string) {
try {
await this.client.startSession({
accessToken,
mode: "voice",
host,
sampleRate: 48000,
});
} catch (error) {
console.error("Failed to start session:", error);
this.showError("Failed to connect");
}
}
async enableAudio() {
try {
await this.client.startAudioPlayback();
this.updateUI("Audio enabled");
} catch (error) {
console.error("Failed to enable audio:", error);
}
}
stopSession() {
this.client.stopSession();
}
toggleMute() {
if (this.client.isConnected) {
// Implement mute state tracking
this.client.mute(); // or unmute()
}
}
private updateUI(status: string) {
// Update your UI with connection status
}
private showAgentSpeaking(speaking: boolean) {
// Show visual indicator when agent is talking
}
private addMessageToChat(sender: "user" | "agent", message: string) {
// Add message to chat interface
}
private showError(error: string) {
// Show error message to user
}
private showMicrophoneError(error: string) {
// Show specific microphone error message
}
}
// Usage
const app = new VoiceChatApp();
// Start session when user clicks connect
document.getElementById("connect-btn")?.addEventListener("click", async () => {
await app.startSession("your-jwt-token", "wss://your-atoms-host.com");
});
// Enable audio when user clicks (required for autoplay policy)
document
.getElementById("enable-audio-btn")
?.addEventListener("click", async () => {
await app.enableAudio();
});Text-Only Chat Application
import { AtomsClient } from "atoms-client-sdk";
class TextChatApp {
private client: AtomsClient;
constructor() {
this.client = new AtomsClient();
this.setupEventListeners();
}
private setupEventListeners() {
this.client.on("session_started", () => {
console.log("Text chat session started");
});
this.client.on("transcript", (data) => {
this.displayMessage("agent", data.text);
});
this.client.on("error", (error) => {
console.error("Chat error:", error);
});
}
async startTextSession(accessToken: string, host: string) {
await this.client.startSession({
accessToken,
mode: "text",
host,
});
}
sendMessage(message: string) {
this.client.sendTextMessage(message);
this.displayMessage("user", message);
}
private displayMessage(sender: "user" | "agent", message: string) {
// Display message in chat UI
const chatContainer = document.getElementById("chat-messages");
const messageElement = document.createElement("div");
messageElement.className = `message ${sender}`;
messageElement.textContent = message;
chatContainer?.appendChild(messageElement);
}
}React Integration Example
import React, { useEffect, useRef, useState } from 'react';
import { AtomsClient } from 'atoms-client-sdk';
interface Message {
sender: 'user' | 'agent';
text: string;
timestamp: number;
}
const VoiceChat: React.FC = () => {
const [client] = useState(() => new AtomsClient());
const [isConnected, setIsConnected] = useState(false);
const [isAgentTalking, setIsAgentTalking] = useState(false);
const [messages, setMessages] = useState<Message[]>([]);
const [isMuted, setIsMuted] = useState(false);
useEffect(() => {
// Setup event listeners
client.on('session_started', () => {
setIsConnected(true);
});
client.on('session_ended', () => {
setIsConnected(false);
});
client.on('agent_start_talking', () => {
setIsAgentTalking(true);
});
client.on('agent_stop_talking', () => {
setIsAgentTalking(false);
});
client.on('transcript', (data) => {
setMessages(prev => [...prev, {
sender: 'agent',
text: data.text,
timestamp: Date.now()
}]);
});
// Cleanup
return () => {
client.stopSession();
};
}, [client]);
const startSession = async () => {
try {
await client.startSession({
accessToken: process.env.REACT_APP_ATOMS_TOKEN!,
mode: 'voice',
host: process.env.REACT_APP_ATOMS_HOST!,
});
await client.startAudioPlayback();
} catch (error) {
console.error('Failed to start session:', error);
}
};
const toggleMute = () => {
if (isMuted) {
client.unmute();
} else {
client.mute();
}
setIsMuted(!isMuted);
};
return (
<div className="voice-chat">
<div className="chat-messages">
{messages.map((message, index) => (
<div key={index} className={`message ${message.sender}`}>
{message.text}
</div>
))}
</div>
<div className="controls">
{!isConnected ? (
<button onClick={startSession}>Start Voice Chat</button>
) : (
<>
<button onClick={toggleMute}>
{isMuted ? 'Unmute' : 'Mute'}
</button>
<button onClick={() => client.stopSession()}>
End Session
</button>
</>
)}
</div>
{isAgentTalking && (
<div className="agent-indicator">Agent is speaking...</div>
)}
</div>
);
};
export default VoiceChat;Browser Support
Minimum Requirements
- Chrome/Edge: 66+
- Firefox: 60+
- Safari: 11.1+
- Mobile Safari: 11.3+
- Chrome Android: 66+
Required APIs
- WebRTC (RTCPeerConnection)
- Web Audio API (for voice mode)
- WebSockets
- MediaDevices API (for microphone access)
Feature Detection
function checkBrowserSupport() {
const hasWebRTC = "RTCPeerConnection" in window;
const hasWebAudio =
"AudioContext" in window || "webkitAudioContext" in window;
const hasMediaDevices =
navigator.mediaDevices && navigator.mediaDevices.getUserMedia;
return {
webRTC: hasWebRTC,
webAudio: hasWebAudio,
mediaDevices: hasMediaDevices,
supported: hasWebRTC && hasWebAudio && hasMediaDevices,
};
}Troubleshooting
Common Issues
1. "AudioContext not supported"
Cause: Browser doesn't support Web Audio API Solution: Check browser compatibility or fallback to text mode
2. "Microphone access required for voice mode"
Cause: User denied microphone permissions Solutions:
- Guide user to enable microphone in browser settings
- Fallback to text mode
- Implement retry mechanism
3. Audio not playing
Cause: Browser autoplay policy
Solution: Always call startAudioPlayback() after user interaction
4. Connection timeouts
Cause: Network issues or invalid credentials Solutions:
- Verify access token is valid and not expired
- Check network connectivity
- Implement retry logic with exponential backoff
5. Poor audio quality
Solutions:
- Increase sample rate (up to 48000)
- Enable audio processing features
- Check for background noise cancellation
Debug Mode
Enable console logging to debug issues:
// The SDK automatically logs important events to console
// Check browser developer tools for detailed logsPerformance Optimization
- Use appropriate sample rates: 16000 for speech, 48000 for high quality
- Clean up resources: Always call
stopSession()when done - Handle connection states: Implement proper state management
- Optimize for mobile: Consider battery usage and data consumption
Getting Help
If you encounter issues:
- Check the browser console for error messages
- Verify your access token and host configuration
- Test with different browsers
- Check network connectivity
- Ensure microphone permissions are granted
For additional support, contact the Atoms team or visit our documentation portal.
License
This SDK is part of the Atoms platform by Smallest AI. Please refer to your license agreement for usage terms.
© 2024 Smallest AI. All rights reserved.
