Skip to content

Commit 1d47398

Browse files
committed
Remove config override for vector dimensions
1 parent ed4d90e commit 1d47398

File tree

3 files changed

+35
-66
lines changed

3 files changed

+35
-66
lines changed

controller/src/controller/uuid_manager.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@ def __init__(self, database=DEFAULT_DATABASE, reid_config_data=None):
3737
self.features_for_database_timestamps = {} # Track when features were added
3838
self.quality_features = {}
3939
self.unique_id_count = 0
40-
# vector_dimensions is optional; if absent, inferred from first observed embedding
40+
# ReID embedding dimensions are inferred from the first observed embedding.
41+
# Do not accept dimension overrides from reid_config_data.
4142
if reid_config_data is None:
4243
reid_config_data = {}
43-
vector_dimensions = reid_config_data.get('vector_dimensions', None)
44-
self._inferred_dimensions = vector_dimensions
44+
self._inferred_dimensions = None
4545
self._dimensions_lock = threading.Lock()
46-
self.reid_database = available_databases[database](dimensions=vector_dimensions)
46+
self.reid_database = available_databases[database](dimensions=None)
4747
self.pool = concurrent.futures.ThreadPoolExecutor()
4848
self.similarity_query_times = collections.deque(
4949
maxlen=DEFAULT_MAX_SIMILARITY_QUERIES_TRACKED)
@@ -122,7 +122,7 @@ def _ensureReIDDimensions(self, embedding):
122122
log.info(f"Inferred ReID embedding dimensions from first observed vector: {dim}")
123123
try:
124124
self.reid_database.ensureSchema(dim)
125-
except ValueError as err:
125+
except (ValueError, RuntimeError) as err:
126126
log.error(f"ReID schema initialization failed: {err}")
127127
return False
128128
self._inferred_dimensions = dim

docs/user-guide/microservices/controller/Extended-ReID.md

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -158,21 +158,19 @@ controller/config/reid-config.json
158158

159159
### Configuration Parameters
160160

161-
| Parameter | Type | Default | Description |
162-
| ----------------------------------- | ----- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
163-
| `stale_feature_timeout_secs` | float | 5.0 | How long (seconds) to accumulate features in memory before flushing to VDMS. Features older than this threshold are persisted to the database for long-term storage. |
164-
| `stale_feature_check_interval_secs` | float | 1.0 | How frequently (seconds) the background timer checks for stale features and flushes them to VDMS. More frequent checks ensure timely database updates. |
165-
| `feature_accumulation_threshold` | int | 12 | Minimum number of quality features required before initiating a similarity query against the database. More features = higher statistical confidence in matching. |
166-
| `feature_slice_size` | int | 10 | When persisting features to VDMS, sample every Nth feature vector from the accumulated set to reduce database bloat. Example: slice_size=10 stores every 10th vector. |
167-
| `similarity_threshold` | int | 60 | Minimum similarity score (0-100) for a match to be considered valid. Higher values = stricter matching. |
168-
| `vector_dimensions` | int | auto | **Optional.** Override the embedding vector length used for the VDMS descriptor set schema. When absent (recommended), the controller infers the dimension automatically from the first received embedding. Set this only to validate that your producer always emits a specific length; a mismatch between the configured value and the received embedding causes the controller to reject all embeddings and log an error. |
161+
| Parameter | Type | Default | Description |
162+
| ----------------------------------- | ----- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
163+
| `stale_feature_timeout_secs` | float | 5.0 | How long (seconds) to accumulate features in memory before flushing to VDMS. Features older than this threshold are persisted to the database for long-term storage. |
164+
| `stale_feature_check_interval_secs` | float | 1.0 | How frequently (seconds) the background timer checks for stale features and flushes them to VDMS. More frequent checks ensure timely database updates. |
165+
| `feature_accumulation_threshold` | int | 12 | Minimum number of quality features required before initiating a similarity query against the database. More features = higher statistical confidence in matching. |
166+
| `feature_slice_size` | int | 10 | When persisting features to VDMS, sample every Nth feature vector from the accumulated set to reduce database bloat. Example: slice_size=10 stores every 10th vector. |
167+
| `similarity_threshold` | int | 60 | Minimum similarity score (0-100) for a match to be considered valid. Higher values = stricter matching. |
169168

