feat(semantic_search): support local OpenAI-compatible embedding servers (#22)

Adds a LocalEmbedder that targets any OpenAI-compatible embedding
endpoint (Ollama, llama.cpp, vLLM, LM Studio, ...). Zero new
Python dependencies — reuses the existing openai SDK with a
custom base_url. Defaults to Ollama at http://localhost:11434/v1
with nomic-embed-text @ 768 dims; override via env vars for other
servers/models (e.g. bge-m3 @ 1024 dims for better Turkish).

Refactors the shared encode/similarity logic into a private base
class so OpenRouterEmbedder and LocalEmbedder don't duplicate ~50
lines. OpenRouter keeps its ranking headers; local sends none.

Adds get_embedder() factory selecting the provider based on
EMBEDDING_PROVIDER (local) or OPENROUTER_API_KEY presence, and
is_semantic_search_available() that returns True for either path.
mcp_server_main now uses these so the semantic_search tool is
exposed when only a local server is configured.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
saidsurucu
2026-05-03 01:50:47 +03:00
co-authored by Claude Opus 4.7
parent 42731a2c03
commit fb29146755
4 changed files with 227 additions and 95 deletions
+10 -8
View File
@@ -265,17 +265,18 @@ from bedesten_mcp_module.models import (
)
from bedesten_mcp_module.enums import BirimAdiEnum
# Semantic Search Module Imports (conditional based on OPENROUTER_API_KEY)
from semantic_search.embedder import is_openrouter_available
SEMANTIC_SEARCH_AVAILABLE = is_openrouter_available()
# Semantic Search Module Imports (enabled if any embedding provider is configured)
from semantic_search.embedder import is_semantic_search_available, is_local_embedding_configured
SEMANTIC_SEARCH_AVAILABLE = is_semantic_search_available()
if SEMANTIC_SEARCH_AVAILABLE:
from semantic_search.embedder import OpenRouterEmbedder
from semantic_search.embedder import get_embedder
from semantic_search.vector_store import VectorStore
from semantic_search.processor import DocumentProcessor
logger.info("Semantic search enabled (OPENROUTER_API_KEY found)")
provider = "local" if is_local_embedding_configured() else "openrouter"
logger.info(f"Semantic search enabled (provider={provider})")
else:
logger.info("Semantic search disabled (OPENROUTER_API_KEY not set)")
logger.info("Semantic search disabled (no embedding provider configured)")
from danistay_mcp_module.client import DanistayApiClient
from emsal_mcp_module.client import EmsalApiClient
@@ -1279,8 +1280,9 @@ YANLIŞ KULLANIM:
logger.info(f"Semantic search tool called with initial_keyword: {initial_keyword}, query: {query}")
try:
# Initialize components
embedder = OpenRouterEmbedder()
# Initialize components (provider chosen via EMBEDDING_PROVIDER /
# OPENROUTER_API_KEY env vars)
embedder = get_embedder()
vector_store = VectorStore(dimension=embedder.dimension)
processor = DocumentProcessor(chunk_size=1500, chunk_overlap=300)