feat: add bounded crop evidence and keyed gallery cache

This commit is contained in:
kiyo9w 2026-09-28 15:49:34 +09:00
parent 69de9ea418
commit 0d0e9f82b9
8 changed files with 1065 additions and 41 deletions

View file

@ -24,6 +24,7 @@ import numpy as np # noqa: E402
from app import ServerSettings, create_app # noqa: E402
from engine import EngineSettings, RecognitionEngine # noqa: E402
from test_live import SmallBoxDetector # noqa: E402
from test_live import FakeDetector, FakeInventory, FakeMatcher # noqa: E402
from fastapi.testclient import TestClient # noqa: E402
@ -57,9 +58,9 @@ def make_scans(engine):
return ScanService(PerceptionService(adapter))
def make_app(*, token=None, max_sessions=4):
def make_app(*, token=None, max_sessions=4, detector=None):
engine = RecognitionEngine(
FakeDetector(), FakeMatcher(), FakeInventory(),
detector or FakeDetector(), FakeMatcher(), FakeInventory(),
settings=EngineSettings(classify_workers=2),
)
settings = ServerSettings(token=token, max_sessions=max_sessions)
@ -350,6 +351,109 @@ class WebSocketProtocolTests(unittest.TestCase):
missing = socket.receive_json()
self.assertEqual(missing["code"], "TRACK_NOT_IN_VIEW")
def crop_app(self):
"""A shelf whose only facing is too small for the frame crop to decide."""
client, engine = make_app(detector=SmallBoxDetector())
self.addCleanup(engine.shutdown)
return client
def test_capabilities_turn_on_the_crop_lane(self):
with self.client.websocket_connect("/v1/stream") as socket:
hello = socket.receive_json()
self.assertIn("max_crop_bytes", hello["limits"])
socket.send_json({"type": "capabilities", "crop": True, "max_crop_bytes": 65536})
ack = socket.receive_json()
self.assertEqual(ack["type"], "capabilities_ack")
self.assertEqual(ack["capabilities"], {"crop": True, "max_crop_bytes": 65536})
def test_a_v1_client_is_never_asked_for_a_crop(self):
with self.client.websocket_connect("/v1/stream") as socket:
socket.receive_json() # hello
for frame_id in (1, 2, 3):
socket.send_bytes(frame_payload(frame_id))
result = socket.receive_json()
for track in result["tracks"]:
self.assertNotIn("crop_request", track)
self.assertEqual(result["stats"]["crops"]["requested"], 0)
def test_a_crop_round_trip_labels_the_track(self):
with self.crop_app().websocket_connect("/v1/stream") as socket:
socket.receive_json() # hello
socket.send_json({"type": "capabilities", "crop": True})
self.assertEqual(socket.receive_json()["type"], "capabilities_ack")
socket.send_bytes(frame_payload(1))
socket.receive_json()
socket.send_bytes(frame_payload(2))
result = socket.receive_json()
request = result["tracks"][0].get("crop_request")
self.assertIsNotNone(request, "a facing too small for the frame crop must be asked about")
self.assertEqual(request["frame_id"], 2)
socket.send_json({
"type": "crop",
"request_id": request["request_id"],
"track_id": request["track_id"],
"frame_id": request["frame_id"],
"jpeg": base64.b64encode(jpeg_bytes(80, 120)).decode("ascii"),
})
self.assertEqual(socket.receive_json()["type"], "crop_ack")
socket.send_bytes(frame_payload(3))
settled = socket.receive_json()
self.assertEqual(settled["stats"]["crops"]["accepted"], 1)
identities = [t["identity"] for t in settled["tracks"]]
self.assertTrue(any(i["source"] == "crop" for i in identities))
def test_a_crop_that_does_not_match_its_request_is_refused(self):
with self.crop_app().websocket_connect("/v1/stream") as socket:
socket.receive_json() # hello
socket.send_json({"type": "capabilities", "crop": True})
socket.receive_json()
socket.send_bytes(frame_payload(1))
socket.receive_json()
socket.send_bytes(frame_payload(2))
request = socket.receive_json()["tracks"][0]["crop_request"]
socket.send_json({
"type": "crop",
"request_id": request["request_id"],
"track_id": request["track_id"],
"frame_id": 4242,
"jpeg": base64.b64encode(jpeg_bytes(80, 120)).decode("ascii"),
})
refused = socket.receive_json()
self.assertEqual(refused["type"], "crop_error")
self.assertEqual(refused["code"], "CROP_MISMATCH")
socket.send_bytes(frame_payload(3))
self.assertEqual(socket.receive_json()["stats"]["crops"]["accepted"], 0)
def test_a_crop_that_is_not_an_image_is_refused(self):
with self.crop_app().websocket_connect("/v1/stream") as socket:
socket.receive_json() # hello
socket.send_json({"type": "capabilities", "crop": True})
socket.receive_json()
socket.send_bytes(frame_payload(1))
socket.receive_json()
socket.send_bytes(frame_payload(2))
request = socket.receive_json()["tracks"][0]["crop_request"]
socket.send_json({
"type": "crop",
"request_id": request["request_id"],
"track_id": request["track_id"],
"frame_id": request["frame_id"],
"jpeg": base64.b64encode(b"not an image").decode("ascii"),
})
self.assertEqual(socket.receive_json()["code"], "CROP_UNDECODABLE")
def test_a_crop_without_an_image_is_refused(self):
with self.client.websocket_connect("/v1/stream") as socket:
socket.receive_json() # hello
socket.send_json({"type": "crop", "request_id": "c1", "track_id": 1, "frame_id": 1})
refused = socket.receive_json()
self.assertEqual(refused["code"], "CROP_BAD_MESSAGE")
socket.send_json({"type": "ping", "t": 1})
self.assertEqual(socket.receive_json(), {"type": "pong", "t": 1})
def test_token_is_enforced_on_the_socket(self):
"""Header or the first post-hello auth frame. A query token is rejected."""
client, engine = make_app(token="s3cret")