From 85b7bd1278f691e3e8a3d7736b70080cd9e7cbf0 Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Fri, 3 Jul 2026 17:00:15 +0800 Subject: [PATCH 1/3] feat: add MiniMax M3 provider --- .../lib/llm-providers/models-dev-data.json | 2 +- .../app/lib/llm-providers/providerIcons.tsx | 2 ++ .../llm-providers/providerTemplates.test.ts | 13 ++++++++ .../lib/llm-providers/providerTemplates.ts | 10 ++++++ .../apps/app/lib/llm-providers/types.ts | 1 + .../app/screens/ai-settings/models.test.ts | 15 +++++++++ .../apps/app/screens/ai-settings/models.ts | 9 +++++ .../ai-settings/provider-form-schema.ts | 1 + .../apps/server/src/agent/provider-factory.ts | 13 ++++++++ .../server/src/lib/clients/llm/provider.ts | 11 +++++++ .../tests/lib/clients/llm/provider.test.ts | 33 +++++++++++++++++++ .../packages/shared/src/schemas/llm.ts | 3 ++ 12 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 packages/browseros-agent/apps/server/tests/lib/clients/llm/provider.test.ts diff --git a/packages/browseros-agent/apps/app/lib/llm-providers/models-dev-data.json b/packages/browseros-agent/apps/app/lib/llm-providers/models-dev-data.json index d6070e6e90..9c7dccc728 100644 --- a/packages/browseros-agent/apps/app/lib/llm-providers/models-dev-data.json +++ b/packages/browseros-agent/apps/app/lib/llm-providers/models-dev-data.json @@ -939,7 +939,7 @@ { "id": "minimax/minimax-m3", "name": "MiniMax-M3", - "contextWindow": 524288, + "contextWindow": 1000000, "maxOutput": 512000, "supportsImages": true, "supportsReasoning": true, diff --git a/packages/browseros-agent/apps/app/lib/llm-providers/providerIcons.tsx b/packages/browseros-agent/apps/app/lib/llm-providers/providerIcons.tsx index 4c1feb3eb5..dd39ab9dd6 100644 --- a/packages/browseros-agent/apps/app/lib/llm-providers/providerIcons.tsx +++ b/packages/browseros-agent/apps/app/lib/llm-providers/providerIcons.tsx @@ -5,6 +5,7 @@ import { Gemini, Kimi, LmStudio, + Minimax, Ollama, OpenAI, OpenRouter, @@ -33,6 +34,7 @@ const providerIconMap: Record = { bedrock: Bedrock, browseros: null, moonshot: Kimi, + minimax: Minimax, 'chatgpt-pro': OpenAI, 'github-copilot': Github, 'qwen-code': Qwen, diff --git a/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.test.ts b/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.test.ts index b03ad1df9f..077f6b081e 100644 --- a/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.test.ts +++ b/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.test.ts @@ -13,4 +13,17 @@ describe('providerTemplates', () => { contextWindow: 1050000, }) }) + + it('defaults MiniMax to M3 on the international endpoint with a 1M context', () => { + const template = providerTemplates.find( + (provider) => provider.id === 'minimax', + ) + + expect(template).toMatchObject({ + name: 'MiniMax', + defaultBaseUrl: 'https://api.minimax.io/v1', + defaultModelId: 'MiniMax-M3', + contextWindow: 1000000, + }) + }) }) diff --git a/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.ts b/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.ts index 7d14defafb..135e039572 100644 --- a/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.ts +++ b/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.ts @@ -91,6 +91,14 @@ export const providerTemplates: ProviderTemplate[] = [ apiKeyUrl: 'https://platform.moonshot.ai/console/api-keys', setupGuideUrl: 'https://platform.moonshot.ai/console/api-keys', }, + { + id: 'minimax', + name: 'MiniMax', + defaultBaseUrl: 'https://api.minimax.io/v1', + defaultModelId: 'MiniMax-M3', + supportsImages: true, + contextWindow: 1000000, + }, enrichTemplate('openai', { defaultModelId: 'gpt-5', apiKeyUrl: 'https://platform.openai.com/api-keys', @@ -163,6 +171,7 @@ export const providerTypeOptions: { value: ProviderType; label: string }[] = [ { value: 'codex', label: 'Codex' }, { value: 'claude-code', label: 'Claude Code' }, { value: 'moonshot', label: 'Moonshot AI' }, + { value: 'minimax', label: 'MiniMax' }, { value: 'anthropic', label: 'Anthropic' }, { value: 'openai', label: 'OpenAI' }, { value: 'openai-compatible', label: 'OpenAI Compatible' }, @@ -198,6 +207,7 @@ const DEFAULT_BASE_URLS: Record = { 'claude-code': '', 'acp-custom': '', moonshot: 'https://api.moonshot.ai/v1', + minimax: 'https://api.minimax.io/v1', anthropic: 'https://api.anthropic.com/v1', openai: 'https://api.openai.com/v1', 'openai-compatible': '', diff --git a/packages/browseros-agent/apps/app/lib/llm-providers/types.ts b/packages/browseros-agent/apps/app/lib/llm-providers/types.ts index d764c17ced..ba96728e73 100644 --- a/packages/browseros-agent/apps/app/lib/llm-providers/types.ts +++ b/packages/browseros-agent/apps/app/lib/llm-providers/types.ts @@ -11,6 +11,7 @@ export type ProviderType = | 'bedrock' | 'browseros' | 'moonshot' + | 'minimax' | 'chatgpt-pro' | 'github-copilot' | 'qwen-code' diff --git a/packages/browseros-agent/apps/app/screens/ai-settings/models.test.ts b/packages/browseros-agent/apps/app/screens/ai-settings/models.test.ts index fc239dad7a..e4233b86e0 100644 --- a/packages/browseros-agent/apps/app/screens/ai-settings/models.test.ts +++ b/packages/browseros-agent/apps/app/screens/ai-settings/models.test.ts @@ -30,3 +30,18 @@ describe('ChatGPT models', () => { expect(getModelContextLength('chatgpt-pro', 'gpt-5.1')).toBe(400000) }) }) + +describe('MiniMax models', () => { + it('offers MiniMax-M3 with a 1M context window', () => { + const models = getModelsForProvider('minimax') + + expect(models[0]).toEqual({ + modelId: 'MiniMax-M3', + contextLength: 1000000, + supportsImages: true, + supportsReasoning: true, + supportsToolCall: true, + }) + expect(getModelContextLength('minimax', 'MiniMax-M3')).toBe(1000000) + }) +}) diff --git a/packages/browseros-agent/apps/app/screens/ai-settings/models.ts b/packages/browseros-agent/apps/app/screens/ai-settings/models.ts index ea073a8bc0..dbe3f53efc 100644 --- a/packages/browseros-agent/apps/app/screens/ai-settings/models.ts +++ b/packages/browseros-agent/apps/app/screens/ai-settings/models.ts @@ -36,6 +36,15 @@ const CUSTOM_PROVIDER_MODELS: Partial> = { { modelId: 'qwen3-coder-flash', contextLength: 1000000 }, { modelId: 'qwen3.5-plus', contextLength: 1000000 }, ], + minimax: [ + { + modelId: 'MiniMax-M3', + contextLength: 1000000, + supportsImages: true, + supportsReasoning: true, + supportsToolCall: true, + }, + ], } function fromModelsDevModel(m: ModelsDevModel): ModelInfo { diff --git a/packages/browseros-agent/apps/app/screens/ai-settings/provider-form-schema.ts b/packages/browseros-agent/apps/app/screens/ai-settings/provider-form-schema.ts index 60d0b8adf9..dda3fc4c53 100644 --- a/packages/browseros-agent/apps/app/screens/ai-settings/provider-form-schema.ts +++ b/packages/browseros-agent/apps/app/screens/ai-settings/provider-form-schema.ts @@ -3,6 +3,7 @@ import { isLocalRuntimeProviderType } from '../../lib/llm-providers/provider-run const providerTypeEnum = z.enum([ 'moonshot', + 'minimax', 'anthropic', 'openai', 'openai-compatible', diff --git a/packages/browseros-agent/apps/server/src/agent/provider-factory.ts b/packages/browseros-agent/apps/server/src/agent/provider-factory.ts index e2cd2ffe99..121c5f93cb 100644 --- a/packages/browseros-agent/apps/server/src/agent/provider-factory.ts +++ b/packages/browseros-agent/apps/server/src/agent/provider-factory.ts @@ -413,6 +413,18 @@ function createMoonshotFactory( }) } +function createMinimaxFactory( + config: ResolvedAgentConfig, +): (modelId: string) => unknown { + if (!config.baseUrl) throw new Error('MiniMax provider requires baseUrl') + if (!config.apiKey) throw new Error('MiniMax provider requires apiKey') + return createOpenAICompatible({ + name: 'minimax', + baseURL: config.baseUrl, + apiKey: config.apiKey, + }) +} + function createQwenCodeFactory( config: ResolvedAgentConfig, ): (modelId: string) => unknown { @@ -459,6 +471,7 @@ const PROVIDER_FACTORIES: Record = { [LLM_PROVIDERS.BROWSEROS]: createBrowserOSFactory, [LLM_PROVIDERS.OPENAI_COMPATIBLE]: createOpenAICompatibleFactory, [LLM_PROVIDERS.MOONSHOT]: createMoonshotFactory, + [LLM_PROVIDERS.MINIMAX]: createMinimaxFactory, [LLM_PROVIDERS.CHATGPT_PRO]: createChatGPTProFactory, [LLM_PROVIDERS.GITHUB_COPILOT]: createGitHubCopilotFactory, [LLM_PROVIDERS.QWEN_CODE]: createQwenCodeFactory, diff --git a/packages/browseros-agent/apps/server/src/lib/clients/llm/provider.ts b/packages/browseros-agent/apps/server/src/lib/clients/llm/provider.ts index a3d8a8af3a..320d21be6e 100644 --- a/packages/browseros-agent/apps/server/src/lib/clients/llm/provider.ts +++ b/packages/browseros-agent/apps/server/src/lib/clients/llm/provider.ts @@ -152,6 +152,16 @@ function createMoonshotModel(config: ResolvedLLMConfig): LanguageModel { })(config.model) } +function createMinimaxModel(config: ResolvedLLMConfig): LanguageModel { + if (!config.baseUrl) throw new Error('MiniMax provider requires baseUrl') + if (!config.apiKey) throw new Error('MiniMax provider requires apiKey') + return createOpenAICompatible({ + name: 'minimax', + baseURL: config.baseUrl, + apiKey: config.apiKey, + })(config.model) +} + function createQwenCodeModel(config: ResolvedLLMConfig): LanguageModel { if (!config.apiKey) throw new Error('Qwen Code requires OAuth authentication') return createOpenAICompatible({ @@ -192,6 +202,7 @@ const PROVIDER_FACTORIES: Record = { [LLM_PROVIDERS.BROWSEROS]: createBrowserOSModel, [LLM_PROVIDERS.OPENAI_COMPATIBLE]: createOpenAICompatibleModel, [LLM_PROVIDERS.MOONSHOT]: createMoonshotModel, + [LLM_PROVIDERS.MINIMAX]: createMinimaxModel, [LLM_PROVIDERS.CHATGPT_PRO]: createChatGPTProModel, [LLM_PROVIDERS.GITHUB_COPILOT]: createGitHubCopilotModel, [LLM_PROVIDERS.QWEN_CODE]: createQwenCodeModel, diff --git a/packages/browseros-agent/apps/server/tests/lib/clients/llm/provider.test.ts b/packages/browseros-agent/apps/server/tests/lib/clients/llm/provider.test.ts new file mode 100644 index 0000000000..6505e13408 --- /dev/null +++ b/packages/browseros-agent/apps/server/tests/lib/clients/llm/provider.test.ts @@ -0,0 +1,33 @@ +/** + * @license + * Copyright 2025 BrowserOS + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { describe, expect, it } from 'bun:test' +import { LLM_PROVIDERS } from '@browseros/shared/schemas/llm' +import { createLLMProvider } from '../../../../src/lib/clients/llm/provider' + +describe('createLLMProvider', () => { + it('creates an OpenAI-compatible model for MiniMax', () => { + const model = createLLMProvider({ + provider: LLM_PROVIDERS.MINIMAX, + model: 'MiniMax-M3', + apiKey: 'test-key', + baseUrl: 'https://api.minimax.io/v1', + }) + + expect(model.provider).toBe('minimax.chat') + expect(model.modelId).toBe('MiniMax-M3') + }) + + it('throws when the MiniMax provider is missing an apiKey', () => { + expect(() => + createLLMProvider({ + provider: LLM_PROVIDERS.MINIMAX, + model: 'MiniMax-M3', + baseUrl: 'https://api.minimax.io/v1', + }), + ).toThrow('MiniMax provider requires apiKey') + }) +}) diff --git a/packages/browseros-agent/packages/shared/src/schemas/llm.ts b/packages/browseros-agent/packages/shared/src/schemas/llm.ts index 3de65f6972..a79ecf1bd6 100644 --- a/packages/browseros-agent/packages/shared/src/schemas/llm.ts +++ b/packages/browseros-agent/packages/shared/src/schemas/llm.ts @@ -24,6 +24,7 @@ export const LLM_PROVIDERS = { BROWSEROS: 'browseros', OPENAI_COMPATIBLE: 'openai-compatible', MOONSHOT: 'moonshot', + MINIMAX: 'minimax', CHATGPT_PRO: 'chatgpt-pro', GITHUB_COPILOT: 'github-copilot', QWEN_CODE: 'qwen-code', @@ -49,6 +50,7 @@ export const LLMProviderSchema: z.ZodEnum< 'browseros', 'openai-compatible', 'moonshot', + 'minimax', 'chatgpt-pro', 'github-copilot', 'qwen-code', @@ -69,6 +71,7 @@ export const LLMProviderSchema: z.ZodEnum< LLM_PROVIDERS.BROWSEROS, LLM_PROVIDERS.OPENAI_COMPATIBLE, LLM_PROVIDERS.MOONSHOT, + LLM_PROVIDERS.MINIMAX, LLM_PROVIDERS.CHATGPT_PRO, LLM_PROVIDERS.GITHUB_COPILOT, LLM_PROVIDERS.QWEN_CODE, From 03af591634b475c3c67c38c83842d937b75a70f0 Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Sun, 12 Jul 2026 13:49:49 +0800 Subject: [PATCH 2/3] fix: complete MiniMax provider metadata --- .../lib/llm-providers/models-dev-data.json | 2 +- .../llm-providers/providerTemplates.test.ts | 2 ++ .../lib/llm-providers/providerTemplates.ts | 2 ++ .../app/screens/ai-settings/models.test.ts | 25 +++++++++++++------ .../apps/app/screens/ai-settings/models.ts | 6 +++++ 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/packages/browseros-agent/apps/app/lib/llm-providers/models-dev-data.json b/packages/browseros-agent/apps/app/lib/llm-providers/models-dev-data.json index 9c7dccc728..d6070e6e90 100644 --- a/packages/browseros-agent/apps/app/lib/llm-providers/models-dev-data.json +++ b/packages/browseros-agent/apps/app/lib/llm-providers/models-dev-data.json @@ -939,7 +939,7 @@ { "id": "minimax/minimax-m3", "name": "MiniMax-M3", - "contextWindow": 1000000, + "contextWindow": 524288, "maxOutput": 512000, "supportsImages": true, "supportsReasoning": true, diff --git a/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.test.ts b/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.test.ts index 077f6b081e..a46b44efd5 100644 --- a/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.test.ts +++ b/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.test.ts @@ -24,6 +24,8 @@ describe('providerTemplates', () => { defaultBaseUrl: 'https://api.minimax.io/v1', defaultModelId: 'MiniMax-M3', contextWindow: 1000000, + setupGuideUrl: + 'https://platform.minimax.io/docs/api-reference/text-openai-api', }) }) }) diff --git a/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.ts b/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.ts index 135e039572..7fee329918 100644 --- a/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.ts +++ b/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.ts @@ -98,6 +98,8 @@ export const providerTemplates: ProviderTemplate[] = [ defaultModelId: 'MiniMax-M3', supportsImages: true, contextWindow: 1000000, + setupGuideUrl: + 'https://platform.minimax.io/docs/api-reference/text-openai-api', }, enrichTemplate('openai', { defaultModelId: 'gpt-5', diff --git a/packages/browseros-agent/apps/app/screens/ai-settings/models.test.ts b/packages/browseros-agent/apps/app/screens/ai-settings/models.test.ts index e4233b86e0..84fba4f64e 100644 --- a/packages/browseros-agent/apps/app/screens/ai-settings/models.test.ts +++ b/packages/browseros-agent/apps/app/screens/ai-settings/models.test.ts @@ -32,16 +32,25 @@ describe('ChatGPT models', () => { }) describe('MiniMax models', () => { - it('offers MiniMax-M3 with a 1M context window', () => { + it('offers current models with their supported capabilities', () => { const models = getModelsForProvider('minimax') - expect(models[0]).toEqual({ - modelId: 'MiniMax-M3', - contextLength: 1000000, - supportsImages: true, - supportsReasoning: true, - supportsToolCall: true, - }) + expect(models).toEqual([ + { + modelId: 'MiniMax-M3', + contextLength: 1000000, + supportsImages: true, + supportsReasoning: true, + supportsToolCall: true, + }, + { + modelId: 'MiniMax-M2.7', + contextLength: 204800, + supportsReasoning: true, + supportsToolCall: true, + }, + ]) expect(getModelContextLength('minimax', 'MiniMax-M3')).toBe(1000000) + expect(getModelContextLength('minimax', 'MiniMax-M2.7')).toBe(204800) }) }) diff --git a/packages/browseros-agent/apps/app/screens/ai-settings/models.ts b/packages/browseros-agent/apps/app/screens/ai-settings/models.ts index dbe3f53efc..0b6c9a539e 100644 --- a/packages/browseros-agent/apps/app/screens/ai-settings/models.ts +++ b/packages/browseros-agent/apps/app/screens/ai-settings/models.ts @@ -44,6 +44,12 @@ const CUSTOM_PROVIDER_MODELS: Partial> = { supportsReasoning: true, supportsToolCall: true, }, + { + modelId: 'MiniMax-M2.7', + contextLength: 204800, + supportsReasoning: true, + supportsToolCall: true, + }, ], } From 8fecffa519bc275e0c15d259cc01a2a0c79a8e41 Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Mon, 13 Jul 2026 13:27:34 +0800 Subject: [PATCH 3/3] fix: support MiniMax Anthropic endpoints --- .../llm-providers/providerTemplates.test.ts | 2 +- .../lib/llm-providers/providerTemplates.ts | 2 +- .../apps/server/src/agent/provider-factory.ts | 9 +- .../src/lib/clients/llm/minimax-provider.ts | 36 +++++++ .../server/src/lib/clients/llm/provider.ts | 9 +- .../tests/lib/clients/llm/provider.test.ts | 97 ++++++++++++++++++- 6 files changed, 138 insertions(+), 17 deletions(-) create mode 100644 packages/browseros-agent/apps/server/src/lib/clients/llm/minimax-provider.ts diff --git a/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.test.ts b/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.test.ts index a46b44efd5..629cc1230e 100644 --- a/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.test.ts +++ b/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.test.ts @@ -25,7 +25,7 @@ describe('providerTemplates', () => { defaultModelId: 'MiniMax-M3', contextWindow: 1000000, setupGuideUrl: - 'https://platform.minimax.io/docs/api-reference/text-openai-api', + 'https://platform.minimax.io/docs/api-reference/api-overview', }) }) }) diff --git a/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.ts b/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.ts index 7fee329918..2e8aa0f625 100644 --- a/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.ts +++ b/packages/browseros-agent/apps/app/lib/llm-providers/providerTemplates.ts @@ -99,7 +99,7 @@ export const providerTemplates: ProviderTemplate[] = [ supportsImages: true, contextWindow: 1000000, setupGuideUrl: - 'https://platform.minimax.io/docs/api-reference/text-openai-api', + 'https://platform.minimax.io/docs/api-reference/api-overview', }, enrichTemplate('openai', { defaultModelId: 'gpt-5', diff --git a/packages/browseros-agent/apps/server/src/agent/provider-factory.ts b/packages/browseros-agent/apps/server/src/agent/provider-factory.ts index 121c5f93cb..f4bce4f3a2 100644 --- a/packages/browseros-agent/apps/server/src/agent/provider-factory.ts +++ b/packages/browseros-agent/apps/server/src/agent/provider-factory.ts @@ -20,6 +20,7 @@ import { import { resolveAcpSpawnCommand } from '../lib/agents/host-acp/launcher' import { getBrowserosDir } from '../lib/browseros-dir' import { createBrowserOSFetch } from '../lib/browseros-fetch' +import { createMinimaxProvider } from '../lib/clients/llm/minimax-provider' import { createMockBrowserOSLanguageModel, shouldUseMockBrowserOSLLM, @@ -416,13 +417,7 @@ function createMoonshotFactory( function createMinimaxFactory( config: ResolvedAgentConfig, ): (modelId: string) => unknown { - if (!config.baseUrl) throw new Error('MiniMax provider requires baseUrl') - if (!config.apiKey) throw new Error('MiniMax provider requires apiKey') - return createOpenAICompatible({ - name: 'minimax', - baseURL: config.baseUrl, - apiKey: config.apiKey, - }) + return createMinimaxProvider(config) } function createQwenCodeFactory( diff --git a/packages/browseros-agent/apps/server/src/lib/clients/llm/minimax-provider.ts b/packages/browseros-agent/apps/server/src/lib/clients/llm/minimax-provider.ts new file mode 100644 index 0000000000..5052382fdf --- /dev/null +++ b/packages/browseros-agent/apps/server/src/lib/clients/llm/minimax-provider.ts @@ -0,0 +1,36 @@ +/** + * @license + * Copyright 2025 BrowserOS + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { createAnthropic } from '@ai-sdk/anthropic' +import { createOpenAICompatible } from '@ai-sdk/openai-compatible' +import type { LanguageModel } from 'ai' + +interface MinimaxProviderConfig { + apiKey?: string + baseUrl?: string +} + +export function createMinimaxProvider( + config: MinimaxProviderConfig, +): (modelId: string) => LanguageModel { + if (!config.baseUrl) throw new Error('MiniMax provider requires baseUrl') + if (!config.apiKey) throw new Error('MiniMax provider requires apiKey') + + const baseUrl = config.baseUrl.replace(/\/+$/, '') + if (baseUrl.endsWith('/anthropic')) { + return createAnthropic({ + name: 'minimax', + baseURL: `${baseUrl}/v1`, + apiKey: config.apiKey, + }) + } + + return createOpenAICompatible({ + name: 'minimax', + baseURL: baseUrl, + apiKey: config.apiKey, + }) +} diff --git a/packages/browseros-agent/apps/server/src/lib/clients/llm/provider.ts b/packages/browseros-agent/apps/server/src/lib/clients/llm/provider.ts index 320d21be6e..e3a61828a8 100644 --- a/packages/browseros-agent/apps/server/src/lib/clients/llm/provider.ts +++ b/packages/browseros-agent/apps/server/src/lib/clients/llm/provider.ts @@ -21,6 +21,7 @@ import { logger } from '../../logger' import { createOpenRouterCompatibleFetch } from '../../openrouter-fetch' import { createCodexFetch } from '../oauth/codex-fetch' import { createCopilotFetch } from '../oauth/copilot-fetch' +import { createMinimaxProvider } from './minimax-provider' import { createMockBrowserOSLanguageModel, shouldUseMockBrowserOSLLM, @@ -153,13 +154,7 @@ function createMoonshotModel(config: ResolvedLLMConfig): LanguageModel { } function createMinimaxModel(config: ResolvedLLMConfig): LanguageModel { - if (!config.baseUrl) throw new Error('MiniMax provider requires baseUrl') - if (!config.apiKey) throw new Error('MiniMax provider requires apiKey') - return createOpenAICompatible({ - name: 'minimax', - baseURL: config.baseUrl, - apiKey: config.apiKey, - })(config.model) + return createMinimaxProvider(config)(config.model) } function createQwenCodeModel(config: ResolvedLLMConfig): LanguageModel { diff --git a/packages/browseros-agent/apps/server/tests/lib/clients/llm/provider.test.ts b/packages/browseros-agent/apps/server/tests/lib/clients/llm/provider.test.ts index 6505e13408..f064044d37 100644 --- a/packages/browseros-agent/apps/server/tests/lib/clients/llm/provider.test.ts +++ b/packages/browseros-agent/apps/server/tests/lib/clients/llm/provider.test.ts @@ -4,10 +4,84 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { describe, expect, it } from 'bun:test' +import { afterEach, describe, expect, it } from 'bun:test' import { LLM_PROVIDERS } from '@browseros/shared/schemas/llm' +import { generateText } from 'ai' import { createLLMProvider } from '../../../../src/lib/clients/llm/provider' +const originalFetch = globalThis.fetch + +afterEach(() => { + globalThis.fetch = originalFetch +}) + +function requestUrl(input: RequestInfo | URL): string { + if (typeof input === 'string') return input + if (input instanceof URL) return input.href + return input.url +} + +function responseBody(protocol: 'anthropic' | 'openai', model: string) { + if (protocol === 'anthropic') { + return { + id: 'msg_test', + type: 'message', + role: 'assistant', + content: [{ type: 'text', text: 'Hello' }], + model, + stop_reason: 'end_turn', + stop_sequence: null, + usage: { input_tokens: 1, output_tokens: 1 }, + } + } + + return { + id: 'chatcmpl_test', + object: 'chat.completion', + created: 0, + model, + choices: [ + { + index: 0, + message: { role: 'assistant', content: 'Hello' }, + finish_reason: 'stop', + }, + ], + usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 }, + } +} + +const endpointCases = [ + { + name: 'global OpenAI', + baseUrl: 'https://api.minimax.io/v1', + requestUrl: 'https://api.minimax.io/v1/chat/completions', + protocol: 'openai', + model: 'MiniMax-M3', + }, + { + name: 'China OpenAI', + baseUrl: 'https://api.minimaxi.com/v1', + requestUrl: 'https://api.minimaxi.com/v1/chat/completions', + protocol: 'openai', + model: 'MiniMax-M2.7', + }, + { + name: 'global Anthropic', + baseUrl: 'https://api.minimax.io/anthropic', + requestUrl: 'https://api.minimax.io/anthropic/v1/messages', + protocol: 'anthropic', + model: 'MiniMax-M3', + }, + { + name: 'China Anthropic', + baseUrl: 'https://api.minimaxi.com/anthropic', + requestUrl: 'https://api.minimaxi.com/anthropic/v1/messages', + protocol: 'anthropic', + model: 'MiniMax-M2.7', + }, +] as const + describe('createLLMProvider', () => { it('creates an OpenAI-compatible model for MiniMax', () => { const model = createLLMProvider({ @@ -30,4 +104,25 @@ describe('createLLMProvider', () => { }), ).toThrow('MiniMax provider requires apiKey') }) + + for (const endpoint of endpointCases) { + it(`sends requests to the ${endpoint.name} endpoint`, async () => { + let capturedUrl = '' + globalThis.fetch = (async (input: RequestInfo | URL) => { + capturedUrl = requestUrl(input) + return Response.json(responseBody(endpoint.protocol, endpoint.model)) + }) as typeof globalThis.fetch + + const model = createLLMProvider({ + provider: LLM_PROVIDERS.MINIMAX, + model: endpoint.model, + apiKey: 'test-key', + baseUrl: endpoint.baseUrl, + }) + const result = await generateText({ model, prompt: 'Hello' }) + + expect(result.text).toBe('Hello') + expect(capturedUrl).toBe(endpoint.requestUrl) + }) + } })