feat: add Gemini API as alternative AI provider

Adds support for Google's Gemini API as an alternative to Claude Agent SDK
for observation extraction. Users can now choose between providers in the
settings UI.

Features:
- New GeminiAgent class using Gemini REST API
- Provider selection in Settings (Claude vs Gemini)
- Gemini API key configuration (via UI or GEMINI_API_KEY env var)
- Model selection: gemini-2.0-flash-exp, gemini-1.5-flash, gemini-1.5-pro
- Graceful fallback to Claude SDK if Gemini selected but no API key
- Seamless transition between providers without worker restart

Settings:
- CLAUDE_MEM_PROVIDER: 'claude' | 'gemini'
- CLAUDE_MEM_GEMINI_API_KEY: API key for Gemini
- CLAUDE_MEM_GEMINI_MODEL: Model selection

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
bigphoot
2025-12-23 11:03:06 -08:00
parent 4cf2c1bdb1
commit ec8dd08c32
11 changed files with 733 additions and 199 deletions
@@ -423,24 +423,65 @@ export function ContextSettingsModal({
{/* Section 4: Advanced */}
<CollapsibleSection
title="Advanced"
description="Model selection and integrations"
description="AI provider and model selection"
defaultOpen={false}
>
<FormField
label="Model"
tooltip="AI model used for generating observations"
label="AI Provider"
tooltip="Choose between Claude (via Agent SDK) or Gemini (via REST API)"
>
<select
value={formState.CLAUDE_MEM_MODEL || 'haiku'}
onChange={(e) => updateSetting('CLAUDE_MEM_MODEL', e.target.value)}
value={formState.CLAUDE_MEM_PROVIDER || 'claude'}
onChange={(e) => updateSetting('CLAUDE_MEM_PROVIDER', e.target.value)}
>
{/* Shorthand names forward to latest model version */}
<option value="haiku">haiku (fastest)</option>
<option value="sonnet">sonnet (balanced)</option>
<option value="opus">opus (highest quality)</option>
<option value="claude">Claude (uses your Claude account)</option>
<option value="gemini">Gemini (uses API key)</option>
</select>
</FormField>
{formState.CLAUDE_MEM_PROVIDER === 'claude' ? (
<FormField
label="Claude Model"
tooltip="Claude model used for generating observations"
>
<select
value={formState.CLAUDE_MEM_MODEL || 'haiku'}
onChange={(e) => updateSetting('CLAUDE_MEM_MODEL', e.target.value)}
>
<option value="haiku">haiku (fastest)</option>
<option value="sonnet">sonnet (balanced)</option>
<option value="opus">opus (highest quality)</option>
</select>
</FormField>
) : (
<>
<FormField
label="Gemini API Key"
tooltip="Your Google AI Studio API key (or set GEMINI_API_KEY env var)"
>
<input
type="password"
value={formState.CLAUDE_MEM_GEMINI_API_KEY || ''}
onChange={(e) => updateSetting('CLAUDE_MEM_GEMINI_API_KEY', e.target.value)}
placeholder="Enter Gemini API key..."
/>
</FormField>
<FormField
label="Gemini Model"
tooltip="Gemini model used for generating observations"
>
<select
value={formState.CLAUDE_MEM_GEMINI_MODEL || 'gemini-2.0-flash-exp'}
onChange={(e) => updateSetting('CLAUDE_MEM_GEMINI_MODEL', e.target.value)}
>
<option value="gemini-2.0-flash-exp">gemini-2.0-flash-exp (fastest)</option>
<option value="gemini-1.5-flash">gemini-1.5-flash (balanced)</option>
<option value="gemini-1.5-pro">gemini-1.5-pro (highest quality)</option>
</select>
</FormField>
</>
)}
<FormField
label="Worker Port"
tooltip="Port for the background worker service"
+5
View File
@@ -8,6 +8,11 @@ export const DEFAULT_SETTINGS = {
CLAUDE_MEM_WORKER_PORT: '37777',
CLAUDE_MEM_WORKER_HOST: '127.0.0.1',
// AI Provider Configuration
CLAUDE_MEM_PROVIDER: 'claude',
CLAUDE_MEM_GEMINI_API_KEY: '',
CLAUDE_MEM_GEMINI_MODEL: 'gemini-2.0-flash-exp',
// Token Economics (all true for backwards compatibility)
CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: 'true',
CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS: 'true',
+5
View File
@@ -20,6 +20,11 @@ export function useSettings() {
CLAUDE_MEM_WORKER_PORT: data.CLAUDE_MEM_WORKER_PORT || DEFAULT_SETTINGS.CLAUDE_MEM_WORKER_PORT,
CLAUDE_MEM_WORKER_HOST: data.CLAUDE_MEM_WORKER_HOST || DEFAULT_SETTINGS.CLAUDE_MEM_WORKER_HOST,
// AI Provider Configuration
CLAUDE_MEM_PROVIDER: data.CLAUDE_MEM_PROVIDER || DEFAULT_SETTINGS.CLAUDE_MEM_PROVIDER,
CLAUDE_MEM_GEMINI_API_KEY: data.CLAUDE_MEM_GEMINI_API_KEY || DEFAULT_SETTINGS.CLAUDE_MEM_GEMINI_API_KEY,
CLAUDE_MEM_GEMINI_MODEL: data.CLAUDE_MEM_GEMINI_MODEL || DEFAULT_SETTINGS.CLAUDE_MEM_GEMINI_MODEL,
// Token Economics Display
CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: data.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS,
CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS: data.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS,
+5
View File
@@ -60,6 +60,11 @@ export interface Settings {
CLAUDE_MEM_WORKER_PORT: string;
CLAUDE_MEM_WORKER_HOST: string;
// AI Provider Configuration
CLAUDE_MEM_PROVIDER?: string; // 'claude' | 'gemini'
CLAUDE_MEM_GEMINI_API_KEY?: string;
CLAUDE_MEM_GEMINI_MODEL?: string; // 'gemini-2.0-flash-exp' | 'gemini-1.5-flash' | 'gemini-1.5-pro'
// Token Economics Display
CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS?: string;
CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS?: string;