feat(core): Stop context window overflow when sending chat (#10459)

This commit is contained in:
Sandy Tao
2025-10-08 15:20:44 -07:00
committed by GitHub
parent bcbcaeb82c
commit 06920402f8
5 changed files with 276 additions and 9 deletions
@@ -1854,6 +1854,109 @@ describe('useGeminiStream', () => {
});
});
it('should add info message for ContextWindowWillOverflow event', async () => {
// Setup mock to return a stream with ContextWindowWillOverflow event
mockSendMessageStream.mockReturnValue(
(async function* () {
yield {
type: ServerGeminiEventType.ContextWindowWillOverflow,
value: {
estimatedRequestTokenCount: 100,
remainingTokenCount: 50,
},
};
})(),
);
const { result } = renderHook(() =>
useGeminiStream(
new MockedGeminiClientClass(mockConfig),
[],
mockAddItem,
mockConfig,
mockLoadedSettings,
mockOnDebugMessage,
mockHandleSlashCommand,
false,
() => 'vscode' as EditorType,
() => {},
() => Promise.resolve(),
false,
() => {},
() => {},
() => {},
() => {},
80,
24,
),
);
// Submit a query
await act(async () => {
await result.current.submitQuery('Test overflow');
});
// Check that the info message was added
await waitFor(() => {
expect(mockAddItem).toHaveBeenCalledWith(
{
type: 'info',
text: `Sending this message (100 tokens) might exceed the remaining context window limit (50 tokens). Please try reducing the size of your message or use the \`/compress\` command to compress the chat history.`,
},
expect.any(Number),
);
});
});
it('should call onCancelSubmit when ContextWindowWillOverflow event is received', async () => {
const onCancelSubmitSpy = vi.fn();
// Setup mock to return a stream with ContextWindowWillOverflow event
mockSendMessageStream.mockReturnValue(
(async function* () {
yield {
type: ServerGeminiEventType.ContextWindowWillOverflow,
value: {
estimatedRequestTokenCount: 100,
remainingTokenCount: 50,
},
};
})(),
);
const { result } = renderHook(() =>
useGeminiStream(
new MockedGeminiClientClass(mockConfig),
[],
mockAddItem,
mockConfig,
mockLoadedSettings,
mockOnDebugMessage,
mockHandleSlashCommand,
false,
() => 'vscode' as EditorType,
() => {},
() => Promise.resolve(),
false,
() => {},
() => {},
onCancelSubmitSpy,
() => {},
80,
24,
),
);
// Submit a query
await act(async () => {
await result.current.submitQuery('Test overflow');
});
// Check that onCancelSubmit was called
await waitFor(() => {
expect(onCancelSubmitSpy).toHaveBeenCalled();
});
});
it('should not add message for STOP finish reason', async () => {
// Setup mock to return a stream with STOP finish reason
mockSendMessageStream.mockReturnValue(
@@ -637,6 +637,21 @@ export const useGeminiStream = (
[addItem, config],
);
const handleContextWindowWillOverflowEvent = useCallback(
(estimatedRequestTokenCount: number, remainingTokenCount: number) => {
onCancelSubmit();
addItem(
{
type: 'info',
text: `Sending this message (${estimatedRequestTokenCount} tokens) might exceed the remaining context window limit (${remainingTokenCount} tokens). Please try reducing the size of your message or use the \`/compress\` command to compress the chat history.`,
},
Date.now(),
);
},
[addItem, onCancelSubmit],
);
const handleLoopDetectionConfirmation = useCallback(
(result: { userSelection: 'disable' | 'keep' }) => {
setLoopDetectionConfirmationRequest(null);
@@ -709,6 +724,12 @@ export const useGeminiStream = (
case ServerGeminiEventType.MaxSessionTurns:
handleMaxSessionTurnsEvent();
break;
case ServerGeminiEventType.ContextWindowWillOverflow:
handleContextWindowWillOverflowEvent(
event.value.estimatedRequestTokenCount,
event.value.remainingTokenCount,
);
break;
case ServerGeminiEventType.Finished:
handleFinishedEvent(
event as ServerGeminiFinishedEvent,
@@ -746,6 +767,7 @@ export const useGeminiStream = (
handleChatCompressionEvent,
handleFinishedEvent,
handleMaxSessionTurnsEvent,
handleContextWindowWillOverflowEvent,
handleCitationEvent,
],
);