Verified catalogue of mobiles and laptops sold in India, collected from real retail listings (FastAPI backend, React frontend, Postgres/pgvector). - REST API under /api/elec (read-only catalogue; admin endpoints need login) - MCP server (FastMCP) at /mcp/ with list_categories, search_products, get_product and price_history tools - Real ratings and reviews read from product pages and search results - Production Dockerfile (requirements-api.txt, no PyTorch) and .env.production.example; remote database only via an explicit ELEC_ALLOW_REMOTE_DB host/name allowlist - docs/API.md: endpoint and MCP reference with live examples Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
109 lines
3.8 KiB
Python
109 lines
3.8 KiB
Python
"""The reachability probe in front of every Ollama call.
|
|
|
|
WHY THIS FILE EXISTS
|
|
--------------------
|
|
`_ensure_client()` asks Ollama for `/api/tags` with a 5-second timeout, and
|
|
`stage_2_row_intake` calls it once per ROW through `fetch_product_details`.
|
|
Uncached, a 2000-row sheet ingested with `use_llm` on, against a configured but
|
|
unreachable Ollama, spends up to ~2.8 hours doing nothing but timing out - and
|
|
shows as a batch that has hung, not one that has failed.
|
|
|
|
That was survivable only while `use_llm` defaulted to false everywhere. It no
|
|
longer does: `UPLOAD_AUTORUN_USE_LLM` is true, so every auto-started upload now
|
|
takes this path. The cache is what makes that default safe, and the first test
|
|
below is the one that stops it being quietly removed in a later refactor.
|
|
|
|
`/api/health` calls the same function, so a down Ollama also stops adding five
|
|
seconds to every health request.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from app.services import ollama_service
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _clean_probe_cache():
|
|
"""The cache is a module global and outlives a test."""
|
|
ollama_service.reset_reachability_cache()
|
|
yield
|
|
ollama_service.reset_reachability_cache()
|
|
|
|
|
|
@pytest.fixture
|
|
def probe_calls(monkeypatch):
|
|
"""Count the HTTP probes, and make every one of them fail.
|
|
|
|
Failure is the case that matters: a reachable Ollama answers in
|
|
milliseconds, an unreachable one costs the full timeout, and it is the
|
|
second that used to be paid per row.
|
|
"""
|
|
calls: list = []
|
|
|
|
def boom(url, **kwargs):
|
|
calls.append(url)
|
|
raise OSError("connection refused")
|
|
|
|
monkeypatch.setattr(ollama_service, "USE_OLLAMA", True)
|
|
monkeypatch.setattr(ollama_service.requests, "get", boom)
|
|
return calls
|
|
|
|
|
|
def test_an_unreachable_ollama_is_probed_once_not_once_per_call(probe_calls):
|
|
"""The whole point. Ten rows must not be ten timeouts."""
|
|
for _ in range(10):
|
|
assert ollama_service._ensure_client() is False
|
|
|
|
assert len(probe_calls) == 1, (
|
|
f"{len(probe_calls)} probes for 10 calls - the cache is not holding, and "
|
|
f"an ingest will pay the 5s timeout per row"
|
|
)
|
|
|
|
|
|
def test_the_cache_expires_so_a_late_start_is_noticed(probe_calls, monkeypatch):
|
|
"""A permanent memo would mean an Ollama started after the API is never
|
|
seen, and /api/health reports it down until someone redeploys."""
|
|
clock = [1000.0]
|
|
monkeypatch.setattr(ollama_service.time, "monotonic", lambda: clock[0])
|
|
|
|
ollama_service._ensure_client()
|
|
assert len(probe_calls) == 1
|
|
|
|
clock[0] += ollama_service._PROBE_TTL_SECONDS + 1
|
|
ollama_service._ensure_client()
|
|
assert len(probe_calls) == 2, "the probe never expired"
|
|
|
|
|
|
def test_a_reachable_ollama_is_also_cached(monkeypatch):
|
|
"""Both outcomes are cached. Caching only the failure would leave the happy
|
|
path paying an HTTP round trip per row - cheap, but per row and pointless."""
|
|
calls: list = []
|
|
|
|
class Ok:
|
|
status_code = 200
|
|
|
|
def ok(url, **kwargs):
|
|
calls.append(url)
|
|
return Ok()
|
|
|
|
monkeypatch.setattr(ollama_service, "USE_OLLAMA", True)
|
|
monkeypatch.setattr(ollama_service.requests, "get", ok)
|
|
|
|
assert [ollama_service._ensure_client() for _ in range(5)] == [True] * 5
|
|
assert len(calls) == 1
|
|
|
|
|
|
def test_disabled_stays_none_and_never_touches_the_network(monkeypatch):
|
|
"""Three return values, not two: `system.py` tells "switched off" from
|
|
"configured but down", and /api/health's `ollama` field means different
|
|
things in each case. Collapsing this to a bool would break that.
|
|
"""
|
|
def never(*_args, **_kwargs):
|
|
raise AssertionError("USE_OLLAMA is false - nothing may be requested")
|
|
|
|
monkeypatch.setattr(ollama_service, "USE_OLLAMA", False)
|
|
monkeypatch.setattr(ollama_service.requests, "get", never)
|
|
|
|
assert ollama_service._ensure_client() is None
|