From f83d90b09092d00a447bbc49064cef435f22cc8f Mon Sep 17 00:00:00 2001 From: abhigyanpatwari Date: Mon, 12 Jan 2026 21:19:53 +0530 Subject: [PATCH] fixed settings icon --- src/components/Header.tsx | 27 +++---- src/components/RightPanel.tsx | 13 +--- src/components/SettingsPanel.tsx | 120 +++++++++++++++++++++++++------ src/core/llm/agent.ts | 17 ++++- src/core/llm/settings-service.ts | 2 + 5 files changed, 135 insertions(+), 44 deletions(-) diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 12ab765c3..1fe99cacd 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -22,7 +22,7 @@ interface HeaderProps { } export const Header = ({ onFocusNode }: HeaderProps) => { - const { projectName, graph, openChatPanel, isRightPanelOpen, rightPanelTab } = useAppState(); + const { projectName, graph, openChatPanel, isRightPanelOpen, rightPanelTab, setSettingsPanelOpen } = useAppState(); const [searchQuery, setSearchQuery] = useState(''); const [isSearchOpen, setIsSearchOpen] = useState(false); const [selectedIndex, setSelectedIndex] = useState(0); @@ -35,7 +35,7 @@ export const Header = ({ onFocusNode }: HeaderProps) => { // Search results - filter nodes by name const searchResults = useMemo(() => { if (!graph || !searchQuery.trim()) return []; - + const query = searchQuery.toLowerCase(); return graph.nodes .filter(node => node.properties.name.toLowerCase().includes(query)) @@ -73,7 +73,7 @@ export const Header = ({ onFocusNode }: HeaderProps) => { // Handle keyboard navigation in results const handleKeyDown = (e: React.KeyboardEvent) => { if (!isSearchOpen || searchResults.length === 0) return; - + if (e.key === 'ArrowDown') { e.preventDefault(); setSelectedIndex(i => Math.min(i + 1, searchResults.length - 1)); @@ -140,7 +140,7 @@ export const Header = ({ onFocusNode }: HeaderProps) => { ⌘K - + {/* Search Results Dropdown */} {isSearchOpen && searchQuery.trim() && (
@@ -154,14 +154,13 @@ export const Header = ({ onFocusNode }: HeaderProps) => {
diff --git a/src/components/SettingsPanel.tsx b/src/components/SettingsPanel.tsx index dc11630bc..25e8e266c 100644 --- a/src/components/SettingsPanel.tsx +++ b/src/components/SettingsPanel.tsx @@ -22,13 +22,13 @@ const fetchGeminiModels = async (apiKey: string): Promise => { const response = await fetch( `https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}` ); - + if (!response.ok) { throw new Error(`API error: ${response.status}`); } - + const data = await response.json(); - + // Filter for chat-capable models and extract model names const models = (data.models || []) .filter((model: any) => { @@ -49,7 +49,7 @@ const fetchGeminiModels = async (apiKey: string): Promise => { }; return score(a) - score(b) || a.localeCompare(b); }); - + return models; } catch (error) { console.warn('Failed to fetch Gemini models:', error); @@ -61,7 +61,7 @@ export const SettingsPanel = ({ isOpen, onClose, onSettingsSaved }: SettingsPane const [settings, setSettings] = useState(loadSettings); const [showApiKey, setShowApiKey] = useState>({}); const [saveStatus, setSaveStatus] = useState<'idle' | 'saved' | 'error'>('idle'); - + // Gemini model fetching state const [geminiModels, setGeminiModels] = useState([]); const [isLoadingModels, setIsLoadingModels] = useState(false); @@ -87,14 +87,14 @@ export const SettingsPanel = ({ isOpen, onClose, onSettingsSaved }: SettingsPane setGeminiModels([]); return; } - + setIsLoadingModels(true); setModelFetchError(null); - + const models = await fetchGeminiModels(apiKey); - + setIsLoadingModels(false); - + if (models.length > 0) { setGeminiModels(models); setModelFetchError(null); @@ -136,14 +136,14 @@ export const SettingsPanel = ({ isOpen, onClose, onSettingsSaved }: SettingsPane if (!isOpen) return null; - const providers: LLMProvider[] = ['gemini', 'anthropic', 'azure-openai']; - - const availableGeminiModels = geminiModels.length > 0 - ? geminiModels + const providers: LLMProvider[] = ['openai', 'gemini', 'anthropic', 'azure-openai']; + + const availableGeminiModels = geminiModels.length > 0 + ? geminiModels : getAvailableModels('gemini'); const currentGeminiModel = settings.gemini?.model ?? 'gemini-2.0-flash'; const isCustomModelSelected = !availableGeminiModels.includes(currentGeminiModel); - + const availableAnthropicModels = getAvailableModels('anthropic'); const currentAnthropicModel = settings.anthropic?.model ?? 'claude-sonnet-4-20250514'; const isCustomAnthropicModelSelected = !availableAnthropicModels.includes(currentAnthropicModel); @@ -201,7 +201,7 @@ export const SettingsPanel = ({ isOpen, onClose, onSettingsSaved }: SettingsPane w-8 h-8 rounded-lg flex items-center justify-center text-lg ${settings.activeProvider === provider ? 'bg-accent/20' : 'bg-surface'} `}> - {provider === 'gemini' ? '💎' : provider === 'anthropic' ? '🧠' : '☁️'} + {provider === 'openai' ? '🤖' : provider === 'gemini' ? '💎' : provider === 'anthropic' ? '🧠' : '☁️'} {getProviderDisplayName(provider)} @@ -209,6 +209,84 @@ export const SettingsPanel = ({ isOpen, onClose, onSettingsSaved }: SettingsPane + {/* OpenAI Settings */} + {settings.activeProvider === 'openai' && ( +
+
+ +
+ setSettings(prev => ({ + ...prev, + openai: { ...prev.openai!, apiKey: e.target.value } + }))} + placeholder="Enter your OpenAI 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{' '} + + OpenAI Platform + +

+
+ +
+ + +
+ +
+ + setSettings(prev => ({ + ...prev, + openai: { ...prev.openai!, baseUrl: e.target.value } + }))} + placeholder="https://api.openai.com/v1 (default)" + 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" + /> +

+ Leave empty to use the default OpenAI API. Set a custom URL for proxies or compatible APIs. +

+
+
+ )} + {/* Gemini Settings */} {settings.activeProvider === 'gemini' && (
@@ -276,7 +354,7 @@ export const SettingsPanel = ({ isOpen, onClose, onSettingsSaved }: SettingsPane )}
- + {/* Model selector or manual input */} {(useCustomModel || isCustomModelSelected) ? (
@@ -329,7 +407,7 @@ export const SettingsPanel = ({ isOpen, onClose, onSettingsSaved }: SettingsPane
)} - + {modelFetchError && !useCustomModel && (

@@ -339,7 +417,7 @@ export const SettingsPanel = ({ isOpen, onClose, onSettingsSaved }: SettingsPane )} - + {/* Anthropic Settings */} {settings.activeProvider === 'anthropic' && (

@@ -379,10 +457,10 @@ export const SettingsPanel = ({ isOpen, onClose, onSettingsSaved }: SettingsPane

- +
- + {(useCustomAnthropicModel || isCustomAnthropicModelSelected) ? (
- Privacy: Your API keys are stored only in your browser's local storage. + Privacy: Your API keys are stored only in your browser's local storage. They're sent directly to the LLM provider when you chat. Your code never leaves your machine.
diff --git a/src/core/llm/agent.ts b/src/core/llm/agent.ts index f54a15c43..dcb43278a 100644 --- a/src/core/llm/agent.ts +++ b/src/core/llm/agent.ts @@ -7,13 +7,14 @@ import { createReactAgent } from '@langchain/langgraph/prebuilt'; import { SystemMessage } from '@langchain/core/messages'; -import { AzureChatOpenAI } from '@langchain/openai'; +import { ChatOpenAI, AzureChatOpenAI } from '@langchain/openai'; import { ChatGoogleGenerativeAI } from '@langchain/google-genai'; import { ChatAnthropic } from '@langchain/anthropic'; import type { BaseChatModel } from '@langchain/core/language_models/chat_models'; import { createGraphRAGTools } from './tools'; import type { ProviderConfig, + OpenAIConfig, AzureOpenAIConfig, GeminiConfig, AnthropicConfig, @@ -109,6 +110,20 @@ GOOD: A["User Data"] --> B["Process and Save"] `; export const createChatModel = (config: ProviderConfig): BaseChatModel => { switch (config.provider) { + case 'openai': { + const openaiConfig = config as OpenAIConfig; + return new ChatOpenAI({ + openAIApiKey: openaiConfig.apiKey, + modelName: openaiConfig.model, + temperature: openaiConfig.temperature ?? 0.1, + maxTokens: openaiConfig.maxTokens, + configuration: openaiConfig.baseUrl ? { + baseURL: openaiConfig.baseUrl, + } : undefined, + streaming: true, + }); + } + case 'azure-openai': { const azureConfig = config as AzureOpenAIConfig; return new AzureChatOpenAI({ diff --git a/src/core/llm/settings-service.ts b/src/core/llm/settings-service.ts index a0218aae6..333463b95 100644 --- a/src/core/llm/settings-service.ts +++ b/src/core/llm/settings-service.ts @@ -261,6 +261,8 @@ export const getProviderDisplayName = (provider: LLMProvider): string => { */ export const getAvailableModels = (provider: LLMProvider): string[] => { switch (provider) { + case 'openai': + return ['gpt-4.5-preview', 'gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo']; case 'azure-openai': // Azure models depend on deployment, so we show common ones return ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'gpt-4', 'gpt-35-turbo'];