-
Notifications
You must be signed in to change notification settings - Fork 2.6k
feat: add cancel button for context condensing operation #6927
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d5b32b5
abcdcb6
5db49ca
c1abf15
95eda6d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -140,6 +140,10 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike { | |
| readonly taskNumber: number | ||
| readonly workspacePath: string | ||
|
|
||
| // Context condensing state | ||
| isCondensing: boolean = false | ||
|
||
| private condensingAbortController?: AbortController | ||
|
|
||
| /** | ||
| * The mode associated with this task. Persisted across sessions | ||
| * to maintain user context when reopening tasks from history. | ||
|
|
@@ -859,72 +863,107 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike { | |
| } | ||
|
|
||
| public async condenseContext(): Promise<void> { | ||
| const systemPrompt = await this.getSystemPrompt() | ||
| // Prevent concurrent condensing operations | ||
| if (this.isCondensing) { | ||
| console.warn("Context condensing is already in progress") | ||
| return | ||
| } | ||
|
|
||
| // Get condensing configuration | ||
| // Using type assertion to handle the case where Phase 1 hasn't been implemented yet | ||
| const state = await this.providerRef.deref()?.getState() | ||
| const customCondensingPrompt = state ? (state as any).customCondensingPrompt : undefined | ||
| const condensingApiConfigId = state ? (state as any).condensingApiConfigId : undefined | ||
| const listApiConfigMeta = state ? (state as any).listApiConfigMeta : undefined | ||
| // Wait a bit if there was a recent abort to ensure cleanup is complete | ||
| if (this.condensingAbortController && this.condensingAbortController.signal.aborted) { | ||
| console.warn("Waiting for previous condensing operation to fully clean up") | ||
| // Give the previous operation time to clean up | ||
| await new Promise((resolve) => setTimeout(resolve, 100)) | ||
| } | ||
|
|
||
| // Determine API handler to use | ||
| let condensingApiHandler: ApiHandler | undefined | ||
| if (condensingApiConfigId && listApiConfigMeta && Array.isArray(listApiConfigMeta)) { | ||
| // Using type assertion for the id property to avoid implicit any | ||
| const matchingConfig = listApiConfigMeta.find((config: any) => config.id === condensingApiConfigId) | ||
| if (matchingConfig) { | ||
| const profile = await this.providerRef.deref()?.providerSettingsManager.getProfile({ | ||
| id: condensingApiConfigId, | ||
| }) | ||
| // Ensure profile and apiProvider exist before trying to build handler | ||
| if (profile && profile.apiProvider) { | ||
| condensingApiHandler = buildApiHandler(profile) | ||
| // Mark as condensing | ||
| this.isCondensing = true | ||
| this.condensingAbortController = new AbortController() | ||
|
||
|
|
||
| try { | ||
| const systemPrompt = await this.getSystemPrompt() | ||
|
|
||
| // Get condensing configuration | ||
| const state = await this.providerRef.deref()?.getState() | ||
| const customCondensingPrompt = state?.customCondensingPrompt | ||
| const condensingApiConfigId = state?.condensingApiConfigId | ||
| const listApiConfigMeta = state?.listApiConfigMeta | ||
|
|
||
| // Determine API handler to use | ||
| let condensingApiHandler: ApiHandler | undefined | ||
| if (condensingApiConfigId && listApiConfigMeta && Array.isArray(listApiConfigMeta)) { | ||
| const matchingConfig = listApiConfigMeta.find((config) => config.id === condensingApiConfigId) | ||
| if (matchingConfig) { | ||
| const profile = await this.providerRef.deref()?.providerSettingsManager.getProfile({ | ||
| id: condensingApiConfigId, | ||
| }) | ||
| // Ensure profile and apiProvider exist before trying to build handler | ||
| if (profile && profile.apiProvider) { | ||
| condensingApiHandler = buildApiHandler(profile) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| const { contextTokens: prevContextTokens } = this.getTokenUsage() | ||
| const { | ||
| messages, | ||
| summary, | ||
| cost, | ||
| newContextTokens = 0, | ||
| error, | ||
| } = await summarizeConversation( | ||
| this.apiConversationHistory, | ||
| this.api, // Main API handler (fallback) | ||
| systemPrompt, // Default summarization prompt (fallback) | ||
| this.taskId, | ||
| prevContextTokens, | ||
| false, // manual trigger | ||
| customCondensingPrompt, // User's custom prompt | ||
| condensingApiHandler, // Specific handler for condensing | ||
| ) | ||
| if (error) { | ||
| this.say( | ||
| "condense_context_error", | ||
| const { contextTokens: prevContextTokens } = this.getTokenUsage() | ||
|
|
||
| // Pass the abort signal to summarizeConversation | ||
| const { | ||
| messages, | ||
| summary, | ||
| cost, | ||
| newContextTokens = 0, | ||
| error, | ||
| } = await summarizeConversation( | ||
| this.apiConversationHistory, | ||
| this.api, // Main API handler (fallback) | ||
| systemPrompt, // Default summarization prompt (fallback) | ||
| this.taskId, | ||
| prevContextTokens, | ||
| false, // manual trigger | ||
| customCondensingPrompt, // User's custom prompt | ||
| condensingApiHandler, // Specific handler for condensing | ||
| this.condensingAbortController.signal, // Pass abort signal | ||
| ) | ||
|
|
||
| if (error) { | ||
| // Don't show error if it was cancelled | ||
| if (!this.condensingAbortController.signal.aborted) { | ||
| this.say( | ||
| "condense_context_error", | ||
| error, | ||
| undefined /* images */, | ||
| false /* partial */, | ||
| undefined /* checkpoint */, | ||
| undefined /* progressStatus */, | ||
| { isNonInteractive: true } /* options */, | ||
| ) | ||
| } | ||
| return | ||
| } | ||
|
|
||
| await this.overwriteApiConversationHistory(messages) | ||
| const contextCondense: ContextCondense = { summary, cost, newContextTokens, prevContextTokens } | ||
| await this.say( | ||
| "condense_context", | ||
| undefined /* text */, | ||
| undefined /* images */, | ||
| false /* partial */, | ||
| undefined /* checkpoint */, | ||
| undefined /* progressStatus */, | ||
| { isNonInteractive: true } /* options */, | ||
| contextCondense, | ||
| ) | ||
| return | ||
| } finally { | ||
| // Clean up | ||
| this.isCondensing = false | ||
| this.condensingAbortController = undefined | ||
| } | ||
| } | ||
|
|
||
| public cancelCondenseContext(): void { | ||
| if (this.condensingAbortController) { | ||
| this.condensingAbortController.abort() | ||
| } | ||
| await this.overwriteApiConversationHistory(messages) | ||
| const contextCondense: ContextCondense = { summary, cost, newContextTokens, prevContextTokens } | ||
| await this.say( | ||
| "condense_context", | ||
| undefined /* text */, | ||
| undefined /* images */, | ||
| false /* partial */, | ||
| undefined /* checkpoint */, | ||
| undefined /* progressStatus */, | ||
| { isNonInteractive: true } /* options */, | ||
| contextCondense, | ||
| ) | ||
| } | ||
|
|
||
| async say( | ||
|
|
@@ -2256,8 +2295,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike { | |
| let condensingApiHandler: ApiHandler | undefined | ||
|
|
||
| if (condensingApiConfigId && listApiConfigMeta && Array.isArray(listApiConfigMeta)) { | ||
| // Using type assertion for the id property to avoid implicit any. | ||
| const matchingConfig = listApiConfigMeta.find((config: any) => config.id === condensingApiConfigId) | ||
| const matchingConfig = listApiConfigMeta.find((config) => config.id === condensingApiConfigId) | ||
|
|
||
| if (matchingConfig) { | ||
| const profile = await this.providerRef.deref()?.providerSettingsManager.getProfile({ | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical Issue: The translation key is not defined in the i18n files. This will cause a runtime error when the cancellation occurs.
You need to add this key to under the section:
Also add translations for other supported languages.