matrice-inference 0.1.2__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.

Potentially problematic release.


This version of matrice-inference might be problematic. Click here for more details.

Files changed (37) hide show
  1. matrice_inference/__init__.py +72 -0
  2. matrice_inference/py.typed +0 -0
  3. matrice_inference/server/__init__.py +23 -0
  4. matrice_inference/server/inference_interface.py +176 -0
  5. matrice_inference/server/model/__init__.py +1 -0
  6. matrice_inference/server/model/model_manager.py +274 -0
  7. matrice_inference/server/model/model_manager_wrapper.py +550 -0
  8. matrice_inference/server/model/triton_model_manager.py +290 -0
  9. matrice_inference/server/model/triton_server.py +1248 -0
  10. matrice_inference/server/proxy_interface.py +371 -0
  11. matrice_inference/server/server.py +1004 -0
  12. matrice_inference/server/stream/__init__.py +0 -0
  13. matrice_inference/server/stream/app_deployment.py +228 -0
  14. matrice_inference/server/stream/consumer_worker.py +201 -0
  15. matrice_inference/server/stream/frame_cache.py +127 -0
  16. matrice_inference/server/stream/inference_worker.py +163 -0
  17. matrice_inference/server/stream/post_processing_worker.py +230 -0
  18. matrice_inference/server/stream/producer_worker.py +147 -0
  19. matrice_inference/server/stream/stream_pipeline.py +451 -0
  20. matrice_inference/server/stream/utils.py +23 -0
  21. matrice_inference/tmp/abstract_model_manager.py +58 -0
  22. matrice_inference/tmp/aggregator/__init__.py +18 -0
  23. matrice_inference/tmp/aggregator/aggregator.py +330 -0
  24. matrice_inference/tmp/aggregator/analytics.py +906 -0
  25. matrice_inference/tmp/aggregator/ingestor.py +438 -0
  26. matrice_inference/tmp/aggregator/latency.py +597 -0
  27. matrice_inference/tmp/aggregator/pipeline.py +968 -0
  28. matrice_inference/tmp/aggregator/publisher.py +431 -0
  29. matrice_inference/tmp/aggregator/synchronizer.py +594 -0
  30. matrice_inference/tmp/batch_manager.py +239 -0
  31. matrice_inference/tmp/overall_inference_testing.py +338 -0
  32. matrice_inference/tmp/triton_utils.py +638 -0
  33. matrice_inference-0.1.2.dist-info/METADATA +28 -0
  34. matrice_inference-0.1.2.dist-info/RECORD +37 -0
  35. matrice_inference-0.1.2.dist-info/WHEEL +5 -0
  36. matrice_inference-0.1.2.dist-info/licenses/LICENSE.txt +21 -0
  37. matrice_inference-0.1.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,451 @@
