feat(condense): allow provider profile override#904
Conversation
|
Important Review skippedNo new commits to review since the last review. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughContext condensation now supports an optional provider API configuration override. Settings and extension state carry the selection, task flows resolve the selected profile, and summarization uses it while retaining the active handler for token counting and fallback. ChangesContext condensing profile override
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Task
participant ProviderSettingsManager
participant ContextManagement
participant SummarizeConversation
Task->>ProviderSettingsManager: resolve selected condensing profile
ProviderSettingsManager-->>Task: return profile configuration
Task->>ContextManagement: pass condensingApiHandler
ContextManagement->>SummarizeConversation: pass condensingApiHandler
SummarizeConversation->>SummarizeConversation: send condensing request through selected handler
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
6b6eb5f to
11e0fd3
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
11e0fd3 to
046bfd0
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/core/condense/__tests__/index.spec.ts (1)
1286-1306: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAssert the fallback warning was actually logged.
warnSpyis created and mocked but never asserted on, so the test doesn't lock in the intentionalconsole.warnfallback-notification behavior added in this PR.✅ Proposed addition
try { await summarizeConversation({ messages: sampleMessages, apiHandler: mockMainApiHandler, condensingApiHandler: invalidHandler, systemPrompt: defaultSystemPrompt, taskId: localTaskId, }) } finally { warnSpy.mockRestore() } expect(mockMainApiHandler.createMessage).toHaveBeenCalledOnce() + expect(warnSpy).toHaveBeenCalled() })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/core/condense/__tests__/index.spec.ts` around lines 1286 - 1306, Update the test around summarizeConversation to assert that the mocked warnSpy records the expected fallback warning when condensingApiHandler is invalid. Keep the existing createMessage assertion and restore behavior unchanged, and match the warning’s established message or relevant arguments.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@webview-ui/src/components/settings/ContextManagementSettings.tsx`:
- Around line 510-553: Remove the duplicate description div rendered inside the
condensingApiConfigOverride block in ContextManagementSettings, while preserving
the description shown below the checkbox and the select dropdown behavior.
---
Nitpick comments:
In `@src/core/condense/__tests__/index.spec.ts`:
- Around line 1286-1306: Update the test around summarizeConversation to assert
that the mocked warnSpy records the expected fallback warning when
condensingApiHandler is invalid. Keep the existing createMessage assertion and
restore behavior unchanged, and match the warning’s established message or
relevant arguments.
🪄 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 Plus
Run ID: 522e9932-8bda-471e-85e3-730b927de00c
📒 Files selected for processing (12)
packages/types/src/global-settings.tspackages/types/src/vscode-extension-host.tssrc/core/condense/__tests__/index.spec.tssrc/core/condense/index.tssrc/core/context-management/index.tssrc/core/task/Task.tssrc/core/task/__tests__/Task.spec.tssrc/core/webview/ClineProvider.tswebview-ui/src/components/settings/ContextManagementSettings.tsxwebview-ui/src/components/settings/SettingsView.tsxwebview-ui/src/components/settings/__tests__/ContextManagementSettings.spec.tsxwebview-ui/src/i18n/locales/en/settings.json
| <SearchableSetting | ||
| settingId="context-condensing-model-override" | ||
| section="contextManagement" | ||
| label={t("settings:contextManagement.condensingApiConfiguration.label")}> | ||
| <VSCodeCheckbox | ||
| checked={condensingApiConfigOverride} | ||
| onChange={(e: any) => setCachedStateField("condensingApiConfigOverride", e.target.checked)} | ||
| data-testid="condensing-model-override-checkbox"> | ||
| <span className="font-medium"> | ||
| {t("settings:contextManagement.condensingApiConfiguration.label")} | ||
| </span> | ||
| </VSCodeCheckbox> | ||
| <div className="text-vscode-descriptionForeground text-sm mt-1"> | ||
| {t("settings:contextManagement.condensingApiConfiguration.description")} | ||
| </div> | ||
| {condensingApiConfigOverride && ( | ||
| <div className="mt-3"> | ||
| <Select | ||
| value={condensingApiConfigId || "current-mode"} | ||
| onValueChange={(value) => | ||
| setCachedStateField("condensingApiConfigId", value === "current-mode" ? "" : value) | ||
| } | ||
| data-testid="condensing-profile-select"> | ||
| <SelectTrigger className="w-full"> | ||
| <SelectValue /> | ||
| </SelectTrigger> | ||
| <SelectContent> | ||
| <SelectItem value="current-mode"> | ||
| {t("settings:contextManagement.condensingApiConfiguration.useCurrentConfig")} | ||
| </SelectItem> | ||
| {listApiConfigMeta.map((config) => ( | ||
| <SelectItem key={config.id} value={config.id}> | ||
| {config.name} | ||
| </SelectItem> | ||
| ))} | ||
| </SelectContent> | ||
| </Select> | ||
| <div className="text-vscode-descriptionForeground text-sm mt-1"> | ||
| {t("settings:contextManagement.condensingApiConfiguration.description")} | ||
| </div> | ||
| </div> | ||
| )} | ||
| </SearchableSetting> | ||
|
|
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Remove the duplicate description text.
The description text is currently rendered twice when the override is enabled: once below the checkbox and again below the select dropdown. Please remove this second instance to keep the UI clean.
♻️ Proposed fix
</Select>
- <div className="text-vscode-descriptionForeground text-sm mt-1">
- {t("settings:contextManagement.condensingApiConfiguration.description")}
- </div>
</div>
)}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <SearchableSetting | |
| settingId="context-condensing-model-override" | |
| section="contextManagement" | |
| label={t("settings:contextManagement.condensingApiConfiguration.label")}> | |
| <VSCodeCheckbox | |
| checked={condensingApiConfigOverride} | |
| onChange={(e: any) => setCachedStateField("condensingApiConfigOverride", e.target.checked)} | |
| data-testid="condensing-model-override-checkbox"> | |
| <span className="font-medium"> | |
| {t("settings:contextManagement.condensingApiConfiguration.label")} | |
| </span> | |
| </VSCodeCheckbox> | |
| <div className="text-vscode-descriptionForeground text-sm mt-1"> | |
| {t("settings:contextManagement.condensingApiConfiguration.description")} | |
| </div> | |
| {condensingApiConfigOverride && ( | |
| <div className="mt-3"> | |
| <Select | |
| value={condensingApiConfigId || "current-mode"} | |
| onValueChange={(value) => | |
| setCachedStateField("condensingApiConfigId", value === "current-mode" ? "" : value) | |
| } | |
| data-testid="condensing-profile-select"> | |
| <SelectTrigger className="w-full"> | |
| <SelectValue /> | |
| </SelectTrigger> | |
| <SelectContent> | |
| <SelectItem value="current-mode"> | |
| {t("settings:contextManagement.condensingApiConfiguration.useCurrentConfig")} | |
| </SelectItem> | |
| {listApiConfigMeta.map((config) => ( | |
| <SelectItem key={config.id} value={config.id}> | |
| {config.name} | |
| </SelectItem> | |
| ))} | |
| </SelectContent> | |
| </Select> | |
| <div className="text-vscode-descriptionForeground text-sm mt-1"> | |
| {t("settings:contextManagement.condensingApiConfiguration.description")} | |
| </div> | |
| </div> | |
| )} | |
| </SearchableSetting> | |
| <SearchableSetting | |
| settingId="context-condensing-model-override" | |
| section="contextManagement" | |
| label={t("settings:contextManagement.condensingApiConfiguration.label")}> | |
| <VSCodeCheckbox | |
| checked={condensingApiConfigOverride} | |
| onChange={(e: any) => setCachedStateField("condensingApiConfigOverride", e.target.checked)} | |
| data-testid="condensing-model-override-checkbox"> | |
| <span className="font-medium"> | |
| {t("settings:contextManagement.condensingApiConfiguration.label")} | |
| </span> | |
| </VSCodeCheckbox> | |
| <div className="text-vscode-descriptionForeground text-sm mt-1"> | |
| {t("settings:contextManagement.condensingApiConfiguration.description")} | |
| </div> | |
| {condensingApiConfigOverride && ( | |
| <div className="mt-3"> | |
| <Select | |
| value={condensingApiConfigId || "current-mode"} | |
| onValueChange={(value) => | |
| setCachedStateField("condensingApiConfigId", value === "current-mode" ? "" : value) | |
| } | |
| data-testid="condensing-profile-select"> | |
| <SelectTrigger className="w-full"> | |
| <SelectValue /> | |
| </SelectTrigger> | |
| <SelectContent> | |
| <SelectItem value="current-mode"> | |
| {t("settings:contextManagement.condensingApiConfiguration.useCurrentConfig")} | |
| </SelectItem> | |
| {listApiConfigMeta.map((config) => ( | |
| <SelectItem key={config.id} value={config.id}> | |
| {config.name} | |
| </SelectItem> | |
| ))} | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| )} | |
| </SearchableSetting> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@webview-ui/src/components/settings/ContextManagementSettings.tsx` around
lines 510 - 553, Remove the duplicate description div rendered inside the
condensingApiConfigOverride block in ContextManagementSettings, while preserving
the description shown below the checkbox and the select dropdown behavior.
There was a problem hiding this comment.
@edelauna, thanks for confirming. Removing the second description beneath the dropdown will keep the setting uncluttered while preserving the explanatory text below the checkbox.
🐇✨
edelauna
left a comment
There was a problem hiding this comment.
Thank you for your contribution! Had a few comments around tightening the tests mainly.
| await expect((task as any).getCondensingApiHandler(state)).resolves.toBeUndefined() | ||
| } finally { | ||
| warnSpy.mockRestore() | ||
| } |
There was a problem hiding this comment.
All four fallback cases assert only resolves.toBeUndefined() — a mutation that collapses all four guards into a single early return undefined would make every case pass. Cases 2 ("profile no longer exists") and 4 ("cannot be loaded") fire console.warn; cases 1 and 3 should not. Could warnSpy be asserted per-case to pin each guard individually?
| }) | ||
|
|
||
| it("should pass AbortController signal to createMessage metadata", async () => { | ||
| const task = new Task({ |
There was a problem hiding this comment.
Missing case: condensingApiConfigOverride: true with condensingApiConfigId: "" — the state stored when a user enables the override but selects "Use the model running in the current mode". The !configId early-return branch in getCondensingApiHandler covers this path but none of the four entries exercise it.
| expect(mockMainApiHandler.createMessage).toHaveBeenCalledOnce() | ||
| }) | ||
|
|
||
| /** |
There was a problem hiding this comment.
warnSpy is created but never asserted. The console.warn in condense/index.ts is the only observable signal that the fallback path was taken (vs. the invalid handler never being supplied) — worth adding expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("invalid"))?
| const handlerToUse = | ||
| condensingApiHandler && typeof condensingApiHandler.createMessage === "function" | ||
| ? condensingApiHandler | ||
| : apiHandler |
There was a problem hiding this comment.
captureContextCondensed fires before this point, so it has no record of whether the override profile was actually used for this condense run. Would it make sense to move that call to here and pass something like usedOverrideProfile: handlerToUse !== apiHandler?
| "label": "Override API Configuration for Context Condensing", | ||
| "description": "Select which provider configuration profile to use for context condensing.", | ||
| "useCurrentConfig": "Use the model running in the current mode" |
There was a problem hiding this comment.
These three strings were reworded here, but the other 17 locale files still carry the old translations. Since fallbackLng: "en" only applies to missing keys (not stale ones), non-English users will see the old copy.
| <SearchableSetting | ||
| settingId="context-condensing-model-override" | ||
| section="contextManagement" | ||
| label={t("settings:contextManagement.condensingApiConfiguration.label")}> | ||
| <VSCodeCheckbox | ||
| checked={condensingApiConfigOverride} | ||
| onChange={(e: any) => setCachedStateField("condensingApiConfigOverride", e.target.checked)} | ||
| data-testid="condensing-model-override-checkbox"> | ||
| <span className="font-medium"> | ||
| {t("settings:contextManagement.condensingApiConfiguration.label")} | ||
| </span> | ||
| </VSCodeCheckbox> | ||
| <div className="text-vscode-descriptionForeground text-sm mt-1"> | ||
| {t("settings:contextManagement.condensingApiConfiguration.description")} | ||
| </div> | ||
| {condensingApiConfigOverride && ( | ||
| <div className="mt-3"> | ||
| <Select | ||
| value={condensingApiConfigId || "current-mode"} | ||
| onValueChange={(value) => | ||
| setCachedStateField("condensingApiConfigId", value === "current-mode" ? "" : value) | ||
| } | ||
| data-testid="condensing-profile-select"> | ||
| <SelectTrigger className="w-full"> | ||
| <SelectValue /> | ||
| </SelectTrigger> | ||
| <SelectContent> | ||
| <SelectItem value="current-mode"> | ||
| {t("settings:contextManagement.condensingApiConfiguration.useCurrentConfig")} | ||
| </SelectItem> | ||
| {listApiConfigMeta.map((config) => ( | ||
| <SelectItem key={config.id} value={config.id}> | ||
| {config.name} | ||
| </SelectItem> | ||
| ))} | ||
| </SelectContent> | ||
| </Select> | ||
| <div className="text-vscode-descriptionForeground text-sm mt-1"> | ||
| {t("settings:contextManagement.condensingApiConfiguration.description")} | ||
| </div> | ||
| </div> | ||
| )} | ||
| </SearchableSetting> | ||
|
|
Related GitHub Issue
Closes: #609
Description
Adds an opt-in context-condensing model override in Context Management settings.
The implementation deliberately reuses the existing provider-profile storage, cached SettingsView state, condensing pipeline, and fallback behavior. This keeps the production change limited to the settings schema/state wiring, one profile resolver, and one optional handler parameter through the existing condensation calls.
This aligns with the roadmap's reliability and provider/model support goals by allowing users to choose a compatible, lower-cost summarization model without changing their active task model.
Test Procedure
Automated verification:
pnpm --dir src exec vitest run core/task/__tests__/Task.spec.ts core/condense/__tests__/index.spec.ts core/context-management/__tests__/context-management.spec.ts core/webview/__tests__/ClineProvider.spec.tspnpm --dir webview-ui exec vitest run src/components/settings/__tests__/ContextManagementSettings.spec.tsx src/components/settings/__tests__/SettingsView.change-detection.spec.tsx src/components/settings/__tests__/SettingsView.unsaved-changes.spec.tsxsrc,webview-ui, andpackages/types.src,webview-ui, andpackages/types.node scripts/find-missing-translations.js.pnpm build.Runtime coverage specifically asserts that:
summarizeConversation.manageContext.summarizeConversationsends the request through the selected handler while retaining the active handler for post-condense token counting.Reviewer verification:
Testing environment: Windows, VS Code 1.108.2. The repository requests Node 20.20.2; local verification used Node 24.11.1 with pnpm 10.8.1 and completed successfully.
Pre-Submission Checklist
Screenshots / Videos
The production VSIX was installed and exercised locally. A screenshot is not attached because the new control uses the existing settings components and visual language; the component tests verify the conditional dropdown and its options. I can add an installed-extension screenshot if maintainers want one before moving the PR out of draft.
Documentation Updates
Additional Notes
Get in Touch
GitHub: @twinlunarstarz-dev. A Discord username was not provided.
Summary by CodeRabbit
New Features
Bug Fixes
Tests