diff --git a/auto-claude-ui/src/renderer/components/onboarding/GraphitiStep.tsx b/auto-claude-ui/src/renderer/components/onboarding/GraphitiStep.tsx
index 98a6f629..8a6b7b24 100644
--- a/auto-claude-ui/src/renderer/components/onboarding/GraphitiStep.tsx
+++ b/auto-claude-ui/src/renderer/components/onboarding/GraphitiStep.tsx
@@ -38,14 +38,28 @@ interface GraphitiConfig {
falkorDbUri: string;
llmProvider: GraphitiProviderType;
apiKey: string;
+ ollamaBaseUrl: string; // For Ollama provider (no API key needed)
}
// Provider display info
-const PROVIDER_INFO: Record = {
- openai: { name: 'OpenAI', placeholder: 'sk-...', link: 'https://platform.openai.com/api-keys' },
- anthropic: { name: 'Anthropic', placeholder: 'sk-ant-...', link: 'https://console.anthropic.com/settings/keys' },
- google: { name: 'Google (Gemini)', placeholder: 'AIza...', link: 'https://aistudio.google.com/apikey' },
- groq: { name: 'Groq', placeholder: 'gsk_...', link: 'https://console.groq.com/keys' },
+const PROVIDER_INFO: Record = {
+ openai: { name: 'OpenAI', placeholder: 'sk-...', link: 'https://platform.openai.com/api-keys', requiresApiKey: true },
+ anthropic: { name: 'Anthropic', placeholder: 'sk-ant-...', link: 'https://console.anthropic.com/settings/keys', requiresApiKey: true },
+ google: { name: 'Google (Gemini)', placeholder: 'AIza...', link: 'https://aistudio.google.com/apikey', requiresApiKey: true },
+ groq: { name: 'Groq', placeholder: 'gsk_...', link: 'https://console.groq.com/keys', requiresApiKey: true },
+ ollama: {
+ name: 'Ollama',
+ placeholder: 'http://localhost:11434',
+ link: 'https://ollama.ai',
+ requiresApiKey: false,
+ description: 'Local LLM - no API key required'
+ },
};
// Helper to get the saved API key for a provider from settings
@@ -55,10 +69,16 @@ function getApiKeyForProvider(provider: GraphitiProviderType, settings: Record): string {
+ return (settings.ollamaBaseUrl as string) || 'http://localhost:11434';
+}
+
interface ValidationStatus {
falkordb: { tested: boolean; success: boolean; message: string } | null;
llm: { tested: boolean; success: boolean; message: string } | null;
@@ -77,7 +97,8 @@ export function GraphitiStep({ onNext, onBack, onSkip }: GraphitiStepProps) {
enabled: false,
falkorDbUri: 'bolt://localhost:6379', // Standard FalkorDB port, will be auto-detected from Docker
llmProvider: savedProvider,
- apiKey: getApiKeyForProvider(savedProvider, settings as Record)
+ apiKey: getApiKeyForProvider(savedProvider, settings as Record),
+ ollamaBaseUrl: getOllamaBaseUrl(settings as Record)
});
const [showApiKey, setShowApiKey] = useState(false);
const [isSaving, setIsSaving] = useState(false);
@@ -128,19 +149,32 @@ export function GraphitiStep({ onNext, onBack, onSkip }: GraphitiStepProps) {
};
const handleProviderChange = (provider: GraphitiProviderType) => {
- // Load saved API key for the selected provider
+ // Load saved API key or base URL for the selected provider
const savedKey = getApiKeyForProvider(provider, settings as Record);
- setConfig(prev => ({ ...prev, llmProvider: provider, apiKey: savedKey }));
+ const savedOllamaUrl = getOllamaBaseUrl(settings as Record);
+ setConfig(prev => ({
+ ...prev,
+ llmProvider: provider,
+ apiKey: savedKey,
+ ollamaBaseUrl: savedOllamaUrl
+ }));
setValidationStatus(prev => ({ ...prev, llm: null }));
setError(null);
};
const handleTestConnection = async () => {
const providerName = PROVIDER_INFO[config.llmProvider].name;
- if (!config.apiKey.trim()) {
+ const providerInfo = PROVIDER_INFO[config.llmProvider];
+
+ // Validate input based on provider type
+ if (providerInfo.requiresApiKey && !config.apiKey.trim()) {
setError(`Please enter a ${providerName} API key to test the connection`);
return;
}
+ if (config.llmProvider === 'ollama' && !config.ollamaBaseUrl.trim()) {
+ setError('Please enter the Ollama server URL to test the connection');
+ return;
+ }
setIsValidating(true);
setError(null);
@@ -149,9 +183,13 @@ export function GraphitiStep({ onNext, onBack, onSkip }: GraphitiStepProps) {
try {
// For now, we still use the OpenAI test endpoint, but pass the provider info
// TODO: Add provider-specific validation endpoints
+ // For Ollama, pass the base URL instead of API key
+ const testCredential = config.llmProvider === 'ollama'
+ ? config.ollamaBaseUrl.trim()
+ : config.apiKey.trim();
const result = await window.electronAPI.testGraphitiConnection(
config.falkorDbUri,
- config.apiKey.trim()
+ testCredential
);
if (result?.success && result?.data) {
@@ -198,10 +236,17 @@ export function GraphitiStep({ onNext, onBack, onSkip }: GraphitiStepProps) {
}
const providerName = PROVIDER_INFO[config.llmProvider].name;
- if (!config.apiKey.trim()) {
+ const providerInfo = PROVIDER_INFO[config.llmProvider];
+
+ // Validate input based on provider type
+ if (providerInfo.requiresApiKey && !config.apiKey.trim()) {
setError(`${providerName} API key is required for Graphiti`);
return;
}
+ if (config.llmProvider === 'ollama' && !config.ollamaBaseUrl.trim()) {
+ setError('Ollama server URL is required for Graphiti');
+ return;
+ }
setIsSaving(true);
setError(null);
@@ -212,7 +257,7 @@ export function GraphitiStep({ onNext, onBack, onSkip }: GraphitiStepProps) {
graphitiLlmProvider: config.llmProvider,
};
- // Save the API key for the selected provider
+ // Save the API key or base URL for the selected provider
if (config.llmProvider === 'openai') {
settingsUpdate.globalOpenAIApiKey = config.apiKey.trim();
} else if (config.llmProvider === 'anthropic') {
@@ -221,6 +266,8 @@ export function GraphitiStep({ onNext, onBack, onSkip }: GraphitiStepProps) {
settingsUpdate.globalGoogleApiKey = config.apiKey.trim();
} else if (config.llmProvider === 'groq') {
settingsUpdate.globalGroqApiKey = config.apiKey.trim();
+ } else if (config.llmProvider === 'ollama') {
+ settingsUpdate.ollamaBaseUrl = config.ollamaBaseUrl.trim();
}
const result = await window.electronAPI.saveSettings(settingsUpdate);
@@ -236,6 +283,8 @@ export function GraphitiStep({ onNext, onBack, onSkip }: GraphitiStepProps) {
storeUpdate.globalGoogleApiKey = config.apiKey.trim();
} else if (config.llmProvider === 'groq') {
storeUpdate.globalGroqApiKey = config.apiKey.trim();
+ } else if (config.llmProvider === 'ollama') {
+ storeUpdate.ollamaBaseUrl = config.ollamaBaseUrl.trim();
}
updateSettings(storeUpdate);
// Proceed to next step immediately after successful save
@@ -404,7 +453,7 @@ export function GraphitiStep({ onNext, onBack, onSkip }: GraphitiStepProps) {
Enable Graphiti Memory
- Requires FalkorDB (Docker) and an LLM API key
+ Requires FalkorDB (Docker) and an LLM provider (API key or local Ollama)
@@ -439,6 +488,7 @@ export function GraphitiStep({ onNext, onBack, onSkip }: GraphitiStepProps) {
Anthropic (Claude)
Google (Gemini)
Groq (Llama)
+ Ollama (Local)
@@ -482,76 +532,124 @@ export function GraphitiStep({ onNext, onBack, onSkip }: GraphitiStepProps) {
- {/* Dynamic API Key based on provider */}
-
-
-
- {validationStatus.llm && (
-
- {validationStatus.llm.success ? (
-
- ) : (
-
- )}
-
- {validationStatus.llm.success ? 'Valid' : 'Invalid'}
-
-
- )}
-
-
+ {/* Dynamic credential field based on provider */}
+ {config.llmProvider === 'ollama' ? (
+ /* Ollama Base URL field */
+
+
+
+ {validationStatus.llm && (
+
+ {validationStatus.llm.success ? (
+
+ ) : (
+
+ )}
+
+ {validationStatus.llm.success ? 'Connected' : 'Failed'}
+
+
+ )}
+
{
- setConfig(prev => ({ ...prev, apiKey: e.target.value }));
+ setConfig(prev => ({ ...prev, ollamaBaseUrl: e.target.value }));
setValidationStatus(prev => ({ ...prev, llm: null }));
}}
- placeholder={PROVIDER_INFO[config.llmProvider].placeholder}
- className="pr-10 font-mono text-sm"
+ placeholder="http://localhost:11434"
+ className="font-mono text-sm"
disabled={isSaving || isValidating}
/>
-
-
-
-
-
- {showApiKey ? 'Hide API key' : 'Show API key'}
-
-
+
+ No API key required. Make sure{' '}
+
+ Ollama
+
+ {' '}is running locally.
+
-
- Required for graph operations. Get your key from{' '}
-
- {PROVIDER_INFO[config.llmProvider].name}
-
-
-
+ ) : (
+ /* API Key field for other providers */
+
+
+
+ {validationStatus.llm && (
+
+ {validationStatus.llm.success ? (
+
+ ) : (
+
+ )}
+
+ {validationStatus.llm.success ? 'Valid' : 'Invalid'}
+
+
+ )}
+
+
+ {
+ setConfig(prev => ({ ...prev, apiKey: e.target.value }));
+ setValidationStatus(prev => ({ ...prev, llm: null }));
+ }}
+ placeholder={PROVIDER_INFO[config.llmProvider].placeholder}
+ className="pr-10 font-mono text-sm"
+ disabled={isSaving || isValidating}
+ />
+
+
+
+
+
+ {showApiKey ? 'Hide API key' : 'Show API key'}
+
+
+
+
+ Required for graph operations. Get your key from{' '}
+
+ {PROVIDER_INFO[config.llmProvider].name}
+
+
+
+ )}
{/* Test Connection Button */}
)}
@@ -603,7 +706,7 @@ export function GraphitiStep({ onNext, onBack, onSkip }: GraphitiStepProps) {