1
+ """
2
+ Ultra-optimized streaming pipeline using MatriceStream and updated inference interface:
3
+ Direct processing with priority queues, dynamic camera configuration
4
+
5
+ Architecture:
6
+ Consumer workers (threading) -> Priority Queue -> Inference workers (threading) ->
7
+ Priority Queue -> Post-processing workers (threading) -> Priority Queue -> Producer workers (threading)
8
+
9
+ Features:
10
+ - Start without initial configuration
11
+ - Dynamic camera configuration while running
12
+ - Support for both Kafka and Redis streams
13
+ - Integration with updated InferenceInterface and PostProcessor
14
+ - Maximum throughput with direct processing
15
+ - Low latency with no batching delays
16
+ - Multi-camera support with topic routing
17
+ - Thread-based parallelism for inference and post-processing
18
+ - Non-blocking threading for consumers/producers
19
+ """
20
+
21
+ import asyncio
22
+ import json
23
+ import time
24
+ import logging
25
+ import threading
26
+ import queue
27
+ import signal
28
+ import copy
29
+ from dataclasses import dataclass, field
30
+ from typing import Any, Dict, Optional, List, Union
31
+ from concurrent.futures import ThreadPoolExecutor
32
+
33
+ from matrice_common.stream.matrice_stream import MatriceStream, StreamType
34
+ from matrice_analytics.post_processing.post_processor import PostProcessor
35
+
36
+ from matrice_inference.server.inference_interface import InferenceInterface
37
+ from matrice_inference.server.model.model_manager_wrapper import ModelManagerWrapper
38
+ from matrice_inference.server.stream.utils import CameraConfig, StreamMessage
39
+ from matrice_inference.server.stream.consumer_worker import ConsumerWorker
40
+ from matrice_inference.server.stream.inference_worker import InferenceWorker
41
+ from matrice_inference.server.stream.post_processing_worker import PostProcessingWorker
42
+ from matrice_inference.server.stream.producer_worker import ProducerWorker
43
+ # from matrice_inference.server.stream.frame_cache import RedisFrameCache
44
+
45
+
46
+
47
+ class StreamingPipeline:
48
+ """Ultra-optimized streaming pipeline with dynamic camera configuration."""
49
+
50
+ def __init__(
51
+ self,
52
+ inference_interface: InferenceInterface,
53
+ post_processor: PostProcessor,
54
+ consumer_threads=1,
55
+ producer_threads=1,
56
+ inference_threads=1,
57
+ postprocessing_threads=1,
58
+ inference_queue_maxsize=5000,
59
+ postproc_queue_maxsize=5000,
60
+ output_queue_maxsize=5000,
61
+ message_timeout=10.0,
62
+ inference_timeout=30.0,
63
+ shutdown_timeout=30.0,
64
+ camera_configs: Optional[Dict[str, CameraConfig]] = None,
65
+ ):
66
+ self.inference_interface = inference_interface
67
+ self.post_processor = post_processor
68
+ self.consumer_threads = consumer_threads
69
+ self.producer_threads = producer_threads
70
+ self.inference_threads = inference_threads
71
+ self.postprocessing_threads = postprocessing_threads
72
+ self.inference_queue_maxsize = inference_queue_maxsize
73
+ self.postproc_queue_maxsize = postproc_queue_maxsize
74
+ self.output_queue_maxsize = output_queue_maxsize
75
+ self.message_timeout = message_timeout
76
+ self.inference_timeout = inference_timeout
77
+ self.shutdown_timeout = shutdown_timeout
78
+
79
+ # Camera configurations (can be empty initially)
80
+ self.camera_configs: Dict[str, CameraConfig] = camera_configs or {}
81
+
82
+ # Priority queues for pipeline stages
83
+ self.inference_queue = queue.PriorityQueue(maxsize=self.inference_queue_maxsize)
84
+ self.postproc_queue = queue.PriorityQueue(maxsize=self.postproc_queue_maxsize)
85
+ self.output_queue = queue.PriorityQueue(maxsize=self.output_queue_maxsize)
86
+
87
+ # Thread pools for CPU/GPU intensive work
88
+ self.inference_executor = ThreadPoolExecutor(self.inference_threads)
89
+ self.postprocessing_executor = ThreadPoolExecutor(self.postprocessing_threads)
90
+
91
+ # No centralized stream management - each worker creates its own streams
92
+
93
+ # Worker instances
94
+ self.consumer_workers: Dict[str, List[ConsumerWorker]] = {}
95
+ self.inference_workers = []
96
+ self.postproc_workers = []
97
+ self.producer_workers = []
98
+
99
+ # Worker threads
100
+ self.worker_threads = []
101
+
102
+ # Control state
103
+ self.running = False
104
+
105
+ self.logger = logging.getLogger(__name__)
106
+
107
+ # Frame cache disabled (commented out)
108
+ # self.frame_cache_config = frame_cache_config or {}
109
+ # self.frame_cache: Optional[RedisFrameCache] = None
110
+
111
+ # Removed set_global_instances method - now passing interfaces as parameters to worker functions
112
+
113
+ async def start(self):
114
+ """Start the pipeline."""
115
+ if self.running:
116
+ self.logger.warning("Pipeline already running")
117
+ return
118
+
119
+ self.running = True
120
+ self.logger.info("Starting ultra-optimized streaming pipeline...")
121
+
122
+ try:
123
+ # Frame cache initialization disabled
124
+ # fc = dict(self.frame_cache_config)
125
+ # if not fc.get("host"):
126
+ # for cfg in self.camera_configs.values():
127
+ # sc = cfg.stream_config
128
+ # if sc.get("stream_type", "kafka").lower() == "redis":
129
+ # fc.setdefault("host", sc.get("host", "localhost"))
130
+ # fc.setdefault("port", sc.get("port", 6379))
131
+ # fc.setdefault("password", sc.get("password"))
132
+ # fc.setdefault("username", sc.get("username"))
133
+ # fc.setdefault("db", sc.get("db", 0))
134
+ # break
135
+ # try:
136
+ # self.frame_cache = RedisFrameCache(
137
+ # host=fc.get("host", "localhost"),
138
+ # port=fc.get("port", 6379),
139
+ # db=fc.get("db", 0),
140
+ # password=fc.get("password"),
141
+ # username=fc.get("username"),
142
+ # ttl_seconds=fc.get("ttl_seconds", 300),
143
+ # prefix=fc.get("prefix", "stream:frames:"),
144
+ # )
145
+ # self.frame_cache.start()
146
+ # except Exception as _:
147
+ # self.frame_cache = None
148
+ # self.logger.warning("Frame cache initialization failed; proceeding without cache")
149
+
150
+ # Initialize streams for existing camera configs
151
+ await self._initialize_streams()
152
+
153
+ # Create and start workers
154
+ await self._create_workers()
155
+ self._start_workers()
156
+
157
+ self.logger.info(
158
+ f"Pipeline started with {len(self.camera_configs)} cameras, "
159
+ f"{sum(len(workers) for workers in self.consumer_workers.values())} consumer workers, "
160
+ f"{len(self.inference_workers)} inference workers, "
161
+ f"{len(self.postproc_workers)} post-processing workers, "
162
+ f"{len(self.producer_workers)} producer workers"
163
+ )
164
+
165
+ except Exception as e:
166
+ self.logger.error(f"Failed to start pipeline: {e}")
167
+ await self.stop()
168
+ raise
169
+
170
+ async def stop(self):
171
+ """Stop the pipeline gracefully."""
172
+ if not self.running:
173
+ return
174
+
175
+ self.logger.info("Stopping pipeline...")
176
+ self.running = False
177
+
178
+ # Stop all workers
179
+ self._stop_workers()
180
+
181
+ # Wait for all threads to complete
182
+ for thread in self.worker_threads:
183
+ if thread.is_alive():
184
+ thread.join(timeout=self.shutdown_timeout)
185
+
186
+ # Close streams
187
+ await self._close_streams()
188
+
189
+ # Shutdown executors
190
+ self.inference_executor.shutdown(wait=False)
191
+ self.postprocessing_executor.shutdown(wait=False)
192
+
193
+ # Frame cache stop disabled
194
+ # try:
195
+ # if self.frame_cache:
196
+ # self.frame_cache.stop()
197
+ # except Exception:
198
+ # pass
199
+
200
+ self.logger.info("Pipeline stopped")
201
+
202
+ async def add_camera_config(self, camera_config: CameraConfig) -> bool:
203
+ """
204
+ Add a camera configuration dynamically while pipeline is running.
205
+
206
+ Args:
207
+ camera_config: Camera configuration to add
208
+
209
+ Returns:
210
+ bool: True if successfully added, False otherwise
211
+ """
212
+ try:
213
+ camera_id = camera_config.camera_id
214
+
215
+ if camera_id in self.camera_configs:
216
+ self.logger.warning(f"Camera {camera_id} already exists, updating configuration")
217
+ # Stop existing workers for this camera
218
+ await self._stop_camera_workers(camera_id)
219
+
220
+ # Add camera config
221
+ self.camera_configs[camera_id] = camera_config
222
+
223
+ # Create workers for this camera if pipeline is running
224
+ if self.running:
225
+ await self._create_camera_workers(camera_config)
226
+ self._start_camera_workers(camera_id)
227
+
228
+ self.logger.info(f"Successfully added camera configuration for {camera_id}")
229
+ return True
230
+
231
+ except Exception as e:
232
+ self.logger.error(f"Failed to add camera config for {camera_config.camera_id}: {str(e)}")
233
+ return False
234
+
235
+ async def remove_camera_config(self, camera_id: str) -> bool:
236
+ """
237
+ Remove a camera configuration dynamically.
238
+
239
+ Args:
240
+ camera_id: ID of camera to remove
241
+
242
+ Returns:
243
+ bool: True if successfully removed, False otherwise
244
+ """
245
+ try:
246
+ if camera_id not in self.camera_configs:
247
+ self.logger.warning(f"Camera {camera_id} does not exist")
248
+ return False
249
+
250
+ # Stop workers for this camera
251
+ await self._stop_camera_workers(camera_id)
252
+
253
+ # Remove camera config
254
+ del self.camera_configs[camera_id]
255
+
256
+ self.logger.info(f"Successfully removed camera configuration for {camera_id}")
257
+ return True
258
+
259
+ except Exception as e:
260
+ self.logger.error(f"Failed to remove camera config for {camera_id}: {str(e)}")
261
+ return False
262
+
263
+ async def update_camera_config(self, camera_config: CameraConfig) -> bool:
264
+ """
265
+ Update an existing camera configuration.
266
+
267
+ Args:
268
+ camera_config: Updated camera configuration
269
+
270
+ Returns:
271
+ bool: True if successfully updated, False otherwise
272
+ """
273
+ return await self.add_camera_config(camera_config)
274
+
275
+ def enable_camera(self, camera_id: str) -> bool:
276
+ """Enable a camera."""
277
+ if camera_id in self.camera_configs:
278
+ self.camera_configs[camera_id].enabled = True
279
+ self.logger.info(f"Camera {camera_id} enabled")
280
+ return True
281
+ return False
282
+
283
+ def disable_camera(self, camera_id: str) -> bool:
284
+ """Disable a camera."""
285
+ if camera_id in self.camera_configs:
286
+ self.camera_configs[camera_id].enabled = False
287
+ self.logger.info(f"Camera {camera_id} disabled")
288
+ return True
289
+ return False
290
+
291
+ async def _initialize_streams(self):
292
+ """No centralized stream initialization - workers create their own streams."""
293
+ pass
294
+
295
+ async def _initialize_camera_streams(self, camera_config: CameraConfig):
296
+ """No centralized camera stream initialization - workers create their own streams."""
297
+ pass
298
+
299
+ async def _close_streams(self):
300
+ """No centralized streams to close - workers manage their own streams."""
301
+ pass
302
+
303
+ async def _close_camera_streams(self, camera_id: str):
304
+ """No centralized camera streams to close - workers manage their own streams."""
305
+ pass
306
+
307
+ async def _create_workers(self):
308
+ """Create all worker instances."""
309
+ # Create consumer workers (per camera)
310
+ for camera_config in self.camera_configs.values():
311
+ await self._create_camera_workers(camera_config)
312
+
313
+ # Create inference workers
314
+ for i in range(self.inference_threads):
315
+ worker = InferenceWorker(
316
+ worker_id=i,
317
+ inference_queue=self.inference_queue,
318
+ postproc_queue=self.postproc_queue,
319
+ inference_executor=self.inference_executor,
320
+ message_timeout=self.message_timeout,
321
+ inference_timeout=self.inference_timeout,
322
+ inference_interface=self.inference_interface
323
+ )
324
+ self.inference_workers.append(worker)
325
+
326
+ # Create post-processing workers
327
+ for i in range(self.postprocessing_threads):
328
+ worker = PostProcessingWorker(
329
+ worker_id=i,
330
+ postproc_queue=self.postproc_queue,
331
+ output_queue=self.output_queue,
332
+ postprocessing_executor=self.postprocessing_executor,
333
+ message_timeout=self.message_timeout,
334
+ inference_timeout=self.inference_timeout,
335
+ post_processor=self.post_processor,
336
+ # frame_cache=self.frame_cache,
337
+ )
338
+ self.postproc_workers.append(worker)
339
+
340
+ # Create producer workers
341
+ for i in range(self.producer_threads):
342
+ worker = ProducerWorker(
343
+ worker_id=i,
344
+ output_queue=self.output_queue,
345
+ camera_configs=self.camera_configs,
346
+ message_timeout=self.message_timeout
347
+ )
348
+ self.producer_workers.append(worker)
349
+
350
+ async def _create_camera_workers(self, camera_config: CameraConfig):
351
+ """Create consumer workers for a specific camera."""
352
+ camera_id = camera_config.camera_id
353
+
354
+ # Create consumer workers for this camera - each worker will create its own stream
355
+ camera_workers = []
356
+ for i in range(self.consumer_threads):
357
+ worker = ConsumerWorker(
358
+ camera_id=camera_id,
359
+ worker_id=i,
360
+ stream_config=camera_config.stream_config,
361
+ input_topic=camera_config.input_topic,
362
+ inference_queue=self.inference_queue,
363
+ message_timeout=self.message_timeout,
364
+ camera_config=camera_config
365
+ )
366
+ camera_workers.append(worker)
367
+
368
+ self.consumer_workers[camera_id] = camera_workers
369
+
370
+ def _start_workers(self):
371
+ """Start all worker instances."""
372
+ # Start consumer workers
373
+ for camera_id in self.consumer_workers:
374
+ self._start_camera_workers(camera_id)
375
+
376
+ # Start inference workers
377
+ for worker in self.inference_workers:
378
+ thread = worker.start()
379
+ self.worker_threads.append(thread)
380
+
381
+ # Start post-processing workers
382
+ for worker in self.postproc_workers:
383
+ thread = worker.start()
384
+ self.worker_threads.append(thread)
385
+
386
+ # Start producer workers
387
+ for worker in self.producer_workers:
388
+ thread = worker.start()
389
+ self.worker_threads.append(thread)
390
+
391
+ def _start_camera_workers(self, camera_id: str):
392
+ """Start consumer workers for a specific camera."""
393
+ if camera_id in self.consumer_workers:
394
+ for worker in self.consumer_workers[camera_id]:
395
+ thread = worker.start()
396
+ self.worker_threads.append(thread)
397
+
398
+ def _stop_workers(self):
399
+ """Stop all worker instances."""
400
+ # Stop all workers
401
+ for workers in self.consumer_workers.values():
402
+ for worker in workers:
403
+ worker.stop()
404
+
405
+ for worker in (self.inference_workers + self.postproc_workers + self.producer_workers):
406
+ worker.stop()
407
+
408
+ async def _stop_camera_workers(self, camera_id: str):
409
+ """Stop consumer workers for a specific camera."""
410
+ if camera_id in self.consumer_workers:
411
+ for worker in self.consumer_workers[camera_id]:
412
+ worker.stop()
413
+ # Remove from tracking
414
+ del self.consumer_workers[camera_id]
415
+
416
+ def get_metrics(self) -> Dict[str, Any]:
417
+ """Get pipeline metrics."""
418
+ return {
419
+ "running": self.running,
420
+ "camera_count": len(self.camera_configs),
421
+ "enabled_cameras": sum(1 for config in self.camera_configs.values() if config.enabled),
422
+ "queue_sizes": {
423
+ "inference": self.inference_queue.qsize(),
424
+ "postproc": self.postproc_queue.qsize(),
425
+ "output": self.output_queue.qsize(),
426
+ },
427
+ "worker_counts": {
428
+ "consumers": sum(len(workers) for workers in self.consumer_workers.values()),
429
+ "inference_workers": len(self.inference_workers),
430
+ "postproc_workers": len(self.postproc_workers),
431
+ "producers": len(self.producer_workers),
432
+ },
433
+ "thread_counts": {
434
+ "total_threads": len(self.worker_threads),
435
+ "active_threads": len([t for t in self.worker_threads if t.is_alive()]),
436
+ },
437
+ "thread_pool_sizes": {
438
+ "inference_threads": self.inference_threads,
439
+ "postprocessing_threads": self.postprocessing_threads,
440
+ },
441
+ "camera_configs": {
442
+ camera_id: {
443
+ "input_topic": config.input_topic,
444
+ "output_topic": config.output_topic,
445
+ "enabled": config.enabled,
446
+ "stream_type": config.stream_config.get("stream_type", "kafka")
447
+ }
448
+ for camera_id, config in self.camera_configs.items()
449
+ }
450
+ }
451
+
@@ -0,0 +1,23 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import Dict, Any, Optional
3
+ from datetime import datetime
4
+
5
+
6
+ @dataclass
7
+ class CameraConfig:
8
+ """Configuration for a camera stream."""
9
+ camera_id: str
10
+ input_topic: str
11
+ output_topic: str
12
+ stream_config: Dict[str, Any] = field(default_factory=dict)
13
+ enabled: bool = True
14
+
15
+
16
+ @dataclass
17
+ class StreamMessage:
18
+ """Raw message from stream."""
19
+ camera_id: str
20
+ message_key: str
21
+ data: Any
22
+ timestamp: datetime
23
+ priority: int = 1
@@ -0,0 +1,58 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Tuple, Dict, Any, Optional, List
3
+
4
+ class AbstractModelManager(ABC):
5
+ """Abstract base class for model management."""
6
+
7
+ @abstractmethod
8
+ def __init__(
9
+ self,
10
+ model_id: str,
11
+ internal_server_type: str,
12
+ internal_port: int,
13
+ internal_host: str,
14
+ action_tracker: Any,
15
+ num_model_instances: int = 1,
16
+ ):
17
+ """Initialize the model manager.
18
+
19
+ Args:
20
+ model_id: ID of the model.
21
+ internal_server_type: Type of internal server.
22
+ internal_port: Internal port number.
23
+ internal_host: Internal host address.
24
+ action_tracker: Tracker for monitoring actions.
25
+ num_model_instances: Number of model instances to create.
26
+ """
27
+ pass
28
+
29
+ @abstractmethod
30
+ def get_model(self) -> Any:
31
+ """Get a model instance for inference."""
32
+ pass
33
+
34
+ @abstractmethod
35
+ def inference(
36
+ self,
37
+ input1: Any,
38
+ input2: Optional[Any] = None,
39
+ extra_params: Optional[Dict[str, Any]] = None,
40
+ stream_key: Optional[str] = None,
41
+ stream_info: Optional[Dict[str, Any]] = None,
42
+ input_hash: Optional[str] = None,
43
+ ) -> Tuple[Any, bool]:
44
+ """Perform single inference."""
45
+ pass
46
+
47
+ @abstractmethod
48
+ def batch_inference(
49
+ self,
50
+ input1: List[Any],
51
+ input2: Optional[List[Any]] = None,
52
+ extra_params: Optional[Dict[str, Any]] = None,
53
+ stream_key: Optional[str] = None,
54
+ stream_info: Optional[Dict[str, Any]] = None,
55
+ input_hash: Optional[str] = None,
56
+ ) -> Tuple[List[Any], bool]:
57
+ """Perform batch inference."""
58
+ pass
@@ -0,0 +1,18 @@
1
+ """
2
+ Aggregator package for handling deployment streaming and results aggregation.
3
+ """
4
+
5
+ # Import modules to make them available for external use
6
+ from matrice_inference.tmp.aggregator.pipeline import ResultsAggregationPipeline
7
+ from matrice_inference.tmp.aggregator.synchronizer import ResultsSynchronizer
8
+ from matrice_inference.tmp.aggregator.ingestor import ResultsIngestor
9
+ from matrice_inference.tmp.aggregator.aggregator import ResultsAggregator
10
+ from matrice_inference.tmp.aggregator.publisher import ResultsPublisher
11
+
12
+ __all__ = [
13
+ "ResultsAggregationPipeline",
14
+ "ResultsSynchronizer",
15
+ "ResultsIngestor",
16
+ "ResultsAggregator",
17
+ "ResultsPublisher",
18
+ ]