diff --git a/gitnexus-web/src/components/SettingsPanel.tsx b/gitnexus-web/src/components/SettingsPanel.tsx index c93dd7df9..916eeffdd 100644 --- a/gitnexus-web/src/components/SettingsPanel.tsx +++ b/gitnexus-web/src/components/SettingsPanel.tsx @@ -4,6 +4,7 @@ import { loadSettings, saveSettings, getProviderDisplayName, + getAvailableModels, fetchOpenRouterModels, } from '../core/llm/settings-service'; import type { LLMSettings, LLMProvider } from '../core/llm/types'; @@ -296,7 +297,7 @@ export const SettingsPanel = ({ isOpen, onClose, onSettingsSaved, backendUrl, is if (!isOpen) return null; - const providers: LLMProvider[] = ['openai', 'gemini', 'anthropic', 'azure-openai', 'ollama', 'openrouter', 'minimax']; + const providers: LLMProvider[] = ['openai', 'gemini', 'anthropic', 'azure-openai', 'ollama', 'openrouter', 'minimax', 'glm']; return ( @@ -381,7 +382,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 === 'minimax' ? '⚡' : '☁️'} + {provider === 'openai' ? '🤖' : provider === 'gemini' ? '💎' : provider === 'anthropic' ? '🧠' : provider === 'ollama' ? '🦙' : provider === 'openrouter' ? '🌐' : provider === 'minimax' ? '⚡' : provider === 'glm' ? '🔮' : '☁️'} {getProviderDisplayName(provider)} @@ -756,6 +757,81 @@ export const SettingsPanel = ({ isOpen, onClose, onSettingsSaved, backendUrl, is /> )} + {/* GLM Settings */} + {settings.activeProvider === 'glm' && ( +
+
+ +
+ setSettings(prev => ({ + ...prev, + glm: { ...prev.glm!, apiKey: e.target.value } + }))} + placeholder="Enter your Z.AI 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{' '} + + Z.AI Platform + +

+
+ +
+ + +
+ +
+ + setSettings(prev => ({ + ...prev, + glm: { ...prev.glm!, baseUrl: e.target.value } + }))} + placeholder="https://api.z.ai/api/coding/paas/v4" + 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" + /> +

+ Coding API (default). Use https://api.z.ai/api/paas/v4 for the general API. +

+
+
+ )} + {/* Privacy Note */}
diff --git a/gitnexus-web/src/core/llm/agent.ts b/gitnexus-web/src/core/llm/agent.ts index 3fcd40bdf..e31c5e92d 100644 --- a/gitnexus-web/src/core/llm/agent.ts +++ b/gitnexus-web/src/core/llm/agent.ts @@ -22,6 +22,7 @@ import type { OllamaConfig, OpenRouterConfig, MiniMaxConfig, + GLMConfig, AgentStreamChunk, } from './types'; import { @@ -246,6 +247,26 @@ export const createChatModel = (config: ProviderConfig): BaseChatModel => { }); } + case 'glm': { + const glmConfig = config as GLMConfig; + + if (!glmConfig.apiKey || glmConfig.apiKey.trim() === '') { + throw new Error('GLM API key is required but was not provided'); + } + + return new ChatOpenAI({ + apiKey: glmConfig.apiKey, + modelName: glmConfig.model, + temperature: glmConfig.temperature ?? 0.1, + maxTokens: glmConfig.maxTokens, + configuration: { + apiKey: glmConfig.apiKey, + baseURL: glmConfig.baseUrl ?? 'https://api.z.ai/api/coding/paas/v4', + }, + streaming: true, + }); + } + 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 7fc9f5765..e07f957a7 100644 --- a/gitnexus-web/src/core/llm/settings-service.ts +++ b/gitnexus-web/src/core/llm/settings-service.ts @@ -16,6 +16,7 @@ import { OllamaConfig, OpenRouterConfig, MiniMaxConfig, + GLMConfig, ProviderConfig, } from './types'; import { DEFAULT_OPENROUTER_BASE_URL, DEFAULT_OLLAMA_BASE_URL } from '../../config/ui-constants'; @@ -53,6 +54,10 @@ const mergeWithDefaults = (parsed?: Partial | null): LLMSettings => ...DEFAULT_LLM_SETTINGS.minimax, ...parsed?.minimax, }, + glm: { + ...DEFAULT_LLM_SETTINGS.glm, + ...parsed?.glm, + }, }); const readSettings = (storage: Storage): Partial | null => { @@ -127,7 +132,9 @@ export const updateProviderSettings = ( T extends 'gemini' ? Partial> : T extends 'anthropic' ? Partial> : T extends 'ollama' ? Partial> : + T extends 'openrouter' ? Partial> : T extends 'minimax' ? Partial> : + T extends 'glm' ? Partial> : never > ): LLMSettings => { @@ -212,6 +219,17 @@ export const updateProviderSettings = ( saveSettings(updated); return updated; } + case 'glm': { + const updated: LLMSettings = { + ...current, + glm: { + ...(current.glm ?? {}), + ...(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 }; @@ -278,6 +296,17 @@ const providerBuilders: Record = { if (!settings.minimax?.apiKey) return null; return { provider: 'minimax', ...settings.minimax } as MiniMaxConfig; }, + glm: (settings) => { + if (!settings.glm?.apiKey) return null; + return { + provider: 'glm', + apiKey: settings.glm.apiKey, + model: settings.glm.model || 'GLM-5', + baseUrl: settings.glm.baseUrl || 'https://api.z.ai/api/coding/paas/v4', + temperature: settings.glm.temperature, + maxTokens: settings.glm.maxTokens, + } as GLMConfig; + }, }; export const getActiveProviderConfig = (): ProviderConfig | null => { @@ -328,6 +357,8 @@ export const getProviderDisplayName = (provider: LLMProvider): string => { return 'OpenRouter'; case 'minimax': return 'MiniMax'; + case 'glm': + return 'GLM (Z.AI)'; default: return provider; } @@ -351,6 +382,8 @@ export const getAvailableModels = (provider: LLMProvider): string[] => { return ['llama3.2', 'llama3.1', 'mistral', 'codellama', 'deepseek-coder']; case 'minimax': return ['MiniMax-M2.5', 'MiniMax-M2.5-highspeed']; + case 'glm': + return ['GLM-5', 'GLM-5-Turbo', 'GLM-4.7', 'GLM-4.5']; default: return []; } diff --git a/gitnexus-web/src/core/llm/types.ts b/gitnexus-web/src/core/llm/types.ts index 4c611294f..d75f71f2a 100644 --- a/gitnexus-web/src/core/llm/types.ts +++ b/gitnexus-web/src/core/llm/types.ts @@ -2,15 +2,14 @@ * LLM Provider Types * * Type definitions for multi-provider LLM support. - * Supports Azure OpenAI and Google Gemini (with extensibility for others). + * Supports OpenAI, Azure OpenAI, Gemini, Anthropic, Ollama, OpenRouter, MiniMax, and GLM5. */ /** * Supported LLM providers */ import { DEFAULT_OLLAMA_BASE_URL, DEFAULT_OPENROUTER_BASE_URL } from '../../config/ui-constants'; - -export type LLMProvider = 'openai' | 'azure-openai' | 'gemini' | 'anthropic' | 'ollama' | 'openrouter' | 'minimax'; +export type LLMProvider = 'openai' | 'azure-openai' | 'gemini' | 'anthropic' | 'ollama' | 'openrouter' | 'minimax' | 'glm'; /** * Base configuration shared by all providers @@ -89,10 +88,20 @@ export interface MiniMaxConfig extends BaseProviderConfig { model: string; // e.g., 'MiniMax-M2.5', 'MiniMax-M2.5-highspeed' } +/** + * GLM (Z.AI) configuration — OpenAI-compatible API + */ +export interface GLMConfig extends BaseProviderConfig { + provider: 'glm'; + apiKey: string; + model: string; // e.g., 'GLM-4.7', 'GLM-4.5', 'GLM-4.5-Air', 'GLM-5' + baseUrl?: string; // defaults to https://api.z.ai/api/coding/paas/v4 +} + /** * Union type for all provider configurations */ -export type ProviderConfig = OpenAIConfig | AzureOpenAIConfig | GeminiConfig | AnthropicConfig | OllamaConfig | OpenRouterConfig | MiniMaxConfig; +export type ProviderConfig = OpenAIConfig | AzureOpenAIConfig | GeminiConfig | AnthropicConfig | OllamaConfig | OpenRouterConfig | MiniMaxConfig | GLMConfig; /** * Stored settings (what goes to localStorage) @@ -110,6 +119,7 @@ export interface LLMSettings { ollama?: Partial>; openrouter?: Partial>; minimax?: Partial>; + glm?: Partial>; // Intelligent Clustering Settings intelligentClustering: boolean; @@ -165,6 +175,12 @@ export const DEFAULT_LLM_SETTINGS: LLMSettings = { model: 'MiniMax-M2.5', temperature: 0.1, }, + glm: { + apiKey: '', + model: 'GLM-5', + baseUrl: 'https://api.z.ai/api/coding/paas/v4', + temperature: 0.1, + }, }; /**