From 09a4d3f15a40453c56ba1a1d37967ce4d2513430 Mon Sep 17 00:00:00 2001 From: Patrick Patel Date: Tue, 24 Feb 2026 22:21:05 -0600 Subject: [PATCH] fix: Resolve Claude-compatible provider for backlog plan when client sends model (#809) When the Plan dialog sends a model (e.g. MiniMax-M2.1 from phase settings), the server now: - Calls getProviderByModelId() so the correct provider config (baseUrl, credentials) is used for backlog plan generation. - Falls back to getPhaseModelWithOverrides('backlogPlanningModel') when model lookup finds no provider, so the phase's provider is used when the model matches. - Uses a plain system prompt instead of the claude_code preset when a Claude-compatible provider is set; the preset is for native Claude CLI and can break requests to MiniMax/GLM APIs. Previously the request was sent to the default Anthropic endpoint and/or used the preset, causing plan generation to fail for MiniMax/GLM users. Co-authored-by: Cursor --- .../src/routes/backlog-plan/generate-plan.ts | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/apps/server/src/routes/backlog-plan/generate-plan.ts b/apps/server/src/routes/backlog-plan/generate-plan.ts index 2f47d293..4a1dc95a 100644 --- a/apps/server/src/routes/backlog-plan/generate-plan.ts +++ b/apps/server/src/routes/backlog-plan/generate-plan.ts @@ -35,6 +35,7 @@ import { getUseClaudeCodeSystemPromptSetting, getPromptCustomization, getPhaseModelWithOverrides, + getProviderByModelId, } from '../../lib/settings-helpers.js'; /** Maximum number of retry attempts for transient CLI failures */ @@ -231,6 +232,35 @@ export async function generateBacklogPlan( effectiveModel = resolved.model; thinkingLevel = resolved.thinkingLevel; credentials = await settingsService?.getCredentials(); + // Resolve Claude-compatible provider when client sends a model (e.g. MiniMax, GLM) + if (settingsService) { + const providerResult = await getProviderByModelId( + effectiveModel, + settingsService, + '[BacklogPlan]' + ); + if (providerResult.provider) { + claudeCompatibleProvider = providerResult.provider; + if (providerResult.credentials) { + credentials = providerResult.credentials; + } + } + // Fallback: use phase settings provider if model lookup found nothing (e.g. model + // string format differs from provider's model id, but backlog planning phase has providerId). + if (!claudeCompatibleProvider) { + const phaseResult = await getPhaseModelWithOverrides( + 'backlogPlanningModel', + settingsService, + projectPath, + '[BacklogPlan]' + ); + const phaseResolved = resolvePhaseModel(phaseResult.phaseModel); + if (phaseResult.provider && phaseResolved.model === effectiveModel) { + claudeCompatibleProvider = phaseResult.provider; + credentials = phaseResult.credentials ?? credentials; + } + } + } } else if (settingsService) { // Use settings-based model with provider info const phaseResult = await getPhaseModelWithOverrides( @@ -291,8 +321,12 @@ CRITICAL INSTRUCTIONS: ${userPrompt}`; finalSystemPrompt = undefined; // System prompt is now embedded in the user prompt + } else if (claudeCompatibleProvider) { + // Claude-compatible providers (MiniMax, GLM, etc.) use a plain API; do not use + // the claude_code preset (which is for Claude CLI/subprocess and can break the request). + finalSystemPrompt = systemPrompt; } else if (useClaudeCodeSystemPrompt) { - // Use claude_code preset for Claude models so the SDK subprocess + // Use claude_code preset for native Claude so the SDK subprocess // authenticates via CLI OAuth or API key the same way all other SDK calls do. finalSystemPrompt = { type: 'preset',