Coverage for langsmith/_internal/_edit_distance.py: 0%

26 statements  

« prev     ^ index     » next       coverage.py v7.10.1, created at 2025-12-11 16:15 -0800

1from typing import Any, Callable, Literal, Optional 

2 

3from typing_extensions import TypedDict 

4 

5METRICS = Literal[ 

6 "damerau_levenshtein", 

7 "levenshtein", 

8 "jaro", 

9 "jaro_winkler", 

10 "hamming", 

11 "indel", 

12] 

13 

14 

15class EditDistanceConfig(TypedDict, total=False): 

16 metric: METRICS 

17 normalize_score: bool 

18 

19 

20class EditDistance: 

21 def __init__( 

22 self, 

23 config: Optional[EditDistanceConfig] = None, 

24 ): 

25 config = config or {} 

26 metric = config.get("metric") or "damerau_levenshtein" 

27 self.metric = self._get_metric( 

28 metric, normalize_score=config.get("normalize_score", True) 

29 ) 

30 

31 def evaluate( 

32 self, 

33 prediction: str, 

34 reference: Optional[str] = None, 

35 ) -> float: 

36 return self.metric(prediction, reference) 

37 

38 @staticmethod 

39 def _get_metric(distance: str, normalize_score: bool = True) -> Callable: 

40 try: 

41 from rapidfuzz import ( # type: ignore[import-not-found] 

42 distance as rf_distance, 

43 ) 

44 except ImportError: 

45 raise ImportError( 

46 "This operation requires the rapidfuzz library to use." 

47 "Please install it with `pip install -U rapidfuzz`." 

48 ) 

49 

50 module_map: dict[str, Any] = { 

51 "damerau_levenshtein": rf_distance.DamerauLevenshtein, 

52 "levenshtein": rf_distance.Levenshtein, 

53 "jaro": rf_distance.Jaro, 

54 "jaro_winkler": rf_distance.JaroWinkler, 

55 "hamming": rf_distance.Hamming, 

56 "indel": rf_distance.Indel, 

57 } 

58 if distance not in module_map: 

59 raise ValueError( 

60 f"Invalid distance metric: {distance}" 

61 f"\nMust be one of: {list(module_map)}" 

62 ) 

63 module = module_map[distance] 

64 if normalize_score: 

65 return module.normalized_distance 

66 else: 

67 return module.distance