Skip to content

Commit 979d106

Browse files
committed
Adding support to route a request to a specific engine instance
Signed-off-by: Braulio Dumba <Braulio.Dumba@ibm.com>
1 parent ff7a6c1 commit 979d106

3 files changed

Lines changed: 67 additions & 7 deletions

File tree

src/vllm_router/routers/main_router.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,39 @@ async def show_models():
145145
return JSONResponse(content=model_list.model_dump())
146146

147147

148+
@main_router.get("/engines")
149+
async def get_engine_instances():
150+
"""
151+
Returns a list of all models available in the stack.
152+
153+
Args:
154+
None
155+
156+
Returns:
157+
JSONResponse: A JSON response containing the list of models.
158+
159+
Raises:
160+
Exception: If there is an error in retrieving the endpoint information.
161+
"""
162+
endpoints = get_service_discovery().get_endpoint_info()
163+
existing_engines = set()
164+
engines_cards = []
165+
166+
for endpoint in endpoints:
167+
if endpoint.Id in existing_engines:
168+
continue
169+
engine_card = {
170+
"engine_id": endpoint.Id,
171+
"serving_models": endpoint.model_names,
172+
"created": endpoint.added_timestamp,
173+
}
174+
175+
engines_cards.append(engine_card)
176+
existing_engines.add(endpoint.Id)
177+
178+
return JSONResponse(content=engines_cards)
179+
180+
148181
@main_router.get("/health")
149182
async def health() -> Response:
150183
"""

src/vllm_router/service_discovery.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import os
2020
import threading
2121
import time
22+
import uuid
2223
from dataclasses import dataclass
2324
from typing import Dict, List, Optional
2425

@@ -46,6 +47,9 @@ class EndpointInfo:
4647
# Model names
4748
model_names: List[str]
4849

50+
# Endpoint Id
51+
Id: str
52+
4953
# Added timestamp
5054
added_timestamp: float
5155

@@ -175,6 +179,7 @@ def __init__(
175179
self.aliases = aliases
176180
self.model_labels = model_labels
177181
self.model_types = model_types
182+
self.engines_id = [str(uuid.uuid4()) for i in range(0, len(urls))]
178183
self.added_timestamp = int(time.time())
179184
self.unhealthy_endpoint_hashes = []
180185
if static_backend_health_checks:
@@ -248,6 +253,7 @@ def get_endpoint_info(self) -> List[EndpointInfo]:
248253
endpoint_info = EndpointInfo(
249254
url=url,
250255
model_names=[model], # Convert single model to list
256+
Id=self.engines_id[i],
251257
added_timestamp=self.added_timestamp,
252258
model_label=model_label,
253259
model_info=self._get_model_info(model),
@@ -439,6 +445,7 @@ def _add_engine(
439445
url=f"http://{engine_ip}:{self.port}",
440446
model_names=model_names,
441447
added_timestamp=int(time.time()),
448+
Id=str(uuid.uuid5(uuid.NAMESPACE_DNS, engine_name)),
442449
model_label=model_label,
443450
pod_name=engine_name,
444451
namespace=self.namespace,

src/vllm_router/services/request_service/request.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,11 @@ async def route_general_request(
175175
request_body = await request.body()
176176
request_json = await request.json() # TODO (ApostaC): merge two awaits into one
177177

178+
if request.query_params:
179+
request_endpoint = request.query_params.get("id")
180+
else:
181+
request_endpoint = None
182+
178183
if hasattr(request.app.state, "callbacks") and (
179184
response_overwrite := request.app.state.callbacks.pre_request(
180185
request, request_body, request_json
@@ -206,26 +211,40 @@ async def route_general_request(
206211
# TODO (ApostaC): merge two awaits into one
207212
service_discovery = get_service_discovery()
208213
endpoints = service_discovery.get_endpoint_info()
209-
engine_stats = request.app.state.engine_stats_scraper.get_engine_stats()
210-
request_stats = request.app.state.request_stats_monitor.get_request_stats(
211-
time.time()
212-
)
213214

214215
aliases = getattr(service_discovery, "aliases", None)
215216
if aliases and requested_model in aliases.keys():
216217
requested_model = aliases[requested_model]
217218
request_body = replace_model_in_request_body(request_json, requested_model)
218219
update_content_length(request, request_body)
219220

220-
# Filter endpoints that have the requested model
221-
endpoints = list(filter(lambda x: requested_model in x.model_names, endpoints))
221+
if not request_endpoint:
222+
endpoints = list(filter(lambda x: requested_model in x.model_names, endpoints))
223+
engine_stats = request.app.state.engine_stats_scraper.get_engine_stats()
224+
request_stats = request.app.state.request_stats_monitor.get_request_stats(
225+
time.time()
226+
)
227+
else:
228+
endpoints = list(
229+
filter(
230+
lambda x: requested_model in x.model_names and x.Id == request_endpoint,
231+
endpoints,
232+
)
233+
)
234+
222235
if not endpoints:
223236
return JSONResponse(
224237
status_code=400, content={"error": f"Model {requested_model} not found."}
225238
)
226239

227240
logger.debug(f"Routing request {request_id} for model: {requested_model}")
228-
if isinstance(request.app.state.router, KvawareRouter) or isinstance(
241+
if request_endpoint:
242+
server_url = endpoints[0].url
243+
logger.debug(
244+
f"Routing request {request_id} to engine with Id: {endpoints[0].Id}"
245+
)
246+
247+
elif isinstance(request.app.state.router, KvawareRouter) or isinstance(
229248
request.app.state.router, PrefixAwareRouter
230249
):
231250
server_url = await request.app.state.router.route_request(
@@ -235,6 +254,7 @@ async def route_general_request(
235254
server_url = request.app.state.router.route_request(
236255
endpoints, engine_stats, request_stats, request
237256
)
257+
238258
curr_time = time.time()
239259
logger.info(
240260
f"Routing request {request_id} to {server_url} at {curr_time}, process time = {curr_time - in_router_time:.4f}"

0 commit comments

Comments
 (0)