diff --git a/elevenlabs-sdk/src/main/java/io/elevenlabs/ConversationConfig.kt b/elevenlabs-sdk/src/main/java/io/elevenlabs/ConversationConfig.kt index 8bc1ff4..60e4f57 100644 --- a/elevenlabs-sdk/src/main/java/io/elevenlabs/ConversationConfig.kt +++ b/elevenlabs-sdk/src/main/java/io/elevenlabs/ConversationConfig.kt @@ -56,7 +56,8 @@ data class ConversationConfig( val onAgentToolResponse: ((toolName: String, toolCallId: String, toolType: String, isError: Boolean) -> Unit)? = null, val onConversationInitiationMetadata: ((conversationId: String, agentOutputFormat: String, userInputFormat: String) -> Unit)? = null, val onInterruption: ((eventId: Int) -> Unit)? = null, - val onDisconnect: ((details: DisconnectionDetails) -> Unit)? = null + val onDisconnect: ((details: DisconnectionDetails) -> Unit)? = null, + val onError: ((code: Int, message: String?) -> Unit)? = null ) { init { diff --git a/elevenlabs-sdk/src/main/java/io/elevenlabs/ConversationEventHandler.kt b/elevenlabs-sdk/src/main/java/io/elevenlabs/ConversationEventHandler.kt index a34376d..96af40c 100644 --- a/elevenlabs-sdk/src/main/java/io/elevenlabs/ConversationEventHandler.kt +++ b/elevenlabs-sdk/src/main/java/io/elevenlabs/ConversationEventHandler.kt @@ -29,7 +29,8 @@ class ConversationEventHandler( private val onAgentToolResponse: ((String, String, String, Boolean) -> Unit)? = null, private val onConversationInitiationMetadata: ((String, String, String) -> Unit)? = null, private val onInterruption: ((Int) -> Unit)? = null, - private val onEndCall: (suspend () -> Unit)? = null + private val onEndCall: (suspend () -> Unit)? = null, + private val onError: ((Int, String?) -> Unit)? = null ) { private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob()) @@ -67,6 +68,7 @@ class ConversationEventHandler( is ConversationEvent.Audio -> handleAudio(event) is ConversationEvent.ConversationInitiationMetadata -> handleConversationInitiationMetadata(event) is ConversationEvent.Interruption -> handleInterruption(event) + is ConversationEvent.ServerError -> handleServerError(event) } } catch (e: Exception) { @@ -214,6 +216,18 @@ class ConversationEventHandler( } } + /** + * Handle server error events + */ + private fun handleServerError(event: ConversationEvent.ServerError) { + Log.e("ConvEventHandler", "Server error (${event.code}): ${event.message ?: "unknown"}") + try { + onError?.invoke(event.code, event.message) + } catch (e: Exception) { + Log.e("ConvEventHandler", "Error in onError callback: ${e.message}", e) + } + } + /** * Handle client tool call events */ diff --git a/elevenlabs-sdk/src/main/java/io/elevenlabs/ConversationSessionImpl.kt b/elevenlabs-sdk/src/main/java/io/elevenlabs/ConversationSessionImpl.kt index 4d8a7d6..3ad8aa1 100644 --- a/elevenlabs-sdk/src/main/java/io/elevenlabs/ConversationSessionImpl.kt +++ b/elevenlabs-sdk/src/main/java/io/elevenlabs/ConversationSessionImpl.kt @@ -75,6 +75,9 @@ internal class ConversationSessionImpl( }, onEndCall = { endSession() + }, + onError = { code, message -> + try { config.onError?.invoke(code, message) } catch (_: Throwable) {} } ) diff --git a/elevenlabs-sdk/src/main/java/io/elevenlabs/models/ConversationEvent.kt b/elevenlabs-sdk/src/main/java/io/elevenlabs/models/ConversationEvent.kt index 2c84b49..2fcb77a 100644 --- a/elevenlabs-sdk/src/main/java/io/elevenlabs/models/ConversationEvent.kt +++ b/elevenlabs-sdk/src/main/java/io/elevenlabs/models/ConversationEvent.kt @@ -158,4 +158,15 @@ sealed class ConversationEvent { val eventId: Int ) : ConversationEvent() + /** + * Event representing a server error + * + * @param code The error code from the server + * @param message Optional error message describing the error + */ + data class ServerError( + val code: Int, + val message: String? + ) : ConversationEvent() + } \ No newline at end of file diff --git a/elevenlabs-sdk/src/main/java/io/elevenlabs/network/ConversationEventParser.kt b/elevenlabs-sdk/src/main/java/io/elevenlabs/network/ConversationEventParser.kt index 00e7f98..e707a05 100644 --- a/elevenlabs-sdk/src/main/java/io/elevenlabs/network/ConversationEventParser.kt +++ b/elevenlabs-sdk/src/main/java/io/elevenlabs/network/ConversationEventParser.kt @@ -48,6 +48,7 @@ object ConversationEventParser { "interruption" -> parseInterruption(jsonObject) "audio_alignment" -> parseAudioAlignment(jsonObject) "ping" -> parsePing(jsonObject) + "error" -> parseError(jsonObject) else -> { handleParsingError(json, IllegalArgumentException("Unknown event type: $eventType")) null @@ -313,6 +314,22 @@ object ConversationEventParser { ) } + /** + * Parse error event from server + * Supports both nested error_event and flat structure: + * {"type":"error","error_event":{"code":1011,"message":"..."}} + * {"type":"error","code":1011,"message":"..."} + */ + private fun parseError(jsonObject: JsonObject): ConversationEvent.ServerError { + val errorEvent = jsonObject.getAsJsonObject("error_event") + val code = errorEvent?.get("code")?.asInt + ?: jsonObject.get("code")?.asInt + ?: 1011 + val message = errorEvent?.get("message")?.let { if (it.isJsonNull) null else it.asString } + ?: jsonObject.get("message")?.let { if (it.isJsonNull) null else it.asString } + return ConversationEvent.ServerError(code = code, message = message) + } + /** * Handle parsing errors */ diff --git a/example-app/src/main/java/io/elevenlabs/example/viewmodels/ConversationViewModel.kt b/example-app/src/main/java/io/elevenlabs/example/viewmodels/ConversationViewModel.kt index 92c918c..6da6413 100644 --- a/example-app/src/main/java/io/elevenlabs/example/viewmodels/ConversationViewModel.kt +++ b/example-app/src/main/java/io/elevenlabs/example/viewmodels/ConversationViewModel.kt @@ -141,6 +141,10 @@ class ConversationViewModel(application: Application) : AndroidViewModel(applica }, onInterruption = { eventId -> Log.d("ConversationViewModel", "onInterruption: eventId=$eventId") + }, + onError = { code, message -> + Log.e("ConversationViewModel", "onError: Server error ($code): ${message ?: "unknown"}") + _errorMessage.postValue("Server error ($code): ${message ?: "unknown"}") } )