mirror of
https://github.com/browseros-ai/BrowserOS.git
synced 2026-05-13 15:46:22 +00:00
feat(llm): Minimax LLM provider for Chinese and International Users (#756)
* feat(llm): Minimax Chinese and International Users providers * fix(llm): Patch for p2 bugs * fix(agent): correct MiniMax base URL handling and enforce API key validation * fix(agent): add minimax entry to PROVIDER_DISPLAY_NAMES The Record<ProviderType, string> map in ChatError.tsx was missing the new minimax key added in this PR, causing a typecheck failure. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: krish-mm <112251957+krish-mm@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -64,6 +64,7 @@ import {
|
||||
import {
|
||||
getDefaultBaseUrlForProviders,
|
||||
getProviderTemplate,
|
||||
MINIMAX_REGIONS,
|
||||
providerTypeOptions,
|
||||
} from '@/lib/llm-providers/providerTemplates'
|
||||
import { type TestResult, testProvider } from '@/lib/llm-providers/testProvider'
|
||||
@@ -87,6 +88,7 @@ const providerTypeEnum = z.enum([
|
||||
'chatgpt-pro',
|
||||
'github-copilot',
|
||||
'qwen-code',
|
||||
'minimax',
|
||||
])
|
||||
|
||||
/**
|
||||
@@ -105,7 +107,7 @@ export const providerFormSchema = z
|
||||
temperature: z.number().min(0).max(2),
|
||||
// Azure-specific
|
||||
resourceName: z.string().optional(),
|
||||
// Bedrock-specific
|
||||
// Bedrock-specific / MiniMax region
|
||||
accessKeyId: z.string().optional(),
|
||||
secretAccessKey: z.string().optional(),
|
||||
region: z.string().optional(),
|
||||
@@ -164,6 +166,30 @@ export const providerFormSchema = z
|
||||
) {
|
||||
// No validation needed — OAuth tokens are on the server
|
||||
}
|
||||
// MiniMax: require baseUrl + apiKey
|
||||
else if (data.type === 'minimax') {
|
||||
if (!data.baseUrl) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Base URL is required',
|
||||
path: ['baseUrl'],
|
||||
})
|
||||
} else if (!/^https?:\/\/.+/.test(data.baseUrl)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Must be a valid URL',
|
||||
path: ['baseUrl'],
|
||||
})
|
||||
}
|
||||
|
||||
if (!data.apiKey?.trim()) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'API Key is required',
|
||||
path: ['apiKey'],
|
||||
})
|
||||
}
|
||||
}
|
||||
// Other providers: require baseUrl
|
||||
else if (!data.baseUrl) {
|
||||
ctx.addIssue({
|
||||
@@ -316,6 +342,9 @@ export const NewProviderDialog: FC<NewProviderDialogProps> = ({
|
||||
if (defaultUrl) {
|
||||
form.setValue('baseUrl', defaultUrl)
|
||||
}
|
||||
if (newType === 'minimax') {
|
||||
form.setValue('region', 'chinese')
|
||||
}
|
||||
form.setValue('modelId', '')
|
||||
}
|
||||
|
||||
@@ -722,6 +751,94 @@ export const NewProviderDialog: FC<NewProviderDialogProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
// Minimax: region selector
|
||||
if (watchedType === 'minimax') {
|
||||
return (
|
||||
<>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="region"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Region *</FormLabel>
|
||||
<Select
|
||||
onValueChange={(v) => {
|
||||
field.onChange(v)
|
||||
form.setValue(
|
||||
'baseUrl',
|
||||
MINIMAX_REGIONS[v as keyof typeof MINIMAX_REGIONS].api,
|
||||
)
|
||||
}}
|
||||
value={field.value || 'chinese'}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="chinese">
|
||||
Chinese (api.minimaxi.com)
|
||||
</SelectItem>
|
||||
<SelectItem value="international">
|
||||
International (api.minimax.io)
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormDescription>
|
||||
Choose the endpoint closest to your location
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="baseUrl"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Base URL *</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="https://api.minimaxi.com/v1" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="apiKey"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>API Key *</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="Enter your MiniMax API key"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
Your API key is encrypted and stored locally.{' '}
|
||||
{setupGuideUrl && (
|
||||
<a
|
||||
href={setupGuideUrl}
|
||||
onClick={handleSetupGuideClick}
|
||||
className="inline-flex cursor-pointer items-center gap-1 text-primary hover:underline"
|
||||
>
|
||||
<ExternalLink className="h-3 w-3" />
|
||||
{setupGuideText}
|
||||
</a>
|
||||
)}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
// Standard providers (OpenAI, Anthropic, Google, etc.)
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -31,6 +31,7 @@ const PROVIDER_DISPLAY_NAMES: Record<ProviderType, string> = {
|
||||
'chatgpt-pro': 'ChatGPT Pro',
|
||||
'github-copilot': 'GitHub Copilot',
|
||||
'qwen-code': 'Qwen Code',
|
||||
minimax: 'MiniMax',
|
||||
}
|
||||
|
||||
const UPSTREAM_RATE_LIMIT_PATTERNS: Array<string | RegExp> = [
|
||||
|
||||
@@ -5402,5 +5402,89 @@
|
||||
"outputCost": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"minimax": {
|
||||
"name": "MiniMax",
|
||||
"api": "https://api.minimaxi.com/v1",
|
||||
"doc": "https://platform.minimax.io",
|
||||
"models": [
|
||||
{
|
||||
"id": "MiniMax-M2.7",
|
||||
"name": "MiniMax M2.7",
|
||||
"contextWindow": 204800,
|
||||
"maxOutput": 8192,
|
||||
"supportsImages": false,
|
||||
"supportsReasoning": true,
|
||||
"supportsToolCall": true,
|
||||
"inputCost": 0.3,
|
||||
"outputCost": 1.2
|
||||
},
|
||||
{
|
||||
"id": "MiniMax-M2.7-highspeed",
|
||||
"name": "MiniMax M2.7 Highspeed",
|
||||
"contextWindow": 204800,
|
||||
"maxOutput": 8192,
|
||||
"supportsImages": false,
|
||||
"supportsReasoning": true,
|
||||
"supportsToolCall": true,
|
||||
"inputCost": 0.6,
|
||||
"outputCost": 2.4
|
||||
},
|
||||
{
|
||||
"id": "MiniMax-M2.5",
|
||||
"name": "MiniMax M2.5",
|
||||
"contextWindow": 204800,
|
||||
"maxOutput": 8192,
|
||||
"supportsImages": false,
|
||||
"supportsReasoning": true,
|
||||
"supportsToolCall": true,
|
||||
"inputCost": 0.3,
|
||||
"outputCost": 1.2
|
||||
},
|
||||
{
|
||||
"id": "MiniMax-M2.5-highspeed",
|
||||
"name": "MiniMax M2.5 Highspeed",
|
||||
"contextWindow": 204800,
|
||||
"maxOutput": 8192,
|
||||
"supportsImages": false,
|
||||
"supportsReasoning": true,
|
||||
"supportsToolCall": true,
|
||||
"inputCost": 0.6,
|
||||
"outputCost": 2.4
|
||||
},
|
||||
{
|
||||
"id": "MiniMax-M2.1",
|
||||
"name": "MiniMax M2.1",
|
||||
"contextWindow": 204800,
|
||||
"maxOutput": 8192,
|
||||
"supportsImages": false,
|
||||
"supportsReasoning": true,
|
||||
"supportsToolCall": true,
|
||||
"inputCost": 0.3,
|
||||
"outputCost": 1.2
|
||||
},
|
||||
{
|
||||
"id": "MiniMax-M2.1-highspeed",
|
||||
"name": "MiniMax M2.1 Highspeed",
|
||||
"contextWindow": 204800,
|
||||
"maxOutput": 8192,
|
||||
"supportsImages": false,
|
||||
"supportsReasoning": true,
|
||||
"supportsToolCall": true,
|
||||
"inputCost": 0.6,
|
||||
"outputCost": 2.4
|
||||
},
|
||||
{
|
||||
"id": "M2-her",
|
||||
"name": "M2-her",
|
||||
"contextWindow": 204800,
|
||||
"maxOutput": 8192,
|
||||
"supportsImages": false,
|
||||
"supportsReasoning": false,
|
||||
"supportsToolCall": true,
|
||||
"inputCost": 0.3,
|
||||
"outputCost": 1.2
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
Gemini,
|
||||
Kimi,
|
||||
LmStudio,
|
||||
Minimax,
|
||||
Ollama,
|
||||
OpenAI,
|
||||
OpenRouter,
|
||||
@@ -36,6 +37,7 @@ const providerIconMap: Record<ProviderType, IconComponent | null> = {
|
||||
'chatgpt-pro': OpenAI,
|
||||
'github-copilot': Github,
|
||||
'qwen-code': Qwen,
|
||||
minimax: Minimax,
|
||||
}
|
||||
|
||||
interface ProviderIconProps {
|
||||
|
||||
@@ -140,8 +140,31 @@ export const providerTemplates: ProviderTemplate[] = [
|
||||
setupGuideUrl:
|
||||
'https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html',
|
||||
}),
|
||||
enrichTemplate('minimax', {
|
||||
defaultModelId: 'MiniMax-M2.7',
|
||||
apiKeyUrl:
|
||||
'https://platform.minimax.io/user-center/basic-information/interface-key',
|
||||
setupGuideUrl: 'https://platform.minimax.io/docs/guides/models-intro',
|
||||
}),
|
||||
]
|
||||
|
||||
export const MINIMAX_REGIONS = {
|
||||
chinese: {
|
||||
api: 'https://api.minimaxi.com/v1',
|
||||
apiKeyUrl:
|
||||
'https://platform.minimaxi.com/user-center/basic-information/interface-key',
|
||||
setupGuideUrl: 'https://platform.minimaxi.com/document',
|
||||
},
|
||||
international: {
|
||||
api: 'https://api.minimax.io/v1',
|
||||
apiKeyUrl:
|
||||
'https://platform.minimax.io/user-center/basic-information/interface-key',
|
||||
setupGuideUrl: 'https://platform.minimax.io/docs/guides/models-intro',
|
||||
},
|
||||
} as const
|
||||
|
||||
export type MinimaxRegion = keyof typeof MINIMAX_REGIONS
|
||||
|
||||
/**
|
||||
* Provider type options for select dropdowns
|
||||
* @public
|
||||
@@ -161,6 +184,7 @@ export const providerTypeOptions: { value: ProviderType; label: string }[] = [
|
||||
{ value: 'lmstudio', label: 'LM Studio' },
|
||||
{ value: 'bedrock', label: 'AWS Bedrock' },
|
||||
{ value: 'browseros', label: 'BrowserOS' },
|
||||
{ value: 'minimax', label: 'MiniMax' },
|
||||
]
|
||||
|
||||
/**
|
||||
@@ -192,6 +216,7 @@ export const DEFAULT_BASE_URLS: Record<ProviderType, string> = {
|
||||
lmstudio: 'http://localhost:1234/v1',
|
||||
bedrock: '',
|
||||
browseros: '',
|
||||
minimax: MINIMAX_REGIONS.chinese.api,
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,7 @@ export type ProviderType =
|
||||
| 'chatgpt-pro'
|
||||
| 'github-copilot'
|
||||
| 'qwen-code'
|
||||
| 'minimax'
|
||||
|
||||
/**
|
||||
* LLM Provider configuration
|
||||
|
||||
@@ -148,6 +148,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<string, ProviderFactory> = {
|
||||
[LLM_PROVIDERS.CHATGPT_PRO]: createChatGPTProModel,
|
||||
[LLM_PROVIDERS.GITHUB_COPILOT]: createGitHubCopilotModel,
|
||||
[LLM_PROVIDERS.QWEN_CODE]: createQwenCodeModel,
|
||||
[LLM_PROVIDERS.MINIMAX]: createMinimaxModel,
|
||||
}
|
||||
|
||||
export function createLLMProvider(config: ResolvedLLMConfig): LanguageModel {
|
||||
|
||||
@@ -27,6 +27,7 @@ export const LLM_PROVIDERS = {
|
||||
CHATGPT_PRO: 'chatgpt-pro',
|
||||
GITHUB_COPILOT: 'github-copilot',
|
||||
QWEN_CODE: 'qwen-code',
|
||||
MINIMAX: 'minimax',
|
||||
} as const
|
||||
|
||||
/**
|
||||
@@ -48,6 +49,7 @@ export const LLMProviderSchema: z.ZodEnum<
|
||||
'chatgpt-pro',
|
||||
'github-copilot',
|
||||
'qwen-code',
|
||||
'minimax',
|
||||
]
|
||||
> = z.enum([
|
||||
LLM_PROVIDERS.ANTHROPIC,
|
||||
@@ -64,6 +66,7 @@ export const LLMProviderSchema: z.ZodEnum<
|
||||
LLM_PROVIDERS.CHATGPT_PRO,
|
||||
LLM_PROVIDERS.GITHUB_COPILOT,
|
||||
LLM_PROVIDERS.QWEN_CODE,
|
||||
LLM_PROVIDERS.MINIMAX,
|
||||
])
|
||||
|
||||
export type LLMProvider = z.infer<typeof LLMProviderSchema>
|
||||
|
||||
Reference in New Issue
Block a user