fix(ai): 遷移 Ollama embedding 至 api embed
All checks were successful
CD Pipeline / deploy (push) Successful in 1m46s
All checks were successful
CD Pipeline / deploy (push) Successful in 1m46s
This commit is contained in:
@@ -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 []
|
||||
|
||||
Reference in New Issue
Block a user