Skip to content

Commit f96d5f9

Browse files
authored
Revert "fix(ui): improve narration suppression and reduce flicker (#2… (#24857)
1 parent 3c5b5db commit f96d5f9

File tree

2 files changed

+9
-192
lines changed

2 files changed

+9
-192
lines changed

packages/cli/src/ui/components/MainContent.test.tsx

Lines changed: 1 addition & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@
66

77
import { renderWithProviders } from '../../test-utils/render.js';
88
import { createMockSettings } from '../../test-utils/settings.js';
9-
import {
10-
makeFakeConfig,
11-
CoreToolCallStatus,
12-
UPDATE_TOPIC_TOOL_NAME,
13-
} from '@google/gemini-cli-core';
9+
import { makeFakeConfig, CoreToolCallStatus } from '@google/gemini-cli-core';
1410
import { waitFor } from '../../test-utils/async.js';
1511
import { MainContent } from './MainContent.js';
1612
import { getToolGroupBorderAppearance } from '../utils/borderStyles.js';
@@ -732,158 +728,6 @@ describe('MainContent', () => {
732728
unmount();
733729
});
734730

735-
describe('Narration Suppression', () => {
736-
const settingsWithNarration = createMockSettings({
737-
merged: {
738-
ui: { inlineThinkingMode: 'expanded' },
739-
experimental: { topicUpdateNarration: true },
740-
},
741-
});
742-
743-
it('suppresses thinking ALWAYS when narration is enabled', async () => {
744-
mockUseSettings.mockReturnValue(settingsWithNarration);
745-
const uiState = {
746-
...defaultMockUiState,
747-
history: [
748-
{ id: 1, type: 'user' as const, text: 'Hello' },
749-
{
750-
id: 2,
751-
type: 'thinking' as const,
752-
thought: {
753-
subject: 'Thinking...',
754-
description: 'Thinking about hello',
755-
},
756-
},
757-
{ id: 3, type: 'gemini' as const, text: 'I am helping.' },
758-
],
759-
};
760-
761-
const { lastFrame, unmount } = await renderWithProviders(
762-
<MainContent />,
763-
{
764-
uiState: uiState as Partial<UIState>,
765-
settings: settingsWithNarration,
766-
},
767-
);
768-
769-
const output = lastFrame();
770-
expect(output).not.toContain('Thinking...');
771-
expect(output).toContain('I am helping.');
772-
unmount();
773-
});
774-
775-
it('suppresses text in intermediate turns (contains non-topic tools)', async () => {
776-
mockUseSettings.mockReturnValue(settingsWithNarration);
777-
const uiState = {
778-
...defaultMockUiState,
779-
history: [
780-
{ id: 100, type: 'user' as const, text: 'Search' },
781-
{
782-
id: 101,
783-
type: 'gemini' as const,
784-
text: 'I will now search the files.',
785-
},
786-
{
787-
id: 102,
788-
type: 'tool_group' as const,
789-
tools: [
790-
{
791-
callId: '1',
792-
name: 'ls',
793-
args: { path: '.' },
794-
status: CoreToolCallStatus.Success,
795-
},
796-
],
797-
},
798-
],
799-
};
800-
801-
const { lastFrame, unmount } = await renderWithProviders(
802-
<MainContent />,
803-
{
804-
uiState: uiState as Partial<UIState>,
805-
settings: settingsWithNarration,
806-
},
807-
);
808-
809-
const output = lastFrame();
810-
expect(output).not.toContain('I will now search the files.');
811-
unmount();
812-
});
813-
814-
it('suppresses text that precedes a topic tool in the same turn', async () => {
815-
mockUseSettings.mockReturnValue(settingsWithNarration);
816-
const uiState = {
817-
...defaultMockUiState,
818-
history: [
819-
{ id: 200, type: 'user' as const, text: 'Hello' },
820-
{ id: 201, type: 'gemini' as const, text: 'I will now help you.' },
821-
{
822-
id: 202,
823-
type: 'tool_group' as const,
824-
tools: [
825-
{
826-
callId: '1',
827-
name: UPDATE_TOPIC_TOOL_NAME,
828-
args: { title: 'Helping', summary: 'Helping the user' },
829-
status: CoreToolCallStatus.Success,
830-
},
831-
],
832-
},
833-
],
834-
};
835-
836-
const { lastFrame, unmount } = await renderWithProviders(
837-
<MainContent />,
838-
{
839-
uiState: uiState as Partial<UIState>,
840-
settings: settingsWithNarration,
841-
},
842-
);
843-
844-
const output = lastFrame();
845-
expect(output).not.toContain('I will now help you.');
846-
expect(output).toContain('Helping');
847-
expect(output).toContain('Helping the user');
848-
unmount();
849-
});
850-
851-
it('shows text in the final turn if it comes AFTER the topic tool', async () => {
852-
mockUseSettings.mockReturnValue(settingsWithNarration);
853-
const uiState = {
854-
...defaultMockUiState,
855-
history: [
856-
{ id: 300, type: 'user' as const, text: 'Hello' },
857-
{
858-
id: 301,
859-
type: 'tool_group' as const,
860-
tools: [
861-
{
862-
callId: '1',
863-
name: UPDATE_TOPIC_TOOL_NAME,
864-
args: { title: 'Final Answer', summary: 'I have finished' },
865-
status: CoreToolCallStatus.Success,
866-
},
867-
],
868-
},
869-
{ id: 302, type: 'gemini' as const, text: 'Here is your answer.' },
870-
],
871-
};
872-
873-
const { lastFrame, unmount } = await renderWithProviders(
874-
<MainContent />,
875-
{
876-
uiState: uiState as Partial<UIState>,
877-
settings: settingsWithNarration,
878-
},
879-
);
880-
881-
const output = lastFrame();
882-
expect(output).toContain('Here is your answer.');
883-
unmount();
884-
});
885-
});
886-
887731
it('renders multiple thinking messages sequentially correctly', async () => {
888732
mockUseSettings.mockReturnValue({
889733
merged: {

packages/cli/src/ui/components/MainContent.tsx

Lines changed: 8 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -91,47 +91,20 @@ export const MainContent = () => {
9191
const flags = new Array<boolean>(combinedHistory.length).fill(false);
9292

9393
if (topicUpdateNarrationEnabled) {
94-
let turnIsIntermediate = false;
95-
let hasTopicToolInTurn = false;
96-
94+
let toolGroupInTurn = false;
9795
for (let i = combinedHistory.length - 1; i >= 0; i--) {
9896
const item = combinedHistory[i];
9997
if (item.type === 'user' || item.type === 'user_shell') {
100-
turnIsIntermediate = false;
101-
hasTopicToolInTurn = false;
98+
toolGroupInTurn = false;
10299
} else if (item.type === 'tool_group') {
103-
const hasTopic = item.tools.some((t) => isTopicTool(t.name));
104-
const hasNonTopic = item.tools.some((t) => !isTopicTool(t.name));
105-
if (hasTopic) {
106-
hasTopicToolInTurn = true;
107-
}
108-
if (hasNonTopic) {
109-
turnIsIntermediate = true;
110-
}
100+
toolGroupInTurn = item.tools.some((t) => isTopicTool(t.name));
111101
} else if (
112-
item.type === 'thinking' ||
113-
item.type === 'gemini' ||
114-
item.type === 'gemini_content'
102+
(item.type === 'thinking' ||
103+
item.type === 'gemini' ||
104+
item.type === 'gemini_content') &&
105+
toolGroupInTurn
115106
) {
116-
// Rule 1: Always suppress thinking when narration is enabled to avoid
117-
// "flashing" as the model starts its response, and because the Topic
118-
// UI provides the necessary high-level intent.
119-
if (item.type === 'thinking') {
120-
flags[i] = true;
121-
continue;
122-
}
123-
124-
// Rule 2: Suppress text in intermediate turns (turns containing non-topic
125-
// tools) to hide mechanical narration.
126-
if (turnIsIntermediate) {
127-
flags[i] = true;
128-
}
129-
130-
// Rule 3: Suppress text that precedes a topic tool in the same turn,
131-
// as the topic tool "replaces" it.
132-
if (hasTopicToolInTurn) {
133-
flags[i] = true;
134-
}
107+
flags[i] = true;
135108
}
136109
}
137110
}

0 commit comments

Comments
 (0)