Coverage for src/qdrant_loader_mcp_server/search/hybrid/components/scoring.py: 91%

23 statements  

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

1from __future__ import annotations 

2 

3from collections.abc import Iterable 

4from dataclasses import dataclass 

5 

6 

7@dataclass 

8class ScoreComponents: 

9 vector_score: float 

10 keyword_score: float 

11 metadata_score: float 

12 

13 

14class HybridScorer: 

15 """Compute hybrid scores from component scores using simple weighted sum. 

16 

17 This is an initial scaffold to enable modularization. The engine continues 

18 to own the weights; this class only applies them deterministically. 

19 """ 

20 

21 def __init__( 

22 self, vector_weight: float, keyword_weight: float, metadata_weight: float 

23 ): 

24 self.vector_weight = float(vector_weight) 

25 self.keyword_weight = float(keyword_weight) 

26 self.metadata_weight = float(metadata_weight) 

27 

28 def compute(self, components: ScoreComponents) -> float: 

29 return ( 

30 components.vector_score * self.vector_weight 

31 + components.keyword_score * self.keyword_weight 

32 + components.metadata_score * self.metadata_weight 

33 ) 

34 

35 def normalize_many(self, scores: Iterable[float]) -> list[float]: 

36 values = list(scores) 

37 if not values: 

38 return [] 

39 max_value = max(values) 

40 if max_value <= 0: 

41 return [0.0 for _ in values] 

42 return [v / max_value for v in values]