Skip to content

Vowel Client

The Vowel client is the core of the library. It manages voice sessions, actions, state, and coordinates between navigation and automation adapters.

Overview

The Vowel class provides:

  • 🎤 Session Management - Start/stop voice sessions
  • 🔧 Action Registration - Define custom voice commands
  • 📢 Event Notifications - Programmatically trigger AI speech
  • 📊 State Management - Track connection, audio, and session state
  • 🧭 Navigation Integration - Voice-controlled routing
  • 🤖 Automation Integration - Voice-controlled page interaction

Basic Setup

typescript
import { Vowel, createDirectAdapters } from '@vowel.to/client';

// Create adapters
const { navigationAdapter, automationAdapter } = createDirectAdapters({
  navigate: (path) => router.push(path),
  routes: [
    { path: '/', description: 'Home page' },
    { path: '/products', description: 'Product catalog' },
    { path: '/cart', description: 'Shopping cart' }
  ],
  enableAutomation: true
});

// Initialize client
const vowel = new Vowel({
  appId: 'your-app-id',
  navigationAdapter,
  automationAdapter
});

Configuration

VowelClientConfig

The Vowel constructor accepts a configuration object:

typescript
interface VowelClientConfig {
  // Required: Your Vowel app ID from vowel.to
  appId: string;
  
  // Optional: Navigation adapter for voice routing
  navigationAdapter?: NavigationAdapter;
  
  // Optional: Automation adapter for page interaction
  automationAdapter?: AutomationAdapter;
  
  // Optional: Voice configuration
  voiceConfig?: VowelVoiceConfig;
  
  // Optional: System instruction override
  systemInstructionOverride?: string;
  
  // Optional: Initial context object (included when session starts)
  initialContext?: Record<string, unknown> | null;
  
  // Optional: Enable floating cursor for automation feedback
  enableFloatingCursor?: boolean;
}

Voice Configuration

Customize the AI voice:

typescript
const vowel = new Vowel({
  appId: 'your-app-id',
  voiceConfig: {
    name: 'Puck',  // Voice name (Gemini Live voices)
    language: 'en-US'
  }
});

System Instructions

Override the default system instructions:

typescript
const vowel = new Vowel({
  appId: 'your-app-id',
  systemInstructionOverride: `
    You are a helpful shopping assistant.
    Always be friendly and concise.
    Help users find products and complete purchases.
  `
});

Dynamic Context

Update the AI's context dynamically during a session. Context is stringified and appended to the system prompt, allowing the AI to stay aware of current application state.

Initial Context

Set initial context when creating the client. This context is automatically included when the session starts:

typescript
const vowel = new Vowel({
  appId: 'your-app-id',
  initialContext: {
    page: 'product',
    productId: 'iphone-15-pro',
    price: 999.99,
    inStock: true
  }
});

// When you start the session, the context is automatically included
await vowel.startSession();

Updating Context

Update context dynamically during a session:

typescript
// Update context with current page info
vowel.updateContext({
  page: 'product',
  productId: 'iphone-15-pro',
  price: 999.99,
  inStock: true
});

// Update with multiple details
vowel.updateContext({
  page: 'checkout',
  cartTotal: 199.99,
  itemCount: 2,
  shippingAddress: '123 Main St'
});

// Clear context
vowel.updateContext(null);

When context changes, a session.update event is automatically sent to update the system prompt. The context object is stringified with JSON.stringify() and wrapped in <context> tags before being appended to the base system instructions.

With React:

tsx
import { useVowel } from '@vowel.to/client/react';

function ProductPage({ product }) {
  const { updateContext } = useVowel();
  
  useEffect(() => {
    // Update context when product changes
    updateContext({
      page: 'product',
      productId: product.id,
      name: product.name,
      price: product.price
    });
    
    // Clear context when component unmounts
    return () => updateContext(null);
  }, [product, updateContext]);
  
  return <div>{product.name}</div>;
}

See Dynamic Context Recipe for detailed patterns and examples.

Session Management

Starting a Session

typescript
// Start voice session
await vowel.startSession();

Stopping a Session

typescript
// Stop voice session
await vowel.stopSession();

Checking Session State

typescript
// Get current state
const state = vowel.state;

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

State Helpers

typescript
// Convenience methods
if (vowel.isUserSpeaking()) {
  console.log('User is speaking');
}

