From 66c1ffa3701ccaa0743c5450094aebda419fe796 Mon Sep 17 00:00:00 2001 From: ximi Date: Fri, 20 Mar 2026 22:18:31 +0800 Subject: [PATCH] feat: add MiniMax provider support (#224) Add MiniMax as a new LLM provider using the Anthropic-compatible API. Changes: - Add MiniMax to LLMProvider type and MiniMaxConfig interface - Add MiniMax chat model creation via ChatAnthropic with custom base URL - Add MiniMax settings persistence and model list (MiniMax-M2.5, MiniMax-M2.5-highspeed) - Add MiniMax provider UI in SettingsPanel with API key and model selection --- gitnexus-web/src/components/SettingsPanel.tsx | 61 ++++++++++++++++++- gitnexus-web/src/core/llm/agent.ts | 34 ++++++++--- gitnexus-web/src/core/llm/settings-service.ts | 38 ++++++++++-- gitnexus-web/src/core/llm/types.ts | 19 +++++- 4 files changed, 137 insertions(+), 15 deletions(-) diff --git a/gitnexus-web/src/components/SettingsPanel.tsx b/gitnexus-web/src/components/SettingsPanel.tsx index ab823d240..d60475107 100644 --- a/gitnexus-web/src/components/SettingsPanel.tsx +++ b/gitnexus-web/src/components/SettingsPanel.tsx @@ -281,7 +281,7 @@ export const SettingsPanel = ({ isOpen, onClose, onSettingsSaved, backendUrl, is if (!isOpen) return null; - const providers: LLMProvider[] = ['openai', 'gemini', 'anthropic', 'azure-openai', 'ollama', 'openrouter']; + const providers: LLMProvider[] = ['openai', 'gemini', 'anthropic', 'azure-openai', 'ollama', 'openrouter', 'minimax']; return ( @@ -366,7 +366,7 @@ export const SettingsPanel = ({ isOpen, onClose, onSettingsSaved, backendUrl, is w-8 h-8 rounded-lg flex items-center justify-center text-lg ${settings.activeProvider === provider ? 'bg-accent/20' : 'bg-surface'} `}> - {provider === 'openai' ? '🤖' : provider === 'gemini' ? '💎' : provider === 'anthropic' ? '🧠' : provider === 'ollama' ? '🦙' : provider === 'openrouter' ? '🌐' : '☁️'} + {provider === 'openai' ? '🤖' : provider === 'gemini' ? '💎' : provider === 'anthropic' ? '🧠' : provider === 'ollama' ? '🦙' : provider === 'openrouter' ? '🌐' : provider === 'minimax' ? '⚡' : '☁️'} {getProviderDisplayName(provider)} @@ -814,7 +814,64 @@ export const SettingsPanel = ({ isOpen, onClose, onSettingsSaved, backendUrl, is )} + {/* MiniMax Settings */} + {settings.activeProvider === 'minimax' && ( +
+
+ +
+ setSettings(prev => ({ + ...prev, + minimax: { ...prev.minimax!, apiKey: e.target.value } + }))} + placeholder="Enter your MiniMax API key" + className="w-full px-4 py-3 pr-12 bg-elevated border border-border-subtle rounded-xl text-text-primary placeholder:text-text-muted focus:border-accent focus:ring-2 focus:ring-accent/20 outline-none transition-all" + /> + +
+

+ Get your API key from{' '} + + MiniMax Platform + +

+
+
+ + setSettings(prev => ({ + ...prev, + minimax: { ...prev.minimax!, model: e.target.value } + }))} + placeholder="e.g., MiniMax-M2.5, MiniMax-M2.5-highspeed" + className="w-full px-4 py-3 bg-elevated border border-border-subtle rounded-xl text-text-primary placeholder:text-text-muted focus:border-accent focus:ring-2 focus:ring-accent/20 outline-none transition-all font-mono text-sm" + /> +

