feat: add OrcaRouter embedder for hosted semantic search
Add a named OrcaRouterEmbedder mirroring the existing OpenRouterEmbedder: a production AI gateway that proxies 200+ models on one OpenAI-compatible endpoint (https://api.orcarouter.ai/v1). Selecting it is a one-line switch: set ORCAROUTER_API_KEY instead of OPENROUTER_API_KEY. - get_embedder() prefers OrcaRouter when ORCAROUTER_API_KEY is present - is_semantic_search_available() now also enables on OrcaRouter keys - document the new option in README (Alternatif 3) and .env.example Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: jinhao.song <jinhao.song@myflashcloud.com>
This commit is contained in:
+14
-2
@@ -75,11 +75,12 @@ JWT_SECRET_KEY=your_jwt_secret_key_here
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
# Embedding provider for the semantic_search tool.
|
# Embedding provider for the semantic_search tool.
|
||||||
# Pick exactly one of: OpenRouter (hosted) or Local (your own server).
|
# Pick exactly one of: OpenRouter (hosted), OrcaRouter (hosted), or Local.
|
||||||
|
|
||||||
# --- Option A: OpenRouter (hosted, default) -----------------------------------
|
# --- Option A: OpenRouter (hosted, default) -----------------------------------
|
||||||
# Get your API key from: https://openrouter.ai/keys
|
# Get your API key from: https://openrouter.ai/keys
|
||||||
# If neither this nor EMBEDDING_PROVIDER=local is set, semantic search is off.
|
# If neither this, nor ORCAROUTER_API_KEY, nor EMBEDDING_PROVIDER=local is set,
|
||||||
|
# semantic search is off.
|
||||||
OPENROUTER_API_KEY=sk-or-v1-your_openrouter_api_key_here
|
OPENROUTER_API_KEY=sk-or-v1-your_openrouter_api_key_here
|
||||||
|
|
||||||
# Optional: override the OpenRouter embedding model and dimension.
|
# Optional: override the OpenRouter embedding model and dimension.
|
||||||
@@ -89,6 +90,17 @@ OPENROUTER_API_KEY=sk-or-v1-your_openrouter_api_key_here
|
|||||||
# OPENROUTER_EMBEDDING_MODEL=google/gemini-embedding-001
|
# OPENROUTER_EMBEDDING_MODEL=google/gemini-embedding-001
|
||||||
# OPENROUTER_EMBEDDING_DIMENSION=3072
|
# OPENROUTER_EMBEDDING_DIMENSION=3072
|
||||||
|
|
||||||
|
# --- Option A2: OrcaRouter (hosted) ------------------------------------------
|
||||||
|
# OrcaRouter is a production AI gateway with one OpenAI-compatible endpoint
|
||||||
|
# (https://api.orcarouter.ai/v1). Get your API key from: https://www.orcarouter.ai
|
||||||
|
# Set ORCAROUTER_API_KEY instead of OPENROUTER_API_KEY to use it.
|
||||||
|
# ORCAROUTER_API_KEY=sk-orca-your_orcarouter_api_key_here
|
||||||
|
|
||||||
|
# Optional: override the OrcaRouter embedding model and dimension.
|
||||||
|
# Defaults: google/gemini-embedding-001 at 3072 dims (multilingual).
|
||||||
|
# ORCAROUTER_EMBEDDING_MODEL=google/gemini-embedding-001
|
||||||
|
# ORCAROUTER_EMBEDDING_DIMENSION=3072
|
||||||
|
|
||||||
# --- Option B: Local OpenAI-compatible server (no API key required) ----------
|
# --- Option B: Local OpenAI-compatible server (no API key required) ----------
|
||||||
# Recommended for Turkish: intfloat/multilingual-e5-large served by HuggingFace
|
# Recommended for Turkish: intfloat/multilingual-e5-large served by HuggingFace
|
||||||
# Text Embeddings Inference (TEI). One-line setup:
|
# Text Embeddings Inference (TEI). One-line setup:
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ Yargı MCP'yi Gemini CLI ile kullanmak için:
|
|||||||
Yargı MCP, **semantik arama** özelliği ile kararları anlamsal olarak sıralayabilir. Opsiyoneldir; iki yoldan biri yapılandırıldığında otomatik etkinleşir:
|
Yargı MCP, **semantik arama** özelliği ile kararları anlamsal olarak sıralayabilir. Opsiyoneldir; iki yoldan biri yapılandırıldığında otomatik etkinleşir:
|
||||||
|
|
||||||
- **Yerel** (önerilen, ücretsiz): kendi makinenizdeki OpenAI-uyumlu embedding sunucusu (HuggingFace TEI, llama.cpp, Ollama, vLLM, LM Studio…)
|
- **Yerel** (önerilen, ücretsiz): kendi makinenizdeki OpenAI-uyumlu embedding sunucusu (HuggingFace TEI, llama.cpp, Ollama, vLLM, LM Studio…)
|
||||||
- **Hosted**: OpenRouter API anahtarı
|
- **Hosted**: OpenRouter ya da [OrcaRouter](https://www.orcarouter.ai) API anahtarı
|
||||||
|
|
||||||
### Semantik Arama Nasıl Çalışır?
|
### Semantik Arama Nasıl Çalışır?
|
||||||
1. `initial_keyword` ile Bedesten API'den 100 karar çekilir
|
1. `initial_keyword` ile Bedesten API'den 100 karar çekilir
|
||||||
@@ -353,11 +353,25 @@ OPENROUTER_API_KEY=sk-or-v1-xxx...
|
|||||||
|
|
||||||
API anahtarınızı [openrouter.ai/keys](https://openrouter.ai/keys) adresinden alın. Varsayılan model `google/gemini-embedding-001` artık ücretli — ücretsiz bir model seçerseniz `OPENROUTER_EMBEDDING_MODEL`, `OPENROUTER_EMBEDDING_DIMENSION` ve uygun `EMBEDDING_PROMPT_STYLE` değerlerini birlikte ayarlayın.
|
API anahtarınızı [openrouter.ai/keys](https://openrouter.ai/keys) adresinden alın. Varsayılan model `google/gemini-embedding-001` artık ücretli — ücretsiz bir model seçerseniz `OPENROUTER_EMBEDDING_MODEL`, `OPENROUTER_EMBEDDING_DIMENSION` ve uygun `EMBEDDING_PROMPT_STYLE` değerlerini birlikte ayarlayın.
|
||||||
|
|
||||||
|
### Alternatif 3: OrcaRouter (hosted)
|
||||||
|
|
||||||
|
[OrcaRouter](https://www.orcarouter.ai), 200+ modeli tek OpenAI-uyumlu uçta toplayan bir üretim AI ağ geçididir (ağ geçidi seviyesinde, sıfır-güven AI ajan güvenliği de içerir). Mevcut SDK kodu `base_url` değiştirilerek aynen çalışır.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ORCAROUTER_API_KEY=sk-orca-xxx...
|
||||||
|
# İsteğe bağlı — varsayılan google/gemini-embedding-001 (3072 dim, çok dilli)
|
||||||
|
# ORCAROUTER_EMBEDDING_MODEL=...
|
||||||
|
# ORCAROUTER_EMBEDDING_DIMENSION=...
|
||||||
|
# EMBEDDING_PROMPT_STYLE=gemini # varsayılan
|
||||||
|
```
|
||||||
|
|
||||||
|
API anahtarınızı [www.orcarouter.ai](https://www.orcarouter.ai) adresinden alın. `OPENROUTER_API_KEY` yerine `ORCAROUTER_API_KEY` ayarlamanız yeterli — semantik arama aynı OpenAI-uyumlu akışı OrcaRouter ucu üzerinden kullanır.
|
||||||
|
|
||||||
### Yapılandırma Referansı
|
### Yapılandırma Referansı
|
||||||
|
|
||||||
| Env Var | Açıklama | Örnek |
|
| Env Var | Açıklama | Örnek |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `EMBEDDING_PROVIDER` | `local` ise yerel sunucu, boş ise OpenRouter | `local` |
|
| `EMBEDDING_PROVIDER` | `local` ise yerel sunucu, boş ise hosted (OpenRouter/OrcaRouter) | `local` |
|
||||||
| `EMBEDDING_PROMPT_STYLE` | `gemini` / `e5` / `raw` — modelin beklediği önek | `e5` |
|
| `EMBEDDING_PROMPT_STYLE` | `gemini` / `e5` / `raw` — modelin beklediği önek | `e5` |
|
||||||
| `LOCAL_EMBEDDING_BASE_URL` | Yerel sunucunun OpenAI-uyumlu URL'i | `http://localhost:8080/v1` |
|
| `LOCAL_EMBEDDING_BASE_URL` | Yerel sunucunun OpenAI-uyumlu URL'i | `http://localhost:8080/v1` |
|
||||||
| `LOCAL_EMBEDDING_MODEL` | Model adı | `intfloat/multilingual-e5-large` |
|
| `LOCAL_EMBEDDING_MODEL` | Model adı | `intfloat/multilingual-e5-large` |
|
||||||
@@ -365,6 +379,9 @@ API anahtarınızı [openrouter.ai/keys](https://openrouter.ai/keys) adresinden
|
|||||||
| `OPENROUTER_API_KEY` | OpenRouter anahtarı (sadece hosted için) | `sk-or-v1-…` |
|
| `OPENROUTER_API_KEY` | OpenRouter anahtarı (sadece hosted için) | `sk-or-v1-…` |
|
||||||
| `OPENROUTER_EMBEDDING_MODEL` | OpenRouter model id'si | `google/gemini-embedding-001` |
|
| `OPENROUTER_EMBEDDING_MODEL` | OpenRouter model id'si | `google/gemini-embedding-001` |
|
||||||
| `OPENROUTER_EMBEDDING_DIMENSION` | OpenRouter modelinin çıktı boyutu | `3072` |
|
| `OPENROUTER_EMBEDDING_DIMENSION` | OpenRouter modelinin çıktı boyutu | `3072` |
|
||||||
|
| `ORCAROUTER_API_KEY` | OrcaRouter anahtarı (sadece hosted için) | `sk-orca-…` |
|
||||||
|
| `ORCAROUTER_EMBEDDING_MODEL` | OrcaRouter model id'si | `google/gemini-embedding-001` |
|
||||||
|
| `ORCAROUTER_EMBEDDING_DIMENSION` | OrcaRouter modelinin çıktı boyutu | `3072` |
|
||||||
|
|
||||||
> 💡 **Not:** Hiçbir embedding sağlayıcı yapılandırılmazsa semantik arama aracı görünmez, diğer 28 araç normal şekilde çalışır.
|
> 💡 **Not:** Hiçbir embedding sağlayıcı yapılandırılmazsa semantik arama aracı görünmez, diğer 28 araç normal şekilde çalışır.
|
||||||
|
|
||||||
|
|||||||
+12
-2
@@ -266,14 +266,24 @@ from bedesten_mcp_module.models import (
|
|||||||
from bedesten_mcp_module.enums import BirimAdiEnum
|
from bedesten_mcp_module.enums import BirimAdiEnum
|
||||||
|
|
||||||
# Semantic Search Module Imports (enabled if any embedding provider is configured)
|
# Semantic Search Module Imports (enabled if any embedding provider is configured)
|
||||||
from semantic_search.embedder import is_semantic_search_available, is_local_embedding_configured
|
from semantic_search.embedder import (
|
||||||
|
is_semantic_search_available,
|
||||||
|
is_local_embedding_configured,
|
||||||
|
is_openrouter_available,
|
||||||
|
is_orcarouter_available,
|
||||||
|
)
|
||||||
SEMANTIC_SEARCH_AVAILABLE = is_semantic_search_available()
|
SEMANTIC_SEARCH_AVAILABLE = is_semantic_search_available()
|
||||||
|
|
||||||
if SEMANTIC_SEARCH_AVAILABLE:
|
if SEMANTIC_SEARCH_AVAILABLE:
|
||||||
from semantic_search.embedder import get_embedder
|
from semantic_search.embedder import get_embedder
|
||||||
from semantic_search.vector_store import VectorStore
|
from semantic_search.vector_store import VectorStore
|
||||||
from semantic_search.processor import DocumentProcessor
|
from semantic_search.processor import DocumentProcessor
|
||||||
provider = "local" if is_local_embedding_configured() else "openrouter"
|
if is_local_embedding_configured():
|
||||||
|
provider = "local"
|
||||||
|
elif is_orcarouter_available():
|
||||||
|
provider = "orcarouter"
|
||||||
|
elif is_openrouter_available():
|
||||||
|
provider = "openrouter"
|
||||||
logger.info(f"Semantic search enabled (provider={provider})")
|
logger.info(f"Semantic search enabled (provider={provider})")
|
||||||
else:
|
else:
|
||||||
logger.info("Semantic search disabled (no embedding provider configured)")
|
logger.info("Semantic search disabled (no embedding provider configured)")
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
from .embedder import (
|
from .embedder import (
|
||||||
OpenRouterEmbedder,
|
OpenRouterEmbedder,
|
||||||
|
OrcaRouterEmbedder,
|
||||||
LocalEmbedder,
|
LocalEmbedder,
|
||||||
get_embedder,
|
get_embedder,
|
||||||
is_openrouter_available,
|
is_openrouter_available,
|
||||||
|
is_orcarouter_available,
|
||||||
is_local_embedding_configured,
|
is_local_embedding_configured,
|
||||||
is_semantic_search_available,
|
is_semantic_search_available,
|
||||||
)
|
)
|
||||||
@@ -13,9 +15,11 @@ from .processor import DocumentProcessor
|
|||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'OpenRouterEmbedder',
|
'OpenRouterEmbedder',
|
||||||
|
'OrcaRouterEmbedder',
|
||||||
'LocalEmbedder',
|
'LocalEmbedder',
|
||||||
'get_embedder',
|
'get_embedder',
|
||||||
'is_openrouter_available',
|
'is_openrouter_available',
|
||||||
|
'is_orcarouter_available',
|
||||||
'is_local_embedding_configured',
|
'is_local_embedding_configured',
|
||||||
'is_semantic_search_available',
|
'is_semantic_search_available',
|
||||||
'VectorStore',
|
'VectorStore',
|
||||||
|
|||||||
@@ -64,6 +64,11 @@ def is_openrouter_available() -> bool:
|
|||||||
return bool(os.getenv("OPENROUTER_API_KEY"))
|
return bool(os.getenv("OPENROUTER_API_KEY"))
|
||||||
|
|
||||||
|
|
||||||
|
def is_orcarouter_available() -> bool:
|
||||||
|
"""Check if OrcaRouter API key is available."""
|
||||||
|
return bool(os.getenv("ORCAROUTER_API_KEY"))
|
||||||
|
|
||||||
|
|
||||||
def is_local_embedding_configured() -> bool:
|
def is_local_embedding_configured() -> bool:
|
||||||
"""Check if the user opted into a local embedding endpoint."""
|
"""Check if the user opted into a local embedding endpoint."""
|
||||||
return os.getenv("EMBEDDING_PROVIDER", "").strip().lower() == "local"
|
return os.getenv("EMBEDDING_PROVIDER", "").strip().lower() == "local"
|
||||||
@@ -71,7 +76,11 @@ def is_local_embedding_configured() -> bool:
|
|||||||
|
|
||||||
def is_semantic_search_available() -> bool:
|
def is_semantic_search_available() -> bool:
|
||||||
"""Returns True if any embedding provider is configured."""
|
"""Returns True if any embedding provider is configured."""
|
||||||
return is_local_embedding_configured() or is_openrouter_available()
|
return (
|
||||||
|
is_local_embedding_configured()
|
||||||
|
or is_openrouter_available()
|
||||||
|
or is_orcarouter_available()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _coerce_dimension(value, env_name: str, default: int) -> int:
|
def _coerce_dimension(value, env_name: str, default: int) -> int:
|
||||||
@@ -261,6 +270,59 @@ class OpenRouterEmbedder(_BaseOpenAICompatibleEmbedder):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OrcaRouterEmbedder(_BaseOpenAICompatibleEmbedder):
|
||||||
|
"""
|
||||||
|
Embedder using OrcaRouter's OpenAI-compatible embedding API.
|
||||||
|
|
||||||
|
OrcaRouter is a production AI gateway that proxies 200+ models on a single
|
||||||
|
OpenAI-compatible endpoint. The model and dimension are configurable so
|
||||||
|
users can pick any embedding model the gateway routes. Configuration
|
||||||
|
precedence: explicit constructor args > environment variables > defaults.
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
ORCAROUTER_API_KEY (required): OrcaRouter credential (sk-orca-...)
|
||||||
|
ORCAROUTER_EMBEDDING_MODEL (optional): override the embedding model id
|
||||||
|
ORCAROUTER_EMBEDDING_DIMENSION (optional): override the vector size
|
||||||
|
|
||||||
|
Defaults: ``google/gemini-embedding-001`` at 3072 dimensions (multilingual,
|
||||||
|
matches the OpenRouter default — good for Turkish legal text).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
model: Optional[str] = None,
|
||||||
|
dimension: Optional[int] = None,
|
||||||
|
prompt_style: Optional[str] = None,
|
||||||
|
):
|
||||||
|
api_key = os.getenv("ORCAROUTER_API_KEY")
|
||||||
|
if not api_key:
|
||||||
|
raise ValueError("ORCAROUTER_API_KEY environment variable is not set")
|
||||||
|
|
||||||
|
try:
|
||||||
|
from openai import OpenAI
|
||||||
|
except ImportError:
|
||||||
|
raise ImportError("openai package is required. Install with: pip install openai")
|
||||||
|
|
||||||
|
self.client = OpenAI(
|
||||||
|
base_url="https://api.orcarouter.ai/v1",
|
||||||
|
api_key=api_key,
|
||||||
|
)
|
||||||
|
self.model = model or os.getenv("ORCAROUTER_EMBEDDING_MODEL") or DEFAULT_MODEL
|
||||||
|
self.dimension = _coerce_dimension(
|
||||||
|
dimension if dimension is not None else os.getenv("ORCAROUTER_EMBEDDING_DIMENSION"),
|
||||||
|
"ORCAROUTER_EMBEDDING_DIMENSION",
|
||||||
|
DEFAULT_DIMENSION,
|
||||||
|
)
|
||||||
|
# Same gemini-style default as the OpenRouter embedder — matches the
|
||||||
|
# multilingual google/gemini-embedding-001 default model.
|
||||||
|
self.prompt_style = _resolve_prompt_style(prompt_style, "gemini")
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"OrcaRouter Embedder initialized with model: {self.model} "
|
||||||
|
f"(dimension={self.dimension}, prompt_style={self.prompt_style})"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class LocalEmbedder(_BaseOpenAICompatibleEmbedder):
|
class LocalEmbedder(_BaseOpenAICompatibleEmbedder):
|
||||||
"""
|
"""
|
||||||
Embedder for a local OpenAI-compatible embedding server — Ollama,
|
Embedder for a local OpenAI-compatible embedding server — Ollama,
|
||||||
@@ -332,17 +394,22 @@ def get_embedder():
|
|||||||
Factory that picks the embedder based on EMBEDDING_PROVIDER.
|
Factory that picks the embedder based on EMBEDDING_PROVIDER.
|
||||||
|
|
||||||
- ``EMBEDDING_PROVIDER=local`` -> ``LocalEmbedder``
|
- ``EMBEDDING_PROVIDER=local`` -> ``LocalEmbedder``
|
||||||
|
- ``ORCAROUTER_API_KEY`` set -> ``OrcaRouterEmbedder``
|
||||||
- otherwise -> ``OpenRouterEmbedder`` (requires OPENROUTER_API_KEY)
|
- otherwise -> ``OpenRouterEmbedder`` (requires OPENROUTER_API_KEY)
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValueError: If no provider is configured (neither local nor OpenRouter).
|
ValueError: If no provider is configured (neither local, OpenRouter,
|
||||||
|
nor OrcaRouter).
|
||||||
"""
|
"""
|
||||||
if is_local_embedding_configured():
|
if is_local_embedding_configured():
|
||||||
return LocalEmbedder()
|
return LocalEmbedder()
|
||||||
|
if is_orcarouter_available():
|
||||||
|
return OrcaRouterEmbedder()
|
||||||
if is_openrouter_available():
|
if is_openrouter_available():
|
||||||
return OpenRouterEmbedder()
|
return OpenRouterEmbedder()
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"No embedding provider configured. Set OPENROUTER_API_KEY for hosted "
|
"No embedding provider configured. Set OPENROUTER_API_KEY or "
|
||||||
"embeddings, or EMBEDDING_PROVIDER=local (with LOCAL_EMBEDDING_* "
|
"ORCAROUTER_API_KEY for hosted embeddings, or EMBEDDING_PROVIDER=local "
|
||||||
"env vars) for a local OpenAI-compatible server like Ollama."
|
"(with LOCAL_EMBEDDING_* env vars) for a local OpenAI-compatible "
|
||||||
|
"server like Ollama."
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user