> 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.

# LiveKit इंटीग्रेशन

यह गाइड बताती है कि LiveKit रूम के लिए वॉइस लेयर के रूप में ElevenLabs स्पीच इंजन का इस्तेमाल कैसे करें। एक LiveKit Agents वर्कर पार्टिसिपेंट के रूप में रूम में शामिल होता है, यूज़र के ऑडियो ट्रैक को सब्सक्राइब करता है, स्पीच इंजन के लिए WebSocket खोलता है और स्पीच इंजन के सिंथेसाइज़ किए गए ऑडियो को अपने ट्रैक के रूप में वापस रूम में पब्लिश करता है।

## आर्किटेक्चर

स्पीच इंजन दो तरह के WebSocket कनेक्शन स्वीकार करता है:

* **ब्रेन WebSocket**, जिससे ElevenLabs API कनेक्ट होता है। आपका सर्वर इसे स्पीच इंजन SDK (`engine.serve()` / `engine.attach()`) के साथ चलाता है और रिस्पॉन्ड करने के लिए ट्रांसक्रिप्ट प्राप्त करता है।
* **कन्वर्सेशन WebSocket**, जिससे क्लाइंट कनेक्ट होते हैं। ब्राउज़र WebRTC टोकन के ज़रिए कनेक्ट होते हैं; नॉन-ब्राउज़र क्लाइंट (जैसे LiveKit Agents वर्कर) साइन किए गए URL के ज़रिए कनेक्ट होते हैं और दोनों दिशाओं में रॉ PCM ऑडियो स्ट्रीम करते हैं।

LiveKit वर्कर दूसरे कनेक्शन का इस्तेमाल करता है। यह LiveKit रूम के पार्टिसिपेंट्स की ओर से स्पीच इंजन के "क्लाइंट" के रूप में काम करता है।

```mermaid
sequenceDiagram
    participant Browser
    participant LK as LiveKit Room
    participant Worker as Agents Worker
    participant EL as ElevenLabs (conversation WS)
    participant Brain as Brain Server

    Browser->>LK: Join room (LiveKit token)
    Worker->>LK: Join room (dispatched)
    Worker->>EL: Open conversation WebSocket (signed URL)

    loop Conversation
        Browser->>LK: Microphone audio (Opus)
        LK->>Worker: Decoded PCM frames
        Worker->>EL: user_audio_chunk (base64 PCM)
        EL->>Brain: user_transcript
        Brain-->>EL: agent_response (streamed)
        EL->>Worker: audio (base64 PCM)
        Worker->>LK: Publish PCM frames
        LK->>Browser: Audio (Opus)
    end
```

ब्रेन सर्वर [स्पीच इंजन क्विकस्टार्ट](/docs/hi/eleven-api/guides/cookbooks/speech-engine) से अपरिवर्तित रहता है — LiveKit वर्कर ऑडियो स्रोत के रूप में ब्राउज़र की जगह लेता है, लेकिन LLM लॉजिक वही रहता है।

## इस पैटर्न का इस्तेमाल कब करें

जब रूम खुद अनुभव का हिस्सा हो, तब LiveKit ब्रिज इस्तेमाल करें:

* ऐसे मल्टी-पार्टिसिपेंट सेशन, जहां यूज़र एक-दूसरे के साथ एजेंट से बात करते हैं
* मौजूदा LiveKit डिप्लॉयमेंट, जहां ट्रांसपोर्ट बदलने से क्लाइंट्स काम करना बंद कर देंगे
* स्क्रीन शेयर, वीडियो या टेक्स्ट चैट के साथ रूम साझा करने वाले वॉइस एजेंट्स
* SIP-से-LiveKit डिस्पैच की गई कॉल्स, जिनमें लाइन पर AI एजेंट चाहिए

