-
Notifications
You must be signed in to change notification settings - Fork 3k
fix(core): Reduce multimodal history payload size #6045
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
558e71f
3dac22e
b833184
fb90b66
b3b6a1b
768787b
5e1bcd6
6fbb4e7
04ce964
9e220e6
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 |
|---|---|---|
|
|
@@ -56,7 +56,14 @@ import { | |
| type CompactTrigger, | ||
| } from '../services/chatCompressionService.js'; | ||
| import { acquireSleepInhibitor } from '../services/sleepInhibitor.js'; | ||
| import { resolveSlimmingConfig } from '../services/compactionInputSlimming.js'; | ||
| import { | ||
| resolveCompactionTuning, | ||
| resolveSlimmingConfig, | ||
| } from '../services/compactionInputSlimming.js'; | ||
| import { | ||
| InMemoryImagePayloadStore, | ||
| prepareImagePayloadsForRequest, | ||
| } from '../services/image-payload-references.js'; | ||
| import { | ||
| estimateContentTokens, | ||
| estimatePromptTokens, | ||
|
|
@@ -1429,6 +1436,8 @@ export class GeminiChat { | |
| | Parameters<ChatRecordingService['recordAssistantTurn']>[0] | ||
| | null = null; | ||
|
|
||
| private readonly imagePayloadStore = new InMemoryImagePayloadStore(); | ||
|
|
||
| /** | ||
| * Monotonically counts user-content pushes that survived into history. | ||
| * Incremented when `sendMessageStream` pushes the user content and decremented | ||
|
|
@@ -1491,8 +1500,30 @@ export class GeminiChat { | |
| * Public history readers still use {@link getHistory}, which returns a | ||
| * defensive deep copy for caller mutation safety. | ||
| */ | ||
| private getRequestHistory(): Content[] { | ||
| return extractCuratedHistory(this.history).map(copyContentContainer); | ||
| private getRequestHistory(currentUserContent?: Content): Content[] { | ||
| const curatedHistory = extractCuratedHistory(this.history); | ||
| const preserveImagePartsForContentIndex = currentUserContent | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Critical] Please preserve the current image parts by part identity or part range through curation rather than by the — gpt-5 via Qwen Code /review |
||
| ? curatedHistory.findIndex((content) => content === currentUserContent) | ||
| : -1; | ||
| const requestHistory = curatedHistory.map(copyContentContainer); | ||
| const preserveLastUserImagePartCount = | ||
| preserveImagePartsForContentIndex === -1 | ||
| ? (currentUserContent?.parts?.length ?? 0) | ||
| : 0; | ||
| const preserveImagePartsForContentIndexOption = | ||
| preserveImagePartsForContentIndex === -1 | ||
| ? undefined | ||
| : preserveImagePartsForContentIndex; | ||
| const { maxRecentImages } = resolveCompactionTuning( | ||
| this.config.getChatCompression(), | ||
| ); | ||
|
LaZzyMan marked this conversation as resolved.
|
||
| return prepareImagePayloadsForRequest(requestHistory, { | ||
| maxRecentImages, | ||
| preserveImagePartsForContentIndex: | ||
| preserveImagePartsForContentIndexOption, | ||
| preserveLastUserImagePartCount, | ||
| store: this.imagePayloadStore, | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -1779,7 +1810,7 @@ export class GeminiChat { | |
| parsedEnvMaxTokensForThreshold ?? | ||
| 0) | ||
| : Math.max(ESCALATED_MAX_TOKENS, tokenLimit(model, 'output'))); | ||
|
|
||
| let currentUserContent: Content | undefined; | ||
| try { | ||
| // The send-lock above is held but the generator's `finally` (which | ||
| // resolves it) has not run yet. Any setup error before returning the | ||
|
|
@@ -1953,6 +1984,7 @@ export class GeminiChat { | |
|
|
||
| // Add user content to history ONCE before any attempts. | ||
| this.history.push(userContent); | ||
| currentUserContent = userContent; | ||
| userContentAdded = true; | ||
| // Record that the user content landed (see `userContentPushCount`). The | ||
| // setup-error path below decrements this if it rolls the push back. | ||
|
|
@@ -1984,7 +2016,7 @@ export class GeminiChat { | |
| .join(', '), | ||
| ); | ||
| } | ||
| requestContents = this.getRequestHistory(); | ||
| requestContents = this.getRequestHistory(currentUserContent); | ||
| } catch (error) { | ||
| if (userContentAdded) { | ||
| this.history.pop(); | ||
|
|
@@ -2287,7 +2319,8 @@ export class GeminiChat { | |
| // other retry branches in case a future in-place | ||
| // tryCompress stops resetting it. | ||
| popPartialIfPushed(); | ||
| requestContents = self.getRequestHistory(); | ||
| requestContents = | ||
| self.getRequestHistory(currentUserContent); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] The latest change threads Please add a focused — gpt-5 via Qwen Code /review |
||
| debugLogger.info( | ||
| `Reactive compression succeeded: ` + | ||
| `${reactiveInfo.originalTokenCount} -> ` + | ||
|
|
@@ -2530,7 +2563,7 @@ export class GeminiChat { | |
| // model's continuation appends to the previous partial output. | ||
| yield { type: StreamEventType.RETRY, isContinuation: true }; | ||
| // Re-send with the updated history (includes partial + recovery) | ||
| const recoveryContents = self.getRequestHistory(); | ||
| const recoveryContents = self.getRequestHistory(currentUserContent); | ||
| escalatedFinishReason = undefined; | ||
| try { | ||
| const recoveryStream = await self.makeApiCallAndProcessStream( | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.