diff --git a/.dev.vars.example b/.dev.vars.example index 6d16811..d1c50f4 100644 --- a/.dev.vars.example +++ b/.dev.vars.example @@ -2,6 +2,7 @@ # .dev.vars is gitignored and used by wrangler dev ANTHROPIC_API_KEY=sk-ant-... +# MINIMAX_API_KEY=sk-minimax-... # Local development mode - skips Cloudflare Access auth and bypasses device pairing # DEV_MODE=true diff --git a/README.md b/README.md index 90bf7b7..80ffc62 100644 --- a/README.md +++ b/README.md @@ -355,6 +355,35 @@ npm run deploy The `AI_GATEWAY_*` variables take precedence over `ANTHROPIC_*` if both are set. +## Optional: MiniMax Provider + +To use the MiniMax provider, set the `MINIMAX_API_KEY` secret and configure the provider in your OpenClaw config file (`clawdbot.json`): + +```bash +npx wrangler secret put MINIMAX_API_KEY +``` + +Then add a `minimax` entry under `models.providers` in the config (via the Control UI or by editing the config directly): + +```json +{ + "models": { + "providers": { + "minimax": { + "baseUrl": "", + "api": "anthropic-messages", + "apiKey": "${MINIMAX_API_KEY}", + "models": [ + { "id": "", "name": "", "contextWindow": 200000 } + ] + } + } + } +} +``` + +The `MINIMAX_API_KEY` environment variable is forwarded into the container automatically. If you set `model.primary` to a minimax model in the config, it will be preserved across restarts. + ## All Secrets Reference | Secret | Required | Description | @@ -364,6 +393,7 @@ The `AI_GATEWAY_*` variables take precedence over `ANTHROPIC_*` if both are set. | `ANTHROPIC_API_KEY` | Yes* | Direct Anthropic API key (fallback if AI Gateway not configured) | | `ANTHROPIC_BASE_URL` | No | Direct Anthropic API base URL (fallback) | | `OPENAI_API_KEY` | No | OpenAI API key (alternative provider) | +| `MINIMAX_API_KEY` | No | MiniMax API key for OpenClaw/OpenClawd provider (optional) | | `CF_ACCESS_TEAM_DOMAIN` | Yes* | Cloudflare Access team domain (required for admin UI) | | `CF_ACCESS_AUD` | Yes* | Cloudflare Access application audience (required for admin UI) | | `MOLTBOT_GATEWAY_TOKEN` | Yes | Gateway token for authentication (pass via `?token=` query param) | diff --git a/src/gateway/env.test.ts b/src/gateway/env.test.ts index 29f033d..7ef1907 100644 --- a/src/gateway/env.test.ts +++ b/src/gateway/env.test.ts @@ -85,6 +85,12 @@ describe('buildEnvVars', () => { expect(result.OPENAI_API_KEY).toBe('sk-openai-key'); }); + it('includes MINIMAX_API_KEY when set', () => { + const env = createMockEnv({ MINIMAX_API_KEY: 'sk-minimax-key' }); + const result = buildEnvVars(env); + expect(result.MINIMAX_API_KEY).toBe('sk-minimax-key'); + }); + it('maps MOLTBOT_GATEWAY_TOKEN to CLAWDBOT_GATEWAY_TOKEN for container', () => { const env = createMockEnv({ MOLTBOT_GATEWAY_TOKEN: 'my-token' }); const result = buildEnvVars(env); diff --git a/src/gateway/env.ts b/src/gateway/env.ts index a57e781..32e933c 100644 --- a/src/gateway/env.ts +++ b/src/gateway/env.ts @@ -30,6 +30,9 @@ export function buildEnvVars(env: MoltbotEnv): Record { if (!envVars.OPENAI_API_KEY && env.OPENAI_API_KEY) { envVars.OPENAI_API_KEY = env.OPENAI_API_KEY; } + if (env.MINIMAX_API_KEY) { + envVars.MINIMAX_API_KEY = env.MINIMAX_API_KEY; + } // Pass base URL (used by start-moltbot.sh to determine provider) if (normalizedBaseUrl) { diff --git a/src/types.ts b/src/types.ts index bb82c8c..8ae4a1e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -14,6 +14,8 @@ export interface MoltbotEnv { ANTHROPIC_API_KEY?: string; ANTHROPIC_BASE_URL?: string; OPENAI_API_KEY?: string; + // MiniMax provider (OpenClaw/OpenClawd) + MINIMAX_API_KEY?: string; MOLTBOT_GATEWAY_TOKEN?: string; // Gateway token (mapped to CLAWDBOT_GATEWAY_TOKEN for container) CLAWDBOT_BIND_MODE?: string; diff --git a/start-moltbot.sh b/start-moltbot.sh index 7e225e8..5fa597d 100644 --- a/start-moltbot.sh +++ b/start-moltbot.sh @@ -235,7 +235,15 @@ if (isOpenAI) { config.agents.defaults.models['openai/gpt-5.2'] = { alias: 'GPT-5.2' }; config.agents.defaults.models['openai/gpt-5'] = { alias: 'GPT-5' }; config.agents.defaults.models['openai/gpt-4.5-preview'] = { alias: 'GPT-4.5' }; - config.agents.defaults.model.primary = 'openai/gpt-5.2'; + + // Preserve user-selected primary model only if it is valid for the current provider catalog. + // If the primary points to a model that isn't in the allowlist, Moltbot may reject the config. + const desiredPrimary = 'openai/gpt-5.2'; + const currentPrimary = config.agents.defaults.model.primary; + const allowlisted = config.agents.defaults.models && currentPrimary && config.agents.defaults.models[currentPrimary]; + if (!currentPrimary || !allowlisted) { + config.agents.defaults.model.primary = desiredPrimary; + } } else if (baseUrl) { console.log('Configuring Anthropic provider with base URL:', baseUrl); config.models = config.models || {}; @@ -259,10 +267,35 @@ if (isOpenAI) { config.agents.defaults.models['anthropic/claude-opus-4-5-20251101'] = { alias: 'Opus 4.5' }; config.agents.defaults.models['anthropic/claude-sonnet-4-5-20250929'] = { alias: 'Sonnet 4.5' }; config.agents.defaults.models['anthropic/claude-haiku-4-5-20251001'] = { alias: 'Haiku 4.5' }; - config.agents.defaults.model.primary = 'anthropic/claude-opus-4-5-20251101'; + + // Preserve user-selected primary model only if it is valid for the current provider catalog. + // If the primary points to a model that isn't in the allowlist, Moltbot may reject the config. + const desiredPrimary = 'anthropic/claude-opus-4-5-20251101'; + const currentPrimary = config.agents.defaults.model.primary; + const allowlisted = config.agents.defaults.models && currentPrimary && config.agents.defaults.models[currentPrimary]; + if (!currentPrimary || !allowlisted) { + config.agents.defaults.model.primary = desiredPrimary; + } } else { // Default to Anthropic without custom base URL (uses built-in pi-ai catalog) - config.agents.defaults.model.primary = 'anthropic/claude-opus-4-5'; + // Preserve a user-selected primary only when it is plausibly supported by the current config. + // If env/provider settings changed and the primary is no longer supported, Moltbot may reject the config. + const defaultPrimary = 'anthropic/claude-opus-4-5'; + const currentPrimary = config.agents.defaults.model.primary; + const hasMinimaxProvider = !!(config.models?.providers?.minimax); + const hasOpenAIProvider = !!(config.models?.providers?.openai); + const hasAnthropicProvider = !!(config.models?.providers?.anthropic); + + const keepPrimary = !!( + currentPrimary === defaultPrimary || + (hasMinimaxProvider && currentPrimary?.startsWith('minimax/')) || + (hasOpenAIProvider && currentPrimary?.startsWith('openai/')) || + (hasAnthropicProvider && currentPrimary?.startsWith('anthropic/')) + ); + + if (!currentPrimary || !keepPrimary) { + config.agents.defaults.model.primary = defaultPrimary; + } } // Write updated config