import React, { useEffect, useMemo, useState } from "react"; import { testProvider } from "../utils/api"; const PROVIDERS = ["ollabridge", "openai", "claude", "watsonx", "ollama"]; const PROVIDER_LABELS = { ollabridge: "OllaBridge Cloud", openai: "OpenAI", claude: "Claude", watsonx: "Watsonx", ollama: "Ollama", }; const AUTH_MODES = [ { id: "device", label: "Device Pairing", icon: "📱" }, { id: "apikey", label: "API Key", icon: "🔑" }, { id: "local", label: "Local Trust", icon: "🏠" }, ]; function LoadingState({ loadingMessage, loadingSlow, onRetry }) { return (
); } export default function LlmSettings() { const [settings, setSettings] = useState(null); const [initialLoading, setInitialLoading] = useState(true); const [loadingSlow, setLoadingSlow] = useState(false); const [saving, setSaving] = useState(false); const [error, setError] = useState(""); const [savedMsg, setSavedMsg] = useState(""); const [modelsByProvider, setModelsByProvider] = useState({}); const [modelsError, setModelsError] = useState(""); const [loadingModelsFor, setLoadingModelsFor] = useState(""); const [testResult, setTestResult] = useState(null); const [testing, setTesting] = useState(false); const [authMode, setAuthMode] = useState("local"); const [pairCode, setPairCode] = useState(""); const [pairing, setPairing] = useState(false); const [pairResult, setPairResult] = useState(null); const loadingMessage = useMemo(() => { if (loadingSlow) { return "Still loading provider configuration…"; } return "Loading current configuration…"; }, [loadingSlow]); const loadSettings = async () => { setInitialLoading(true); setError(""); setLoadingSlow(false); let slowTimer; try { slowTimer = window.setTimeout(() => { setLoadingSlow(true); }, 1500); const res = await fetch("/api/settings"); const data = await res.json(); if (!res.ok) { throw new Error(data.error || "Failed to load settings"); } setSettings(data); } catch (e) { console.error(e); setError(e.message || "Failed to load settings"); } finally { window.clearTimeout(slowTimer); setInitialLoading(false); } }; useEffect(() => { loadSettings(); }, []); const updateField = (section, field, value) => { setSettings((prev) => ({ ...prev, [section]: { ...prev[section], [field]: value, }, })); }; const handleSave = async () => { setSaving(true); setError(""); setSavedMsg(""); try { const res = await fetch("/api/settings/llm", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(settings), }); const data = await res.json(); if (!res.ok) throw new Error(data.error || "Failed to save settings"); setSettings(data); setSavedMsg("Settings saved successfully!"); setTimeout(() => setSavedMsg(""), 3000); } catch (e) { console.error(e); setError(e.message || "Failed to save settings"); } finally { setSaving(false); } }; const loadModelsForProvider = async (provider) => { setModelsError(""); setLoadingModelsFor(provider); try { const res = await fetch(`/api/settings/models?provider=${provider}`); const data = await res.json(); if (!res.ok || data.error) { throw new Error(data.error || "Failed to load models"); } setModelsByProvider((prev) => ({ ...prev, [provider]: data.models || [], })); } catch (e) { console.error(e); setModelsError(e.message || "Failed to load models"); } finally { setLoadingModelsFor(""); } }; const handlePair = async () => { if (!pairCode.trim()) return; setPairing(true); setPairResult(null); try { const baseUrl = settings?.ollabridge?.base_url || "https://ruslanmv-ollabridge.hf.space"; const res = await fetch("/api/ollabridge/pair", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ base_url: baseUrl, code: pairCode.trim() }), }); const data = await res.json(); if (data.success) { setPairResult({ ok: true, message: "Paired successfully!" }); if (data.token) { updateField("ollabridge", "api_key", data.token); } } else { setPairResult({ ok: false, message: data.error || "Pairing failed", }); } } catch (e) { setPairResult({ ok: false, message: e.message || "Pairing failed" }); } finally { setPairing(false); } }; const handleTestConnection = async () => { setTesting(true); setTestResult(null); try { const activeProvider = settings?.provider || "ollama"; const config = { provider: activeProvider }; if (activeProvider === "openai" && settings?.openai) { config.openai = { api_key: settings.openai.api_key, base_url: settings.openai.base_url, model: settings.openai.model, }; } else if (activeProvider === "claude" && settings?.claude) { config.claude = { api_key: settings.claude.api_key, base_url: settings.claude.base_url, model: settings.claude.model, }; } else if (activeProvider === "watsonx" && settings?.watsonx) { config.watsonx = { api_key: settings.watsonx.api_key, project_id: settings.watsonx.project_id, base_url: settings.watsonx.base_url, model_id: settings.watsonx.model_id, }; } else if (activeProvider === "ollama" && settings?.ollama) { config.ollama = { base_url: settings.ollama.base_url, model: settings.ollama.model, }; } else if (activeProvider === "ollabridge" && settings?.ollabridge) { config.ollabridge = { base_url: settings.ollabridge.base_url, model: settings.ollabridge.model, api_key: settings.ollabridge.api_key, }; } const result = await testProvider(config); setTestResult(result); } catch (err) { setTestResult({ health: "error", warning: err.message || "Test failed", }); } finally { setTesting(false); } }; if (initialLoading) { return ( ); } if (!settings) { return (

AI Providers

Admin / LLM Settings

{error || "Unable to load current configuration."}

); } const { provider } = settings; const availableModels = modelsByProvider[provider] || []; return (

AI Providers

Choose which LLM provider GitPilot should use for planning and agent workflows. Provider settings are stored on the server.

{error &&
{error}
} {savedMsg &&
{savedMsg}
}
{PROVIDERS.map((p) => ( ))}
{provider === "ollabridge" && (
OllaBridge Cloud Configuration
Connect to OllaBridge Cloud or any OllaBridge instance for LLM inference. No API key required for public endpoints.
{AUTH_MODES.map((m) => ( ))}
{authMode === "device" && (
Enter the pairing code from your OllaBridge console and click Pair.
setPairCode(e.target.value.toUpperCase())} onKeyDown={(e) => e.key === "Enter" && handlePair()} />
{pairResult && (
{pairResult.message}
)}
)} updateField("ollabridge", "base_url", e.target.value) } placeholder="https://your-ollabridge-endpoint" /> {(authMode === "apikey" || authMode === "local") && ( <> updateField("ollabridge", "api_key", e.target.value) } placeholder="Optional API key" /> )}
updateField("ollabridge", "model", e.target.value) } placeholder="qwen2.5:1.5b" />
)} {provider === "openai" && (
OpenAI Configuration
updateField("openai", "api_key", e.target.value)} placeholder="sk-..." /> updateField("openai", "base_url", e.target.value)} placeholder="Optional custom base URL" /> updateField("openai", "model", e.target.value)} placeholder="gpt-4o-mini" />
)} {provider === "claude" && (
Claude Configuration
updateField("claude", "api_key", e.target.value)} placeholder="Anthropic API key" /> updateField("claude", "base_url", e.target.value)} placeholder="Optional custom base URL" /> updateField("claude", "model", e.target.value)} placeholder="claude-sonnet-4-5" />
)} {provider === "watsonx" && (
Watsonx Configuration
updateField("watsonx", "api_key", e.target.value)} placeholder="Watsonx API key" /> updateField("watsonx", "project_id", e.target.value) } placeholder="Watsonx project ID" /> updateField("watsonx", "base_url", e.target.value)} placeholder="https://api.watsonx.ai/v1" /> updateField("watsonx", "model_id", e.target.value) } placeholder="meta-llama/llama-3-3-70b-instruct" />
)} {provider === "ollama" && (
Ollama Configuration
updateField("ollama", "base_url", e.target.value)} placeholder="http://localhost:11434" />
updateField("ollama", "model", e.target.value)} placeholder="llama3" />
)} {availableModels.length > 0 && (
Available Models
{availableModels.map((model) => ( ))}
)} {modelsError &&
{modelsError}
} {testResult && (
{testResult.health === "ok" ? testResult.details || "Provider connection successful." : testResult.warning || "Provider connection failed."}
)}
); }