Skip to content

Commit 5188601

Browse files
fix(core): force update_topic tool to execute sequentially (google-gemini#27357)
1 parent e6f92d6 commit 5188601

2 files changed

Lines changed: 46 additions & 0 deletions

File tree

packages/core/src/scheduler/scheduler.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,10 @@ export class Scheduler {
547547
}
548548

549549
private _isParallelizable(request: ToolCallRequestInfo): boolean {
550+
// update_topic tool is forced as sequential call
551+
if (request.name === UPDATE_TOPIC_TOOL_NAME) {
552+
return false;
553+
}
550554
if (request.args) {
551555
const wait = request.args['wait_for_previous'];
552556
if (typeof wait === 'boolean') {

packages/core/src/scheduler/scheduler_parallel.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ import {
7979
type Status,
8080
type ToolCall,
8181
} from './types.js';
82+
import { UPDATE_TOPIC_TOOL_NAME } from '../tools/tool-names.js';
8283
import { GeminiCliOperation } from '../telemetry/constants.js';
8384
import type { EditorType } from '../utils/editor.js';
8485

@@ -172,6 +173,12 @@ describe('Scheduler Parallel Execution', () => {
172173
isReadOnly: false,
173174
build: vi.fn(),
174175
} as unknown as AnyDeclarativeTool;
176+
const topicTool = {
177+
name: UPDATE_TOPIC_TOOL_NAME,
178+
kind: Kind.Other,
179+
isReadOnly: false,
180+
build: vi.fn(),
181+
} as unknown as AnyDeclarativeTool;
175182

176183
const mockInvocation = {
177184
shouldConfirmExecute: vi.fn().mockResolvedValue(false),
@@ -195,6 +202,7 @@ describe('Scheduler Parallel Execution', () => {
195202
if (name === 'write-tool') return writeTool;
196203
if (name === 'agent-tool-1') return agentTool1;
197204
if (name === 'agent-tool-2') return agentTool2;
205+
if (name === UPDATE_TOPIC_TOOL_NAME) return topicTool;
198206
return undefined;
199207
}),
200208
getAllToolNames: vi
@@ -205,6 +213,7 @@ describe('Scheduler Parallel Execution', () => {
205213
'write-tool',
206214
'agent-tool-1',
207215
'agent-tool-2',
216+
UPDATE_TOPIC_TOOL_NAME,
208217
]),
209218
} as unknown as Mocked<ToolRegistry>;
210219

@@ -333,6 +342,9 @@ describe('Scheduler Parallel Execution', () => {
333342
vi.mocked(agentTool2.build).mockReturnValue(
334343
mockInvocation as unknown as AnyToolInvocation,
335344
);
345+
vi.mocked(topicTool.build).mockReturnValue(
346+
mockInvocation as unknown as AnyToolInvocation,
347+
);
336348
});
337349

338350
afterEach(() => {
@@ -555,4 +567,34 @@ describe('Scheduler Parallel Execution', () => {
555567
expect(executionLog[2]).toBe('start-r2');
556568
expect(executionLog[3]).toBe('end-r2');
557569
});
570+
571+
it('should execute UPDATE_TOPIC_TOOL_NAME sequentially even without wait_for_previous', async () => {
572+
const executionLog: string[] = [];
573+
mockExecutor.execute.mockImplementation(async ({ call }) => {
574+
const id = call.request.callId;
575+
executionLog.push(`start-${id}`);
576+
await new Promise<void>((resolve) => setTimeout(resolve, 10));
577+
executionLog.push(`end-${id}`);
578+
return {
579+
status: 'success',
580+
response: { callId: id, responseParts: [] },
581+
} as unknown as SuccessfulToolCall;
582+
});
583+
584+
const topicReq: ToolCallRequestInfo = {
585+
callId: 'call-topic',
586+
name: UPDATE_TOPIC_TOOL_NAME,
587+
args: { title: 'New Topic' },
588+
isClientInitiated: false,
589+
prompt_id: 'p1',
590+
schedulerId: ROOT_SCHEDULER_ID,
591+
};
592+
593+
await scheduler.schedule([req1, topicReq, req2], signal);
594+
595+
expect(executionLog[0]).toBe('start-call-topic');
596+
expect(executionLog[1]).toBe('end-call-topic');
597+
expect(executionLog.slice(2, 4)).toContain('start-call-1');
598+
expect(executionLog.slice(2, 4)).toContain('start-call-2');
599+
});
558600
});

0 commit comments

Comments
 (0)