lib: add UnsupportedArchitectureException for triaged error message

This commit is contained in:
Han Yin 2025-08-06 16:18:50 -07:00
parent 173c4c61a4
commit 518d042e24
4 changed files with 31 additions and 19 deletions

View File

@ -80,7 +80,7 @@ class StubInferenceEngine : InferenceEngine {
// If coroutine is cancelled, propagate cancellation // If coroutine is cancelled, propagate cancellation
throw e throw e
} catch (e: Exception) { } catch (e: Exception) {
_state.value = State.Error(e.message ?: "Unknown error during model loading") _state.value = State.Error(e)
} }
} }
@ -107,7 +107,7 @@ class StubInferenceEngine : InferenceEngine {
// If coroutine is cancelled, propagate cancellation // If coroutine is cancelled, propagate cancellation
throw e throw e
} catch (e: Exception) { } catch (e: Exception) {
_state.value = State.Error(e.message ?: "Unknown error during model loading") _state.value = State.Error(e)
} }
} }
@ -142,13 +142,13 @@ class StubInferenceEngine : InferenceEngine {
_state.value = State.ModelReady _state.value = State.ModelReady
throw e throw e
} catch (e: Exception) { } catch (e: Exception) {
_state.value = State.Error(e.message ?: "Unknown error during generation") _state.value = State.Error(e)
throw e throw e
} }
}.catch { e -> }.catch { e ->
// If it's not a cancellation, update state to error // If it's not a cancellation, update state to error
if (e !is CancellationException) { if (e !is CancellationException) {
_state.value = State.Error(e.message ?: "Unknown error during generation") _state.value = State.Error(Exception(e))
} }
throw e throw e
} }
@ -198,7 +198,7 @@ class StubInferenceEngine : InferenceEngine {
_state.value = State.ModelReady _state.value = State.ModelReady
throw e throw e
} catch (e: Exception) { } catch (e: Exception) {
_state.value = State.Error(e.message ?: "Unknown error during benchmarking") _state.value = State.Error(e)
"Error: ${e.message}" "Error: ${e.message}"
} }
} }

View File

