Coverage for src/qdrant_loader_mcp_server/search/enhanced/intent/models.py: 95%

58 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-08 06:06 +0000

1""" 

2Core data models for intent classification and adaptive search. 

3 

4This module defines the fundamental data types used throughout the intent 

5classification system, including intent types, search intent containers, 

6and adaptive search configurations. 

7""" 

8 

9from __future__ import annotations 

10 

11from dataclasses import dataclass, field 

12from enum import Enum 

13from typing import TYPE_CHECKING, Any 

14 

15if TYPE_CHECKING: 

16 from ..knowledge_graph import TraversalStrategy 

17else: 

18 # Runtime import to avoid circular dependencies 

19 try: 

20 from ..knowledge_graph import TraversalStrategy 

21 except ImportError: 

22 # Fallback if knowledge_graph isn't available 

23 TraversalStrategy = None 

24 

25 

26# Sentinel to distinguish between an explicit None and an omitted argument 

27_UNSET = object() 

28 

29 

30class IntentType(Enum): 

31 """Types of search intents for adaptive search strategies.""" 

32 

33 TECHNICAL_LOOKUP = "technical_lookup" # API docs, code examples, implementation 

34 BUSINESS_CONTEXT = "business_context" # Requirements, objectives, strategy 

35 VENDOR_EVALUATION = "vendor_evaluation" # Proposals, comparisons, criteria 

36 PROCEDURAL = "procedural" # How-to guides, step-by-step 

37 INFORMATIONAL = "informational" # What is, definitions, overviews 

38 EXPLORATORY = "exploratory" # Broad discovery, browsing 

39 TROUBLESHOOTING = "troubleshooting" # Error solving, debugging 

40 GENERAL = "general" # Fallback for unclear intent 

41 

42 

43@dataclass 

44class SearchIntent: 

45 """Container for classified search intent with confidence and context.""" 

46 

47 intent_type: IntentType 

48 confidence: float # 0.0 - 1.0 confidence score 

49 secondary_intents: list[tuple[IntentType, float]] = field(default_factory=list) 

50 

51 # Linguistic evidence 

52 supporting_evidence: dict[str, Any] = field(default_factory=dict) 

53 linguistic_features: dict[str, Any] = field(default_factory=dict) 

54 

55 # Context information 

56 query_complexity: float = 0.0 # From spaCy analysis 

57 is_question: bool = False 

58 is_technical: bool = False 

59 

60 # Behavioral context 

61 session_context: dict[str, Any] = field(default_factory=dict) 

62 previous_intents: list[IntentType] = field(default_factory=list) 

63 

64 # Processing metadata 

65 classification_time_ms: float = 0.0 

66 

67 

68@dataclass 

69class AdaptiveSearchConfig: 

70 """Configuration for adaptive search based on intent.""" 

71 

72 # Core search parameters 

73 search_strategy: str = "hybrid" # hybrid, vector, keyword 

74 vector_weight: float = 0.7 # Weight for vector search 

75 keyword_weight: float = 0.3 # Weight for keyword search 

76 

77 # Knowledge graph integration 

78 use_knowledge_graph: bool = False 

79 # Use Optional[TraversalStrategy] while leveraging a sentinel default to detect omission 

80 kg_traversal_strategy: TraversalStrategy | None = field(default=_UNSET) 

81 max_graph_hops: int = 2 

82 kg_expansion_weight: float = 0.2 

83 

84 # Result filtering and ranking 

85 result_filters: dict[str, Any] = field(default_factory=dict) 

86 ranking_boosts: dict[str, float] = field(default_factory=dict) 

87 source_type_preferences: dict[str, float] = field(default_factory=dict) 

88 

89 # Query expansion 

90 expand_query: bool = True 

91 expansion_aggressiveness: float = 0.3 # 0.0 - 1.0 

92 semantic_expansion: bool = True 

93 entity_expansion: bool = True 

94 

95 # Performance tuning 

96 max_results: int = 20 

97 min_score_threshold: float = 0.1 

98 diversity_factor: float = 0.0 # 0.0 = relevance only, 1.0 = max diversity 

99 

100 # Contextual parameters 

101 temporal_bias: float = 0.0 # Bias toward recent content 

102 authority_bias: float = 0.0 # Bias toward authoritative sources 

103 personal_bias: float = 0.0 # Bias toward user's previous interests 

104 

105 def __post_init__(self): 

106 """Assign default TraversalStrategy only when the field was omitted. 

107 

108 This preserves explicit None values provided by callers. 

109 """ 

110 if self.kg_traversal_strategy is _UNSET: 

111 if TraversalStrategy is not None: 

112 self.kg_traversal_strategy = TraversalStrategy.SEMANTIC 

113 else: 

114 # If TraversalStrategy isn't available at runtime, keep as None 

115 self.kg_traversal_strategy = None