diff --git a/apps/server/src/routes/enhance-prompt/routes/enhance.ts b/apps/server/src/routes/enhance-prompt/routes/enhance.ts index dbdde007..5dbd7268 100644 --- a/apps/server/src/routes/enhance-prompt/routes/enhance.ts +++ b/apps/server/src/routes/enhance-prompt/routes/enhance.ts @@ -219,18 +219,21 @@ export function createEnhanceHandler( } } - // Resolve the model - use provider resolved model, passed model, or default to sonnet - const resolvedModel = - providerResolvedModel || resolveModelString(model, CLAUDE_MODEL_MAP.sonnet); + // Resolve the model for API call. + // CRITICAL: For custom providers (GLM, MiniMax), pass the provider's model ID (e.g. "GLM-4.7") + // to the API, NOT the resolved Claude model - otherwise we get "model not found" + const modelForApi = claudeCompatibleProvider + ? model + : providerResolvedModel || resolveModelString(model, CLAUDE_MODEL_MAP.sonnet); - logger.debug(`Using model: ${resolvedModel}`); + logger.debug(`Using model: ${modelForApi}`); // Use simpleQuery - provider abstraction handles routing to correct provider // The system prompt is combined with user prompt since some providers // don't have a separate system prompt concept const result = await simpleQuery({ prompt: [systemPrompt, projectContext, userPrompt].filter(Boolean).join('\n\n'), - model: resolvedModel, + model: modelForApi, cwd: process.cwd(), // Enhancement doesn't need a specific working directory maxTurns: 1, allowedTools: [], diff --git a/apps/server/src/routes/github/routes/validate-issue.ts b/apps/server/src/routes/github/routes/validate-issue.ts index 69a13b83..9f3af5cf 100644 --- a/apps/server/src/routes/github/routes/validate-issue.ts +++ b/apps/server/src/routes/github/routes/validate-issue.ts @@ -188,8 +188,11 @@ ${basePrompt}`; } } - // Use provider resolved model if available, otherwise use original model - const effectiveModel = providerResolvedModel || (model as string); + // CRITICAL: For custom providers (GLM, MiniMax), pass the provider's model ID (e.g. "GLM-4.7") + // to the API, NOT the resolved Claude model - otherwise we get "model not found" + const effectiveModel = claudeCompatibleProvider + ? (model as string) + : providerResolvedModel || (model as string); logger.info(`Using model: ${effectiveModel}`); // Use streamingQuery with event callbacks diff --git a/apps/server/src/services/agent-service.ts b/apps/server/src/services/agent-service.ts index 09c91979..e5458a98 100644 --- a/apps/server/src/services/agent-service.ts +++ b/apps/server/src/services/agent-service.ts @@ -325,8 +325,9 @@ export class AgentService { const effectiveThinkingLevel = thinkingLevel ?? session.thinkingLevel; const effectiveReasoningEffort = reasoningEffort ?? session.reasoningEffort; - // When using a provider model, use the resolved Claude model (from mapsToClaudeModel) - // e.g., "GLM-4.5-Air" -> "claude-haiku-4-5" + // When using a custom provider (GLM, MiniMax), use resolved Claude model for SDK config + // (thinking level budgets, allowedTools) but we MUST pass the provider's model ID + // (e.g. "GLM-4.7") to the API - not "claude-sonnet-4-20250514" which causes "model not found" const modelForSdk = providerResolvedModel || model; const sessionModelForSdk = providerResolvedModel ? undefined : session.model; @@ -387,10 +388,16 @@ export class AgentService { } // Get provider for this model (with prefix) - const provider = ProviderFactory.getProviderForModel(effectiveModel); + // When using custom provider (GLM, MiniMax), requestedModel routes to Claude provider + const modelForProvider = claudeCompatibleProvider ? requestedModel : effectiveModel; + const provider = ProviderFactory.getProviderForModel(modelForProvider); // Strip provider prefix - providers should receive bare model IDs - const bareModel = stripProviderPrefix(effectiveModel); + // CRITICAL: For custom providers (GLM, MiniMax), pass the provider's model ID (e.g. "GLM-4.7") + // to the API, NOT the resolved Claude model - otherwise we get "model not found" + const bareModel = claudeCompatibleProvider + ? requestedModel + : stripProviderPrefix(effectiveModel); // Build options for provider const options: ExecuteOptions = { diff --git a/apps/server/src/services/auto-mode/facade.ts b/apps/server/src/services/auto-mode/facade.ts index d1faa33f..d56985a5 100644 --- a/apps/server/src/services/auto-mode/facade.ts +++ b/apps/server/src/services/auto-mode/facade.ts @@ -20,7 +20,7 @@ import { createLogger, loadContextFiles, classifyError } from '@automaker/utils' import { getFeatureDir } from '@automaker/platform'; import * as secureFs from '../../lib/secure-fs.js'; import { validateWorkingDirectory } from '../../lib/sdk-options.js'; -import { getPromptCustomization } from '../../lib/settings-helpers.js'; +import { getPromptCustomization, getProviderByModelId } from '../../lib/settings-helpers.js'; import { TypedEventBus } from '../typed-event-bus.js'; import { ConcurrencyManager } from '../concurrency-manager.js'; import { WorktreeResolver } from '../worktree-resolver.js'; @@ -169,6 +169,23 @@ export class AutoModeServiceFacade { const provider = ProviderFactory.getProviderForModel(resolvedModel); const effectiveBareModel = stripProviderPrefix(resolvedModel); + // Resolve custom provider (GLM, MiniMax, etc.) for baseUrl and credentials + let claudeCompatibleProvider: + | import('@automaker/types').ClaudeCompatibleProvider + | undefined; + let credentials: import('@automaker/types').Credentials | undefined; + if (resolvedModel && settingsService) { + const providerResult = await getProviderByModelId( + resolvedModel, + settingsService, + '[AutoModeFacade]' + ); + if (providerResult.provider) { + claudeCompatibleProvider = providerResult.provider; + credentials = providerResult.credentials; + } + } + await agentExecutor.execute( { workDir, @@ -187,6 +204,8 @@ export class AutoModeServiceFacade { branchName: opts?.branchName as string | null | undefined, provider, effectiveBareModel, + credentials, + claudeCompatibleProvider, }, { waitForApproval: (fId, projPath) => planApprovalService.waitForApproval(fId, projPath), @@ -273,6 +292,23 @@ export class AutoModeServiceFacade { const provider = ProviderFactory.getProviderForModel(resolvedModel); const effectiveBareModel = stripProviderPrefix(resolvedModel); + // Resolve custom provider (GLM, MiniMax, etc.) for baseUrl and credentials + let claudeCompatibleProvider: + | import('@automaker/types').ClaudeCompatibleProvider + | undefined; + let credentials: import('@automaker/types').Credentials | undefined; + if (resolvedModel && settingsService) { + const providerResult = await getProviderByModelId( + resolvedModel, + settingsService, + '[AutoModeFacade]' + ); + if (providerResult.provider) { + claudeCompatibleProvider = providerResult.provider; + credentials = providerResult.credentials; + } + } + await agentExecutor.execute( { workDir, @@ -290,6 +326,8 @@ export class AutoModeServiceFacade { branchName: opts?.branchName, provider, effectiveBareModel, + credentials, + claudeCompatibleProvider, }, { waitForApproval: (fId, projPath) => planApprovalService.waitForApproval(fId, projPath), diff --git a/apps/server/src/services/ideation-service.ts b/apps/server/src/services/ideation-service.ts index 62edeaae..efa32802 100644 --- a/apps/server/src/services/ideation-service.ts +++ b/apps/server/src/services/ideation-service.ts @@ -230,10 +230,9 @@ export class IdeationService { ); if (providerResult.provider) { claudeCompatibleProvider = providerResult.provider; - // Use resolved model from provider if available (maps to Claude model) - if (providerResult.resolvedModel) { - modelId = providerResult.resolvedModel; - } + // CRITICAL: For custom providers, use the provider's model ID (e.g. "GLM-4.7") + // for the API call, NOT the resolved Claude model - otherwise we get "model not found" + modelId = options.model; credentials = providerResult.credentials ?? credentials; } } diff --git a/libs/types/src/settings.ts b/libs/types/src/settings.ts index eb53564d..c4720149 100644 --- a/libs/types/src/settings.ts +++ b/libs/types/src/settings.ts @@ -466,7 +466,7 @@ export const CLAUDE_PROVIDER_TEMPLATES: ClaudeCompatibleProviderTemplate[] = [ defaultModels: [ { id: 'GLM-4.5-Air', displayName: 'GLM 4.5 Air', mapsToClaudeModel: 'haiku' }, { id: 'GLM-4.7', displayName: 'GLM 4.7', mapsToClaudeModel: 'sonnet' }, - { id: 'GLM-4.7', displayName: 'GLM 4.7', mapsToClaudeModel: 'opus' }, + { id: 'GLM-5', displayName: 'GLM 5', mapsToClaudeModel: 'opus' }, ], }, { @@ -549,7 +549,7 @@ export const CLAUDE_API_PROFILE_TEMPLATES: ClaudeApiProfileTemplate[] = [ modelMappings: { haiku: 'GLM-4.5-Air', sonnet: 'GLM-4.7', - opus: 'GLM-4.7', + opus: 'GLM-5', }, disableNonessentialTraffic: true, description: '3× usage at fraction of cost via GLM Coding Plan',