feat: Add OpenRouter as LLM/embedding provider (#162)
* feat: Add OpenRouter as LLM/embedding provider Add OpenRouter provider support for Graphiti memory integration, enabling access to multiple LLM providers through a single API. Changes: Backend: - Created openrouter_llm.py: OpenRouter LLM provider using OpenAI-compatible API - Created openrouter_embedder.py: OpenRouter embedder provider - Updated config.py: Added OpenRouter to provider enums and configuration - New fields: openrouter_api_key, openrouter_base_url, openrouter_llm_model, openrouter_embedding_model - Validation methods updated for OpenRouter - Updated factory.py: Added OpenRouter to LLM and embedder factories - Updated provider __init__.py files: Exported new OpenRouter functions Frontend: - Updated project.ts types: Added 'openrouter' to provider type unions - GraphitiProviderConfig extended with OpenRouter fields - Updated GraphitiStep.tsx: Added OpenRouter to provider arrays - LLM_PROVIDERS: 'Multi-provider aggregator' - EMBEDDING_PROVIDERS: 'OpenAI-compatible embeddings' - Added OpenRouter API key input field with show/hide toggle - Link to https://openrouter.ai/keys - Updated env-handlers.ts: OpenRouter .env generation and parsing - Template generation for OPENROUTER_* variables - Parsing from .env files with proper type casting Documentation: - Updated .env.example with OpenRouter section - Configuration examples - Popular model recommendations - Example configuration (#6) Fixes #92 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * refactor: address CodeRabbit review comments for OpenRouter - Add globalOpenRouterApiKey to settings types and store updates - Initialize openrouterApiKey from global settings - Update documentation to include OpenRouter in provider lists - Add OpenRouter handling to get_embedding_dimension() method - Add openrouter to provider cleanup list - Add OpenRouter to get_available_providers() function - Clarify Legacy comment for openrouterLlmModel These changes complete the OpenRouter integration by ensuring proper settings persistence and provider detection across the application. * fix: apply ruff formatting to OpenRouter code - Break long error message across multiple lines - Format provider list with one item per line - Fixes lint CI failure 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
f168bdc3ac
commit
02bef954f3
@@ -153,10 +153,10 @@ GRAPHITI_ENABLED=true
|
||||
# Choose which providers to use for LLM and embeddings.
|
||||
# Default is "openai" for both.
|
||||
|
||||
# LLM provider: openai | anthropic | azure_openai | ollama | google
|
||||
# LLM provider: openai | anthropic | azure_openai | ollama | google | openrouter
|
||||
# GRAPHITI_LLM_PROVIDER=openai
|
||||
|
||||
# Embedder provider: openai | voyage | azure_openai | ollama | google
|
||||
# Embedder provider: openai | voyage | azure_openai | ollama | google | openrouter
|
||||
# GRAPHITI_EMBEDDER_PROVIDER=openai
|
||||
|
||||
# =============================================================================
|
||||
@@ -221,6 +221,28 @@ GRAPHITI_ENABLED=true
|
||||
# Google Embedding Model (default: text-embedding-004)
|
||||
# GOOGLE_EMBEDDING_MODEL=text-embedding-004
|
||||
|
||||
# =============================================================================
|
||||
# GRAPHITI: OpenRouter Provider (Multi-provider aggregator)
|
||||
# =============================================================================
|
||||
# Use OpenRouter to access multiple LLM providers through a single API.
|
||||
# OpenRouter provides access to Anthropic, OpenAI, Google, and many other models.
|
||||
# Get API key from: https://openrouter.ai/keys
|
||||
#
|
||||
# Required: OPENROUTER_API_KEY
|
||||
|
||||
# OpenRouter API Key
|
||||
# OPENROUTER_API_KEY=sk-or-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
|
||||
# OpenRouter Base URL (default: https://openrouter.ai/api/v1)
|
||||
# OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
|
||||
|
||||
# OpenRouter LLM Model (default: anthropic/claude-3.5-sonnet)
|
||||
# Popular choices: anthropic/claude-3.5-sonnet, openai/gpt-4o, google/gemini-2.0-flash
|
||||
# OPENROUTER_LLM_MODEL=anthropic/claude-3.5-sonnet
|
||||
|
||||
# OpenRouter Embedding Model (default: openai/text-embedding-3-small)
|
||||
# OPENROUTER_EMBEDDING_MODEL=openai/text-embedding-3-small
|
||||
|
||||
# =============================================================================
|
||||
# GRAPHITI: Azure OpenAI Provider
|
||||
# =============================================================================
|
||||
@@ -307,3 +329,11 @@ GRAPHITI_ENABLED=true
|
||||
# GRAPHITI_LLM_PROVIDER=google
|
||||
# GRAPHITI_EMBEDDER_PROVIDER=google
|
||||
# GOOGLE_API_KEY=AIzaSyxxxxxxxx
|
||||
#
|
||||
# --- Example 6: OpenRouter (multi-provider aggregator) ---
|
||||
# GRAPHITI_ENABLED=true
|
||||
# GRAPHITI_LLM_PROVIDER=openrouter
|
||||
# GRAPHITI_EMBEDDER_PROVIDER=openrouter
|
||||
# OPENROUTER_API_KEY=sk-or-xxxxxxxx
|
||||
# OPENROUTER_LLM_MODEL=anthropic/claude-3.5-sonnet
|
||||
# OPENROUTER_EMBEDDING_MODEL=openai/text-embedding-3-small
|
||||
|
||||
@@ -8,8 +8,8 @@ Follows the same patterns as linear_config.py for consistency.
|
||||
Uses LadybugDB as the embedded graph database (no Docker required, requires Python 3.12+).
|
||||
|
||||
Multi-Provider Support (V2):
|
||||
- LLM Providers: OpenAI, Anthropic, Azure OpenAI, Ollama, Google AI
|
||||
- Embedder Providers: OpenAI, Voyage AI, Azure OpenAI, Ollama, Google AI
|
||||
- LLM Providers: OpenAI, Anthropic, Azure OpenAI, Ollama, Google AI, OpenRouter
|
||||
- Embedder Providers: OpenAI, Voyage AI, Azure OpenAI, Ollama, Google AI, OpenRouter
|
||||
|
||||
Environment Variables:
|
||||
# Core
|
||||
@@ -89,6 +89,7 @@ class LLMProvider(str, Enum):
|
||||
AZURE_OPENAI = "azure_openai"
|
||||
OLLAMA = "ollama"
|
||||
GOOGLE = "google"
|
||||
OPENROUTER = "openrouter"
|
||||
|
||||
|
||||
class EmbedderProvider(str, Enum):
|
||||
@@ -99,6 +100,7 @@ class EmbedderProvider(str, Enum):
|
||||
AZURE_OPENAI = "azure_openai"
|
||||
OLLAMA = "ollama"
|
||||
GOOGLE = "google"
|
||||
OPENROUTER = "openrouter"
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -141,6 +143,12 @@ class GraphitiConfig:
|
||||
google_llm_model: str = "gemini-2.0-flash"
|
||||
google_embedding_model: str = "text-embedding-004"
|
||||
|
||||
# OpenRouter settings (multi-provider aggregator)
|
||||
openrouter_api_key: str = ""
|
||||
openrouter_base_url: str = "https://openrouter.ai/api/v1"
|
||||
openrouter_llm_model: str = "anthropic/claude-3.5-sonnet"
|
||||
openrouter_embedding_model: str = "openai/text-embedding-3-small"
|
||||
|
||||
# Ollama settings (local)
|
||||
ollama_base_url: str = DEFAULT_OLLAMA_BASE_URL
|
||||
ollama_llm_model: str = ""
|
||||
@@ -196,6 +204,18 @@ class GraphitiConfig:
|
||||
"GOOGLE_EMBEDDING_MODEL", "text-embedding-004"
|
||||
)
|
||||
|
||||
# OpenRouter settings
|
||||
openrouter_api_key = os.environ.get("OPENROUTER_API_KEY", "")
|
||||
openrouter_base_url = os.environ.get(
|
||||
"OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1"
|
||||
)
|
||||
openrouter_llm_model = os.environ.get(
|
||||
"OPENROUTER_LLM_MODEL", "anthropic/claude-3.5-sonnet"
|
||||
)
|
||||
openrouter_embedding_model = os.environ.get(
|
||||
"OPENROUTER_EMBEDDING_MODEL", "openai/text-embedding-3-small"
|
||||
)
|
||||
|
||||
# Ollama settings
|
||||
ollama_base_url = os.environ.get("OLLAMA_BASE_URL", DEFAULT_OLLAMA_BASE_URL)
|
||||
ollama_llm_model = os.environ.get("OLLAMA_LLM_MODEL", "")
|
||||
@@ -227,6 +247,10 @@ class GraphitiConfig:
|
||||
google_api_key=google_api_key,
|
||||
google_llm_model=google_llm_model,
|
||||
google_embedding_model=google_embedding_model,
|
||||
openrouter_api_key=openrouter_api_key,
|
||||
openrouter_base_url=openrouter_base_url,
|
||||
openrouter_llm_model=openrouter_llm_model,
|
||||
openrouter_embedding_model=openrouter_embedding_model,
|
||||
ollama_base_url=ollama_base_url,
|
||||
ollama_llm_model=ollama_llm_model,
|
||||
ollama_embedding_model=ollama_embedding_model,
|
||||
@@ -267,6 +291,8 @@ class GraphitiConfig:
|
||||
return bool(self.ollama_embedding_model)
|
||||
elif self.embedder_provider == "google":
|
||||
return bool(self.google_api_key)
|
||||
elif self.embedder_provider == "openrouter":
|
||||
return bool(self.openrouter_api_key)
|
||||
return False
|
||||
|
||||
def get_validation_errors(self) -> list[str]:
|
||||
@@ -309,6 +335,11 @@ class GraphitiConfig:
|
||||
elif self.embedder_provider == "google":
|
||||
if not self.google_api_key:
|
||||
errors.append("Google embedder provider requires GOOGLE_API_KEY")
|
||||
elif self.embedder_provider == "openrouter":
|
||||
if not self.openrouter_api_key:
|
||||
errors.append(
|
||||
"OpenRouter embedder provider requires OPENROUTER_API_KEY"
|
||||
)
|
||||
else:
|
||||
errors.append(f"Unknown embedder provider: {self.embedder_provider}")
|
||||
|
||||
@@ -367,6 +398,18 @@ class GraphitiConfig:
|
||||
elif self.embedder_provider == "azure_openai":
|
||||
# Depends on the deployment, default to 1536
|
||||
return 1536
|
||||
elif self.embedder_provider == "openrouter":
|
||||
# OpenRouter uses provider/model format
|
||||
# Extract underlying provider to determine dimension
|
||||
model = self.openrouter_embedding_model.lower()
|
||||
if model.startswith("openai/"):
|
||||
return 1536 # OpenAI text-embedding-3-small
|
||||
elif model.startswith("voyage/"):
|
||||
return 1024 # Voyage-3
|
||||
elif model.startswith("google/"):
|
||||
return 768 # Google text-embedding-004
|
||||
# Add more providers as needed
|
||||
return 1536 # Default for unknown OpenRouter models
|
||||
return 768 # Safe default
|
||||
|
||||
def get_provider_signature(self) -> str:
|
||||
@@ -403,7 +446,14 @@ class GraphitiConfig:
|
||||
base_name = self.database
|
||||
|
||||
# Remove existing provider suffix if present
|
||||
for provider in ["openai", "ollama", "voyage", "google", "azure_openai"]:
|
||||
for provider in [
|
||||
"openai",
|
||||
"ollama",
|
||||
"voyage",
|
||||
"google",
|
||||
"azure_openai",
|
||||
"openrouter",
|
||||
]:
|
||||
if f"_{provider}_" in base_name:
|
||||
base_name = base_name.split(f"_{provider}_")[0]
|
||||
break
|
||||
@@ -617,6 +667,11 @@ def get_available_providers() -> dict:
|
||||
available_llm.append("google")
|
||||
available_embedder.append("google")
|
||||
|
||||
# Check OpenRouter
|
||||
if config.openrouter_api_key:
|
||||
available_llm.append("openrouter")
|
||||
available_embedder.append("openrouter")
|
||||
|
||||
# Check Ollama
|
||||
if config.ollama_llm_model:
|
||||
available_llm.append("ollama")
|
||||
|
||||
@@ -18,6 +18,7 @@ from .ollama_embedder import (
|
||||
get_embedding_dim_for_model,
|
||||
)
|
||||
from .openai_embedder import create_openai_embedder
|
||||
from .openrouter_embedder import create_openrouter_embedder
|
||||
from .voyage_embedder import create_voyage_embedder
|
||||
|
||||
__all__ = [
|
||||
@@ -26,6 +27,7 @@ __all__ = [
|
||||
"create_azure_openai_embedder",
|
||||
"create_ollama_embedder",
|
||||
"create_google_embedder",
|
||||
"create_openrouter_embedder",
|
||||
"KNOWN_OLLAMA_EMBEDDING_MODELS",
|
||||
"get_embedding_dim_for_model",
|
||||
]
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
"""
|
||||
OpenRouter Embedder Provider
|
||||
=============================
|
||||
|
||||
OpenRouter embedder implementation for Graphiti.
|
||||
Uses OpenAI-compatible embedding API.
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ...config import GraphitiConfig
|
||||
|
||||
from ..exceptions import ProviderError, ProviderNotInstalled
|
||||
|
||||
|
||||
def create_openrouter_embedder(config: "GraphitiConfig") -> Any:
|
||||
"""
|
||||
Create OpenRouter embedder client.
|
||||
|
||||
OpenRouter uses OpenAI-compatible API, so we use the OpenAI embedder
|
||||
with custom base URL.
|
||||
|
||||
Args:
|
||||
config: GraphitiConfig with OpenRouter settings
|
||||
|
||||
Returns:
|
||||
OpenAI-compatible embedder instance
|
||||
|
||||
Raises:
|
||||
ProviderNotInstalled: If graphiti-core is not installed
|
||||
ProviderError: If API key is missing
|
||||
|
||||
Example:
|
||||
>>> from auto_claude.integrations.graphiti.config import GraphitiConfig
|
||||
>>> config = GraphitiConfig(
|
||||
... openrouter_api_key="sk-or-...",
|
||||
... openrouter_embedding_model="openai/text-embedding-3-small"
|
||||
... )
|
||||
>>> embedder = create_openrouter_embedder(config)
|
||||
"""
|
||||
try:
|
||||
from graphiti_core.embedder import EmbedderConfig, OpenAIEmbedder
|
||||
except ImportError as e:
|
||||
raise ProviderNotInstalled(
|
||||
f"OpenRouter provider requires graphiti-core. "
|
||||
f"Install with: pip install graphiti-core\n"
|
||||
f"Error: {e}"
|
||||
)
|
||||
|
||||
if not config.openrouter_api_key:
|
||||
raise ProviderError("OpenRouter provider requires OPENROUTER_API_KEY")
|
||||
|
||||
embedder_config = EmbedderConfig(
|
||||
api_key=config.openrouter_api_key,
|
||||
model=config.openrouter_embedding_model,
|
||||
base_url=config.openrouter_base_url,
|
||||
)
|
||||
|
||||
return OpenAIEmbedder(config=embedder_config)
|
||||
@@ -16,6 +16,7 @@ from .embedder_providers import (
|
||||
create_google_embedder,
|
||||
create_ollama_embedder,
|
||||
create_openai_embedder,
|
||||
create_openrouter_embedder,
|
||||
create_voyage_embedder,
|
||||
)
|
||||
from .exceptions import ProviderError
|
||||
@@ -25,6 +26,7 @@ from .llm_providers import (
|
||||
create_google_llm_client,
|
||||
create_ollama_llm_client,
|
||||
create_openai_llm_client,
|
||||
create_openrouter_llm_client,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -58,6 +60,8 @@ def create_llm_client(config: "GraphitiConfig") -> Any:
|
||||
return create_ollama_llm_client(config)
|
||||
elif provider == "google":
|
||||
return create_google_llm_client(config)
|
||||
elif provider == "openrouter":
|
||||
return create_openrouter_llm_client(config)
|
||||
else:
|
||||
raise ProviderError(f"Unknown LLM provider: {provider}")
|
||||
|
||||
@@ -90,5 +94,7 @@ def create_embedder(config: "GraphitiConfig") -> Any:
|
||||
return create_ollama_embedder(config)
|
||||
elif provider == "google":
|
||||
return create_google_embedder(config)
|
||||
elif provider == "openrouter":
|
||||
return create_openrouter_embedder(config)
|
||||
else:
|
||||
raise ProviderError(f"Unknown embedder provider: {provider}")
|
||||
|
||||
@@ -15,6 +15,7 @@ from .azure_openai_llm import create_azure_openai_llm_client
|
||||
from .google_llm import create_google_llm_client
|
||||
from .ollama_llm import create_ollama_llm_client
|
||||
from .openai_llm import create_openai_llm_client
|
||||
from .openrouter_llm import create_openrouter_llm_client
|
||||
|
||||
__all__ = [
|
||||
"create_openai_llm_client",
|
||||
@@ -22,4 +23,5 @@ __all__ = [
|
||||
"create_azure_openai_llm_client",
|
||||
"create_ollama_llm_client",
|
||||
"create_google_llm_client",
|
||||
"create_openrouter_llm_client",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
"""
|
||||
OpenRouter LLM Provider
|
||||
=======================
|
||||
|
||||
OpenRouter LLM client implementation for Graphiti.
|
||||
Uses OpenAI-compatible API.
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ...config import GraphitiConfig
|
||||
|
||||
from ..exceptions import ProviderError, ProviderNotInstalled
|
||||
|
||||
|
||||
def create_openrouter_llm_client(config: "GraphitiConfig") -> Any:
|
||||
"""
|
||||
Create OpenRouter LLM client.
|
||||
|
||||
OpenRouter uses OpenAI-compatible API, so we use the OpenAI client
|
||||
with custom base URL.
|
||||
|
||||
Args:
|
||||
config: GraphitiConfig with OpenRouter settings
|
||||
|
||||
Returns:
|
||||
OpenAI-compatible LLM client instance
|
||||
|
||||
Raises:
|
||||
ProviderNotInstalled: If graphiti-core is not installed
|
||||
ProviderError: If API key is missing
|
||||
|
||||
Example:
|
||||
>>> from auto_claude.integrations.graphiti.config import GraphitiConfig
|
||||
>>> config = GraphitiConfig(
|
||||
... openrouter_api_key="sk-or-...",
|
||||
... openrouter_llm_model="anthropic/claude-3.5-sonnet"
|
||||
... )
|
||||
>>> client = create_openrouter_llm_client(config)
|
||||
"""
|
||||
try:
|
||||
from graphiti_core.llm_client.config import LLMConfig
|
||||
from graphiti_core.llm_client.openai_client import OpenAIClient
|
||||
except ImportError as e:
|
||||
raise ProviderNotInstalled(
|
||||
f"OpenRouter provider requires graphiti-core. "
|
||||
f"Install with: pip install graphiti-core\n"
|
||||
f"Error: {e}"
|
||||
)
|
||||
|
||||
if not config.openrouter_api_key:
|
||||
raise ProviderError("OpenRouter provider requires OPENROUTER_API_KEY")
|
||||
|
||||
llm_config = LLMConfig(
|
||||
api_key=config.openrouter_api_key,
|
||||
model=config.openrouter_llm_model,
|
||||
base_url=config.openrouter_base_url,
|
||||
)
|
||||
|
||||
# OpenRouter uses OpenAI-compatible API
|
||||
# Disable reasoning/verbosity for compatibility
|
||||
return OpenAIClient(config=llm_config, reasoning=None, verbosity=None)
|
||||
@@ -44,6 +44,7 @@ const LLM_PROVIDERS: Array<{
|
||||
{ id: 'anthropic', name: 'Anthropic', description: 'Claude models', requiresApiKey: true },
|
||||
{ id: 'google', name: 'Google AI', description: 'Gemini models', requiresApiKey: true },
|
||||
{ id: 'groq', name: 'Groq', description: 'Llama models (fast inference)', requiresApiKey: true },
|
||||
{ id: 'openrouter', name: 'OpenRouter', description: 'Multi-provider aggregator', requiresApiKey: true },
|
||||
{ id: 'azure_openai', name: 'Azure OpenAI', description: 'Enterprise Azure deployment', requiresApiKey: true },
|
||||
{ id: 'ollama', name: 'Ollama', description: 'Local models (free)', requiresApiKey: false }
|
||||
];
|
||||
@@ -58,6 +59,7 @@ const EMBEDDING_PROVIDERS: Array<{
|
||||
{ id: 'openai', name: 'OpenAI', description: 'text-embedding-3-small (recommended)', requiresApiKey: true },
|
||||
{ id: 'voyage', name: 'Voyage AI', description: 'voyage-3 (great with Anthropic)', requiresApiKey: true },
|
||||
{ id: 'google', name: 'Google AI', description: 'Gemini text-embedding-004', requiresApiKey: true },
|
||||
{ id: 'openrouter', name: 'OpenRouter', description: 'OpenAI-compatible embeddings', requiresApiKey: true },
|
||||
{ id: 'azure_openai', name: 'Azure OpenAI', description: 'Enterprise Azure embeddings', requiresApiKey: true }
|
||||
];
|
||||
|
||||
@@ -82,6 +84,11 @@ interface GraphitiConfig {
|
||||
googleApiKey: string;
|
||||
// Groq
|
||||
groqApiKey: string;
|
||||
// OpenRouter
|
||||
openrouterApiKey: string;
|
||||
openrouterBaseUrl: string;
|
||||
openrouterLlmModel: string;
|
||||
openrouterEmbeddingModel: string;
|
||||
// HuggingFace
|
||||
huggingfaceApiKey: string;
|
||||
// Ollama
|
||||
@@ -118,6 +125,10 @@ export function GraphitiStep({ onNext, onBack, onSkip }: GraphitiStepProps) {
|
||||
voyageApiKey: '',
|
||||
googleApiKey: settings.globalGoogleApiKey || '',
|
||||
groqApiKey: settings.globalGroqApiKey || '',
|
||||
openrouterApiKey: settings.globalOpenRouterApiKey || '',
|
||||
openrouterBaseUrl: 'https://openrouter.ai/api/v1',
|
||||
openrouterLlmModel: 'anthropic/claude-3.5-sonnet',
|
||||
openrouterEmbeddingModel: 'openai/text-embedding-3-small',
|
||||
huggingfaceApiKey: '',
|
||||
ollamaBaseUrl: settings.ollamaBaseUrl || 'http://localhost:11434',
|
||||
ollamaLlmModel: '',
|
||||
@@ -194,6 +205,9 @@ export function GraphitiStep({ onNext, onBack, onSkip }: GraphitiStepProps) {
|
||||
if (llmProvider === 'groq') {
|
||||
if (!config.groqApiKey.trim()) return 'Groq API key';
|
||||
}
|
||||
if (llmProvider === 'openrouter' || embeddingProvider === 'openrouter') {
|
||||
if (!config.openrouterApiKey.trim()) return 'OpenRouter API key';
|
||||
}
|
||||
if (llmProvider === 'ollama') {
|
||||
if (!config.ollamaLlmModel.trim()) return 'Ollama LLM model name';
|
||||
}
|
||||
@@ -221,9 +235,11 @@ export function GraphitiStep({ onNext, onBack, onSkip }: GraphitiStepProps) {
|
||||
config.llmProvider === 'anthropic' ? config.anthropicApiKey :
|
||||
config.llmProvider === 'google' ? config.googleApiKey :
|
||||
config.llmProvider === 'groq' ? config.groqApiKey :
|
||||
config.llmProvider === 'openrouter' ? config.openrouterApiKey :
|
||||
config.llmProvider === 'azure_openai' ? config.azureOpenaiApiKey :
|
||||
config.llmProvider === 'ollama' ? '' : // Ollama doesn't need API key
|
||||
config.embeddingProvider === 'openai' ? config.openaiApiKey : '';
|
||||
config.embeddingProvider === 'openai' ? config.openaiApiKey :
|
||||
config.embeddingProvider === 'openrouter' ? config.openrouterApiKey : '';
|
||||
|
||||
const result = await window.electronAPI.testGraphitiConnection({
|
||||
dbPath: config.dbPath || undefined,
|
||||
@@ -303,6 +319,9 @@ export function GraphitiStep({ onNext, onBack, onSkip }: GraphitiStepProps) {
|
||||
if (config.groqApiKey.trim()) {
|
||||
settingsToSave.globalGroqApiKey = config.groqApiKey.trim();
|
||||
}
|
||||
if (config.openrouterApiKey.trim()) {
|
||||
settingsToSave.globalOpenRouterApiKey = config.openrouterApiKey.trim();
|
||||
}
|
||||
if (config.ollamaBaseUrl.trim()) {
|
||||
settingsToSave.ollamaBaseUrl = config.ollamaBaseUrl.trim();
|
||||
}
|
||||
@@ -311,11 +330,12 @@ export function GraphitiStep({ onNext, onBack, onSkip }: GraphitiStepProps) {
|
||||
|
||||
if (result?.success) {
|
||||
// Update local settings store with API key settings
|
||||
const storeUpdate: Partial<Pick<AppSettings, 'globalOpenAIApiKey' | 'globalAnthropicApiKey' | 'globalGoogleApiKey' | 'globalGroqApiKey' | 'ollamaBaseUrl'>> = {};
|
||||
const storeUpdate: Partial<Pick<AppSettings, 'globalOpenAIApiKey' | 'globalAnthropicApiKey' | 'globalGoogleApiKey' | 'globalGroqApiKey' | 'globalOpenRouterApiKey' | 'ollamaBaseUrl'>> = {};
|
||||
if (config.openaiApiKey.trim()) storeUpdate.globalOpenAIApiKey = config.openaiApiKey.trim();
|
||||
if (config.anthropicApiKey.trim()) storeUpdate.globalAnthropicApiKey = config.anthropicApiKey.trim();
|
||||
if (config.googleApiKey.trim()) storeUpdate.globalGoogleApiKey = config.googleApiKey.trim();
|
||||
if (config.groqApiKey.trim()) storeUpdate.globalGroqApiKey = config.groqApiKey.trim();
|
||||
if (config.openrouterApiKey.trim()) storeUpdate.globalOpenRouterApiKey = config.openrouterApiKey.trim();
|
||||
if (config.ollamaBaseUrl.trim()) storeUpdate.ollamaBaseUrl = config.ollamaBaseUrl.trim();
|
||||
updateSettings(storeUpdate);
|
||||
onNext();
|
||||
@@ -355,6 +375,7 @@ export function GraphitiStep({ onNext, onBack, onSkip }: GraphitiStepProps) {
|
||||
const needsVoyage = embeddingProvider === 'voyage';
|
||||
const needsGoogle = llmProvider === 'google' || embeddingProvider === 'google';
|
||||
const needsGroq = llmProvider === 'groq';
|
||||
const needsOpenRouter = llmProvider === 'openrouter' || embeddingProvider === 'openrouter';
|
||||
const needsOllama = llmProvider === 'ollama' || embeddingProvider === 'ollama';
|
||||
|
||||
return (
|
||||
@@ -606,6 +627,39 @@ export function GraphitiStep({ onNext, onBack, onSkip }: GraphitiStepProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* OpenRouter API Key */}
|
||||
{needsOpenRouter && (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="openrouter-key" className="text-sm font-medium text-foreground">
|
||||
OpenRouter API Key
|
||||
</Label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
id="openrouter-key"
|
||||
type={showApiKey['openrouter'] ? 'text' : 'password'}
|
||||
value={config.openrouterApiKey}
|
||||
onChange={(e) => setConfig(prev => ({ ...prev, openrouterApiKey: e.target.value }))}
|
||||
placeholder="sk-or-..."
|
||||
className="pr-10 font-mono text-sm"
|
||||
disabled={isSaving || isValidating}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => toggleShowApiKey('openrouter')}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
{showApiKey['openrouter'] ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Get your key from{' '}
|
||||
<a href="https://openrouter.ai/keys" target="_blank" rel="noopener noreferrer" className="text-primary hover:text-primary/80">
|
||||
OpenRouter Dashboard
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Ollama Settings */}
|
||||
{needsOllama && (
|
||||
<div className="space-y-3 p-3 rounded-md bg-muted/50">
|
||||
|
||||
@@ -176,12 +176,12 @@ export interface GraphitiConnectionTestResult {
|
||||
}
|
||||
|
||||
// Memory Provider Types
|
||||
// Embedding Providers: OpenAI, Voyage AI, Azure OpenAI, Ollama (local), Google
|
||||
// Embedding Providers: OpenAI, Voyage AI, Azure OpenAI, Ollama (local), Google, OpenRouter
|
||||
// Note: LLM provider removed - Claude SDK handles RAG queries
|
||||
export type GraphitiEmbeddingProvider = 'openai' | 'voyage' | 'azure_openai' | 'ollama' | 'google';
|
||||
export type GraphitiEmbeddingProvider = 'openai' | 'voyage' | 'azure_openai' | 'ollama' | 'google' | 'openrouter';
|
||||
|
||||
// Legacy type aliases for backward compatibility
|
||||
export type GraphitiLLMProvider = 'openai' | 'anthropic' | 'azure_openai' | 'ollama' | 'google' | 'groq';
|
||||
export type GraphitiLLMProvider = 'openai' | 'anthropic' | 'azure_openai' | 'ollama' | 'google' | 'groq' | 'openrouter';
|
||||
export type GraphitiProviderType = GraphitiLLMProvider;
|
||||
|
||||
export interface GraphitiProviderConfig {
|
||||
@@ -206,6 +206,12 @@ export interface GraphitiProviderConfig {
|
||||
googleApiKey?: string;
|
||||
googleEmbeddingModel?: string;
|
||||
|
||||
// OpenRouter (multi-provider aggregator)
|
||||
openrouterApiKey?: string;
|
||||
openrouterBaseUrl?: string; // Default: https://openrouter.ai/api/v1
|
||||
openrouterLlmModel?: string; // LLM model selection (e.g., 'anthropic/claude-3.5-sonnet')
|
||||
openrouterEmbeddingModel?: string;
|
||||
|
||||
// Ollama Embeddings (local, no API key required)
|
||||
ollamaBaseUrl?: string; // Default: http://localhost:11434
|
||||
ollamaEmbeddingModel?: string;
|
||||
|
||||
@@ -89,6 +89,7 @@ export interface AppSettings {
|
||||
globalAnthropicApiKey?: string;
|
||||
globalGoogleApiKey?: string;
|
||||
globalGroqApiKey?: string;
|
||||
globalOpenRouterApiKey?: string;
|
||||
// Graphiti LLM provider settings
|
||||
graphitiLlmProvider?: 'openai' | 'anthropic' | 'google' | 'groq' | 'ollama';
|
||||
ollamaBaseUrl?: string;
|
||||
|
||||
Reference in New Issue
Block a user