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

# Zintegruj własny model

> **Note**
>
> Custom LLM pozwala połączyć rozmowy z własnym LLM przez zewnętrzny endpoint.
> ElevenLabs obsługuje też [natywnie zintegrowane LLM](/docs/pl/eleven-agents/customization/llm)

**Custom LLMs** pozwala użyć własnego klucza API OpenAI lub uruchomić całkowicie własny serwer LLM.

Custom LLM nie może być używany z [modelem Realtime](/docs/pl/eleven-agents/customization/realtime-models).

## Omówienie

Domyślnie używamy własnych wewnętrznych danych uwierzytelniających do popularnych modeli, takich jak OpenAI. Aby użyć własnego serwera LLM, musi on obsługiwać jedną z poniższych struktur żądań i odpowiedzi zgodnych z OpenAI:

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

> **Info**
>
> Responses API to nowszy format API OpenAI, który obsługuje dodatkowe funkcje. Oba formaty API
> są w pełni obsługiwane w integracji z Custom LLM.

Poniższe przewodniki obejmują oba zastosowania:

1. **Użyj własnego klucza OpenAI**: Użyj własnego klucza API OpenAI na naszej platformie.
2. **Własny serwer LLM**: Hostuj i połącz własną implementację serwera LLM.

Dowiesz się, jak:

