This commit is contained in:
Deleted user 2026-05-27 11:25:22 +08:00 committed by GitHub
commit 8623dd1a55
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 93 additions and 82 deletions

View file

@ -1614,36 +1614,15 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
const { contextTokens: prevContextTokens } = this.getTokenUsage()
// Build tools for condensing metadata (same tools used for normal API calls)
const provider = this.providerRef.deref()
let allTools: import("openai").default.Chat.ChatCompletionTool[] = []
if (provider) {
const modelInfo = this.api.getModel().info
const toolsResult = await buildNativeToolsArrayWithRestrictions({
provider,
cwd: this.cwd,
mode,
customModes: state?.customModes,
experiments: state?.experiments,
apiConfiguration,
disabledTools: state?.disabledTools,
modelInfo,
includeAllToolsWithRestrictions: false,
})
allTools = toolsResult.tools
}
// Build metadata with tools and taskId for the condensing API call
// Build metadata for the condensing API call.
// NOTE: We intentionally omit tools/tool_choice/parallelToolCalls here because
// the condensation flow converts all tool_use/tool_result blocks to plain text
// and the SUMMARY_PROMPT instructs the model not to call tools. Including tool
// definitions with tool_choice:"auto" but no tool blocks in messages causes
// 500 errors on some proxies (e.g. Roo Code Router). See #11743.
const metadata: ApiHandlerCreateMessageMetadata = {
mode,
taskId: this.taskId,
...(allTools.length > 0
? {
tools: allTools,
tool_choice: "auto",
parallelToolCalls: true,
}
: {}),
}
// Generate environment details to include in the condensed summary
const environmentDetails = await getEnvironmentDetails(this, true)
@ -3741,35 +3720,15 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
// Send condenseTaskContextStarted to show in-progress indicator
await this.providerRef.deref()?.postMessageToWebview({ type: "condenseTaskContextStarted", text: this.taskId })
// Build tools for condensing metadata (same tools used for normal API calls)
const provider = this.providerRef.deref()
let allTools: import("openai").default.Chat.ChatCompletionTool[] = []
if (provider) {
const toolsResult = await buildNativeToolsArrayWithRestrictions({
provider,
cwd: this.cwd,
mode,
customModes: state?.customModes,
experiments: state?.experiments,
apiConfiguration,
disabledTools: state?.disabledTools,
modelInfo,
includeAllToolsWithRestrictions: false,
})
allTools = toolsResult.tools
}
// Build metadata with tools and taskId for the condensing API call
// Build metadata for the condensing API call.
// NOTE: We intentionally omit tools/tool_choice/parallelToolCalls here because
// the condensation flow converts all tool_use/tool_result blocks to plain text
// and the SUMMARY_PROMPT instructs the model not to call tools. Including tool
// definitions with tool_choice:"auto" but no tool blocks in messages causes
// 500 errors on some proxies (e.g. Roo Code Router). See #11743.
const metadata: ApiHandlerCreateMessageMetadata = {
mode,
taskId: this.taskId,
...(allTools.length > 0
? {
tools: allTools,
tool_choice: "auto",
parallelToolCalls: true,
}
: {}),
}
try {
@ -3953,38 +3912,15 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
?.postMessageToWebview({ type: "condenseTaskContextStarted", text: this.taskId })
}
// Build tools for condensing metadata (same tools used for normal API calls)
// This ensures the condensing API call includes tool definitions for providers that need them
let contextMgmtTools: import("openai").default.Chat.ChatCompletionTool[] = []
{
const provider = this.providerRef.deref()
if (provider) {
const toolsResult = await buildNativeToolsArrayWithRestrictions({
provider,
cwd: this.cwd,
mode,
customModes: state?.customModes,
experiments: state?.experiments,
apiConfiguration,
disabledTools: state?.disabledTools,
modelInfo,
includeAllToolsWithRestrictions: false,
})
contextMgmtTools = toolsResult.tools
}
}
// Build metadata with tools and taskId for the condensing API call
// Build metadata for the condensing API call.
// NOTE: We intentionally omit tools/tool_choice/parallelToolCalls here because
// the condensation flow converts all tool_use/tool_result blocks to plain text
// and the SUMMARY_PROMPT instructs the model not to call tools. Including tool
// definitions with tool_choice:"auto" but no tool blocks in messages causes
// 500 errors on some proxies (e.g. Roo Code Router). See #11743.
const contextMgmtMetadata: ApiHandlerCreateMessageMetadata = {
mode,
taskId: this.taskId,
...(contextMgmtTools.length > 0
? {
tools: contextMgmtTools,
tool_choice: "auto",
parallelToolCalls: true,
}
: {}),
}
// Only generate environment details when context management will actually run.

View file

@ -1981,6 +1981,81 @@ describe("Queued message processing after condense", () => {
})
})
describe("condenseContext metadata does not include tools (#11743)", () => {
function createProvider(): any {
const storageUri = { fsPath: path.join(os.tmpdir(), "test-storage") }
const ctx = {
globalState: {
get: vi.fn().mockImplementation((_key: keyof GlobalState) => undefined),
update: vi.fn().mockResolvedValue(undefined),
keys: vi.fn().mockReturnValue([]),
},
globalStorageUri: storageUri,
workspaceState: {
get: vi.fn().mockImplementation((_key: any) => undefined),
update: vi.fn().mockResolvedValue(undefined),
keys: vi.fn().mockReturnValue([]),
},
secrets: {
get: vi.fn().mockResolvedValue(undefined),
store: vi.fn().mockResolvedValue(undefined),
delete: vi.fn().mockResolvedValue(undefined),
},
extensionUri: { fsPath: "/mock/extension/path" },
extension: { packageJSON: { version: "1.0.0" } },
} as unknown as vscode.ExtensionContext
const output = {
appendLine: vi.fn(),
append: vi.fn(),
clear: vi.fn(),
show: vi.fn(),
hide: vi.fn(),
dispose: vi.fn(),
}
const provider = new ClineProvider(ctx, output as any, "sidebar", new ContextProxy(ctx)) as any
provider.postMessageToWebview = vi.fn().mockResolvedValue(undefined)
provider.postStateToWebview = vi.fn().mockResolvedValue(undefined)
provider.postStateToWebviewWithoutTaskHistory = vi.fn().mockResolvedValue(undefined)
provider.getState = vi.fn().mockResolvedValue({})
return provider
}
const apiConfig: ProviderSettings = {
apiProvider: "anthropic",
apiModelId: "claude-3-5-sonnet-20241022",
apiKey: "test-api-key",
} as any
it("does not pass tools, tool_choice, or parallelToolCalls to summarizeConversation", async () => {
const { summarizeConversation } = await import("../../condense")
const mockSummarize = vi.mocked(summarizeConversation)
mockSummarize.mockClear()
const provider = createProvider()
const task = new Task({
provider,
apiConfiguration: apiConfig,
task: "test task",
startTask: false,
})
vi.spyOn(task as any, "getSystemPrompt").mockResolvedValue("system")
await task.condenseContext()
expect(mockSummarize).toHaveBeenCalledTimes(1)
const callArgs = mockSummarize.mock.calls[0][0]
const metadata = callArgs.metadata
expect(metadata).toBeDefined()
expect(metadata).not.toHaveProperty("tools")
expect(metadata).not.toHaveProperty("tool_choice")
expect(metadata).not.toHaveProperty("parallelToolCalls")
expect(metadata).toHaveProperty("taskId")
})
})
describe("pushToolResultToUserContent", () => {
let mockProvider: any
let mockApiConfig: ProviderSettings