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();