> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

# Expo React Native के साथ क्रॉस-प्लैटफ़ॉर्म वॉइस एजेंट

> **Note**
>
> **ट्यूटोरियल** · इसमें माना गया है कि आपने [ElevenAgents क्विकस्टार्ट](/docs/hi/eleven-agents/quickstart) पूरा कर लिया है और आपका Expo डेवलपमेंट एनवायरनमेंट सेट अप है।

## परिचय

इस ट्यूटोरियल में आप जानेंगे कि [Expo React Native](https://expo.dev/) और WebRTC सपोर्ट वाले ElevenLabs [React Native SDK](/docs/hi/eleven-agents/libraries/react-native) का उपयोग करके iOS और Android पर काम करने वाला वॉइस एजेंट कैसे बनाएं।

## आवश्यकताएं

* API कुंजी वाले ElevenLabs अकाउंट की आवश्यकता होगी: [API key](https://elevenlabs.io/app/settings/api-keys)।
* आपके सिस्टम पर Node.js v18 या इसके बाद का वर्ज़न इंस्टॉल होना चाहिए।

## सेटअप

### नया Expo प्रोजेक्ट बनाएं

`create-expo-app` का उपयोग करके नया खाली Expo प्रोजेक्ट बनाएं:

```bash
npx create-expo-app@latest --template blank-typescript
```

### डिपेंडेंसी इंस्टॉल करें

ElevenLabs React Native SDK और उसकी डिपेंडेंसी इंस्टॉल करें:

```bash
npx expo install @elevenlabs/react-native @livekit/react-native @livekit/react-native-webrtc @config-plugins/react-native-webrtc @livekit/react-native-expo-plugin @livekit/react-native-expo-plugin livekit-client
```

> **Note**
>
> अगर आपको peer dependencies से जुड़ी समस्या आ रही है, तो प्रोजेक्ट की रूट डायरेक्टरी में `.npmrc` फ़ाइल जोड़ें
> और उसमें यह कंटेंट डालें: `legacy-peer-deps=true`।

### माइक्रोफ़ोन अनुमतियां चालू करें और Expo प्लगइन जोड़ें

`app.json` फ़ाइल में ये अनुमतियां जोड़ें:

**`app.json`**

```json app.json
{
  "expo": {
    "scheme": "elevenlabs",
    // ...
    "ios": {
      "infoPlist": {
        "NSMicrophoneUsageDescription": "This app uses the microphone to record audio."
      },
      "supportsTablet": true,
      "bundleIdentifier": "YOUR.BUNDLE.ID"
    },
    "android": {
      "permissions": [
        "android.permission.RECORD_AUDIO",
        "android.permission.ACCESS_NETWORK_STATE",
        "android.permission.CAMERA",
        "android.permission.INTERNET",
        "android.permission.MODIFY_AUDIO_SETTINGS",
        "android.permission.SYSTEM_ALERT_WINDOW",
        "android.permission.WAKE_LOCK",
        "android.permission.BLUETOOTH"
      ],
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#ffffff"
      },
      "package": "YOUR.PACKAGE.ID"
    },
    "plugins": ["@livekit/react-native-expo-plugin", "@config-plugins/react-native-webrtc"]
    // ...
  }
}
```

इससे बातचीत शुरू होने पर React Native माइक्रोफ़ोन की अनुमति मांग सकेगा।

> **नोट**
>
> Android एम्युलेटर के लिए, एम्युलेटर माइक्रोफ़ोन सेटिंग में आपको "Virtual microphone uses host audio input" चालू करना होगा।

## अपने ऐप में ElevenLabs Agents जोड़ें

अपने `./App.tsx` फ़ाइल में यह कोड जोड़कर अपने ऐप में ElevenLabs Agents जोड़ें:

**`./App.tsx`**

```tsx ./App.tsx
import { ConversationProvider, useConversation } from "@elevenlabs/react-native";
import type { ConversationStatus, ConversationEvent, Role } from "@elevenlabs/react-native";
import React, { useState } from "react";
import {
  View,
  Text,
  StyleSheet,
  TouchableOpacity,
  Keyboard,
  TouchableWithoutFeedback,
  Platform,
} from "react-native";
import { TextInput } from "react-native";

import { getBatteryLevel, changeBrightness, flashScreen } from "./utils/tools";

const ConversationScreen = () => {
  const conversation = useConversation({
    clientTools: {
      getBatteryLevel,
      changeBrightness,
      flashScreen,
    },
    onConnect: ({ conversationId }: { conversationId: string }) => {
      console.log("✅ Connected to conversation", conversationId);
    },
    onDisconnect: (details: string) => {
      console.log("❌ Disconnected from conversation", details);
    },
    onError: (message: string, context?: Record<string, unknown>) => {
      console.error("❌ Conversation error:", message, context);
    },
    onMessage: ({ message, source }: { message: ConversationEvent; source: Role }) => {
      console.log(`💬 Message from ${source}:`, message);
    },
    onModeChange: ({ mode }: { mode: "speaking" | "listening" }) => {
      console.log(`🔊 Mode: ${mode}`);
    },
    onStatusChange: ({ status }: { status: ConversationStatus }) => {
      console.log(`📡 Status: ${status}`);
    },
    onCanSendFeedbackChange: ({ canSendFeedback }: { canSendFeedback: boolean }) => {
      console.log(`🔊 Can send feedback: ${canSendFeedback}`);
    },
  });

  const [isStarting, setIsStarting] = useState(false);
  const [textInput, setTextInput] = useState("");

  const handleSubmitText = () => {
    if (textInput.trim()) {
      conversation.sendUserMessage(textInput.trim());
      setTextInput("");
      Keyboard.dismiss();
    }
  };

  const startConversation = async () => {
    if (isStarting) return;

    setIsStarting(true);
    try {
      await conversation.startSession({
        agentId: process.env.EXPO_PUBLIC_AGENT_ID,
        dynamicVariables: {
          platform: Platform.OS,
        },
      });
    } catch (error) {
      console.error("Failed to start conversation:", error);
    } finally {
      setIsStarting(false);
    }
  };

  const endConversation = async () => {
    try {
      await conversation.endSession();
    } catch (error) {
      console.error("Failed to end conversation:", error);
    }
  };

  const getStatusColor = (status: ConversationStatus): string => {
    switch (status) {
      case "connected":
        return "#10B981";
      case "connecting":
        return "#F59E0B";
      case "disconnected":
        return "#EF4444";
      default:
        return "#6B7280";
    }
  };

  const getStatusText = (status: ConversationStatus): string => {
    return status[0].toUpperCase() + status.slice(1);
  };

  const canStart = conversation.status === "disconnected" && !isStarting;
  const canEnd = conversation.status === "connected";

  return (
    <TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
      <View style={styles.container}>
        <Text style={styles.title}>ElevenLabs React Native Example</Text>
        <Text style={styles.subtitle}>Remember to set the agentId in the .env file!</Text>

        <View style={styles.statusContainer}>
          <View
            style={[styles.statusDot, { backgroundColor: getStatusColor(conversation.status) }]}
          />
          <Text style={styles.statusText}>{getStatusText(conversation.status)}</Text>
        </View>

        {/* Speaking Indicator */}
        {conversation.status === "connected" && (
          <View style={styles.speakingContainer}>
            <View
              style={[
                styles.speakingDot,
                {
                  backgroundColor: conversation.isSpeaking ? "#8B5CF6" : "#D1D5DB",
                },
              ]}
            />
            <Text
              style={[
                styles.speakingText,
                { color: conversation.isSpeaking ? "#8B5CF6" : "#9CA3AF" },
              ]}
            >
              {conversation.isSpeaking ? "🎤 AI Speaking" : "👂 AI Listening"}
            </Text>
          </View>
        )}

        <View style={styles.buttonContainer}>
          <TouchableOpacity
            style={[styles.button, styles.startButton, !canStart && styles.disabledButton]}
            onPress={startConversation}
            disabled={!canStart}
          >
            <Text style={styles.buttonText}>
              {isStarting ? "Starting..." : "Start Conversation"}
            </Text>
          </TouchableOpacity>

          <TouchableOpacity
            style={[styles.button, styles.endButton, !canEnd && styles.disabledButton]}
            onPress={endConversation}
            disabled={!canEnd}
          >
            <Text style={styles.buttonText}>End Conversation</Text>
          </TouchableOpacity>
        </View>

        {/* Feedback Buttons */}
        {conversation.status === "connected" && conversation.canSendFeedback && (
          <View style={styles.feedbackContainer}>
            <Text style={styles.feedbackLabel}>How was that response?</Text>
            <View style={styles.feedbackButtons}>
              <TouchableOpacity
                style={[styles.button, styles.likeButton]}
                onPress={() => conversation.sendFeedback(true)}
              >
                <Text style={styles.buttonText}>👍 Like</Text>
              </TouchableOpacity>
              <TouchableOpacity
                style={[styles.button, styles.dislikeButton]}
                onPress={() => conversation.sendFeedback(false)}
              >
                <Text style={styles.buttonText}>👎 Dislike</Text>
              </TouchableOpacity>
            </View>
          </View>
        )}

        {/* Text Input and Messaging */}
        {conversation.status === "connected" && (
          <View style={styles.messagingContainer}>
            <Text style={styles.messagingLabel}>Send Text Message</Text>
            <TextInput
              style={styles.textInput}
              value={textInput}
              onChangeText={(text) => {
                setTextInput(text);
                // Prevent agent from interrupting while user is typing
                if (text.length > 0) {
                  conversation.sendUserActivity();
                }
              }}
              placeholder="Type your message or context... (Press Enter to send)"
              multiline
              onSubmitEditing={handleSubmitText}
              returnKeyType="send"
              blurOnSubmit={true}
            />
            <View style={styles.messageButtons}>
              <TouchableOpacity
                style={[styles.button, styles.messageButton]}
                onPress={handleSubmitText}
                disabled={!textInput.trim()}
              >
                <Text style={styles.buttonText}>💬 Send Message</Text>
              </TouchableOpacity>
              <TouchableOpacity
                style={[styles.button, styles.contextButton]}
                onPress={() => {
                  if (textInput.trim()) {
                    conversation.sendContextualUpdate(textInput.trim());
                    setTextInput("");
                    Keyboard.dismiss();
                  }
                }}
                disabled={!textInput.trim()}
              >
                <Text style={styles.buttonText}>📝 Send Context</Text>
              </TouchableOpacity>
            </View>
          </View>
        )}
      </View>
    </TouchableWithoutFeedback>
  );
};

export default function App() {
  return (
    <ConversationProvider>
      <ConversationScreen />
    </ConversationProvider>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "#F3F4F6",
    padding: 20,
  },
  title: {
    fontSize: 24,
    fontWeight: "bold",
    marginBottom: 8,
    color: "#1F2937",
  },
  subtitle: {
    fontSize: 16,
    color: "#6B7280",
    marginBottom: 32,
  },
  statusContainer: {
    flexDirection: "row",
    alignItems: "center",
    marginBottom: 24,
  },
  statusDot: {
    width: 12,
    height: 12,
    borderRadius: 6,
    marginRight: 8,
  },
  statusText: {
    fontSize: 16,
    fontWeight: "500",
    color: "#374151",
  },
  speakingContainer: {
    flexDirection: "row",
    alignItems: "center",
    marginBottom: 24,
  },
  speakingDot: {
    width: 12,
    height: 12,
    borderRadius: 6,
    marginRight: 8,
  },
  speakingText: {
    fontSize: 14,
    fontWeight: "500",
  },
  toolsContainer: {
    backgroundColor: "#E5E7EB",
    padding: 16,
    borderRadius: 8,
    marginBottom: 24,
    width: "100%",
  },
  toolsTitle: {
    fontSize: 14,
    fontWeight: "600",
    color: "#374151",
    marginBottom: 8,
  },
  toolItem: {
    fontSize: 12,
    color: "#6B7280",
    fontFamily: "monospace",
    marginBottom: 4,
  },
  buttonContainer: {
    width: "100%",
    gap: 16,
  },
  button: {
    backgroundColor: "#3B82F6",
    paddingVertical: 16,
    paddingHorizontal: 32,
    borderRadius: 8,
    alignItems: "center",
  },
  startButton: {
    backgroundColor: "#10B981",
  },
  endButton: {
    backgroundColor: "#EF4444",
  },
  disabledButton: {
    backgroundColor: "#9CA3AF",
  },
  buttonText: {
    color: "white",
    fontSize: 16,
    fontWeight: "600",
  },
  instructions: {
    marginTop: 24,
    fontSize: 14,
    color: "#6B7280",
    textAlign: "center",
    lineHeight: 20,
  },
  feedbackContainer: {
    marginTop: 24,
    alignItems: "center",
  },
  feedbackLabel: {
    fontSize: 16,
    fontWeight: "500",
    color: "#374151",
    marginBottom: 12,
  },
  feedbackButtons: {
    flexDirection: "row",
    gap: 16,
  },
  likeButton: {
    backgroundColor: "#10B981",
  },
  dislikeButton: {
    backgroundColor: "#EF4444",
  },
  messagingContainer: {
    marginTop: 24,
    width: "100%",
  },
  messagingLabel: {
    fontSize: 16,
    fontWeight: "500",
    color: "#374151",
    marginBottom: 8,
  },
  textInput: {
    backgroundColor: "#FFFFFF",
    borderRadius: 8,
    padding: 16,
    minHeight: 100,
    textAlignVertical: "top",
    borderWidth: 1,
    borderColor: "#D1D5DB",
    marginBottom: 16,
  },
  messageButtons: {
    flexDirection: "row",
    gap: 16,
  },
  messageButton: {
    backgroundColor: "#3B82F6",
    flex: 1,
  },
  contextButton: {
    backgroundColor: "#4F46E5",
    flex: 1,
  },
  activityContainer: {
    marginTop: 24,
    alignItems: "center",
  },
  activityLabel: {
    fontSize: 14,
    color: "#6B7280",
    marginBottom: 8,
    textAlign: "center",
  },
  activityButton: {
    backgroundColor: "#F59E0B",
  },
});
```

### नेटिव क्लाइंट टूल्स

ElevenLabs एजेंट बनाने का एक अहम हिस्सा एजेंट को फ़ंक्शनैलिटी का ऐक्सेस देना और उन्हें डाइनैमिक रूप से चलाने की अनुमति देना है। यह [क्लाइंट टूल्स](/docs/hi/eleven-agents/customization/tools/client-tools) के ज़रिए किया जा सकता है।

अपने क्लाइंट टूल्स के लिए नई फ़ाइल बनाएं: `./utils/tools.ts` और इसमें यह कोड जोड़ें:

**`./utils/tools.ts`**

```ts ./utils/tools.ts
import * as Battery from "expo-battery";
import * as Brightness from "expo-brightness";

const getBatteryLevel = async () => {
  const batteryLevel = await Battery.getBatteryLevelAsync();
  console.log("batteryLevel", batteryLevel);
  if (batteryLevel === -1) {
    return "Error: Device does not support retrieving the battery level.";
  }
  return batteryLevel;
};

const changeBrightness = ({ brightness }: { brightness: number }) => {
  console.log("changeBrightness", brightness);
  Brightness.setSystemBrightnessAsync(brightness);
  return brightness;
};

const flashScreen = () => {
  Brightness.setSystemBrightnessAsync(1);
  setTimeout(() => {
    Brightness.setSystemBrightnessAsync(0);
  }, 200);
  return "Successfully flashed the screen.";
};

export { getBatteryLevel, changeBrightness, flashScreen };
```

### डाइनैमिक वेरिएबल्स

क्लाइंट टूल्स के अलावा, हम प्लेटफ़ॉर्म (web, iOS, Android) को पहले मैसेज और प्रॉम्प्ट, दोनों में [डाइनैमिक वेरिएबल](https://elevenlabs.io/docs/eleven-agents/customization/personalization/dynamic-variables) के रूप में भी डाल रहे हैं:

**`./App.tsx`**

```tsx ./App.tsx
// ...
const startConversation = async () => {
  if (isStarting) return;

  setIsStarting(true);
  try {
    await conversation.startSession({
      agentId: process.env.EXPO_PUBLIC_AGENT_ID,
      dynamicVariables: {
        platform: Platform.OS,
      },
    });
  } catch (error) {
    console.error("Failed to start conversation:", error);
  } finally {
    setIsStarting(false);
  }
};
// ...
```

## एजेंट कॉन्फ़िगरेशन

#### ElevenLabs में साइन इन करें

[elevenlabs.io](https://elevenlabs.io/app/sign-up) पर जाएं और अपने अकाउंट में साइन इन करें।

#### नया एजेंट बनाएं

[Agents Platform > Agents](https://elevenlabs.io/app/agents/agents) पर जाएं और
खाली टेम्पलेट से नया एजेंट बनाएं।

#### पहला मैसेज सेट करें

पहला मैसेज सेट करें और प्लेटफ़ॉर्म के लिए डाइनैमिक वेरिएबल तय करें।

```txt
Hi there, woah, so cool that I'm running on {{platform}}. What can I help you with?
```

#### सिस्टम प्रॉम्प्ट सेट करें

सिस्टम प्रॉम्प्ट सेट करें। यहां भी डाइनैमिक वेरिएबल शामिल किए जा सकते हैं।

```txt
You are a helpful assistant running on {{platform}}. You have access to certain tools that allow you to check the user device battery level and change the display brightness. Use these tools if the user asks about them. Otherwise, just answer the question.
```

#### क्लाइंट टूल्स सेट अप करें

ये क्लाइंट टूल्स सेट अप करें:

* नाम: `getBatteryLevel`
  * विवरण: डिवाइस की बैटरी का स्तर दशमलव प्रतिशत के रूप में प्राप्त करता है।
  * जवाब का इंतज़ार करें: `true`
  * रिस्पॉन्स टाइमआउट (सेकंड): 3
* नाम: `changeBrightness`
  * विवरण: डिवाइस स्क्रीन की ब्राइटनेस बदलता है।
  * जवाब का इंतज़ार करें: `true`
  * रिस्पॉन्स टाइमआउट (सेकंड): 3
  * पैरामीटर:
    * डेटा टाइप: `number`
    * आइडेंटिफ़ायर: `brightness`
    * आवश्यक: `true`
    * वैल्यू टाइप: `LLM Prompt`
    * विवरण: 0 से 1 के बीच की संख्या, दोनों शामिल हैं, जो स्क्रीन की वांछित ब्राइटनेस दर्शाती है।
* नाम: `flashScreen`
  * विवरण: स्क्रीन को तेज़ी से ऑन और ऑफ़ करता है।
  * जवाब का इंतज़ार करें: `true`
  * रिस्पॉन्स टाइमआउट (सेकंड): 3

## ऐप चलाएं

इस ऐप के लिए कुछ नेटिव डिपेंडेंसी चाहिए, जो Expo Go में सपोर्ट नहीं होतीं। इसलिए आपको पहले ऐप को prebuild करना होगा और फिर उसे नेटिव डिवाइस पर चलाना होगा।

* टर्मिनल 1:
  * `npx expo prebuild --clean` चलाएं

```bash
npx expo prebuild --clean
```

* https पर Expo डेवलपमेंट सर्वर शुरू करने के लिए `npx expo start --tunnel` चलाएं।

```bash
npx expo start --tunnel
```

* टर्मिनल 2:
  * अपने iOS डिवाइस पर ऐप चलाने के लिए `npx expo run:ios --device` चलाएं।

```bash
npx expo run:ios --device
```

## अगले चरण

#### [ElevenAgents](/docs/hi/eleven-agents/quickstart)

पूरे ElevenAgents प्लेटफ़ॉर्म के साथ वॉइस एजेंट बनाएं और डिप्लॉय करें।

#### [ElevenAgents इंटीग्रेशन ओवरव्यू](/docs/hi/eleven-agents/integrate/overview)

वेब विजेट, नेटिव SDK और टेलीफ़ोनी समेत सभी इंटीग्रेशन विकल्प देखें।