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

# Kotlin SDK

> **Info**
>
> Zobacz [opis ElevenAgents](/docs/pl/eleven-agents/overview), aby dowiedzieć się, jak działa
> ElevenAgents.

## Instalacja

Dodaj SDK ElevenLabs do projektu Androida, umieszczając poniższą zależność w pliku `build.gradle` na poziomie aplikacji:

**`build.gradle.kts`**

```kotlin build.gradle.kts
dependencies {
    // ElevenLabs Agents SDK (Android)
    implementation("io.elevenlabs:elevenlabs-android:<latest>")

    // Kotlin coroutines, AndroidX, etc., as needed by your app
}
```

> **Tip**
>
> Przykładową aplikację na Androida korzystającą z tego SDK znajdziesz
> [tutaj](https://github.com/elevenlabs/elevenlabs-android/tree/main/example-app)

## Wymagania

* Android API na poziomie 21 (Android 5.0) lub wyższym
* Uprawnienie do internetu dla wywołań API
* Uprawnienie do mikrofonu dla wejścia głosowego
* Konfiguracja zabezpieczeń sieciowych dla wywołań HTTPS

## Konfiguracja

### Konfiguracja manifestu

Dodaj wymagane uprawnienia do `AndroidManifest.xml`:

```xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
```

### Uprawnienia w czasie działania

W Androidzie 6.0 (API na poziomie 23) i nowszym musisz poprosić o uprawnienie do mikrofonu w czasie działania:

```kotlin
import android.Manifest
import android.content.pm.PackageManager
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat

private fun requestMicrophonePermission() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
        != PackageManager.PERMISSION_GRANTED) {

        if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) {
            // Show explanation to the user
            showPermissionExplanationDialog()
        } else {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.RECORD_AUDIO),
                MICROPHONE_PERMISSION_REQUEST_CODE
            )
        }
    }
}
```

## Użycie

Zainicjuj SDK ElevenLabs w klasie `Application` lub głównej aktywności:

Rozpocznij sesję rozmowy, przekazując:

* Agenta publicznego: przekaż `agentId`
* Agenta prywatnego: przekaż `conversationToken` udostępniony przez backend (nigdy nie ujawniaj klucza API klientowi).

```kotlin
import io.elevenlabs.ConversationClient
import io.elevenlabs.ConversationConfig
import io.elevenlabs.ConversationSession
import io.elevenlabs.ClientTool
import io.elevenlabs.ClientToolResult

// Start a public agent session (token generated for you)
val config = ConversationConfig(
    agentId = "<your_public_agent_id>", // OR conversationToken = "<token>"
    userId = "your-user-id",
    // Optional callbacks
    onConnect = { conversationId ->
        // Called when the conversation is connected and returns the conversation ID. You can access conversationId via session.getId() too
    },
    onMessage = { source, messageJson ->
        // Raw JSON messages from data channel; useful for logging/telemetry
    },
    onModeChange = { mode ->
        // "speaking" | "listening" — drive UI indicators
    },
    onStatusChange = { status ->
        // "connected" | "connecting" | "disconnected"
    },
    onCanSendFeedbackChange = { canSend ->
        // Enable/disable thumbs up/down buttons for feedback reporting
    },
    onUnhandledClientToolCall = { call ->
        // Agent requested a client tool not registered on the device
    },
    onVadScore = { score ->
        // Voice Activity Detection score, range from 0 to 1 where higher values indicate higher confidence of speech
    },
    onAudioAlignment = { alignment ->
        // Character-level timing data for synchronized text display
        val chars = alignment["chars"] as? List<*>
        val startTimes = alignment["char_start_times_ms"] as? List<*>
        val durations = alignment["char_durations_ms"] as? List<*>
        Log.d("ExampleApp", "Audio alignment: $chars")
    },
    // List of client tools the agent can invoke
    clientTools = mapOf(
        "logMessage" to object : ClientTool {
            override suspend fun execute(parameters: Map<String, Any>): ClientToolResult {
                val message = parameters["message"] as? String

                Log.d("ExampleApp", "[INFO] Client Tool Log: $message")
                return ClientToolResult.success("Message logged successfully")
            }
        }
    ),
)

// In an Activity context
val session: ConversationSession = ConversationClient.startSession(config, this)
```

Pamiętaj, że ElevenAgents wymaga dostępu do mikrofonu. Rozważ wyjaśnienie tego i poproszenie o uprawnienia w interfejsie aplikacji przed rozpoczęciem rozmowy, szczególnie w Androidzie 6.0+, gdzie wymagane są uprawnienia w czasie działania.

> **Note**
>
> Jeśli narzędzie ma na serwerze ustawione `expects_response=false`, zwróć `null` z `execute`,
> aby nie wysyłać wyniku narzędzia z powrotem do agenta.

## Agenci publiczni i prywatni

* **Agenci publiczni** (bez uwierzytelniania): Zainicjuj za pomocą `agentId` w `ConversationConfig`. SDK prosi ElevenLabs o token rozmowy bez potrzeby używania klucza API na urządzeniu.
* **Agenci prywatni** (z uwierzytelnianiem): Zainicjuj za pomocą `conversationToken` w `ConversationConfig`. Twój serwer prosi ElevenLabs o token rozmowy, używając klucza API ElevenLabs.

> **Error**
>
> Nigdy nie umieszczaj kluczy API w klientach. Łatwo je wyodrębnić i wykorzystać w złych celach.

## Narzędzia klienta

Zarejestruj narzędzia klienta, aby agent mógł wywoływać lokalne funkcje urządzenia.

```kotlin
val config = ConversationConfig(
    agentId = "<public_agent>",
    clientTools = mapOf(
        "logMessage" to object : io.elevenlabs.ClientTool {
            override suspend fun execute(parameters: Map<String, Any>): io.elevenlabs.ClientToolResult? {
                val message = parameters["message"] as? String ?: return io.elevenlabs.ClientToolResult.failure("Missing 'message'")

                android.util.Log.d("ClientTool", "Log: $message")
                return null // No response needed for fire-and-forget tools
            }
        }
    )
)
```

Gdy agent wywoła `client_tool_call`, SDK uruchomi pasujące narzędzie i odpowie za pomocą `client_tool_result`. Jeśli narzędzie nie jest zarejestrowane, wywoływane jest `onUnhandledClientToolCall`, a agent otrzymuje wynik błędu (jeśli oczekiwana jest odpowiedź).

### Przegląd callbacków

* **onConnect** - Wywoływany po ustanowieniu połączenia WebRTC. Zwraca identyfikator rozmowy.
* **onMessage** - Wywoływany po odebraniu nowej wiadomości. Mogą to być wstępne lub końcowe transkrypcje głosu użytkownika, odpowiedzi wygenerowane przez LLM albo komunikaty debugowania. Udostępnia źródło (`"ai"` lub `"user"`) oraz surową wiadomość JSON.
* **onModeChange** - Wywoływany przy zmianie trybu rozmowy. Przydaje się do wskazania, czy agent mówi (`"speaking"`), czy słucha (`"listening"`).
* **onStatusChange** - Wywoływany przy zmianie statusu rozmowy (`"connected"`, `"connecting"` lub `"disconnected"`).
* **onCanSendFeedbackChange** - Wywoływany przy zmianie możliwości wysłania opinii. Włącza/wyłącza przyciski opinii.
* **onUnhandledClientToolCall** - Wywoływany, gdy agent prosi o narzędzie klienta, które nie jest zarejestrowane na urządzeniu.
* **onVadScore** - Wywoływany przy zmianie wyniku wykrywania aktywności głosowej. Zakres od 0 do 1, gdzie wyższe wartości oznaczają większą pewność wykrycia mowy.
* **onAudioAlignment** - Wywoływany po otrzymaniu danych synchronizacji audio, które zawierają informacje o czasie na poziomie znaków dla mowy agenta.

> **Warning**
>
> Nie wszystkie zdarzenia klienta są domyślnie włączone dla agenta. Jeśli masz włączony callback, ale
> nie otrzymujesz zdarzeń, sprawdź, czy odpowiednie zdarzenie jest włączone dla agenta ElevenLabs.
> Możesz to zrobić na karcie „Advanced” w ustawieniach agenta w panelu ElevenLabs.

### Metody

#### startSession

Metoda `startSession` inicjuje połączenie WebRTC i zaczyna korzystać z mikrofonu, aby komunikować się z agentem ElevenLabs Agents.

##### Agenci publiczni

W przypadku agentów publicznych (czyli agentów bez włączonego uwierzytelniania) wymagany jest tylko `agentId`. ID agenta znajdziesz w [interfejsie ElevenLabs](https://elevenlabs.io/app/agents).

```kotlin
val session = ConversationClient.startSession(
    config = ConversationConfig(
        agentId = "your-agent-id"
    ),
    context = this
)
```

##### Agenci prywatni

W przypadku agentów prywatnych musisz przekazać `conversationToken` uzyskany z API ElevenLabs. Wygenerowanie tego tokenu wymaga klucza API ElevenLabs.

> **Tip**
>
> `conversationToken` jest ważny przez 10 minut.

```typescript maxLines=0
// Server-side token generation (Node.js example)

app.get("/conversation-token", yourAuthMiddleware, async (req, res) => {
  const response = await fetch(
    `https://api.elevenlabs.io/v1/convai/conversation/token?agent_id=${process.env.AGENT_ID}`,
    {
      headers: {
        // Requesting a conversation token requires your ElevenLabs API key
        // Do NOT expose your API key to the client!
        "xi-api-key": process.env.ELEVENLABS_API_KEY,
      },
    }
  );

  if (!response.ok) {
    return res.status(500).send("Failed to get conversation token");
  }

  const body = await response.json();
  res.send(body.token);
});
```

Następnie przekaż token do metody `startSession`. Pamiętaj, że w przypadku agentów prywatnych wymagany jest tylko `conversationToken`.

```kotlin

