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
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 12:01 +0000
1"""
2Route loader for the Semantic Guard guardrail.
4Loads route definitions from built-in YAML templates and custom configs,
5then builds a SemanticRouter for prompt matching.
6"""
8import os
9from collections.abc import Mapping, Sequence
10from typing import TYPE_CHECKING, Any, Final
12import yaml
14from litellm._logging import verbose_logger
15from litellm.constants import DEFAULT_SEMANTIC_GUARD_SIMILARITY_THRESHOLD
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
21 from litellm.router import Router
24ROUTE_TEMPLATES_DIR: Final = os.path.join(os.path.dirname(__file__), "route_templates")
27class SemanticGuardRouteLoader:
28 """Loads route definitions from YAML templates and custom configs, builds SemanticRouter."""
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)
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)
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}")
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
76 routes: Final[list[Route]] = []
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 )
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 )
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 )
116 verbose_logger.info("SemanticGuard: built %s routes", len(routes))
117 return routes
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
130 from litellm.router_strategy.auto_router.litellm_encoder import (
131 LiteLLMRouterEncoder,
132 )
134 encoder: Final = LiteLLMRouterEncoder(
135 litellm_router_instance=litellm_router,
136 model_name=embedding_model,
137 score_threshold=global_threshold,
138 )
140 return SemanticRouter(
141 routes=routes,
142 encoder=encoder,
143 auto_sync="local",
144 )