feat(cot): DeepSeek-R1 CoT accordion, multimodal EXIF downsampling, TokenStorage hardening & test suite - #3
Merged
Conversation
…tool, and reconcile 12GB RAM tier for Gemma 4 E4B
…, 3-tier scoping, and test suite - Decouple unclosed tag parsing from generation lifecycle to show 'Thinking (stopped)' on cancellation - Add 3-tier reasoning model scoping (catalog non-reasoning, catalog reasoning, and custom imported prefix check) - Add live-streaming token prefix anticipation and remember(message.id) state latching - Support arbitrary multi-block think sequences joined with dividers - Add sampled bitmap decode with 3-stream EXIF matrix transform and vision memory recycling - Route Hugging Face tokens strictly via TokenStorage on Dispatchers.IO and prevent passive session eviction - Add ParseThinkBlocksTest JUnit test suite with 42/42 unit tests passing and verify assembleDebug
…ning detection to confirmed prefix
- Replace ambiguous partial match latch with confirmed tag check (trimmed.startsWith('<think>'))
- Prevent custom models outputting <html> or XML tags from locking permanently into reasoning mode
- Remove ephemeral remember(message.id) latch from ChatBubble, avoiding state resets on LazyColumn item disposal
- Retain confirmed startsWith('<think>') prefix gating on message.content across live streaming and scroll recycling
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR hardens the offline Android client (
com.example.auralocalai) across five key audit areas:SharedPreferenceswrite-path bypasses and migration loops.ParseThinkBlocksTest; verified 42/42 unit tests passing and clean debug APK packaging.Key Changes
1. Reasoning CoT Accordion & Cancellation Lifecycle (
ChatScreen.kt)<think>tag presence from the generation lifecycle. When a user cancels mid-thought before</think>arrives, the header immediately transitions from"Thinking in progress…"to"Thinking (stopped)"in muted slate, collapsing by default rather than remaining frozen in live streaming mode.Nthought blocks joined with\n\n---\n\ndividers, aggregating interleaved responses cleanly intomainContent.qwen2.5-coder-3b,qwen-1.5b,gemma4-e2b): Strictly disabled from accordion parsing to prevent false-positive collapses when outputting XML/HTML tags or discussing prompt engineering.deepseek-1.5b,qwq): Enabled by default.message.content.trimStart().startsWith("<think>"). Ambiguous prefixes (e.g.<or<th) are treated as provisional plain text until confirmed, avoiding false-positive locks on<html>or code blocks.remember(message.id)latches inChatBubble. Reasoning classification evaluates against the immutablemessage.content, surviving off-screen item disposal and recomposition when users scroll during long generations.modelIdevaluate content prefix independently ofuiState.activeModelId, ensuring switching models does not alter past DeepSeek chat presentation.2. Multimodal Image Downsampling & EXIF Correction (
LlmViewModel.kt,LlmInferenceEngine.kt)decodeSampledBitmap(context, uri, maxDimension = 1024)using 3 independent stream passes:inJustDecodeBounds = true).ExifInterface(stream).inSampleSizedecode.Bitmap.createBitmappass, recycling intermediate allocations.sendMessage'sfinallyblock. Decoupled from Coil UI image loading.3. Keystore TokenStorage Hardening & Startup ANRs (
LlmViewModel.kt)saveHfToken()andclearHfToken()asynchronously throughtokenStorage.saveToken()andtokenStorage.clearToken()onDispatchers.IO. Dropped rawapp_settingsplaintext writes.initblock 2, eliminating startup race conditions and retiring the accidentalmigrateTokenIfPresent()loop.migrateExistingModels()andrefreshDownloadedModels()off the main thread intoviewModelScope.launch(Dispatchers.IO).4. Passive Download Completion Policy (
LlmViewModel.kt)modelState is ModelState.Unloaded || modelState is ModelState.Error; otherwise, a non-intrusive Toast is displayed ("$displayName downloaded. Ready in Model Settings.").Test & Build Verification
1. Unit Tests (
./gradlew.bat testDebugUnitTest)BUILD SUCCESSFUL in 30sParseThinkBlocksTest(9 tests):testSingleClosedThinkBlock: PASSEDtestUnclosedThinkBlock_MidGenerationOrCancelled: PASSEDtestMultipleThinkBlocks_Interleaved: PASSEDtestMultipleThinkBlocks_TrailingUnclosed: PASSEDtestNoThinkBlock_StandardOrCodingOutput: PASSEDtestThinkBlock_EmbeddedInCode: PASSEDtestEmptyAndWhitespaceThinkBlocks: PASSEDtestLeadingWhitespaceBeforeThink: PASSEDtestModelPresetReasoningResolution: PASSEDTokenStorageTest(5 tests): PASSEDModelSafetyValidatorTest(10 tests): PASSEDModelDownloaderTest(6 tests): PASSEDChatRepositoryTest(6 tests): PASSEDTelemetryBenchmarkTest(6 tests): PASSED2. Full Debug APK Build (
./gradlew.bat assembleDebug)BUILD SUCCESSFUL in 19s(38 actionable tasks, 0 compiler/DEX errors).Manual Verification Protocol
"Thinking (stopped)"in muted gray; animation stops cleanly.<think>LazyColumnduring generation"Thinking Process".