// Get conversation token from your server
val conversationToken = fetchConversationTokenFromServer()

// For private agents, pass in the conversation token
val session = ConversationClient.startSession(
    config = ConversationConfig(
        conversationToken = conversationToken
    ),
    context = this
)
```

Opcjonalnie możesz przekazać ID użytkownika, aby zidentyfikować go w rozmowie. Może to być twój własny identyfikator klienta. Zostanie on dołączony do danych inicjujących rozmowę wysyłanych na serwer.

```kotlin
val session = ConversationClient.startSession(
    config = ConversationConfig(
        agentId = "your-agent-id",
        userId = "your-user-id"
    ),
    context = this
)
```

#### endSession

Metoda ręcznego zakończenia rozmowy. Rozłącza i kończy rozmowę.

```kotlin
session.endSession()
```

#### sendUserMessage

Wyślij wiadomość tekstową do agenta podczas aktywnej rozmowy. Agent na nią odpowie.

```kotlin
session.sendUserMessage("Hello, how can you help me?")
```

#### sendContextualUpdate

Wysyła agentowi informacje kontekstowe, które nie wywołają odpowiedzi.

```kotlin
session.sendContextualUpdate(
    "User navigated to the profile page. Consider this for next response."
)
```

#### sendFeedback

Przekaż opinię o jakości rozmowy. Pomaga to poprawiać działanie agenta. Użyj `onCanSendFeedbackChange`, aby włączyć interfejs z kciukiem w górę/dół, gdy opinie są dozwolone.

```kotlin
// Positive feedback
session.sendFeedback(true)

