diff --git a/packages/ai/src/providers/openai-completions.ts b/packages/ai/src/providers/openai-completions.ts index 1637b289..cd81bc93 100644 --- a/packages/ai/src/providers/openai-completions.ts +++ b/packages/ai/src/providers/openai-completions.ts @@ -26,6 +26,26 @@ import { parseStreamingJson } from "../utils/json-parse.js"; import { sanitizeSurrogates } from "../utils/sanitize-unicode.js"; import { transformMessages } from "./transorm-messages.js"; +import type { Message } from "../types.js"; + +/** + * Check if conversation messages contain tool calls or tool results. + * This is needed because Anthropic (via proxy) requires the tools param + * to be present when messages include tool_calls or tool role messages. + */ +function hasToolHistory(messages: Message[]): boolean { + for (const msg of messages) { + if (msg.role === "toolResult") { + return true; + } + if (msg.role === "assistant") { + if (msg.content.some((block) => block.type === "toolCall")) { + return true; + } + } + } + return false; +} export interface OpenAICompletionsOptions extends StreamOptions { toolChoice?: "auto" | "none" | "required" | { type: "function"; function: { name: string } }; @@ -296,6 +316,9 @@ function buildParams(model: Model<"openai-completions">, context: Context, optio if (context.tools) { params.tools = convertTools(context.tools); + } else if (hasToolHistory(context.messages)) { + // Anthropic (via LiteLLM/proxy) requires tools param when conversation has tool_calls/tool_results + params.tools = []; } if (options?.toolChoice) {