Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions packages/types/src/experiment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export const experimentIds = [
"preventFocusDisruption",
"imageGeneration",
"runSlashCommand",
"nativeToolCalling",
] as const

export const experimentIdsSchema = z.enum(experimentIds)
Expand All @@ -29,7 +28,6 @@ export const experimentsSchema = z.object({
preventFocusDisruption: z.boolean().optional(),
imageGeneration: z.boolean().optional(),
runSlashCommand: z.boolean().optional(),
nativeToolCalling: z.boolean().optional(),
})

export type Experiments = z.infer<typeof experimentsSchema>
Expand Down
16 changes: 2 additions & 14 deletions src/core/assistant-message/presentAssistantMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,13 +281,7 @@ export async function presentAssistantMessage(cline: Task) {
let hasToolResult = false

// Check if we're using native tool protocol (do this once before defining pushToolResult)
const state = await cline.providerRef.deref()?.getState()
const toolProtocol = resolveToolProtocol(
cline.apiConfiguration,
cline.api.getModel().info,
cline.apiConfiguration.apiProvider,
state?.experiments,
)
const toolProtocol = resolveToolProtocol(cline.apiConfiguration, cline.api.getModel().info)
const isNative = isNativeProtocol(toolProtocol)
const toolCallId = (block as any).id

Expand Down Expand Up @@ -518,13 +512,7 @@ export async function presentAssistantMessage(cline: Task) {
await checkpointSaveAndMark(cline)

// Check if native protocol is enabled - if so, always use single-file class-based tool
const state = await cline.providerRef.deref()?.getState()
const applyDiffToolProtocol = resolveToolProtocol(
cline.apiConfiguration,
cline.api.getModel().info,
cline.apiConfiguration.apiProvider,
state?.experiments,
)
const applyDiffToolProtocol = resolveToolProtocol(cline.apiConfiguration, cline.api.getModel().info)
if (isNativeProtocol(applyDiffToolProtocol)) {
await applyDiffToolClass.handle(cline, block as ToolUse<"apply_diff">, {
askApproval,
Expand Down
74 changes: 10 additions & 64 deletions src/core/task/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,12 +411,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
// Initialize the assistant message parser only for XML protocol.
// For native protocol, tool calls come as tool_call chunks, not XML.
// experiments is always provided via TaskOptions (defaults to experimentDefault in provider)
const toolProtocol = resolveToolProtocol(
this.apiConfiguration,
this.api.getModel().info,
this.apiConfiguration.apiProvider,
experimentsConfig,
)
const toolProtocol = resolveToolProtocol(this.apiConfiguration, this.api.getModel().info)
this.assistantMessageParser = toolProtocol === "xml" ? new AssistantMessageParser() : undefined

this.messageQueueService = new MessageQueueService()
Expand Down Expand Up @@ -1271,12 +1266,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
)
const modelInfo = this.api.getModel().info
const state = await this.providerRef.deref()?.getState()
const toolProtocol = resolveToolProtocol(
this.apiConfiguration,
modelInfo,
this.apiConfiguration.apiProvider,
state?.experiments,
)
const toolProtocol = resolveToolProtocol(this.apiConfiguration, modelInfo)
return formatResponse.toolError(formatResponse.missingToolParameterError(paramName, toolProtocol))
}

Expand Down Expand Up @@ -1420,12 +1410,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
// conversations with tool uses and no tool schema.
// For native protocol, we preserve tool_use and tool_result blocks as they're expected by the API.
const state = await this.providerRef.deref()?.getState()
const protocol = resolveToolProtocol(
this.apiConfiguration,
this.api.getModel().info,
this.apiConfiguration.apiProvider,
state?.experiments,
)
const protocol = resolveToolProtocol(this.apiConfiguration, this.api.getModel().info)
const useNative = isNativeProtocol(protocol)

// Only convert tool blocks to text for XML protocol
Expand Down Expand Up @@ -1803,12 +1788,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
} else {
const modelInfo = this.api.getModel().info
const state = await this.providerRef.deref()?.getState()
const toolProtocol = resolveToolProtocol(
this.apiConfiguration,
modelInfo,
this.apiConfiguration.apiProvider,
state?.experiments,
)
const toolProtocol = resolveToolProtocol(this.apiConfiguration, modelInfo)
nextUserContent = [{ type: "text", text: formatResponse.noToolsUsed(toolProtocol) }]
this.consecutiveMistakeCount++
}
Expand Down Expand Up @@ -2456,12 +2436,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
// Check if we're using native protocol
const state = await this.providerRef.deref()?.getState()
const isNative = isNativeProtocol(
resolveToolProtocol(
this.apiConfiguration,
this.api.getModel().info,
this.apiConfiguration.apiProvider,
state?.experiments,
),
resolveToolProtocol(this.apiConfiguration, this.api.getModel().info),
)

if (isNative) {
Expand Down Expand Up @@ -2602,12 +2577,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
if (!didToolUse) {
const modelInfo = this.api.getModel().info
const state = await this.providerRef.deref()?.getState()
const toolProtocol = resolveToolProtocol(
this.apiConfiguration,
modelInfo,
this.apiConfiguration.apiProvider,
state?.experiments,
)
const toolProtocol = resolveToolProtocol(this.apiConfiguration, modelInfo)
this.userMessageContent.push({ type: "text", text: formatResponse.noToolsUsed(toolProtocol) })
this.consecutiveMistakeCount++
}
Expand All @@ -2634,14 +2604,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
// user messages (which would cause tool_result validation errors).
let state = await this.providerRef.deref()?.getState()
if (
isNativeProtocol(
resolveToolProtocol(
this.apiConfiguration,
this.api.getModel().info,
this.apiConfiguration.apiProvider,
state?.experiments,
),
) &&
isNativeProtocol(resolveToolProtocol(this.apiConfiguration, this.api.getModel().info)) &&
this.apiConversationHistory.length > 0
) {
const lastMessage = this.apiConversationHistory[this.apiConversationHistory.length - 1]
Expand Down Expand Up @@ -2707,14 +2670,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
// For native protocol, re-add the user message we removed
// Reuse the state variable from above
if (
isNativeProtocol(
resolveToolProtocol(
this.apiConfiguration,
this.api.getModel().info,
this.apiConfiguration.apiProvider,
state?.experiments,
),
)
isNativeProtocol(resolveToolProtocol(this.apiConfiguration, this.api.getModel().info))
) {
await this.addToApiConversationHistory({
role: "user",
Expand Down Expand Up @@ -2813,12 +2769,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
const canUseBrowserTool = modelSupportsBrowser && modeSupportsBrowser && (browserToolEnabled ?? true)

// Resolve the tool protocol based on profile, model, and provider settings
const toolProtocol = resolveToolProtocol(
apiConfiguration ?? this.apiConfiguration,
modelInfo,
(apiConfiguration ?? this.apiConfiguration)?.apiProvider,
experiments,
)
const toolProtocol = resolveToolProtocol(apiConfiguration ?? this.apiConfiguration, modelInfo)

return SYSTEM_PROMPT(
provider.context,
Expand Down Expand Up @@ -3057,12 +3008,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
// 1. Tool protocol is set to NATIVE
// 2. Model supports native tools
const modelInfo = this.api.getModel().info
const toolProtocol = resolveToolProtocol(
this.apiConfiguration,
modelInfo,
this.apiConfiguration.apiProvider,
state?.experiments,
)
const toolProtocol = resolveToolProtocol(this.apiConfiguration, modelInfo)
const shouldIncludeTools = toolProtocol === TOOL_PROTOCOL.NATIVE && (modelInfo.supportsNativeTools ?? false)

// Build complete tools array: native tools + dynamic MCP tools, filtered by mode restrictions
Expand Down
11 changes: 3 additions & 8 deletions src/core/tools/MultiApplyDiffTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,7 @@ export async function applyDiffTool(
removeClosingTag: RemoveClosingTag,
) {
// Check if native protocol is enabled - if so, always use single-file class-based tool
const provider = cline.providerRef.deref()
const state = await provider?.getState()
const toolProtocol = resolveToolProtocol(
cline.apiConfiguration,
cline.api.getModel().info,
cline.apiConfiguration.apiProvider,
state?.experiments,
)
const toolProtocol = resolveToolProtocol(cline.apiConfiguration, cline.api.getModel().info)
if (isNativeProtocol(toolProtocol)) {
return applyDiffToolClass.handle(cline, block as ToolUse<"apply_diff">, {
askApproval,
Expand All @@ -80,6 +73,8 @@ export async function applyDiffTool(
}

// Check if MULTI_FILE_APPLY_DIFF experiment is enabled
const provider = cline.providerRef.deref()
const state = await provider?.getState()
if (provider && state) {
const isMultiFileApplyDiffEnabled = experiments.isEnabled(
state.experiments ?? {},
Expand Down
8 changes: 1 addition & 7 deletions src/core/tools/ReadFileTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,7 @@ export class ReadFileTool extends BaseTool<"read_file"> {
const { handleError, pushToolResult } = callbacks
const fileEntries = params.files
const modelInfo = task.api.getModel().info
const state = await task.providerRef.deref()?.getState()
const protocol = resolveToolProtocol(
task.apiConfiguration,
modelInfo,
task.apiConfiguration.apiProvider,
state?.experiments,
)
const protocol = resolveToolProtocol(task.apiConfiguration, modelInfo)
const useNative = isNativeProtocol(protocol)

if (!fileEntries || fileEntries.length === 0) {
Expand Down
10 changes: 5 additions & 5 deletions src/core/tools/__tests__/applyDiffTool.experiment.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,22 +155,22 @@ describe("applyDiffTool experiment routing", () => {
expect(applyDiffToolClass.handle).not.toHaveBeenCalled()
})

it("should use class-based tool when native protocol is enabled regardless of experiment", async () => {
// Update model to support native tools
it("should use class-based tool when model defaults to native protocol", async () => {
// Update model to support native tools and default to native protocol
mockCline.api.getModel = vi.fn().mockReturnValue({
id: "test-model",
info: {
maxTokens: 4096,
contextWindow: 128000,
supportsPromptCache: false,
supportsNativeTools: true, // Enable native tools support
supportsNativeTools: true, // Model supports native tools
defaultToolProtocol: "native", // Model defaults to native protocol
},
})

mockProvider.getState.mockResolvedValue({
experiments: {
[EXPERIMENT_IDS.MULTI_FILE_APPLY_DIFF]: true,
[EXPERIMENT_IDS.NATIVE_TOOL_CALLING]: true, // Enable native tool calling experiment
},
})
;(applyDiffToolClass.handle as any).mockResolvedValue(undefined)
Expand All @@ -184,7 +184,7 @@ describe("applyDiffTool experiment routing", () => {
mockRemoveClosingTag,
)

// When native protocol is enabled, should always use class-based tool
// When native protocol is used, should always use class-based tool
expect(applyDiffToolClass.handle).toHaveBeenCalledWith(mockCline, mockBlock, {
askApproval: mockAskApproval,
handleError: mockHandleError,
Expand Down
2 changes: 1 addition & 1 deletion src/core/webview/generateSystemPrompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const generateSystemPrompt = async (provider: ClineProvider, message: Web
const canUseBrowserTool = modelSupportsBrowser && modeSupportsBrowser && (browserToolEnabled ?? true)

// Resolve tool protocol for system prompt generation
const toolProtocol = resolveToolProtocol(apiConfiguration, modelInfo, apiConfiguration.apiProvider, experiments)
const toolProtocol = resolveToolProtocol(apiConfiguration, modelInfo)

const systemPrompt = await SYSTEM_PROMPT(
provider.context,
Expand Down
12 changes: 0 additions & 12 deletions src/shared/__tests__/experiments.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,6 @@ describe("experiments", () => {
})
})

describe("NATIVE_TOOL_CALLING", () => {
it("is configured correctly", () => {
expect(EXPERIMENT_IDS.NATIVE_TOOL_CALLING).toBe("nativeToolCalling")
expect(experimentConfigsMap.NATIVE_TOOL_CALLING).toMatchObject({
enabled: false,
})
})
})

describe("isEnabled", () => {
it("returns false when POWER_STEERING experiment is not enabled", () => {
const experiments: Record<ExperimentId, boolean> = {
Expand All @@ -40,7 +31,6 @@ describe("experiments", () => {
preventFocusDisruption: false,
imageGeneration: false,
runSlashCommand: false,
nativeToolCalling: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
})
Expand All @@ -52,7 +42,6 @@ describe("experiments", () => {
preventFocusDisruption: false,
imageGeneration: false,
runSlashCommand: false,
nativeToolCalling: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(true)
})
Expand All @@ -64,7 +53,6 @@ describe("experiments", () => {
preventFocusDisruption: false,
imageGeneration: false,
runSlashCommand: false,
nativeToolCalling: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
})
Expand Down
2 changes: 0 additions & 2 deletions src/shared/experiments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ export const EXPERIMENT_IDS = {
PREVENT_FOCUS_DISRUPTION: "preventFocusDisruption",
IMAGE_GENERATION: "imageGeneration",
RUN_SLASH_COMMAND: "runSlashCommand",
NATIVE_TOOL_CALLING: "nativeToolCalling",
} as const satisfies Record<string, ExperimentId>

type _AssertExperimentIds = AssertEqual<Equals<ExperimentId, Values<typeof EXPERIMENT_IDS>>>
Expand All @@ -23,7 +22,6 @@ export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
PREVENT_FOCUS_DISRUPTION: { enabled: false },
IMAGE_GENERATION: { enabled: false },
RUN_SLASH_COMMAND: { enabled: false },
NATIVE_TOOL_CALLING: { enabled: false },
}

export const experimentDefault = Object.fromEntries(
Expand Down
Loading
Loading