if (vowel.isAISpeaking()) {
  console.log('AI is speaking');
}

if (vowel.isAIThinking()) {
  console.log('AI is thinking');
}

Action Registration

Register custom voice commands:

typescript
vowel.registerAction('addToCart', {
  description: 'Add product to shopping cart',
  parameters: {
    productId: {
      type: 'string',
      description: 'Product ID'
    },
    quantity: {
      type: 'number',
      description: 'Quantity to add',
      default: 1
    }
  }
}, async ({ productId, quantity }) => {
  // Your business logic
  await addProductToCart(productId, quantity);
  
  return {
    success: true,
    message: `Added ${quantity} item(s) to cart`
  };
});

See Actions Guide for detailed information.

Event Notifications

Programmatically trigger AI speech:

typescript
// Simple notification
await vowel.notifyEvent('Order placed successfully!');

// With context
await vowel.notifyEvent('New message received', {
  from: 'John Doe',
  preview: 'Hey, are you available?'
});

See Event Notifications Guide for detailed patterns.

State Subscription

Listen to state changes:

typescript
// Subscribe to state changes
const unsubscribe = vowel.onStateChange((state) => {
  console.log('State changed:', state);
  
  if (state.isConnected) {
    console.log('Session connected');
  }
  
  if (state.error) {
    console.error('Error:', state.error);
  }
});

// Cleanup
unsubscribe();

Adapters

Provides voice-controlled routing:

typescript
const vowel = new Vowel({
  appId: 'your-app-id',
  navigationAdapter: new DirectNavigationAdapter({
    navigate: (path) => router.push(path),
    getCurrentPath: () => window.location.pathname,
    routes: [/* your routes */]
  })
});

When provided, automatically registers the navigate_to_page action.

Automation Adapter

Provides voice-controlled page interaction:

typescript
const vowel = new Vowel({
  appId: 'your-app-id',
  automationAdapter: new DirectAutomationAdapter()
});

When provided, automatically registers these actions:

  • search_page_elements
  • get_page_snapshot
  • click_element
  • type_into_element
  • focus_element
  • scroll_to_element
  • press_key

See Adapters Guide for detailed information.

Floating Cursor

Enable visual feedback for automation:

typescript
const vowel = new Vowel({
  appId: 'your-app-id',
  automationAdapter: new DirectAutomationAdapter(),
  enableFloatingCursor: true
});

// Access cursor manager
const cursor = vowel.floatingCursor;
if (cursor) {
  cursor.show({ x: 100, y: 100 });
  cursor.hide();
}

Error Handling

Handle errors gracefully:

typescript
try {
  await vowel.startSession();
} catch (error) {
  console.error('Failed to start session:', error);
  // Show user-friendly error message
}

// Or subscribe to state errors
vowel.onStateChange((state) => {
  if (state.error) {
    console.error('Session error:', state.error);
    // Handle error
  }
});

Cleanup

Always cleanup when done:

typescript
// Stop session
await vowel.stopSession();

// Unsubscribe from state changes
unsubscribe();

// Clear automation store (if using controlled adapters)
automationAdapter.clearStore?.();

Complete Example

typescript
import { Vowel, createDirectAdapters } from '@vowel.to/client';
import { useRouter } from 'next/navigation';

// Setup
const router = useRouter();
const { navigationAdapter, automationAdapter } = createDirectAdapters({
  navigate: (path) => router.push(path),
  routes: [
    { path: '/', description: 'Home page' },
    { path: '/products', description: 'Browse products' },
    { path: '/cart', description: 'Shopping cart' }
  ],
  enableAutomation: true
});

// Initialize client
const vowel = new Vowel({
  appId: 'your-app-id',
  navigationAdapter,
  automationAdapter,
  voiceConfig: {
    name: 'Puck',
    language: 'en-US'
  },
  enableFloatingCursor: true
});

// Register custom actions
vowel.registerAction('searchProducts', {
  description: 'Search for products',
  parameters: {
    query: { type: 'string', description: 'Search query' }
  }
}, async ({ query }) => {
  const results = await searchProducts(query);
  return { success: true, results };
});

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

// Start session
await vowel.startSession();

// Notify events
await vowel.notifyEvent('Welcome to our store!');

// Later: cleanup
await vowel.stopSession();
unsubscribe();