Skip to content

Commit def7f1b

Browse files
committed
Pre-warm analyzer engine to prevent recognizer reloads
Added a warm-up call to AnalyzerEngine during initialization to ensure recognizers are loaded only once, preventing repeated reloads on each analyze() call. Also added a test to verify recognizers are not reloaded, improving performance and avoiding unnecessary registry calls.
1 parent 43e1152 commit def7f1b

File tree

4 files changed

+69
-2
lines changed

4 files changed

+69
-2
lines changed

optillm/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Version information
2-
__version__ = "0.3.0"
2+
__version__ = "0.3.1"
33

44
# Import from server module
55
from .server import (

optillm/plugins/privacy_plugin.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ def get_analyzer_engine() -> AnalyzerEngine:
105105
global _analyzer_engine
106106
if _analyzer_engine is None:
107107
_analyzer_engine = AnalyzerEngine()
108+
# Pre-warm the analyzer to load all recognizers once during initialization
109+
# This prevents recognizers from being reloaded on each analyze() call
110+
_analyzer_engine.analyze(text="warm up", language="en")
108111
return _analyzer_engine
109112

110113
def get_anonymizer_engine() -> AnonymizerEngine:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "optillm"
7-
version = "0.3.0"
7+
version = "0.3.1"
88
description = "An optimizing inference proxy for LLMs."
99
readme = "README.md"
1010
license = "Apache-2.0"

tests/test_privacy_plugin_performance.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,64 @@ def test_singleton_instances_are_reused():
181181
print(f"❌ Singleton test failed: {e}")
182182
raise
183183

184+
def test_recognizers_not_reloaded():
185+
"""
186+
Test that recognizers are not fetched/reloaded on each analyze() call.
187+
This prevents the performance regression where "Fetching all recognizers for language en"
188+
appears in logs on every request.
189+
"""
190+
print("\nTesting that recognizers are not reloaded on each call...")
191+
192+
# Reset module state
193+
if 'optillm.plugins.privacy_plugin' in sys.modules:
194+
del sys.modules['optillm.plugins.privacy_plugin']
195+
196+
try:
197+
# Mock at the presidio level to track registry calls
198+
with patch('presidio_analyzer.AnalyzerEngine') as MockAnalyzerEngine, \
199+
patch('spacy.util.is_package', return_value=True):
200+
201+
# Create a mock analyzer instance
202+
mock_analyzer_instance = MagicMock()
203+
mock_registry = MagicMock()
204+
205+
# Track calls to get_recognizers
206+
mock_registry.get_recognizers = MagicMock(return_value=[])
207+
mock_analyzer_instance.registry = mock_registry
208+
mock_analyzer_instance.analyze = MagicMock(return_value=[])
209+
210+
MockAnalyzerEngine.return_value = mock_analyzer_instance
211+
212+
# Import module with mocks
213+
import optillm.plugins.privacy_plugin as privacy_plugin
214+
215+
# First call to get_analyzer_engine - should create and warm up
216+
analyzer1 = privacy_plugin.get_analyzer_engine()
217+
initial_analyze_calls = mock_analyzer_instance.analyze.call_count
218+
219+
print(f"Warm-up analyze calls: {initial_analyze_calls}")
220+
assert initial_analyze_calls == 1, f"Expected 1 warm-up analyze call, got {initial_analyze_calls}"
221+
222+
# Second call - should return cached instance without additional analyze
223+
analyzer2 = privacy_plugin.get_analyzer_engine()
224+
second_analyze_calls = mock_analyzer_instance.analyze.call_count
225+
226+
print(f"Total analyze calls after second get_analyzer_engine: {second_analyze_calls}")
227+
assert second_analyze_calls == 1, f"Analyzer should not call analyze() again on cached retrieval, got {second_analyze_calls} calls"
228+
229+
# Verify it's the same instance
230+
assert analyzer1 is analyzer2, "Should return the same cached analyzer instance"
231+
232+
print("✅ Recognizer reload test PASSED - Recognizers are pre-warmed and not reloaded!")
233+
return True
234+
235+
except ImportError as e:
236+
print(f"⚠️ Skipping recognizer reload test - dependencies not installed: {e}")
237+
return True
238+
except Exception as e:
239+
print(f"❌ Recognizer reload test failed: {e}")
240+
raise
241+
184242
if __name__ == "__main__":
185243
print("=" * 60)
186244
print("Privacy Plugin Performance & Caching Tests")
@@ -200,6 +258,12 @@ def test_singleton_instances_are_reused():
200258
all_passed = False
201259
print(f"❌ Singleton instance test failed: {e}")
202260

261+
try:
262+
test_recognizers_not_reloaded()
263+
except Exception as e:
264+
all_passed = False
265+
print(f"❌ Recognizer reload test failed: {e}")
266+
203267
try:
204268
test_privacy_plugin_performance()
205269
except Exception as e:

0 commit comments

Comments
 (0)