Skip to content

fix(provider): gc removed model state#1577

Merged
zerob13 merged 1 commit into
codex/p1-selectable-model-sourcefrom
codex/p2-provider-gc
Apr 30, 2026
Merged

fix(provider): gc removed model state#1577
zerob13 merged 1 commit into
codex/p1-selectable-model-sourcefrom
codex/p2-provider-gc

Conversation

@zhangmo8
Copy link
Copy Markdown
Collaborator

@zhangmo8 zhangmo8 commented Apr 30, 2026

Summary\n- clean persisted model status entries when a provider is removed\n- clear the provider-specific stored model/custom-model payload at the same time\n- wire provider removal through cleanup hooks and cover the helper behavior with focused main-process tests\n\n## Testing\n- commit hook ran oxfmt and typecheck automatically during git commit\n

Summary by CodeRabbit

  • Bug Fixes

    • Improved provider removal to ensure all associated model statuses and stored data are properly cleaned up, preventing orphaned data from persisting in the system.
  • Tests

    • Added comprehensive test coverage for provider cleanup operations to ensure data consistency during removal.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 30, 2026

📝 Walkthrough

Walkthrough

The PR introduces a provider cleanup lifecycle by adding methods to delete provider model statuses and clear provider model stores, then wires these cleanup operations into the provider removal flow via configurable hooks in ProviderHelper.

Changes

Cohort / File(s) Summary
Cleanup Hook Infrastructure
src/main/presenter/configPresenter/index.ts, src/main/presenter/configPresenter/providerHelper.ts
Establishes cleanup hook mechanism: ConfigPresenter passes bound callbacks to ProviderHelper.setCleanupHooks during construction; ProviderHelper defines ProviderCleanupHooks interface and invokes hooks during removeProviderAtomic with try/catch error isolation.
Cleanup Implementations
src/main/presenter/configPresenter/modelStatusHelper.ts, src/main/presenter/configPresenter/providerModelHelper.ts
Adds deletion methods: deleteProviderModelStatuses scans and removes provider-scoped ElectronStore keys and invalidates caches; clearProviderModelStore wipes provider-specific model entries and removes in-memory store references.
Test Coverage
test/main/presenter/configPresenter/modelStatusHelper.test.ts, test/main/presenter/configPresenter/providerHelper.test.ts, test/main/presenter/configPresenter/providerModelHelper.test.ts
Comprehensive unit tests: validates provider model status deletion, verifies cleanup hooks are invoked during atomic provider removal, tests model store clearing with mocked ElectronStore; extends test mock with clear() support.

Sequence Diagram

sequenceDiagram
    participant ConfigPresenter
    participant ProviderHelper
    participant ModelStatusHelper
    participant ProviderModelHelper
    participant ElectronStore

    ConfigPresenter->>ProviderHelper: setCleanupHooks(deleteProviderModelStatuses, clearProviderModelStore)
    Note over ProviderHelper: Stores hook callbacks
    
    ConfigPresenter->>ProviderHelper: removeProviderAtomic(providerId)
    ProviderHelper->>ElectronStore: Remove provider from persisted list
    
    ProviderHelper->>ModelStatusHelper: deleteProviderModelStatuses(providerId)
    ModelStatusHelper->>ElectronStore: Scan & delete model_status_<provider>_* keys
    ModelStatusHelper->>ModelStatusHelper: Invalidate provider cache
    
    ProviderHelper->>ProviderModelHelper: clearProviderModelStore(providerId)
    ProviderModelHelper->>ElectronStore: clear() provider model store
    ProviderModelHelper->>ProviderModelHelper: Remove in-memory store reference
    ProviderModelHelper->>ProviderModelHelper: Invalidate models cache
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • zerob13

Poem

🐰 Hooky clean-up hops in place,
Models scatter without a trace,
Providers removed with graceful care,
Memory freed beyond compare! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding garbage collection cleanup logic for removed provider model state through new cleanup hooks and deletion methods.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/p2-provider-gc

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/presenter/configPresenter/modelStatusHelper.ts`:
- Around line 235-251: The current prefix match in deleteProviderModelStatuses
(using MODEL_STATUS_KEY_PREFIX + providerId + '_') can delete keys for other
providers with IDs containing that providerId; fix by changing the delimiter
used when composing model-status keys to a collision-safe token (e.g., replace
the '_' with a delimiter like '##' or encode providerId) and update all places
that create/read model-status keys accordingly: update MODEL_STATUS_KEY_PREFIX
usage and every key-creation site to use
`${MODEL_STATUS_KEY_PREFIX}${providerId}##` (or use
encodeURIComponent/base64(providerId) when composing keys), then change
deleteProviderModelStatuses to compute that exact prefix and only delete keys
starting with that new prefix, leaving clearProviderModelStatusCache and
store.delete calls intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: af272571-cb57-4f88-8495-48ed4e8c372f

📥 Commits

Reviewing files that changed from the base of the PR and between 41d3f6d and 083360e.

📒 Files selected for processing (7)
  • src/main/presenter/configPresenter/index.ts
  • src/main/presenter/configPresenter/modelStatusHelper.ts
  • src/main/presenter/configPresenter/providerHelper.ts
  • src/main/presenter/configPresenter/providerModelHelper.ts
  • test/main/presenter/configPresenter/modelStatusHelper.test.ts
  • test/main/presenter/configPresenter/providerHelper.test.ts
  • test/main/presenter/configPresenter/providerModelHelper.test.ts

