diff --git a/semantic_router/layer.py b/semantic_router/layer.py index 08afccfab19b590a1fc975e9325c69d6cb961d0b..35f5140594bd972fea4027fb9dcbf5345cdc7c2c 100644 --- a/semantic_router/layer.py +++ b/semantic_router/layer.py @@ -13,7 +13,7 @@ from semantic_router.llms import BaseLLM, OpenAILLM from semantic_router.route import Route from semantic_router.schema import Encoder, EncoderType, RouteChoice from semantic_router.utils.logger import logger - +import importlib def is_valid(layer_config: str) -> bool: """Make sure the given string is json format and contains the 3 keys: ["encoder_name", "encoder_type", "routes"]""" @@ -77,7 +77,6 @@ class LayerConfig: @classmethod def from_file(cls, path: str) -> "LayerConfig": - """Load the routes from a file in JSON or YAML format""" logger.info(f"Loading route config from {path}") _, ext = os.path.splitext(path) with open(path, "r") as f: @@ -86,21 +85,33 @@ class LayerConfig: elif ext in [".yaml", ".yml"]: layer = yaml.safe_load(f) else: - raise ValueError( - "Unsupported file type. Only .json and .yaml are supported" - ) + raise ValueError("Unsupported file type. Only .json and .yaml are supported") - route_config_str = json.dumps(layer) - if is_valid(route_config_str): - encoder_type = layer["encoder_type"] - encoder_name = layer["encoder_name"] - routes = [Route.from_dict(route) for route in layer["routes"]] - return cls( - encoder_type=encoder_type, encoder_name=encoder_name, routes=routes - ) - else: + if not is_valid(json.dumps(layer)): raise Exception("Invalid config JSON or YAML") + encoder_type = layer["encoder_type"] + encoder_name = layer["encoder_name"] + routes = [] + for route_data in layer["routes"]: + # Handle the 'llm' field specially if it exists + if 'llm' in route_data: + llm_data = route_data.pop('llm') # Remove 'llm' from route_data and handle it separately + # Use the module path directly from llm_data without modification + llm_module_path = llm_data['module'] + # Dynamically import the module and then the class from that module + llm_module = importlib.import_module(llm_module_path) + llm_class = getattr(llm_module, llm_data['class']) + # Instantiate the LLM class with the provided model name + llm = llm_class(name=llm_data['model']) + route_data['llm'] = llm # Reassign the instantiated llm object back to route_data + + # Dynamically create the Route object using the remaining route_data + route = Route(**route_data) + routes.append(route) + + return cls(encoder_type=encoder_type, encoder_name=encoder_name, routes=routes) + def to_dict(self) -> Dict[str, Any]: return { "encoder_type": self.encoder_type, diff --git a/semantic_router/route.py b/semantic_router/route.py index 1fe9983d8b4bd6bed120ca0582610e446b19bfc2..1b08547a54c405385fd7579bb0107bf181c8d7d6 100644 --- a/semantic_router/route.py +++ b/semantic_router/route.py @@ -68,8 +68,18 @@ class Route(BaseModel): func_call = None return RouteChoice(name=self.name, function_call=func_call) + # def to_dict(self) -> Dict[str, Any]: + # return self.dict() + def to_dict(self) -> Dict[str, Any]: - return self.dict() + data = self.dict() + if self.llm is not None: + data['llm'] = { + 'module': self.llm.__module__, + 'class': self.llm.__class__.__name__, + 'model': self.llm.name + } + return data @classmethod def from_dict(cls, data: Dict[str, Any]):