V10.417 protect embedding fallback routing
All checks were successful
CD Pipeline / deploy (push) Successful in 1m4s

This commit is contained in:
OoO
2026-05-24 14:51:36 +08:00
committed by AiderHeal Bot
parent 30e30ac7b6
commit 353e565e52
11 changed files with 170 additions and 30 deletions

View File

@@ -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(

View File

@@ -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,
),
]

View File

@@ -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 取新主機)
# ═══════════════════════════════════════════════════════════════════════════

View File

@@ -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 = {}