Comment on lines +235 to +251
deleteProviderModelStatuses(providerId: string): void {
const prefix = `${MODEL_STATUS_KEY_PREFIX}${providerId}_`
const candidate = this.store as ElectronStore<any> & {
store?: Record<string, unknown>
}
const rawStore = candidate.store

if (rawStore && typeof rawStore === 'object') {
for (const key of Object.keys(rawStore)) {
if (key.startsWith(prefix)) {
this.store.delete(key)
}
}
}

this.clearProviderModelStatusCache(providerId)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Inspect key construction/deletion logic"
rg -n -C3 "MODEL_STATUS_KEY_PREFIX|getStatusKey|deleteProviderModelStatuses|clearProviderModelStatusCache" src/main/presenter/configPresenter/modelStatusHelper.ts

echo
echo "2) Locate provider ID validation rules or schema constraints"
rg -n --type=ts --type=tsx -C3 "providerId|id.*provider|addProvider|setProviderById|updateProviderAtomic|regex\\(|z\\.string\\(" src test

Repository: ThinkInAIXYZ/deepchat

Length of output: 3625


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Search for provider schema/type definitions ==="
rg -n "providerId.*schema|providerId.*z\.|interface.*Provider|type.*Provider" src --type=ts 2>/dev/null | head -30

echo
echo "=== Search for provider validation and creation logic ==="
rg -n "addProvider|createProvider|validateProvider|providerId.*=" src --type=ts 2>/dev/null | head -30

echo
echo "=== Look for LLMProvider class/interface definitions ==="
fd -name "*[Pp]rovider*.ts" src | head -20

Repository: ThinkInAIXYZ/deepchat

Length of output: 7167


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Read ProviderSchema definition ==="
sed -n '140,180p' src/shared/types/model-db.ts

echo
echo "=== Search for provider ID character restrictions ==="
rg -n "providerId.*regex|providerId.*match|id.*regex|z\.string\(\).*regex|pattern" src/shared/types/model-db.ts

echo
echo "=== Look for any provider ID sanitization/validation ==="
rg -n "trim\(\)|toLowerCase\(\)|sanitize.*provider|validate.*provider" src --type=ts | grep -i "id" | head -20

Repository: ThinkInAIXYZ/deepchat

Length of output: 1154


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Find ProviderSchema definition ==="
rg -n "ProviderSchema\s*=" src/shared/types/model-db.ts -A 15

echo
echo "=== Check what characters are allowed in provider IDs ==="
rg -n "id.*z\.|providerId.*z\." src/shared/types/model-db.ts -B 2 -A 2

echo
echo "=== Check LLM_PROVIDER interface/type ==="
fd -name "*.ts" src | xargs rg -l "LLM_PROVIDER|interface.*Provider" | head -5 | xargs rg -n "interface LLM_PROVIDER|type LLM_PROVIDER" -A 20

Repository: ThinkInAIXYZ/deepchat

Length of output: 8211


Prefix-based deletion can cause cross-provider data loss.

The deleteProviderModelStatuses() method uses a simple prefix match (model_status_${providerId}_) to delete status keys. Since provider IDs have no character restrictions (ProviderSchema only requires z.string().min(1)), this creates a collision risk: deleting provider foo will also delete statuses for provider foo_bar because the prefix model_status_foo_ matches both model_status_foo_xyz and model_status_foo_bar_xyz.

The fix requires either: (1) validate provider IDs to exclude underscores, (2) use exact key matching instead of prefix matching, or (3) use a delimiter that cannot appear in provider IDs (e.g., ## or a null byte).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/presenter/configPresenter/modelStatusHelper.ts` around lines 235 -
251, The current prefix match in deleteProviderModelStatuses (using
MODEL_STATUS_KEY_PREFIX + providerId + '_') can delete keys for other providers
with IDs containing that providerId; fix by changing the delimiter used when
composing model-status keys to a collision-safe token (e.g., replace the '_'
with a delimiter like '##' or encode providerId) and update all places that
create/read model-status keys accordingly: update MODEL_STATUS_KEY_PREFIX usage
and every key-creation site to use `${MODEL_STATUS_KEY_PREFIX}${providerId}##`
(or use encodeURIComponent/base64(providerId) when composing keys), then change
deleteProviderModelStatuses to compute that exact prefix and only delete keys
starting with that new prefix, leaving clearProviderModelStatusCache and
store.delete calls intact.

@zerob13 zerob13 changed the base branch from dev to codex/p1-selectable-model-source April 30, 2026 13:48
@zerob13 zerob13 merged commit d2b96a9 into codex/p1-selectable-model-source Apr 30, 2026
3 checks passed
zerob13 added a commit that referenced this pull request Apr 30, 2026
* refactor(model): derive selectable model source

* refactor(model): unify selection resolver (#1576)

* refactor(chat): split acp status bar state (#1578)

* refactor(model): unify selection resolver

* refactor(chat): extract acp status bar state

* perf(agent): refresh acp agents incrementally (#1579)

* fix(provider): gc removed model state (#1577)

* fix: address pr review feedback

---------

Co-authored-by: zerob13 <zerob13@gmail.com>
@zhangmo8 zhangmo8 deleted the codex/p2-provider-gc branch May 12, 2026 07:51
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.

2 participants