Merge b7f1716c58 into 56f6d09df0
This commit is contained in:
commit
debb7bf330
7 changed files with 280 additions and 1 deletions
|
|
@ -1,8 +1,11 @@
|
||||||
from .client import EmbedderClient
|
from .client import EmbedderClient
|
||||||
from .openai import OpenAIEmbedder, OpenAIEmbedderConfig
|
from .openai import OpenAIEmbedder, OpenAIEmbedderConfig
|
||||||
|
from .ollama import OllamaEmbedder, OllamaEmbedderConfig
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'EmbedderClient',
|
'EmbedderClient',
|
||||||
'OpenAIEmbedder',
|
'OpenAIEmbedder',
|
||||||
'OpenAIEmbedderConfig',
|
'OpenAIEmbedderConfig',
|
||||||
|
'OllamaEmbedder',
|
||||||
|
'OllamaEmbedderConfig',
|
||||||
]
|
]
|
||||||
|
|
|
||||||
131
graphiti_core/embedder/ollama.py
Normal file
131
graphiti_core/embedder/ollama.py
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
"""
|
||||||
|
Copyright 2024, Zep Software, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from collections.abc import Iterable
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
from pydantic import Field
|
||||||
|
|
||||||
|
from .client import EmbedderClient, EmbedderConfig
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
DEFAULT_EMBEDDING_MODEL = 'nomic-embed-text'
|
||||||
|
DEFAULT_BASE_URL = 'http://localhost:11434'
|
||||||
|
|
||||||
|
|
||||||
|
class OllamaEmbedderConfig(EmbedderConfig):
|
||||||
|
embedding_model: str = Field(default=DEFAULT_EMBEDDING_MODEL)
|
||||||
|
base_url: str = Field(default=DEFAULT_BASE_URL)
|
||||||
|
|
||||||
|
|
||||||
|
class OllamaEmbedder(EmbedderClient):
|
||||||
|
"""
|
||||||
|
Ollama Embedder Client
|
||||||
|
|
||||||
|
Uses Ollama's native API endpoint for embeddings.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, config: OllamaEmbedderConfig | None = None):
|
||||||
|
if config is None:
|
||||||
|
config = OllamaEmbedderConfig()
|
||||||
|
self.config = config
|
||||||
|
self.base_url = config.base_url.rstrip('/')
|
||||||
|
self.embed_url = f"{self.base_url}/api/embed"
|
||||||
|
|
||||||
|
async def create(
|
||||||
|
self, input_data: str | list[str] | Iterable[int] | Iterable[Iterable[int]]
|
||||||
|
) -> list[float]:
|
||||||
|
"""
|
||||||
|
Create embeddings for the given input data using Ollama's embedding model.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
input_data: The input data to create embeddings for. Can be a string, list of strings,
|
||||||
|
or an iterable of integers or iterables of integers.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A list of floats representing the embedding vector.
|
||||||
|
"""
|
||||||
|
# Convert input to string if needed
|
||||||
|
if isinstance(input_data, str):
|
||||||
|
text_input = input_data
|
||||||
|
elif isinstance(input_data, list) and len(input_data) > 0:
|
||||||
|
if isinstance(input_data[0], str):
|
||||||
|
# For list of strings, take the first one for single embedding
|
||||||
|
text_input = input_data[0]
|
||||||
|
else:
|
||||||
|
# Convert other types to string
|
||||||
|
text_input = str(input_data[0])
|
||||||
|
else:
|
||||||
|
text_input = str(input_data)
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"model": self.config.embedding_model,
|
||||||
|
"input": text_input
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
response = await client.post(
|
||||||
|
self.embed_url,
|
||||||
|
json=payload,
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
timeout=30.0
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
error_text = response.text
|
||||||
|
raise Exception(f"Ollama API error {response.status_code}: {error_text}")
|
||||||
|
|
||||||
|
result = response.json()
|
||||||
|
|
||||||
|
if "embeddings" not in result:
|
||||||
|
raise Exception(f"No embeddings in response: {result}")
|
||||||
|
|
||||||
|
embeddings = result["embeddings"]
|
||||||
|
if not embeddings or len(embeddings) == 0:
|
||||||
|
raise Exception("Empty embeddings returned")
|
||||||
|
|
||||||
|
# Return the first embedding, truncated to the configured dimension
|
||||||
|
embedding = embeddings[0]
|
||||||
|
return embedding[: self.config.embedding_dim]
|
||||||
|
|
||||||
|
except httpx.HTTPStatusError as e:
|
||||||
|
logger.error(f"HTTP error creating Ollama embedding: {e.response.status_code} - {e.response.text}")
|
||||||
|
raise Exception(f"Ollama API error {e.response.status_code}: {e.response.text}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error creating Ollama embedding: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
async def create_batch(self, input_data_list: list[str]) -> list[list[float]]:
|
||||||
|
"""
|
||||||
|
Create batch embeddings using Ollama's embedding model.
|
||||||
|
|
||||||
|
Note: Ollama doesn't support batch embeddings natively, so we process them sequentially.
|
||||||
|
"""
|
||||||
|
embeddings = []
|
||||||
|
|
||||||
|
for text in input_data_list:
|
||||||
|
try:
|
||||||
|
embedding = await self.create(text)
|
||||||
|
embeddings.append(embedding)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error creating embedding for text '{text[:50]}...': {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
return embeddings
|
||||||
|
|
@ -23,6 +23,17 @@ MODEL_NAME=gpt-4.1-mini
|
||||||
# Optional: Only needed for non-standard OpenAI endpoints
|
# Optional: Only needed for non-standard OpenAI endpoints
|
||||||
# OPENAI_BASE_URL=https://api.openai.com/v1
|
# OPENAI_BASE_URL=https://api.openai.com/v1
|
||||||
|
|
||||||
|
# Embedder Configuration
|
||||||
|
# Provider is auto-detected based on configuration:
|
||||||
|
# - Azure: if AZURE_OPENAI_EMBEDDING_ENDPOINT is set
|
||||||
|
# - Ollama: if USE_OLLAMA_FOR_EMBEDDER is set to true
|
||||||
|
# - OpenAI: default (no additional config needed)
|
||||||
|
# USE_OLLAMA_FOR_EMBEDDER=true # Set this to true to use Ollama
|
||||||
|
# OLLAMA_EMBEDDER_API_KEY=ollama # Ollama API key (optional, defaults to 'ollama')
|
||||||
|
# OLLAMA_EMBEDDER_BASE_URL=http://localhost:11434 # Ollama base URL (when using Ollama)
|
||||||
|
# OLLAMA_EMBEDDER_MODEL_NAME=nomic-embed-text # Ollama embedding model to use
|
||||||
|
# OLLAMA_EMBEDDER_DIMENSION=768 # Ollama embedding dimension (model-specific)
|
||||||
|
|
||||||
# Optional: Group ID for namespacing graph data
|
# Optional: Group ID for namespacing graph data
|
||||||
# GROUP_ID=my_project
|
# GROUP_ID=my_project
|
||||||
|
|
||||||
|
|
|
||||||
45
mcp_server/.env.example.gemini_ollama
Normal file
45
mcp_server/.env.example.gemini_ollama
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
# Graphiti MCP Server Environment Configuration
|
||||||
|
|
||||||
|
# Neo4j Database Configuration
|
||||||
|
# These settings are used to connect to your Neo4j database
|
||||||
|
NEO4J_URI=bolt://localhost:7687
|
||||||
|
NEO4J_USER=neo4j
|
||||||
|
NEO4J_PASSWORD=demodemo
|
||||||
|
|
||||||
|
# OpenAI API Configuration
|
||||||
|
# Required for LLM operations
|
||||||
|
OPENAI_API_KEY=your_gemini_api_key_here
|
||||||
|
MODEL_NAME=gemini-2.5-flash
|
||||||
|
SMALL_MODEL_NAME=gemini-2.5-flash
|
||||||
|
|
||||||
|
# Optional: Only needed for non-standard OpenAI endpoints
|
||||||
|
OPENAI_BASE_URL=https://generativelanguage.googleapis.com/v1beta
|
||||||
|
|
||||||
|
# Embedder Configuration
|
||||||
|
# Optional: Separate API key and URL for embedder (falls back to OPENAI_API_KEY and OPENAI_BASE_URL if not set)
|
||||||
|
# Note: OpenRouter does not support embeddings API, using Ollama as free alternative
|
||||||
|
USE_OLLAMA_FOR_EMBEDDER=true
|
||||||
|
OLLAMA_EMBEDDER_API_KEY=ollama
|
||||||
|
OLLAMA_EMBEDDER_BASE_URL=http://localhost:11434
|
||||||
|
OLLAMA_EMBEDDER_MODEL_NAME=nomic-embed-text
|
||||||
|
OLLAMA_EMBEDDER_DIMENSION=768
|
||||||
|
|
||||||
|
# Optional: Group ID for namespacing graph data
|
||||||
|
# GROUP_ID=my_project
|
||||||
|
|
||||||
|
# Optional: Path configuration for Docker
|
||||||
|
# PATH=/root/.local/bin:${PATH}
|
||||||
|
|
||||||
|
# Optional: Memory settings for Neo4j (used in Docker Compose)
|
||||||
|
# NEO4J_server_memory_heap_initial__size=512m
|
||||||
|
# NEO4J_server_memory_heap_max__size=1G
|
||||||
|
# NEO4J_server_memory_pagecache_size=512m
|
||||||
|
|
||||||
|
# Azure OpenAI configuration
|
||||||
|
# Optional: Only needed for Azure OpenAI endpoints
|
||||||
|
# AZURE_OPENAI_ENDPOINT=your_azure_openai_endpoint_here
|
||||||
|
# AZURE_OPENAI_API_VERSION=2025-01-01-preview
|
||||||
|
# AZURE_OPENAI_DEPLOYMENT_NAME=gpt-4o-gpt-4o-mini-deployment
|
||||||
|
# AZURE_OPENAI_EMBEDDING_API_VERSION=2023-05-15
|
||||||
|
# AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME=text-embedding-3-large-deployment
|
||||||
|
# AZURE_OPENAI_USE_MANAGED_IDENTITY=false
|
||||||
44
mcp_server/.env.example.openrouter_ollama
Normal file
44
mcp_server/.env.example.openrouter_ollama
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Graphiti MCP Server Environment Configuration
|
||||||
|
|
||||||
|
# Neo4j Database Configuration
|
||||||
|
# These settings are used to connect to your Neo4j database
|
||||||
|
NEO4J_URI=bolt://localhost:7687
|
||||||
|
NEO4J_USER=neo4j
|
||||||
|
NEO4J_PASSWORD=demodemo
|
||||||
|
|
||||||
|
# OpenAI API Configuration
|
||||||
|
# Required for LLM operations
|
||||||
|
OPENAI_API_KEY=your_open_router_api_key_here
|
||||||
|
MODEL_NAME=gpt-4.1-mini
|
||||||
|
|
||||||
|
# Optional: Only needed for non-standard OpenAI endpoints
|
||||||
|
OPENAI_BASE_URL=https://openrouter.ai/api/v1
|
||||||
|
|
||||||
|
# Embedder Configuration
|
||||||
|
# Optional: Separate API key and URL for embedder (falls back to OPENAI_API_KEY and OPENAI_BASE_URL if not set)
|
||||||
|
# Note: OpenRouter does not support embeddings API, using Ollama as free alternative
|
||||||
|
USE_OLLAMA_FOR_EMBEDDER=true
|
||||||
|
OLLAMA_EMBEDDER_API_KEY=ollama
|
||||||
|
OLLAMA_EMBEDDER_BASE_URL=http://localhost:11434
|
||||||
|
OLLAMA_EMBEDDER_MODEL_NAME=nomic-embed-text
|
||||||
|
OLLAMA_EMBEDDER_DIMENSION=768
|
||||||
|
|
||||||
|
# Optional: Group ID for namespacing graph data
|
||||||
|
# GROUP_ID=my_project
|
||||||
|
|
||||||
|
# Optional: Path configuration for Docker
|
||||||
|
# PATH=/root/.local/bin:${PATH}
|
||||||
|
|
||||||
|
# Optional: Memory settings for Neo4j (used in Docker Compose)
|
||||||
|
# NEO4J_server_memory_heap_initial__size=512m
|
||||||
|
# NEO4J_server_memory_heap_max__size=1G
|
||||||
|
# NEO4J_server_memory_pagecache_size=512m
|
||||||
|
|
||||||
|
# Azure OpenAI configuration
|
||||||
|
# Optional: Only needed for Azure OpenAI endpoints
|
||||||
|
# AZURE_OPENAI_ENDPOINT=your_azure_openai_endpoint_here
|
||||||
|
# AZURE_OPENAI_API_VERSION=2025-01-01-preview
|
||||||
|
# AZURE_OPENAI_DEPLOYMENT_NAME=gpt-4o-gpt-4o-mini-deployment
|
||||||
|
# AZURE_OPENAI_EMBEDDING_API_VERSION=2023-05-15
|
||||||
|
# AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME=text-embedding-3-large-deployment
|
||||||
|
# AZURE_OPENAI_USE_MANAGED_IDENTITY=false
|
||||||
|
|
@ -102,6 +102,11 @@ The server supports both Neo4j and FalkorDB as database backends. Use the `DATAB
|
||||||
- `MODEL_NAME`: OpenAI model name to use for LLM operations.
|
- `MODEL_NAME`: OpenAI model name to use for LLM operations.
|
||||||
- `SMALL_MODEL_NAME`: OpenAI model name to use for smaller LLM operations.
|
- `SMALL_MODEL_NAME`: OpenAI model name to use for smaller LLM operations.
|
||||||
- `LLM_TEMPERATURE`: Temperature for LLM responses (0.0-2.0).
|
- `LLM_TEMPERATURE`: Temperature for LLM responses (0.0-2.0).
|
||||||
|
- `USE_OLLAMA_FOR_EMBEDDER`: Set to `true` to use Ollama for embeddings (auto-detects Ollama provider)
|
||||||
|
- `OLLAMA_EMBEDDER_API_KEY`: Ollama API key (optional, defaults to 'ollama')
|
||||||
|
- `OLLAMA_EMBEDDER_BASE_URL`: Ollama base URL for embedder API (when using Ollama)
|
||||||
|
- `OLLAMA_EMBEDDER_MODEL_NAME`: Ollama embedding model name
|
||||||
|
- `OLLAMA_EMBEDDER_DIMENSION`: Ollama embedding dimension
|
||||||
- `AZURE_OPENAI_ENDPOINT`: Optional Azure OpenAI LLM endpoint URL
|
- `AZURE_OPENAI_ENDPOINT`: Optional Azure OpenAI LLM endpoint URL
|
||||||
- `AZURE_OPENAI_DEPLOYMENT_NAME`: Optional Azure OpenAI LLM deployment name
|
- `AZURE_OPENAI_DEPLOYMENT_NAME`: Optional Azure OpenAI LLM deployment name
|
||||||
- `AZURE_OPENAI_API_VERSION`: Optional Azure OpenAI LLM API version
|
- `AZURE_OPENAI_API_VERSION`: Optional Azure OpenAI LLM API version
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ from graphiti_core.edges import EntityEdge
|
||||||
from graphiti_core.embedder.azure_openai import AzureOpenAIEmbedderClient
|
from graphiti_core.embedder.azure_openai import AzureOpenAIEmbedderClient
|
||||||
from graphiti_core.embedder.client import EmbedderClient
|
from graphiti_core.embedder.client import EmbedderClient
|
||||||
from graphiti_core.embedder.openai import OpenAIEmbedder, OpenAIEmbedderConfig
|
from graphiti_core.embedder.openai import OpenAIEmbedder, OpenAIEmbedderConfig
|
||||||
|
from graphiti_core.embedder.ollama import OllamaEmbedder, OllamaEmbedderConfig
|
||||||
from graphiti_core.llm_client import LLMClient
|
from graphiti_core.llm_client import LLMClient
|
||||||
from graphiti_core.llm_client.azure_openai_client import AzureOpenAILLMClient
|
from graphiti_core.llm_client.azure_openai_client import AzureOpenAILLMClient
|
||||||
from graphiti_core.llm_client.config import LLMConfig
|
from graphiti_core.llm_client.config import LLMConfig
|
||||||
|
|
@ -360,6 +361,7 @@ class GraphitiEmbedderConfig(BaseModel):
|
||||||
azure_openai_deployment_name: str | None = None
|
azure_openai_deployment_name: str | None = None
|
||||||
azure_openai_api_version: str | None = None
|
azure_openai_api_version: str | None = None
|
||||||
azure_openai_use_managed_identity: bool = False
|
azure_openai_use_managed_identity: bool = False
|
||||||
|
use_ollama_for_embedder: bool = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_env(cls) -> 'GraphitiEmbedderConfig':
|
def from_env(cls) -> 'GraphitiEmbedderConfig':
|
||||||
|
|
@ -369,6 +371,13 @@ class GraphitiEmbedderConfig(BaseModel):
|
||||||
model_env = os.environ.get('EMBEDDER_MODEL_NAME', '')
|
model_env = os.environ.get('EMBEDDER_MODEL_NAME', '')
|
||||||
model = model_env if model_env.strip() else DEFAULT_EMBEDDER_MODEL
|
model = model_env if model_env.strip() else DEFAULT_EMBEDDER_MODEL
|
||||||
|
|
||||||
|
# Get embedder-specific API key and base URL, fallback to general OpenAI settings
|
||||||
|
|
||||||
|
# Detect provider based on configuration (similar to Azure pattern)
|
||||||
|
use_ollama_for_embedder = (
|
||||||
|
os.environ.get('USE_OLLAMA_FOR_EMBEDDER', 'false').lower() == 'true'
|
||||||
|
)
|
||||||
|
|
||||||
azure_openai_endpoint = os.environ.get('AZURE_OPENAI_EMBEDDING_ENDPOINT', None)
|
azure_openai_endpoint = os.environ.get('AZURE_OPENAI_EMBEDDING_ENDPOINT', None)
|
||||||
azure_openai_api_version = os.environ.get('AZURE_OPENAI_EMBEDDING_API_VERSION', None)
|
azure_openai_api_version = os.environ.get('AZURE_OPENAI_EMBEDDING_API_VERSION', None)
|
||||||
azure_openai_deployment_name = os.environ.get(
|
azure_openai_deployment_name = os.environ.get(
|
||||||
|
|
@ -405,11 +414,19 @@ class GraphitiEmbedderConfig(BaseModel):
|
||||||
api_key=api_key,
|
api_key=api_key,
|
||||||
azure_openai_api_version=azure_openai_api_version,
|
azure_openai_api_version=azure_openai_api_version,
|
||||||
azure_openai_deployment_name=azure_openai_deployment_name,
|
azure_openai_deployment_name=azure_openai_deployment_name,
|
||||||
|
use_ollama_for_embedder=False,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
if use_ollama_for_embedder:
|
||||||
|
api_key_env = os.environ.get("OLLAMA_EMBEDDER_API_KEY")
|
||||||
|
api_key = api_key_env if api_key_env else 'ollama'
|
||||||
|
logger.info(f'ollama api_key: {api_key}')
|
||||||
|
else:
|
||||||
|
api_key = os.environ.get("OPENAI_API_KEY")
|
||||||
return cls(
|
return cls(
|
||||||
model=model,
|
model=model,
|
||||||
api_key=os.environ.get('OPENAI_API_KEY'),
|
api_key=api_key,
|
||||||
|
use_ollama_for_embedder=use_ollama_for_embedder,
|
||||||
)
|
)
|
||||||
|
|
||||||
def create_client(self) -> EmbedderClient | None:
|
def create_client(self) -> EmbedderClient | None:
|
||||||
|
|
@ -441,6 +458,29 @@ class GraphitiEmbedderConfig(BaseModel):
|
||||||
else:
|
else:
|
||||||
logger.error('OPENAI_API_KEY must be set when using Azure OpenAI API')
|
logger.error('OPENAI_API_KEY must be set when using Azure OpenAI API')
|
||||||
return None
|
return None
|
||||||
|
elif self.use_ollama_for_embedder:
|
||||||
|
|
||||||
|
base_url_env = os.environ.get('OLLAMA_EMBEDDER_BASE_URL')
|
||||||
|
base_url = base_url_env if base_url_env else 'http://localhost:11434'
|
||||||
|
|
||||||
|
model_env = os.environ.get('OLLAMA_EMBEDDER_MODEL_NAME')
|
||||||
|
model = model_env if model_env else 'nomic-embed-text'
|
||||||
|
|
||||||
|
# Get embedding dimension from environment
|
||||||
|
embedding_dim_env = os.environ.get('OLLAMA_EMBEDDER_DIMENSION')
|
||||||
|
embedding_dim = int(embedding_dim_env) if embedding_dim_env else 768
|
||||||
|
|
||||||
|
logger.info(f'ollama model: {model}')
|
||||||
|
logger.info(f'ollama base_url: {base_url}')
|
||||||
|
logger.info(f'ollama embedding_dim: {embedding_dim}')
|
||||||
|
|
||||||
|
# Ollama API setup
|
||||||
|
ollama_config = OllamaEmbedderConfig(
|
||||||
|
embedding_model=model,
|
||||||
|
base_url=base_url,
|
||||||
|
embedding_dim=embedding_dim # nomic-embed-text default
|
||||||
|
)
|
||||||
|
return OllamaEmbedder(config=ollama_config)
|
||||||
else:
|
else:
|
||||||
# OpenAI API setup
|
# OpenAI API setup
|
||||||
if not self.api_key:
|
if not self.api_key:
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue