Skip to content

feat: add 'keep chat images' toggle to preserve chat images across restarts (#563) - #828

Open
ghshhf wants to merge 1 commit into
AAswordman:mainfrom
ghshhf:pr/keep-chat-images
Open

feat: add 'keep chat images' toggle to preserve chat images across restarts (#563)#828
ghshhf wants to merge 1 commit into
AAswordman:mainfrom
ghshhf:pr/keep-chat-images

Conversation

@ghshhf

@ghshhf ghshhf commented Jul 27, 2026

Copy link
Copy Markdown

Summary

Implements upstream issue #563: a user setting "Keep chat images" that, when enabled, prevents ImagePoolManager from clearing the on-disk image cache on app startup and from deleting disk files when the in-memory LRU pool evicts entries. Chat images are therefore preserved across restarts and cache eviction.

Changes

  • ImagePoolManager: added keepChatImages flag; skip clearDiskCache() at startup and skip deleteFromDisk() on LRU eviction when enabled.
  • UserPreferencesManager: new KEEP_CHAT_IMAGES DataStore preference with Flow + setter.
  • OperitApplication: read the preference before initializing the image pool, so startup does not wipe the disk cache.
  • ChatHistorySettingsScreen: new Switch (with description) bound to the preference.
  • values/strings.xml + values-en/strings.xml: new strings (zh + en).
  • JsCalculatorRegressionTest: added pure-JVM regression tests under core/tools/calculator (no Android dependency).

Testing

  • Build + existing JVM test suite; the JsCalculator tests run without Android dependencies.
  • Manual verification recommended: enable the toggle, restart the app, and confirm chat images remain on disk.

Closes #563

…starts (AAswordman#563)

Implement upstream issue AAswordman#563: a user setting that, when enabled, stops ImagePoolManager from clearing the on-disk image cache on startup and from deleting disk files when the in-memory LRU pool evicts entries. Plumbing: new KEEP_CHAT_IMAGES DataStore preference, injected into ImagePoolManager at Application startup. UI: a Switch added to ChatHistorySettingsScreen with en/base string resources. Also adds JsCalculator JVM regression tests under core/tools.
@luojiaping
luojiaping requested review from AAswordman and luojiaping and removed request for luojiaping July 27, 2026 23:44
@CATMIAOZHI

Copy link
Copy Markdown
Collaborator

Review Comment

Thanks for the contribution — the feature direction is correct and the implementation is clean for what it covers. However, I'd like to request a few changes before this can be merged.

Blocking issues

1. Merge conflicts — mergeable: CONFLICTING

GitHub currently reports this PR as conflicting with main. Please rebase onto the latest main, resolve conflicts, and push again.

2. Toggle does not take effect without app restart

Currently the preference is read once in OperitApplication.onCreate() and written to ImagePoolManager.keepChatImages:

// OperitApplication.kt — startup only
val keepChatImages = runBlocking { ... .keepChatImages.first() }
ImagePoolManager.keepChatImages = keepChatImages

The settings screen only calls setKeepChatImages() which writes to DataStore but does not update ImagePoolManager.keepChatImages at runtime:

// ChatHistorySettingsScreen.kt — only persists, no runtime sync
userPreferencesManager.setKeepChatImages(enabled)

This means:

  • Enabling the toggle requires a restart before images start being preserved.
  • Disabling the toggle also requires a restart before cleanup resumes.

There's no UI hint about needing a restart, so users will assume the switch is broken.

Suggested fix: Either have ImagePoolManager observe the keepChatImages Flow, or provide a unified setter (e.g. setKeepChatImages(value: Boolean) on ImagePoolManager that updates both the runtime flag and the persisted preference) and call it from the settings screen.

3. CI: JVM checks failing

The PR Check workflow fails at Step 31 "Run Android JVM checks" (not at a secrets/config step — the build and dependency steps all succeed). This is the JVM unit test stage, so it's likely that either the new JsCalculatorRegressionTest or an existing test is failing. Could you check the logs and fix? If it's an environment issue unrelated to the PR, a maintainer can re-run.

Non-blocking but should address

4. Unrelated calculator tests should be split out

The 94-line JsCalculatorRegressionTest.kt is entirely unrelated to the image retention feature. It should be moved to a separate PR, or at minimum explained in the PR description with its motivation. Mixing unrelated changes makes review harder and complicates revert if either change needs to be rolled back.

5. No tests for the image retention feature itself

The PR adds tests for the calculator but none for the keepChatImages behavior. Ideally we'd have coverage for:

  • Startup cleanup respects the flag
  • LRU eviction respects the flag (skips deleteFromDisk)
  • Toggle change propagates to runtime (once issue 新用户的一些建议 #2 is fixed)

ImagePoolManager is an object with file-system dependencies, so this may require some refactoring to make it testable, but at least the LRU eviction logic could be tested if the disk operations were abstracted.

Note on removeImagesFromLastUserMessage

Issue #563 lists three "cleanup triggers," and one of them is PhoneAgent.removeImagesFromLastUserMessage(). I checked the implementation:

fun removeImagesFromLastUserMessage(history: MutableList<Pair<String, String>>) {
    val lastUserMessageIndex = history.indexOfLast { it.first == "user" }
    if (lastUserMessageIndex != -1) {
        val (role, content) = history[lastUserMessageIndex]
        if (content.contains("<link type=\"image\"")) {
            val stripped = content.replace(Regex("""<link type=\"image\".*?</link>"""), "").trim()
            history[lastUserMessageIndex] = role to stripped
        }
    }
}

This method only strips <link type="image"> tags from the in-memory context history — it does not call ImagePoolManager.removeImage() or deleteFromDisk(). The image data remains in the pool and on disk. So not modifying this method is acceptable; the two real file-deletion paths (startup clearDiskCache() and LRU deleteFromDisk()) are correctly guarded.

Summary

Item Status
Merge conflict ❌ Must resolve
Toggle not live-updating ❌ Must fix
CI JVM checks failing ❌ Must investigate
Unrelated calculator tests ⚠️ Should split
Missing feature tests ⚠️ Should add
removeImagesFromLastUserMessage ✅ No change needed

Verdict: Request changes. The core logic is sound, but the runtime-sync gap and CI failure need to be addressed first.

@AAswordman AAswordman left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes.\n\nP1: The PR is currently conflicting with main and Candidate checks fail during Android JVM tests; both must be resolved on an updated branch.\n\nP1: keepChatImages is read once at application startup, while the settings switch only persists DataStore. Toggling it during a running session therefore does not change ImagePoolManager behavior, although the UI gives no restart requirement. Wire the setting change to the runtime manager.\n\nP2: JsCalculatorRegressionTest is unrelated to chat-image retention and should be split from this feature. The image-retention behavior itself also needs focused coverage for startup cleanup and LRU eviction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

【 Future request】增加"保留聊天图片"开关,支持消息中的图片持久化保存

3 participants