mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-29 14:34:55 -07:00
refactor(cli): switch useToolScheduler to event-driven engine (#18565)
This commit is contained in:
@@ -45,7 +45,6 @@ export const createMockConfig = (overrides: Partial<Config> = {}): Config =>
|
|||||||
setRemoteAdminSettings: vi.fn(),
|
setRemoteAdminSettings: vi.fn(),
|
||||||
isYoloModeDisabled: vi.fn(() => false),
|
isYoloModeDisabled: vi.fn(() => false),
|
||||||
isPlanEnabled: vi.fn(() => false),
|
isPlanEnabled: vi.fn(() => false),
|
||||||
isEventDrivenSchedulerEnabled: vi.fn(() => false),
|
|
||||||
getCoreTools: vi.fn(() => []),
|
getCoreTools: vi.fn(() => []),
|
||||||
getAllowedTools: vi.fn(() => []),
|
getAllowedTools: vi.fn(() => []),
|
||||||
getApprovalMode: vi.fn(() => 'default'),
|
getApprovalMode: vi.fn(() => 'default'),
|
||||||
|
|||||||
@@ -389,7 +389,6 @@ export const useGeminiStream = (
|
|||||||
toolCalls.length > 0 &&
|
toolCalls.length > 0 &&
|
||||||
toolCalls.every((tc) => pushedToolCallIds.has(tc.request.callId));
|
toolCalls.every((tc) => pushedToolCallIds.has(tc.request.callId));
|
||||||
|
|
||||||
const isEventDriven = config.isEventDrivenSchedulerEnabled();
|
|
||||||
const anyVisibleInHistory = pushedToolCallIds.size > 0;
|
const anyVisibleInHistory = pushedToolCallIds.size > 0;
|
||||||
const anyVisibleInPending = remainingTools.some((tc) => {
|
const anyVisibleInPending = remainingTools.some((tc) => {
|
||||||
// AskUser tools are rendered by AskUserDialog, not ToolGroupMessage
|
// AskUser tools are rendered by AskUserDialog, not ToolGroupMessage
|
||||||
@@ -400,7 +399,6 @@ export const useGeminiStream = (
|
|||||||
if (tc.request.name === ASK_USER_TOOL_NAME && isInProgress) {
|
if (tc.request.name === ASK_USER_TOOL_NAME && isInProgress) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!isEventDriven) return true;
|
|
||||||
return (
|
return (
|
||||||
tc.status !== 'scheduled' &&
|
tc.status !== 'scheduled' &&
|
||||||
tc.status !== 'validating' &&
|
tc.status !== 'validating' &&
|
||||||
@@ -422,7 +420,7 @@ export const useGeminiStream = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}, [toolCalls, pushedToolCallIds, config]);
|
}, [toolCalls, pushedToolCallIds]);
|
||||||
|
|
||||||
const activeToolPtyId = useMemo(() => {
|
const activeToolPtyId = useMemo(() => {
|
||||||
const executingShellTool = toolCalls.find(
|
const executingShellTool = toolCalls.find(
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ import type {
|
|||||||
ToolCallRequestInfo,
|
ToolCallRequestInfo,
|
||||||
} from '@google/gemini-cli-core';
|
} from '@google/gemini-cli-core';
|
||||||
import {
|
import {
|
||||||
useReactToolScheduler,
|
|
||||||
type TrackedToolCall as LegacyTrackedToolCall,
|
|
||||||
type TrackedScheduledToolCall,
|
type TrackedScheduledToolCall,
|
||||||
type TrackedValidatingToolCall,
|
type TrackedValidatingToolCall,
|
||||||
type TrackedWaitingToolCall,
|
type TrackedWaitingToolCall,
|
||||||
@@ -24,12 +22,13 @@ import {
|
|||||||
} from './useReactToolScheduler.js';
|
} from './useReactToolScheduler.js';
|
||||||
import {
|
import {
|
||||||
useToolExecutionScheduler,
|
useToolExecutionScheduler,
|
||||||
type TrackedToolCall as NewTrackedToolCall,
|
type TrackedToolCall,
|
||||||
} from './useToolExecutionScheduler.js';
|
} from './useToolExecutionScheduler.js';
|
||||||
|
|
||||||
// Re-export specific state types from Legacy, as the structures are compatible
|
// Re-export specific state types from Legacy, as the structures are compatible
|
||||||
// and useGeminiStream relies on them for narrowing.
|
// and useGeminiStream relies on them for narrowing.
|
||||||
export type {
|
export type {
|
||||||
|
TrackedToolCall,
|
||||||
TrackedScheduledToolCall,
|
TrackedScheduledToolCall,
|
||||||
TrackedValidatingToolCall,
|
TrackedValidatingToolCall,
|
||||||
TrackedWaitingToolCall,
|
TrackedWaitingToolCall,
|
||||||
@@ -40,9 +39,6 @@ export type {
|
|||||||
CancelAllFn,
|
CancelAllFn,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Unified type that covers both implementations
|
|
||||||
export type TrackedToolCall = LegacyTrackedToolCall | NewTrackedToolCall;
|
|
||||||
|
|
||||||
// Unified Schedule function (Promise<void> | Promise<CompletedToolCall[]>)
|
// Unified Schedule function (Promise<void> | Promise<CompletedToolCall[]>)
|
||||||
export type ScheduleFn = (
|
export type ScheduleFn = (
|
||||||
request: ToolCallRequestInfo | ToolCallRequestInfo[],
|
request: ToolCallRequestInfo | ToolCallRequestInfo[],
|
||||||
@@ -59,30 +55,16 @@ export type UseToolSchedulerReturn = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Facade hook that switches between the Legacy and Event-Driven schedulers
|
* Hook that uses the Event-Driven scheduler for tool execution.
|
||||||
* based on configuration.
|
|
||||||
*
|
|
||||||
* Note: This conditionally calls hooks, which technically violates the standard
|
|
||||||
* Rules of Hooks linting. However, this is safe here because
|
|
||||||
* `config.isEventDrivenSchedulerEnabled()` is static for the lifetime of the
|
|
||||||
* application session (it essentially acts as a compile-time feature flag).
|
|
||||||
*/
|
*/
|
||||||
export function useToolScheduler(
|
export function useToolScheduler(
|
||||||
onComplete: (tools: CompletedToolCall[]) => Promise<void>,
|
onComplete: (tools: CompletedToolCall[]) => Promise<void>,
|
||||||
config: Config,
|
config: Config,
|
||||||
getPreferredEditor: () => EditorType | undefined,
|
getPreferredEditor: () => EditorType | undefined,
|
||||||
): UseToolSchedulerReturn {
|
): UseToolSchedulerReturn {
|
||||||
const isEventDriven = config.isEventDrivenSchedulerEnabled();
|
return useToolExecutionScheduler(
|
||||||
|
onComplete,
|
||||||
// Note: We return the hooks directly without casting. They return compatible
|
config,
|
||||||
// tuple structures, but use explicit tuple signatures rather than the
|
getPreferredEditor,
|
||||||
// UseToolSchedulerReturn named type to avoid circular dependencies back to
|
) as UseToolSchedulerReturn;
|
||||||
// this facade.
|
|
||||||
if (isEventDriven) {
|
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
||||||
return useToolExecutionScheduler(onComplete, config, getPreferredEditor);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
||||||
return useReactToolScheduler(onComplete, config, getPreferredEditor);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright 2026 Google LLC
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
||||||
import { renderHook } from '../../test-utils/render.js';
|
|
||||||
import { useToolScheduler } from './useToolScheduler.js';
|
|
||||||
import { useReactToolScheduler } from './useReactToolScheduler.js';
|
|
||||||
import { useToolExecutionScheduler } from './useToolExecutionScheduler.js';
|
|
||||||
import type { Config } from '@google/gemini-cli-core';
|
|
||||||
|
|
||||||
vi.mock('./useReactToolScheduler.js', () => ({
|
|
||||||
useReactToolScheduler: vi.fn().mockReturnValue(['legacy']),
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./useToolExecutionScheduler.js', () => ({
|
|
||||||
useToolExecutionScheduler: vi.fn().mockReturnValue(['modern']),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('useToolScheduler (Facade)', () => {
|
|
||||||
let mockConfig: Config;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
vi.clearAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('delegates to useReactToolScheduler when event-driven scheduler is disabled', () => {
|
|
||||||
mockConfig = {
|
|
||||||
isEventDrivenSchedulerEnabled: () => false,
|
|
||||||
} as unknown as Config;
|
|
||||||
|
|
||||||
const onComplete = vi.fn();
|
|
||||||
const getPreferredEditor = vi.fn();
|
|
||||||
|
|
||||||
const { result } = renderHook(() =>
|
|
||||||
useToolScheduler(onComplete, mockConfig, getPreferredEditor),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result.current).toEqual(['legacy']);
|
|
||||||
expect(useReactToolScheduler).toHaveBeenCalledWith(
|
|
||||||
onComplete,
|
|
||||||
mockConfig,
|
|
||||||
getPreferredEditor,
|
|
||||||
);
|
|
||||||
expect(useToolExecutionScheduler).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('delegates to useToolExecutionScheduler when event-driven scheduler is enabled', () => {
|
|
||||||
mockConfig = {
|
|
||||||
isEventDrivenSchedulerEnabled: () => true,
|
|
||||||
} as unknown as Config;
|
|
||||||
|
|
||||||
const onComplete = vi.fn();
|
|
||||||
const getPreferredEditor = vi.fn();
|
|
||||||
|
|
||||||
const { result } = renderHook(() =>
|
|
||||||
useToolScheduler(onComplete, mockConfig, getPreferredEditor),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result.current).toEqual(['modern']);
|
|
||||||
expect(useToolExecutionScheduler).toHaveBeenCalledWith(
|
|
||||||
onComplete,
|
|
||||||
mockConfig,
|
|
||||||
getPreferredEditor,
|
|
||||||
);
|
|
||||||
expect(useReactToolScheduler).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user