170169
### Embedding Dimension Inference
171170

172-
The controller automatically infers the ReID embedding dimension from the first vector it receives at runtime, removing the need to keep `vector_dimensions` in configuration synchronized with the model:
171+
The controller automatically infers the ReID embedding dimension from the first vector it receives at runtime:
173172

174-
- **Auto-inference (default)**: On the first decoded embedding the controller reads the vector length from the payload, creates the VDMS descriptor set schema with that dimension, and locks that dimension for the process lifetime. All subsequent embeddings are validated against that inferred length; mismatches are discarded with a warning.
175-
- **Optional override** (`vector_dimensions` in config): Set this when you want the controller to reject embeddings whose length does not match a specific value. Useful in deployments where the embedding model is fixed and any deviation should be treated as a misconfiguration. A mismatch triggers an error and no vectors are stored for that run.
173+
- **Runtime inference only**: On the first decoded embedding the controller reads the vector length from the payload, creates the VDMS descriptor set schema with that dimension, and locks that dimension for the process lifetime. All subsequent embeddings are validated against that inferred length; mismatches are discarded with a warning.
176174
- **Switching ReID models**: Because the dimension is locked after the first embedding, switching to a model with a different output length requires restarting the controller. The VDMS descriptor set must also be recreated if the stored dimension differs (VDMS does not support in-place schema migration).
177175
- **Base64 compatibility**: The controller decodes base64 embeddings using the payload byte length by default. Producers can also include an optional `embedding_dimensions` field alongside `embedding_vector`; if provided, it must match the packed float count.
178176

@@ -191,7 +189,7 @@ python scene_controller.py \
191189
**Current Implementation Note**:
192190

193191
- `stale_feature_timeout_secs`, `stale_feature_check_interval_secs`, `feature_accumulation_threshold`, `feature_slice_size`, and `similarity_threshold` are fully implemented
194-
- `vector_dimensions` is optional; omit it to let the controller infer dimensions automatically from the first received embedding
192+
- ReID embedding dimensions are inferred at runtime from the first received embedding; there is no configuration override for dimension
195193
- All semantic metadata attributes are currently used for TIER 1 filtering. Selective metadata filtering is planned for Phase 2.
196194

197195
### Tuning Recommendations

tests/sscape_tests/uuid_manager/test_uuid_manager.py

Lines changed: 20 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -18,36 +18,6 @@
1818
class TestUUIDManagerInitialization:
1919
"""Test UUIDManager initialization and basic setup."""
2020

