mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-21 02:24:09 -07:00
refactor(cli): centralize tool mapping and decouple legacy scheduler (#17044)
This commit is contained in:
@@ -7,33 +7,27 @@
|
||||
import type {
|
||||
Config,
|
||||
ToolCallRequestInfo,
|
||||
ExecutingToolCall,
|
||||
ScheduledToolCall,
|
||||
ValidatingToolCall,
|
||||
WaitingToolCall,
|
||||
CompletedToolCall,
|
||||
CancelledToolCall,
|
||||
OutputUpdateHandler,
|
||||
AllToolCallsCompleteHandler,
|
||||
ToolCallsUpdateHandler,
|
||||
ToolCall,
|
||||
ToolCallConfirmationDetails,
|
||||
Status as CoreStatus,
|
||||
EditorType,
|
||||
CompletedToolCall,
|
||||
ExecutingToolCall,
|
||||
ScheduledToolCall,
|
||||
ValidatingToolCall,
|
||||
WaitingToolCall,
|
||||
CancelledToolCall,
|
||||
} from '@google/gemini-cli-core';
|
||||
import { CoreToolScheduler, debugLogger } from '@google/gemini-cli-core';
|
||||
import { CoreToolScheduler } from '@google/gemini-cli-core';
|
||||
import { useCallback, useState, useMemo, useEffect, useRef } from 'react';
|
||||
import type {
|
||||
HistoryItemToolGroup,
|
||||
IndividualToolCallDisplay,
|
||||
} from '../types.js';
|
||||
import { ToolCallStatus } from '../types.js';
|
||||
|
||||
export type ScheduleFn = (
|
||||
request: ToolCallRequestInfo | ToolCallRequestInfo[],
|
||||
signal: AbortSignal,
|
||||
) => Promise<void>;
|
||||
export type MarkToolsAsSubmittedFn = (callIds: string[]) => void;
|
||||
export type CancelAllFn = (signal: AbortSignal) => void;
|
||||
|
||||
export type TrackedScheduledToolCall = ScheduledToolCall & {
|
||||
responseSubmittedToGemini?: boolean;
|
||||
@@ -63,8 +57,12 @@ export type TrackedToolCall =
|
||||
| TrackedCompletedToolCall
|
||||
| TrackedCancelledToolCall;
|
||||
|
||||
export type CancelAllFn = (signal: AbortSignal) => void;
|
||||
|
||||
/**
|
||||
* Legacy scheduler implementation based on CoreToolScheduler callbacks.
|
||||
*
|
||||
* This is currently the default implementation used by useGeminiStream.
|
||||
* It will be phased out once the event-driven scheduler migration is complete.
|
||||
*/
|
||||
export function useReactToolScheduler(
|
||||
onComplete: (tools: CompletedToolCall[]) => Promise<void>,
|
||||
config: Config,
|
||||
@@ -82,7 +80,6 @@ export function useReactToolScheduler(
|
||||
>([]);
|
||||
const [lastToolOutputTime, setLastToolOutputTime] = useState<number>(0);
|
||||
|
||||
// Store callbacks in refs to keep them up-to-date without causing re-renders.
|
||||
const onCompleteRef = useRef(onComplete);
|
||||
const getPreferredEditorRef = useRef(getPreferredEditor);
|
||||
|
||||
@@ -131,7 +128,6 @@ export function useReactToolScheduler(
|
||||
existingTrackedCall?.responseSubmittedToGemini ?? false;
|
||||
|
||||
if (coreTc.status === 'executing') {
|
||||
// Preserve live output if it exists from a previous render.
|
||||
const liveOutput = (existingTrackedCall as TrackedExecutingToolCall)
|
||||
?.liveOutput;
|
||||
return {
|
||||
@@ -215,135 +211,3 @@ export function useReactToolScheduler(
|
||||
lastToolOutputTime,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a CoreToolScheduler status to the UI's ToolCallStatus enum.
|
||||
*/
|
||||
function mapCoreStatusToDisplayStatus(coreStatus: CoreStatus): ToolCallStatus {
|
||||
switch (coreStatus) {
|
||||
case 'validating':
|
||||
return ToolCallStatus.Executing;
|
||||
case 'awaiting_approval':
|
||||
return ToolCallStatus.Confirming;
|
||||
case 'executing':
|
||||
return ToolCallStatus.Executing;
|
||||
case 'success':
|
||||
return ToolCallStatus.Success;
|
||||
case 'cancelled':
|
||||
return ToolCallStatus.Canceled;
|
||||
case 'error':
|
||||
return ToolCallStatus.Error;
|
||||
case 'scheduled':
|
||||
return ToolCallStatus.Pending;
|
||||
default: {
|
||||
const exhaustiveCheck: never = coreStatus;
|
||||
debugLogger.warn(`Unknown core status encountered: ${exhaustiveCheck}`);
|
||||
return ToolCallStatus.Error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms `TrackedToolCall` objects into `HistoryItemToolGroup` objects for UI display.
|
||||
*/
|
||||
export function mapToDisplay(
|
||||
toolOrTools: TrackedToolCall[] | TrackedToolCall,
|
||||
): HistoryItemToolGroup {
|
||||
const toolCalls = Array.isArray(toolOrTools) ? toolOrTools : [toolOrTools];
|
||||
|
||||
const toolDisplays = toolCalls.map(
|
||||
(trackedCall): IndividualToolCallDisplay => {
|
||||
let displayName: string;
|
||||
let description: string;
|
||||
let renderOutputAsMarkdown = false;
|
||||
|
||||
if (trackedCall.status === 'error') {
|
||||
displayName =
|
||||
trackedCall.tool === undefined
|
||||
? trackedCall.request.name
|
||||
: trackedCall.tool.displayName;
|
||||
description = JSON.stringify(trackedCall.request.args);
|
||||
} else {
|
||||
displayName = trackedCall.tool.displayName;
|
||||
description = trackedCall.invocation.getDescription();
|
||||
renderOutputAsMarkdown = trackedCall.tool.isOutputMarkdown;
|
||||
}
|
||||
|
||||
const baseDisplayProperties: Omit<
|
||||
IndividualToolCallDisplay,
|
||||
'status' | 'resultDisplay' | 'confirmationDetails'
|
||||
> = {
|
||||
callId: trackedCall.request.callId,
|
||||
name: displayName,
|
||||
description,
|
||||
renderOutputAsMarkdown,
|
||||
};
|
||||
|
||||
switch (trackedCall.status) {
|
||||
case 'success':
|
||||
return {
|
||||
...baseDisplayProperties,
|
||||
status: mapCoreStatusToDisplayStatus(trackedCall.status),
|
||||
resultDisplay: trackedCall.response.resultDisplay,
|
||||
confirmationDetails: undefined,
|
||||
outputFile: trackedCall.response.outputFile,
|
||||
};
|
||||
case 'error':
|
||||
return {
|
||||
...baseDisplayProperties,
|
||||
status: mapCoreStatusToDisplayStatus(trackedCall.status),
|
||||
resultDisplay: trackedCall.response.resultDisplay,
|
||||
confirmationDetails: undefined,
|
||||
};
|
||||
case 'cancelled':
|
||||
return {
|
||||
...baseDisplayProperties,
|
||||
status: mapCoreStatusToDisplayStatus(trackedCall.status),
|
||||
resultDisplay: trackedCall.response.resultDisplay,
|
||||
confirmationDetails: undefined,
|
||||
};
|
||||
case 'awaiting_approval':
|
||||
return {
|
||||
...baseDisplayProperties,
|
||||
status: mapCoreStatusToDisplayStatus(trackedCall.status),
|
||||
resultDisplay: undefined,
|
||||
confirmationDetails:
|
||||
trackedCall.confirmationDetails as ToolCallConfirmationDetails,
|
||||
};
|
||||
case 'executing':
|
||||
return {
|
||||
...baseDisplayProperties,
|
||||
status: mapCoreStatusToDisplayStatus(trackedCall.status),
|
||||
resultDisplay: trackedCall.liveOutput ?? undefined,
|
||||
confirmationDetails: undefined,
|
||||
ptyId: trackedCall.pid,
|
||||
};
|
||||
case 'validating': // Fallthrough
|
||||
case 'scheduled':
|
||||
return {
|
||||
...baseDisplayProperties,
|
||||
status: mapCoreStatusToDisplayStatus(trackedCall.status),
|
||||
resultDisplay: undefined,
|
||||
confirmationDetails: undefined,
|
||||
};
|
||||
default: {
|
||||
const exhaustiveCheck: never = trackedCall;
|
||||
return {
|
||||
callId: (exhaustiveCheck as TrackedToolCall).request.callId,
|
||||
name: 'Unknown Tool',
|
||||
description: 'Encountered an unknown tool call state.',
|
||||
status: ToolCallStatus.Error,
|
||||
resultDisplay: 'Unknown tool call state',
|
||||
confirmationDetails: undefined,
|
||||
renderOutputAsMarkdown: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
type: 'tool_group',
|
||||
tools: toolDisplays,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user