fix(api): enforce global ollama endpoint order
All checks were successful
Code Review / ai-code-review (push) Successful in 11s
CD Pipeline / tests (push) Successful in 5m13s
CD Pipeline / build-and-deploy (push) Successful in 3m31s
CD Pipeline / post-deploy-checks (push) Successful in 1m18s

This commit is contained in:
Your Name
2026-05-19 12:31:56 +08:00
parent 5fa0e1452c
commit 45cd55b2da
7 changed files with 359 additions and 228 deletions

View File

@@ -22,7 +22,7 @@ import httpx
import structlog
from src.services.model_registry import get_model as _get_model
from src.services.ollama_endpoint_resolver import resolve_ollama_endpoint
from src.services.ollama_endpoint_resolver import resolve_ollama_order
logger = structlog.get_logger(__name__)
@@ -98,7 +98,15 @@ class OllamaEmbeddingService:
P1 修復 (2026-03-29): 維度配置化,支援更多模型
"""
self._model = model
self._ollama_url = ollama_url or resolve_ollama_endpoint("embedding")
if ollama_url:
self._ollama_endpoints = ((ollama_url, "custom"),)
else:
self._ollama_endpoints = tuple(
(endpoint.url, endpoint.provider_name)
for endpoint in resolve_ollama_order("embedding")
if endpoint.url
)
self._ollama_url = self._ollama_endpoints[0][0] if self._ollama_endpoints else ""
self._timeout = timeout
self._default_dimension = default_dimension or self.MODEL_DIMENSIONS.get(
model, self.DEFAULT_DIMENSION
@@ -142,43 +150,65 @@ class OllamaEmbeddingService:
"""
client = await self._get_client()
try:
response = await client.post(
f"{self._ollama_url}/api/embeddings",
json={
"model": self._model,
"prompt": text,
},
)
response.raise_for_status()
last_error: Exception | None = None
for endpoint_url, provider_name in self._ollama_endpoints:
try:
response = await client.post(
f"{endpoint_url}/api/embeddings",
json={
"model": self._model,
"prompt": text,
},
)
response.raise_for_status()
data = response.json()
embedding = data.get("embedding", [])
data = response.json()
embedding = data.get("embedding", [])
# 更新維度快取
if self._dimension is None and embedding:
self._dimension = len(embedding)
logger.info(
"embedding_dimension_detected",
# 更新維度快取
if self._dimension is None and embedding:
self._dimension = len(embedding)
logger.info(
"embedding_dimension_detected",
model=self._model,
dimension=self._dimension,
provider=provider_name,
)
return embedding
except httpx.TimeoutException as e:
last_error = e
logger.error(
"embedding_timeout",
model=self._model,
dimension=self._dimension,
text_len=len(text),
provider=provider_name,
)
except httpx.HTTPStatusError as e:
last_error = e
logger.error(
"embedding_http_error",
status=e.response.status_code,
model=self._model,
provider=provider_name,
)
except Exception as e:
last_error = e
logger.error(
"embedding_error",
error=str(e),
model=self._model,
provider=provider_name,
)
return embedding
except httpx.TimeoutException as e:
logger.error("embedding_timeout", model=self._model, text_len=len(text))
raise EmbeddingError(f"Embedding timeout after {self._timeout}s") from e
except httpx.HTTPStatusError as e:
logger.error(
"embedding_http_error",
status=e.response.status_code,
model=self._model,
)
raise EmbeddingError(f"Ollama API error: {e.response.status_code}") from e
except Exception as e:
logger.error("embedding_error", error=str(e), model=self._model)
raise EmbeddingError(f"Embedding failed: {e}") from e
if isinstance(last_error, httpx.TimeoutException):
raise EmbeddingError(f"Embedding timeout after {self._timeout}s") from last_error
if isinstance(last_error, httpx.HTTPStatusError):
raise EmbeddingError(
f"Ollama API error: {last_error.response.status_code}"
) from last_error
raise EmbeddingError("Embedding failed on all Ollama endpoints") from last_error
async def embed_batch(
self,