Skip to main content
Glama
test_detectors.py19.7 kB
""" Unit tests for detection logic and confidence scoring. Tests sigmoid function, calculate_confidence, detect_save_intent, detect_recall_intent, and create_activation_signal for: - Mathematical correctness (sigmoid curve, confidence formula) - Signal detection and weighting - Threshold-based decisions (auto/ask thresholds) - Edge cases (overflow, empty input, boundary values) """ import pytest from cortexgraph.activation.config import ActivationConfig, ConfidenceThreshold, PatternLibrary from cortexgraph.activation.detectors import ( calculate_confidence, create_activation_signal, detect_recall_intent, detect_save_intent, sigmoid, ) from cortexgraph.activation.patterns import PatternMatcher @pytest.fixture def test_config(): """Create test activation configuration.""" patterns = PatternLibrary( explicit_save_triggers=["remember this", "don't forget", "i prefer"], explicit_recall_triggers=["what did i say", "remind me"], importance_markers=["critical", "must", "important"], exclusion_patterns=["what is", "how do"], uncertainty_markers=["maybe", "might", "not sure"], case_sensitive=False, partial_match=True, ) thresholds = ConfidenceThreshold( auto_save_min=0.7, auto_recall_min=0.7, clarification_min=0.4, clarification_max=0.7, ) weights = { "explicit_save_request": 4.0, "explicit_recall_request": 5.0, "critical_marker": 3.5, "important_marker": 2.5, "uncertainty_marker": -2.0, "preference_statement": 3.0, "entity_count": 0.8, } return ActivationConfig( patterns=patterns, thresholds=thresholds, weights=weights, bias=-2.0, ) @pytest.fixture def test_matcher(test_config): """Create pattern matcher with test config.""" return PatternMatcher(test_config.patterns) class TestSigmoidFunction: """Tests for sigmoid activation function.""" def test_sigmoid_zero(self): """Test sigmoid(0) = 0.5.""" result = sigmoid(0.0) assert abs(result - 0.5) < 0.01 def test_sigmoid_positive(self): """Test sigmoid of positive values.""" result = sigmoid(2.0) # sigmoid(2) ≈ 0.88 assert result > 0.5 assert result < 1.0 def test_sigmoid_negative(self): """Test sigmoid of negative values.""" result = sigmoid(-2.0) # sigmoid(-2) ≈ 0.12 assert result < 0.5 assert result > 0.0 def test_sigmoid_large_positive(self): """Test sigmoid handles large positive values.""" result = sigmoid(100.0) # Should approach 1.0 assert result > 0.99 def test_sigmoid_large_negative(self): """Test sigmoid handles large negative values.""" result = sigmoid(-100.0) # Should approach 0.0 assert result < 0.01 def test_sigmoid_overflow_protection(self): """Test sigmoid handles overflow gracefully.""" # Very large negative value that would overflow exp() result = sigmoid(-1000.0) assert result == 0.0 # Very large positive value result = sigmoid(1000.0) assert result == 1.0 def test_sigmoid_symmetry(self): """Test sigmoid(x) + sigmoid(-x) = 1.""" x = 3.5 assert abs(sigmoid(x) + sigmoid(-x) - 1.0) < 0.01 class TestCalculateConfidence: """Tests for calculate_confidence function.""" def test_empty_signals(self): """Test confidence with no signals.""" signals = {} bias = -2.0 confidence = calculate_confidence(signals, bias) # sigmoid(-2) ≈ 0.12 assert 0.1 < confidence < 0.2 def test_single_strong_signal(self): """Test confidence with single strong signal.""" signals = {"explicit_save_request": 4.0} bias = -2.0 confidence = calculate_confidence(signals, bias) # sigmoid(4 - 2 = 2) ≈ 0.88 assert 0.8 < confidence < 0.9 def test_multiple_signals(self): """Test confidence with multiple signals.""" signals = { "explicit_save_request": 4.0, "entity_count": 1.6, # 2 entities * 0.8 } bias = -2.0 confidence = calculate_confidence(signals, bias) # sigmoid(4 + 1.6 - 2 = 3.6) ≈ 0.97 assert confidence > 0.9 def test_negative_signals(self): """Test confidence with negative signals.""" signals = { "explicit_save_request": 4.0, "uncertainty_marker": -2.0, } bias = -2.0 confidence = calculate_confidence(signals, bias) # sigmoid(4 - 2 - 2 = 0) = 0.5 assert 0.4 < confidence < 0.6 def test_strong_negative_dominates(self): """Test strong negative signal dominates.""" signals = { "exclusion": -5.0, } bias = -2.0 confidence = calculate_confidence(signals, bias) # sigmoid(-5 - 2 = -7) ≈ 0.001 assert confidence < 0.1 def test_bias_effect(self): """Test bias term affects confidence.""" signals = {"explicit_save_request": 2.0} # Lower bias = higher confidence conf_low_bias = calculate_confidence(signals, bias=-1.0) conf_high_bias = calculate_confidence(signals, bias=-3.0) assert conf_low_bias > conf_high_bias class TestDetectSaveIntent: """Tests for detect_save_intent function.""" def test_explicit_save_request(self, test_config, test_matcher): """Test detection of explicit save request.""" message = "Remember this: I prefer PostgreSQL for databases" analysis = detect_save_intent(message, test_config, test_matcher) assert analysis.should_save is True assert analysis.confidence >= 0.7 assert "postgresql" in analysis.suggested_entities assert "save_request" in analysis.phrase_signals def test_importance_marker_critical(self, test_config, test_matcher): """Test detection of critical importance marker.""" message = "This is critical information about security" analysis = detect_save_intent(message, test_config, test_matcher) # May or may not auto-save depending on other signals assert isinstance(analysis.should_save, bool) assert "critical_marker" in analysis.phrase_signals def test_importance_marker_important(self, test_config, test_matcher): """Test detection of important marker.""" message = "This is important information" analysis = detect_save_intent(message, test_config, test_matcher) assert "importance_marker" in analysis.phrase_signals def test_exclusion_pattern_prevents_save(self, test_config, test_matcher): """Test exclusion pattern blocks save.""" message = "What is PostgreSQL?" analysis = detect_save_intent(message, test_config, test_matcher) # Exclusion should strongly reduce confidence assert analysis.should_save is False assert "exclusion_pattern" in analysis.phrase_signals def test_uncertainty_marker_reduces_confidence(self, test_config, test_matcher): """Test uncertainty marker reduces confidence.""" message = "I might prefer PostgreSQL, maybe" analysis = detect_save_intent(message, test_config, test_matcher) assert "uncertainty_marker" in analysis.phrase_signals # Uncertainty should lower confidence assert analysis.confidence < 0.9 def test_entity_count_contribution(self, test_config, test_matcher): """Test entity count increases confidence.""" message_few = "Remember this: PostgreSQL" message_many = "Remember this: PostgreSQL MongoDB Redis FastAPI Django" analysis_few = detect_save_intent(message_few, test_config, test_matcher) analysis_many = detect_save_intent(message_many, test_config, test_matcher) # More entities should increase confidence assert analysis_many.confidence >= analysis_few.confidence assert len(analysis_many.suggested_entities) > len(analysis_few.suggested_entities) def test_preference_statement(self, test_config, test_matcher): """Test preference detection.""" message = "I prefer PostgreSQL over MongoDB" analysis = detect_save_intent(message, test_config, test_matcher) assert analysis.should_save is True assert ( "decision_marker" in analysis.phrase_signals or "save_request" in analysis.phrase_signals ) def test_suggested_strength_calculation(self, test_config, test_matcher): """Test suggested_strength based on confidence.""" # Very high confidence message_high = "Remember this: critical security information about PostgreSQL" analysis_high = detect_save_intent(message_high, test_config, test_matcher) # Lower confidence message_low = "Some regular information" analysis_low = detect_save_intent(message_low, test_config, test_matcher) # High confidence should suggest higher strength if analysis_high.confidence >= 0.9: assert analysis_high.suggested_strength >= 1.5 if analysis_low.confidence < 0.7: assert analysis_low.suggested_strength <= 1.5 def test_suggested_tags_generation(self, test_config, test_matcher): """Test automatic tag suggestion.""" message = "I prefer PostgreSQL for my API databases" analysis = detect_save_intent(message, test_config, test_matcher) # Should suggest relevant tags assert any(tag in ["database", "api", "preference"] for tag in analysis.suggested_tags) def test_reasoning_string_format(self, test_config, test_matcher): """Test reasoning string is informative.""" message = "Remember this: I prefer PostgreSQL" analysis = detect_save_intent(message, test_config, test_matcher) # Reasoning should contain signal breakdown assert "Signals:" in analysis.reasoning assert "Raw score:" in analysis.reasoning assert "Confidence:" in analysis.reasoning def test_empty_message(self, test_config, test_matcher): """Test detection with empty message.""" message = "" analysis = detect_save_intent(message, test_config, test_matcher) assert analysis.should_save is False assert analysis.confidence < 0.5 assert len(analysis.suggested_entities) == 0 class TestDetectRecallIntent: """Tests for detect_recall_intent function.""" def test_explicit_recall_request(self, test_config, test_matcher): """Test detection of explicit recall request.""" query = "What did I say about PostgreSQL?" analysis = detect_recall_intent(query, test_config, test_matcher) assert analysis.should_search is True assert analysis.confidence >= 0.7 assert "recall_request" in analysis.phrase_signals def test_past_reference_detection(self, test_config, test_matcher): """Test detection of past references.""" query = "What did we discuss last time about databases?" analysis = detect_recall_intent(query, test_config, test_matcher) assert "past_reference" in analysis.phrase_signals assert analysis.confidence > 0.5 def test_question_marker_detection(self, test_config, test_matcher): """Test question markers increase recall likelihood.""" query = "What database preferences do we have?" analysis = detect_recall_intent(query, test_config, test_matcher) assert "question_marker" in analysis.phrase_signals def test_possessive_reference_detection(self, test_config, test_matcher): """Test possessive references suggest recall.""" query = "Tell me about my database preference" analysis = detect_recall_intent(query, test_config, test_matcher) assert "possessive_reference" in analysis.phrase_signals assert analysis.confidence > 0.5 def test_exclusion_pattern_blocks_recall(self, test_config, test_matcher): """Test exclusion pattern blocks general questions.""" query = "What is PostgreSQL?" analysis = detect_recall_intent(query, test_config, test_matcher) # General "what is" questions should not trigger recall assert analysis.should_search is False assert "exclusion_pattern" in analysis.phrase_signals def test_suggested_query_extraction(self, test_config, test_matcher): """Test extraction of search query.""" query = "What did I say about PostgreSQL preferences?" analysis = detect_recall_intent(query, test_config, test_matcher) # Should strip recall triggers but keep content assert "postgresql" in analysis.suggested_query.lower() assert "preferences" in analysis.suggested_query.lower() def test_suggested_tags_from_entities(self, test_config, test_matcher): """Test tags generated from entities.""" query = "What did I say about PostgreSQL and MongoDB?" analysis = detect_recall_intent(query, test_config, test_matcher) # Should use entities as tags (first 3) assert len(analysis.suggested_tags) <= 3 if analysis.suggested_entities: assert analysis.suggested_tags[0] in analysis.suggested_entities def test_entity_count_increases_confidence(self, test_config, test_matcher): """Test more entities increase confidence.""" query_few = "What did I say?" query_many = "What did I say about PostgreSQL MongoDB Redis FastAPI?" analysis_few = detect_recall_intent(query_few, test_config, test_matcher) analysis_many = detect_recall_intent(query_many, test_config, test_matcher) # More specific query should have higher confidence assert analysis_many.confidence >= analysis_few.confidence def test_reasoning_string_format(self, test_config, test_matcher): """Test reasoning string format.""" query = "What did I say about PostgreSQL?" analysis = detect_recall_intent(query, test_config, test_matcher) assert "Signals:" in analysis.reasoning assert "Raw score:" in analysis.reasoning assert "Confidence:" in analysis.reasoning def test_empty_query(self, test_config, test_matcher): """Test recall detection with empty query.""" query = "" analysis = detect_recall_intent(query, test_config, test_matcher) assert analysis.should_search is False assert analysis.confidence < 0.5 class TestCreateActivationSignal: """Tests for create_activation_signal factory function.""" def test_create_save_signal(self): """Test creating save activation signal.""" signal = create_activation_signal( signal_type="save", confidence=0.92, matched_patterns=["remember this", "i prefer"], context="Remember this: I prefer PostgreSQL for databases", ) assert signal.type == "save" assert signal.confidence == 0.92 assert len(signal.matched_patterns) == 2 assert "remember this" in signal.matched_patterns assert signal.context == "Remember this: I prefer PostgreSQL for databases" assert signal.timestamp > 0 def test_create_recall_signal(self): """Test creating recall activation signal.""" signal = create_activation_signal( signal_type="recall", confidence=0.85, matched_patterns=["what did i say"], context="What did I say about databases?", ) assert signal.type == "recall" assert signal.confidence == 0.85 def test_create_reinforce_signal(self): """Test creating reinforce activation signal.""" signal = create_activation_signal( signal_type="reinforce", confidence=0.75, matched_patterns=[], context="Using PostgreSQL knowledge again", ) assert signal.type == "reinforce" assert signal.confidence == 0.75 def test_context_truncation(self): """Test context is truncated to max length.""" long_context = "x" * 2000 signal = create_activation_signal( signal_type="save", confidence=0.8, matched_patterns=[], context=long_context, ) # Context should be truncated to 1000 chars assert len(signal.context) == 1000 def test_empty_matched_patterns(self): """Test creating signal with no matched patterns.""" signal = create_activation_signal( signal_type="save", confidence=0.6, matched_patterns=[], context="Some context", ) assert len(signal.matched_patterns) == 0 def test_timestamp_is_current(self): """Test timestamp is set to current time.""" import time before = int(time.time()) signal = create_activation_signal( signal_type="save", confidence=0.8, matched_patterns=[], context="Test", ) after = int(time.time()) assert before <= signal.timestamp <= after class TestEdgeCases: """Tests for edge cases and boundary conditions.""" def test_confidence_at_auto_threshold(self, test_config, test_matcher): """Test decision at exact auto threshold.""" # Craft message to hit threshold exactly (difficult, so test near it) message = "Remember this: PostgreSQL" analysis = detect_save_intent(message, test_config, test_matcher) # Should make clear decision assert isinstance(analysis.should_save, bool) def test_confidence_at_ask_threshold(self, test_config, test_matcher): """Test decision at ask threshold.""" # Craft message with medium confidence message = "Maybe PostgreSQL is important" analysis = detect_save_intent(message, test_config, test_matcher) # Between ask and auto thresholds if 0.4 <= analysis.confidence < 0.7: # Should not auto-save assert analysis.should_save is False def test_very_long_message(self, test_config, test_matcher): """Test detection with very long message.""" message = "Remember this: " + "PostgreSQL " * 500 analysis = detect_save_intent(message, test_config, test_matcher) # Should handle without error assert isinstance(analysis.confidence, float) def test_unicode_text(self, test_config, test_matcher): """Test detection with unicode characters.""" message = "Remember this: I prefer PostgreSQL for my café ☕" analysis = detect_save_intent(message, test_config, test_matcher) assert analysis.should_save is True def test_multiline_message(self, test_config, test_matcher): """Test detection with multiline text.""" message = """Remember this: I prefer PostgreSQL for databases""" analysis = detect_save_intent(message, test_config, test_matcher) assert analysis.should_save is True def test_special_characters(self, test_config, test_matcher): """Test detection with special characters.""" message = "Remember this: API keys = sk-abc123!" analysis = detect_save_intent(message, test_config, test_matcher) assert analysis.should_save is True

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/prefrontalsys/mnemex'

If you have feedback or need assistance with the MCP directory API, please join our Discord server