+ Available models: MiniMax-M2.5 (default), MiniMax-M2.5-highspeed (faster) +

+
+
+ )} {/* Privacy Note */}
diff --git a/gitnexus-web/src/core/llm/agent.ts b/gitnexus-web/src/core/llm/agent.ts index 6649f5742..88e07d40f 100644 --- a/gitnexus-web/src/core/llm/agent.ts +++ b/gitnexus-web/src/core/llm/agent.ts @@ -13,14 +13,15 @@ import { ChatAnthropic } from '@langchain/anthropic'; import { ChatOllama } from '@langchain/ollama'; import type { BaseChatModel } from '@langchain/core/language_models/chat_models'; import { createGraphRAGTools } from './tools'; -import type { - ProviderConfig, +import type { + ProviderConfig, OpenAIConfig, - AzureOpenAIConfig, + AzureOpenAIConfig, GeminiConfig, AnthropicConfig, OllamaConfig, OpenRouterConfig, + MiniMaxConfig, AgentStreamChunk, } from './types'; import { @@ -197,7 +198,7 @@ export const createChatModel = (config: ProviderConfig): BaseChatModel => { case 'openrouter': { const openRouterConfig = config as OpenRouterConfig; - + // Debug logging if (import.meta.env.DEV) { console.log('🌐 OpenRouter config:', { @@ -207,11 +208,11 @@ export const createChatModel = (config: ProviderConfig): BaseChatModel => { baseUrl: openRouterConfig.baseUrl, }); } - + if (!openRouterConfig.apiKey || openRouterConfig.apiKey.trim() === '') { throw new Error('OpenRouter API key is required but was not provided'); } - + return new ChatOpenAI({ openAIApiKey: openRouterConfig.apiKey, apiKey: openRouterConfig.apiKey, // Fallback for some versions @@ -225,7 +226,26 @@ export const createChatModel = (config: ProviderConfig): BaseChatModel => { streaming: true, }); } - + + case 'minimax': { + const minimaxConfig = config as MiniMaxConfig; + + if (!minimaxConfig.apiKey || minimaxConfig.apiKey.trim() === '') { + throw new Error('MiniMax API key is required but was not provided'); + } + + return new ChatAnthropic({ + anthropicApiKey: minimaxConfig.apiKey, + model: minimaxConfig.model, + temperature: minimaxConfig.temperature ?? 0.1, + maxTokens: minimaxConfig.maxTokens ?? 8192, + streaming: true, + clientOptions: { + baseURL: 'https://api.minimax.io/anthropic', + }, + }); + } + default: throw new Error(`Unsupported provider: ${(config as any).provider}`); } diff --git a/gitnexus-web/src/core/llm/settings-service.ts b/gitnexus-web/src/core/llm/settings-service.ts index 0c35b9623..f4ef5993a 100644 --- a/gitnexus-web/src/core/llm/settings-service.ts +++ b/gitnexus-web/src/core/llm/settings-service.ts @@ -5,9 +5,9 @@ * All API keys are stored locally - never sent to any server except the LLM provider. */ -import { - LLMSettings, - DEFAULT_LLM_SETTINGS, +import { + LLMSettings, + DEFAULT_LLM_SETTINGS, LLMProvider, OpenAIConfig, AzureOpenAIConfig, @@ -15,6 +15,7 @@ import { AnthropicConfig, OllamaConfig, OpenRouterConfig, + MiniMaxConfig, ProviderConfig, } from './types'; @@ -60,6 +61,10 @@ export const loadSettings = (): LLMSettings => { ...DEFAULT_LLM_SETTINGS.openrouter, ...parsed.openrouter, }, + minimax: { + ...DEFAULT_LLM_SETTINGS.minimax, + ...parsed.minimax, + }, }; } catch (error) { console.warn('Failed to load LLM settings:', error); @@ -89,6 +94,7 @@ export const updateProviderSettings = ( T extends 'gemini' ? Partial> : T extends 'anthropic' ? Partial> : T extends 'ollama' ? Partial> : + T extends 'minimax' ? Partial> : never > ): LLMSettings => { @@ -162,6 +168,17 @@ export const updateProviderSettings = ( saveSettings(updated); return updated; } + case 'minimax': { + const updated: LLMSettings = { + ...current, + minimax: { + ...(current.minimax ?? {}), + ...(updates as Partial>), + }, + }; + saveSettings(updated); + return updated; + } default: { // Should be unreachable due to T extends LLMProvider, but keep a safe fallback const updated: LLMSettings = { ...current }; @@ -245,7 +262,16 @@ export const getActiveProviderConfig = (): ProviderConfig | null => { temperature: settings.openrouter.temperature, maxTokens: settings.openrouter.maxTokens, } as OpenRouterConfig; - + + case 'minimax': + if (!settings.minimax?.apiKey) { + return null; + } + return { + provider: 'minimax', + ...settings.minimax, + } as MiniMaxConfig; + default: return null; } @@ -282,6 +308,8 @@ export const getProviderDisplayName = (provider: LLMProvider): string => { return 'Ollama (Local)'; case 'openrouter': return 'OpenRouter'; + case 'minimax': + return 'MiniMax'; default: return provider; } @@ -303,6 +331,8 @@ export const getAvailableModels = (provider: LLMProvider): string[] => { return ['claude-sonnet-4-20250514', 'claude-3-5-sonnet-20241022', 'claude-3-5-haiku-20241022', 'claude-3-opus-20240229']; case 'ollama': return ['llama3.2', 'llama3.1', 'mistral', 'codellama', 'deepseek-coder']; + case 'minimax': + return ['MiniMax-M2.5', 'MiniMax-M2.5-highspeed']; default: return []; } diff --git a/gitnexus-web/src/core/llm/types.ts b/gitnexus-web/src/core/llm/types.ts index 8e43673fa..d2597ec18 100644 --- a/gitnexus-web/src/core/llm/types.ts +++ b/gitnexus-web/src/core/llm/types.ts @@ -8,7 +8,7 @@ /** * Supported LLM providers */ -export type LLMProvider = 'openai' | 'azure-openai' | 'gemini' | 'anthropic' | 'ollama' | 'openrouter'; +export type LLMProvider = 'openai' | 'azure-openai' | 'gemini' | 'anthropic' | 'ollama' | 'openrouter' | 'minimax'; /** * Base configuration shared by all providers @@ -78,10 +78,19 @@ export interface OpenRouterConfig extends BaseProviderConfig { baseUrl?: string; // defaults to https://openrouter.ai/api/v1 } +/** + * MiniMax configuration (Anthropic-compatible API) + */ +export interface MiniMaxConfig extends BaseProviderConfig { + provider: 'minimax'; + apiKey: string; + model: string; // e.g., 'MiniMax-M2.5', 'MiniMax-M2.5-highspeed' +} + /** * Union type for all provider configurations */ -export type ProviderConfig = OpenAIConfig | AzureOpenAIConfig | GeminiConfig | AnthropicConfig | OllamaConfig | OpenRouterConfig; +export type ProviderConfig = OpenAIConfig | AzureOpenAIConfig | GeminiConfig | AnthropicConfig | OllamaConfig | OpenRouterConfig | MiniMaxConfig; /** * Stored settings (what goes to localStorage) @@ -98,6 +107,7 @@ export interface LLMSettings { anthropic?: Partial>; ollama?: Partial>; openrouter?: Partial>; + minimax?: Partial>; // Intelligent Clustering Settings intelligentClustering: boolean; @@ -148,6 +158,11 @@ export const DEFAULT_LLM_SETTINGS: LLMSettings = { baseUrl: 'https://openrouter.ai/api/v1', temperature: 0.1, }, + minimax: { + apiKey: '', + model: 'MiniMax-M2.5', + temperature: 0.1, + }, }; /**