Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ where = ["src"]
dev = [
"pytest==9.1.1",
"pytest-asyncio==1.4.0",
"black==26.3.1",
"ruff==0.1.14",
"black==26.5.1",
"ruff==0.16.1",
"pip-audit==2.10.1",
"scikit-learn==1.9.0",
# Used only by the standalone OCR benchmark scripts (scripts/benchmark_ocr_variants.py
Expand All @@ -87,6 +87,22 @@ dev = [
"python-levenshtein>=0.27.3",
]

[tool.ruff]
target-version = "py312"

[tool.ruff.lint]
# Pin the rule set explicitly rather than inheriting whatever ruff defaults to.
# Ruff changes its default selection between releases: 0.16 turned on B, BLE, I,
# S, SIM, UP, RUF and more, which surfaced 874 findings on an otherwise
# unchanged tree and was the only thing blocking the 0.1.14 -> 0.16 bump.
# Selecting here means the lint surface changes when we decide to change it,
# not as a side effect of bumping the linter.
#
# This is ruff's own stable default — pyflakes plus the pycodestyle errors that
# catch real mistakes rather than formatting noise — so it is exactly the
# baseline the backend was already green on under 0.1.14.
select = ["E4", "E7", "E9", "F"]

[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src"]
Expand Down
4 changes: 2 additions & 2 deletions backend/scripts/score_ocr_accuracy.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,12 @@ def main():
m = all_results.get("mobile", {}).get(category)
s = all_results.get("server", {}).get(category)
m_str = (
f"{m['exact_match_rate']*100:.0f}% / CER {m['avg_char_error_rate']}"
f"{m['exact_match_rate'] * 100:.0f}% / CER {m['avg_char_error_rate']}"
if m
else "n/a"
)
s_str = (
f"{s['exact_match_rate']*100:.0f}% / CER {s['avg_char_error_rate']}"
f"{s['exact_match_rate'] * 100:.0f}% / CER {s['avg_char_error_rate']}"
if s
else "n/a"
)
Expand Down
6 changes: 3 additions & 3 deletions backend/src/find_api/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ class Settings(BaseSettings):
# Runtime/build profile. Docker images set this explicitly so the API can
# distinguish installed capabilities from hardware that merely exists on
# the host. ``development`` keeps source checkouts backwards compatible.
FIND_BUILD_PROFILE: Literal[
"development", "no-ai", "mock", "cpu", "nvidia"
] = "development"
FIND_BUILD_PROFILE: Literal["development", "no-ai", "mock", "cpu", "nvidia"] = (
"development"
)

# ML Models
ML_MODE: Literal["disabled", "full", "mock", "remote"] = "full"
Expand Down
6 changes: 3 additions & 3 deletions backend/tests/test_hybrid_embedding.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,9 +383,9 @@ def test_empty_string_never_embedded_in_any_scenario(self):
f"caption={caption!r}, objects={objects}"
)
else:
assert (
"" not in args
), f"Empty string found in embed_text list call: {args}"
assert "" not in args, (
f"Empty string found in embed_text list call: {args}"
)

def test_ocr_present_uses_weighted_hybrid(self):
"""When OCR text is present, weighted fusion should include OCR signal."""
Expand Down
12 changes: 6 additions & 6 deletions backend/tests/test_perf_timeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ def test_buckets_aggregate_under_budget(self, client, seeded, capsys):
f"{MONTHS} months: {elapsed * 1000:.1f} ms "
f"(budget {BUCKETS_BUDGET_S * 1000:.0f} ms)"
)
assert (
elapsed < BUCKETS_BUDGET_S
), f"/timeline/buckets took {elapsed:.3f}s (budget {BUCKETS_BUDGET_S}s)"
assert elapsed < BUCKETS_BUDGET_S, (
f"/timeline/buckets took {elapsed:.3f}s (budget {BUCKETS_BUDGET_S}s)"
)

def test_single_bucket_window_under_budget(self, client, seeded, capsys):
# Pick a month that actually has assets.
Expand All @@ -114,9 +114,9 @@ def test_single_bucket_window_under_budget(self, client, seeded, capsys):
f"\n[perf] /timeline/bucket ({month_key}, {body['count']} assets): "
f"{elapsed * 1000:.1f} ms (budget {BUCKET_BUDGET_S * 1000:.0f} ms)"
)
assert (
elapsed < BUCKET_BUDGET_S
), f"/timeline/bucket took {elapsed:.3f}s (budget {BUCKET_BUDGET_S}s)"
assert elapsed < BUCKET_BUDGET_S, (
f"/timeline/bucket took {elapsed:.3f}s (budget {BUCKET_BUDGET_S}s)"
)

