fix(ui): show actual LLM error; skip API key dialog for on-device providers#202
Conversation
…vice providers - TagChipRow + SuggestionBottomSheet: replace hardcoded "Could not reach LLM" with state.llmError so users see the real reason (e.g. "Model is still downloading — try again in a few minutes" vs. a network failure) - LlmProviderSettings.onEditProvider: skip EditBuiltInProviderKeyDialog when the tapped provider's kind is ON_DEVICE — on-device providers have no credentials to configure so opening the key dialog was a no-op that confused users into thinking credentials were required Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Improves AI-related UI behavior by surfacing the actual LLM failure message to users (instead of a hardcoded generic error) and preventing the built-in API key dialog from opening for on-device LLM providers that don’t require credentials.
Changes:
- Display
llmError/state.llmErrordirectly in tag suggestion UI surfaces (TagChipRow,SuggestionBottomSheet). - Update provider-edit behavior so
ON_DEVICEproviders don’t trigger the built-in API key dialog in Settings → AI Providers.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/ui/components/tags/TagChipRow.kt | Shows the actual LLM error string when present instead of a hardcoded message. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/ui/components/tags/SuggestionBottomSheet.kt | Surfaces state.llmError to present provider-specific failure reasons to users. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/ui/components/settings/LlmProviderSettings.kt | Prevents opening the built-in provider key dialog when the selected provider is on-device. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
JVM Load Benchmark (Desktop)Synthetic in-memory benchmark measuring load performance for the desktop (JVM) app.
Flamegraphs (this PR)**Allocation** — object allocation pressure (JDBC/SQLite churn)Alloc flamegraph not available CPU — method-level hotspots by on-CPU time CPU flamegraph not available Top allocation hotspots (this PR)`37.4%` byte[]_[k] `7%` java.lang.String_[k] `6.9%` java.util.LinkedHashMap$Entry_[k] `6.7%` int[]_[k] `3.6%` java.lang.StringBuilder_[k]Top CPU hotspots (this PR)`94.2%` /usr/lib/x86_64-linux-gnu/libc.so.6 `2.2%` clock_nanosleep `1.4%` /tmp/sqlite-3.51.3.0-edfc9df3-6b2e-4f54-b6db-d689ad7ec397-libsqlitejdbc.so `0.6%` __libc_pwrite `0.3%` fsync |
…ditProvider null != ON_DEVICE is true in Kotlin, so an unrecognized provider id would silently open the API key dialog. == REMOTE is safe for null and future LlmProviderKind variants. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Android Load BenchmarkInstrumented benchmark on an API 30 x86_64 emulator — 500-page synthetic graph. Comparing Graph Load
Interactive Write Latency (during Phase 3)
SAF I/O Overhead (ContentProvider vs direct File read)Measures Binder IPC cost added by ContentResolver per readFile() call.
|
LlmProviderRow now accepts nullable onClick — passes null for ON_DEVICE providers so the row is non-clickable and the ChevronRight/Edit icon is hidden. Also fixes null-safe inversion in LlmProviderSettings.onEditProvider: use == REMOTE allowlist instead of != ON_DEVICE denylist. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
TagChipRowandSuggestionBottomSheetshowed a hardcoded "Could not reach LLM" regardless of the actual failure reason. Now surfacesstate.llmErrordirectly — users see "Model is still downloading — try again in a few minutes" instead of a generic network error message.EditBuiltInProviderKeyDialog. On-device providers need no credentials.onEditProvidernow checksregistry.find(id)?.kind != LlmProviderKind.ON_DEVICEbefore opening the dialog.Test plan
🤖 Generated with Claude Code