mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-13 05:12:55 -07:00
fix(ui): ensure rationale renders before tool calls (#17043)
This commit is contained in:
committed by
Sandy Tao
parent
de86bccd0d
commit
f6a5fa0e03
@@ -2205,6 +2205,98 @@ describe('useGeminiStream', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should flush pending text rationale before scheduling tool calls to ensure correct history order', async () => {
|
||||||
|
const addItemOrder: string[] = [];
|
||||||
|
let capturedOnComplete: any;
|
||||||
|
|
||||||
|
const mockScheduleToolCalls = vi.fn(async (requests) => {
|
||||||
|
addItemOrder.push('scheduleToolCalls_START');
|
||||||
|
// Simulate tools completing and triggering onComplete immediately.
|
||||||
|
// This mimics the behavior that caused the regression where tool results
|
||||||
|
// were added to history during the await scheduleToolCalls(...) block.
|
||||||
|
const tools = requests.map((r: any) => ({
|
||||||
|
request: r,
|
||||||
|
status: 'success',
|
||||||
|
tool: { displayName: r.name, name: r.name },
|
||||||
|
invocation: { getDescription: () => 'desc' },
|
||||||
|
response: { responseParts: [], resultDisplay: 'done' },
|
||||||
|
startTime: Date.now(),
|
||||||
|
endTime: Date.now(),
|
||||||
|
}));
|
||||||
|
await capturedOnComplete(tools);
|
||||||
|
addItemOrder.push('scheduleToolCalls_END');
|
||||||
|
});
|
||||||
|
|
||||||
|
mockAddItem.mockImplementation((item: any) => {
|
||||||
|
addItemOrder.push(`addItem:${item.type}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// We need to capture the onComplete callback from useReactToolScheduler
|
||||||
|
const mockUseReactToolScheduler = useReactToolScheduler as Mock;
|
||||||
|
mockUseReactToolScheduler.mockImplementation((onComplete) => {
|
||||||
|
capturedOnComplete = onComplete;
|
||||||
|
return [
|
||||||
|
[], // toolCalls
|
||||||
|
mockScheduleToolCalls,
|
||||||
|
vi.fn(), // markToolsAsSubmitted
|
||||||
|
vi.fn(), // setToolCallsForDisplay
|
||||||
|
vi.fn(), // cancelAllToolCalls
|
||||||
|
0, // lastToolOutputTime
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useGeminiStream(
|
||||||
|
new MockedGeminiClientClass(mockConfig),
|
||||||
|
[],
|
||||||
|
mockAddItem,
|
||||||
|
mockConfig,
|
||||||
|
mockLoadedSettings,
|
||||||
|
vi.fn(),
|
||||||
|
vi.fn(),
|
||||||
|
false,
|
||||||
|
() => 'vscode' as EditorType,
|
||||||
|
vi.fn(),
|
||||||
|
vi.fn(),
|
||||||
|
false,
|
||||||
|
vi.fn(),
|
||||||
|
vi.fn(),
|
||||||
|
vi.fn(),
|
||||||
|
80,
|
||||||
|
24,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const mockStream = (async function* () {
|
||||||
|
yield {
|
||||||
|
type: ServerGeminiEventType.Content,
|
||||||
|
value: 'Rationale rationale.',
|
||||||
|
};
|
||||||
|
yield {
|
||||||
|
type: ServerGeminiEventType.ToolCallRequest,
|
||||||
|
value: { callId: '1', name: 'test_tool', args: {} },
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
mockSendMessageStream.mockReturnValue(mockStream);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await result.current.submitQuery('test input');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Expectation: addItem:gemini (rationale) MUST happen before scheduleToolCalls_START
|
||||||
|
const rationaleIndex = addItemOrder.indexOf('addItem:gemini');
|
||||||
|
const scheduleIndex = addItemOrder.indexOf('scheduleToolCalls_START');
|
||||||
|
const toolGroupIndex = addItemOrder.indexOf('addItem:tool_group');
|
||||||
|
|
||||||
|
expect(rationaleIndex).toBeGreaterThan(-1);
|
||||||
|
expect(scheduleIndex).toBeGreaterThan(-1);
|
||||||
|
expect(toolGroupIndex).toBeGreaterThan(-1);
|
||||||
|
|
||||||
|
// This is the core fix validation: Rationale comes before tools are even scheduled (awaited)
|
||||||
|
expect(rationaleIndex).toBeLessThan(scheduleIndex);
|
||||||
|
expect(rationaleIndex).toBeLessThan(toolGroupIndex);
|
||||||
|
});
|
||||||
|
|
||||||
it('should process @include commands, adding user turn after processing to prevent race conditions', async () => {
|
it('should process @include commands, adding user turn after processing to prevent race conditions', async () => {
|
||||||
const rawQuery = '@include file.txt Summarize this.';
|
const rawQuery = '@include file.txt Summarize this.';
|
||||||
const processedQueryParts = [
|
const processedQueryParts = [
|
||||||
|
|||||||
@@ -921,6 +921,10 @@ export const useGeminiStream = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (toolCallRequests.length > 0) {
|
if (toolCallRequests.length > 0) {
|
||||||
|
if (pendingHistoryItemRef.current) {
|
||||||
|
addItem(pendingHistoryItemRef.current, userMessageTimestamp);
|
||||||
|
setPendingHistoryItem(null);
|
||||||
|
}
|
||||||
await scheduleToolCalls(toolCallRequests, signal);
|
await scheduleToolCalls(toolCallRequests, signal);
|
||||||
}
|
}
|
||||||
return StreamProcessingStatus.Completed;
|
return StreamProcessingStatus.Completed;
|
||||||
@@ -938,6 +942,9 @@ export const useGeminiStream = (
|
|||||||
handleChatModelEvent,
|
handleChatModelEvent,
|
||||||
handleAgentExecutionStoppedEvent,
|
handleAgentExecutionStoppedEvent,
|
||||||
handleAgentExecutionBlockedEvent,
|
handleAgentExecutionBlockedEvent,
|
||||||
|
addItem,
|
||||||
|
pendingHistoryItemRef,
|
||||||
|
setPendingHistoryItem,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
const submitQuery = useCallback(
|
const submitQuery = useCallback(
|
||||||
|
|||||||
Reference in New Issue
Block a user