अगर आपको अन्य पार्टिसिपेंट्स के बिना सिर्फ़ ब्राउज़र-से-स्पीच-इंजन वॉइस लूप चाहिए, तो [स्पीच इंजन क्विकस्टार्ट](/docs/hi/eleven-api/guides/cookbooks/speech-engine#client-setup) में WebRTC क्लाइंट आसान है — स्पीच इंजन सीधे ब्राउज़र से WebRTC पर बात करता है, किसी LiveKit रूम की ज़रूरत नहीं होती।

## ज़रूरी शर्तें

* एक LiveKit प्रोजेक्ट ([LiveKit Cloud](https://cloud.livekit.io/) या सेल्फ़-होस्टेड सर्वर)। वर्कर को `LIVEKIT_URL`, `LIVEKIT_API_KEY` और `LIVEKIT_API_SECRET` चाहिए।
* एक ElevenLabs स्पीच इंजन। इसे बनाने और ब्रेन सर्वर चलाने के लिए [स्पीच इंजन क्विकस्टार्ट](/docs/hi/eleven-api/guides/cookbooks/speech-engine) फ़ॉलो करें।
* Python 3.9+ या Node.js 18+।

> **Note**
>
> Node ब्रिज वर्कर
> [`@livekit/rtc-node`](https://www.npmjs.com/package/@livekit/rtc-node) का इस्तेमाल करता है, जो अभी
> डेवलपर प्रीव्यू में है। प्रोडक्शन डिप्लॉयमेंट के लिए Python वर्कर को प्राथमिकता दें।

## स्पीच इंजन ऑडियो फ़ॉर्मैट कॉन्फ़िगर करें

LiveKit का `AudioStream` आने वाले Opus ट्रैक्स को आपके मांगे गए PCM सैंपल रेट पर री-सैंपल करता है, इसलिए आप इसे सीधे स्पीच इंजन के इनपुट से मैच कर सकते हैं। ASR इनपुट के लिए 16 kHz PCM स्वीकार करने और TTS आउटपुट के लिए 24 kHz PCM देने हेतु स्पीच इंजन अपडेट करें।

**`configure_engine.py`**

```python title="configure_engine.py"
import asyncio
import os
from elevenlabs import AsyncElevenLabs

elevenlabs = AsyncElevenLabs(api_key=os.environ["ELEVENLABS_API_KEY"])


async def update_engine():
    await elevenlabs.speech_engine.update(
        speech_engine_id="seng_8k3m9xr4hjnfg983brhmhkd98n6",
        asr={"user_input_audio_format": "pcm_16000"},
        tts={"agent_output_audio_format": "pcm_24000"},
    )


asyncio.run(update_engine())
```

**`configure-engine.mts`**

```typescript title="configure-engine.mts"
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import "dotenv/config";

const elevenlabs = new ElevenLabsClient({
  apiKey: process.env.ELEVENLABS_API_KEY,
});

await elevenlabs.speechEngine.update("seng_8k3m9xr4hjnfg983brhmhkd98n6", {
  asr: { userInputAudioFormat: "pcm_16000" },
  tts: { agentOutputAudioFormat: "pcm_24000" },
});
```

स्पीच इंजन PCM में हर जगह साइन किया हुआ 16-बिट लिटल-एंडियन फ़ॉर्मैट इस्तेमाल होता है। अन्य समर्थित रेट्स के लिए [ऑडियो फ़ॉर्मैट रेफरेंस](#audio-format-reference) देखें।

## ब्रिज वर्कर बनाएं

वर्कर एक लंबे समय तक चलने वाली प्रक्रिया है जो आपके LiveKit सर्वर से कनेक्ट होती है, जॉब्स का इंतज़ार करती है, असाइन किए गए रूम्स में शामिल होती है और रूम व Speech Engine के बीच ऑडियो ब्रिज करती है।

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

**`Python`**

```bash title="Python"
pip install "livekit-agents" "livekit-api" "elevenlabs" "aiohttp" "python-dotenv"
```

**`Node`**

```bash title="Node"
npm install @livekit/agents @livekit/rtc-node @elevenlabs/elevenlabs-js ws dotenv
```

#### Speech Engine का साइन किया हुआ URL बनाएं

वर्कर Speech Engine कन्वर्सेशन WebSocket के लिए कम समय तक मान्य रहने वाला साइन किया हुआ URL मांगता है। इस URL में इंजन ID और एक बार इस्तेमाल होने वाला सिग्नेचर शामिल होता है, ताकि वर्कर आपकी API कुंजी दिखाए बिना WebSocket खोल सके।

**`bridge.py`**

```python title="bridge.py"
from elevenlabs import AsyncElevenLabs

elevenlabs = AsyncElevenLabs(api_key=os.environ["ELEVENLABS_API_KEY"])

async def signed_url() -> str:
    response = await elevenlabs.conversational_ai.conversations.get_signed_url(
        agent_id=os.environ["SPEECH_ENGINE_ID"],
    )
    return response.signed_url
```

**`bridge.mts`**

```typescript title="bridge.mts"
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";

const elevenlabs = new ElevenLabsClient({
  apiKey: process.env.ELEVENLABS_API_KEY,
});

async function signedUrl(): Promise<string> {
  const response = await elevenlabs.conversationalAi.conversations.getSignedUrl({
    agentId: process.env.SPEECH_ENGINE_ID!,
  });
  return response.signedUrl;
}
```

#### वर्कर एंट्रीपॉइंट तय करें

हर बार वर्कर को किसी रूम में भेजे जाने पर उसका एंट्रीपॉइंट चलता है। एंट्रीपॉइंट रूम से कनेक्ट होता है, Speech Engine कन्वर्सेशन WebSocket खोलता है और दो ऑडियो ब्रिज शुरू करता है: एक Speech Engine को भेजे जाने वाले कॉलर ऑडियो के लिए, और दूसरा वापस आने वाले सिंथेसाइज़्ड ऑडियो के लिए।

**`bridge.py`**

```python title="bridge.py" maxLines=0
import asyncio
import base64
import json
import os

import aiohttp
from dotenv import load_dotenv
from elevenlabs import AsyncElevenLabs
from livekit import agents, rtc
from livekit.agents import JobContext, WorkerOptions, cli

load_dotenv()

elevenlabs = AsyncElevenLabs(api_key=os.environ["ELEVENLABS_API_KEY"])
SPEECH_ENGINE_ID = os.environ["SPEECH_ENGINE_ID"]

USER_INPUT_RATE = 16000
AGENT_OUTPUT_RATE = 24000


async def signed_url() -> str:
    response = await elevenlabs.conversational_ai.conversations.get_signed_url(
        agent_id=SPEECH_ENGINE_ID,
    )
    return response.signed_url


async def entrypoint(ctx: JobContext):
    el_ws_ready: asyncio.Future[aiohttp.ClientWebSocketResponse] = (
        asyncio.get_running_loop().create_future()
    )

    async def pump_user_audio(track: rtc.Track):
        el_ws = await el_ws_ready
        stream = rtc.AudioStream(
            track, sample_rate=USER_INPUT_RATE, num_channels=1,
        )
        async for event in stream:
            payload = base64.b64encode(bytes(event.frame.data)).decode()
            await el_ws.send_str(json.dumps({"user_audio_chunk": payload}))

    # Register the subscriber BEFORE ctx.connect() so we don't miss tracks
    # that get auto-subscribed during the connection handshake.
    @ctx.room.on("track_subscribed")
    def on_track_subscribed(track, publication, participant):
        if track.kind != rtc.TrackKind.KIND_AUDIO:
            return
        if participant.identity == ctx.room.local_participant.identity:
            return
        asyncio.create_task(pump_user_audio(track))

    await ctx.connect()

    # Publish a track for the agent's synthesized audio.
    source = rtc.AudioSource(sample_rate=AGENT_OUTPUT_RATE, num_channels=1)
    track = rtc.LocalAudioTrack.create_audio_track("elevenlabs-agent", source)
    await ctx.room.local_participant.publish_track(
        track,
        rtc.TrackPublishOptions(source=rtc.TrackSource.SOURCE_MICROPHONE),
    )

    # Open the Speech Engine conversation WebSocket.
    http = aiohttp.ClientSession()
    el_ws = await http.ws_connect(await signed_url())
    await el_ws.send_str(json.dumps({"type": "conversation_initiation_client_data"}))
    el_ws_ready.set_result(el_ws)

    async def el_to_room():
        async for msg in el_ws:
            if msg.type != aiohttp.WSMsgType.TEXT:
                continue
            event = json.loads(msg.data)
            etype = event.get("type")
            if etype == "audio":
                pcm = base64.b64decode(event["audio_event"]["audio_base_64"])
                samples_per_channel = len(pcm) // 2
                frame = rtc.AudioFrame(
                    pcm, AGENT_OUTPUT_RATE, 1, samples_per_channel,
                )
                await source.capture_frame(frame)
            elif etype == "interruption":
                source.clear_queue()
            elif etype == "ping":
                event_id = event.get("ping_event", {}).get("event_id")
                await el_ws.send_str(json.dumps({
                    "type": "pong", "event_id": event_id,
                }))

    pump_task = asyncio.create_task(el_to_room())

    async def cleanup():
        pump_task.cancel()
        await el_ws.close()
        await http.close()

    ctx.add_shutdown_callback(cleanup)


if __name__ == "__main__":
    cli.run_app(WorkerOptions(
        entrypoint_fnc=entrypoint,
        agent_name="elevenlabs-bridge",
    ))
```

**`bridge.mts`**

```typescript title="bridge.mts" maxLines=0
import {
  type JobContext,
  WorkerOptions,
  cli,
  defineAgent,
} from "@livekit/agents";
import {
  AudioFrame,
  AudioSource,
  AudioStream,
  LocalAudioTrack,
  RoomEvent,
  TrackKind,
  TrackPublishOptions,
  TrackSource,
} from "@livekit/rtc-node";
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import WebSocket from "ws";
import { fileURLToPath } from "node:url";
import "dotenv/config";

const elevenlabs = new ElevenLabsClient({
  apiKey: process.env.ELEVENLABS_API_KEY,
});
const SPEECH_ENGINE_ID = process.env.SPEECH_ENGINE_ID!;

const USER_INPUT_RATE = 16000;
const AGENT_OUTPUT_RATE = 24000;

async function signedUrl(): Promise<string> {
  const response = await elevenlabs.conversationalAi.conversations.getSignedUrl({
    agentId: SPEECH_ENGINE_ID,
  });
  return response.signedUrl;
}

export default defineAgent({
  entry: async (ctx: JobContext) => {
    let resolveElReady: (ws: WebSocket) => void;
    const elReady = new Promise<WebSocket>((resolve) => {
      resolveElReady = resolve;
    });

    // Register the subscriber BEFORE ctx.connect() so we don't miss
    // tracks that get auto-subscribed during the connection handshake.
    ctx.room.on(RoomEvent.TrackSubscribed, (track, _pub, participant) => {
      if (track.kind !== TrackKind.KIND_AUDIO) return;
      if (participant.identity === ctx.room.localParticipant?.identity) return;

      (async () => {
        const ws = await elReady;
        const stream = new AudioStream(track, {
          sampleRate: USER_INPUT_RATE,
          numChannels: 1,
        });
        for await (const frame of stream) {
          const payload = Buffer.from(
            frame.data.buffer,
            frame.data.byteOffset,
            frame.data.byteLength,
          ).toString("base64");
          ws.send(JSON.stringify({ user_audio_chunk: payload }));
        }
      })();
    });

    await ctx.connect();

    const source = new AudioSource(AGENT_OUTPUT_RATE, 1);
    const track = LocalAudioTrack.createAudioTrack("elevenlabs-agent", source);
    const publishOptions = new TrackPublishOptions();
    publishOptions.source = TrackSource.SOURCE_MICROPHONE;
    await ctx.room.localParticipant!.publishTrack(track, publishOptions);

    const ws = new WebSocket(await signedUrl());
    await new Promise<void>((resolve, reject) => {
      ws.once("open", () => resolve());
      ws.once("error", reject);
    });
    ws.send(JSON.stringify({ type: "conversation_initiation_client_data" }));
    resolveElReady!(ws);

    // Serialize captureFrame calls — concurrent captures throw
    // InvalidState in the rtc-node native layer.
    let captureChain: Promise<unknown> = Promise.resolve();

    ws.on("message", (raw) => {
      const event = JSON.parse(raw.toString());
      if (event.type === "audio") {
        const pcm = Buffer.from(event.audio_event.audio_base_64, "base64");
        const samples = new Int16Array(
          pcm.buffer, pcm.byteOffset, pcm.byteLength / 2,
        );
        const frame = new AudioFrame(
          samples, AGENT_OUTPUT_RATE, 1, samples.length,
        );
        captureChain = captureChain
          .then(() => source.captureFrame(frame))
          .catch((err) => console.warn("captureFrame:", err.message));
      } else if (event.type === "interruption") {
        source.clearQueue();
      } else if (event.type === "ping") {
        ws.send(JSON.stringify({
          type: "pong", event_id: event.ping_event?.event_id,
        }));
      }
    });

    ctx.addShutdownCallback(async () => {
      ws.close();
    });
  },
});

cli.runApp(new WorkerOptions({
  agent: fileURLToPath(import.meta.url),
  agentName: "elevenlabs-bridge",
}));
```

वर्कर `track_subscribed` हैंडलर में लोकल पार्टिसिपेंट की आइडेंटिटी से तुलना करके अपने प्रकाशित ऑडियो को फ़िल्टर कर देता है। इस जांच के बिना, वर्कर अपने ही सिंथेसाइज़्ड ऑडियो को वापस Speech Engine पर भेजने की कोशिश करता।

सही तरीके से काम करने के लिए क्रम से जुड़ी दो बातें अहम हैं:

* **लिस्नर टाइमिंग**: `TrackSubscribed` को `ctx.connect()` से पहले रजिस्टर किया जाता है। कनेक्शन हैंडशेक के दौरान LiveKit मौजूदा ट्रैक्स को अपने-आप सब्सक्राइब करता है और बाद में रजिस्टर किया गया लिस्नर इवेंट मिस कर सकता है। ऑडियो पंप Speech Engine WebSocket के लिए `Future` / `Promise` पर इंतज़ार करता है, ताकि वह तुरंत सब्सक्राइब कर सके और कनेक्शन खुलते ही ऑडियो फ़ॉरवर्ड कर सके।
* **सिर्फ़ TypeScript — कैप्चर सीरियलाइज़ेशन**: `@livekit/rtc-node` का `AudioSource.captureFrame` एक साथ कॉल होने पर `InvalidState` थ्रो करता है। TypeScript हैंडलर प्रॉमिस चेन के साथ कैप्चर्स को सीरियलाइज़ करता है। Python का सिंगल `async for el_to_room` लूप स्वाभाविक रूप से क्रमिक है और उसे इसकी ज़रूरत नहीं होती।

#### वर्कर शुरू करें

**`Python`**

```bash title="Python"
python bridge.py dev
```

**`Node`**

```bash title="Node"
npx tsx bridge.mts dev
```

`dev` हॉट रीलोड और रंगीन लॉग्स सक्षम करता है। प्रोडक्शन में JSON लॉग्स और बेहतर तरीके से शटडाउन के लिए `start` इस्तेमाल करें।

वर्कर आपके LiveKit सर्वर से कनेक्ट होता है और जॉब असाइनमेंट का इंतज़ार करता है। भेजे जाने तक वह किसी रूम में शामिल नहीं होता।

## वर्कर को किसी रूम में भेजें

वर्कर के पास `agent_name` है, इसलिए यह एक्सप्लिसिट डिस्पैच का इस्तेमाल करता है — यह सिर्फ़ तब रूम्स में शामिल होता है, जब आपका बैकएंड इसे ऐसा करने के लिए कहता है। सबसे आसान तरीका है कि ब्राउज़र के कनेक्ट करने के लिए इस्तेमाल होने वाले LiveKit एक्सेस टोकन में `RoomAgentDispatch` शामिल करें।

**`token_server.py`**

```python title="token_server.py"
import os

from dotenv import load_dotenv
from flask import Flask, jsonify, request
from livekit.api import AccessToken, RoomAgentDispatch, VideoGrants

load_dotenv()

app = Flask(**name**)

@app.route("/api/livekit-token")
def get_token():
room_name = request.args.get("room", "demo-room")
identity = request.args.get("identity", "web-user")

    token = (
        AccessToken(
            os.environ["LIVEKIT_API_KEY"],
            os.environ["LIVEKIT_API_SECRET"],
        )
        .with_identity(identity)
        .with_grants(VideoGrants(room_join=True, room=room_name))
        .with_room_config(
            room_configuration={
                "agents": [RoomAgentDispatch(agent_name="elevenlabs-bridge")],
            },
        )
    )

    return jsonify(token=token.to_jwt(), url=os.environ["LIVEKIT_URL"])

if **name** == "**main**":
app.run(port=3002)

```

**`token-server.mts`**

```typescript title="token-server.mts"
import express from "express";
import { AccessToken } from "livekit-server-sdk";
import "dotenv/config";

const app = express();

app.get("/api/livekit-token", async (req, res) => {
  const room = (req.query.room as string) ?? "demo-room";
  const identity = (req.query.identity as string) ?? "web-user";

  const token = new AccessToken(
    process.env.LIVEKIT_API_KEY!,
    process.env.LIVEKIT_API_SECRET!,
    { identity },
  );
  token.addGrant({ roomJoin: true, room });
  token.roomConfig = {
    agents: [{ agentName: "elevenlabs-bridge" }],
  };

  res.json({
    token: await token.toJwt(),
    url: process.env.LIVEKIT_URL,
  });
});

app.listen(3002, () => {
  console.log("Token server listening on port 3002");
});
```

जब कोई ब्राउज़र इस टोकन का इस्तेमाल करके रूम बनाता है या उसमें शामिल होता है, तो LiveKit अपने-आप ब्रिज वर्कर को उसी रूम में भेज देता है।

## ब्राउज़र से कनेक्ट करें

ब्राउज़र को सिर्फ़ स्टैंडर्ड LiveKit क्लाइंट चाहिए — यह सीधे Speech Engine से इंटरैक्ट नहीं करता।

**`App.tsx`**

```typescript title="App.tsx"
import { Room, RoomEvent, Track } from "livekit-client";
import { useCallback, useState } from "react";

export default function App() {
  const [room] = useState(() => new Room());

  const join = useCallback(async () => {
    const response = await fetch("/api/livekit-token");
    const { token, url } = await response.json();

    room.on(RoomEvent.TrackSubscribed, (track) => {
      if (track.kind === Track.Kind.Audio) {
        document.body.appendChild(track.attach());
      }
    });

    await room.connect(url, token);
    await room.localParticipant.setMicrophoneEnabled(true);
  }, [room]);

  return <button onClick={join}>Start conversation</button>;
}
```

बटन पर क्लिक होने पर ब्राउज़र LiveKit टोकन फ़ेच करता है, माइक्रोफ़ोन चालू करके रूम में शामिल होता है और एजेंट का ऑडियो ट्रैक पाना शुरू करता है। वर्कर भेजा जाता है, अपना Speech Engine सेशन खोलता है और दोनों दिशाओं में ऑडियो ब्रिज करता है।

## ऑडियो फ़ॉर्मैट रेफरेंस

Speech Engine नीचे दिए गए ऑडियो फ़ॉर्मैट्स को सपोर्ट करता है। इन्हें इंजन पर `asr.user_input_audio_format` और `tts.agent_output_audio_format` के ज़रिए कॉन्फ़िगर करें।

| फ़ॉर्मैट    | सैंपल रेट | एन्कोडिंग            | नोट्स                                                               |
| ----------- | --------- | -------------------- | ------------------------------------------------------------------- |
| `pcm_8000`  | 8 kHz     | Signed 16-bit LE PCM | सिर्फ़ ASR इनपुट।                                                   |
| `pcm_16000` | 16 kHz    | Signed 16-bit LE PCM | LiveKit यूज़र इनपुट के लिए सुझाया गया।                              |
| `pcm_22050` | 22.05 kHz | Signed 16-bit LE PCM |                                                                     |
| `pcm_24000` | 24 kHz    | Signed 16-bit LE PCM | LiveKit एजेंट आउटपुट के लिए सुझाया गया।                             |
| `pcm_44100` | 44.1 kHz  | Signed 16-bit LE PCM | TTS आउटपुट के लिए Independent Publisher टियर या उससे ऊपर ज़रूरी है। |
| `pcm_48000` | 48 kHz    | Signed 16-bit LE PCM | सिर्फ़ ASR इनपुट।                                                   |
| `ulaw_8000` | 8 kHz     | μ-law                | Twilio Media Streams द्वारा इस्तेमाल किया जाता है।                  |

LiveKit में `AudioStream` और `AudioSource` आपके लिए री-सैंपलिंग संभालते हैं — आप `AudioStream` से कोई भी सैंपल रेट मांग सकते हैं और SDK अंतर्निहित 48 kHz Opus ट्रैक से कन्वर्ट कर देता है।

## प्रोडक्शन से जुड़ी बातें

* **एक्सप्लिसिट डिस्पैच**: `WorkerOptions` पर हमेशा `agent_name` / `agentName` सेट करें। ऑटो-डिस्पैच आपके LiveKit प्रोजेक्ट में बनाए गए हर रूम के लिए वर्कर चलाता है, जो आमतौर पर आपकी ज़रूरत नहीं होती।
* **ब्रेन सर्वर ऑथेंटिकेशन**: Speech Engine पर एक शेयर किया गया सीक्रेट सेट करें और अपने ब्रेन सर्वर में इसे वेरिफ़ाई करें, ताकि सिर्फ़ Speech Engine ही आपके एंडपॉइंट तक पहुंच सके:
  ```python
  await elevenlabs.speech_engine.update(
      speech_engine_id="seng_8k3m9xr4hjnfg983brhmhkd98n6",
      speech_engine={"request_headers": {"x-api-key": os.environ["SHARED_SECRET"]}},
  )
  ```
  फिर ब्रेन सर्वर WebSocket अपग्रेड स्वीकार करने से पहले `request.headers["x-api-key"]` की जांच करता है।
* **टोकन सर्वर**: LiveKit और Speech Engine टोकन सर्वर-साइड बनाएं। `LIVEKIT_API_SECRET` या `ELEVENLABS_API_KEY` को कभी ब्राउज़र के सामने न लाएं।
* **इवेंट लूप हाइजीन**: CPU-बाउंड काम को वर्कर के इवेंट लूप से अलग रखें। `AudioSource.capture_frame` और `AudioStream` इटरेशन समय के प्रति संवेदनशील हैं; लंबे सिंक्रोनस कॉल्स इंटरप्शन इवेंट्स को देर से पहुंचा सकते हैं या ड्रॉप कर सकते हैं। ब्लॉकिंग काम के लिए `asyncio.to_thread()` (Python) या `worker_threads` (Node) का इस्तेमाल करें।
* **शटडाउन**: ElevenLabs WebSocket को साफ़ तरीके से बंद करने के लिए `ctx.add_shutdown_callback` / `ctx.addShutdownCallback` रजिस्टर करें। डिफ़ॉल्ट रूप से, आख़िरी नॉन-एजेंट पार्टिसिपेंट के चले जाने पर रूम (और जॉब) समाप्त हो जाता है।

## अगले चरण

#### [Speech Engine क्विकस्टार्ट](/docs/hi/eleven-api/guides/cookbooks/speech-engine)

ट्रांसक्रिप्ट का जवाब देने वाला ब्रेन सर्वर बनाएं।

#### [Pipecat इंटीग्रेशन](/docs/hi/eleven-api/guides/how-to/speech-engine/pipecat-integration)

Speech Engine के पीछे LLM पाइपलाइन के रूप में Pipecat का इस्तेमाल करें।

#### [Python SDK रेफरेंस](/docs/hi/eleven-api/resources/libraries/speech-engine/python-sdk-reference)

Speech Engine Python SDK के लिए क्लासेस, मेथड्स और इवेंट्स।

#### [JavaScript SDK रेफरेंस](/docs/hi/eleven-api/resources/libraries/speech-engine/javascript-sdk-reference)

Speech Engine JavaScript SDK के लिए क्लासेस, मेथड्स और इवेंट्स।