mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
feat(core): Introduce DeclarativeTool and ToolInvocation. (#5613)
This commit is contained in:
@@ -9,101 +9,243 @@ import { ToolErrorType } from './tool-error.js';
|
||||
import { DiffUpdateResult } from '../ide/ideContext.js';
|
||||
|
||||
/**
|
||||
* Interface representing the base Tool functionality
|
||||
* Represents a validated and ready-to-execute tool call.
|
||||
* An instance of this is created by a `ToolBuilder`.
|
||||
*/
|
||||
export interface Tool<
|
||||
TParams = unknown,
|
||||
TResult extends ToolResult = ToolResult,
|
||||
export interface ToolInvocation<
|
||||
TParams extends object,
|
||||
TResult extends ToolResult,
|
||||
> {
|
||||
/**
|
||||
* The internal name of the tool (used for API calls)
|
||||
* The validated parameters for this specific invocation.
|
||||
*/
|
||||
name: string;
|
||||
params: TParams;
|
||||
|
||||
/**
|
||||
* The user-friendly display name of the tool
|
||||
* Gets a pre-execution description of the tool operation.
|
||||
* @returns A markdown string describing what the tool will do.
|
||||
*/
|
||||
displayName: string;
|
||||
getDescription(): string;
|
||||
|
||||
/**
|
||||
* Description of what the tool does
|
||||
* Determines what file system paths the tool will affect.
|
||||
* @returns A list of such paths.
|
||||
*/
|
||||
description: string;
|
||||
toolLocations(): ToolLocation[];
|
||||
|
||||
/**
|
||||
* The icon to display when interacting via ACP
|
||||
*/
|
||||
icon: Icon;
|
||||
|
||||
/**
|
||||
* Function declaration schema from @google/genai
|
||||
*/
|
||||
schema: FunctionDeclaration;
|
||||
|
||||
/**
|
||||
* Whether the tool's output should be rendered as markdown
|
||||
*/
|
||||
isOutputMarkdown: boolean;
|
||||
|
||||
/**
|
||||
* Whether the tool supports live (streaming) output
|
||||
*/
|
||||
canUpdateOutput: boolean;
|
||||
|
||||
/**
|
||||
* Validates the parameters for the tool
|
||||
* Should be called from both `shouldConfirmExecute` and `execute`
|
||||
* `shouldConfirmExecute` should return false immediately if invalid
|
||||
* @param params Parameters to validate
|
||||
* @returns An error message string if invalid, null otherwise
|
||||
*/
|
||||
validateToolParams(params: TParams): string | null;
|
||||
|
||||
/**
|
||||
* Gets a pre-execution description of the tool operation
|
||||
* @param params Parameters for the tool execution
|
||||
* @returns A markdown string describing what the tool will do
|
||||
* Optional for backward compatibility
|
||||
*/
|
||||
getDescription(params: TParams): string;
|
||||
|
||||
/**
|
||||
* Determines what file system paths the tool will affect
|
||||
* @param params Parameters for the tool execution
|
||||
* @returns A list of such paths
|
||||
*/
|
||||
toolLocations(params: TParams): ToolLocation[];
|
||||
|
||||
/**
|
||||
* Determines if the tool should prompt for confirmation before execution
|
||||
* @param params Parameters for the tool execution
|
||||
* @returns Whether execute should be confirmed.
|
||||
* Determines if the tool should prompt for confirmation before execution.
|
||||
* @returns Confirmation details or false if no confirmation is needed.
|
||||
*/
|
||||
shouldConfirmExecute(
|
||||
params: TParams,
|
||||
abortSignal: AbortSignal,
|
||||
): Promise<ToolCallConfirmationDetails | false>;
|
||||
|
||||
/**
|
||||
* Executes the tool with the given parameters
|
||||
* @param params Parameters for the tool execution
|
||||
* @returns Result of the tool execution
|
||||
* Executes the tool with the validated parameters.
|
||||
* @param signal AbortSignal for tool cancellation.
|
||||
* @param updateOutput Optional callback to stream output.
|
||||
* @returns Result of the tool execution.
|
||||
*/
|
||||
execute(
|
||||
params: TParams,
|
||||
signal: AbortSignal,
|
||||
updateOutput?: (output: string) => void,
|
||||
): Promise<TResult>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A type alias for a tool invocation where the specific parameter and result types are not known.
|
||||
*/
|
||||
export type AnyToolInvocation = ToolInvocation<object, ToolResult>;
|
||||
|
||||
/**
|
||||
* An adapter that wraps the legacy `Tool` interface to make it compatible
|
||||
* with the new `ToolInvocation` pattern.
|
||||
*/
|
||||
export class LegacyToolInvocation<
|
||||
TParams extends object,
|
||||
TResult extends ToolResult,
|
||||
> implements ToolInvocation<TParams, TResult>
|
||||
{
|
||||
constructor(
|
||||
private readonly legacyTool: BaseTool<TParams, TResult>,
|
||||
readonly params: TParams,
|
||||
) {}
|
||||
|
||||
getDescription(): string {
|
||||
return this.legacyTool.getDescription(this.params);
|
||||
}
|
||||
|
||||
toolLocations(): ToolLocation[] {
|
||||
return this.legacyTool.toolLocations(this.params);
|
||||
}
|
||||
|
||||
shouldConfirmExecute(
|
||||
abortSignal: AbortSignal,
|
||||
): Promise<ToolCallConfirmationDetails | false> {
|
||||
return this.legacyTool.shouldConfirmExecute(this.params, abortSignal);
|
||||
}
|
||||
|
||||
execute(
|
||||
signal: AbortSignal,
|
||||
updateOutput?: (output: string) => void,
|
||||
): Promise<TResult> {
|
||||
return this.legacyTool.execute(this.params, signal, updateOutput);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for a tool builder that validates parameters and creates invocations.
|
||||
*/
|
||||
export interface ToolBuilder<
|
||||
TParams extends object,
|
||||
TResult extends ToolResult,
|
||||
> {
|
||||
/**
|
||||
* The internal name of the tool (used for API calls).
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* The user-friendly display name of the tool.
|
||||
*/
|
||||
displayName: string;
|
||||
|
||||
/**
|
||||
* Description of what the tool does.
|
||||
*/
|
||||
description: string;
|
||||
|
||||
/**
|
||||
* The icon to display when interacting via ACP.
|
||||
*/
|
||||
icon: Icon;
|
||||
|
||||
/**
|
||||
* Function declaration schema from @google/genai.
|
||||
*/
|
||||
schema: FunctionDeclaration;
|
||||
|
||||
/**
|
||||
* Whether the tool's output should be rendered as markdown.
|
||||
*/
|
||||
isOutputMarkdown: boolean;
|
||||
|
||||
/**
|
||||
* Whether the tool supports live (streaming) output.
|
||||
*/
|
||||
canUpdateOutput: boolean;
|
||||
|
||||
/**
|
||||
* Validates raw parameters and builds a ready-to-execute invocation.
|
||||
* @param params The raw, untrusted parameters from the model.
|
||||
* @returns A valid `ToolInvocation` if successful. Throws an error if validation fails.
|
||||
*/
|
||||
build(params: TParams): ToolInvocation<TParams, TResult>;
|
||||
}
|
||||
|
||||
/**
|
||||
* New base class for tools that separates validation from execution.
|
||||
* New tools should extend this class.
|
||||
*/
|
||||
export abstract class DeclarativeTool<
|
||||
TParams extends object,
|
||||
TResult extends ToolResult,
|
||||
> implements ToolBuilder<TParams, TResult>
|
||||
{
|
||||
constructor(
|
||||
readonly name: string,
|
||||
readonly displayName: string,
|
||||
readonly description: string,
|
||||
readonly icon: Icon,
|
||||
readonly parameterSchema: Schema,
|
||||
readonly isOutputMarkdown: boolean = true,
|
||||
readonly canUpdateOutput: boolean = false,
|
||||
) {}
|
||||
|
||||
get schema(): FunctionDeclaration {
|
||||
return {
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
parameters: this.parameterSchema,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the raw tool parameters.
|
||||
* Subclasses should override this to add custom validation logic
|
||||
* beyond the JSON schema check.
|
||||
* @param params The raw parameters from the model.
|
||||
* @returns An error message string if invalid, null otherwise.
|
||||
*/
|
||||
protected validateToolParams(_params: TParams): string | null {
|
||||
// Base implementation can be extended by subclasses.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The core of the new pattern. It validates parameters and, if successful,
|
||||
* returns a `ToolInvocation` object that encapsulates the logic for the
|
||||
* specific, validated call.
|
||||
* @param params The raw, untrusted parameters from the model.
|
||||
* @returns A `ToolInvocation` instance.
|
||||
*/
|
||||
abstract build(params: TParams): ToolInvocation<TParams, TResult>;
|
||||
|
||||
/**
|
||||
* A convenience method that builds and executes the tool in one step.
|
||||
* Throws an error if validation fails.
|
||||
* @param params The raw, untrusted parameters from the model.
|
||||
* @param signal AbortSignal for tool cancellation.
|
||||
* @param updateOutput Optional callback to stream output.
|
||||
* @returns The result of the tool execution.
|
||||
*/
|
||||
async buildAndExecute(
|
||||
params: TParams,
|
||||
signal: AbortSignal,
|
||||
updateOutput?: (output: string) => void,
|
||||
): Promise<TResult> {
|
||||
const invocation = this.build(params);
|
||||
return invocation.execute(signal, updateOutput);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* New base class for declarative tools that separates validation from execution.
|
||||
* New tools should extend this class, which provides a `build` method that
|
||||
* validates parameters before deferring to a `createInvocation` method for
|
||||
* the final `ToolInvocation` object instantiation.
|
||||
*/
|
||||
export abstract class BaseDeclarativeTool<
|
||||
TParams extends object,
|
||||
TResult extends ToolResult,
|
||||
> extends DeclarativeTool<TParams, TResult> {
|
||||
build(params: TParams): ToolInvocation<TParams, TResult> {
|
||||
const validationError = this.validateToolParams(params);
|
||||
if (validationError) {
|
||||
throw new Error(validationError);
|
||||
}
|
||||
return this.createInvocation(params);
|
||||
}
|
||||
|
||||
protected abstract createInvocation(
|
||||
params: TParams,
|
||||
): ToolInvocation<TParams, TResult>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A type alias for a declarative tool where the specific parameter and result types are not known.
|
||||
*/
|
||||
export type AnyDeclarativeTool = DeclarativeTool<object, ToolResult>;
|
||||
|
||||
/**
|
||||
* Base implementation for tools with common functionality
|
||||
* @deprecated Use `DeclarativeTool` for new tools.
|
||||
*/
|
||||
export abstract class BaseTool<
|
||||
TParams = unknown,
|
||||
TParams extends object,
|
||||
TResult extends ToolResult = ToolResult,
|
||||
> implements Tool<TParams, TResult>
|
||||
{
|
||||
> extends DeclarativeTool<TParams, TResult> {
|
||||
/**
|
||||
* Creates a new instance of BaseTool
|
||||
* @param name Internal name of the tool (used for API calls)
|
||||
@@ -121,17 +263,24 @@ export abstract class BaseTool<
|
||||
readonly parameterSchema: Schema,
|
||||
readonly isOutputMarkdown: boolean = true,
|
||||
readonly canUpdateOutput: boolean = false,
|
||||
) {}
|
||||
) {
|
||||
super(
|
||||
name,
|
||||
displayName,
|
||||
description,
|
||||
icon,
|
||||
parameterSchema,
|
||||
isOutputMarkdown,
|
||||
canUpdateOutput,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function declaration schema computed from name, description, and parameterSchema
|
||||
*/
|
||||
get schema(): FunctionDeclaration {
|
||||
return {
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
parameters: this.parameterSchema,
|
||||
};
|
||||
build(params: TParams): ToolInvocation<TParams, TResult> {
|
||||
const validationError = this.validateToolParams(params);
|
||||
if (validationError) {
|
||||
throw new Error(validationError);
|
||||
}
|
||||
return new LegacyToolInvocation(this, params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user