From 7d90e43e3ae1e390b46ca250e20176a6b36281a1 Mon Sep 17 00:00:00 2001 From: Sidney Swift <158200036+sidneyswift@users.noreply.github.com> Date: Wed, 8 Apr 2026 13:58:53 -0400 Subject: [PATCH 1/5] fix: disable Anthropic thinking to fix tool chain crash Anthropic's API rejects requests that combine extended thinking with forced tool_choice. The ToolLoopAgent enables thinking globally, and prepareStep forces toolChoice for every tool chain step (create_new_artist, create_release_report). This causes "Thinking may not be enabled when tool_choice forces tool use" on any Anthropic model. The Vercel AI SDK does not support overriding providerOptions per-step (open issue vercel/ai#11761), so the fix must be at the constructor level. Disables Anthropic thinking until the SDK adds per-step providerOptions support or an alternative approach is implemented. Made-with: Cursor --- lib/agents/generalAgent/__tests__/getGeneralAgent.test.ts | 2 +- lib/agents/generalAgent/getGeneralAgent.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/agents/generalAgent/__tests__/getGeneralAgent.test.ts b/lib/agents/generalAgent/__tests__/getGeneralAgent.test.ts index d29ed8c5..838b0e63 100644 --- a/lib/agents/generalAgent/__tests__/getGeneralAgent.test.ts +++ b/lib/agents/generalAgent/__tests__/getGeneralAgent.test.ts @@ -480,7 +480,7 @@ describe("getGeneralAgent", () => { expect(settings.providerOptions).toBeDefined(); expect(settings.providerOptions.anthropic).toEqual( expect.objectContaining({ - thinking: { type: "enabled", budgetTokens: 12000 }, + thinking: { type: "disabled" }, }), ); expect(settings.providerOptions.google).toEqual( diff --git a/lib/agents/generalAgent/getGeneralAgent.ts b/lib/agents/generalAgent/getGeneralAgent.ts index 7c2c9407..29510db9 100644 --- a/lib/agents/generalAgent/getGeneralAgent.ts +++ b/lib/agents/generalAgent/getGeneralAgent.ts @@ -66,7 +66,7 @@ export default async function getGeneralAgent(body: ChatRequestBody): Promise Date: Wed, 8 Apr 2026 14:22:12 -0400 Subject: [PATCH 2/5] fix: use fallback model for tool chain steps to avoid Anthropic thinking conflict Instead of disabling Anthropic thinking globally, tool chain steps now switch to openai/gpt-5-mini (via TOOL_CHAIN_FALLBACK_MODEL) when no specific model is mapped in TOOL_MODEL_MAP. This preserves extended thinking for regular Anthropic conversations while ensuring forced toolChoice works during tool chain execution. Made-with: Cursor --- lib/agents/generalAgent/__tests__/getGeneralAgent.test.ts | 2 +- lib/agents/generalAgent/getGeneralAgent.ts | 2 +- .../toolChains/__tests__/getPrepareStepResult.test.ts | 7 +++++-- lib/chat/toolChains/getPrepareStepResult.ts | 8 ++------ lib/chat/toolChains/toolChains.ts | 6 ++++-- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/agents/generalAgent/__tests__/getGeneralAgent.test.ts b/lib/agents/generalAgent/__tests__/getGeneralAgent.test.ts index 838b0e63..d29ed8c5 100644 --- a/lib/agents/generalAgent/__tests__/getGeneralAgent.test.ts +++ b/lib/agents/generalAgent/__tests__/getGeneralAgent.test.ts @@ -480,7 +480,7 @@ describe("getGeneralAgent", () => { expect(settings.providerOptions).toBeDefined(); expect(settings.providerOptions.anthropic).toEqual( expect.objectContaining({ - thinking: { type: "disabled" }, + thinking: { type: "enabled", budgetTokens: 12000 }, }), ); expect(settings.providerOptions.google).toEqual( diff --git a/lib/agents/generalAgent/getGeneralAgent.ts b/lib/agents/generalAgent/getGeneralAgent.ts index 29510db9..7c2c9407 100644 --- a/lib/agents/generalAgent/getGeneralAgent.ts +++ b/lib/agents/generalAgent/getGeneralAgent.ts @@ -66,7 +66,7 @@ export default async function getGeneralAgent(body: ChatRequestBody): Promise ({ step_two: "gemini-2.5-pro", custom_step_one: "gpt-4-turbo", }, + TOOL_CHAIN_FALLBACK_MODEL: "openai/gpt-5-mini", })); describe("getPrepareStepResult", () => { @@ -86,6 +87,7 @@ describe("getPrepareStepResult", () => { const result = getPrepareStepResult(options as any); expect(result).toEqual({ toolChoice: { type: "tool", toolName: "step_one" }, + model: "openai/gpt-5-mini", }); }); @@ -255,6 +257,7 @@ describe("getPrepareStepResult", () => { const result = getPrepareStepResult(options as any); expect(result).toEqual({ toolChoice: { type: "tool", toolName: "custom_step_two" }, + model: "openai/gpt-5-mini", messages: [ { role: "user", content: "Hello" }, { role: "user", content: "Reference message" }, @@ -291,7 +294,7 @@ describe("getPrepareStepResult", () => { expect(result?.model).toBe("gemini-2.5-pro"); }); - it("does not include model when tool is not in TOOL_MODEL_MAP", () => { + it("uses fallback model when tool is not in TOOL_MODEL_MAP", () => { const options = { steps: [ { @@ -310,7 +313,7 @@ describe("getPrepareStepResult", () => { }; const result = getPrepareStepResult(options as any); - expect(result?.model).toBeUndefined(); + expect(result?.model).toBe("openai/gpt-5-mini"); }); }); }); diff --git a/lib/chat/toolChains/getPrepareStepResult.ts b/lib/chat/toolChains/getPrepareStepResult.ts index 02dd8e71..56d79717 100644 --- a/lib/chat/toolChains/getPrepareStepResult.ts +++ b/lib/chat/toolChains/getPrepareStepResult.ts @@ -1,5 +1,5 @@ import { LanguageModel, ModelMessage, StepResult, ToolSet } from "ai"; -import { PrepareStepResult, TOOL_CHAINS, TOOL_MODEL_MAP } from "./toolChains"; +import { PrepareStepResult, TOOL_CHAIN_FALLBACK_MODEL, TOOL_CHAINS, TOOL_MODEL_MAP } from "./toolChains"; import getExecutedToolTimeline from "./getExecutedToolTimeline"; type PrepareStepOptions = { @@ -57,11 +57,7 @@ const getPrepareStepResult = (options: PrepareStepOptions): PrepareStepResult | result.messages = options.messages.concat(nextToolItem.messages); } - // Add model if specified for this tool - const model = TOOL_MODEL_MAP[nextToolItem.toolName]; - if (model) { - result.model = model; - } + result.model = TOOL_MODEL_MAP[nextToolItem.toolName] || TOOL_CHAIN_FALLBACK_MODEL; return result; } diff --git a/lib/chat/toolChains/toolChains.ts b/lib/chat/toolChains/toolChains.ts index 1275199d..eeb93908 100644 --- a/lib/chat/toolChains/toolChains.ts +++ b/lib/chat/toolChains/toolChains.ts @@ -15,11 +15,13 @@ export type PrepareStepResult = { messages?: ModelMessage[]; }; +// Forced toolChoice is incompatible with Anthropic extended thinking. +// Tool chain steps use this model unless overridden by TOOL_MODEL_MAP. +export const TOOL_CHAIN_FALLBACK_MODEL: LanguageModel = "openai/gpt-5-mini"; + // Map specific tools to their required models export const TOOL_MODEL_MAP: Record = { update_account_info: "gemini-2.5-pro", - // Add other tools that need specific models here - // e.g., create_segments: "gpt-4-turbo", }; // Map trigger tool -> sequence AFTER trigger From 6b8d28498939587e1c52d3fdf9f7a8b566c24a84 Mon Sep 17 00:00:00 2001 From: Sidney Swift <158200036+sidneyswift@users.noreply.github.com> Date: Wed, 8 Apr 2026 14:27:06 -0400 Subject: [PATCH 3/5] chore: use openai/gpt-5.4-mini as tool chain fallback model Made-with: Cursor --- .../toolChains/__tests__/getPrepareStepResult.test.ts | 8 ++++---- lib/chat/toolChains/toolChains.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/chat/toolChains/__tests__/getPrepareStepResult.test.ts b/lib/chat/toolChains/__tests__/getPrepareStepResult.test.ts index 8de3c220..a5c23c49 100644 --- a/lib/chat/toolChains/__tests__/getPrepareStepResult.test.ts +++ b/lib/chat/toolChains/__tests__/getPrepareStepResult.test.ts @@ -21,7 +21,7 @@ vi.mock("../toolChains", () => ({ step_two: "gemini-2.5-pro", custom_step_one: "gpt-4-turbo", }, - TOOL_CHAIN_FALLBACK_MODEL: "openai/gpt-5-mini", + TOOL_CHAIN_FALLBACK_MODEL: "openai/gpt-5.4-mini", })); describe("getPrepareStepResult", () => { @@ -87,7 +87,7 @@ describe("getPrepareStepResult", () => { const result = getPrepareStepResult(options as any); expect(result).toEqual({ toolChoice: { type: "tool", toolName: "step_one" }, - model: "openai/gpt-5-mini", + model: "openai/gpt-5.4-mini", }); }); @@ -257,7 +257,7 @@ describe("getPrepareStepResult", () => { const result = getPrepareStepResult(options as any); expect(result).toEqual({ toolChoice: { type: "tool", toolName: "custom_step_two" }, - model: "openai/gpt-5-mini", + model: "openai/gpt-5.4-mini", messages: [ { role: "user", content: "Hello" }, { role: "user", content: "Reference message" }, @@ -313,7 +313,7 @@ describe("getPrepareStepResult", () => { }; const result = getPrepareStepResult(options as any); - expect(result?.model).toBe("openai/gpt-5-mini"); + expect(result?.model).toBe("openai/gpt-5.4-mini"); }); }); }); diff --git a/lib/chat/toolChains/toolChains.ts b/lib/chat/toolChains/toolChains.ts index eeb93908..2c14aed5 100644 --- a/lib/chat/toolChains/toolChains.ts +++ b/lib/chat/toolChains/toolChains.ts @@ -17,7 +17,7 @@ export type PrepareStepResult = { // Forced toolChoice is incompatible with Anthropic extended thinking. // Tool chain steps use this model unless overridden by TOOL_MODEL_MAP. -export const TOOL_CHAIN_FALLBACK_MODEL: LanguageModel = "openai/gpt-5-mini"; +export const TOOL_CHAIN_FALLBACK_MODEL: LanguageModel = "openai/gpt-5.4-mini"; // Map specific tools to their required models export const TOOL_MODEL_MAP: Record = { From 1df0052b9ac86d525fe5fc32afc6eb8eb837d268 Mon Sep 17 00:00:00 2001 From: Sidney Swift <158200036+sidneyswift@users.noreply.github.com> Date: Wed, 8 Apr 2026 14:49:22 -0400 Subject: [PATCH 4/5] style: fix prettier formatting on import Made-with: Cursor --- lib/chat/toolChains/getPrepareStepResult.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/chat/toolChains/getPrepareStepResult.ts b/lib/chat/toolChains/getPrepareStepResult.ts index 56d79717..6519ce21 100644 --- a/lib/chat/toolChains/getPrepareStepResult.ts +++ b/lib/chat/toolChains/getPrepareStepResult.ts @@ -1,5 +1,10 @@ import { LanguageModel, ModelMessage, StepResult, ToolSet } from "ai"; -import { PrepareStepResult, TOOL_CHAIN_FALLBACK_MODEL, TOOL_CHAINS, TOOL_MODEL_MAP } from "./toolChains"; +import { + PrepareStepResult, + TOOL_CHAIN_FALLBACK_MODEL, + TOOL_CHAINS, + TOOL_MODEL_MAP, +} from "./toolChains"; import getExecutedToolTimeline from "./getExecutedToolTimeline"; type PrepareStepOptions = { From a2e96d265ad0023588204b81417d6729533a7a87 Mon Sep 17 00:00:00 2001 From: Sidney Swift <158200036+sidneyswift@users.noreply.github.com> Date: Wed, 8 Apr 2026 15:10:30 -0400 Subject: [PATCH 5/5] refactor: remove TOOL_CHAIN_FALLBACK_MODEL, use TOOL_MODEL_MAP for all chain tools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses code review feedback — use existing TOOL_MODEL_MAP pattern instead of a separate fallback constant. All tool chain tools now have explicit model entries in TOOL_MODEL_MAP. Made-with: Cursor --- .../__tests__/getPrepareStepResult.test.ts | 7 ++----- lib/chat/toolChains/getPrepareStepResult.ts | 12 +++++------ lib/chat/toolChains/toolChains.ts | 20 +++++++++++++++---- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/lib/chat/toolChains/__tests__/getPrepareStepResult.test.ts b/lib/chat/toolChains/__tests__/getPrepareStepResult.test.ts index a5c23c49..df582c01 100644 --- a/lib/chat/toolChains/__tests__/getPrepareStepResult.test.ts +++ b/lib/chat/toolChains/__tests__/getPrepareStepResult.test.ts @@ -21,7 +21,6 @@ vi.mock("../toolChains", () => ({ step_two: "gemini-2.5-pro", custom_step_one: "gpt-4-turbo", }, - TOOL_CHAIN_FALLBACK_MODEL: "openai/gpt-5.4-mini", })); describe("getPrepareStepResult", () => { @@ -87,7 +86,6 @@ describe("getPrepareStepResult", () => { const result = getPrepareStepResult(options as any); expect(result).toEqual({ toolChoice: { type: "tool", toolName: "step_one" }, - model: "openai/gpt-5.4-mini", }); }); @@ -257,7 +255,6 @@ describe("getPrepareStepResult", () => { const result = getPrepareStepResult(options as any); expect(result).toEqual({ toolChoice: { type: "tool", toolName: "custom_step_two" }, - model: "openai/gpt-5.4-mini", messages: [ { role: "user", content: "Hello" }, { role: "user", content: "Reference message" }, @@ -294,7 +291,7 @@ describe("getPrepareStepResult", () => { expect(result?.model).toBe("gemini-2.5-pro"); }); - it("uses fallback model when tool is not in TOOL_MODEL_MAP", () => { + it("does not include model when tool is not in TOOL_MODEL_MAP", () => { const options = { steps: [ { @@ -313,7 +310,7 @@ describe("getPrepareStepResult", () => { }; const result = getPrepareStepResult(options as any); - expect(result?.model).toBe("openai/gpt-5.4-mini"); + expect(result?.model).toBeUndefined(); }); }); }); diff --git a/lib/chat/toolChains/getPrepareStepResult.ts b/lib/chat/toolChains/getPrepareStepResult.ts index 6519ce21..4362ea48 100644 --- a/lib/chat/toolChains/getPrepareStepResult.ts +++ b/lib/chat/toolChains/getPrepareStepResult.ts @@ -1,10 +1,5 @@ import { LanguageModel, ModelMessage, StepResult, ToolSet } from "ai"; -import { - PrepareStepResult, - TOOL_CHAIN_FALLBACK_MODEL, - TOOL_CHAINS, - TOOL_MODEL_MAP, -} from "./toolChains"; +import { PrepareStepResult, TOOL_CHAINS, TOOL_MODEL_MAP } from "./toolChains"; import getExecutedToolTimeline from "./getExecutedToolTimeline"; type PrepareStepOptions = { @@ -62,7 +57,10 @@ const getPrepareStepResult = (options: PrepareStepOptions): PrepareStepResult | result.messages = options.messages.concat(nextToolItem.messages); } - result.model = TOOL_MODEL_MAP[nextToolItem.toolName] || TOOL_CHAIN_FALLBACK_MODEL; + const model = TOOL_MODEL_MAP[nextToolItem.toolName]; + if (model) { + result.model = model; + } return result; } diff --git a/lib/chat/toolChains/toolChains.ts b/lib/chat/toolChains/toolChains.ts index 2c14aed5..98f2c6f7 100644 --- a/lib/chat/toolChains/toolChains.ts +++ b/lib/chat/toolChains/toolChains.ts @@ -16,12 +16,24 @@ export type PrepareStepResult = { }; // Forced toolChoice is incompatible with Anthropic extended thinking. -// Tool chain steps use this model unless overridden by TOOL_MODEL_MAP. -export const TOOL_CHAIN_FALLBACK_MODEL: LanguageModel = "openai/gpt-5.4-mini"; - -// Map specific tools to their required models +// Every tool used in a chain must have a model here to avoid the conflict. export const TOOL_MODEL_MAP: Record = { update_account_info: "gemini-2.5-pro", + get_spotify_search: "openai/gpt-5.4-mini", + update_artist_socials: "openai/gpt-5.4-mini", + artist_deep_research: "openai/gpt-5.4-mini", + spotify_deep_research: "openai/gpt-5.4-mini", + get_artist_socials: "openai/gpt-5.4-mini", + get_spotify_artist_top_tracks: "openai/gpt-5.4-mini", + get_spotify_artist_albums: "openai/gpt-5.4-mini", + get_spotify_album: "openai/gpt-5.4-mini", + search_web: "openai/gpt-5.4-mini", + generate_txt_file: "openai/gpt-5.4-mini", + create_segments: "openai/gpt-5.4-mini", + youtube_login: "openai/gpt-5.4-mini", + web_deep_research: "openai/gpt-5.4-mini", + create_knowledge_base: "openai/gpt-5.4-mini", + send_email: "openai/gpt-5.4-mini", }; // Map trigger tool -> sequence AFTER trigger