diff --git a/public/config.json b/public/config.json index acdc552..4bd2d00 100644 --- a/public/config.json +++ b/public/config.json @@ -15,6 +15,11 @@ "api_key": "", "model": "claude-sonnet-4-0" }, + "claude_openrouter": { + "api_key": "", + "base_url": "https://openrouter.ai/api/v1/chat/completions", + "model": "anthropic/claude-sonnet-4" + }, "openai_compatible": { "api_key": "", "base_url": "", diff --git a/src/components/settings/sections/GeneralSettings.tsx b/src/components/settings/sections/GeneralSettings.tsx index 4ba2a24..d0504ca 100644 --- a/src/components/settings/sections/GeneralSettings.tsx +++ b/src/components/settings/sections/GeneralSettings.tsx @@ -9,6 +9,9 @@ const GeneralSettings: React.FC = () => { const [geminiModel, setGeminiModel] = useState(''); const [claudeKey, setClaudeKey] = useState(''); const [claudeModel, setClaudeModel] = useState(''); + const [claudeOpenRouterKey, setClaudeOpenRouterKey] = useState(''); + const [claudeOpenRouterBaseUrl, setClaudeOpenRouterBaseUrl] = useState(''); + const [claudeOpenRouterModel, setClaudeOpenRouterModel] = useState(''); const [openaiFlex, setOpenaiFlex] = useState(false); const [compatibleKey, setCompatibleKey] = useState(''); const [compatibleBaseUrl, setCompatibleBaseUrl] = useState(''); @@ -46,6 +49,9 @@ const GeneralSettings: React.FC = () => { setGeminiModel((configManager.get('general.gemini.model') as string) || ''); setClaudeKey((configManager.get('general.claude.api_key') as string) || ''); setClaudeModel((configManager.get('general.claude.model') as string) || ''); + setClaudeOpenRouterKey((configManager.get('general.claude_openrouter.api_key') as string) || ''); + setClaudeOpenRouterBaseUrl((configManager.get('general.claude_openrouter.base_url') as string) || ''); + setClaudeOpenRouterModel((configManager.get('general.claude_openrouter.model') as string) || ''); setCompatibleKey((configManager.get('general.openai_compatible.api_key') as string) || ''); setCompatibleBaseUrl((configManager.get('general.openai_compatible.base_url') as string) || ''); setCompatibleModel((configManager.get('general.openai_compatible.model') as string) || ''); @@ -121,6 +127,21 @@ const GeneralSettings: React.FC = () => { debouncedSave('general.claude.model', value); }; + const handleClaudeOpenRouterKeyChange = (value: string) => { + setClaudeOpenRouterKey(value); + debouncedSave('general.claude_openrouter.api_key', value); + }; + + const handleClaudeOpenRouterModelChange = (value: string) => { + setClaudeOpenRouterModel(value); + debouncedSave('general.claude_openrouter.model', value); + }; + + const handleClaudeOpenRouterBaseUrlChange = (value: string) => { + setClaudeOpenRouterBaseUrl(value); + debouncedSave('general.claude_openrouter.base_url', value); + }; + const handleCompatibleKeyChange = (value: string) => { setCompatibleKey(value); debouncedSave('general.openai_compatible.api_key', value); @@ -164,6 +185,7 @@ const GeneralSettings: React.FC = () => { {/* */} + @@ -284,6 +306,58 @@ const GeneralSettings: React.FC = () => { */} +
+

Anthropic Claude (via OpenRouter)

+ +
+ + handleClaudeOpenRouterKeyChange(e.target.value)} + /> +
+ {isLocalEnvironment + ? 'Keys are persisted locally (the IndexedDB in your browser).' + : 'For security, keys are not persisted on non-local hosts and are kept in-memory for this session.'} +
+
+ +
+ + handleClaudeOpenRouterBaseUrlChange(e.target.value)} + /> +
+ This is the base URL for the OpenRouter API. Please do not change this unless you know what you are doing. +
+
+ +
+ + +
+
+

OpenAI Compatible Server

@@ -312,7 +386,7 @@ const GeneralSettings: React.FC = () => { handleCompatibleBaseUrlChange(e.target.value)} /> diff --git a/src/core/config/ConfigManager.ts b/src/core/config/ConfigManager.ts index cafde76..562211d 100644 --- a/src/core/config/ConfigManager.ts +++ b/src/core/config/ConfigManager.ts @@ -7,7 +7,7 @@ import { DB_CONSTANTS } from '../../constants/coreConstants'; interface AppConfig { general: { language: string; - llm_provider: 'openai' | 'gemini' | 'claude' | 'openai_compatible'; + llm_provider: 'openai' | 'gemini' | 'claude' | 'claude_openrouter' | 'openai_compatible'; openai: { api_key: string; flex: boolean; @@ -21,6 +21,11 @@ interface AppConfig { api_key: string; model: string; }; + claude_openrouter: { + api_key: string; + base_url: string; + model: string; + }; openai_compatible: { api_key: string; base_url: string; @@ -169,6 +174,11 @@ export class ConfigManager { api_key: '', model: 'claude-sonnet-4-0' }, + claude_openrouter: { + api_key: '', + base_url: 'https://openrouter.ai/api/v1/chat/completions', + model: 'anthropic/claude-sonnet-4' + }, openai_compatible: { api_key: '', base_url: '',