21-
def test_initialization_applies_vector_dimensions_from_reid_config(self):
22-
"""Verify explicit vector_dimensions is forwarded to the selected DB constructor."""
23-
mock_db_instance = MagicMock()
24-
captured_kwargs = {}
25-
26-
def fake_db_constructor(**kwargs):
27-
captured_kwargs.update(kwargs)
28-
return mock_db_instance
29-
30-
with patch.dict(UUIDManager.__init__.__globals__['available_databases'], {'VDMS': fake_db_constructor}):
31-
manager = UUIDManager(database="VDMS", reid_config_data={"vector_dimensions": 512})
32-
33-
assert captured_kwargs.get('dimensions') == 512
34-
assert manager.reid_database is mock_db_instance
35-
36-
def test_initialization_without_vector_dimensions_passes_none_to_database(self):
37-
"""Verify that omitting vector_dimensions passes None to the DB constructor (auto-infer)."""
38-
mock_db_instance = MagicMock()
39-
captured_kwargs = {}
40-
41-
def fake_db_constructor(**kwargs):
42-
captured_kwargs.update(kwargs)
43-
return mock_db_instance
44-
45-
with patch.dict(UUIDManager.__init__.__globals__['available_databases'], {'VDMS': fake_db_constructor}):
46-
manager = UUIDManager(database="VDMS", reid_config_data={})
47-
48-
assert captured_kwargs.get('dimensions') is None, "Should pass None when vector_dimensions absent"
49-
assert manager._inferred_dimensions is None, "Should start with no inferred dimensions"
50-
5121
@patch('controller.uuid_manager.VDMSDatabase')
5222
def test_initialization_with_default_database(self, mock_vdms_class):
5323
"""Verify UUIDManager initializes with default VDMS database."""
@@ -386,7 +356,11 @@ def test_assign_id_does_not_increment_counter_when_reid_present(self, mock_vdms_
386356
mock_vdms_instance = MagicMock()
387357
mock_vdms_class.return_value = mock_vdms_instance
388358

389-
manager = UUIDManager()
359+
def fake_db_constructor(**kwargs):
360+
return mock_vdms_instance
361+
362+
with patch.dict(UUIDManager.__init__.__globals__['available_databases'], {'VDMS': fake_db_constructor}):
363+
manager = UUIDManager()
390364
initial_count = manager.unique_id_count
391365

392366
obj = MagicMock()
@@ -428,7 +402,11 @@ def test_assign_id_gathers_quality_features_for_new_tracker(self, mock_vdms_clas
428402
mock_vdms_instance = MagicMock()
429403
mock_vdms_class.return_value = mock_vdms_instance
430404

431-
manager = UUIDManager()
405+
def fake_db_constructor(**kwargs):
406+
return mock_vdms_instance
407+
408+
with patch.dict(UUIDManager.__init__.__globals__['available_databases'], {'VDMS': fake_db_constructor}):
409+
manager = UUIDManager()
432410

433411
obj = MagicMock()
434412
obj.rv_id = "new_tracker_with_features"
@@ -472,7 +450,11 @@ def test_assign_id_does_not_submit_query_without_sufficient_features(self, mock_
472450
mock_vdms_instance = MagicMock()
473451
mock_vdms_class.return_value = mock_vdms_instance
474452

475-
manager = UUIDManager()
453+
def fake_db_constructor(**kwargs):
454+
return mock_vdms_instance
455+
456+
with patch.dict(UUIDManager.__init__.__globals__['available_databases'], {'VDMS': fake_db_constructor}):
457+
manager = UUIDManager()
476458
manager.pool = MagicMock()
477459

478460
obj = MagicMock()
@@ -495,7 +477,11 @@ def test_assign_id_submits_query_with_sufficient_features(self, mock_vdms_class)
495477
mock_vdms_instance = MagicMock()
496478
mock_vdms_class.return_value = mock_vdms_instance
497479

498-
manager = UUIDManager()
480+
def fake_db_constructor(**kwargs):
481+
return mock_vdms_instance
482+
483+
with patch.dict(UUIDManager.__init__.__globals__['available_databases'], {'VDMS': fake_db_constructor}):
484+
manager = UUIDManager()
499485
manager.pool = MagicMock()
500486

501487
obj = MagicMock()
@@ -669,21 +655,6 @@ def test_reject_embedding_with_inconsistent_dimension(self):
669655
assert result is False, "Should reject embedding with different dimension"
670656
assert manager._inferred_dimensions == 256, "Locked dimension should remain unchanged"
671657

672-
def test_explicit_config_dimension_used_as_initial_inferred(self):
673-
"""Verify that a configured vector_dimensions seeds _inferred_dimensions."""
674-
manager, _ = self._make_manager_with_mock_db(reid_config_data={"vector_dimensions": 512})
675-
676-
assert manager._inferred_dimensions == 512, "Configured dimension should seed inferred value"
677-
678-
def test_reject_embedding_mismatching_configured_dimension(self):
679-
"""Verify embedding is rejected when its dimension differs from an explicitly configured one."""
680-
manager, _ = self._make_manager_with_mock_db(reid_config_data={"vector_dimensions": 512})
681-
wrong = np.arange(256, dtype=np.float32)
682-
683-
result = manager._ensureReIDDimensions(wrong)
684-
685-
assert result is False, "Should reject embedding whose dimension mismatches configured value"
686-
687658
def test_ensure_schema_error_causes_false_return(self):
688659
"""Verify False is returned and dimension remains unset when ensureSchema raises."""
689660
mock_db = MagicMock()

0 commit comments

Comments
 (0)