Skip to main content

Overview

The useMobileVoiceAgent hook provides a complete voice agent integration for React Native apps. It handles WebRTC transport, backend communication, function execution, and real-time event processing.

Usage

import { useMobileVoiceAgent } from '@navai/voice-mobile';

function VoiceScreen({ runtime, runtimeLoading, runtimeError, navigation }) {
  const { status, error, isConnecting, isConnected, start, stop } = useMobileVoiceAgent({
    runtime,
    runtimeLoading,
    runtimeError,
    navigate: (path) => navigation.navigate(path)
  });

  return (
    <View>
      <Text>Status: {status}</Text>
      {error && <Text>Error: {error}</Text>}
      <Button title="Start" onPress={start} disabled={isConnecting || isConnected} />
      <Button title="Stop" onPress={stop} disabled={!isConnected} />
    </View>
  );
}

Options

runtime
ResolveNavaiMobileApplicationRuntimeConfigResult | null
required
Runtime configuration object containing app environment, routes, and function module loaders. Obtain from resolveNavaiMobileApplicationRuntimeConfig.
runtimeLoading
boolean
required
Indicates whether the runtime configuration is still loading. Set to true during async runtime resolution.
runtimeError
string | null
required
Error message if runtime configuration failed to load. Set to error message string or null if no error.
navigate
(path: string) => void
required
Navigation callback function invoked when the agent calls the navigate_to tool. Receives the target path as parameter.
navigate: (path) => navigation.navigate(path)

Return Value

Returns UseMobileVoiceAgentResult object with the following properties:
status
'idle' | 'connecting' | 'connected' | 'error'
Current session status:
  • idle: Not connected
  • connecting: Connection in progress
  • connected: Active voice session
  • error: Connection failed or error occurred
error
string | null
Error message if status is error, otherwise null.
isConnecting
boolean
Convenience flag: true when status is connecting.
isConnected
boolean
Convenience flag: true when status is connected.
start
() => Promise<void>
Async function to start the voice session. Handles:
  • Microphone permission request (Android)
  • WebRTC connection initialization
  • Backend function loading
  • Agent runtime creation
  • Real-time event loop setup
Throws error if connection fails.
stop
() => Promise<void>
Async function to stop the voice session and clean up resources. Safe to call multiple times.

Examples

Basic Integration

import { useMobileVoiceAgent } from '@navai/voice-mobile';
import { useNavigation } from '@react-navigation/native';

function VoiceAssistant({ runtime, runtimeLoading, runtimeError }) {
  const navigation = useNavigation();
  const { status, error, start, stop } = useMobileVoiceAgent({
    runtime,
    runtimeLoading,
    runtimeError,
    navigate: (path) => navigation.navigate(path)
  });

  return (
    <View>
      {status === 'connected' && <Text>🎤 Listening...</Text>}
      {error && <Text style={{ color: 'red' }}>{error}</Text>}
      <Button title="Start Voice" onPress={start} />
      <Button title="Stop" onPress={stop} />
    </View>
  );
}

With Status Indicators

function VoiceControl({ runtime, runtimeLoading, runtimeError, navigation }) {
  const { status, error, isConnecting, isConnected, start, stop } = useMobileVoiceAgent({
    runtime,
    runtimeLoading,
    runtimeError,
    navigate: (path) => navigation.navigate(path)
  });

  const statusColor = {
    idle: 'gray',
    connecting: 'yellow',
    connected: 'green',
    error: 'red'
  }[status];

  return (
    <View>
      <View style={{ backgroundColor: statusColor, padding: 10 }}>
        <Text>{status.toUpperCase()}</Text>
      </View>
      
      {error && <Text style={{ color: 'red' }}>{error}</Text>}
      
      <Button 
        title={isConnected ? "Stop Voice" : "Start Voice"}
        onPress={isConnected ? stop : start}
        disabled={isConnecting}
      />
    </View>
  );
}

Custom Navigation Handler

function App() {
  const navigation = useNavigation();
  const [navigationHistory, setNavigationHistory] = useState([]);
  
  const { start, stop } = useMobileVoiceAgent({
    runtime,
    runtimeLoading,
    runtimeError,
    navigate: (path) => {
      // Log navigation
      setNavigationHistory(prev => [...prev, { path, timestamp: Date.now() }]);
      
      // Custom navigation logic
      if (path.startsWith('/settings')) {
        navigation.navigate('Settings', { screen: path.replace('/settings/', '') });
      } else {
        navigation.navigate(path);
      }
    }
  });

  // ...
}

Behavior

Microphone Permissions

On Android, the hook automatically requests RECORD_AUDIO permission when start() is called. If permission is denied or permanently blocked, an error is returned.

Function Loading

The hook loads frontend functions asynchronously when the runtime changes. The start() function will fail if functions are still loading.

Tool Call Handling

The hook automatically:
  • Extracts tool calls from real-time events
  • Executes navigate_to and execute_app_function tools
  • Handles both frontend and backend function execution
  • Returns results to the agent via real-time events

Cleanup

The hook automatically cleans up resources (WebRTC connections, event listeners) when the component unmounts.

Error Handling

const { status, error, start } = useMobileVoiceAgent(options);

const handleStart = async () => {
  try {
    await start();
    console.log('Voice session started');
  } catch (err) {
    console.error('Failed to start:', err);
    // error state is also available in the error property
    console.error('Error state:', error);
  }
};
Common errors:
  • "WebRTC native module is not available." - react-native-webrtc not installed
  • "Runtime configuration is not available." - runtime prop is null
  • "Functions are still loading. Try again in a moment." - functions not loaded yet
  • "Microphone permission denied." - User denied permission (Android)

Build docs developers (and LLMs) love