Skip to content

Sibilance Client

The Sibilance client is the core of the library. It manages voice survey sessions, survey state, and coordinates with the Sibilance platform.

Overview

The SibilanceClient class provides:

  • 🎤 Session Management - Start/stop voice survey sessions
  • 📊 Survey State - Track survey progress and collected information
  • 📝 YAML Output - Generate structured data from conversations
  • 🔄 Lifecycle Callbacks - Handle survey events (complete, pause, error)
  • 🔐 Secure - Domain validation and encrypted API key storage

Basic Setup

typescript
import { SibilanceClient } from '@sibilance.is/client';

const client = new SibilanceClient({
  surveyKey: 'sibilance_your_key_here',
}, {
  onComplete: (yaml) => {
    console.log('Survey completed!', yaml);
  }
});

Configuration

SibilanceConfig

The SibilanceClient constructor accepts a configuration object:

typescript
interface SibilanceConfig {
  // Production mode: Fetch survey from backend
  surveyKey?: string;
  
  // Editor/test mode: Use provided survey config directly
  survey?: SurveyConfig;
  
  // Optional: Custom backend URL
  backendUrl?: string;
  
  // Optional: Auto-start voice session
  autoStart?: boolean;
  
  // Optional: Additional AI instructions
  customInstructions?: string;
  
  // Optional: Voice configuration
  voiceConfig?: {
    provider?: 'vowel-prime' | 'gemini' | 'openai';
    model?: string;
    voiceId?: string;
    speakingRate?: number;
  };
}

Survey Configuration (Editor/Test Mode)

When using direct survey config (for testing):

typescript
const client = new SibilanceClient({
  survey: {
    markdown: `# My Survey\n## Start\nWelcome!`,
    steps: [
      { id: 'start', script: 'Welcome!' }
    ],
    mermaidDiagram: 'graph LR\n  Start[Start]',
    surveyName: 'Test Survey'
  }
});

Session Management

Starting a Session

typescript
// Start voice session
await client.connect();

Stopping a Session

typescript
// Stop voice session
await client.disconnect();

Toggling Session

typescript
// Toggle voice session
await client.toggleSession();

Checking Session State

typescript
// Get current voice state
const voiceState = client.getVoiceState();

console.log(voiceState.isConnected);      // Is session connected?
console.log(voiceState.isUserSpeaking);   // Is user speaking?
console.log(voiceState.isAISpeaking);     // Is AI speaking?
console.log(voiceState.isAIThinking);     // Is AI thinking?

Survey State

Getting Survey State

typescript
// Get current survey state
const surveyState = client.getSurveyState();

console.log(surveyState.isActive);           // Is survey active?
console.log(surveyState.currentStepId);      // Current step ID
console.log(surveyState.collectedInfo);      // Collected information
console.log(surveyState.conversationLog);    // Conversation history
console.log(surveyState.isComplete);         // Is survey complete?

Survey State Structure

typescript
interface SurveyState {
  isActive: boolean;
  currentStepId: string | null;
  collectedInfo: CollectedInformation[];
  conversationLog: ConversationLogEntry[];
  yamlData: any[];
  isComplete: boolean;
}

interface CollectedInformation {
  field: string;
  value: string;
  sourceStep?: string;
  timestamp: number;
}

interface ConversationLogEntry {
  speaker: 'ai' | 'user';
  message: string;
  timestamp: number;
}

Callbacks

onComplete

Called when survey is completed:

typescript
const client = new SibilanceClient({
  surveyKey: 'sibilance_your_key_here'
}, {
  onComplete: (yaml) => {
    // yaml is an array of collected information
    console.log('Survey completed!', yaml);
    
    // Submit to your backend
    fetch('/api/survey-results', {
      method: 'POST',
      body: JSON.stringify({ results: yaml })
    });
  }
});

onRecordInformation

Called when information is collected:

typescript
const client = new SibilanceClient({
  surveyKey: 'sibilance_your_key_here'
}, {
  onRecordInformation: (info) => {
    console.log('Recorded:', info.field, '=', info.value);
    // info: { field: string, value: string, sourceStep?: string, timestamp: number }
  }
});

onLogConversation

Called for each conversation entry:

typescript
const client = new SibilanceClient({
  surveyKey: 'sibilance_your_key_here'
}, {
  onLogConversation: (speaker, message) => {
    console.log(`${speaker}: ${message}`);
  }
});

onError

Called when an error occurs:

typescript
const client = new SibilanceClient({
  surveyKey: 'sibilance_your_key_here'
}, {
  onError: (error) => {
    console.error('Survey error:', error);
    // Handle error (show notification, retry, etc.)
  }
});

onStepChange

Called when survey step changes:

typescript
const client = new SibilanceClient({
  surveyKey: 'sibilance_your_key_here'
}, {
  onStepChange: (stepId) => {
    console.log('Moved to step:', stepId);
  }
});

Survey Methods

Complete Survey

Manually complete the survey:

typescript
client.complete();

Pause Survey

Pause the survey (mute mic/speaker but keep connection):

typescript
client.pause();

Resume Survey

Resume a paused survey:

typescript
client.resume();

Get YAML Output

Get collected information as YAML:

typescript
const yaml = client.getYAML();
console.log('Collected data:', yaml);

Get Survey Config

Get the current survey configuration:

typescript
const config = client.getSurveyConfig();
console.log('Survey name:', config?.surveyName);
console.log('Steps:', config?.steps);

State Subscription

Listen to voice state changes:

typescript
// Subscribe to voice state changes
const unsubscribe = client.onStateChange((state) => {
  console.log('Voice state changed:', state);
  
  if (state.isConnected) {
    console.log('Session connected');
  }
  
  if (state.isUserSpeaking) {
    console.log('User is speaking');
  }
  
  if (state.isAISpeaking) {
    console.log('AI is speaking');
  }
});

// Cleanup
unsubscribe();

Complete Example

typescript
import { SibilanceClient } from '@sibilance.is/client';

// Create client
const client = new SibilanceClient({
  surveyKey: 'sibilance_your_key_here',
  autoStart: false
}, {
  onComplete: async (yaml) => {
    console.log('Survey completed!', yaml);
    
    // Submit to backend
    await fetch('/api/survey-results', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ results: yaml })
    });
  },
  onRecordInformation: (info) => {
    console.log(`Recorded ${info.field}: ${info.value}`);
  },
  onError: (error) => {
    console.error('Error:', error);
  },
  onStepChange: (stepId) => {
    console.log('Step changed to:', stepId);
  }
});

// Subscribe to state changes
const unsubscribe = client.onStateChange((state) => {
  console.log('Connection:', state.isConnected);
  console.log('User speaking:', state.isUserSpeaking);
  console.log('AI speaking:', state.isAISpeaking);
});

// Start session
await client.connect();

// Later: cleanup
await client.disconnect();
unsubscribe();

Error Handling

Handle errors gracefully:

typescript
try {
  await client.connect();
} catch (error) {
  console.error('Failed to start session:', error);
  
  if (error.message.includes('microphone')) {
    alert('Please allow microphone access to use voice surveys.');
  } else if (error.message.includes('survey')) {
    alert('Survey not found. Please check your Survey Key.');
  } else {
    alert('Failed to start survey. Please try again.');
  }
}

// Or use onError callback
const client = new SibilanceClient({
  surveyKey: 'sibilance_your_key_here'
}, {
  onError: (error) => {
    console.error('Survey error:', error);
    // Handle error
  }
});

Cleanup

Always cleanup when done:

typescript
// Stop session
await client.disconnect();

// Unsubscribe from state changes
unsubscribe();

// Destroy client (if needed)
client.destroy();