Coverage for .venv/lib/python3.13/site-packages/litellm/proxy/guardrails/guardrail_hooks/semantic_guard/route_loader.py: 22%

64 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 12:01 +0000

1""" 

2Route loader for the Semantic Guard guardrail. 

3 

4Loads route definitions from built-in YAML templates and custom configs, 

5then builds a SemanticRouter for prompt matching. 

6""" 

7 

8import os 

9from collections.abc import Mapping, Sequence 

10from typing import TYPE_CHECKING, Any, Final 

11 

12import yaml 

13 

14from litellm._logging import verbose_logger 

15from litellm.constants import DEFAULT_SEMANTIC_GUARD_SIMILARITY_THRESHOLD 

16 

17if TYPE_CHECKING: 17 ↛ 18line 17 didn't jump to line 18 because the condition on line 17 was never true

18 from semantic_router.routers import SemanticRouter 

19 from semantic_router.routers.base import Route 

20 

21 from litellm.router import Router 

22 

23 

24ROUTE_TEMPLATES_DIR: Final = os.path.join(os.path.dirname(__file__), "route_templates") 

25 

26 

27class SemanticGuardRouteLoader: 

28 """Loads route definitions from YAML templates and custom configs, builds SemanticRouter.""" 

29 

30 @staticmethod 

31 def load_builtin_template(template_name: str) -> dict[str, Any]: 

32 """Load a built-in route template YAML by name.""" 

33 file_path: Final = os.path.join(ROUTE_TEMPLATES_DIR, f"{template_name}.yaml") 

34 if not os.path.exists(file_path): 

35 raise ValueError( 

36 f"SemanticGuard: unknown route template '{template_name}'. " 

37 f"Available templates: {SemanticGuardRouteLoader.list_builtin_templates()}" 

38 ) 

39 with open(file_path, "r") as f: 

40 return yaml.safe_load(f) 

41 

42 @staticmethod 

43 def list_builtin_templates() -> list[str]: 

44 """List available built-in template names.""" 

45 templates: Final = [] 

46 if os.path.isdir(ROUTE_TEMPLATES_DIR): 

47 for fname in os.listdir(ROUTE_TEMPLATES_DIR): 

48 if fname.endswith(".yaml"): 

49 templates.append(fname.replace(".yaml", "")) 

50 return sorted(templates) 

51 

52 @staticmethod 

53 def load_custom_routes_file(file_path: str) -> list[dict[str, Any]]: 

54 """Load custom routes from a YAML file.""" 

55 if not os.path.exists(file_path): 

56 raise ValueError(f"SemanticGuard: custom routes file not found: {file_path}") 

57 with open(file_path, "r") as f: 

58 data: Final = yaml.safe_load(f) 

59 if isinstance(data, list): 

60 return data 

61 if isinstance(data, dict): 

62 return [data] 

63 raise ValueError(f"SemanticGuard: invalid custom routes file format in {file_path}") 

64 

65 @classmethod 

66 def build_routes( 

67 cls, 

68 route_templates: list[str] | None, 

69 custom_routes_file: str | None, 

70 custom_routes: Sequence[Mapping[str, object]] | None, 

71 global_threshold: float = DEFAULT_SEMANTIC_GUARD_SIMILARITY_THRESHOLD, 

72 ) -> list["Route"]: 

73 """Build semantic-router Route objects from templates + custom config.""" 

74 from semantic_router.routers.base import Route 

75 

76 routes: Final[list[Route]] = [] 

77 

78 if route_templates: 

79 for template_name in route_templates: 

80 template_data = cls.load_builtin_template(template_name) 

81 threshold = template_data.get("similarity_threshold", global_threshold) 

82 routes.append( 

83 Route( 

84 name=template_data["route_name"], 

85 description=template_data.get("description", ""), 

86 utterances=template_data["utterances"], 

87 score_threshold=threshold, 

88 ) 

89 ) 

90 

91 if custom_routes_file: 

92 custom_defs: Final = cls.load_custom_routes_file(custom_routes_file) 

93 for route_def in custom_defs: 

94 threshold = route_def.get("similarity_threshold", global_threshold) 

95 routes.append( 

96 Route( 

97 name=route_def["route_name"], 

98 description=route_def.get("description", ""), 

99 utterances=route_def["utterances"], 

100 score_threshold=threshold, 

101 ) 

102 ) 

103 

104 if custom_routes: 

105 for route_def in custom_routes: 

106 threshold = route_def.get("similarity_threshold", global_threshold) 

107 routes.append( 

108 Route( 

109 name=route_def["route_name"], 

110 description=route_def.get("description", ""), 

111 utterances=route_def["utterances"], 

112 score_threshold=threshold, 

113 ) 

114 ) 

115 

116 verbose_logger.info("SemanticGuard: built %s routes", len(routes)) 

117 return routes 

118 

119 @classmethod 

120 def build_semantic_router( 

121 cls, 

122 routes: list["Route"], 

123 litellm_router: "Router", 

124 embedding_model: str, 

125 global_threshold: float, 

126 ) -> "SemanticRouter": 

127 """Build the SemanticRouter with LiteLLMRouterEncoder.""" 

128 from semantic_router.routers import SemanticRouter 

129 

130 from litellm.router_strategy.auto_router.litellm_encoder import ( 

131 LiteLLMRouterEncoder, 

132 ) 

133 

134 encoder: Final = LiteLLMRouterEncoder( 

135 litellm_router_instance=litellm_router, 

136 model_name=embedding_model, 

137 score_threshold=global_threshold, 

138 ) 

139 

140 return SemanticRouter( 

141 routes=routes, 

142 encoder=encoder, 

143 auto_sync="local", 

144 )