main
py 101 lines 3.17 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4
5 import asyncio
6 import json
7 from http import HTTPStatus
8
9 from fastapi import APIRouter, Depends, FastAPI, HTTPException, Request, Response
10 from fastapi.responses import JSONResponse, StreamingResponse
11
12 from vllm.engine.protocol import EngineClient
13 from vllm.entrypoints.serve.tokenize.serving import ServingTokenization
14 from vllm.entrypoints.serve.utils.api_utils import (
15 load_aware_call,
16 validate_json_request,
17 with_cancellation,
18 )
19 from vllm.logger import init_logger
20
21 from ...serve.engine.protocol import ErrorResponse
22 from .protocol import (
23 GenerateRequest,
24 GenerateResponse,
25 )
26 from .serving import ServingTokens
27
28 logger = init_logger(__name__)
29
30
31 def tokenization(request: Request) -> ServingTokenization:
32 return request.app.state.serving_tokenization
33
34
35 def generate_tokens(request: Request) -> ServingTokens | None:
36 return request.app.state.serving_tokens
37
38
39 def engine_client(request: Request) -> EngineClient:
40 return request.app.state.engine_client
41
42
43 router = APIRouter()
44
45
46 @router.post(
47 "/inference/v1/generate",
48 dependencies=[Depends(validate_json_request)],
49 responses={
50 HTTPStatus.OK.value: {"content": {"text/event-stream": {}}},
51 HTTPStatus.BAD_REQUEST.value: {"model": ErrorResponse},
52 HTTPStatus.NOT_FOUND.value: {"model": ErrorResponse},
53 HTTPStatus.INTERNAL_SERVER_ERROR.value: {"model": ErrorResponse},
54 },
55 )
56 @with_cancellation
57 @load_aware_call
58 async def generate(request: GenerateRequest, raw_request: Request):
59 handler = generate_tokens(raw_request)
60 if handler is None:
61 raise NotImplementedError("The model does not support generate tokens API")
62
63 generator = await handler.serve_tokens(request, raw_request)
64
65 if isinstance(generator, ErrorResponse):
66 return JSONResponse(
67 content=generator.model_dump(), status_code=generator.error.code
68 )
69
70 elif isinstance(generator, GenerateResponse):
71 return JSONResponse(content=generator.model_dump())
72
73 return StreamingResponse(content=generator, media_type="text/event-stream")
74
75
76 def attach_router(app: FastAPI):
77 if getattr(app.state.args, "tokens_only", False):
78
79 @router.post("/abort_requests")
80 async def abort_requests(raw_request: Request):
81 """Abort one or more requests. To be used in a
82 Disaggregated Everything setup.
83 """
84 try:
85 body = await raw_request.json()
86 except json.JSONDecodeError as e:
87 raise HTTPException(
88 status_code=HTTPStatus.BAD_REQUEST.value,
89 detail=f"JSON decode error: {e}",
90 ) from e
91 request_ids = body.get("request_ids")
92 if request_ids is None:
93 raise HTTPException(
94 status_code=HTTPStatus.BAD_REQUEST.value,
95 detail="Missing 'request_ids' in request body",
96 )
97 # Abort requests in background
98 asyncio.create_task(engine_client(raw_request).abort(request_ids))
99 return Response(status_code=200)
100
101 app.include_router(router)