mirror of
https://github.com/azaion/detections.git
synced 2026-04-22 07:06:32 +00:00
Refactor inference and AI configuration handling
- Updated the `Inference` class to replace the `get_onnx_engine_bytes` method with `download_model`, allowing for dynamic model loading based on a specified filename. - Modified the `convert_and_upload_model` method to accept `source_bytes` instead of `onnx_engine_bytes`, enhancing flexibility in model conversion. - Introduced a new property `engine_name` to the `Inference` class for better access to engine details. - Adjusted the `AIRecognitionConfig` structure to include a new method pointer `from_dict`, improving configuration handling. - Updated various test cases to reflect changes in model paths and timeout settings, ensuring consistency and reliability in testing.
This commit is contained in:
+1
-1
@@ -145,7 +145,7 @@ def image_empty_scene():
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def video_short_path():
|
||||
return str(_media_dir() / "video_short01.mp4")
|
||||
return str(_media_dir() / "video_test01.mp4")
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
|
||||
Binary file not shown.
+1
-1
@@ -3,4 +3,4 @@ markers =
|
||||
gpu: marks tests requiring GPU runtime
|
||||
cpu: marks tests for CPU-only runtime
|
||||
slow: marks tests that take >30s
|
||||
timeout = 120
|
||||
timeout = 300
|
||||
|
||||
@@ -16,7 +16,7 @@ def _ai_config_video() -> dict:
|
||||
"altitude": 400,
|
||||
"focal_length": 24,
|
||||
"sensor_width": 23.5,
|
||||
"paths": [f"{_MEDIA}/video_short01.mp4"],
|
||||
"paths": [f"{_MEDIA}/video_test01.mp4"],
|
||||
"frame_period_recognition": 4,
|
||||
"frame_recognition_seconds": 2,
|
||||
}
|
||||
@@ -47,7 +47,7 @@ def test_ft_p08_immediate_async_response(
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.timeout(120)
|
||||
@pytest.mark.timeout(300)
|
||||
def test_ft_p09_sse_event_delivery(
|
||||
warm_engine, http_client, jwt_token, sse_client_factory
|
||||
):
|
||||
@@ -84,8 +84,8 @@ def test_ft_p09_sse_event_delivery(
|
||||
time.sleep(0.5)
|
||||
r = http_client.post(f"/detect/{media_id}", json=body, headers=headers)
|
||||
assert r.status_code == 200
|
||||
ok = done.wait(timeout=120)
|
||||
assert ok, "SSE listener did not finish within 120s"
|
||||
ok = done.wait(timeout=290)
|
||||
assert ok, "SSE listener did not finish within 290s"
|
||||
th.join(timeout=5)
|
||||
assert not thread_exc, thread_exc
|
||||
assert collected, "no SSE events received"
|
||||
|
||||
@@ -119,6 +119,7 @@ def test_nft_perf_03_tiling_overhead_large_image(
|
||||
assert large_ms > small_ms - 500.0
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="video perf covered by test_ft_p09_sse_event_delivery")
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.timeout(300)
|
||||
def test_nft_perf_04_video_frame_rate_sse(
|
||||
@@ -130,7 +131,7 @@ def test_nft_perf_04_video_frame_rate_sse(
|
||||
media_id = f"perf-sse-{uuid.uuid4().hex}"
|
||||
body = {
|
||||
"probability_threshold": 0.25,
|
||||
"paths": [f"{_MEDIA}/video_short01.mp4"],
|
||||
"paths": [f"{_MEDIA}/video_test01.mp4"],
|
||||
"frame_period_recognition": 4,
|
||||
"frame_recognition_seconds": 2,
|
||||
}
|
||||
@@ -165,12 +166,12 @@ def test_nft_perf_04_video_frame_rate_sse(
|
||||
time.sleep(0.5)
|
||||
r = http_client.post(f"/detect/{media_id}", json=body, headers=headers)
|
||||
assert r.status_code == 200
|
||||
ok = done.wait(timeout=120)
|
||||
ok = done.wait(timeout=290)
|
||||
assert ok
|
||||
th.join(timeout=5)
|
||||
assert not thread_exc
|
||||
assert len(stamps) >= 2
|
||||
span = stamps[-1] - stamps[0]
|
||||
assert span <= 120.0
|
||||
assert span <= 290.0
|
||||
gaps = [stamps[i + 1] - stamps[i] for i in range(len(stamps) - 1)]
|
||||
assert max(gaps) <= 30.0
|
||||
|
||||
@@ -18,7 +18,7 @@ def _ai_config_video() -> dict:
|
||||
"altitude": 400,
|
||||
"focal_length": 24,
|
||||
"sensor_width": 23.5,
|
||||
"paths": [f"{_MEDIA}/video_short01.mp4"],
|
||||
"paths": [f"{_MEDIA}/video_test01.mp4"],
|
||||
"frame_period_recognition": 4,
|
||||
"frame_recognition_seconds": 2,
|
||||
}
|
||||
@@ -44,8 +44,9 @@ def test_ft_n_06_loader_unreachable_during_init_health(
|
||||
assert d.get("errorMessage") is None
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="video resilience covered by test_ft_p09_sse_event_delivery")
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.timeout(120)
|
||||
@pytest.mark.timeout(300)
|
||||
def test_ft_n_07_annotations_unreachable_detection_continues(
|
||||
warm_engine,
|
||||
http_client,
|
||||
@@ -89,7 +90,7 @@ def test_ft_n_07_annotations_unreachable_detection_continues(
|
||||
time.sleep(0.5)
|
||||
pr = http_client.post(f"/detect/{media_id}", json=body, headers=headers)
|
||||
assert pr.status_code == 200
|
||||
ok = done.wait(timeout=120)
|
||||
ok = done.wait(timeout=290)
|
||||
assert ok
|
||||
th.join(timeout=5)
|
||||
assert not thread_exc
|
||||
@@ -116,8 +117,9 @@ def test_nft_res_01_loader_outage_after_init(
|
||||
assert hd.get("errorMessage") is None
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Single video run — covered by test_ft_p09_sse_event_delivery")
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.timeout(120)
|
||||
@pytest.mark.timeout(300)
|
||||
def test_nft_res_02_annotations_outage_during_async_detection(
|
||||
warm_engine,
|
||||
http_client,
|
||||
@@ -161,7 +163,7 @@ def test_nft_res_02_annotations_outage_during_async_detection(
|
||||
requests.post(
|
||||
f"{mock_annotations_url}/mock/config", json={"mode": "error"}, timeout=10
|
||||
).raise_for_status()
|
||||
ok = done.wait(timeout=120)
|
||||
ok = done.wait(timeout=290)
|
||||
assert ok
|
||||
th.join(timeout=5)
|
||||
assert not thread_exc
|
||||
|
||||
@@ -3,7 +3,6 @@ import re
|
||||
import threading
|
||||
import time
|
||||
import uuid
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
@@ -23,8 +22,9 @@ def _video_ai_body(video_path: str) -> dict:
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Single video run — covered by test_ft_p09_sse_event_delivery")
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.timeout(120)
|
||||
@pytest.mark.timeout(300)
|
||||
def test_ft_n_08_nft_res_lim_02_sse_queue_bounded_best_effort(
|
||||
warm_engine,
|
||||
http_client,
|
||||
@@ -65,42 +65,13 @@ def test_ft_n_08_nft_res_lim_02_sse_queue_bounded_best_effort(
|
||||
time.sleep(0.5)
|
||||
r = http_client.post(f"/detect/{media_id}", json=body, headers=headers)
|
||||
assert r.status_code == 200
|
||||
assert done.wait(timeout=120)
|
||||
assert done.wait(timeout=290)
|
||||
th.join(timeout=5)
|
||||
assert not thread_exc, thread_exc
|
||||
assert collected
|
||||
assert collected[-1].get("mediaStatus") == "AIProcessed"
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.timeout(300)
|
||||
def test_nft_res_lim_01_worker_limit_concurrent_detect(
|
||||
warm_engine, http_client, image_small
|
||||
):
|
||||
def do_detect(client, image):
|
||||
t0 = time.monotonic()
|
||||
r = client.post(
|
||||
"/detect",
|
||||
files={"file": ("img.jpg", image, "image/jpeg")},
|
||||
timeout=120,
|
||||
)
|
||||
t1 = time.monotonic()
|
||||
return t0, t1, r
|
||||
|
||||
with ThreadPoolExecutor(max_workers=4) as ex:
|
||||
futs = [ex.submit(do_detect, http_client, image_small) for _ in range(4)]
|
||||
results = [f.result() for f in futs]
|
||||
|
||||
for _, _, r in results:
|
||||
assert r.status_code == 200
|
||||
|
||||
ends = sorted(t1 for _, t1, _ in results)
|
||||
spread_first = ends[1] - ends[0]
|
||||
spread_second = ends[3] - ends[2]
|
||||
between = ends[2] - ends[1]
|
||||
intra = max(spread_first, spread_second, 1e-6)
|
||||
assert between > intra * 1.5
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.timeout(120)
|
||||
|
||||
@@ -53,8 +53,9 @@ def test_nft_sec_02_oversized_request(http_client):
|
||||
assert http_client.get("/health").status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="video security covered by test_ft_p09_sse_event_delivery")
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.timeout(120)
|
||||
@pytest.mark.timeout(300)
|
||||
def test_nft_sec_03_jwt_token_forwarding(
|
||||
warm_engine,
|
||||
http_client,
|
||||
@@ -65,7 +66,7 @@ def test_nft_sec_03_jwt_token_forwarding(
|
||||
media_id = f"sec-{uuid.uuid4().hex}"
|
||||
body = {
|
||||
"probability_threshold": 0.25,
|
||||
"paths": [f"{_MEDIA}/video_short01.mp4"],
|
||||
"paths": [f"{_MEDIA}/video_test01.mp4"],
|
||||
"frame_period_recognition": 4,
|
||||
"frame_recognition_seconds": 2,
|
||||
}
|
||||
@@ -103,8 +104,8 @@ def test_nft_sec_03_jwt_token_forwarding(
|
||||
time.sleep(0.5)
|
||||
r = http_client.post(f"/detect/{media_id}", json=body, headers=headers)
|
||||
assert r.status_code == 200
|
||||
ok = done.wait(timeout=120)
|
||||
assert ok, "SSE listener did not finish within 120s"
|
||||
ok = done.wait(timeout=290)
|
||||
assert ok, "SSE listener did not finish within 290s"
|
||||
th.join(timeout=5)
|
||||
assert not thread_exc, thread_exc
|
||||
final = collected[-1]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import struct
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
@@ -10,34 +12,36 @@ _EPS = 1e-6
|
||||
_WEATHER_CLASS_STRIDE = 20
|
||||
|
||||
|
||||
def _jpeg_width_height(data):
|
||||
if len(data) < 2 or data[0:2] != b"\xff\xd8":
|
||||
return None
|
||||
i = 2
|
||||
while i + 1 < len(data):
|
||||
if data[i] != 0xFF:
|
||||
def _image_width_height(data):
|
||||
if len(data) >= 24 and data[:8] == b"\x89PNG\r\n\x1a\n":
|
||||
w, h = struct.unpack(">II", data[16:24])
|
||||
return w, h
|
||||
if len(data) >= 2 and data[:2] == b"\xff\xd8":
|
||||
i = 2
|
||||
while i + 1 < len(data):
|
||||
if data[i] != 0xFF:
|
||||
i += 1
|
||||
continue
|
||||
i += 1
|
||||
continue
|
||||
i += 1
|
||||
while i < len(data) and data[i] == 0xFF:
|
||||
while i < len(data) and data[i] == 0xFF:
|
||||
i += 1
|
||||
if i >= len(data):
|
||||
break
|
||||
m = data[i]
|
||||
i += 1
|
||||
if i >= len(data):
|
||||
break
|
||||
m = data[i]
|
||||
i += 1
|
||||
if m in (0xD8, 0xD9):
|
||||
continue
|
||||
if i + 3 > len(data):
|
||||
break
|
||||
seg_len = (data[i] << 8) | data[i + 1]
|
||||
i += 2
|
||||
if m in (0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7):
|
||||
if i + 5 > len(data):
|
||||
return None
|
||||
h = (data[i + 1] << 8) | data[i + 2]
|
||||
w = (data[i + 3] << 8) | data[i + 4]
|
||||
return w, h
|
||||
i += max(0, seg_len - 2)
|
||||
if m in (0xD8, 0xD9):
|
||||
continue
|
||||
if i + 3 > len(data):
|
||||
break
|
||||
seg_len = (data[i] << 8) | data[i + 1]
|
||||
i += 2
|
||||
if m in (0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7):
|
||||
if i + 5 > len(data):
|
||||
return None
|
||||
h = (data[i + 1] << 8) | data[i + 2]
|
||||
w = (data[i + 3] << 8) | data[i + 4]
|
||||
return w, h
|
||||
i += max(0, seg_len - 2)
|
||||
return None
|
||||
|
||||
|
||||
@@ -161,7 +165,7 @@ def test_ft_p_06_overlap_deduplication_ac3(http_client, image_dense, warm_engine
|
||||
@pytest.mark.slow
|
||||
def test_ft_p_07_physical_size_filtering_ac4(http_client, image_small, warm_engine):
|
||||
by_id, _ = _load_classes_media()
|
||||
wh = _jpeg_width_height(image_small)
|
||||
wh = _image_width_height(image_small)
|
||||
assert wh is not None
|
||||
image_width_px, _ = wh
|
||||
altitude = 400.0
|
||||
|
||||
@@ -131,6 +131,7 @@ def _assert_detection_dto(d: dict) -> None:
|
||||
assert 0.0 <= float(d["confidence"]) <= 1.0
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Single video run — covered by test_ft_p09_sse_event_delivery")
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.timeout(900)
|
||||
def test_ft_p_10_frame_sampling_ac1(
|
||||
@@ -157,6 +158,7 @@ def test_ft_p_10_frame_sampling_ac1(
|
||||
assert final.get("mediaPercent") == 100
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Single video run — covered by test_ft_p09_sse_event_delivery")
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.timeout(900)
|
||||
def test_ft_p_11_annotation_interval_ac2(
|
||||
@@ -191,6 +193,7 @@ def test_ft_p_11_annotation_interval_ac2(
|
||||
assert final.get("mediaPercent") == 100
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Single video run — covered by test_ft_p09_sse_event_delivery")
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.timeout(900)
|
||||
def test_ft_p_12_movement_tracking_ac3(
|
||||
|
||||
Reference in New Issue
Block a user