11import { Anthropic } from "@anthropic-ai/sdk"
22import { AnthropicVertex } from "@anthropic-ai/vertex-sdk"
33import { GoogleAuth , JWTInput } from "google-auth-library"
4+ import OpenAI from "openai"
45
56import {
67 type ModelInfo ,
78 type VertexModelId ,
89 vertexDefaultModelId ,
910 vertexModels ,
1011 ANTHROPIC_DEFAULT_MAX_TOKENS ,
12+ TOOL_PROTOCOL ,
1113} from "@roo-code/types"
1214
1315import { ApiHandlerOptions } from "../../shared/api"
@@ -17,6 +19,8 @@ import { ApiStream } from "../transform/stream"
1719import { addCacheBreakpoints } from "../transform/caching/vertex"
1820import { getModelParams } from "../transform/model-params"
1921import { filterNonAnthropicBlocks } from "../transform/anthropic-filter"
22+ import { resolveToolProtocol } from "../../utils/resolveToolProtocol"
23+ import { convertOpenAIToolsToAnthropic } from "../../core/prompts/tools/native-tools/converters"
2024
2125import { BaseProvider } from "./base-provider"
2226import type { SingleCompletionHandler , ApiHandlerCreateMessageMetadata } from "../index"
@@ -63,17 +67,30 @@ export class AnthropicVertexHandler extends BaseProvider implements SingleComple
6367 messages : Anthropic . Messages . MessageParam [ ] ,
6468 metadata ?: ApiHandlerCreateMessageMetadata ,
6569 ) : ApiStream {
66- let {
67- id,
68- info : { supportsPromptCache } ,
69- temperature,
70- maxTokens,
71- reasoning : thinking ,
72- } = this . getModel ( )
70+ let { id, info, temperature, maxTokens, reasoning : thinking } = this . getModel ( )
71+
72+ const { supportsPromptCache } = info
7373
7474 // Filter out non-Anthropic blocks (reasoning, thoughtSignature, etc.) before sending to the API
7575 const sanitizedMessages = filterNonAnthropicBlocks ( messages )
7676
77+ // Enable native tools using resolveToolProtocol (which checks model's defaultToolProtocol)
78+ // This matches the approach used in AnthropicHandler
79+ // Also exclude tools when tool_choice is "none" since that means "don't use tools"
80+ const toolProtocol = resolveToolProtocol ( this . options , info , metadata ?. toolProtocol )
81+ const shouldIncludeNativeTools =
82+ metadata ?. tools &&
83+ metadata . tools . length > 0 &&
84+ toolProtocol === TOOL_PROTOCOL . NATIVE &&
85+ metadata ?. tool_choice !== "none"
86+
87+ const nativeToolParams = shouldIncludeNativeTools
88+ ? {
89+ tools : convertOpenAIToolsToAnthropic ( metadata . tools ! ) ,
90+ tool_choice : this . convertOpenAIToolChoice ( metadata . tool_choice , metadata . parallelToolCalls ) ,
91+ }
92+ : { }
93+
7794 /**
7895 * Vertex API has specific limitations for prompt caching:
7996 * 1. Maximum of 4 blocks can have cache_control
@@ -98,6 +115,7 @@ export class AnthropicVertexHandler extends BaseProvider implements SingleComple
98115 : systemPrompt ,
99116 messages : supportsPromptCache ? addCacheBreakpoints ( sanitizedMessages ) : sanitizedMessages ,
100117 stream : true ,
118+ ...nativeToolParams ,
101119 }
102120
103121 const stream = await this . client . messages . create ( params )
@@ -144,6 +162,17 @@ export class AnthropicVertexHandler extends BaseProvider implements SingleComple
144162 yield { type : "reasoning" , text : ( chunk . content_block as any ) . thinking }
145163 break
146164 }
165+ case "tool_use" : {
166+ // Emit initial tool call partial with id and name
167+ yield {
168+ type : "tool_call_partial" ,
169+ index : chunk . index ,
170+ id : chunk . content_block ! . id ,
171+ name : chunk . content_block ! . name ,
172+ arguments : undefined ,
173+ }
174+ break
175+ }
147176 }
148177
149178 break
@@ -158,12 +187,24 @@ export class AnthropicVertexHandler extends BaseProvider implements SingleComple
158187 yield { type : "reasoning" , text : ( chunk . delta as any ) . thinking }
159188 break
160189 }
190+ case "input_json_delta" : {
191+ // Emit tool call partial chunks as arguments stream in
192+ yield {
193+ type : "tool_call_partial" ,
194+ index : chunk . index ,
195+ id : undefined ,
196+ name : undefined ,
197+ arguments : ( chunk . delta as any ) . partial_json ,
198+ }
199+ break
200+ }
161201 }
162202
163203 break
164204 }
165205 case "content_block_stop" : {
166206 // Block complete - no action needed for now.
207+ // NativeToolCallParser handles tool call completion
167208 // Note: Signature for multi-turn thinking would require using stream.finalMessage()
168209 // after iteration completes, which requires restructuring the streaming approach.
169210 break
@@ -227,4 +268,47 @@ export class AnthropicVertexHandler extends BaseProvider implements SingleComple
227268 throw error
228269 }
229270 }
271+
272+ /**
273+ * Converts OpenAI tool_choice to Anthropic ToolChoice format
274+ * @param toolChoice - OpenAI tool_choice parameter
275+ * @param parallelToolCalls - When true, allows parallel tool calls. When false (default), disables parallel tool calls.
276+ */
277+ private convertOpenAIToolChoice (
278+ toolChoice : OpenAI . Chat . ChatCompletionCreateParams [ "tool_choice" ] ,
279+ parallelToolCalls ?: boolean ,
280+ ) : Anthropic . Messages . MessageCreateParams [ "tool_choice" ] | undefined {
281+ // Anthropic allows parallel tool calls by default. When parallelToolCalls is false or undefined,
282+ // we disable parallel tool use to ensure one tool call at a time.
283+ const disableParallelToolUse = ! parallelToolCalls
284+
285+ if ( ! toolChoice ) {
286+ // Default to auto with parallel tool use control
287+ return { type : "auto" , disable_parallel_tool_use : disableParallelToolUse }
288+ }
289+
290+ if ( typeof toolChoice === "string" ) {
291+ switch ( toolChoice ) {
292+ case "none" :
293+ return undefined // Anthropic doesn't have "none", just omit tools
294+ case "auto" :
295+ return { type : "auto" , disable_parallel_tool_use : disableParallelToolUse }
296+ case "required" :
297+ return { type : "any" , disable_parallel_tool_use : disableParallelToolUse }
298+ default :
299+ return { type : "auto" , disable_parallel_tool_use : disableParallelToolUse }
300+ }
301+ }
302+
303+ // Handle object form { type: "function", function: { name: string } }
304+ if ( typeof toolChoice === "object" && "function" in toolChoice ) {
305+ return {
306+ type : "tool" ,
307+ name : toolChoice . function . name ,
308+ disable_parallel_tool_use : disableParallelToolUse ,
309+ }
310+ }
311+
312+ return { type : "auto" , disable_parallel_tool_use : disableParallelToolUse }
313+ }
230314}
0 commit comments