Skip to content

Dynamic Context ​

Learn how to dynamically update the AI's context during a voice session to keep it aware of current application state.

Overview ​

Dynamic context allows you to update the AI's understanding of the current application state in real-time. The context object is stringified and appended to the system prompt, enabling the AI to provide more relevant and contextual responses.

Key Features:

  • Set initial context when creating the client
  • Update context anytime during a session
  • Context is automatically sent via session.update when changed
  • Context persists across session reconnections
  • Works seamlessly with React hooks

Basic Usage ​

Initial Context ​

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

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

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

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

Updating Context ​

Update context dynamically during a session:

typescript
import { vowel } from './vowel.client';

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

// Get current context
const context = vowel.getContext();
console.log(context); // { page: 'product', productId: 'iphone-15-pro', price: 999.99 }

// Clear context
vowel.updateContext(null);

How It Works ​

Initial Context ​

When you provide initialContext in the config:

  1. Context is stored in the client during construction
  2. When startSession() is called, the context is automatically included in the initial system instructions
  3. The context is stringified and wrapped in <context> tags
  4. It's sent to the AI as part of the initial connection

Dynamic Updates ​

When you call updateContext(), the context object is:

  1. Stored in the client
  2. Stringified using JSON.stringify(context, null, 2) (pretty-printed)
  3. Wrapped in <context> tags
  4. Appended to the base system instructions
  5. Sent to the AI via session.update (if session is active)

The AI receives the context in this format:

<context>
{
  "page": "product",
  "productId": "iphone-15-pro",
  "price": 999.99
}
</context>

Common Patterns ​

Page-Based Context ​

Update context when navigating between pages:

typescript
// Product page
function showProductPage(productId: string) {
  const product = getProduct(productId);
  
  vowel.updateContext({
    page: 'product',
    productId: product.id,
    name: product.name,
    price: product.price,
    inStock: product.inStock,
    description: product.description
  });
}

// Checkout page
function showCheckoutPage(cart: Cart) {
  vowel.updateContext({
    page: 'checkout',
    cartTotal: cart.getTotal(),
    itemCount: cart.items.length,
    shippingAddress: cart.shippingAddress,
    paymentMethod: cart.paymentMethod
  });
}

// Home page
function showHomePage() {
  vowel.updateContext(null); // Clear context
}

User State Context ​

Keep the AI aware of user information:

typescript
async function loginUser(user: User) {
  await authenticate(user);
  
  vowel.updateContext({
    user: {
      id: user.id,
      name: user.name,
      email: user.email,
      role: user.role,
      preferences: user.preferences
    }
  });
}

async function logoutUser() {
  await signOut();
  vowel.updateContext(null); // Clear user context
}

Shopping Cart Context ​

Update context as cart changes:

typescript
class CartManager {
  private cart: Cart;
  
  addItem(product: Product, quantity: number) {
    this.cart.add(product, quantity);
    this.updateContext();
  }
  
  removeItem(productId: string) {
    this.cart.remove(productId);
    this.updateContext();
  }
  
  updateQuantity(productId: string, quantity: number) {
    this.cart.updateQuantity(productId, quantity);
    this.updateContext();
  }
  
  private updateContext() {
    vowel.updateContext({
      cart: {
        items: this.cart.items.map(item => ({
          productId: item.productId,
          name: item.name,
          quantity: item.quantity,
          price: item.price
        })),
        total: this.cart.getTotal(),
        itemCount: this.cart.items.length
      }
    });
  }
}

Form Context ​

Provide form state to the AI:

typescript
function handleFormChange(formData: FormData) {
  vowel.updateContext({
    form: {
      type: 'contact',
      fields: {
        name: formData.name,
        email: formData.email,
        message: formData.message
      },
      isValid: validateForm(formData),
      progress: calculateProgress(formData)
    }
  });
}

function handleFormSubmit() {
  // Clear form context after submission
  vowel.updateContext(null);
}

Search Context ​

Keep AI aware of search state:

typescript
function performSearch(query: string, filters: SearchFilters) {
  const results = search(query, filters);
  
  vowel.updateContext({
    search: {
      query,
      filters,
      resultCount: results.length,
      results: results.slice(0, 5).map(r => ({
        id: r.id,
        title: r.title,
        type: r.type
      }))
    }
  });
}

function clearSearch() {
  vowel.updateContext(null);
}

React Integration ​

The useSyncContext hook automatically syncs context with the Vowel client, handling updates and cleanup for you:

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

function ProductPage({ productId }: { productId: string }) {
  const product = useProduct(productId);
  
  // Automatically sync product context - updates on change, clears on unmount
  useSyncContext(
    product ? {
      page: 'product',
      productId: product.id,
      name: product.name,
      price: product.price,
      inStock: product.inStock
    } : null
  );
  
  return <div>{product?.name}</div>;
}

Benefits:

  • ✅ Automatic updates when context changes
  • ✅ Automatic cleanup on unmount
  • ✅ Deep comparison to avoid unnecessary updates
  • ✅ Simple, declarative API

Using the useVowel Hook (Manual) ​

For more control, you can manually manage context updates:

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

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

Custom Hook for Context Management ​

If you need custom logic, you can create your own hook:

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

