Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/bitterbot-ai_bitterbot-desktop.json
T

1 line
25 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "bitterbot-ai/bitterbot-desktop", "files": {"docs/providers/xiaomi.md": {"bb1339160fff88bb6e83d6f95cd6001639f64a5f": {"t": 1785595131.6294305, "n": 1804, "h": "ffcbebb696cd418f", "c": "---\nsummary: \"Use Xiaomi MiMo (mimo-v2-flash) with Bitterbot\"\nread_when:\n - You want Xiaomi MiMo models in Bitterbot\n - You need XIAOMI_API_KEY setup\ntitle: \"Xiaomi MiMo\"\n---\n\n# Xiaomi MiMo\n\nXiaomi MiMo is the API platform for **MiMo** models. It provides REST APIs compatible with\nOpenAI and Anthropic formats and uses API keys for authentication. Create your API key in\nthe [Xiaomi MiMo console](https://platform.xiaomimimo.com/#/console/api-keys). Bitterbot uses\nthe `xiaomi` provider with a Xiaomi MiMo API key.\n\n## Model overview\n\n- **mimo-v2-flash**: 262144-token context window, Anthropic Messages API compatible.\n- Base URL: `https://api.xiaomimimo.com/anthropic`\n- Authorization: `Bearer $XIAOMI_API_KEY`\n\n## CLI setup\n\n```bash\nbitterbot onboard --auth-choice xiaomi-api-key\n# or non-interactive\nbitterbot onboard --auth-choice xiaomi-api-key --xiaomi-api-key \"$XIAOMI_API_KEY\"\n```\n\n## Config snippet\n\n```json5\n{\n env: { XIAOMI_API_KEY: \"your-key\" },\n agents: { defaults: { model: { primary: \"xiaomi/mimo-v2-flash\" } } },\n models: {\n mode: \"merge\",\n providers: {\n xiaomi: {\n baseUrl: \"https://api.xiaomimimo.com/anthropic\",\n api: \"anthropic-messages\",\n apiKey: \"XIAOMI_API_KEY\",\n models: [\n {\n id: \"mimo-v2-flash\",\n name: \"Xiaomi MiMo V2 Flash\",\n reasoning: false,\n input: [\"text\"],\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n contextWindow: 262144,\n maxTokens: 8192,\n },\n ],\n },\n },\n },\n}\n```\n\n## Notes\n\n- Model ref: `xiaomi/mimo-v2-flash`.\n- The provider is injected automatically when `XIAOMI_API_KEY` is set (or an auth profile exists).\n- See [/concepts/model-providers](/concepts/model-providers) for provider rules.\n"}}, "src/agents/auth-probe.ts": {"bb1339160fff88bb6e83d6f95cd6001639f64a5f": {"t": 1785746045.3843193, "n": 5146, "h": "723f2d9c35511bf0", "c": "import type { BitterbotConfig } from \"../config/config.js\";\nimport { fetchWithTimeout } from \"../utils/fetch-timeout.js\";\nimport { normalizeProviderId } from \"./model-selection.js\";\n\nconst PROBE_TIMEOUT_MS = 10_000;\n\n/**\n * Live credential probes shared by the onboarding wizard and the\n * models.auth.* gateway RPCs. A probe hits the provider's cheapest\n * authenticated endpoint (the model listing) so keys can be verified\n * before they are saved, without consuming tokens.\n *\n * The probe deliberately never logs or returns the key it was given.\n */\nexport type AuthProbeResult = {\n ok: boolean;\n /** HTTP status when the endpoint answered. */\n status?: number;\n /** Human-readable failure detail (never contains the key). */\n error?: string;\n /** True when this provider has no probe recipe; saving is still allowed. */\n unsupported?: boolean;\n};\n\ntype ProbeStyle = \"openai\" | \"anthropic\";\n\n/**\n * Built-in base URLs for providers whose keys can be probed directly.\n * OpenAI-compatible providers all expose GET {base}/models with a Bearer\n * header; Anthropic exposes GET /v1/models with x-api-key.\n */\nconst PROVIDER_PROBE_RECIPES: Record<string, { baseUrl: string; style: ProbeStyle }> = {\n anthropic: { baseUrl: \"https://api.anthropic.com/v1\", style: \"anthropic\" },\n openai: { baseUrl: \"https://api.openai.com/v1\", style: \"openai\" },\n groq: { baseUrl: \"https://api.groq.com/openai/v1\", style: \"openai\" },\n openrouter: { baseUrl: \"https://openrouter.ai/api/v1\", style: \"openai\" },\n mistral: { baseUrl: \"https://api.mistral.ai/v1\", style: \"openai\" },\n xai: { baseUrl: \"https://api.x.ai/v1\", style: \"openai\" },\n together: { baseUrl: \"https://api.together.xyz/v1\", style: \"openai\" },\n cerebras: { baseUrl: \"https://api.cerebras.ai/v1\", style: \"openai\" },\n deepseek: { baseUrl: \"https://api.deepseek.com/v1\", style: \"openai\" },\n moonshot: { baseUrl: \"https://api.moonshot.ai/v1\", style: \"openai\" },\n google: {\n baseUrl: \"https://generativelanguage.googleapis.com/v1beta/openai\",\n style: \"openai\",\n },\n};\n\nfunction buildProbeHeaders(style: ProbeStyle, apiKey: string): Record<string, string> {\n if (style === \"anthropic\") {\n return { \"x-api-key\": apiKey, \"anthropic-version\": \"2023-06-01\" };\n }\n return { Authorization: `Bearer ${apiKey}` };\n}\n\nfunction joinModelsEndpoint(baseUrl: string): string {\n return new URL(\"models\", baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`).href;\n}\n\nexport function resolveProbeRecipe(params: {\n provider: string;\n cfg?: BitterbotConfig;\n baseUrl?: string;\n}): { baseUrl: string; style: ProbeStyle } | null {\n const provider = normalizeProviderId(params.provider);\n const configured = params.cfg?.models?.providers?.[provider];\n const explicitBase = params.baseUrl?.trim() || configured?.baseUrl?.trim();\n if (explicitBase) {\n // Style precedence: configured api mode, then the provider's built-in\n // recipe (an anthropic key tested against a custom proxy still needs\n // x-api-key), then openai-compatible as the default.\n const style: ProbeStyle =\n configured?.api === \"anthropic-messages\"\n ? \"anthropic\"\n : configured?.api\n ? \"openai\"\n : (PROVIDER_PROBE_RECIPES[provider]?.style ?? \"openai\");\n return { baseUrl: explicitBase, style };\n }\n return PROVIDER_PROBE_RECIPES[provider] ?? null;\n}\n\nfunction describeProbeFailure(status: number): string {\n if (status === 401 || status === 403) {\n return \"The provider rejected the key (unauthorized).\";\n }\n if (status === 429) {\n return \"The key authenticated but the provider is rate-limiting it.\";\n }\n return `The provider answered with HTTP ${status}.`;\n}\n\nexport async function probeProviderKey(params: {\n provider: string;\n apiKey: string;\n cfg?: BitterbotConfig;\n baseUrl?: string;\n timeoutMs?: number;\n}): Promise<AuthProbeResult> {\n const recipe = resolveProbeRecipe(params);\n if (!recipe) {\n return {\n ok: false,\n unsupported: true,\n error: `No live probe available for provider \"${normalizeProviderId(params.provider)}\". The key can still be saved.`,\n };\n }\n const apiKey = params.apiKey.trim();\n if (!apiKey) {\n return { ok: false, error: \"Empty key.\" };\n }\n try {\n const res = await fetchWithTimeout(\n joinModelsEndpoint(recipe.baseUrl),\n { method: \"GET\", headers: buildProbeHeaders(recipe.style, apiKey) },\n params.timeoutMs ?? PROBE_TIMEOUT_MS,\n );\n // Rate-limited means the credential authenticated; treat as success so a\n // busy account can still save a valid key with an honest status attached.\n if (res.status === 429) {\n return { ok: true, status: res.status, error: describeProbeFailure(res.status) };\n }\n if (res.ok) {\n return { ok: true, status: res.status };\n }\n return { ok: false, status: res.status, error: describeProbeFailure(res.status) };\n } catch (error) {\n const message =\n error instanceof Error && error.name === \"AbortError\"\n ? \"The provider did not answer within the probe timeout.\"\n : error instanceof Error\n ? error.message\n : String(error);\n return { ok: false, error: message };\n }\n}\n"}, "c7fc31d9fc8dcf08afe54fc50ef65d2fbd98f024": {"t": 1785759158.492305, "n": 5146, "h": "723f2d9c35511bf0", "c": "import type { BitterbotConfig } from \"../config/config.js\";\nimport { fetchWithTimeout } from \"../utils/fetch-timeout.js\";\nimport { normalizeProviderId } from \"./model-selection.js\";\n\nconst PROBE_TIMEOUT_MS = 10_000;\n\n/**\n * Live credential probes shared by the onboarding wizard and the\n * models.auth.* gateway RPCs. A probe hits the provider's cheapest\n * authenticated endpoint (the model listing) so keys can be verified\n * before they are saved, without consuming tokens.\n *\n * The probe deliberately never logs or returns the key it was given.\n */\nexport type AuthProbeResult = {\n ok: boolean;\n /** HTTP status when the endpoint answered. */\n status?: number;\n /** Human-readable failure detail (never contains the key). */\n error?: string;\n /** True when this provider has no probe recipe; saving is still allowed. */\n unsupported?: boolean;\n};\n\ntype ProbeStyle = \"openai\" | \"anthropic\";\n\n/**\n * Built-in base URLs for providers whose keys can be probed directly.\n * OpenAI-compatible providers all expose GET {base}/models with a Bearer\n * header; Anthropic exposes GET /v1/models with x-api-key.\n */\nconst PROVIDER_PROBE_RECIPES: Record<string, { baseUrl: string; style: ProbeStyle }> = {\n anthropic: { baseUrl: \"https://api.anthropic.com/v1\", style: \"anthropic\" },\n openai: { baseUrl: \"https://api.openai.com/v1\", style: \"openai\" },\n groq: { baseUrl: \"https://api.groq.com/openai/v1\", style: \"openai\" },\n openrouter: { baseUrl: \"https://openrouter.ai/api/v1\", style: \"openai\" },\n mistral: { baseUrl: \"https://api.mistral.ai/v1\", style: \"openai\" },\n xai: { baseUrl: \"https://api.x.ai/v1\", style: \"openai\" },\n together: { baseUrl: \"https://api.together.xyz/v1\", style: \"openai\" },\n cerebras: { baseUrl: \"https://api.cerebras.ai/v1\", style: \"openai\" },\n deepseek: { baseUrl: \"https://api.deepseek.com/v1\", style: \"openai\" },\n moonshot: { baseUrl: \"https://api.moonshot.ai/v1\", style: \"openai\" },\n google: {\n baseUrl: \"https://generativelanguage.googleapis.com/v1beta/openai\",\n style: \"openai\",\n },\n};\n\nfunction buildProbeHeaders(style: ProbeStyle, apiKey: string): Record<string, string> {\n if (style === \"anthropic\") {\n return { \"x-api-key\": apiKey, \"anthropic-version\": \"2023-06-01\" };\n }\n return { Authorization: `Bearer ${apiKey}` };\n}\n\nfunction joinModelsEndpoint(baseUrl: string): string {\n return new URL(\"models\", baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`).href;\n}\n\nexport function resolveProbeRecipe(params: {\n provider: string;\n cfg?: BitterbotConfig;\n baseUrl?: string;\n}): { baseUrl: string; style: ProbeStyle } | null {\n const provider = normalizeProviderId(params.provider);\n const configured = params.cfg?.models?.providers?.[provider];\n const explicitBase = params.baseUrl?.trim() || configured?.baseUrl?.trim();\n if (explicitBase) {\n // Style precedence: configured api mode, then the provider's built-in\n // recipe (an anthropic key tested against a custom proxy still needs\n // x-api-key), then openai-compatible as the default.\n const style: ProbeStyle =\n configured?.api === \"anthropic-messages\"\n ? \"anthropic\"\n : configured?.api\n ? \"openai\"\n : (PROVIDER_PROBE_RECIPES[provider]?.style ?? \"openai\");\n return { baseUrl: explicitBase, style };\n }\n return PROVIDER_PROBE_RECIPES[provider] ?? null;\n}\n\nfunction describeProbeFailure(status: number): string {\n if (status === 401 || status === 403) {\n return \"The provider rejected the key (unauthorized).\";\n }\n if (status === 429) {\n return \"The key authenticated but the provider is rate-limiting it.\";\n }\n return `The provider answered with HTTP ${status}.`;\n}\n\nexport async function probeProviderKey(params: {\n provider: string;\n apiKey: string;\n cfg?: BitterbotConfig;\n baseUrl?: string;\n timeoutMs?: number;\n}): Promise<AuthProbeResult> {\n const recipe = resolveProbeRecipe(params);\n if (!recipe) {\n return {\n ok: false,\n unsupported: true,\n error: `No live probe available for provider \"${normalizeProviderId(params.provider)}\". The key can still be saved.`,\n };\n }\n const apiKey = params.apiKey.trim();\n if (!apiKey) {\n return { ok: false, error: \"Empty key.\" };\n }\n try {\n const res = await fetchWithTimeout(\n joinModelsEndpoint(recipe.baseUrl),\n { method: \"GET\", headers: buildProbeHeaders(recipe.style, apiKey) },\n params.timeoutMs ?? PROBE_TIMEOUT_MS,\n );\n // Rate-limited means the credential authenticated; treat as success so a\n // busy account can still save a valid key with an honest status attached.\n if (res.status === 429) {\n return { ok: true, status: res.status, error: describeProbeFailure(res.status) };\n }\n if (res.ok) {\n return { ok: true, status: res.status };\n }\n return { ok: false, status: res.status, error: describeProbeFailure(res.status) };\n } catch (error) {\n const message =\n error instanceof Error && error.name === \"AbortError\"\n ? \"The provider did not answer within the probe timeout.\"\n : error instanceof Error\n ? error.message\n : String(error);\n return { ok: false, error: message };\n }\n}\n"}, "dd57ae8fe47803af810015019ef8d3699b07d7fa": {"t": 1785759159.9824827, "n": 4823, "h": "46074acdf231010d", "c": "import type { BitterbotConfig } from \"../config/config.js\";\nimport { fetchWithTimeout } from \"../utils/fetch-timeout.js\";\nimport { normalizeProviderId } from \"./model-selection.js\";\n\nconst PROBE_TIMEOUT_MS = 10_000;\n\n/**\n * Live credential probes shared by the onboarding wizard and the\n * models.auth.* gateway RPCs. A probe hits the provider's cheapest\n * authenticated endpoint (the model listing) so keys can be verified\n * before they are saved, without consuming tokens.\n *\n * The probe deliberately never logs or returns the key it was given.\n */\nexport type AuthProbeResult = {\n ok: boolean;\n /** HTTP status when the endpoint answered. */\n status?: number;\n /** Human-readable failure detail (never contains the key). */\n error?: string;\n /** True when this provider has no probe recipe; saving is still allowed. */\n unsupported?: boolean;\n};\n\ntype ProbeStyle = \"openai\" | \"anthropic\";\n\n/**\n * Built-in base URLs for providers whose keys can be probed directly.\n * OpenAI-compatible providers all expose GET {base}/models with a Bearer\n * header; Anthropic exposes GET /v1/models with x-api-key.\n */\nconst PROVIDER_PROBE_RECIPES: Record<string, { baseUrl: string; style: ProbeStyle }> = {\n anthropic: { baseUrl: \"https://api.anthropic.com/v1\", style: \"anthropic\" },\n openai: { baseUrl: \"https://api.openai.com/v1\", style: \"openai\" },\n groq: { baseUrl: \"https://api.groq.com/openai/v1\", style: \"openai\" },\n openrouter: { baseUrl: \"https://openrouter.ai/api/v1\", style: \"openai\" },\n mistral: { baseUrl: \"https://api.mistral.ai/v1\", style: \"openai\" },\n xai: { baseUrl: \"https://api.x.ai/v1\", style: \"openai\" },\n together: { baseUrl: \"https://api.together.xyz/v1\", style: \"openai\" },\n cerebras: { baseUrl: \"https://api.cerebras.ai/v1\", style: \"openai\" },\n deepseek: { baseUrl: \"https://api.deepseek.com/v1\", style: \"openai\" },\n moonshot: { baseUrl: \"https://api.moonshot.ai/v1\", style: \"openai\" },\n google: {\n baseUrl: \"https://generativelanguage.googleapis.com/v1beta/openai\",\n style: \"openai\",\n },\n};\n\nfunction buildProbeHeaders(style: ProbeStyle, apiKey: string): Record<string, string> {\n if (style === \"anthropic\") {\n return { \"x-api-key\": apiKey, \"anthropic-version\": \"2023-06-01\" };\n }\n return { Authorization: `Bearer ${apiKey}` };\n}\n\nfunction joinModelsEndpoint(baseUrl: string): string {\n return new URL(\"models\", baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`).href;\n}\n\nexport function resolveProbeRecipe(params: {\n provider: string;\n cfg?: BitterbotConfig;\n baseUrl?: string;\n}): { baseUrl: string; style: ProbeStyle } | null {\n const provider = normalizeProviderId(params.provider);\n const configured = params.cfg?.models?.providers?.[provider];\n const explicitBase = params.baseUrl?.trim() || configured?.baseUrl?.trim();\n if (explicitBase) {\n const style: ProbeStyle = configured?.api === \"anthropic-messages\" ? \"anthropic\" : \"openai\";\n return { baseUrl: explicitBase, style };\n }\n return PROVIDER_PROBE_RECIPES[provider] ?? null;\n}\n\nfunction describeProbeFailure(status: number): string {\n if (status === 401 || status === 403) {\n return \"The provider rejected the key (unauthorized).\";\n }\n if (status === 429) {\n return \"The key authenticated but the provider is rate-limiting it.\";\n }\n return `The provider answered with HTTP ${status}.`;\n}\n\nexport async function probeProviderKey(params: {\n provider: string;\n apiKey: string;\n cfg?: BitterbotConfig;\n baseUrl?: string;\n timeoutMs?: number;\n}): Promise<AuthProbeResult> {\n const recipe = resolveProbeRecipe(params);\n if (!recipe) {\n return {\n ok: false,\n unsupported: true,\n error: `No live probe available for provider \"${normalizeProviderId(params.provider)}\". The key can still be saved.`,\n };\n }\n const apiKey = params.apiKey.trim();\n if (!apiKey) {\n return { ok: false, error: \"Empty key.\" };\n }\n try {\n const res = await fetchWithTimeout(\n joinModelsEndpoint(recipe.baseUrl),\n { method: \"GET\", headers: buildProbeHeaders(recipe.style, apiKey) },\n params.timeoutMs ?? PROBE_TIMEOUT_MS,\n );\n // Rate-limited means the credential authenticated; treat as success so a\n // busy account can still save a valid key with an honest status attached.\n if (res.status === 429) {\n return { ok: true, status: res.status, error: describeProbeFailure(res.status) };\n }\n if (res.ok) {\n return { ok: true, status: res.status };\n }\n return { ok: false, status: res.status, error: describeProbeFailure(res.status) };\n } catch (error) {\n const message =\n error instanceof Error && error.name === \"AbortError\"\n ? \"The provider did not answer within the probe timeout.\"\n : error instanceof Error\n ? error.message\n : String(error);\n return { ok: false, error: message };\n }\n}\n"}}, "docs/providers/minimax.md": {"a6cea7896521667b7079217e521c3cdaad3decba": {"t": 1785846813.201012, "n": 6736, "h": "3bd72ea4acfe5fe5", "c": "---\nsummary: \"Use MiniMax M2.1 in Bitterbot\"\nread_when:\n - You want MiniMax models in Bitterbot\n - You need MiniMax setup guidance\ntitle: \"MiniMax\"\n---\n\n# MiniMax\n\nMiniMax is an AI company that builds the **M2/M2.1** model family. The current\ncoding-focused release is **MiniMax M2.1** (December 23, 2025), built for\nreal-world complex tasks.\n\nSource: [MiniMax M2.1 release note](https://www.minimax.io/news/minimax-m21)\n\n## Model overview (M2.1)\n\nMiniMax highlights these improvements in M2.1:\n\n- Stronger **multi-language coding** (Rust, Java, Go, C++, Kotlin, Objective-C, TS/JS).\n- Better **web/app development** and aesthetic output quality (including native mobile).\n- Improved **composite instruction** handling for office-style workflows, building on\n interleaved thinking and integrated constraint execution.\n- **More concise responses** with lower token usage and faster iteration loops.\n- Stronger **tool/agent framework** compatibility and context management (Claude Code,\n Droid/Factory AI, Cline, Kilo Code, Roo Code, BlackBox).\n- Higher-quality **dialogue and technical writing** outputs.\n\n## MiniMax M2.1 vs MiniMax M2.1 Lightning\n\n- **Speed:** Lightning is the “fast” variant in MiniMax’s pricing docs.\n- **Cost:** Pricing shows the same input cost, but Lightning has higher output cost.\n- **Coding plan routing:** The Lightning back-end isn’t directly available on the MiniMax\n coding plan. MiniMax auto-routes most requests to Lightning, but falls back to the\n regular M2.1 back-end during traffic spikes.\n\n## Choose a setup\n\n### MiniMax OAuth (Coding Plan) — recommended\n\n**Best for:** quick setup with MiniMax Coding Plan via OAuth, no API key required.\n\nEnable the bundled OAuth plugin and authenticate:\n\n```bash\nbitterbot plugins enable minimax-portal-auth # skip if already loaded.\nbitterbot gateway restart # restart if gateway is already running\nbitterbot onboard --auth-choice minimax-portal\n```\n\nYou will be prompted to select an endpoint:\n\n- **Global** - International users (`api.minimax.io`)\n- **CN** - Users in China (`api.minimaxi.com`)\n\nSee [MiniMax OAuth plugin README](https://github.com/bitterbot/bitterbot/tree/main/extensions/minimax-portal-auth) for details.\n\n### MiniMax M2.1 (API key)\n\n**Best for:** hosted MiniMax with Anthropic-compatible API.\n\nConfigure via CLI:\n\n- Run `bitterbot configure`\n- Select **Model/auth**\n- Choose **MiniMax M2.1**\n\n```json5\n{\n env: { MINIMAX_API_KEY: \"sk-...\" },\n agents: { defaults: { model: { primary: \"minimax/MiniMax-M2.1\" } } },\n models: {\n mode: \"merge\",\n providers: {\n minimax: {\n baseUrl: \"https://api.minimax.io/anthropic\",\n apiKey: \"${MINIMAX_API_KEY}\",\n api: \"anthropic-messages\",\n models: [\n {\n id: \"MiniMax-M2.1\",\n name: \"MiniMax M2.1\",\n reasoning: false,\n input: [\"text\"],\n cost: { input: 15, output: 60, cacheRead: 2, cacheWrite: 10 },\n contextWindow: 200000,\n maxTokens: 8192,\n },\n ],\n },\n },\n },\n}\n```\n\n### MiniMax M2.1 as fallback (Opus primary)\n\n**Best for:** keep Opus 4.6 as primary, fail over to MiniMax M2.1.\n\n```json5\n{\n env: { MINIMAX_API_KEY: \"sk-...\" },\n agents: {\n defaults: {\n models: {\n \"anthropic/claude-opus-4-6\": { alias: \"opus\" },\n \"minimax/MiniMax-M2.1\": { alias: \"minimax\" },\n },\n model: {\n primary: \"anthropic/claude-opus-4-6\",\n fallbacks: [\"minimax/MiniMax-M2.1\"],\n },\n },\n },\n}\n```\n\n### Optional: Local via LM Studio (manual)\n\n**Best for:** local inference with LM Studio.\nWe have seen strong results with MiniMax M2.1 on powerful hardware (e.g. a\ndesktop/server) using LM Studio's local server.\n\nConfigure manually via `bitterbot.json`:\n\n```json5\n{\n agents: {\n defaults: {\n model: { primary: \"lmstudio/minimax-m2.1-gs32\" },\n models: { \"lmstudio/minimax-m2.1-gs32\": { alias: \"Minimax\" } },\n },\n },\n models: {\n mode: \"merge\",\n providers: {\n lmstudio: {\n baseUrl: \"http://127.0.0.1:1234/v1\",\n apiKey: \"lmstudio\",\n api: \"openai-responses\",\n models: [\n {\n id: \"minimax-m2.1-gs32\",\n name: \"MiniMax M2.1 GS32\",\n reasoning: false,\n input: [\"text\"],\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n contextWindow: 196608,\n maxTokens: 8192,\n },\n ],\n },\n },\n },\n}\n```\n\n## Configure via `bitterbot configure`\n\nUse the interactive config wizard to set MiniMax without editing JSON:\n\n1. Run `bitterbot configure`.\n2. Select **Model/auth**.\n3. Choose **MiniMax M2.1**.\n4. Pick your default model when prompted.\n\n## Configuration options\n\n- `models.providers.minimax.baseUrl`: prefer `https://api.minimax.io/anthropic` (Anthropic-compatible); `https://api.minimax.io/v1` is optional for OpenAI-compatible payloads.\n- `models.providers.minimax.api`: prefer `anthropic-messages`; `openai-completions` is optional for OpenAI-compatible payloads.\n- `models.providers.minimax.apiKey`: MiniMax API key (`MINIMAX_API_KEY`).\n- `models.providers.minimax.models`: define `id`, `name`, `reasoning`, `contextWindow`, `maxTokens`, `cost`.\n- `agents.defaults.models`: alias models you want in the allowlist.\n- `models.mode`: keep `merge` if you want to add MiniMax alongside built-ins.\n\n## Notes\n\n- Model refs are `minimax/<model>`.\n- Coding Plan usage API: `https://api.minimaxi.com/v1/api/openplatform/coding_plan/remains` (requires a coding plan key).\n- Update pricing values in `models.json` if you need exact cost tracking.\n- Referral link for MiniMax Coding Plan (10% off): [https://platform.minimax.io/subscribe/coding-plan?code=DbXJTRClnb&source=link](https://platform.minimax.io/subscribe/coding-plan?code=DbXJTRClnb&source=link)\n- See [/concepts/model-providers](/concepts/model-providers) for provider rules.\n- Use `bitterbot models list` and `bitterbot models set minimax/MiniMax-M2.1` to switch.\n\n## Troubleshooting\n\n### “Unknown model: minimax/MiniMax-M2.1”\n\nThis usually means the **MiniMax provider isn’t configured** (no provider entry\nand no MiniMax auth profile/env key found). A fix for this detection is in\n**2026.1.12** (unreleased at the time of writing). Fix by:\n\n- Upgrading to **2026.1.12** (or run from source `main`), then restarting the gateway.\n- Running `bitterbot configure` and selecting **MiniMax M2.1**, or\n- Adding the `models.providers.minimax` block manually, or\n- Setting `MINIMAX_API_KEY` (or a MiniMax auth profile) so the provider can be injected.\n\nMake sure the model id is **case‑sensitive**:\n\n- `minimax/MiniMax-M2.1`\n- `minimax/MiniMax-M2.1-lightning`\n\nThen recheck with:\n\n```bash\nbitterbot models list\n```\n"}}}}