Direct answer
A model-variant switch is a user preference and runtime lifecycle transition, not a file migration. Keep the selected variant separate from the bytes currently installed. When the user deliberately changes the choice, serialize the transition with active inference: persist the new choice, close the old engine safely, update the model configuration, then inspect whether the selected variant’s artifact exists and present the next state. Do not claim the new variant is ready merely because it is selected. A variant can be chosen but not downloaded, downloaded but not loaded, or loaded but not accepted for a specific task. Switching the selection should not delete the prior artifact unless the user separately requests cleanup. The local ModelManager.setVariant implementation is concrete evidence for this intent. It runs under a load mutex, returns early for the already-selected variant, saves a new preference, closes and clears the active engine, updates the ModelConfig reference, and refreshes the model state. It does not automatically delete the old variant file. ModelPreferences persists a per-app variant selection in SharedPreferences; the code comments state each Cove app has independent preferences. These project behaviors support documentation about an explicit setting transition, but not a claim that a particular variant is publicly available, supported on a named phone, or faster or more accurate. Do not confuse switching with runtime migration. A runtime migration changes the execution package or runtime revision while a given model artifact may remain the same; it requires its own compatibility and cache policy. Variant switching changes which configured model identity the user selects, often with different artifact metadata and file presence. It may lead to a new download requirement, but that should be an explicit state and action. Likewise, a model picker is primarily the UI for displaying choices. This guide focuses on the state transition after an explicit selection: engine ownership, preference persistence, selected artifact lookup, load/recovery, and preservation of the old file. State should be modeled as two related values: user-selected variant and active engine/artifact identity. If the user chooses a variant that is absent, show “selected; setup required” rather than “ready.” If the old engine is still processing, either let the current task finish or request a supported cancellation and wait for resource release before switching. Never mutate a shared config object while a native engine still uses it. Use a mutex or single owner for selection, load, release, and inference start. After process recreation, read the persisted selection and reconcile it with actual files; do not restore an in-memory READY state from a saved Boolean. Storage handling is part of the choice. Keep old and new variant artifacts distinguishable by stable identity. A user may switch back later, so retaining the old file can avoid repeating setup, but storage cleanup should remain a separate user-visible action. The current project has an E2B configured size and empty SHA-256, while E4B size and digest are explicitly placeholders; do not present those placeholders as verified storage requirements or a ready-to-download guarantee. Show known facts only, state when metadata is not verified, and let the transfer flow decide availability based on its actual configuration. Test the user action, not just the selector widget. Exercise same-variant no-op, switch while idle, switch during inference, selected target file present, target absent, load failure, process recreation after preference write, and rapid alternating choices. Verify only the latest accepted choice becomes selected, engine close happens before a new load, old artifacts remain until deliberate cleanup, and a missing candidate produces a repairable state. Since preferences are per app, verify that selecting a variant in one app does not silently change another app’s setting. Keep task content out of event logs; record variant IDs and lifecycle stages only. ### Procedure 1. Define stable variant identities and keep selected preference separate from installed-artifact and active-engine identity. Store each app’s preference in its intended namespace. 2. Route user selection through one lifecycle owner. Serialize selection, inference start, engine release, configuration change, file lookup, and reload; reject or defer overlapping transitions. 3. Persist the explicit choice and close the old engine before updating runtime configuration. Treat same-variant selection as a no-op and do not delete its existing artifact. 4. Check the selected artifact on disk and show a truthful state: setup needed, available to load, loading, ready for the tested path, or recoverable failure. Keep unverified size/hash metadata labeled accordingly. 5. Test interruption and switching back. Recreate the process after preference update, alternate choices rapidly, verify the actual selected file, and confirm the old variant remains until explicit cleanup. ### Acceptance criteria - The user-selected variant, installed artifact, and active engine are separate state values with a deterministic reconciliation rule. - A switch cannot race an active native engine; the old engine closes before the new configuration loads. - Missing or placeholder artifact metadata does not produce a ready, compatibility, or download-availability claim. - Prior variant bytes and user records remain untouched until the user explicitly invokes a separate cleanup action. ### Comparison | Operation | What changes | What must not be assumed | |---|---|---| | User variant switch | Selected model identity and possibly required artifact | That the target file exists or is ready | | Runtime migration | Runtime/package revision for an artifact | That a different model variant was selected | | Model-picker UI | Presentation and input for a choice | That the lifecycle transition completed successfully | ### FAQ **Does selecting a variant mean its model file is installed?** No. The preference records a choice; the app must check the exact artifact path and show setup-required state if it is absent. Loading is another separate stage. **Should switching variants delete the previous model?** Not automatically. A user may switch back, and deletion affects storage and recovery. Keep cleanup as a separate explicit action and preserve unrelated model files. **Is switching variants the same as upgrading LiteRT-LM?** No. Variant switching changes the selected model identity. A runtime upgrade changes the software that executes an artifact and needs a separate compatibility, cache, and rollback assessment. **Can two app modules share one selected-variant preference?** Only if that is an intentional product rule. The current project uses per-app preferences, so validate that one app’s selection does not silently change another app’s model choice. ### Primary sources
A user changes the selected variant while a session is active
A user deliberately selects a second model variant in app settings while the current inference engine is still loaded. The target artifact may or may not exist, and the user may later switch back. The app must keep preference, engine, and files consistent.
Decision
Serialize the explicit choice with engine lifecycle, release the old engine before reconfiguration, check the selected artifact, and keep old bytes until a separate cleanup action.
Expected outcome
The UI reports whether setup or loading is needed, no variant is falsely marked ready, and switching back remains possible while the prior file is retained.
Evidence to collect
The claim becomes reviewable only when the following evidence is attached to the same artifact and test run:
- Concurrent-switch test
- Old variant preservation policy
- New variant load result
A completed byte stream is not enough. Final placement, integrity, runtime load, and recovery must also pass.
Implementation workflow
- Separate choice from readiness. Represent selected variant, installed artifact identity, active engine identity, and task readiness independently. A persisted selection is not proof that the file exists or has loaded.
- Serialize the transition. Use one owner or mutex for inference start, release, selection, and load. If work is active, finish or cancel according to a documented policy and wait until native resources are released.
- Persist choice and release the old engine. Write the user’s per-app selection, close and clear the old engine, then update configuration. Same-variant selection should not reload, and the old model file should remain.
- Reconcile the target artifact. Search the configured storage locations for the exact selected identity. If absent, show setup required; if present, load and report each stage instead of showing ready from preference alone.
- Test recovery and scope. Simulate rapid choices, process recreation, load failure, and switching back. Verify preferences remain isolated per app and no old file or user record is removed by the selection action.
Keep each transition observable. A failure should identify the stage, artifact, runtime, and recovery action without logging private user content.
Acceptance criteria
- Selection persistence is separate from file existence, engine readiness, and task acceptance.
- Switching is serialized and releases the current engine before a different configuration loads.
- Unavailable or placeholder target metadata leads to an honest setup/recovery state, not a support claim.
- The old artifact remains until a separate user-visible cleanup action and per-app preferences stay scoped.
Decision table
| Dimension | Choice or evidence | How to interpret it |
|---|---|---|
| Variant choice | User preference transition | Changes the selected identity but does not install or load it. |
| Runtime migration | Execution stack transition | Changes runtime/package assumptions and needs separate migration evidence. |
| Picker screen | Choice presentation | Collects input but cannot prove lifecycle, file, or load completion. |
Failure patterns to prevent
- Switching during inference
- Using placeholder metadata
Also prevent silent fallback, unpinned artifacts, missing cancellation, and conclusions that combine unlike configurations. Store unsuccessful runs alongside successful ones.
Minimum reproducibility record
| Layer | Record |
|---|---|
| Device | Manufacturer, model, chipset, RAM class, operating-system build |
| Software | Application version and git commit |
| Model | Family, variant, revision, format, file length, hash, quantization |
| Runtime | Name, revision, requested backend, observed backend evidence |
| Workload | Fixture revision, input hash, prompt hash, output policy |
| Outcome | Completed, failed, cancelled, fallback, and privacy-safe diagnostics |
Release checklist
- ☐ The primary query is answered without an unsupported number.
- ☐ Every artifact and runtime is pinned.
- ☐ The representative task and failure policy are explicit.
- ☐ Lifecycle, cancellation, cleanup, and fallback are tested.
- ☐ User-content and network boundaries are documented.
- ☐ Result wording applies only to the recorded configuration.
- ☐ The page links to raw method or evidence when results are added.
Sources and related evidence
- Android SharedPreferences API
- LiteRT-LM Kotlin API lifecycle and close behavior
- Android Activity lifecycle
Chinese deployment and troubleshooting content is organized in the .奇连 AI 端侧专题.
Frequently asked questions
Does choosing a model variant mean it is already installed?
No. The preference stores a choice. Check the exact artifact path and show setup-required state when absent; loading and task readiness are separate checks.
Should the previous model be deleted during a switch?
Not automatically. The user may switch back, and deletion affects recovery. Keep cleanup separate and explicit, and protect other variants and user files.
Is a variant switch the same as a runtime upgrade?
No. One changes selected model identity; the other changes execution software. Runtime migration needs its own compatibility, cache invalidation, and rollback plan.
Should separate apps share one selected variant preference?
Only if product policy explicitly says so. The current repository uses per-app preferences, so test that one app selection does not silently affect another.