function usePageContext(context: Record<string, unknown> | null) {
  const { updateContext } = useVowel();
  const previousContext = useRef<Record<string, unknown> | null>(null);
  
  useEffect(() => {
    // Only update if context actually changed
    if (JSON.stringify(context) !== JSON.stringify(previousContext.current)) {
      updateContext(context);
      previousContext.current = context;
    }
    
    return () => {
      updateContext(null);
    };
  }, [context, updateContext]);
}

// Usage
function ProductPage({ product }) {
  usePageContext({
    page: 'product',
    productId: product.id,
    name: product.name
  });
  
  return <div>{product.name}</div>;
}

Note: The built-in useSyncContext hook provides the same functionality with better performance and less boilerplate. Use custom hooks only when you need additional logic beyond basic context syncing.

Context Provider Pattern ​

Create a context provider that manages application-wide context:

tsx
import { createContext, useContext, useEffect, useState } from 'react';
import { useVowel } from '@vowel.to/client/react';

interface AppContext {
  user: User | null;
  currentPage: string;
  cart: Cart | null;
}

const AppContextContext = createContext<AppContext | null>(null);

export function AppContextProvider({ children }: { children: React.ReactNode }) {
  const { updateContext } = useVowel();
  const [appContext, setAppContext] = useState<AppContext>({
    user: null,
    currentPage: 'home',
    cart: null
  });
  
  // Sync app context to Vowel context
  useEffect(() => {
    const vowelContext: Record<string, unknown> = {};
    
    if (appContext.user) {
      vowelContext.user = {
        id: appContext.user.id,
        name: appContext.user.name,
        role: appContext.user.role
      };
    }
    
    if (appContext.currentPage) {
      vowelContext.page = appContext.currentPage;
    }
    
    if (appContext.cart) {
      vowelContext.cart = {
        itemCount: appContext.cart.items.length,
        total: appContext.cart.getTotal()
      };
    }
    
    updateContext(Object.keys(vowelContext).length > 0 ? vowelContext : null);
  }, [appContext, updateContext]);
  
  return (
    <AppContextContext.Provider value={appContext}>
      {children}
    </AppContextContext.Provider>
  );
}

export function useAppContext() {
  const context = useContext(AppContextContext);
  if (!context) {
    throw new Error('useAppContext must be used within AppContextProvider');
  }
  return context;
}

Advanced Patterns ​

Debounced Context Updates ​

Prevent excessive context updates:

typescript
import { debounce } from 'lodash';

const updateContextDebounced = debounce((context: Record<string, unknown> | null) => {
  vowel.updateContext(context);
}, 300);

// Usage
function handleCartChange(cart: Cart) {
  updateContextDebounced({
    cart: {
      itemCount: cart.items.length,
      total: cart.getTotal()
    }
  });
}

Context Merging ​

Merge new context with existing context:

typescript
function mergeContext(updates: Record<string, unknown>) {
  const current = vowel.getContext();
  const merged = {
    ...(current || {}),
    ...updates
  };
  vowel.updateContext(merged);
}

// Usage
mergeContext({ page: 'product' }); // Adds/updates page
mergeContext({ productId: '123' }); // Adds productId, keeps page

Context History ​

Track context changes for debugging:

typescript
class ContextManager {
  private history: Array<{ timestamp: number; context: Record<string, unknown> | null }> = [];
  
  updateContext(context: Record<string, unknown> | null) {
    this.history.push({
      timestamp: Date.now(),
      context: context ? { ...context } : null
    });
    
    // Keep only last 50 entries
    if (this.history.length > 50) {
      this.history.shift();
    }
    
    vowel.updateContext(context);
  }
  
  getHistory() {
    return this.history;
  }
  
  rollback(steps: number = 1) {
    if (this.history.length > steps) {
      const targetIndex = this.history.length - steps - 1;
      const targetContext = this.history[targetIndex].context;
      this.history = this.history.slice(0, targetIndex + 1);
      vowel.updateContext(targetContext);
    }
  }
}

const contextManager = new ContextManager();

Conditional Context Updates ​

Only update context if session is active:

typescript
function safeUpdateContext(context: Record<string, unknown> | null) {
  if (vowel.state.isConnected) {
    vowel.updateContext(context);
  } else {
    console.log('Context will be applied on next session start:', context);
    // Context is still stored and will be applied when session starts
    vowel.updateContext(context);
  }
}

Best Practices ​

  1. Keep context concise - Only include relevant information the AI needs
  2. Update on meaningful changes - Don't update on every keystroke or mouse move
  3. Clear when appropriate - Set context to null when leaving pages or resetting state
  4. Use structured data - Objects with clear keys are easier for the AI to understand
  5. Avoid sensitive data - Don't include passwords, tokens, or other sensitive information
  6. Debounce frequent updates - Use debouncing for rapidly changing data (like form inputs)
  7. Check session state - Context updates work even when disconnected (will apply on next connection)

Common Use Cases ​

E-commerce ​

  • Product page context (product details, price, availability)
  • Cart context (items, totals, shipping info)
  • Checkout context (payment method, shipping address)

Dashboards ​

  • Current view/filter context
  • Selected data context
  • User preferences context

Forms ​

  • Form state and validation
  • Progress tracking
  • Field values
  • Current page/route
  • Navigation history
  • Breadcrumbs

Error Handling ​

typescript
function safeUpdateContext(context: Record<string, unknown> | null) {
  try {
    vowel.updateContext(context);
  } catch (error) {
    console.error('Failed to update context:', error);
    // Context update failures are non-critical
    // The session will continue to work
  }
}