matrice-analytics 0.1.56__py3-none-any.whl → 0.1.58__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-analytics might be problematic. Click here for more details.

@@ -56,7 +56,26 @@ class EmbeddingConfig:
56
56
 
57
57
 
58
58
  class EmbeddingManager:
59
- """Manages face embeddings, search operations, and caching."""
59
+ """
60
+ Manages face embeddings, search operations, and caching.
61
+
62
+ CRITICAL INITIALIZATION FLOW:
63
+ 1. __init__() creates the manager but does NOT load embeddings or start background refresh
64
+ 2. External caller MUST call await _load_staff_embeddings() to load embeddings synchronously
65
+ 3. After successful load, caller SHOULD call start_background_refresh() for periodic updates
66
+ 4. The _embeddings_loaded flag tracks whether embeddings are ready for use
67
+ 5. All search operations check _embeddings_loaded before proceeding
68
+
69
+ This design prevents race conditions where:
70
+ - Background thread tries to load while main thread is loading
71
+ - Search operations are called before embeddings are loaded
72
+ - Multiple threads compete for the embeddings_lock during initialization
73
+
74
+ Thread Safety:
75
+ - _embeddings_lock protects embeddings_matrix and embedding_metadata
76
+ - _cache_lock protects track_id_cache
77
+ - _embeddings_loaded is set only after successful load under lock
78
+ """
60
79
 
61
80
  def __init__(self, config: EmbeddingConfig, face_client: FacialRecognitionClient = None):
62
81
  self.config = config
@@ -89,11 +108,47 @@ class EmbeddingManager:
89
108
  self._is_running = False
90
109
  self._stop_event = threading.Event()
91
110
 
92
- # Start background refresh if enabled
93
- if self.config.enable_background_refresh and self.face_client:
94
- self.start_background_refresh()
95
- self.logger.info(f"Background embedding refresh enabled - interval: {self.config.background_refresh_interval}s")
111
+ # Initialization status flag
112
+ self._embeddings_loaded = False
113
+
114
+ # DON'T start background refresh yet - wait for initial load in initialize()
115
+ # This prevents race conditions where background thread interferes with main init
116
+ self.logger.info(f"EmbeddingManager created - background refresh will start after initial load (interval: {self.config.background_refresh_interval}s)")
96
117
 
118
+ def is_ready(self) -> bool:
119
+ """
120
+ Check if embeddings are loaded and ready for use.
121
+
122
+ Returns:
123
+ True if embeddings are loaded and matrix is valid, False otherwise
124
+ """
125
+ return (
126
+ self._embeddings_loaded
127
+ and self.embeddings_matrix is not None
128
+ and len(self.embedding_metadata) > 0
129
+ )
130
+
131
+ def get_status(self) -> Dict[str, Any]:
132
+ """
133
+ Get detailed status of embedding manager for debugging and health checks.
134
+
135
+ Returns:
136
+ Dictionary with status information
137
+ """
138
+ with self._embeddings_lock:
139
+ matrix_shape = self.embeddings_matrix.shape if self.embeddings_matrix is not None else None
140
+
141
+ return {
142
+ "embeddings_loaded": self._embeddings_loaded,
143
+ "embeddings_count": len(self.staff_embeddings),
144
+ "matrix_shape": matrix_shape,
145
+ "metadata_count": len(self.embedding_metadata),
146
+ "cache_size": len(self.track_id_cache),
147
+ "last_update": self.staff_embeddings_last_update,
148
+ "is_running": self._is_running,
149
+ "is_ready": self.is_ready(),
150
+ }
151
+
97
152
  def set_face_client(self, face_client: FacialRecognitionClient):
98
153
  """Set the face recognition client."""
99
154
  self.face_client = face_client
@@ -272,6 +327,8 @@ class EmbeddingManager:
272
327
 
273
328
  self.embedding_metadata = self.staff_embeddings.copy()
274
329
  self.staff_embeddings_last_update = time.time()
330
+ self._embeddings_loaded = True # Mark as successfully loaded
331
+
275
332
  self.logger.info(f"Successfully loaded and cached {len(self.staff_embeddings)} staff embeddings (dim={self.embeddings_matrix.shape[1]})")
