@camstack/addon-pipeline 1.2.228 → 1.2.231

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.
Files changed (123) hide show
  1. package/dist/audio-analyzer/index.js +2 -2
  2. package/dist/audio-analyzer/index.mjs +2 -2
  3. package/dist/{default-detection-model-D0Em2AXV.js → default-detection-model-C7fsHtHQ.js} +1 -1
  4. package/dist/{default-detection-model-C4C723aL.mjs → default-detection-model-DC4yVaFM.mjs} +1 -1
  5. package/dist/detection-pipeline/index.js +4 -4
  6. package/dist/detection-pipeline/index.mjs +4 -4
  7. package/dist/{dist-Dcp-eVbN.mjs → dist-DTPYOq7J.mjs} +274 -9
  8. package/dist/{dist-Dwhv3eOj.js → dist-p5lZW8pr.js} +279 -8
  9. package/dist/motion-wasm/index.js +1 -1
  10. package/dist/motion-wasm/index.mjs +1 -1
  11. package/dist/{node-CSDLSs7t.js → node-CGVUk5S9.js} +1 -1
  12. package/dist/{node-ClxCG7kX.mjs → node-Cz92fVGW.mjs} +1 -1
  13. package/dist/pipeline-runner/index.js +4 -4
  14. package/dist/pipeline-runner/index.mjs +4 -4
  15. package/dist/{process-memory-CJ9kKdO6.mjs → process-memory-BwijtYhM.mjs} +1 -1
  16. package/dist/{process-memory-DYCrYZZl.js → process-memory-COypUeEc.js} +1 -1
  17. package/dist/recorder/index.js +3 -3
  18. package/dist/recorder/index.mjs +3 -3
  19. package/dist/{restream-intent-B1Difcnq.js → restream-intent-C4adO0P8.js} +5 -0
  20. package/dist/{restream-intent-DWe2kInY.mjs → restream-intent-MeJhGCK9.mjs} +5 -0
  21. package/dist/{segment-demux-js-CelIP7H_.js → segment-demux-js-B-yzHYVc.js} +1 -1
  22. package/dist/{segment-demux-js-BWZQJ1AS.mjs → segment-demux-js-CyaNDtrw.mjs} +1 -1
  23. package/dist/stream-broker/_stub.js +2 -2
  24. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BOpWY0lf.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B9KO-igY.mjs} +3 -3
  25. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Bc-sSkWV.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-mnvO611B.mjs} +1 -1
  26. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-B84T0KI-.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-JoG-yDpV.mjs} +1 -1
  27. package/dist/stream-broker/demux-worker-child.js +1 -1
  28. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  29. package/dist/stream-broker/{hostInit-D79Uznvf.mjs → hostInit-CDwsQyIR.mjs} +3 -3
  30. package/dist/stream-broker/index.js +1380 -90
  31. package/dist/stream-broker/index.mjs +1380 -90
  32. package/dist/stream-broker/remoteEntry.js +1 -1
  33. package/package.json +1 -1
  34. package/python/inference_pool.py +39 -9
  35. package/python/test_inference_pool_ov_requests.py +79 -0
  36. package/python/__pycache__/inference_pool.cpython-312.pyc +0 -0
  37. package/python/__pycache__/inference_pool.cpython-313.pyc +0 -0
  38. package/python/__pycache__/inference_pool.cpython-314.pyc +0 -0
  39. package/python/__pycache__/test_inference_pool_backpressure.cpython-312-pytest-9.1.1.pyc +0 -0
  40. package/python/__pycache__/test_inference_pool_backpressure.cpython-312.pyc +0 -0
  41. package/python/__pycache__/test_inference_pool_backpressure.cpython-314.pyc +0 -0
  42. package/python/__pycache__/test_inference_pool_coreml_cache.cpython-312-pytest-9.1.1.pyc +0 -0
  43. package/python/__pycache__/test_inference_pool_coreml_cache.cpython-312.pyc +0 -0
  44. package/python/__pycache__/test_inference_pool_coreml_cache.cpython-314.pyc +0 -0
  45. package/python/__pycache__/test_inference_pool_deadline.cpython-314.pyc +0 -0
  46. package/python/__pycache__/test_inference_pool_device_selection.cpython-312-pytest-9.1.1.pyc +0 -0
  47. package/python/__pycache__/test_inference_pool_device_selection.cpython-312.pyc +0 -0
  48. package/python/__pycache__/test_inference_pool_device_selection.cpython-314.pyc +0 -0
  49. package/python/__pycache__/test_inference_pool_layout.cpython-312-pytest-9.1.1.pyc +0 -0
  50. package/python/__pycache__/test_inference_pool_layout.cpython-314.pyc +0 -0
  51. package/python/__pycache__/test_inference_pool_memstats.cpython-312-pytest-9.1.1.pyc +0 -0
  52. package/python/__pycache__/test_inference_pool_memstats.cpython-312.pyc +0 -0
  53. package/python/__pycache__/test_inference_pool_memstats.cpython-314.pyc +0 -0
  54. package/python/__pycache__/test_inference_pool_ov_ppp.cpython-312-pytest-9.1.1.pyc +0 -0
  55. package/python/__pycache__/test_inference_pool_ov_ppp.cpython-312.pyc +0 -0
  56. package/python/__pycache__/test_inference_pool_ov_ppp.cpython-314.pyc +0 -0
  57. package/python/__pycache__/test_inference_pool_preprocess.cpython-312-pytest-9.1.1.pyc +0 -0
  58. package/python/__pycache__/test_inference_pool_preprocess.cpython-314.pyc +0 -0
  59. package/python/__pycache__/test_inference_pool_reconfigure.cpython-314.pyc +0 -0
  60. package/python/__pycache__/test_inference_pool_static_batch.cpython-312-pytest-9.1.1.pyc +0 -0
  61. package/python/__pycache__/test_inference_pool_static_batch.cpython-312.pyc +0 -0
  62. package/python/__pycache__/test_inference_pool_static_batch.cpython-314.pyc +0 -0
  63. package/python/__pycache__/yamnet_audio.cpython-314.pyc +0 -0
  64. package/python/postprocessors/__pycache__/__init__.cpython-312.pyc +0 -0
  65. package/python/postprocessors/__pycache__/__init__.cpython-313.pyc +0 -0
  66. package/python/postprocessors/__pycache__/__init__.cpython-314.pyc +0 -0
  67. package/python/postprocessors/__pycache__/_safety.cpython-313.pyc +0 -0
  68. package/python/postprocessors/__pycache__/arcface.cpython-312.pyc +0 -0
  69. package/python/postprocessors/__pycache__/arcface.cpython-313.pyc +0 -0
  70. package/python/postprocessors/__pycache__/arcface.cpython-314.pyc +0 -0
  71. package/python/postprocessors/__pycache__/clip.cpython-312.pyc +0 -0
  72. package/python/postprocessors/__pycache__/clip.cpython-313.pyc +0 -0
  73. package/python/postprocessors/__pycache__/clip.cpython-314.pyc +0 -0
  74. package/python/postprocessors/__pycache__/ctc.cpython-312.pyc +0 -0
  75. package/python/postprocessors/__pycache__/ctc.cpython-313.pyc +0 -0
  76. package/python/postprocessors/__pycache__/ctc.cpython-314.pyc +0 -0
  77. package/python/postprocessors/__pycache__/plate_slots.cpython-312.pyc +0 -0
  78. package/python/postprocessors/__pycache__/plate_slots.cpython-314.pyc +0 -0
  79. package/python/postprocessors/__pycache__/rfdetr.cpython-312.pyc +0 -0
  80. package/python/postprocessors/__pycache__/rfdetr.cpython-314.pyc +0 -0
  81. package/python/postprocessors/__pycache__/saliency.cpython-312.pyc +0 -0
  82. package/python/postprocessors/__pycache__/saliency.cpython-313.pyc +0 -0
  83. package/python/postprocessors/__pycache__/saliency.cpython-314.pyc +0 -0
  84. package/python/postprocessors/__pycache__/scrfd.cpython-312.pyc +0 -0
  85. package/python/postprocessors/__pycache__/scrfd.cpython-313.pyc +0 -0
  86. package/python/postprocessors/__pycache__/scrfd.cpython-314.pyc +0 -0
  87. package/python/postprocessors/__pycache__/softmax.cpython-312.pyc +0 -0
  88. package/python/postprocessors/__pycache__/softmax.cpython-313.pyc +0 -0
  89. package/python/postprocessors/__pycache__/softmax.cpython-314.pyc +0 -0
  90. package/python/postprocessors/__pycache__/ssd.cpython-312.pyc +0 -0
  91. package/python/postprocessors/__pycache__/ssd.cpython-313.pyc +0 -0
  92. package/python/postprocessors/__pycache__/ssd.cpython-314.pyc +0 -0
  93. package/python/postprocessors/__pycache__/test_clip.cpython-312-pytest-9.1.1.pyc +0 -0
  94. package/python/postprocessors/__pycache__/test_clip.cpython-313-pytest-9.1.1.pyc +0 -0
  95. package/python/postprocessors/__pycache__/test_ctc.cpython-312-pytest-9.1.1.pyc +0 -0
  96. package/python/postprocessors/__pycache__/test_ctc.cpython-312.pyc +0 -0
  97. package/python/postprocessors/__pycache__/test_ctc.cpython-313-pytest-9.1.1.pyc +0 -0
  98. package/python/postprocessors/__pycache__/test_ctc.cpython-313.pyc +0 -0
  99. package/python/postprocessors/__pycache__/test_ctc.cpython-314.pyc +0 -0
  100. package/python/postprocessors/__pycache__/test_plate_slots.cpython-312-pytest-9.1.1.pyc +0 -0
  101. package/python/postprocessors/__pycache__/test_rfdetr.cpython-312-pytest-9.1.1.pyc +0 -0
  102. package/python/postprocessors/__pycache__/test_rfdetr.cpython-314.pyc +0 -0
  103. package/python/postprocessors/__pycache__/test_saliency.cpython-312-pytest-9.1.1.pyc +0 -0
  104. package/python/postprocessors/__pycache__/test_saliency.cpython-313-pytest-9.1.1.pyc +0 -0
  105. package/python/postprocessors/__pycache__/test_saliency.cpython-314.pyc +0 -0
  106. package/python/postprocessors/__pycache__/test_softmax.cpython-312-pytest-9.1.1.pyc +0 -0
  107. package/python/postprocessors/__pycache__/test_softmax.cpython-314.pyc +0 -0
  108. package/python/postprocessors/__pycache__/test_yolo_nms.cpython-312-pytest-9.1.1.pyc +0 -0
  109. package/python/postprocessors/__pycache__/test_yolo_nms.cpython-314.pyc +0 -0
  110. package/python/postprocessors/__pycache__/test_yolo_nms.cpython-39.pyc +0 -0
  111. package/python/postprocessors/__pycache__/test_yolonas.cpython-312-pytest-9.1.1.pyc +0 -0
  112. package/python/postprocessors/__pycache__/yamnet.cpython-312.pyc +0 -0
  113. package/python/postprocessors/__pycache__/yamnet.cpython-313.pyc +0 -0
  114. package/python/postprocessors/__pycache__/yamnet.cpython-314.pyc +0 -0
  115. package/python/postprocessors/__pycache__/yolo.cpython-312.pyc +0 -0
  116. package/python/postprocessors/__pycache__/yolo.cpython-313.pyc +0 -0
  117. package/python/postprocessors/__pycache__/yolo.cpython-314.pyc +0 -0
  118. package/python/postprocessors/__pycache__/yolo.cpython-39.pyc +0 -0
  119. package/python/postprocessors/__pycache__/yolo_seg.cpython-312.pyc +0 -0
  120. package/python/postprocessors/__pycache__/yolo_seg.cpython-313.pyc +0 -0
  121. package/python/postprocessors/__pycache__/yolo_seg.cpython-314.pyc +0 -0
  122. package/python/postprocessors/__pycache__/yolonas.cpython-312.pyc +0 -0
  123. package/python/postprocessors/__pycache__/yolonas.cpython-314.pyc +0 -0
