fix(studio): harden runtime integration and dependency defaults

Stabilize studio publish/visualization flow and tighten export behavior while aligning project dependencies with the monorepo runtime expectations.
This commit is contained in:
2026-03-03 17:59:56 +08:00
parent 00fcda4fe3
commit 967a10c10e
7 changed files with 122 additions and 87 deletions
+37 -7
View File
@@ -1,7 +1,9 @@
from __future__ import annotations
from collections.abc import Callable
import copy
from contextlib import suppress
import inspect
import logging
from pathlib import Path
import time
@@ -146,6 +148,8 @@ class ScoliosisPipeline:
_visualizer: OpenCVVisualizer | None
_last_viz_payload: _VizPayload | None
_frame_pacer: _FramePacer | None
_visualizer_accepts_pose_data: bool | None
_visualizer_signature_owner: object | None
def __init__(
self,
@@ -205,6 +209,31 @@ class ScoliosisPipeline:
self._visualizer = None
self._last_viz_payload = None
self._frame_pacer = _FramePacer(target_fps) if target_fps is not None else None
self._visualizer_accepts_pose_data = None
self._visualizer_signature_owner = None
def _detect_visualizer_pose_kwarg(self) -> bool:
visualizer = self._visualizer
if visualizer is None:
return False
if (
self._visualizer_signature_owner is visualizer
and self._visualizer_accepts_pose_data is not None
):
return self._visualizer_accepts_pose_data
update_fn = getattr(visualizer, "update", None)
if update_fn is None or not callable(update_fn):
self._visualizer_signature_owner = visualizer
self._visualizer_accepts_pose_data = False
return False
try:
signature = inspect.signature(update_fn)
accepts_pose_data = "pose_data" in signature.parameters
except (ValueError, TypeError):
accepts_pose_data = False
self._visualizer_signature_owner = visualizer
self._visualizer_accepts_pose_data = accepts_pose_data
return accepts_pose_data
@staticmethod
def _extract_int(meta: dict[str, object], key: str, fallback: int) -> int:
@@ -459,7 +488,7 @@ class ScoliosisPipeline:
viz_payload = None
try:
viz_payload = self.process_frame(frame_u8, metadata)
except (RuntimeError, ValueError, TypeError, OSError) as frame_error:
except (RuntimeError, ValueError, OSError) as frame_error:
logger.warning(
"Skipping frame %d due to processing error: %s",
frame_idx,
@@ -474,6 +503,9 @@ class ScoliosisPipeline:
viz_payload_dict = cast(_VizPayload, viz_payload)
cached: _VizPayload = {}
for k, v in viz_payload_dict.items():
if k == "pose" and isinstance(v, dict):
cached[k] = cast(dict[str, object], copy.deepcopy(v))
continue
copy_method = cast(
Callable[[], object] | None, getattr(v, "copy", None)
)
@@ -531,8 +563,7 @@ class ScoliosisPipeline:
confidence = None
pose_data = None
# Try keyword arg for pose_data (backward compatible with old signatures)
try:
if self._detect_visualizer_pose_kwarg():
keep_running = self._visualizer.update(
frame_u8,
bbox,
@@ -546,8 +577,7 @@ class ScoliosisPipeline:
ema_fps,
pose_data=pose_data,
)
except TypeError:
# Fallback for legacy visualizers that don't accept pose_data
else:
keep_running = self._visualizer.update(
frame_u8,
bbox,
@@ -676,7 +706,7 @@ class ScoliosisPipeline:
"frame": pa.array(frames, type=pa.int64()),
"track_id": pa.array(track_ids, type=pa.int64()),
"timestamp_ns": pa.array(timestamps, type=pa.int64()),
"silhouette": pa.array(silhouettes, type=pa.list_(pa.float64())),
"silhouette": pa.array(silhouettes, type=pa.list_(pa.float32())),
}
)
@@ -746,7 +776,7 @@ class ScoliosisPipeline:
track_ids.append(result["track_id"])
labels.append(result["label"])
confidences.append(result["confidence"])
windows.append(result["window"])
windows.append(int(result["window"]))
timestamps.append(result["timestamp_ns"])
table = pa.table(