def test_liked_filter_aggregate_under_budget(self, client, seeded):
# The favorites filter must not change the query's scaling characteristics.
Expand Down
44 changes: 27 additions & 17 deletions backend/tests/test_remote_ml_dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ def test_remote_mode_calls_remote_analyze(self, monkeypatch):
fake = MagicMock(
return_value={"caption": "a bike", "objects": [], "ocr_text": ""}
)
with patch("find_api.ml.remote_client.remote_analyze", fake), patch(
"find_api.ml.remote_client._feature_enabled", lambda f: True
with (
patch("find_api.ml.remote_client.remote_analyze", fake),
patch("find_api.ml.remote_client._feature_enabled", lambda f: True),
):
result = processors.extract_image_metadata(_rgb_image())

Expand All @@ -42,8 +43,9 @@ def test_remote_mode_skips_transmission_when_no_features_enabled(self, monkeypat
monkeypatch.setattr(processors, "current_ml_mode", lambda: "remote")

fake = MagicMock()
with patch("find_api.ml.remote_client.remote_analyze", fake), patch(
"find_api.ml.remote_client._feature_enabled", lambda f: False
with (
patch("find_api.ml.remote_client.remote_analyze", fake),
patch("find_api.ml.remote_client._feature_enabled", lambda f: False),
):
result = processors.extract_image_metadata(_rgb_image())

Expand All @@ -54,8 +56,9 @@ def test_remote_mode_calls_remote_embed(self, monkeypatch):
monkeypatch.setattr(processors, "current_ml_mode", lambda: "remote")

fake = MagicMock(return_value=[0.1] * 768)
with patch("find_api.ml.remote_client.remote_embed", fake), patch(
"find_api.ml.remote_client._feature_enabled", lambda f: True
with (
patch("find_api.ml.remote_client.remote_embed", fake),
patch("find_api.ml.remote_client._feature_enabled", lambda f: True),
):
vector = processors.generate_hybrid_embedding(_rgb_image(), {})

Expand All @@ -79,8 +82,9 @@ def test_remote_cluster_is_dispatched(self):
embeddings = np.eye(4, dtype=np.float32)
fake = MagicMock(return_value={"labels": [0, 0, 1, 1], "info": {"n": 4}})

with patch("find_api.ml.remote_client.remote_cluster", fake), patch(
"find_api.ml.remote_client._feature_enabled", lambda f: True
with (
patch("find_api.ml.remote_client.remote_cluster", fake),
patch("find_api.ml.remote_client._feature_enabled", lambda f: True),
):
labels, info = jobs._remote_cluster_embeddings(embeddings)

Expand All @@ -98,9 +102,11 @@ def test_disabled_cluster_feature_falls_back_to_local(self):
fake_local = MagicMock()
fake_local.cluster.return_value = (np.array([-1, -1, -1, -1]), {})

with patch("find_api.ml.remote_client.remote_cluster", fake_remote), patch(
"find_api.ml.remote_client._feature_enabled", lambda f: False
), patch("find_api.ml.clusterer.get_image_clusterer", lambda: fake_local):
with (
patch("find_api.ml.remote_client.remote_cluster", fake_remote),
patch("find_api.ml.remote_client._feature_enabled", lambda f: False),
patch("find_api.ml.clusterer.get_image_clusterer", lambda: fake_local),
):
jobs._remote_cluster_embeddings(embeddings)

assert fake_remote.call_count == 0
Expand Down Expand Up @@ -136,9 +142,12 @@ def test_enabled_features_are_sent_to_the_server(self, monkeypatch):
monkeypatch.setattr(processors, "current_ml_mode", lambda: "remote")

fake = MagicMock(return_value={"caption": "x"})
with patch("find_api.ml.remote_client.remote_analyze", fake), patch(
"find_api.ml.remote_client._feature_enabled",
lambda f: f in {"caption", "detect"},
with (
patch("find_api.ml.remote_client.remote_analyze", fake),
patch(
"find_api.ml.remote_client._feature_enabled",
lambda f: f in {"caption", "detect"},
),
):
result = processors.extract_image_metadata(_rgb_image())

Expand Down Expand Up @@ -175,9 +184,10 @@ def test_disabled_embed_raises_rather_than_returning_a_mock_vector(
"""
monkeypatch.setattr(processors, "current_ml_mode", lambda: "remote")

with patch(
"find_api.ml.remote_client._feature_enabled", lambda f: False
), pytest.raises(processors.RemoteFeatureDisabled):
with (
patch("find_api.ml.remote_client._feature_enabled", lambda f: False),
pytest.raises(processors.RemoteFeatureDisabled),
):
processors.generate_hybrid_embedding(_rgb_image(), {})


Expand Down
Loading
Loading