V10.417 protect embedding fallback routing
All checks were successful
CD Pipeline / deploy (push) Successful in 1m4s
All checks were successful
CD Pipeline / deploy (push) Successful in 1m4s
This commit is contained in:
@@ -82,7 +82,7 @@ def test_process_one_embedding_writes_signature(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
learning.ollama_service,
|
||||
"generate_embedding",
|
||||
lambda text, model="bge-m3:latest": [0.1] * 1024,
|
||||
lambda text, model="bge-m3:latest", **_kwargs: [0.1] * 1024,
|
||||
)
|
||||
|
||||
ok = learning._process_one_embedding(
|
||||
|
||||
@@ -24,7 +24,11 @@ def test_generate_embedding_uses_current_embed_endpoint(monkeypatch):
|
||||
|
||||
assert vec == [0.1, 0.2, 0.3]
|
||||
assert calls == [
|
||||
("http://ollama/api/embed", {"model": "bge-m3:latest", "input": "hello"}, 7),
|
||||
(
|
||||
"http://ollama/api/embed",
|
||||
{"model": "bge-m3:latest", "input": "hello", "keep_alive": "1m"},
|
||||
7,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@@ -43,10 +47,42 @@ def test_generate_embedding_falls_back_to_legacy_embeddings_endpoint(monkeypatch
|
||||
|
||||
assert vec == [0.4, 0.5]
|
||||
assert calls == [
|
||||
("http://ollama/api/embed", {"model": "bge-m3:latest", "input": "hello"}, 9),
|
||||
(
|
||||
"http://ollama/api/embed",
|
||||
{"model": "bge-m3:latest", "input": "hello", "keep_alive": "1m"},
|
||||
9,
|
||||
),
|
||||
("http://ollama/api/embeddings", {"model": "bge-m3:latest", "prompt": "hello"}, 9),
|
||||
]
|
||||
|
||||
|
||||
def test_extract_embedding_accepts_flat_embeddings_shape():
|
||||
assert OllamaService._extract_embedding({"embeddings": [0.1, 0.2]}) == [0.1, 0.2]
|
||||
|
||||
|
||||
def test_generate_embedding_caps_timeout_and_clips_input(monkeypatch):
|
||||
calls = []
|
||||
|
||||
def fake_post(url, json, timeout):
|
||||
calls.append((url, json, timeout))
|
||||
return FakeResponse(200, {"embeddings": [[0.1, 0.2, 0.3]]})
|
||||
|
||||
monkeypatch.setattr("services.ollama_service.EMBED_MAX_TIMEOUT", 3)
|
||||
monkeypatch.setattr("services.ollama_service.EMBED_MAX_CHARS", 5)
|
||||
monkeypatch.setattr("services.ollama_service.requests.post", fake_post)
|
||||
|
||||
vec = OllamaService().generate_embedding(
|
||||
"hello world",
|
||||
model="bge-m3:latest",
|
||||
host="http://ollama",
|
||||
timeout=45,
|
||||
)
|
||||
|
||||
assert vec == [0.1, 0.2, 0.3]
|
||||
assert calls == [
|
||||
(
|
||||
"http://ollama/api/embed",
|
||||
{"model": "bge-m3:latest", "input": "hello", "keep_alive": "1m"},
|
||||
3,
|
||||
),
|
||||
]
|
||||
|
||||
@@ -279,6 +279,51 @@ def test_embedding_all_three_hosts_fail_returns_empty():
|
||||
assert mock_post.call_count == 3
|
||||
|
||||
|
||||
def test_embedding_can_disable_111_fallback_for_background_rag_work():
|
||||
"""背景 embedding/RAG 任務只跑 GCP-A/GCP-B,避免 111 承接 bge-m3 長任務。"""
|
||||
import requests
|
||||
from services import ollama_service as oss
|
||||
from services.ollama_service import OllamaService
|
||||
|
||||
svc = OllamaService()
|
||||
hosts = [
|
||||
oss.OLLAMA_HOST_SECONDARY,
|
||||
oss.OLLAMA_HOST_FALLBACK,
|
||||
]
|
||||
|
||||
with patch('services.ollama_service.resolve_ollama_host', side_effect=hosts), \
|
||||
patch.dict('os.environ', {}, clear=False), \
|
||||
patch('services.ollama_service.requests.post',
|
||||
side_effect=requests.Timeout('secondary timeout')) as mock_post:
|
||||
import os
|
||||
os.environ.pop('EMBEDDING_HOST', None)
|
||||
vec = svc.generate_embedding('test text', allow_111_fallback=False)
|
||||
|
||||
posted_hosts = [call.args[0].split('/api/embed')[0] for call in mock_post.call_args_list]
|
||||
assert vec == []
|
||||
assert posted_hosts == [oss.OLLAMA_HOST_SECONDARY]
|
||||
assert oss.OLLAMA_HOST_FALLBACK not in posted_hosts
|
||||
|
||||
|
||||
def test_embedding_ignores_111_embedding_host_when_fallback_disabled():
|
||||
"""EMBEDDING_HOST 若誤設 111,背景 embedding 仍回 GCP resolver,不直接棄跑。"""
|
||||
from services import ollama_service as oss
|
||||
from services.ollama_service import OllamaService
|
||||
|
||||
svc = OllamaService()
|
||||
fake_ok = MagicMock(status_code=200)
|
||||
fake_ok.json.return_value = {'embeddings': [[0.7, 0.8]]}
|
||||
|
||||
with patch('services.ollama_service.resolve_ollama_host', return_value=oss.OLLAMA_HOST_SECONDARY), \
|
||||
patch.dict('os.environ', {'EMBEDDING_HOST': oss.OLLAMA_HOST_FALLBACK}, clear=False), \
|
||||
patch('services.ollama_service.requests.post', return_value=fake_ok) as mock_post:
|
||||
vec = svc.generate_embedding('test text', allow_111_fallback=False)
|
||||
|
||||
posted_hosts = [call.args[0].split('/api/embed')[0] for call in mock_post.call_args_list]
|
||||
assert vec == [0.7, 0.8]
|
||||
assert posted_hosts == [oss.OLLAMA_HOST_SECONDARY]
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# T4: mark_unhealthy 觸發 cache 失效(驗 self.host 取新主機)
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -99,7 +99,7 @@ class TestRagEnabledHits:
|
||||
|
||||
monkeypatch.setattr(
|
||||
'services.ollama_service.ollama_service.generate_embedding',
|
||||
lambda text, model="bge-m3:latest": _fake_embedding(),
|
||||
lambda text, model="bge-m3:latest", **_kwargs: _fake_embedding(),
|
||||
)
|
||||
|
||||
sig = rs.get_embedding_signature()
|
||||
@@ -132,7 +132,7 @@ class TestRagEnabledHits:
|
||||
|
||||
monkeypatch.setattr(
|
||||
'services.ollama_service.ollama_service.generate_embedding',
|
||||
lambda text, model="bge-m3:latest": _fake_embedding(),
|
||||
lambda text, model="bge-m3:latest", **_kwargs: _fake_embedding(),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
rs.rag_service,
|
||||
@@ -170,7 +170,7 @@ class TestRagEnabledHits:
|
||||
|
||||
monkeypatch.setattr(
|
||||
'services.ollama_service.ollama_service.generate_embedding',
|
||||
lambda text, model="bge-m3:latest": _fake_embedding(),
|
||||
lambda text, model="bge-m3:latest", **_kwargs: _fake_embedding(),
|
||||
)
|
||||
fake_session = MagicMock()
|
||||
fake_session.execute.return_value.fetchall.return_value = []
|
||||
@@ -227,7 +227,7 @@ class TestEmbeddingSignature:
|
||||
|
||||
monkeypatch.setattr(
|
||||
'services.ollama_service.ollama_service.generate_embedding',
|
||||
lambda text, model="bge-m3:latest": _fake_embedding(),
|
||||
lambda text, model="bge-m3:latest", **_kwargs: _fake_embedding(),
|
||||
)
|
||||
sig = rs.get_embedding_signature()
|
||||
fake_rows = [
|
||||
@@ -261,7 +261,7 @@ class TestEmbeddingSignature:
|
||||
|
||||
monkeypatch.setattr(
|
||||
'services.ollama_service.ollama_service.generate_embedding',
|
||||
lambda text, model="bge-m3:latest": _fake_embedding(),
|
||||
lambda text, model="bge-m3:latest", **_kwargs: _fake_embedding(),
|
||||
)
|
||||
fake_rows = [
|
||||
_make_row_obj(
|
||||
@@ -288,7 +288,7 @@ class TestFireAndForgetLog:
|
||||
|
||||
monkeypatch.setattr(
|
||||
'services.ollama_service.ollama_service.generate_embedding',
|
||||
lambda text, model="bge-m3:latest": _fake_embedding(),
|
||||
lambda text, model="bge-m3:latest", **_kwargs: _fake_embedding(),
|
||||
)
|
||||
# SELECT session 正常;INSERT session 故意 raise
|
||||
select_session = MagicMock()
|
||||
@@ -344,7 +344,7 @@ class TestFireAndForgetLog:
|
||||
|
||||
monkeypatch.setattr(
|
||||
'services.ollama_service.ollama_service.generate_embedding',
|
||||
lambda text, model="bge-m3:latest": [],
|
||||
lambda text, model="bge-m3:latest", **_kwargs: [],
|
||||
)
|
||||
# DB 不應被呼叫
|
||||
called = {'count': 0}
|
||||
@@ -466,7 +466,7 @@ class TestParamGuards:
|
||||
|
||||
monkeypatch.setattr(
|
||||
'services.ollama_service.ollama_service.generate_embedding',
|
||||
lambda text, model="bge-m3:latest": _fake_embedding(),
|
||||
lambda text, model="bge-m3:latest", **_kwargs: _fake_embedding(),
|
||||
)
|
||||
fake_session = MagicMock()
|
||||
fake_session.execute.return_value.fetchall.return_value = []
|
||||
@@ -487,7 +487,7 @@ class TestParamGuards:
|
||||
|
||||
monkeypatch.setattr(
|
||||
'services.ollama_service.ollama_service.generate_embedding',
|
||||
lambda text, model="bge-m3:latest": _fake_embedding(),
|
||||
lambda text, model="bge-m3:latest", **_kwargs: _fake_embedding(),
|
||||
)
|
||||
fake_session = MagicMock()
|
||||
captured = {}
|
||||
|
||||
Reference in New Issue
Block a user