* feat: refactor Claude API Profiles to Claude Compatible Providers
- Rename ClaudeApiProfile to ClaudeCompatibleProvider with models[] array
- Each ProviderModel has mapsToClaudeModel field for Claude tier mapping
- Add providerType field for provider-specific icons (glm, minimax, openrouter)
- Add thinking level support for provider models in phase selectors
- Show all mapped Claude models per provider model (e.g., "Maps to Haiku, Sonnet, Opus")
- Add Bulk Replace feature to switch all phases to a provider at once
- Hide Bulk Replace button when no providers are enabled
- Fix project-level phaseModelOverrides not persisting after refresh
- Fix deleting last provider not persisting (remove empty array guard)
- Add getProviderByModelId() helper for all SDK routes
- Update all routes to pass provider config for provider models
- Update terminology from "profiles" to "providers" throughout UI
- Update documentation to reflect new provider system
* fix: atomic writer race condition and bulk replace reset to defaults
1. AtomicWriter Race Condition Fix (libs/utils/src/atomic-writer.ts):
- Changed temp file naming from Date.now() to Date.now() + random hex
- Uses crypto.randomBytes(4).toString('hex') for uniqueness
- Prevents ENOENT errors when multiple concurrent writes happen
within the same millisecond
2. Bulk Replace "Anthropic Direct" Reset (both dialogs):
- When selecting "Anthropic Direct", now uses DEFAULT_PHASE_MODELS
- Properly resets thinking levels and other settings to defaults
- Added thinkingLevel to the change detection comparison
- Affects both global and project-level bulk replace dialogs
* fix: update tests for new model resolver passthrough behavior
1. model-resolver tests:
- Unknown models now pass through unchanged (provider model support)
- Removed expectations for warnings on unknown models
- Updated case sensitivity and edge case tests accordingly
- Added tests for provider-like model names (GLM-4.7, MiniMax-M2.1)
2. atomic-writer tests:
- Updated regex to match new temp file format with random suffix
- Format changed from .tmp.{timestamp} to .tmp.{timestamp}.{hex}
* refactor: simplify getPhaseModelWithOverrides calls per code review
Address code review feedback on PR #629:
- Make settingsService parameter optional in getPhaseModelWithOverrides
- Function now handles undefined settingsService gracefully by returning defaults
- Remove redundant ternary checks in 4 call sites:
- apps/server/src/routes/context/routes/describe-file.ts
- apps/server/src/routes/context/routes/describe-image.ts
- apps/server/src/routes/worktree/routes/generate-commit-message.ts
- apps/server/src/services/auto-mode-service.ts
- Remove unused DEFAULT_PHASE_MODELS imports where applicable
* test: fix server tests for provider model passthrough behavior
- Update model-resolver.test.ts to expect unknown models to pass through
unchanged (supports ClaudeCompatibleProvider models like GLM-4.7)
- Remove warning expectations for unknown models (valid for providers)
- Add missing getCredentials and getGlobalSettings mocks to
ideation-service.test.ts for settingsService
* fix: address code review feedback for model providers
- Honor thinkingLevel in generate-commit-message.ts
- Pass claudeCompatibleProvider in ideation-service.ts for provider models
- Resolve provider configuration for model overrides in generate-suggestions.ts
- Update "Active Profile" to "Active Provider" label in project-claude-section
- Use substring instead of deprecated substr in api-profiles-section
- Preserve provider enabled state when editing in api-profiles-section
* fix: address CodeRabbit review issues for Claude Compatible Providers
- Fix TypeScript TS2339 error in generate-suggestions.ts where
settingsService was narrowed to 'never' type in else branch
- Use DEFAULT_PHASE_MODELS per-phase defaults instead of hardcoded
'sonnet' in settings-helpers.ts
- Remove duplicate eventHooks key in use-settings-migration.ts
- Add claudeCompatibleProviders to localStorage migration parsing
and merging functions
- Handle canonical claude-* model IDs (claude-haiku, claude-sonnet,
claude-opus) in project-models-section display names
This resolves the CI build failures and addresses code review feedback.
* fix: skip broken list-view-priority E2E test and add Priority column label
- Skip list-view-priority.spec.ts with TODO explaining the infrastructure
issue: setupRealProject only sets localStorage but server settings
take precedence with localStorageMigrated: true
- Add 'Priority' label to list-header.tsx for the priority column
(was empty string, now shows proper header text)
- Increase column width to accommodate the label
The E2E test issue is that tests create features in a temp directory,
but the server loads from the E2E Test Project fixture path set in
setup-e2e-fixtures.mjs. Needs infrastructure fix to properly switch
projects or create features through UI instead of on disk.
10 KiB
Claude Compatible Providers System
This document describes the implementation of Claude Compatible Providers, allowing users to configure alternative API endpoints that expose Claude-compatible models to the application.
Overview
Claude Compatible Providers allow Automaker to work with third-party API endpoints that implement Claude's API protocol. This enables:
- Cost savings: Use providers like z.AI GLM or MiniMax at lower costs
- Alternative models: Access models like GLM-4.7 or MiniMax M2.1 through familiar interfaces
- Flexibility: Configure per-phase model selection to optimize for speed vs quality
- Project overrides: Use different providers for different projects
Architecture
Type Definitions
ClaudeCompatibleProvider
export interface ClaudeCompatibleProvider {
id: string; // Unique identifier (UUID)
name: string; // Display name (e.g., "z.AI GLM")
baseUrl: string; // API endpoint URL
providerType?: string; // Provider type for icon/grouping (e.g., 'glm', 'minimax', 'openrouter')
apiKeySource?: ApiKeySource; // 'inline' | 'env' | 'credentials'
apiKey?: string; // API key (when apiKeySource = 'inline')
useAuthToken?: boolean; // Use ANTHROPIC_AUTH_TOKEN header
timeoutMs?: number; // Request timeout in milliseconds
disableNonessentialTraffic?: boolean; // Minimize non-essential API calls
enabled?: boolean; // Whether provider is active (default: true)
models?: ProviderModel[]; // Models exposed by this provider
}
ProviderModel
export interface ProviderModel {
id: string; // Model ID sent to API (e.g., "GLM-4.7")
displayName: string; // Display name in UI (e.g., "GLM 4.7")
mapsToClaudeModel?: ClaudeModelAlias; // Which Claude tier this replaces ('haiku' | 'sonnet' | 'opus')
capabilities?: {
supportsVision?: boolean; // Whether model supports image inputs
supportsThinking?: boolean; // Whether model supports extended thinking
maxThinkingLevel?: ThinkingLevel; // Maximum thinking level if supported
};
}
PhaseModelEntry
Phase model configuration now supports provider models:
export interface PhaseModelEntry {
providerId?: string; // Provider ID (undefined = native Claude)
model: string; // Model ID or alias
thinkingLevel?: ThinkingLevel; // 'none' | 'low' | 'medium' | 'high'
}
Provider Templates
Available provider templates in CLAUDE_PROVIDER_TEMPLATES:
| Template | Provider Type | Base URL | Description |
|---|---|---|---|
| Direct Anthropic | anthropic | https://api.anthropic.com |
Standard Anthropic API |
| OpenRouter | openrouter | https://openrouter.ai/api |
Access Claude and 300+ models |
| z.AI GLM | glm | https://api.z.ai/api/anthropic |
GLM models at lower cost |
| MiniMax | minimax | https://api.minimax.io/anthropic |
MiniMax M2.1 model |
| MiniMax (China) | minimax | https://api.minimaxi.com/anthropic |
MiniMax for China region |
Model Mappings
Each provider model specifies which Claude model tier it maps to via mapsToClaudeModel:
z.AI GLM:
GLM-4.5-Air→ haikuGLM-4.7→ sonnet, opus
MiniMax:
MiniMax-M2.1→ haiku, sonnet, opus
OpenRouter:
anthropic/claude-3.5-haiku→ haikuanthropic/claude-3.5-sonnet→ sonnetanthropic/claude-3-opus→ opus
Server-Side Implementation
API Key Resolution
The buildEnv() function in claude-provider.ts resolves API keys based on apiKeySource:
function buildEnv(
providerConfig?: ClaudeCompatibleProvider,
credentials?: Credentials
): Record<string, string | undefined> {
if (providerConfig) {
let apiKey: string | undefined;
const source = providerConfig.apiKeySource ?? 'inline';
switch (source) {
case 'inline':
apiKey = providerConfig.apiKey;
break;
case 'env':
apiKey = process.env.ANTHROPIC_API_KEY;
break;
case 'credentials':
apiKey = credentials?.apiKeys?.anthropic;
break;
}
// ... build environment with resolved key
}
}
Provider Lookup
The getProviderByModelId() helper resolves provider configuration from model IDs:
export async function getProviderByModelId(
modelId: string,
settingsService: SettingsService,
logPrefix?: string
): Promise<{
provider?: ClaudeCompatibleProvider;
resolvedModel?: string;
credentials?: Credentials;
}>;
This is used by all routes that call the Claude SDK to:
- Check if the model ID belongs to a provider
- Get the provider configuration (baseUrl, auth, etc.)
- Resolve the
mapsToClaudeModelfor the SDK
Phase Model Resolution
The getPhaseModelWithOverrides() helper gets effective phase model config:
export async function getPhaseModelWithOverrides(
phaseKey: PhaseModelKey,
settingsService: SettingsService,
projectPath?: string,
logPrefix?: string
): Promise<{
model: string;
thinkingLevel?: ThinkingLevel;
providerId?: string;
providerConfig?: ClaudeCompatibleProvider;
credentials?: Credentials;
}>;
This handles:
- Project-level overrides (if projectPath provided)
- Global phase model settings
- Default fallback models
UI Implementation
Model Selection Dropdowns
Phase model selectors (PhaseModelSelector) display:
- Claude Models - Native Claude models (Haiku, Sonnet, Opus)
- Provider Sections - Each enabled provider as a separate group:
- Section header:
{provider.name} (via Claude) - Models with their mapped Claude tiers: "Maps to Haiku, Sonnet, Opus"
- Thinking level submenu for models that support it
- Section header:
Provider Icons
Icons are determined by providerType:
glm→ Z logominimax→ MiniMax logoopenrouter→ OpenRouter logo- Generic → OpenRouter as fallback
Bulk Replace
The "Bulk Replace" feature allows switching all phase models to a provider at once:
- Select a provider from the dropdown
- Preview shows which models will be assigned:
- haiku phases → provider's haiku-mapped model
- sonnet phases → provider's sonnet-mapped model
- opus phases → provider's opus-mapped model
- Apply replaces all phase model configurations
The Bulk Replace button only appears when at least one provider is enabled.
Project-Level Overrides
Projects can override global phase model settings via phaseModelOverrides:
interface Project {
// ...
phaseModelOverrides?: PhaseModelConfig; // Per-phase overrides
}
Storage
Project overrides are stored in .automaker/settings.json:
{
"phaseModelOverrides": {
"enhancementModel": {
"providerId": "provider-uuid",
"model": "GLM-4.5-Air",
"thinkingLevel": "none"
}
}
}
Resolution Priority
- Project override for specific phase (if set)
- Global phase model setting
- Default model for phase
Migration
v5 → v6 Migration
The system migrated from claudeApiProfiles to claudeCompatibleProviders:
// Old: modelMappings object
{
modelMappings: {
haiku: 'GLM-4.5-Air',
sonnet: 'GLM-4.7',
opus: 'GLM-4.7'
}
}
// New: models array with mapsToClaudeModel
{
models: [
{ 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' },
]
}
The migration is automatic and preserves existing provider configurations.
Files Changed
Types
| File | Changes |
|---|---|
libs/types/src/settings.ts |
ClaudeCompatibleProvider, ProviderModel, PhaseModelEntry types |
libs/types/src/provider.ts |
ExecuteOptions.claudeCompatibleProvider field |
libs/types/src/index.ts |
Exports for new types |
Server
| File | Changes |
|---|---|
apps/server/src/providers/claude-provider.ts |
Provider config handling, buildEnv updates |
apps/server/src/lib/settings-helpers.ts |
getProviderByModelId(), getPhaseModelWithOverrides() |
apps/server/src/services/settings-service.ts |
v5→v6 migration |
apps/server/src/routes/**/*.ts |
Provider lookup for all SDK calls |
UI
| File | Changes |
|---|---|
apps/ui/src/.../phase-model-selector.tsx |
Provider model rendering, thinking levels |
apps/ui/src/.../bulk-replace-dialog.tsx |
Bulk replace feature |
apps/ui/src/.../api-profiles-section.tsx |
Provider management UI |
apps/ui/src/components/ui/provider-icon.tsx |
Provider-specific icons |
apps/ui/src/hooks/use-project-settings-loader.ts |
Load phaseModelOverrides |
Testing
# Build and run
npm run build:packages
npm run dev:web
# Run server tests
npm run test:server
Test Cases
- Provider setup: Add z.AI GLM provider with inline API key
- Model selection: Select GLM-4.7 for a phase, verify it appears in dropdown
- Thinking levels: Select thinking level for provider model
- Bulk replace: Switch all phases to a provider at once
- Project override: Set per-project model override, verify it persists
- Provider deletion: Delete all providers, verify empty state persists
Future Enhancements
Potential improvements:
- Provider validation: Test API connection before saving
- Usage tracking: Show which phases use which provider
- Cost estimation: Display estimated costs per provider
- Model capabilities: Auto-detect supported features from provider