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

# Intégrez votre propre modèle

> **Note**
>
> Custom LLM vous permet de connecter vos conversations à votre propre LLM via un endpoint externe.
> ElevenLabs prend également en charge les [LLM intégrés nativement](/docs/fr/eleven-agents/customization/llm)

Les **LLM personnalisés** vous permettent d’utiliser votre propre clé API OpenAI ou d’exécuter un serveur LLM entièrement personnalisé.

Un LLM personnalisé ne peut pas être utilisé avec un [modèle Realtime](/docs/fr/eleven-agents/customization/realtime-models).

## Vue d’ensemble

Par défaut, nous utilisons nos propres identifiants internes pour des modèles populaires comme OpenAI. Pour utiliser un serveur LLM personnalisé, celui-ci doit respecter l’une des structures de requête/réponse compatibles avec OpenAI suivantes :

* [API Chat Completions](https://platform.openai.com/docs/api-reference/chat/create) (`/v1/chat/completions`)
* [API Responses](https://platform.openai.com/docs/api-reference/responses/create) (`/v1/responses`)

> **Info**
>
> L’API Responses est le format d’API plus récent d’OpenAI et prend en charge des fonctionnalités supplémentaires. Les deux formats d’API
> sont entièrement pris en charge pour l’intégration de LLM personnalisés.

Les guides suivants couvrent les deux cas d’usage :

1. **Utiliser votre propre clé OpenAI** : utilisez votre propre clé API OpenAI avec notre plateforme.
2. **Serveur LLM personnalisé** : hébergez et connectez votre propre implémentation de serveur LLM.

Vous apprendrez à :

* Stocker votre clé API OpenAI dans ElevenLabs
* Héberger un serveur qui reproduit l’endpoint [Chat Completions](https://platform.openai.com/docs/api-reference/chat/create) ou [Responses](https://platform.openai.com/docs/api-reference/responses/create) d’OpenAI
* Diriger ElevenLabs vers votre endpoint personnalisé
* Transmettre des paramètres supplémentaires à votre LLM selon vos besoins

## Résumé du raisonnement

Votre endpoint doit renvoyer le raisonnement séparément de la réponse finale. ElevenLabs ne génère pas le raisonnement à partir de la réponse finale.

Pour demander le raisonnement à un endpoint pris en charge, activez **Résumé du raisonnement** dans les paramètres LLM de l’agent ou définissez `enable_reasoning_summary` via l’API.

### Renvoyer le raisonnement

Utilisez le format correspondant à votre endpoint :

#### API Chat Completions

Diffusez le raisonnement dans le champ `reasoning` ou `reasoning_content` de chaque delta de réponse.

Pour les endpoints compatibles avec Gemini, ElevenLabs demande les réflexions avec
`google.thinking_config.include_thoughts` et lit le contenu marqué avec
`extra_content.google.thought`.

#### API Responses

Renvoyez un élément de sortie `reasoning` contenant le texte du résumé dans son champ `summary`.

Lorsque l’effort de raisonnement est configuré et que Résumé du raisonnement est activé, ElevenLabs définit
`reasoning.summary` sur `auto` dans la requête.

Consultez [Résumé du raisonnement](/docs/fr/eleven-agents/customization/llm#reasoning-summary) pour connaître le stockage, la diffusion et les limites.

## Utiliser votre propre clé OpenAI

Pour intégrer une clé OpenAI personnalisée, mettez à jour les paramètres de votre agent dans le Dashboard ElevenLabs afin de le diriger vers votre serveur LLM personnalisé, puis créez un secret contenant votre `OPENAI_API_KEY` :

Dans les paramètres de votre agent du Dashboard ElevenLabs, sélectionnez « Custom LLM » dans le menu déroulant « LLM » à droite.

![Ajouter un secret](/docs/_fern-img/83c62b81ef04860b9d884e28e1b4ac7bbceaa6e481e72e15ae82a76ec9bbad8e.webp)

Cliquez sur le champ sous « LLM » et faites défiler la liste pour sélectionner « Custom LLM ».

Saisissez l’URL du serveur et l’ID de modèle de votre serveur LLM personnalisé.

![Saisir l’URL](/docs/_fern-img/85ed8154868096a5c8de4c5e1c09d83c861069ea098c7643a960cac1736b67cb.webp)

Cliquez sur le menu déroulant sous « API key » et sélectionnez « Create new secret ». Nommez la clé `OPENAI_API_KEY`, ajoutez la clé au champ « value », puis cliquez sur « Add secret ».

Cliquez sur le bouton « x » pour fermer la fenêtre modale LLM, puis cliquez sur « Publish » pour enregistrer vos modifications.

## Serveur LLM personnalisé

Pour utiliser un serveur LLM personnalisé, configurez un endpoint de serveur compatible suivant le format d’OpenAI. Vous pouvez implémenter l’API Chat Completions (`/v1/chat/completions`) ou l’API Responses (`/v1/responses`).

Les deux endpoints doivent renvoyer des réponses au format SSE (Server-Sent Events) avec `Content-Type: text/event-stream`.

#### API Chat Completions

L’API Chat Completions utilise l’endpoint `/v1/chat/completions`.

Chaque segment doit être formaté comme `data: {json}\n\n` et le flux doit se terminer par `data: [DONE]\n\n`.

Voici un exemple d’implémentation de serveur :

```python
import json
import os
import fastapi
from fastapi.responses import StreamingResponse
from openai import AsyncOpenAI
import uvicorn
import logging
from dotenv import load_dotenv
from pydantic import BaseModel
from typing import List, Optional

# Load environment variables from .env file
load_dotenv()

# Retrieve API key from environment
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
if not OPENAI_API_KEY:
    raise ValueError("OPENAI_API_KEY not found in environment variables")

app = fastapi.FastAPI()
oai_client = AsyncOpenAI(api_key=OPENAI_API_KEY)

class Message(BaseModel):
    role: str
    content: str

class ChatCompletionRequest(BaseModel):
    messages: List[Message]
    model: str
    temperature: Optional[float] = 0.7
    max_tokens: Optional[int] = None
    stream: Optional[bool] = False
    user_id: Optional[str] = None

@app.post("/v1/chat/completions")
async def create_chat_completion(request: ChatCompletionRequest) -> StreamingResponse:
    oai_request = request.dict(exclude_none=True)
    if "user_id" in oai_request:
        oai_request["user"] = oai_request.pop("user_id")

    chat_completion_coroutine = await oai_client.chat.completions.create(**oai_request)

    async def event_stream():
        try:
            async for chunk in chat_completion_coroutine:
                # Convert the ChatCompletionChunk to a dictionary before JSON serialization
                chunk_dict = chunk.model_dump()
                yield f"data: {json.dumps(chunk_dict)}\n\n"
            yield "data: [DONE]\n\n"
        except Exception as e:
            logging.error("An error occurred: %s", str(e))
            yield f"data: {json.dumps({'error': 'Internal error occurred!'})}\n\n"

    return StreamingResponse(event_stream(), media_type="text/event-stream")

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8013)
```

#### API Responses

L’API Responses utilise l’endpoint `/v1/responses`.

Chaque segment doit être formaté comme `event: {type}\ndata: {json}\n\n` et le flux doit se terminer par `data: [DONE]\n\n`. Les événements minimaux requis sont :

* `response.output_text.delta` : pour diffuser le contenu textuel
* `response.completed` : pour signaler la fin

**`server.py`**

```python title="server.py"
import json
import os
import fastapi
from fastapi.responses import StreamingResponse
from openai import AsyncOpenAI
import uvicorn
import logging
from dotenv import load_dotenv
from pydantic import BaseModel
from typing import List, Optional

load_dotenv()

OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
if not OPENAI_API_KEY:
    raise ValueError("OPENAI_API_KEY not found in environment variables")

app = fastapi.FastAPI()
oai_client = AsyncOpenAI(api_key=OPENAI_API_KEY)

class InputMessage(BaseModel):
    role: str
    content: str

class ResponseCreateRequest(BaseModel):
    model: str
    input: List[InputMessage]
    instructions: Optional[str] = None
    temperature: Optional[float] = 0.7
    max_output_tokens: Optional[int] = None
    stream: Optional[bool] = False

@app.post("/v1/responses")
async def create_response(request: ResponseCreateRequest) -> StreamingResponse:
    input_messages = [{"role": msg.role, "content": msg.content} for msg in request.input]

    response_stream = await oai_client.responses.create(
        model=request.model,
        input=input_messages,
        instructions=request.instructions,
        temperature=request.temperature,
        max_output_tokens=request.max_output_tokens,
        stream=True
    )

    async def event_stream():
        try:
            async for event in response_stream:
                if event.type == "response.output_text.delta":
                    yield f"event: response.output_text.delta\ndata: {json.dumps({'type': 'response.output_text.delta', 'delta': event.delta})}\n\n"
                elif event.type == "response.completed":
                    yield f"event: response.completed\ndata: {json.dumps({'type': 'response.completed', 'response': {'id': event.response.id, 'status': 'completed'}})}\n\n"

            yield "data: [DONE]\n\n"

        except Exception as e:
            logging.error("An error occurred: %s", str(e))
            yield f"event: error\ndata: {json.dumps({'type': 'error', 'error': {'message': str(e)}})}\n\n"

    return StreamingResponse(event_stream(), media_type="text/event-stream")

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8013)

```

**`server.ts`**

```typescript title="server.ts"
import express, { Request, Response } from "express";
import OpenAI from "openai";

const app = express();
app.use(express.json());

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

interface InputMessage {
  role: string;
  content: string;
}

interface ResponseCreateRequest {
  model: string;
  input: InputMessage[];
  instructions?: string;
  temperature?: number;
  max_output_tokens?: number;
  stream?: boolean;
}

app.post("/v1/responses", async (req: Request, res: Response) => {
  const request = req.body as ResponseCreateRequest;

  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Connection", "keep-alive");

  try {
    const stream = await openai.responses.create({
      model: request.model,
      input: request.input,
      instructions: request.instructions,
      temperature: request.temperature ?? 0.7,
      max_output_tokens: request.max_output_tokens,
      stream: true,
    });

    for await (const event of stream) {
      if (event.type === "response.output_text.delta") {
        res.write(
          `event: response.output_text.delta\ndata: ${JSON.stringify({ type: "response.output_text.delta", delta: event.delta })}\n\n`
        );
      } else if (event.type === "response.completed") {
        res.write(
          `event: response.completed\ndata: ${JSON.stringify({ type: "response.completed", response: { id: event.response.id, status: "completed" } })}\n\n`
        );
      }
    }

    res.write("data: [DONE]\n\n");
    res.end();
  } catch (error) {
    console.error("An error occurred:", error);
    res.write(
      `event: error\ndata: ${JSON.stringify({ type: "error", error: { message: String(error) } })}\n\n`
    );
    res.end();
  }
});

app.listen(8013, () => console.log("Server running on port 8013"));
```

Exécutez ce code ou le code de votre propre serveur.

![](/docs/_fern-img/8032697eeac9a62bb084feb13ab05294df2fee44f14a97897f0b7c10ffee813c.webp)

### Configurer une URL publique pour votre serveur

Pour rendre votre serveur accessible, créez une URL publique à l’aide d’un outil de tunneling tel que ngrok :

```shell
ngrok http --url=<Your url>.ngrok.app 8013
```

![](/docs/_fern-img/16d139cc2889d46e026c8aa7578fcdcd8d228dbe0f4df245091ea6de2ceb345a.webp)

### Configurer CustomLLM d’ElevenLabs

Mettez ensuite à jour les paramètres de votre agent dans le Dashboard ElevenLabs afin de le diriger vers votre serveur LLM personnalisé.

![](/docs/_fern-img/d57d81e44dffb7e6f581a4eed449d887f6c365b8ceb4f4f0174bd7e2ffd62537.webp)

Dirigez l’URL de votre serveur vers l’endpoint ngrok et définissez « Limit token usage » sur 5000.

Vous pouvez désormais commencer à interagir avec votre agent à l’aide de votre propre serveur LLM.

## Optimisation pour les LLM à traitement lent

Si votre LLM personnalisé présente des temps de traitement lents, peut-être en raison d'un raisonnement agentique ou d'exigences de prétraitement, vous pouvez améliorer la fluidité des échanges en intégrant des **mots tampons** dans vos réponses en streaming. Cette technique aide à maintenir une prosodie naturelle pendant que votre LLM génère la réponse complète.

### Mots tampons

Lorsque votre LLM a besoin de plus de temps pour traiter la réponse complète, renvoyez une réponse initiale se terminant par `"... "` (des points de suspension suivis d'une espace). Cela permet au système Text to Speech de maintenir un flux naturel tout en gardant l'échange dynamique.
Cela crée des pauses naturelles qui s'enchaînent bien avec le contenu suivant, sur lequel le LLM peut raisonner plus longtemps. L'espace supplémentaire est essentielle pour éviter que le contenu suivant soit ajouté à « ... », ce qui pourrait entraîner des distorsions audio.

### Implémentation

Voici comment modifier votre serveur LLM personnalisé pour implémenter des mots tampons :

**`server.py`**

```python title="server.py"
@app.post("/v1/chat/completions")
async def create_chat_completion(request: ChatCompletionRequest) -> StreamingResponse:
    oai_request = request.dict(exclude_none=True)
    if "user_id" in oai_request:
        oai_request["user"] = oai_request.pop("user_id")

    async def event_stream():
        try:
            # Send initial buffer chunk while processing
            initial_chunk = {
                "id": "chatcmpl-buffer",
                "object": "chat.completion.chunk",
                "created": 1234567890,
                "model": request.model,
                "choices": [{
                    "delta": {"content": "Let me think about that... "},
                    "index": 0,
                    "finish_reason": None
                }]
            }
            yield f"data: {json.dumps(initial_chunk)}\n\n"

            # Process the actual LLM response
            chat_completion_coroutine = await oai_client.chat.completions.create(**oai_request)

            async for chunk in chat_completion_coroutine:
                chunk_dict = chunk.model_dump()
                yield f"data: {json.dumps(chunk_dict)}\n\n"
            yield "data: [DONE]\n\n"

        except Exception as e:
            logging.error("An error occurred: %s", str(e))
            yield f"data: {json.dumps({'error': 'Internal error occurred!'})}\n\n"

    return StreamingResponse(event_stream(), media_type="text/event-stream")

```

**`server.ts`**

```typescript title="server.ts"
app.post("/v1/chat/completions", async (req: Request, res: Response) => {
  const request = req.body as ChatCompletionRequest;
  const oaiRequest = { ...request };

  if (oaiRequest.user_id) {
    oaiRequest.user = oaiRequest.user_id;
    delete oaiRequest.user_id;
  }

  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Connection", "keep-alive");

  try {
    // Send initial buffer chunk while processing
    const initialChunk = {
      id: "chatcmpl-buffer",
      object: "chat.completion.chunk",
      created: Math.floor(Date.now() / 1000),
      model: request.model,
      choices: [
        {
          delta: { content: "Let me think about that... " },
          index: 0,
          finish_reason: null,
        },
      ],
    };
    res.write(`data: ${JSON.stringify(initialChunk)}\n\n`);

    // Process the actual LLM response
    const stream = await openai.chat.completions.create({
      ...oaiRequest,
      stream: true,
    });

    for await (const chunk of stream) {
      res.write(`data: ${JSON.stringify(chunk)}\n\n`);
    }

    res.write("data: [DONE]\n\n");
    res.end();
  } catch (error) {
    console.error("An error occurred:", error);
    res.write(`data: ${JSON.stringify({ error: "Internal error occurred!" })}\n\n`);
    res.end();
  }
});
```

## Intégration des outils système

Votre LLM personnalisé peut déclencher des [outils système](/docs/fr/eleven-agents/customization/tools/system-tools) pour contrôler le flux et l'état de la conversation. Ces outils sont automatiquement inclus dans le paramètre `tools` de vos requêtes de complétion de chat lorsqu'ils sont configurés dans votre agent.

### Fonctionnement des outils système

1. **Décision du LLM** : votre LLM personnalisé décide quand appeler ces outils selon le contexte de la conversation
2. **Réponse de l'outil** : le LLM répond avec des appels de fonction au format OpenAI standard
3. **Traitement côté backend** : ElevenLabs traite les appels d'outils et met à jour l'état de la conversation

Pour en savoir plus sur les outils système, consultez [notre guide](/docs/fr/eleven-agents/customization/tools/system-tools).

### Outils système disponibles

#### Terminer l'appel

**Objectif** : terminer automatiquement les conversations lorsque les conditions appropriées sont réunies.

**Conditions de déclenchement** : le LLM doit appeler cet outil lorsque :

* La tâche principale est terminée et l'utilisateur est satisfait
* La conversation atteint une conclusion naturelle, d'un commun accord
* L'utilisateur indique explicitement vouloir terminer la conversation

**Paramètres** :

* `reason` (chaîne, obligatoire) : la raison de la fin de l'appel
* `message` (chaîne, facultatif) : un message d'au revoir à envoyer à l'utilisateur avant de terminer l'appel

**Format d'appel de fonction** :

```json
{
  "type": "function",
  "function": {
    "name": "end_call",
    "arguments": "{\"reason\": \"Task completed successfully\", \"message\": \"Thank you for using our service. Have a great day!\"}"
  }
}
```

**Implémentation** : configurez cet outil comme outil système dans les paramètres de votre agent. Le LLM recevra des instructions détaillées sur le moment où appeler cette fonction.

En savoir plus : [outil de fin d'appel](/docs/fr/eleven-agents/customization/tools/system-tools/end-call)

#### Détection de la langue

**Objectif** : passer automatiquement à la langue détectée de l'utilisateur pendant les conversations.

**Conditions de déclenchement** : le LLM doit appeler cet outil lorsque :

* L'utilisateur parle dans une langue différente de la langue actuelle de la conversation
* L'utilisateur demande explicitement à changer de langue
* La conversation nécessite une prise en charge multilingue

**Paramètres** :

* `reason` (chaîne, obligatoire) : la raison du changement de langue
* `language` (chaîne, obligatoire) : le code de la langue vers laquelle passer, qui doit figurer dans la liste des langues prises en charge

**Format d'appel de fonction** :

```json
{
  "type": "function",
  "function": {
    "name": "language_detection",
    "arguments": "{\"reason\": \"User requested Spanish\", \"language\": \"es\"}"
  }
}
```

**Implémentation** : configurez les langues prises en charge dans les paramètres de l'agent et ajoutez l'outil système de détection de la langue. L'agent adaptera automatiquement sa voix et ses réponses aux langues détectées.

En savoir plus : [outil de détection de la langue](/docs/fr/eleven-agents/customization/tools/system-tools/language-detection)

#### Transfert d'agent

**Objectif** : transférer les conversations entre agents IA spécialisés selon les besoins de l'utilisateur.

**Conditions de déclenchement** : le LLM doit appeler cet outil lorsque :

* La demande de l'utilisateur nécessite des connaissances spécialisées ou des capacités d'agent différentes
* L'agent actuel ne peut pas traiter la demande de manière adéquate
* Le flux de la conversation indique qu'un autre type d'agent est nécessaire

**Paramètres** :

* `reason` (chaîne, facultatif) : la raison du transfert d'agent
* `agent_number` (entier, obligatoire) : le numéro, indexé à partir de zéro, de l'agent vers lequel transférer la conversation, selon les règles de transfert configurées

**Format d'appel de fonction** :

```json
{
  "type": "function",
  "function": {
    "name": "transfer_to_agent",
    "arguments": "{\"reason\": \"User needs billing support\", \"agent_number\": 0}"
  }
}
```

**Implémentation** : définissez des règles de transfert associant des conditions à des identifiants d'agent spécifiques. Configurez les agents vers lesquels l'agent actuel peut transférer la conversation. Les agents sont référencés par des numéros indexés à partir de zéro dans la configuration de transfert.

En savoir plus : [outil de transfert d'agent](/docs/fr/eleven-agents/customization/tools/system-tools/agent-transfer)

#### Transfert vers un intervenant humain

**Objectif** : transférer de manière fluide les conversations à des intervenants humains lorsque l'assistance IA ne suffit pas.

**Conditions de déclenchement** : le LLM doit appeler cet outil lorsque :

* Des problèmes complexes nécessitent un jugement humain
* L'utilisateur demande explicitement une assistance humaine
* L'IA atteint les limites de ses capacités pour la demande spécifique
* Des protocoles d'escalade sont déclenchés

**Paramètres** :

* `reason` (chaîne, facultatif) : la raison du transfert
* `transfer_number` (chaîne, obligatoire) : le numéro de téléphone vers lequel transférer l'appel, qui doit correspondre à l'un des numéros configurés
* `client_message` (chaîne, obligatoire) : message lu au client pendant l'attente du transfert
* `agent_message` (chaîne, obligatoire) : message destiné à l'intervenant humain qui reçoit l'appel

**Format d'appel de fonction** :

```json
{
  "type": "function",
  "function": {
    "name": "transfer_to_number",
    "arguments": "{\"reason\": \"Complex billing issue\", \"transfer_number\": \"+15551234567\", \"client_message\": \"I'm transferring you to a billing specialist who can help with your account.\", \"agent_message\": \"Customer has a complex billing dispute about order #12345 from last month.\"}"
  }
}
```

**Implémentation** : configurez les numéros de téléphone et les conditions de transfert. Définissez des messages pour le client et pour l'intervenant humain qui reçoit l'appel. Fonctionne avec Twilio et le trunking SIP.

En savoir plus : [outil de transfert vers un intervenant humain](/docs/fr/eleven-agents/customization/tools/system-tools/transfer-to-number)

#### Ignorer le tour

**Objectif** : permettre à l'agent de faire une pause et d'attendre la saisie de l'utilisateur sans parler.

**Conditions de déclenchement** : le LLM doit appeler cet outil lorsque :

* L'utilisateur indique avoir besoin d'un moment (« Donnez-moi une seconde », « Laissez-moi réfléchir »)
* L'utilisateur demande une pause dans le flux de la conversation
* L'agent détecte que l'utilisateur a besoin de temps pour traiter les informations

**Paramètres** :

* `reason` (chaîne, facultatif) : raison libre expliquant pourquoi la pause est nécessaire

**Format d'appel de fonction** :

```json
{
  "type": "function",
  "function": {
    "name": "skip_turn",
    "arguments": "{\"reason\": \"User requested time to think\"}"
  }
}
```

**Implémentation** : aucune configuration supplémentaire n'est nécessaire. L'outil indique simplement à l'agent de rester silencieux jusqu'à ce que l'utilisateur parle de nouveau.

En savoir plus : [outil pour ignorer le tour](/docs/fr/eleven-agents/customization/tools/system-tools/skip-turn)

#### Détection de messagerie vocale

**Paramètres** :

* `reason` (chaîne, obligatoire) : raison de la détection de messagerie vocale (par exemple, « message d’accueil automatisé détecté », « aucune réponse humaine »)

**Format d’appel de fonction** :

```json
{
  "type": "function",
  "function": {
    "name": "voicemail_detection",
    "arguments": "{\"reason\": \"Automated greeting detected with request to leave message\"}"
  }
}
```

En savoir plus : [outil de détection de messagerie vocale](/docs/fr/eleven-agents/customization/tools/system-tools/voicemail-detection)

### Exemple de requête avec des outils système

Lorsque des outils système sont configurés, votre LLM personnalisé reçoit des requêtes incluant les outils au format OpenAI standard :

```json
{
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant. You have access to system tools for managing conversations."
    },
    {
      "role": "user",
      "content": "I think we're done here, thanks for your help!"
    }
  ],
  "model": "your-custom-model",
  "temperature": 0.7,
  "max_tokens": 1000,
  "stream": true,
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "end_call",
        "description": "Call this function to end the current conversation when the main task has been completed...",
        "parameters": {
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "description": "The reason for the tool call."
            },
            "message": {
              "type": "string",
              "description": "A farewell message to send to the user along right before ending the call."
            }
          },
          "required": ["reason"]
        }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "language_detection",
        "description": "Change the conversation language when the user expresses a language preference explicitly...",
        "parameters": {
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "description": "The reason for the tool call."
            },
            "language": {
              "type": "string",
              "description": "The language to switch to. Must be one of language codes in tool description."
            }
          },
          "required": ["reason", "language"]
        }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "skip_turn",
        "description": "Skip a turn when the user explicitly indicates they need a moment to think...",
        "parameters": {
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "description": "Optional free-form reason explaining why the pause is needed."
            }
          },
          "required": []
        }
      }
    }
  ]
}
```

> **Note**
>
> Votre LLM personnalisé doit prendre en charge les appels de fonction pour utiliser les outils système. Assurez-vous que votre modèle peut générer
> des réponses d'appel de fonction correctes au format OpenAI.

# Fonctionnalités supplémentaires

#### Paramètres de LLM personnalisé

Vous pouvez transmettre des paramètres supplémentaires à votre implémentation de LLM personnalisé.

#### Python

#### Définir les paramètres supplémentaires

Créez un objet contenant vos paramètres personnalisés :

```python
from elevenlabs.conversational_ai.conversation import Conversation, ConversationInitiationData

extra_body_for_convai = {
    "UUID": "123e4567-e89b-12d3-a456-426614174000",
    "parameter-1": "value-1",
    "parameter-2": "value-2",
}

config = ConversationInitiationData(
    extra_body=extra_body_for_convai,
)
```

#### Mettre à jour l'implémentation du LLM

Modifiez votre code de LLM personnalisé pour gérer les paramètres supplémentaires :

```python
import json
import os
import fastapi
from fastapi.responses import StreamingResponse
from fastapi import Request
from openai import AsyncOpenAI
import uvicorn
import logging
from dotenv import load_dotenv
from pydantic import BaseModel
from typing import List, Optional

# Load environment variables from .env file
load_dotenv()

# Retrieve API key from environment
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
if not OPENAI_API_KEY:
    raise ValueError("OPENAI_API_KEY not found in environment variables")

app = fastapi.FastAPI()
oai_client = AsyncOpenAI(api_key=OPENAI_API_KEY)

class Message(BaseModel):
    role: str
    content: str

class ChatCompletionRequest(BaseModel):
    messages: List[Message]
    model: str
    temperature: Optional[float] = 0.7
    max_tokens: Optional[int] = None
    stream: Optional[bool] = False
    user_id: Optional[str] = None
    elevenlabs_extra_body: Optional[dict] = None

@app.post("/v1/chat/completions")
async def create_chat_completion(request: ChatCompletionRequest) -> StreamingResponse:
    oai_request = request.dict(exclude_none=True)
    print(oai_request)
    if "user_id" in oai_request:
        oai_request["user"] = oai_request.pop("user_id")

    if "elevenlabs_extra_body" in oai_request:
        oai_request.pop("elevenlabs_extra_body")

    chat_completion_coroutine = await oai_client.chat.completions.create(**oai_request)

    async def event_stream():
        try:
            async for chunk in chat_completion_coroutine:
                chunk_dict = chunk.model_dump()
                yield f"data: {json.dumps(chunk_dict)}\n\n"
            yield "data: [DONE]\n\n"
        except Exception as e:
            logging.error("An error occurred: %s", str(e))
            yield f"data: {json.dumps({'error': 'Internal error occurred!'})}\n\n"

    return StreamingResponse(event_stream(), media_type="text/event-stream")

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8013)
```

### Exemple de requête

Avec cette configuration de message personnalisé, votre LLM reçoit des requêtes au format suivant :

```json
{
  "messages": [
    {
      "role": "system",
      "content": "\n  <Redacted>"
    },
    {
      "role": "assistant",
      "content": "Hey I'm currently unavailable."
    },
    {
      "role": "user",
      "content": "Hey, who are you?"
    }
  ],
  "model": "gpt-4o",
  "temperature": 0.5,
  "max_tokens": 5000,
  "stream": true,
  "elevenlabs_extra_body": {
    "UUID": "123e4567-e89b-12d3-a456-426614174000",
    "parameter-1": "value-1",
    "parameter-2": "value-2"
  }
}
```