@@ -1593,6 +1593,79 @@ def _extract_model_from_vertex_ai_setup(setup_response: dict) -> Optional[str]:
15931593 return None
15941594
15951595
1596+ class SafeRouteAdder :
1597+ """
1598+ Wrapper class for adding routes to FastAPI app.
1599+ Only adds routes if they don't already exist on the app.
1600+ """
1601+
1602+ @staticmethod
1603+ def _is_path_registered (app : FastAPI , path : str , methods : List [str ]) -> bool :
1604+ """
1605+ Check if a path with any of the specified methods is already registered on the app.
1606+
1607+ Args:
1608+ app: The FastAPI application instance
1609+ path: The path to check (e.g., "/v1/chat/completions")
1610+ methods: List of HTTP methods to check (e.g., ["GET", "POST"])
1611+
1612+ Returns:
1613+ True if the path is already registered with any of the methods, False otherwise
1614+ """
1615+ for route in app .routes :
1616+ # Use getattr to safely access route attributes
1617+ route_path = getattr (route , "path" , None )
1618+ route_methods = getattr (route , "methods" , None )
1619+
1620+ if route_path == path and route_methods is not None :
1621+ # Check if any of the methods overlap
1622+ if any (method in route_methods for method in methods ):
1623+ return True
1624+ return False
1625+
1626+ @staticmethod
1627+ def add_api_route_if_not_exists (
1628+ app : FastAPI ,
1629+ path : str ,
1630+ endpoint : Any ,
1631+ methods : List [str ],
1632+ dependencies : Optional [List ] = None ,
1633+ ) -> bool :
1634+ """
1635+ Add an API route to the app only if it doesn't already exist.
1636+
1637+ Args:
1638+ app: The FastAPI application instance
1639+ path: The path for the route
1640+ endpoint: The endpoint function/callable
1641+ methods: List of HTTP methods
1642+ dependencies: Optional list of dependencies
1643+
1644+ Returns:
1645+ True if route was added, False if it already existed
1646+ """
1647+ if SafeRouteAdder ._is_path_registered (app = app , path = path , methods = methods ):
1648+ verbose_proxy_logger .debug (
1649+ "Skipping route registration - path %s with methods %s already registered on app" ,
1650+ path ,
1651+ methods ,
1652+ )
1653+ return False
1654+
1655+ app .add_api_route (
1656+ path = path ,
1657+ endpoint = endpoint ,
1658+ methods = methods ,
1659+ dependencies = dependencies ,
1660+ )
1661+ verbose_proxy_logger .debug (
1662+ "Successfully added route: %s with methods %s" ,
1663+ path ,
1664+ methods ,
1665+ )
1666+ return True
1667+
1668+
15961669class InitPassThroughEndpointHelpers :
15971670 @staticmethod
15981671 def add_exact_path_route (
@@ -1623,7 +1696,9 @@ def add_exact_path_route(
16231696 dependencies ,
16241697 )
16251698
1626- app .add_api_route (
1699+ # Use SafeRouteAdder to only add route if it doesn't exist on the app
1700+ was_added = SafeRouteAdder .add_api_route_if_not_exists (
1701+ app = app ,
16271702 path = path ,
16281703 endpoint = create_pass_through_route ( # type: ignore
16291704 path ,
@@ -1638,20 +1713,21 @@ def add_exact_path_route(
16381713 dependencies = dependencies ,
16391714 )
16401715
1641- # Register the route to prevent duplicates
1642- _registered_pass_through_routes [route_key ] = {
1643- "endpoint_id" : endpoint_id ,
1644- "path" : path ,
1645- "type" : "exact" ,
1646- "passthrough_params" : {
1647- "target" : target ,
1648- "custom_headers" : custom_headers ,
1649- "forward_headers" : forward_headers ,
1650- "merge_query_params" : merge_query_params ,
1651- "dependencies" : dependencies ,
1652- "cost_per_request" : cost_per_request ,
1653- },
1654- }
1716+ # Register the route to prevent duplicates only if it was added
1717+ if was_added :
1718+ _registered_pass_through_routes [route_key ] = {
1719+ "endpoint_id" : endpoint_id ,
1720+ "path" : path ,
1721+ "type" : "exact" ,
1722+ "passthrough_params" : {
1723+ "target" : target ,
1724+ "custom_headers" : custom_headers ,
1725+ "forward_headers" : forward_headers ,
1726+ "merge_query_params" : merge_query_params ,
1727+ "dependencies" : dependencies ,
1728+ "cost_per_request" : cost_per_request ,
1729+ },
1730+ }
16551731
16561732 @staticmethod
16571733 def add_subpath_route (
@@ -1683,7 +1759,9 @@ def add_subpath_route(
16831759 dependencies ,
16841760 )
16851761
1686- app .add_api_route (
1762+ # Use SafeRouteAdder to only add route if it doesn't exist on the app
1763+ was_added = SafeRouteAdder .add_api_route_if_not_exists (
1764+ app = app ,
16871765 path = wildcard_path ,
16881766 endpoint = create_pass_through_route ( # type: ignore
16891767 path ,
@@ -1699,20 +1777,21 @@ def add_subpath_route(
16991777 dependencies = dependencies ,
17001778 )
17011779
1702- # Register the route to prevent duplicates
1703- _registered_pass_through_routes [route_key ] = {
1704- "endpoint_id" : endpoint_id ,
1705- "path" : path ,
1706- "type" : "subpath" ,
1707- "passthrough_params" : {
1708- "target" : target ,
1709- "custom_headers" : custom_headers ,
1710- "forward_headers" : forward_headers ,
1711- "merge_query_params" : merge_query_params ,
1712- "dependencies" : dependencies ,
1713- "cost_per_request" : cost_per_request ,
1714- },
1715- }
1780+ # Register the route to prevent duplicates only if it was added
1781+ if was_added :
1782+ _registered_pass_through_routes [route_key ] = {
1783+ "endpoint_id" : endpoint_id ,
1784+ "path" : path ,
1785+ "type" : "subpath" ,
1786+ "passthrough_params" : {
1787+ "target" : target ,
1788+ "custom_headers" : custom_headers ,
1789+ "forward_headers" : forward_headers ,
1790+ "merge_query_params" : merge_query_params ,
1791+ "dependencies" : dependencies ,
1792+ "cost_per_request" : cost_per_request ,
1793+ },
1794+ }
17161795
17171796 @staticmethod
17181797 def remove_endpoint_routes (endpoint_id : str ):
0 commit comments