supervisely 6.73.412__py3-none-any.whl → 6.73.414__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- supervisely/api/module_api.py +2 -0
- supervisely/api/project_api.py +1 -0
- supervisely/app/fastapi/subapp.py +87 -0
- supervisely/nn/inference/inference.py +22 -8
- {supervisely-6.73.412.dist-info → supervisely-6.73.414.dist-info}/METADATA +1 -1
- {supervisely-6.73.412.dist-info → supervisely-6.73.414.dist-info}/RECORD +10 -10
- {supervisely-6.73.412.dist-info → supervisely-6.73.414.dist-info}/LICENSE +0 -0
- {supervisely-6.73.412.dist-info → supervisely-6.73.414.dist-info}/WHEEL +0 -0
- {supervisely-6.73.412.dist-info → supervisely-6.73.414.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.412.dist-info → supervisely-6.73.414.dist-info}/top_level.txt +0 -0
supervisely/api/module_api.py
CHANGED
supervisely/api/project_api.py
CHANGED
|
@@ -2455,6 +2455,7 @@ class ProjectApi(CloneableModuleApi, UpdateableModule, RemoveableModuleApi):
|
|
|
2455
2455
|
request_body = {
|
|
2456
2456
|
ApiField.PROJECT_ID: project_id,
|
|
2457
2457
|
ApiField.LIMIT: limit,
|
|
2458
|
+
ApiField.UNIQUE_ITEMS: limit, # the same as limit, but for diverse search
|
|
2458
2459
|
}
|
|
2459
2460
|
|
|
2460
2461
|
if dataset_id is not None:
|
|
@@ -3,7 +3,9 @@ import json
|
|
|
3
3
|
import os
|
|
4
4
|
import signal
|
|
5
5
|
import sys
|
|
6
|
+
import time
|
|
6
7
|
from contextlib import suppress
|
|
8
|
+
from contextvars import ContextVar
|
|
7
9
|
from functools import wraps
|
|
8
10
|
from pathlib import Path
|
|
9
11
|
from threading import Event as ThreadingEvent
|
|
@@ -61,6 +63,10 @@ SUPERVISELY_SERVER_PATH_PREFIX = sly_env.supervisely_server_path_prefix()
|
|
|
61
63
|
if SUPERVISELY_SERVER_PATH_PREFIX and not SUPERVISELY_SERVER_PATH_PREFIX.startswith("/"):
|
|
62
64
|
SUPERVISELY_SERVER_PATH_PREFIX = f"/{SUPERVISELY_SERVER_PATH_PREFIX}"
|
|
63
65
|
|
|
66
|
+
HEALTH_ENDPOINTS = ["/health", "/is_ready"]
|
|
67
|
+
|
|
68
|
+
# Context variable for response time
|
|
69
|
+
response_time_ctx: ContextVar[float] = ContextVar("response_time", default=None)
|
|
64
70
|
|
|
65
71
|
class ReadyzFilter(logging.Filter):
|
|
66
72
|
def filter(self, record):
|
|
@@ -70,11 +76,22 @@ class ReadyzFilter(logging.Filter):
|
|
|
70
76
|
return True
|
|
71
77
|
|
|
72
78
|
|
|
79
|
+
class ResponseTimeFilter(logging.Filter):
|
|
80
|
+
def filter(self, record):
|
|
81
|
+
# Check if this is an HTTP access log line by logger name
|
|
82
|
+
if getattr(record, "name", "") == "uvicorn.access":
|
|
83
|
+
response_time = response_time_ctx.get(None)
|
|
84
|
+
if response_time is not None:
|
|
85
|
+
record.responseTime = f"{round(response_time, 2)}ms"
|
|
86
|
+
return True
|
|
87
|
+
|
|
88
|
+
|
|
73
89
|
def _init_uvicorn_logger():
|
|
74
90
|
uvicorn_logger = logging.getLogger("uvicorn.access")
|
|
75
91
|
for handler in uvicorn_logger.handlers:
|
|
76
92
|
handler.setFormatter(create_formatter())
|
|
77
93
|
uvicorn_logger.addFilter(ReadyzFilter())
|
|
94
|
+
uvicorn_logger.addFilter(ResponseTimeFilter())
|
|
78
95
|
|
|
79
96
|
|
|
80
97
|
_init_uvicorn_logger()
|
|
@@ -794,6 +811,8 @@ def _init(
|
|
|
794
811
|
|
|
795
812
|
@app.middleware("http")
|
|
796
813
|
async def get_state_from_request(request: Request, call_next):
|
|
814
|
+
# Start timer for response time measurement
|
|
815
|
+
start_time = time.perf_counter()
|
|
797
816
|
if headless is False:
|
|
798
817
|
await StateJson.from_request(request)
|
|
799
818
|
|
|
@@ -829,6 +848,9 @@ def _init(
|
|
|
829
848
|
except Exception as exc:
|
|
830
849
|
need_to_handle_error = is_production()
|
|
831
850
|
response = await process_server_error(request, exc, need_to_handle_error)
|
|
851
|
+
# Calculate response time and set it for uvicorn logger in ms
|
|
852
|
+
elapsed_ms = (time.perf_counter() - start_time) * 1000
|
|
853
|
+
response_time_ctx.set(elapsed_ms)
|
|
832
854
|
return response
|
|
833
855
|
|
|
834
856
|
def verify_localhost(request: Request):
|
|
@@ -905,7 +927,33 @@ class Application(metaclass=Singleton):
|
|
|
905
927
|
Callable
|
|
906
928
|
] = None, # function to check if the app is ready for requests (e.g serving app: model is served and ready)
|
|
907
929
|
show_header: bool = True,
|
|
930
|
+
hide_health_check_logs: bool = True, # whether to hide health check logs in info level
|
|
931
|
+
health_check_endpoints: Optional[List[str]] = None, # endpoints to check health of the app
|
|
908
932
|
):
|
|
933
|
+
"""Initialize the Supervisely Application.
|
|
934
|
+
|
|
935
|
+
:param layout: Main layout of the application.
|
|
936
|
+
:type layout: Widget
|
|
937
|
+
:param templates_dir: Directory with Jinja2 templates. It is preferred to use `layout` instead of `templates_dir`.
|
|
938
|
+
:type templates_dir: str, optional
|
|
939
|
+
:param static_dir: Directory with static files (e.g. CSS, JS), used for serving static content.
|
|
940
|
+
:type static_dir: str, optional
|
|
941
|
+
:param hot_reload: Whether to enable hot reload during development (default is False).
|
|
942
|
+
:type hot_reload: bool, optional
|
|
943
|
+
:param session_info_extra_content: Additional content to be displayed in the session info area.
|
|
944
|
+
:type session_info_extra_content: Widget, optional
|
|
945
|
+
:param session_info_solid: Whether to use solid background for the session info area.
|
|
946
|
+
:type session_info_solid: bool, optional
|
|
947
|
+
:param ready_check_function: Function to check if the app is ready for requests.
|
|
948
|
+
:type ready_check_function: Callable, optional
|
|
949
|
+
:param show_header: Whether to show the header in the application.
|
|
950
|
+
:type show_header: bool, optional
|
|
951
|
+
:param hide_health_check_logs: Whether to hide health check logs in info level.
|
|
952
|
+
:type hide_health_check_logs: bool, optional
|
|
953
|
+
:param health_check_endpoints: List of additional endpoints to check health of the app.
|
|
954
|
+
Add your custom endpoints here to be able to manage logging of health check requests on info level with `hide_health_check_logs`.
|
|
955
|
+
:type health_check_endpoints: List[str], optional
|
|
956
|
+
"""
|
|
909
957
|
self._favicon = os.environ.get("icon", "https://cdn.supervisely.com/favicon.ico")
|
|
910
958
|
JinjaWidgets().context["__favicon__"] = self._favicon
|
|
911
959
|
JinjaWidgets().context["__no_html_mode__"] = True
|
|
@@ -980,6 +1028,17 @@ class Application(metaclass=Singleton):
|
|
|
980
1028
|
hot_reload=hot_reload,
|
|
981
1029
|
before_shutdown_callbacks=self._before_shutdown_callbacks,
|
|
982
1030
|
)
|
|
1031
|
+
|
|
1032
|
+
# add filter to hide health check logs for info level
|
|
1033
|
+
if health_check_endpoints is None or len(health_check_endpoints) == 0:
|
|
1034
|
+
self._health_check_endpoints = HEALTH_ENDPOINTS
|
|
1035
|
+
else:
|
|
1036
|
+
health_check_endpoints = [endpoint.strip() for endpoint in health_check_endpoints]
|
|
1037
|
+
self._health_check_endpoints = HEALTH_ENDPOINTS + health_check_endpoints
|
|
1038
|
+
|
|
1039
|
+
if hide_health_check_logs:
|
|
1040
|
+
self._setup_health_check_filter()
|
|
1041
|
+
|
|
983
1042
|
self.test_client = TestClient(self._fastapi)
|
|
984
1043
|
|
|
985
1044
|
if not headless:
|
|
@@ -1126,6 +1185,34 @@ class Application(metaclass=Singleton):
|
|
|
1126
1185
|
def set_ready_check_function(self, func: Callable):
|
|
1127
1186
|
self._ready_check_function = func
|
|
1128
1187
|
|
|
1188
|
+
def _setup_health_check_filter(self):
|
|
1189
|
+
"""Setup filter to hide health check logs for info level."""
|
|
1190
|
+
|
|
1191
|
+
class HealthCheckFilter(logging.Filter):
|
|
1192
|
+
def __init__(self, app_instance):
|
|
1193
|
+
super().__init__()
|
|
1194
|
+
self.app: Application = app_instance
|
|
1195
|
+
|
|
1196
|
+
def filter(self, record):
|
|
1197
|
+
# Hide health check requests if NOT in debug mode
|
|
1198
|
+
if not self.app._fastapi.debug and hasattr(record, "getMessage"):
|
|
1199
|
+
message = record.getMessage()
|
|
1200
|
+
# Check if the message contains health check paths
|
|
1201
|
+
if any(path in message for path in self.app._health_check_endpoints):
|
|
1202
|
+
return False
|
|
1203
|
+
return True
|
|
1204
|
+
|
|
1205
|
+
# Apply filter to uvicorn access logger
|
|
1206
|
+
health_filter = HealthCheckFilter(self)
|
|
1207
|
+
uvicorn_logger = logging.getLogger("uvicorn.access")
|
|
1208
|
+
|
|
1209
|
+
# Remove old filters of this type, if any (for safety)
|
|
1210
|
+
uvicorn_logger.filters = [
|
|
1211
|
+
f for f in uvicorn_logger.filters if not isinstance(f, HealthCheckFilter)
|
|
1212
|
+
]
|
|
1213
|
+
|
|
1214
|
+
uvicorn_logger.addFilter(health_filter)
|
|
1215
|
+
|
|
1129
1216
|
|
|
1130
1217
|
def set_autostart_flag_from_state(default: Optional[str] = None):
|
|
1131
1218
|
"""Set `autostart` flag recieved from task state. Env name: `modal.state.autostart`.
|
|
@@ -34,7 +34,6 @@ import supervisely.io.env as sly_env
|
|
|
34
34
|
import supervisely.io.fs as sly_fs
|
|
35
35
|
import supervisely.io.json as sly_json
|
|
36
36
|
import supervisely.nn.inference.gui as GUI
|
|
37
|
-
from supervisely.nn.experiments import ExperimentInfo
|
|
38
37
|
from supervisely import DatasetInfo, batched
|
|
39
38
|
from supervisely._utils import (
|
|
40
39
|
add_callback,
|
|
@@ -69,13 +68,14 @@ from supervisely.decorators.inference import (
|
|
|
69
68
|
from supervisely.geometry.any_geometry import AnyGeometry
|
|
70
69
|
from supervisely.imaging.color import get_predefined_colors
|
|
71
70
|
from supervisely.io.fs import list_files
|
|
71
|
+
from supervisely.nn.experiments import ExperimentInfo
|
|
72
72
|
from supervisely.nn.inference.cache import InferenceImageCache
|
|
73
73
|
from supervisely.nn.inference.inference_request import (
|
|
74
74
|
InferenceRequest,
|
|
75
75
|
InferenceRequestsManager,
|
|
76
76
|
)
|
|
77
77
|
from supervisely.nn.inference.uploader import Uploader
|
|
78
|
-
from supervisely.nn.model.model_api import Prediction
|
|
78
|
+
from supervisely.nn.model.model_api import ModelAPI, Prediction
|
|
79
79
|
from supervisely.nn.prediction_dto import Prediction as PredictionDTO
|
|
80
80
|
from supervisely.nn.utils import (
|
|
81
81
|
CheckpointInfo,
|
|
@@ -93,7 +93,6 @@ from supervisely.project.project_meta import ProjectMeta
|
|
|
93
93
|
from supervisely.sly_logger import logger
|
|
94
94
|
from supervisely.task.progress import Progress
|
|
95
95
|
from supervisely.video.video import ALLOWED_VIDEO_EXTENSIONS, VideoFrameReader
|
|
96
|
-
from supervisely.nn.model.model_api import ModelAPI
|
|
97
96
|
|
|
98
97
|
try:
|
|
99
98
|
from typing import Literal
|
|
@@ -383,7 +382,7 @@ class Inference:
|
|
|
383
382
|
if m_name and m_name.lower() == model.lower():
|
|
384
383
|
return m
|
|
385
384
|
return None
|
|
386
|
-
|
|
385
|
+
|
|
387
386
|
runtime = get_runtime(runtime)
|
|
388
387
|
logger.debug(f"Runtime: {runtime}")
|
|
389
388
|
|
|
@@ -869,7 +868,7 @@ class Inference:
|
|
|
869
868
|
"""
|
|
870
869
|
team_id = sly_env.team_id()
|
|
871
870
|
local_model_files = {}
|
|
872
|
-
|
|
871
|
+
|
|
873
872
|
# Sort files to download 'checkpoint' first
|
|
874
873
|
files_order = sorted(model_files.keys(), key=lambda x: (0 if x == "checkpoint" else 1, x))
|
|
875
874
|
for file in files_order:
|
|
@@ -910,12 +909,12 @@ class Inference:
|
|
|
910
909
|
logger.debug("Model files will be downloaded from Team Files")
|
|
911
910
|
local_model_files[file] = file_path
|
|
912
911
|
continue
|
|
913
|
-
|
|
912
|
+
|
|
914
913
|
local_model_files[file] = file_path
|
|
915
914
|
if log_progress:
|
|
916
915
|
self.gui.download_progress.hide()
|
|
917
916
|
return local_model_files
|
|
918
|
-
|
|
917
|
+
|
|
919
918
|
def _get_deploy_parameters_from_custom_checkpoint(self, checkpoint_path: str, device: str, runtime: str) -> dict:
|
|
920
919
|
def _read_experiment_info(artifacts_dir: str) -> Optional[dict]:
|
|
921
920
|
exp_path = os.path.join(artifacts_dir, "experiment_info.json")
|
|
@@ -1159,6 +1158,8 @@ class Inference:
|
|
|
1159
1158
|
if model_source == ModelSource.CUSTOM:
|
|
1160
1159
|
self._set_model_meta_custom_model(model_info)
|
|
1161
1160
|
self._set_checkpoint_info_custom_model(deploy_params)
|
|
1161
|
+
elif model_source == ModelSource.PRETRAINED:
|
|
1162
|
+
self._set_checkpoint_info_pretrained(deploy_params)
|
|
1162
1163
|
|
|
1163
1164
|
try:
|
|
1164
1165
|
if is_production():
|
|
@@ -1232,6 +1233,19 @@ class Inference:
|
|
|
1232
1233
|
model_source=ModelSource.CUSTOM,
|
|
1233
1234
|
)
|
|
1234
1235
|
|
|
1236
|
+
def _set_checkpoint_info_pretrained(self, deploy_params: dict):
|
|
1237
|
+
checkpoint_name = os.path.basename(deploy_params["model_files"]["checkpoint"])
|
|
1238
|
+
model_name = deploy_params["model_info"]["model_name"]
|
|
1239
|
+
checkpoint_url = deploy_params["model_info"]["meta"]["model_files"]["checkpoint"]
|
|
1240
|
+
model_source = ModelSource.PRETRAINED
|
|
1241
|
+
self.checkpoint_info = CheckpointInfo(
|
|
1242
|
+
checkpoint_name=checkpoint_name,
|
|
1243
|
+
model_name=model_name,
|
|
1244
|
+
architecture=self.FRAMEWORK_NAME,
|
|
1245
|
+
checkpoint_url=checkpoint_url,
|
|
1246
|
+
model_source=model_source,
|
|
1247
|
+
)
|
|
1248
|
+
|
|
1235
1249
|
def shutdown_model(self):
|
|
1236
1250
|
self._model_served = False
|
|
1237
1251
|
self._model_frozen = False
|
|
@@ -1447,7 +1461,7 @@ class Inference:
|
|
|
1447
1461
|
if api is None:
|
|
1448
1462
|
api = self.api
|
|
1449
1463
|
return api
|
|
1450
|
-
|
|
1464
|
+
|
|
1451
1465
|
def _inference_auto(
|
|
1452
1466
|
self,
|
|
1453
1467
|
source: List[Union[str, np.ndarray]],
|
|
@@ -36,10 +36,10 @@ supervisely/api/import_storage_api.py,sha256=BDCgmR0Hv6OoiRHLCVPKt3iDxSVlQp1WrnK
|
|
|
36
36
|
supervisely/api/issues_api.py,sha256=BqDJXmNoTzwc3xe6_-mA7FDFC5QQ-ahGbXk_HmpkSeQ,17925
|
|
37
37
|
supervisely/api/labeling_job_api.py,sha256=G2_BV_WtA2lAhfw_nAQmWmv1P-pwimD0ba9GVKoGjiA,55537
|
|
38
38
|
supervisely/api/labeling_queue_api.py,sha256=ilNjAL1d9NSa9yabQn6E-W26YdtooT3ZGXIFZtGnAvY,30158
|
|
39
|
-
supervisely/api/module_api.py,sha256
|
|
39
|
+
supervisely/api/module_api.py,sha256=-PVLcsicG47hVj_4vN8cuc0X-gqadsBJTj0ZxQOcO9g,46384
|
|
40
40
|
supervisely/api/object_class_api.py,sha256=7-npNFMYjWNtSXYZg6syc6bX56_oCzDU2kFRPGQWCwA,10399
|
|
41
41
|
supervisely/api/plugin_api.py,sha256=SFm0IlTTOjuHBLUMgG4d4k6U3cWJocE-SVb-f08fwMQ,5286
|
|
42
|
-
supervisely/api/project_api.py,sha256=
|
|
42
|
+
supervisely/api/project_api.py,sha256=5WR4bNIq3eUlAkJga8DP_rYFIXKIU-myDlKNv911ssA,96449
|
|
43
43
|
supervisely/api/project_class_api.py,sha256=5cyjdGPPb2tpttu5WmYoOxUNiDxqiojschkhZumF0KM,1426
|
|
44
44
|
supervisely/api/remote_storage_api.py,sha256=1O4rTIwW8s9gxC00yvFuKbEMGNsa7YSRlZ8j494ARwY,17793
|
|
45
45
|
supervisely/api/report_api.py,sha256=Om7CGulUbQ4BuJ16eDtz7luLe0JQNqab-LoLpUXu7YE,7123
|
|
@@ -99,7 +99,7 @@ supervisely/app/fastapi/index.html,sha256=dz_e-0RE5ZbOU0ToUaEHe1ROI6Tc3SPL-mHt1C
|
|
|
99
99
|
supervisely/app/fastapi/no_html_main.html,sha256=NhQP7noyORBx72lFh1CQKgBRupkWjiq6Gaw-9Hkvg7c,37
|
|
100
100
|
supervisely/app/fastapi/offline.py,sha256=CwMMkJ1frD6wiZS-SEoNDtQ1UJcJe1Ob6ohE3r4CQL8,7414
|
|
101
101
|
supervisely/app/fastapi/request.py,sha256=NU7rKmxJ1pfkDZ7_yHckRcRAueJRQIqCor11UO2OHr8,766
|
|
102
|
-
supervisely/app/fastapi/subapp.py,sha256=
|
|
102
|
+
supervisely/app/fastapi/subapp.py,sha256=mhmXs64qDWBOBN1lfgeoxOHkelAdQwfM0CoD4sr-xB0,50132
|
|
103
103
|
supervisely/app/fastapi/templating.py,sha256=pcghBW2OWVrNtplZuYa-mx04ektLiSvnBg-mhmyCoJc,2929
|
|
104
104
|
supervisely/app/fastapi/utils.py,sha256=t_UquzlFrdkKtAJmH6eJ279pE8Aa3BaIu4XjX-SEaIE,946
|
|
105
105
|
supervisely/app/fastapi/websocket.py,sha256=TlRSPOAhRItTv1HGvdukK1ZvhRjMUxRa-lJlsRR9rJw,1308
|
|
@@ -894,7 +894,7 @@ supervisely/nn/benchmark/visualization/widgets/table/__init__.py,sha256=47DEQpj8
|
|
|
894
894
|
supervisely/nn/benchmark/visualization/widgets/table/table.py,sha256=atmDnF1Af6qLQBUjLhK18RMDKAYlxnsuVHMSEa5a-e8,4319
|
|
895
895
|
supervisely/nn/inference/__init__.py,sha256=QFukX2ip-U7263aEPCF_UCFwj6EujbMnsgrXp5Bbt8I,1623
|
|
896
896
|
supervisely/nn/inference/cache.py,sha256=rfmb1teJ9lNDfisUSh6bwDCVkPZocn8GMvDgLQktnbo,35023
|
|
897
|
-
supervisely/nn/inference/inference.py,sha256=
|
|
897
|
+
supervisely/nn/inference/inference.py,sha256=QTdx7RLekCs5JksRSc2skOlA9buNl05FtB6j9LcgKMQ,199183
|
|
898
898
|
supervisely/nn/inference/inference_request.py,sha256=y6yw0vbaRRcEBS27nq3y0sL6Gmq2qLA_Bm0GrnJGegE,14267
|
|
899
899
|
supervisely/nn/inference/session.py,sha256=dIg2F-OBl68pUzcmtmcI0YQIp1WWNnrJTVMjwFN91Q4,35824
|
|
900
900
|
supervisely/nn/inference/uploader.py,sha256=21a9coOimCHhEqAbV-llZWcp12847DEMoQp3N16bpK0,5425
|
|
@@ -1115,9 +1115,9 @@ supervisely/worker_proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
|
1115
1115
|
supervisely/worker_proto/worker_api_pb2.py,sha256=VQfi5JRBHs2pFCK1snec3JECgGnua3Xjqw_-b3aFxuM,59142
|
|
1116
1116
|
supervisely/worker_proto/worker_api_pb2_grpc.py,sha256=3BwQXOaP9qpdi0Dt9EKG--Lm8KGN0C5AgmUfRv77_Jk,28940
|
|
1117
1117
|
supervisely_lib/__init__.py,sha256=7-3QnN8Zf0wj8NCr2oJmqoQWMKKPKTECvjH9pd2S5vY,159
|
|
1118
|
-
supervisely-6.73.
|
|
1119
|
-
supervisely-6.73.
|
|
1120
|
-
supervisely-6.73.
|
|
1121
|
-
supervisely-6.73.
|
|
1122
|
-
supervisely-6.73.
|
|
1123
|
-
supervisely-6.73.
|
|
1118
|
+
supervisely-6.73.414.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
1119
|
+
supervisely-6.73.414.dist-info/METADATA,sha256=SzMumx5iuDhjBRPoWi8EcGKiaIIhxoVUvBJXPiED05Q,35254
|
|
1120
|
+
supervisely-6.73.414.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
|
|
1121
|
+
supervisely-6.73.414.dist-info/entry_points.txt,sha256=U96-5Hxrp2ApRjnCoUiUhWMqijqh8zLR03sEhWtAcms,102
|
|
1122
|
+
supervisely-6.73.414.dist-info/top_level.txt,sha256=kcFVwb7SXtfqZifrZaSE3owHExX4gcNYe7Q2uoby084,28
|
|
1123
|
+
supervisely-6.73.414.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|