Фикс локальной LLM: таймаут 300с для LOCAL (инициализация движка ~17с + генерация), CPU-бэкенд без спекуляции (MTP виснет на CPU, GPU-делегат падает), диагностика chat()

Проверено на железе (HONOR FCP-N49): Gemma 4 E2B отвечает
«Здравствуйте! Чем я могу вам помочь с вашими очками Viewmate?»
(15 чанков, 61 символ, ~19с генерации).
This commit is contained in:
2026-08-23 04:38:02 +03:00
parent 7527cf8848
commit 75496dd35d
2 changed files with 54 additions and 10 deletions
@@ -112,13 +112,16 @@ class PhoneApp : Application() {
private var localLlm: LocalLlmClient? = null
/** Выбор модели LLM (персистится, [pw.binom.viewmate.phone.agent.LlmPrefs]). */
val llmPrefs = LlmPrefs(this)
private val _llmChoice = MutableStateFlow<LlmModelChoice>(llmPrefs.current())
val llmChoice: StateFlow<LlmModelChoice> = _llmChoice.asStateFlow()
// lazy: конструктор Application выполняется ДО attachBaseContext — контекст (и
// getSharedPreferences/getExternalFilesDir) доступен только после; ленивость —
// тот же паттерн, что у chatDao/downloadManager (иначе NPE при старте).
val llmPrefs: LlmPrefs by lazy { LlmPrefs(this) }
private val _llmChoice: MutableStateFlow<LlmModelChoice> by lazy { MutableStateFlow(llmPrefs.current()) }
val llmChoice: StateFlow<LlmModelChoice> by lazy { _llmChoice.asStateFlow() }
/** Состояние локальной модели Gemma 4 E2B для UI настроек. */
private val _localLlmState = MutableStateFlow<LocalLlmModelState>(initialLocalLlmState())
val localLlmState: StateFlow<LocalLlmModelState> = _localLlmState.asStateFlow()
private val _localLlmState: MutableStateFlow<LocalLlmModelState> by lazy { MutableStateFlow(initialLocalLlmState()) }
val localLlmState: StateFlow<LocalLlmModelState> by lazy { _localLlmState.asStateFlow() }
private var localLlmDownloadJob: Job? = null
private fun initialLocalLlmState(): LocalLlmModelState =
@@ -203,6 +206,10 @@ class PhoneApp : Application() {
chatDao = chatDao,
chat = { history, system -> llm.chat(history, system) },
toolkit = tk,
// Локальной модели нужно время: инициализация движка ~17с на
// CPU + генерация. Серверная укладывается в 30с, локальной
// даём 5 минут (иначе withTimeoutOrNull режет генерацию).
timeoutMs = if (choice == LlmModelChoice.LOCAL) 300_000L else 30_000L,
).also {
assistant = it
assistantChoice = choice