@ -1,6 +1,7 @@
package com.example.llama.ui.screens package com.example.llama.ui.screens
import android.llama.cpp.InferenceEngine.State import android.llama.cpp.InferenceEngine.State
import android.llama.cpp.UnsupportedArchitectureException
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically import androidx.compose.animation.expandVertically
@ -112,7 +113,7 @@ fun ModelLoadingScreen(
// Check if we're in a loading state // Check if we're in a loading state
val isLoading = engineState !is State.Initialized && engineState !is State.ModelReady val isLoading = engineState !is State.Initialized && engineState !is State.ModelReady
val errorMessage = (engineState as? State.Error)?.errorMessage val exception = (engineState as? State.Error)?.exception
// Handle back navigation requests // Handle back navigation requests
BackHandler { BackHandler {
@ -331,7 +332,7 @@ fun ModelLoadingScreen(
} }
}, },
modifier = Modifier.fillMaxWidth().height(56.dp), modifier = Modifier.fillMaxWidth().height(56.dp),
colors = if (errorMessage != null) colors = if (exception != null)
ButtonDefaults.buttonColors( ButtonDefaults.buttonColors(
disabledContainerColor = MaterialTheme.colorScheme.errorContainer.copy(alpha = 0.3f), disabledContainerColor = MaterialTheme.colorScheme.errorContainer.copy(alpha = 0.3f),
disabledContentColor = MaterialTheme.colorScheme.onErrorContainer.copy(alpha = 0.7f) disabledContentColor = MaterialTheme.colorScheme.onErrorContainer.copy(alpha = 0.7f)
@ -340,15 +341,21 @@ fun ModelLoadingScreen(
(!useSystemPrompt || hasActiveSystemPrompt) (!useSystemPrompt || hasActiveSystemPrompt)
) { ) {
when { when {
errorMessage != null -> { exception != null -> {
val message = if (exception is UnsupportedArchitectureException) {
"Unsupported architecture: ${selectedModel?.metadata?.architecture?.architecture}"
} else {
exception.message ?: "Unknown error"
}
Icon( Icon(
imageVector = Icons.Default.Error, imageVector = Icons.Default.Error,
contentDescription = errorMessage, contentDescription = message,
tint = MaterialTheme.colorScheme.error tint = MaterialTheme.colorScheme.error
) )
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
Text( Text(
text = errorMessage, text = message,
color = MaterialTheme.colorScheme.onErrorContainer color = MaterialTheme.colorScheme.onErrorContainer
) )
} }

View File

@ -15,6 +15,8 @@ interface InferenceEngine {
/** /**
* Load a model from the given path. * Load a model from the given path.
*
* @throws UnsupportedArchitectureException if model architecture not supported
*/ */
suspend fun loadModel(pathToModel: String) suspend fun loadModel(pathToModel: String)
@ -61,7 +63,7 @@ interface InferenceEngine {
object Generating : State() object Generating : State()
data class Error(val errorMessage: String = "") : State() data class Error(val exception: Exception) : State()
} }
companion object { companion object {
@ -81,3 +83,5 @@ val State.isModelLoaded: Boolean
this !is State.Initialized && this !is State.Initialized &&
this !is State.LoadingModel && this !is State.LoadingModel &&
this !is State.UnloadingModel this !is State.UnloadingModel
class UnsupportedArchitectureException : Exception()

View File

@ -2,6 +2,7 @@ package android.llama.cpp.internal
import android.llama.cpp.InferenceEngine import android.llama.cpp.InferenceEngine
import android.llama.cpp.LLamaTier import android.llama.cpp.LLamaTier
import android.llama.cpp.UnsupportedArchitectureException
import android.util.Log import android.util.Log
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -143,7 +144,7 @@ internal class InferenceEngineImpl private constructor(
_state.value = InferenceEngine.State.LoadingModel _state.value = InferenceEngine.State.LoadingModel
load(pathToModel).let { load(pathToModel).let {
// TODO-han.yin: find a better way to pass other error codes // TODO-han.yin: find a better way to pass other error codes
if (it != 0) throw IOException("Unsupported architecture") if (it != 0) throw UnsupportedArchitectureException()
} }
prepare().let { prepare().let {
if (it != 0) throw IOException("Failed to prepare resources") if (it != 0) throw IOException("Failed to prepare resources")
@ -152,9 +153,8 @@ internal class InferenceEngineImpl private constructor(
_readyForSystemPrompt = true _readyForSystemPrompt = true
_state.value = InferenceEngine.State.ModelReady _state.value = InferenceEngine.State.ModelReady
} catch (e: Exception) { } catch (e: Exception) {
val msg = e.message ?: "Unknown error" Log.e(TAG, (e.message ?: "Error loading model") + "\n" + pathToModel, e)
Log.e(TAG, msg + "\n" + pathToModel, e) _state.value = InferenceEngine.State.Error(e)
_state.value = InferenceEngine.State.Error(msg)
throw e throw e
} }
} }
@ -177,9 +177,10 @@ internal class InferenceEngineImpl private constructor(
_state.value = InferenceEngine.State.ProcessingSystemPrompt _state.value = InferenceEngine.State.ProcessingSystemPrompt
processSystemPrompt(prompt).let { result -> processSystemPrompt(prompt).let { result ->
if (result != 0) { if (result != 0) {
val errorMessage = "Failed to process system prompt: $result" RuntimeException("Failed to process system prompt: $result").also {
_state.value = InferenceEngine.State.Error(errorMessage) _state.value = InferenceEngine.State.Error(it)
throw IllegalStateException(errorMessage) throw it
}
} }
} }
Log.i(TAG, "System prompt processed! Awaiting user prompt...") Log.i(TAG, "System prompt processed! Awaiting user prompt...")
@ -225,7 +226,7 @@ internal class InferenceEngineImpl private constructor(
throw e throw e
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Error during generation!", e) Log.e(TAG, "Error during generation!", e)
_state.value = InferenceEngine.State.Error(e.message ?: "Unknown error") _state.value = InferenceEngine.State.Error(e)
throw e throw e
} }
}.flowOn(llamaDispatcher) }.flowOn(llamaDispatcher)