Refactor backend project and tighten CV test coverage
This commit is contained in:
@@ -26,6 +26,7 @@ OCR_LANGUAGES = "eng"
|
||||
IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".webp"}
|
||||
OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://127.0.0.1:11434").rstrip("/")
|
||||
OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "")
|
||||
SKIP_MODEL_LOAD = os.getenv("AI_SERVICE_SKIP_MODEL_LOAD", "") == "1"
|
||||
|
||||
|
||||
def _load_runtime():
|
||||
@@ -39,7 +40,10 @@ def _load_runtime():
|
||||
return tokenizer, model, device, has_cuda, gpu_name
|
||||
|
||||
|
||||
tokenizer, model, device, GPU_AVAILABLE, GPU_NAME = _load_runtime()
|
||||
if SKIP_MODEL_LOAD:
|
||||
tokenizer, model, device, GPU_AVAILABLE, GPU_NAME = None, None, torch.device("cpu"), False, None
|
||||
else:
|
||||
tokenizer, model, device, GPU_AVAILABLE, GPU_NAME = _load_runtime()
|
||||
cache = TTLCache(maxsize=1024, ttl=60 * 60)
|
||||
|
||||
|
||||
@@ -298,6 +302,8 @@ def _role_focused_excerpt(text: str) -> dict:
|
||||
|
||||
|
||||
def _model_summarize(text: str, max_length: int, min_length: int) -> str:
|
||||
if tokenizer is None or model is None:
|
||||
raise HTTPException(status_code=503, detail="Summarizer model is not loaded.")
|
||||
inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=1024)
|
||||
input_ids = inputs.input_ids.to(device)
|
||||
attention_mask = inputs.attention_mask.to(device) if hasattr(inputs, "attention_mask") else None
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
-r requirements.txt
|
||||
pytest==8.3.5
|
||||
httpx==0.28.1
|
||||
@@ -0,0 +1,77 @@
|
||||
import importlib
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
|
||||
def load_app_module(monkeypatch):
|
||||
monkeypatch.setenv("AI_SERVICE_SKIP_MODEL_LOAD", "1")
|
||||
monkeypatch.delenv("OLLAMA_MODEL", raising=False)
|
||||
if "app" in sys.modules:
|
||||
del sys.modules["app"]
|
||||
module = importlib.import_module("app")
|
||||
return importlib.reload(module)
|
||||
|
||||
|
||||
def test_health_reports_runtime_without_ollama(monkeypatch):
|
||||
module = load_app_module(monkeypatch)
|
||||
client = TestClient(module.app)
|
||||
|
||||
response = client.get("/health")
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload["ok"] is True
|
||||
assert payload["device"] == "cpu"
|
||||
assert payload["ollama_configured"] is False
|
||||
assert payload["ollama_model"] is None
|
||||
|
||||
|
||||
def test_classify_block_returns_structured_json(monkeypatch):
|
||||
module = load_app_module(monkeypatch)
|
||||
|
||||
def fake_generate_json(prompt: str):
|
||||
assert "Senior Platform Engineer" in prompt
|
||||
return {
|
||||
"section": "Work Experience",
|
||||
"confidence": 0.91,
|
||||
"reason": "job block",
|
||||
"title": "Senior Platform Engineer",
|
||||
"company": "Atlas Systems",
|
||||
"location": "Oslo",
|
||||
"start": "2019",
|
||||
"end": "Present",
|
||||
"bullets": ["Built event-driven APIs and migration tooling."],
|
||||
}
|
||||
|
||||
monkeypatch.setattr(module, "_ollama_generate_json", fake_generate_json)
|
||||
client = TestClient(module.app)
|
||||
|
||||
response = client.post("/cv/classify-block", json={"block": "Senior Platform Engineer at Atlas Systems, Oslo, 2019 - Present. Built event-driven APIs and migration tooling."})
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload["section"] == "Work Experience"
|
||||
assert payload["title"] == "Senior Platform Engineer"
|
||||
assert payload["company"] == "Atlas Systems"
|
||||
assert payload["bullets"] == ["Built event-driven APIs and migration tooling."]
|
||||
|
||||
|
||||
def test_classify_block_defaults_missing_section_to_other(monkeypatch):
|
||||
module = load_app_module(monkeypatch)
|
||||
monkeypatch.setattr(module, "_ollama_generate_json", lambda prompt: {"bullets": []})
|
||||
client = TestClient(module.app)
|
||||
|
||||
response = client.post("/cv/classify-block", json={"block": "Miscellaneous profile text"})
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload["section"] == "Other"
|
||||
assert payload["bullets"] == []
|
||||
Reference in New Issue
Block a user