fix(ai): 遷移 Ollama embedding 至 api embed
All checks were successful
CD Pipeline / deploy (push) Successful in 1m46s

This commit is contained in:
OoO
2026-04-30 10:24:15 +08:00
parent 7f695928c3
commit 72fa166729
11 changed files with 114 additions and 14 deletions

View File

@@ -21,6 +21,7 @@ OLLAMA_HOST = os.getenv('OLLAMA_HOST', 'http://192.168.0.111:11434')
DEFAULT_MODEL = os.getenv('OLLAMA_MODEL', 'llama3.1:8b') # 較快速的模型
TIMEOUT = int(os.getenv('OLLAMA_TIMEOUT', '120')) # 秒 - 2 分鐘
COPY_TIMEOUT = int(os.getenv('OLLAMA_COPY_TIMEOUT', '180')) # 文案生成專用超時 - 3 分鐘
EMBED_TIMEOUT = int(os.getenv('OLLAMA_EMBED_TIMEOUT', os.getenv('EMBEDDING_TIMEOUT', '45')))
@dataclass
@@ -505,8 +506,25 @@ class OllamaService:
return self.generate(prompt, system_prompt=system_prompt, temperature=0.5, timeout=120)
@staticmethod
def _extract_embedding(payload: Dict[str, Any]) -> List[float]:
"""Normalize Ollama /api/embed and legacy /api/embeddings response shapes."""
embeddings = payload.get("embeddings")
if isinstance(embeddings, list) and embeddings:
first = embeddings[0]
if isinstance(first, list):
return first
if all(isinstance(value, (int, float)) for value in embeddings):
return embeddings
embedding = payload.get("embedding")
if isinstance(embedding, list):
return embedding
return []
def generate_embedding(self, text: str, model: str = "bge-m3:latest",
host: str = None) -> List[float]:
host: str = None, timeout: int = None) -> List[float]:
"""
[ADR-007, Step 3] 呼叫 Ollama API 將文字轉換為向量 Embedding
@@ -516,22 +534,38 @@ class OllamaService:
避免 self.host 若指向公開 ollama.wooo.work 時回 401。
可透過 host 參數 override。
"""
target_host = host or os.getenv("EMBEDDING_HOST", "http://192.168.0.111:11434")
target_host = (host or os.getenv("EMBEDDING_HOST", "http://192.168.0.111:11434")).rstrip("/")
request_timeout = timeout or EMBED_TIMEOUT
try:
payload = {"model": model, "prompt": text}
payload = {"model": model, "input": text}
response = requests.post(
f"{target_host}/api/embeddings",
f"{target_host}/api/embed",
json=payload,
timeout=60,
timeout=request_timeout,
)
if response.status_code == 200:
data = response.json()
return data.get("embedding", [])
else:
vec = self._extract_embedding(response.json())
if vec:
return vec
logger.warning(f"Ollama Embed Empty Response @ {target_host}/api/embed")
elif response.status_code not in (404, 405):
logger.error(
f"Ollama Embed Error HTTP {response.status_code} @ {target_host}: {response.text[:200]}"
f"Ollama Embed Error HTTP {response.status_code} @ {target_host}/api/embed: {response.text[:200]}"
)
return []
# V-Fix: 舊 Ollama 相容;/api/embeddings 已 deprecated但仍是部分舊節點唯一可用路徑。
legacy_response = requests.post(
f"{target_host}/api/embeddings",
json={"model": model, "prompt": text},
timeout=request_timeout,
)
if legacy_response.status_code == 200:
return self._extract_embedding(legacy_response.json())
logger.error(
f"Ollama Embed Error HTTP {legacy_response.status_code} @ {target_host}/api/embeddings: {legacy_response.text[:200]}"
)
return []
except Exception as e:
logger.error(f"Ollama Embed Exception @ {target_host}: {e}")
return []