// Negative feedback
session.sendFeedback(false)
```

#### sendUserActivity

Informuje agenta o aktywności użytkownika, by zapobiec przerwaniu. Przydaje się, gdy użytkownik aktywnie korzysta z aplikacji, a agent powinien przestać mówić, np. gdy użytkownik pisze na czacie.

Agent przestanie mówić na około 2 sekundy po otrzymaniu tego sygnału.

```kotlin
session.sendUserActivity()
```

#### getId

Pobierz ID rozmowy.

```kotlin
val conversationId = session.getId()
Log.d("Conversation", "Conversation ID: $conversationId")
// e.g., "conv_123"
```

#### Wyciszanie / włączanie mikrofonu

```kotlin
session.toggleMute()
session.setMicMuted(true)   // mute
session.setMicMuted(false)  // unmute
```

Obserwuj `session.isMuted`, aby zaktualizować etykietę interfejsu między „Wycisz” a „Włącz mikrofon”.

### Właściwości

#### status

Pobierz aktualny status rozmowy.

```kotlin
val status = session.status
Log.d("Conversation", "Current status: $status")
// Values: DISCONNECTED, CONNECTING, CONNECTED
```

## ProGuard / R8

Jeśli zmniejszasz lub zaciemniasz kod, upewnij się, że modele Gson i LiveKit zostają zachowane. Przykładowe reguły (dostosuj w razie potrzeby):

```proguard
-keep class io.elevenlabs.** { *; }
-keep class io.livekit.** { *; }
-keepattributes *Annotation*
```

## Rozwiązywanie problemów

* Upewnij się, że uprawnienie do mikrofonu zostało przyznane w czasie działania
* Jeśli ponowne połączenie się zawiesza, sprawdź, czy aplikacja wywołuje `session.endSession()` i czy przed ponownym połączeniem uruchamiasz nową instancję sesji
* W emulatorach sprawdź, czy działają ścieżki wejścia/wyjścia audio; urządzenia fizyczne są zwykle bardziej niezawodne

## Przykładowa implementacja

Przykładową implementację znajdziesz w przykładowej aplikacji w [repozytorium ElevenLabs Android SDK](https://github.com/elevenlabs/elevenlabs-android/tree/main/example-app). Aplikacja pokazuje:

* Łączenie/rozłączanie jednym stuknięciem
* Wskaźnik mówienia/słuchania
* Przyciski opinii z włączaniem/wyłączaniem w interfejsie
* Wskaźnik pisania przez `sendUserActivity()`
* Wiadomości kontekstowe i wiadomości użytkownika z pola wejściowego
* Przycisk wyciszania/włączania mikrofonu