276
333
  print(f"=============== SUCCESS: LOADED {len(self.staff_embeddings)} EMBEDDINGS, MATRIX SHAPE: {self.embeddings_matrix.shape} ===============")
277
334
  try:
@@ -336,9 +393,16 @@ class EmbeddingManager:
336
393
 
337
394
  def _find_best_local_match(self, query_embedding: List[float]) -> Optional[Tuple[StaffEmbedding, float]]:
338
395
  """Find best matching staff member using optimized matrix operations (thread-safe)."""
396
+ # Check if embeddings are loaded at all
397
+ if not self._embeddings_loaded:
398
+ print(f"ERROR: _find_best_local_match called but embeddings not loaded yet (_embeddings_loaded={self._embeddings_loaded})")
399
+ self.logger.error("Embeddings not loaded - _find_best_local_match cannot proceed")
400
+ return None
401
+
339
402
  with self._embeddings_lock:
340
403
  if self.embeddings_matrix is None or len(self.embedding_metadata) == 0:
341
- print(f"ERROR: _find_best_local_match - embeddings_matrix is None={self.embeddings_matrix is None}, metadata_len={len(self.embedding_metadata)}")
404
+ print(f"ERROR: _find_best_local_match - embeddings_matrix is None={self.embeddings_matrix is None}, metadata_len={len(self.embedding_metadata)}, _embeddings_loaded={self._embeddings_loaded}")
405
+ self.logger.error(f"Embeddings matrix is None despite _embeddings_loaded={self._embeddings_loaded}")
342
406
  return None
343
407
 
344
408
  # Create local copies to avoid issues with concurrent modifications
@@ -346,6 +410,7 @@ class EmbeddingManager:
346
410
  embedding_metadata = self.embedding_metadata.copy()
347
411
 
348
412
  if embeddings_matrix is None:
413
+ print("ERROR: _find_best_local_match - embeddings_matrix copy is None")
349
414
  return None
350
415
 
351
416
  try:
@@ -143,6 +143,13 @@ class TemporalIdentityManager:
143
143
 
144
144
  # PRIMARY PATH: Local similarity search using EmbeddingManager (FAST - ~1-5ms)
145
145
  if self.embedding_manager:
146
+ # Defensive check: ensure embeddings are loaded before attempting search
147
+ if not self.embedding_manager.is_ready():
148
+ status = self.embedding_manager.get_status()
149
+ self.logger.error(f"EmbeddingManager not ready for search - status: {status}")
150
+ print(f"ERROR: _compute_best_identity - embeddings not ready. Status: {status}")
151
+ return None, "Unknown", 0.0, None, {}, "unknown"
152
+
146
153
  try:
147
154
  local_match = self.embedding_manager._find_best_local_match(emb)
148
155
 
@@ -540,11 +547,30 @@ class FaceRecognitionEmbeddingUseCase(BaseProcessor):
540
547
  Async initialization method to set up face client and all components.
541
548
  Must be called after __init__ before process() can be called.
542
549
 
550
+ CRITICAL INITIALIZATION SEQUENCE:
551
+ 1. Initialize face client and update deployment
552
+ 2. Create EmbeddingManager (does NOT load embeddings yet)
553
+ 3. Synchronously load embeddings with _load_staff_embeddings() - MUST succeed
554
+ 4. Verify embeddings are actually loaded (fail-fast if not)
555
+ 5. Start background refresh thread (only after successful load)
556
+ 6. Initialize TemporalIdentityManager with loaded EmbeddingManager
557
+ 7. Final verification of all components
558
+
559
+ This sequence ensures:
560
+ - No race conditions between main load and background thread
561
+ - Fail-fast behavior if embeddings can't be loaded
562
+ - All components have verified embeddings before use
563
+
543
564
  Args:
544
565
  config: Optional config to use. If not provided, uses config from __init__.