* Zapisać klucz API OpenAI w ElevenLabs
* Hostować serwer odwzorowujący endpoint [Chat Completions](https://platform.openai.com/docs/api-reference/chat/create) lub [Responses](https://platform.openai.com/docs/api-reference/responses/create) OpenAI
* Skierować ElevenLabs do własnego endpointu
* W razie potrzeby przekazywać dodatkowe parametry do LLM

## Podsumowanie rozumowania

Endpoint musi zwracać rozumowanie oddzielnie od końcowej odpowiedzi. ElevenLabs nie generuje rozumowania na podstawie końcowej odpowiedzi.

Aby żądać rozumowania z obsługiwanego endpointu, włącz **Reasoning summary** w ustawieniach LLM agenta lub ustaw `enable_reasoning_summary` przez API.

### Zwracanie rozumowania

Użyj formatu zgodnego z endpointem:

#### Chat Completions API

Strumieniuj rozumowanie w polu `reasoning` lub `reasoning_content` każdego delta odpowiedzi.

W przypadku endpointów zgodnych z Gemini ElevenLabs żąda przemyśleń za pomocą
`google.thinking_config.include_thoughts` i odczytuje treść oznaczoną przez
`extra_content.google.thought`.

#### Responses API

Zwróć element wyjściowy `reasoning` z tekstem podsumowania w polu `summary`.

Przy skonfigurowanym wysiłku rozumowania i włączonym Reasoning summary ElevenLabs ustawia
`reasoning.summary` na `auto` w żądaniu.

Informacje o przechowywaniu, dostarczaniu i ograniczeniach znajdziesz w [Reasoning summary](/docs/pl/eleven-agents/customization/llm#reasoning-summary).

## Używanie własnego klucza OpenAI

Aby zintegrować własny klucz OpenAI, zaktualizuj ustawienia agenta w panelu ElevenLabs, aby wskazywały własny serwer LLM, i utwórz sekret zawierający `OPENAI_API_KEY`:

W ustawieniach agenta w panelu ElevenLabs wybierz „Custom LLM” z menu rozwijanego „LLM” po prawej stronie.

![Dodaj sekret](/docs/_fern-img/83c62b81ef04860b9d884e28e1b4ac7bbceaa6e481e72e15ae82a76ec9bbad8e.webp)

Kliknij pole pod „LLM” i przewiń w dół, aby wybrać „Custom LLM”.

Wpisz adres URL serwera i ID modelu własnego serwera LLM.

![Wpisz adres URL](/docs/_fern-img/85ed8154868096a5c8de4c5e1c09d83c861069ea098c7643a960cac1736b67cb.webp)

Kliknij menu rozwijane pod „API key” i wybierz „Create new secret”. Nazwij klucz `OPENAI_API_KEY`, dodaj go w polu „value”, a następnie kliknij „Add secret”.

Kliknij przycisk „x”, aby zamknąć okno LLM, a następnie kliknij „Publish”, aby zapisać zmiany.

## Własny serwer LLM

Aby użyć własnego serwera LLM, skonfiguruj zgodny endpoint serwera w stylu OpenAI. Możesz zaimplementować Chat Completions API (`/v1/chat/completions`) albo Responses API (`/v1/responses`).

Oba endpointy muszą zwracać odpowiedzi w formacie SSE (Server-Sent Events) z `Content-Type: text/event-stream`.

#### Chat Completions API

Chat Completions API używa endpointu `/v1/chat/completions`.

Każdy fragment musi mieć format `data: {json}\n\n`, a strumień musi kończyć się `data: [DONE]\n\n`.

Oto przykładowa implementacja serwera:

```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)
```

#### Responses API

Responses API używa endpointu `/v1/responses`.

Każdy fragment musi mieć format `event: {type}\ndata: {json}\n\n`, a strumień musi kończyć się `data: [DONE]\n\n`. Minimalne wymagane zdarzenia to:

* `response.output_text.delta` — do strumieniowania treści tekstowej
* `response.completed` — do sygnalizowania zakończenia

**`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"));
```

Uruchom ten kod lub własny kod serwera.

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

### Konfigurowanie publicznego adresu URL serwera

Aby udostępnić serwer, utwórz publiczny adres URL za pomocą narzędzia do tunelowania, takiego jak ngrok:

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

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

### Konfigurowanie CustomLLM w ElevenLabs

Następnie zaktualizuj ustawienia agenta w panelu ElevenLabs, aby wskazywały własny serwer LLM.

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

Ustaw adres URL serwera na endpoint ngrok i wartość „Limit token usage” na 5000.

Możesz teraz rozpocząć interakcję z agentem korzystającym z własnego serwera LLM.

## Optymalizacja dla wolno działających LLM-ów

Jeśli twój własny LLM działa wolno (na przykład ze względu na rozumowanie agentowe lub wymagania dotyczące wstępnego przetwarzania), możesz poprawić płynność rozmowy, stosując **słowa buforujące** w odpowiedziach strumieniowanych. Ta technika pomaga zachować naturalną prozodię mowy, gdy LLM generuje pełną odpowiedź.

### Słowa buforujące

Gdy LLM potrzebuje więcej czasu na przetworzenie pełnej odpowiedzi, zwróć początkową odpowiedź kończącą się `"... "` (wielokropkiem ze spacją). Dzięki temu system Text to Speech może zachować naturalny rytm, a rozmowa pozostaje dynamiczna.
Tworzy to naturalne pauzy, które płynnie przechodzą w kolejne treści, nad którymi LLM może dłużej się zastanawiać. Dodatkowa spacja jest kluczowa, aby kolejne treści nie zostały dołączone do „...”, co może powodować zniekształcenia dźwięku.

### Implementacja

Tak zmodyfikujesz serwer własnego LLM-a, aby stosował słowa buforujące:

**`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();
  }
});
```

## Integracja narzędzi systemowych

Twój własny LLM może uruchamiać [narzędzia systemowe](/docs/pl/eleven-agents/customization/tools/system-tools), aby kontrolować przebieg i stan rozmowy. Po skonfigurowaniu w agencie narzędzia te są automatycznie uwzględniane w parametrze `tools` żądań ukończenia czatu.

### Jak działają narzędzia systemowe

1. **Decyzja LLM-a**: Twój własny LLM decyduje, kiedy wywołać te narzędzia na podstawie kontekstu rozmowy
2. **Odpowiedź narzędzia**: LLM odpowiada wywołaniami funkcji w standardowym formacie OpenAI
3. **Przetwarzanie backendowe**: ElevenLabs przetwarza wywołania narzędzi i aktualizuje stan rozmowy

Więcej informacji o narzędziach systemowych znajdziesz w [naszym przewodniku](/docs/pl/eleven-agents/customization/tools/system-tools)

### Dostępne narzędzia systemowe

#### Zakończenie rozmowy

**Cel**: Automatyczne kończenie rozmów, gdy zostaną spełnione odpowiednie warunki.

**Warunki uruchomienia**: LLM powinien wywołać to narzędzie, gdy:

* Główne zadanie zostało ukończone, a użytkownik jest zadowolony
* Rozmowa dobiegła naturalnego końca za obopólną zgodą
* Użytkownik wyraźnie wskazuje, że chce zakończyć rozmowę

**Parametry**:

* `reason` (ciąg znaków, wymagany): Powód zakończenia rozmowy
* `message` (ciąg znaków, opcjonalny): Wiadomość pożegnalna wysyłana użytkownikowi przed zakończeniem rozmowy

**Format wywołania funkcji**:

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

**Implementacja**: Skonfiguruj jako narzędzie systemowe w ustawieniach agenta. LLM otrzyma szczegółowe instrukcje, kiedy wywołać tę funkcję.

Dowiedz się więcej: [Narzędzie zakończenia rozmowy](/docs/pl/eleven-agents/customization/tools/system-tools/end-call)

#### Wykrywanie języka

**Cel**: Automatyczne przełączanie na wykryty język użytkownika podczas rozmów.

**Warunki uruchomienia**: LLM powinien wywołać to narzędzie, gdy:

* Użytkownik mówi w języku innym niż obecny język rozmowy
* Użytkownik wyraźnie prosi o zmianę języka
* Rozmowa wymaga obsługi wielu języków

**Parametry**:

* `reason` (ciąg znaków, wymagany): Powód zmiany języka
* `language` (ciąg znaków, wymagany): Kod języka, na który należy przełączyć (musi być na liście obsługiwanych języków)

**Format wywołania funkcji**:

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

**Implementacja**: Skonfiguruj obsługiwane języki w ustawieniach agenta i dodaj narzędzie systemowe wykrywania języka. Agent automatycznie dopasuje głos i odpowiedzi do wykrytego języka.

Dowiedz się więcej: [Narzędzie wykrywania języka](/docs/pl/eleven-agents/customization/tools/system-tools/language-detection)

#### Przekazanie do agenta

**Cel**: Przekazywanie rozmów między wyspecjalizowanymi agentami AI zależnie od potrzeb użytkownika.

**Warunki uruchomienia**: LLM powinien wywołać to narzędzie, gdy:

* Prośba użytkownika wymaga specjalistycznej wiedzy lub innych możliwości agenta
* Obecny agent nie może odpowiednio obsłużyć zapytania
* Przebieg rozmowy wskazuje na potrzebę innego typu agenta

**Parametry**:

* `reason` (ciąg znaków, opcjonalny): Powód przekazania do agenta
* `agent_number` (liczba całkowita, wymagany): Numer indeksowany od zera agenta, do którego należy przekazać rozmowę (na podstawie skonfigurowanych reguł przekazywania)

**Format wywołania funkcji**:

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

**Implementacja**: Zdefiniuj reguły przekazywania, które mapują warunki na konkretne identyfikatory agentów. Skonfiguruj, do których agentów może przekazywać rozmowy bieżący agent. Agenci są oznaczani numerami indeksowanymi od zera w konfiguracji przekazywania.

Dowiedz się więcej: [Narzędzie przekazywania do agenta](/docs/pl/eleven-agents/customization/tools/system-tools/agent-transfer)

#### Przekazanie do człowieka

**Cel**: Płynne przekazywanie rozmów do ludzkich operatorów, gdy pomoc AI nie wystarcza.

**Warunki uruchomienia**: LLM powinien wywołać to narzędzie, gdy:

* Występują złożone problemy wymagające ludzkiego osądu
* Użytkownik wyraźnie prosi o pomoc człowieka
* AI osiąga granice swoich możliwości w danym zapytaniu
* Zostają uruchomione procedury eskalacji

**Parametry**:

* `reason` (ciąg znaków, opcjonalny): Powód przekazania
* `transfer_number` (ciąg znaków, wymagany): Numer telefonu, na który należy przekazać rozmowę (musi odpowiadać skonfigurowanym numerom)
* `client_message` (ciąg znaków, wymagany): Komunikat odczytywany klientowi podczas oczekiwania na przekazanie
* `agent_message` (ciąg znaków, wymagany): Komunikat dla ludzkiego operatora odbierającego rozmowę

**Format wywołania funkcji**:

```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.\"}"
  }
}
```

**Implementacja**: Skonfiguruj numery telefonów i warunki przekazywania. Zdefiniuj komunikaty zarówno dla klienta, jak i odbierającego rozmowę operatora. Działa z Twilio i SIP trunkingiem.

Dowiedz się więcej: [Narzędzie przekazywania do człowieka](/docs/pl/eleven-agents/customization/tools/system-tools/transfer-to-number)

#### Pomiń turę

**Cel**: Pozwala agentowi zrobić pauzę i czekać na dane od użytkownika bez mówienia.

**Warunki uruchomienia**: LLM powinien wywołać to narzędzie, gdy:

* Użytkownik wskazuje, że potrzebuje chwili („Daj mi sekundę”, „Muszę pomyśleć”)
* Użytkownik prosi o przerwę w rozmowie
* Agent wykrywa, że użytkownik potrzebuje czasu na przetworzenie informacji

**Parametry**:

* `reason` (ciąg znaków, opcjonalny): Dowolny powód wyjaśniający, dlaczego potrzebna jest pauza

**Format wywołania funkcji**:

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

**Implementacja**: Nie wymaga dodatkowej konfiguracji. Narzędzie po prostu sygnalizuje agentowi, aby zachował ciszę, dopóki użytkownik nie odezwie się ponownie.

Dowiedz się więcej: [Narzędzie pomijania tury](/docs/pl/eleven-agents/customization/tools/system-tools/skip-turn)

#### Wykrywanie poczty głosowej

**Parametry**:

* `reason` (string, wymagany): Powód wykrycia poczty głosowej (np. „wykryto automatyczne powitanie”, „brak odpowiedzi człowieka”)

**Format wywołania funkcji**:

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

Dowiedz się więcej: [Narzędzie wykrywania poczty głosowej](/docs/pl/eleven-agents/customization/tools/system-tools/voicemail-detection)

### Przykładowe żądanie z narzędziami systemowymi

Gdy narzędzia systemowe są skonfigurowane, twój własny LLM otrzyma żądania zawierające narzędzia w standardowym formacie OpenAI:

```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**
>
> Twój własny LLM musi obsługiwać wywoływanie funkcji, aby korzystać z narzędzi systemowych. Upewnij się, że model może generować
> poprawne odpowiedzi wywołań funkcji w formacie OpenAI.

# Dodatkowe funkcje

#### Własne parametry LLM-a

Możesz przekazać dodatkowe parametry do implementacji własnego LLM-a.

#### Python

#### Zdefiniuj dodatkowe parametry

Utwórz obiekt zawierający własne parametry:

```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,
)
```

#### Zaktualizuj implementację LLM-a

Zmodyfikuj kod własnego LLM-a, aby obsługiwał dodatkowe parametry:

```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)
```

### Przykładowe żądanie

Przy tej konfiguracji własnych wiadomości twój LLM otrzyma żądania w tym formacie:

```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"
  }
}
```