@@ -30,7 +30,7 @@ async function d(e) {
30
30
  }
31
31
  }
32
32
  async function f() {
33
- return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BOpWY0lf.mjs")).catch((e) => {
33
+ return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B9KO-igY.mjs")).catch((e) => {
34
34
  throw l = void 0, e;
35
35
  }), l;
36
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-pipeline",
3
- "version": "1.2.228",
3
+ "version": "1.2.231",
4
4
  "description": "Pipeline bundle — runner, detection, motion, audio + stream broker. Multi-entry npm package shipping pipeline addons under a single bundle.",
5
5
  "keywords": [
6
6
  "camstack",
@@ -279,6 +279,33 @@ _ov_optimal_reqs: int = 0
279
279
  # query the device's optimal request count). ≈ measured optimal on Intel
280
280
  # CPU/iGPU/NPU (4-5). Threads idle-block on infer, so over-provisioning is cheap.
281
281
  OV_DEFAULT_CONCURRENCY: int = 4
282
+ # Floor for the InferRequest / predict-pool size when the device DOES answer.
283
+ # Under PERFORMANCE_HINT=LATENCY an Intel iGPU typically reports 1-2: one
284
+ # execution stream, and the extra requests buy pipelining the LATENCY hint is
285
+ # not asking for. Two rather than one so a second frame can be preprocessed
286
+ # while the first infers, without paying for four.
287
+ OV_MIN_CONCURRENCY: int = 2
288
+
289
+
290
+ def _ov_request_count(optimal: int) -> int:
291
+ """How many InferRequests to create up front.
292
+
293
+ ONE function because two call sites must agree: the per-model request pool
294
+ in `_load_model` and the predict-pool worker count in `_run`. They used to
295
+ compute it separately with the same expression, and the expression was
296
+ `max(optimal, OV_DEFAULT_CONCURRENCY)` — a FLOOR of four, not the fallback
297
+ its own comment described. On this hub that meant every model held four
298
+ GPU-resident InferRequests forever, per pool, while the device was asking
299
+ for one or two and `intel_gpu_top` showed the GPU 92% idle. Creating a
300
+ VAAPI device for an HEVC decode then failed with ENOMEM.
301
+
302
+ `optimal <= 0` means the device could not be asked — models load lazily,
303
+ the property is missing — and UNKNOWN is not SMALL: that case keeps the
304
+ old default rather than guessing low.
305
+ """
306
+ if optimal <= 0:
307
+ return OV_DEFAULT_CONCURRENCY
308
+ return max(optimal, OV_MIN_CONCURRENCY)
282
309
  # OpenVINO device decision. AUTO is NEVER used: the AUTO plugin runs inference
283
310
  # on a CPU "helper" while the GPU JIT-compiles, then hands the model over to the
284
311
  # GPU mid-flight. InferRequests created during that window bind to the
@@ -978,17 +1005,18 @@ def _load_model(slot: ModelSlot, config: dict) -> None:
978
1005
  except Exception:
979
1006
  pass
980
1007
 
981
- # Size N ONCE from the device's optimal infer-request count (fallback to
982
- # OV_DEFAULT_CONCURRENCY when the property is missing/zero) so it matches
983
- # the predict-pool worker count sized in _run(). Both the sync request-pool
984
- # and the async queue below are sized to this same N.
1008
+ # Size N ONCE from the device's optimal infer-request count (falling back
1009
+ # to OV_DEFAULT_CONCURRENCY only when the property is missing/zero) so it
1010
+ # matches the predict-pool worker count sized in _run(). Both the sync
1011
+ # request-pool and the async queue below are sized to this same N, and
1012
+ # both go through `_ov_request_count` so they cannot drift apart.
985
1013
  import queue as _queue
986
1014
 
987
1015
  try:
988
1016
  _n_req = int(compiled.get_property("OPTIMAL_NUMBER_OF_INFER_REQUESTS"))
989
1017
  except Exception:
990
1018
  _n_req = 0
991
- _n_req = max(_n_req, OV_DEFAULT_CONCURRENCY)
1019
+ _n_req = _ov_request_count(_n_req)
992
1020
 
993
1021
  if _OV_ASYNC_ENABLED:
994
1022
  # Async pipelining path (CAMSTACK_OV_ASYNC=1). An ov.AsyncInferQueue of
@@ -2386,11 +2414,13 @@ async def _run() -> None:
2386
2414
  # model in _load_model) is actually fed by concurrent predict workers. The
2387
2415
  # addon passes concurrency=1 (it predates the request pool) and models load
2388
2416
  # lazily AFTER this point, so we can't read OPTIMAL_NUMBER_OF_INFER_REQUESTS
2389
- # here — default to OV_DEFAULT_CONCURRENCY (≈ the measured optimal of 4-5
2390
- # on Intel CPU/iGPU/NPU). If startup-loaded models report a higher optimal,
2391
- # use that. The per-model InferRequest pool is sized to the same target.
2417
+ # here — `_ov_request_count(0)` then answers OV_DEFAULT_CONCURRENCY, because
2418
+ # a device that could not be asked is UNKNOWN and not small. A startup-loaded
2419
+ # model that DID answer sizes it to what the device actually wants. The
2420
+ # per-model InferRequest pool goes through the same function, so the two
2421
+ # cannot drift apart.
2392
2422
  if runtime == "openvino":
2393
- target = max(_ov_optimal_reqs, OV_DEFAULT_CONCURRENCY)
2423
+ target = _ov_request_count(_ov_optimal_reqs)
2394
2424
  if target > concurrency:
2395
2425
  sys.stderr.write(
2396
2426
  f"OpenVINO LATENCY: predict concurrency {concurrency} -> {target}\n"
@@ -0,0 +1,79 @@
1
+ """Unit tests for `_ov_request_count` — how many InferRequests a model holds.
2
+
3
+ The failure this guards: the sizing used to be `max(optimal, 4)`, a FLOOR of
4
+ four rather than the fallback the comment beside it described. Every loaded
5
+ model then held four GPU-resident InferRequests for ever, per pool, while an
6
+ Intel iGPU under PERFORMANCE_HINT=LATENCY was asking for one or two and the GPU
7
+ sat 92% idle. Creating a VAAPI device for an HEVC decode failed with ENOMEM
8
+ because of it.
9
+
10
+ Pure-python: stubs numpy / PIL / postprocessors so the module imports without
11
+ the ML runtime installed.
12
+
13
+ Run: python3 -m unittest test_inference_pool_ov_requests -v
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import sys
18
+ import types
19
+ import unittest
20
+
21
+ try: # real numpy when the sandbox has it — a blind stub POISONS the whole
22
+ import numpy # noqa: F401 # pytest session for every sibling that needs it
23
+ except ImportError:
24
+ _np = types.ModuleType("numpy")
25
+ _np.array = lambda seq, dtype=None: list(seq) # noqa: E731
26
+ _np.float32 = "float32"
27
+ sys.modules["numpy"] = _np
28
+
29
+ try: # real PIL when present, for the same reason
30
+ import PIL.Image # noqa: F401
31
+ except ImportError:
32
+ _pil = types.ModuleType("PIL")
33
+ _pil_image = types.ModuleType("PIL.Image")
34
+ _pil.Image = _pil_image
35
+ sys.modules["PIL"] = _pil
36
+ sys.modules["PIL.Image"] = _pil_image
37
+
38
+ if "postprocessors" not in sys.modules:
39
+ _pp = types.ModuleType("postprocessors")
40
+ _pp.POSTPROCESSORS = {}
41
+ sys.modules["postprocessors"] = _pp
42
+
43
+ from inference_pool import ( # noqa: E402
44
+ OV_DEFAULT_CONCURRENCY,
45
+ OV_MIN_CONCURRENCY,
46
+ _ov_request_count,
47
+ )
48
+
49
+
50
+ class OvRequestCountTest(unittest.TestCase):
51
+ def test_honours_what_the_device_asked_for(self):
52
+ # The whole point: a device that says 2 gets 2, not 4.
53
+ self.assertEqual(_ov_request_count(2), 2)
54
+ self.assertEqual(_ov_request_count(5), 5)
55
+ self.assertEqual(_ov_request_count(8), 8)
56
+
57
+ def test_a_device_asking_for_one_still_gets_the_floor(self):
58
+ # One request means a frame cannot be prepared while another infers.
59
+ # Two is the floor; it is not four.
60
+ self.assertEqual(_ov_request_count(1), OV_MIN_CONCURRENCY)
61
+ self.assertEqual(OV_MIN_CONCURRENCY, 2)
62
+
63
+ def test_UNKNOWN_is_not_small_and_keeps_the_old_default(self):
64
+ # Models load lazily, so at dispatcher-sizing time the device has not
65
+ # been asked at all. Absent is not zero (D393): that case keeps the
66
+ # measured default rather than guessing low and starving the pool.
67
+ self.assertEqual(_ov_request_count(0), OV_DEFAULT_CONCURRENCY)
68
+ self.assertEqual(_ov_request_count(-1), OV_DEFAULT_CONCURRENCY)
69
+ self.assertEqual(OV_DEFAULT_CONCURRENCY, 4)
70
+
71
+ def test_is_never_a_floor_of_the_default(self):
72
+ # The defect, stated as a test: the old expression was
73
+ # `max(optimal, OV_DEFAULT_CONCURRENCY)`, so a device asking for 2 got
74
+ # 4. If this ever goes back to a floor, this fails.
75
+ self.assertLess(_ov_request_count(2), OV_DEFAULT_CONCURRENCY)
76
+
77
+
78
+ if __name__ == "__main__":
79
+ unittest.main()