566
+
567
+ Raises:
568
+ RuntimeError: If embeddings fail to load or verification fails
545
569
  """
570
+ print("=============== INITIALIZE() CALLED ===============")
546
571
  if self._initialized:
547
572
  self.logger.debug("Use case already initialized, skipping")
573
+ print("=============== ALREADY INITIALIZED, SKIPPING ===============")
548
574
  return
549
575
 
550
576
  # Use provided config or fall back to default config from __init__
@@ -558,10 +584,12 @@ class FaceRecognitionEmbeddingUseCase(BaseProcessor):
558
584
  raise TypeError(f"Invalid config type for initialization: {type(init_config)}, expected FaceRecognitionEmbeddingConfig")
559
585
 
560
586
  self.logger.info("Initializing face recognition use case with provided config")
587
+ print("=============== STEP 1: INITIALIZING FACE CLIENT ===============")
561
588
 
562
589
  # Initialize face client (includes deployment update)
563
590
  try:
564
591
  self.face_client = await self._get_facial_recognition_client(init_config)
592
+ print(f"=============== FACE CLIENT INITIALIZED: {self.face_client is not None} ===============")
565
593
 
566
594
  # Initialize People activity logging if enabled
567
595
  if init_config.enable_people_activity_logging:
@@ -570,7 +598,9 @@ class FaceRecognitionEmbeddingUseCase(BaseProcessor):
570
598
  self.logger.info("People activity logging enabled and started")
571
599
 
572
600
  # Initialize EmbeddingManager
601
+ print("=============== STEP 2: INITIALIZING EMBEDDING MANAGER ===============")
573
602
  if not init_config.embedding_config:
603
+ print("=============== CREATING EMBEDDING CONFIG ===============")
574
604
  init_config.embedding_config = EmbeddingConfig(
575
605
  similarity_threshold=init_config.similarity_threshold,
576
606
  confidence_threshold=init_config.confidence_threshold,
@@ -581,16 +611,42 @@ class FaceRecognitionEmbeddingUseCase(BaseProcessor):
581
611
  staff_embeddings_cache_ttl=43200,
582
612
  )
583
613
  self.embedding_manager = EmbeddingManager(init_config.embedding_config, self.face_client)
614
+ print(f"=============== EMBEDDING MANAGER CREATED: {self.embedding_manager is not None} ===============")
584
615
  self.logger.info("Embedding manager initialized")
585
616
 
586
617
  # Load staff embeddings immediately for fast startup (avoid race conditions)
618
+ # This MUST succeed before we can proceed - fail fast if it doesn't
619
+ print("=============== STEP 3: CALLING _load_staff_embeddings() ===============")
587
620
  embeddings_loaded = await self.embedding_manager._load_staff_embeddings()
588
- if embeddings_loaded:
589
- self.logger.info(f"Loaded {len(self.embedding_manager.staff_embeddings)} staff embeddings at initialization")
590
- else:
591
- self.logger.warning("Failed to load staff embeddings at initialization - will retry in background")
621
+ print(f"=============== EMBEDDINGS LOADED: {embeddings_loaded} ===============")
622
+
623
+ if not embeddings_loaded:
624
+ error_msg = "CRITICAL: Failed to load staff embeddings at initialization - cannot proceed without embeddings"
625
+ print(f"=============== {error_msg} ===============")
626
+ self.logger.error(error_msg)
627
+ raise RuntimeError(error_msg)
628
+
629
+ # Verify embeddings are actually loaded using is_ready() method
630
+ if not self.embedding_manager.is_ready():
631
+ status = self.embedding_manager.get_status()
632
+ error_msg = f"CRITICAL: Embeddings not ready after load - status: {status}"
633
+ print(f"=============== {error_msg} ===============")
634
+ self.logger.error(error_msg)
635
+ raise RuntimeError(error_msg)
636
+
637
+ print(f"=============== STAFF EMBEDDINGS COUNT: {len(self.embedding_manager.staff_embeddings)} ===============")
638
+ print(f"=============== EMBEDDINGS MATRIX SHAPE: {self.embedding_manager.embeddings_matrix.shape} ===============")
639
+ print(f"=============== EMBEDDINGS LOADED FLAG: {self.embedding_manager._embeddings_loaded} ===============")
640
+ self.logger.info(f"Successfully loaded {len(self.embedding_manager.staff_embeddings)} staff embeddings at initialization")
641
+
642
+ # NOW start background refresh after successful initial load (prevents race conditions)
643
+ if init_config.embedding_config.enable_background_refresh:
644
+ print("=============== STEP 4: STARTING BACKGROUND REFRESH ===============")
645
+ self.embedding_manager.start_background_refresh()
646
+ self.logger.info("Background embedding refresh started after successful initial load")
592
647
 
593
648
  # Initialize TemporalIdentityManager with EmbeddingManager for fast local search
649
+ print("=============== STEP 5: INITIALIZING TEMPORAL IDENTITY MANAGER ===============")
594
650
  self.temporal_identity_manager = TemporalIdentityManager(
595
651
  face_client=self.face_client,
596
652
  embedding_manager=self.embedding_manager,
@@ -602,8 +658,26 @@ class FaceRecognitionEmbeddingUseCase(BaseProcessor):
602
658
  )
603
659
  self.logger.info("Temporal identity manager initialized with embedding manager for local similarity search")
604
660
 
661
+ # Final verification before marking as initialized
662
+ print("=============== STEP 6: FINAL VERIFICATION ===============")
663
+ if not self.embedding_manager.is_ready():
664
+ status = self.embedding_manager.get_status()
665
+ error_msg = f"CRITICAL: Final verification failed - embeddings not ready. Status: {status}"
666
+ print(f"=============== {error_msg} ===============")
667
+ self.logger.error(error_msg)
668
+ raise RuntimeError(error_msg)
669
+
670
+ # Log detailed status for debugging
671
+ status = self.embedding_manager.get_status()
672
+ print(f"=============== FINAL CHECKS PASSED ===============")
673
+ print(f" - Face client: {self.face_client is not None}")
674
+ print(f" - Embedding manager: {self.embedding_manager is not None}")
675
+ print(f" - Embedding manager status: {status}")
676
+ print(f" - Temporal identity manager: {self.temporal_identity_manager is not None}")
677
+
605
678
  self._initialized = True
606
- self.logger.info("Face recognition use case fully initialized")
679
+ self.logger.info("Face recognition use case fully initialized and verified")
680
+ print("=============== INITIALIZATION COMPLETE ===============")
607
681
 
608
682
  except Exception as e:
609
683
  self.logger.error(f"Error during use case initialization: {e}", exc_info=True)
@@ -747,7 +821,7 @@ class FaceRecognitionEmbeddingUseCase(BaseProcessor):
747
821
  self.logger.debug("Applied category filtering")
748
822
 
749
823
  print("------------------TILL TRACKER MS----------------------------")
750
- print("LATENCY:",(time.time() - processing_start)*1000,"| Throughput fps:",(1.0 / (time.time() - processing_start)) if (time.time() - processing_start) > 0 else None)
824
+ print(self._initialized,"LATENCY:",(time.time() - processing_start)*1000,"| Throughput fps:",(1.0 / (time.time() - processing_start)) if (time.time() - processing_start) > 0 else None)
751
825
  print("------------------TILL TRACKER MS----------------------------")
752
826
  # Advanced tracking (BYTETracker-like) - only if enabled
753
827
  if config.enable_face_tracking:
@@ -794,6 +868,19 @@ class FaceRecognitionEmbeddingUseCase(BaseProcessor):
794
868
 
795
869
  # Process face recognition for each detection (if enabled)
796
870
  if config.enable_face_recognition:
871
+ # Additional safety check: verify embeddings are still loaded and ready
872
+ if not self.embedding_manager or not self.embedding_manager.is_ready():
873
+ status = self.embedding_manager.get_status() if self.embedding_manager else {}
874
+ error_msg = f"CRITICAL: Cannot process face recognition - embeddings not ready. Status: {status}"
875
+ self.logger.error(error_msg)
876
+ print(f"ERROR: {error_msg}")
877
+ return self.create_error_result(
878
+ error_msg,
879
+ usecase=self.name,
880
+ category=self.category,
881
+ context=context,
882
+ )
883
+
797
884
  face_recognition_result = await self._process_face_recognition(
798
885
  processed_data, config, stream_info, input_bytes
799
886
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: matrice_analytics
3
- Version: 0.1.56
3
+ Version: 0.1.58
4
4
  Summary: Common server utilities for Matrice.ai services
5
5
  Author-email: "Matrice.ai" <dipendra@matrice.ai>
6
6
  License-Expression: MIT
@@ -28,8 +28,8 @@ matrice_analytics/post_processing/core/config.py,sha256=uyxWndO-DE9PeGD_h5K3TeB0
28
28
  matrice_analytics/post_processing/core/config_utils.py,sha256=QuAS-_JKSoNOtfUWgr7Alf_wsqODzN2rHlQu-cHRK0s,34311
29
29
  matrice_analytics/post_processing/face_reg/__init__.py,sha256=yntaiGlW9vdjBpPZQXNuovALihJPzRlFyUE88l3MhBA,1364
30
30
  matrice_analytics/post_processing/face_reg/compare_similarity.py,sha256=NlFc8b2a74k0PqSFAbuM_fUbA1BT3pr3VUgvSqRpJzQ,23396
31
- matrice_analytics/post_processing/face_reg/embedding_manager.py,sha256=euZdFxslvEStli7pcUrBhyvJ-FR5uUGjU7HkrQ2XIfU,41940
32
- matrice_analytics/post_processing/face_reg/face_recognition.py,sha256=CkJaf1M4Ah7YSoxOjSVoSPqAp912QkrVS0v2cLeWMCQ,103595
31
+ matrice_analytics/post_processing/face_reg/embedding_manager.py,sha256=zd4KIqZijRssPzA7QJsg9-DCJ983aIyUPoo8WuRW2rs,44956
32
+ matrice_analytics/post_processing/face_reg/face_recognition.py,sha256=qkFl7f3EkyK9V6nWVCCC61ODV8R2sRffp1OB-ZVPjSU,109247
33
33
  matrice_analytics/post_processing/face_reg/face_recognition_client.py,sha256=eF2NYju1uWKXhILndI1rh4_VhWrKSGidui2jjbPQXgM,27596
34
34
  matrice_analytics/post_processing/face_reg/people_activity_logging.py,sha256=vZbIvkK1h3h58ROeF0_ygF3lqr19O2h5222bN8XyIis,13675
35
35
  matrice_analytics/post_processing/ocr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -189,8 +189,8 @@ matrice_analytics/post_processing/utils/format_utils.py,sha256=UTF7A5h9j0_S12xH9
189
189
  matrice_analytics/post_processing/utils/geometry_utils.py,sha256=BWfdM6RsdJTTLR1GqkWfdwpjMEjTCJyuBxA4zVGKdfk,9623
190
190
  matrice_analytics/post_processing/utils/smoothing_utils.py,sha256=78U-yucAcjUiZ0NIAc9NOUSIT0PWP1cqyIPA_Fdrjp0,14699
191
191
  matrice_analytics/post_processing/utils/tracking_utils.py,sha256=rWxuotnJ3VLMHIBOud2KLcu4yZfDp7hVPWUtNAq_2xw,8288
192
- matrice_analytics-0.1.56.dist-info/licenses/LICENSE.txt,sha256=_uQUZpgO0mRYL5-fPoEvLSbNnLPv6OmbeEDCHXhK6Qc,1066
193
- matrice_analytics-0.1.56.dist-info/METADATA,sha256=plXBTb5-QrbiN9-4ui3W5hj6bo15-ka3tgANA0Pxzpk,14378
194
- matrice_analytics-0.1.56.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
195
- matrice_analytics-0.1.56.dist-info/top_level.txt,sha256=STAPEU-e-rWTerXaspdi76T_eVRSrEfFpURSP7_Dt8E,18
196
- matrice_analytics-0.1.56.dist-info/RECORD,,
192
+ matrice_analytics-0.1.58.dist-info/licenses/LICENSE.txt,sha256=_uQUZpgO0mRYL5-fPoEvLSbNnLPv6OmbeEDCHXhK6Qc,1066
193
+ matrice_analytics-0.1.58.dist-info/METADATA,sha256=K3BFU6zoK1DBwLnrxRRi0bM8biLmRcJ2rHbuzPVinsI,14378
194
+ matrice_analytics-0.1.58.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
195
+ matrice_analytics-0.1.58.dist-info/top_level.txt,sha256=STAPEU-e-rWTerXaspdi76T_eVRSrEfFpURSP7_Dt8E,18
196
+ matrice_analytics-0.1.58.dist-info/RECORD,,