Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,8 @@
"form-data": ">=4.0.4",
"bluebird": ">=3.7.2"
}
},
"dependencies": {
"@streamparser/json": "^0.0.22"
}
}
1 change: 1 addition & 0 deletions packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ export const globalSettingsSchema = z.object({
rateLimitSeconds: z.number().optional(),
diffEnabled: z.boolean().optional(),
fuzzyMatchThreshold: z.number().optional(),

experiments: experimentsSchema.optional(),

codebaseIndexModels: codebaseIndexModelsSchema.optional(),
Expand Down
14 changes: 13 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Anthropic } from "@anthropic-ai/sdk"
import type { ProviderSettings, ModelInfo } from "@roo-code/types"

import { ApiStream } from "./transform/stream"
import type { ToolSpec } from "./transform/tool-converters"

import {
GlamaHandler,
Expand Down Expand Up @@ -73,6 +74,7 @@ export interface ApiHandler {
systemPrompt: string,
messages: Anthropic.Messages.MessageParam[],
metadata?: ApiHandlerCreateMessageMetadata,
tools?: ToolSpec[],
): ApiStream

getModel(): { id: string; info: ModelInfo }
Expand All @@ -86,6 +88,15 @@ export interface ApiHandler {
* @returns A promise resolving to the token count
*/
countTokens(content: Array<Anthropic.Messages.ContentBlockParam>): Promise<number>

/**
* Returns whether this provider supports native tool calling
* Providers that support native tools can receive tool specifications and
* handle them in their native format instead of using XML-based tools
*
* @returns true if the provider supports native tool calling, false otherwise
*/
supportsNativeTools(): boolean
}

export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
Expand Down
39 changes: 39 additions & 0 deletions src/api/providers/anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { ApiHandlerOptions } from "../../shared/api"

import { ApiStream } from "../transform/stream"
import { getModelParams } from "../transform/model-params"
import { toolSpecToAnthropicTool, type ToolSpec } from "../transform/tool-converters"

import { BaseProvider } from "./base-provider"
import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index"
Expand All @@ -40,6 +41,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
systemPrompt: string,
messages: Anthropic.Messages.MessageParam[],
metadata?: ApiHandlerCreateMessageMetadata,
tools?: ToolSpec[],
): ApiStream {
let stream: AnthropicStream<Anthropic.Messages.RawMessageStreamEvent>
const cacheControl: CacheControlEphemeral = { type: "ephemeral" }
Expand All @@ -53,6 +55,10 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
betas.push("context-1m-2025-08-07")
}

// Convert tools to Anthropic format if provided
const anthropicTools = tools?.map(toolSpecToAnthropicTool)
const nativeToolsOn = anthropicTools && anthropicTools.length > 0

switch (modelId) {
case "claude-sonnet-4-5":
case "claude-sonnet-4-20250514":
Expand Down Expand Up @@ -107,6 +113,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
return message
}),
stream: true,
tools: nativeToolsOn ? anthropicTools : undefined,
},
(() => {
// prompt caching: https://x.com/alexalbert__/status/1823751995901272068
Expand Down Expand Up @@ -151,6 +158,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
let outputTokens = 0
let cacheWriteTokens = 0
let cacheReadTokens = 0
const lastStartedToolCall = { id: "", name: "", arguments: "" }

for await (const chunk of stream) {
switch (chunk.type) {
Expand Down Expand Up @@ -202,6 +210,14 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa

yield { type: "reasoning", text: chunk.content_block.thinking }
break
case "tool_use":
// Convert Anthropic tool_use to OpenAI-compatible format
if (chunk.content_block.id && chunk.content_block.name) {
lastStartedToolCall.id = chunk.content_block.id
lastStartedToolCall.name = chunk.content_block.name
lastStartedToolCall.arguments = ""
}
break
case "text":
// We may receive multiple text blocks, in which
// case just insert a line break between them.
Expand All @@ -218,13 +234,32 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
case "thinking_delta":
yield { type: "reasoning", text: chunk.delta.thinking }
break
case "input_json_delta":
// Stream tool arguments as they arrive
if (lastStartedToolCall.id && lastStartedToolCall.name && chunk.delta.partial_json) {
yield {
type: "tool_calls",
tool_call: {
function: {
id: lastStartedToolCall.id,
name: lastStartedToolCall.name,
arguments: chunk.delta.partial_json,
},
},
}
}
break
case "text_delta":
yield { type: "text", text: chunk.delta.text }
break
}

break
case "content_block_stop":
// Reset tool call state when block ends
lastStartedToolCall.id = ""
lastStartedToolCall.name = ""
lastStartedToolCall.arguments = ""
break
}
}
Expand Down Expand Up @@ -326,4 +361,8 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
return super.countTokens(content)
}
}

protected override hasNativeToolCapability(): boolean {
return true
}
}
25 changes: 25 additions & 0 deletions src/api/providers/base-provider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Anthropic } from "@anthropic-ai/sdk"
import * as vscode from "vscode"

import type { ModelInfo } from "@roo-code/types"

Expand Down Expand Up @@ -32,4 +33,28 @@ export abstract class BaseProvider implements ApiHandler {

return countTokens(content, { useWorker: true })
}

/**
* Returns whether this provider has the capability to support native tool calling.
* Default implementation returns false (no native tool support).
* Providers that DO support native tools should override this to return true.
*
* @returns false by default
*/
protected hasNativeToolCapability(): boolean {
return false
}

/**
* Returns whether this provider supports native tool calling AND the user has enabled it.
* Combines provider capability with user setting.
*
* @returns true if provider supports it AND setting is enabled, false otherwise
*/
supportsNativeTools(): boolean {
if (!this.hasNativeToolCapability()) {
return false
}
return vscode.workspace.getConfiguration("roo-cline").get<boolean>("nativeToolCalling", false)
}
}
4 changes: 4 additions & 0 deletions src/api/providers/fake-ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,8 @@ export class FakeAIHandler implements ApiHandler, SingleCompletionHandler {
completePrompt(prompt: string): Promise<string> {
return this.ai.completePrompt(prompt)
}

supportsNativeTools(): boolean {
return false
}
}
4 changes: 4 additions & 0 deletions src/api/providers/human-relay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ export class HumanRelayHandler implements ApiHandler, SingleCompletionHandler {

return response
}

supportsNativeTools(): boolean {
return false
}
}

/**
Expand Down
Loading