main
py 126 lines 4.76 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 from http import HTTPStatus
4
5 from fastapi import APIRouter, Depends, Request
6 from fastapi.responses import JSONResponse
7
8 from vllm.entrypoints.serve.engine.protocol import ErrorResponse
9 from vllm.entrypoints.serve.utils.api_utils import validate_json_request
10 from vllm.logger import init_logger
11
12 from ..token_in_token_out.protocol import (
13 DerenderChatRequestUnion,
14 DerenderChatStreamRequest,
15 DerenderChatStreamResponse,
16 DerenderCompletionRequestUnion,
17 DerenderCompletionStreamRequest,
18 DerenderCompletionStreamResponse,
19 )
20 from .serving import ServingDerender
21
22 logger = init_logger(__name__)
23
24 router = APIRouter()
25
26
27 def derender(request: Request) -> ServingDerender | None:
28 return getattr(request.app.state, "serving_derender", None)
29
30
31 @router.post(
32 "/v1/chat/completions/derender",
33 dependencies=[Depends(validate_json_request)],
34 responses={
35 HTTPStatus.BAD_REQUEST.value: {"model": ErrorResponse},
36 HTTPStatus.NOT_FOUND.value: {"model": ErrorResponse},
37 HTTPStatus.INTERNAL_SERVER_ERROR.value: {"model": ErrorResponse},
38 },
39 )
40 async def derender_chat_completion(
41 request: DerenderChatRequestUnion,
42 raw_request: Request,
43 ):
44 """Derender a generate response into a ChatCompletionResponse.
45
46 Accepts both non-streaming (``stream=false``, default) and streaming
47 (``stream=true``) request bodies on the same path; FastAPI validates and
48 routes on the ``stream`` discriminator.
49
50 Non-streaming: body is ``DerenderChatRequest`` (``generate_response`` with
51 the complete token list). Returns a ``ChatCompletionResponse``.
52
53 Streaming: body is ``DerenderChatStreamRequest`` (one ``generate_chunk``
54 delta + optional ``stream_state``). Returns a ``DerenderChatStreamResponse``
55 (``chunk`` + ``stream_state``). The client carries ``stream_state`` between
56 successive calls, one per SSE chunk from ``/inference/v1/generate``.
57 """
58 handler = derender(raw_request)
59 if handler is None:
60 raise NotImplementedError(
61 "The model does not support Chat Completions Derender API"
62 )
63
64 if isinstance(request, DerenderChatStreamRequest):
65 stream_result = await handler.derender_chat_stream_response(request)
66 if isinstance(stream_result, ErrorResponse):
67 return JSONResponse(
68 content=stream_result.model_dump(),
69 status_code=stream_result.error.code,
70 )
71 chunk, stream_state = stream_result
72 response = DerenderChatStreamResponse(chunk=chunk, stream_state=stream_state)
73 return JSONResponse(content=response.model_dump())
74
75 result = await handler.derender_chat_response(request)
76 if isinstance(result, ErrorResponse):
77 return JSONResponse(content=result.model_dump(), status_code=result.error.code)
78 return JSONResponse(content=result.model_dump())
79
80
81 @router.post(
82 "/v1/completions/derender",
83 dependencies=[Depends(validate_json_request)],
84 responses={
85 HTTPStatus.BAD_REQUEST.value: {"model": ErrorResponse},
86 HTTPStatus.NOT_FOUND.value: {"model": ErrorResponse},
87 HTTPStatus.INTERNAL_SERVER_ERROR.value: {"model": ErrorResponse},
88 },
89 )
90 async def derender_completion(
91 request: DerenderCompletionRequestUnion,
92 raw_request: Request,
93 ):
94 """Derender a generate response into a CompletionResponse.
95
96 Accepts both non-streaming (``stream=false``, default) and streaming
97 (``stream=true``) request bodies on the same path.
98
99 Non-streaming: body is ``DerenderCompletionRequest``. Returns a
100 ``CompletionResponse``.
101
102 Streaming: body is ``DerenderCompletionStreamRequest`` (one
103 ``generate_chunk`` + optional ``stream_state``). Returns a
104 ``DerenderCompletionStreamResponse`` (``chunk`` + ``stream_state``).
105 """
106 handler = derender(raw_request)
107 if handler is None:
108 raise NotImplementedError("The model does not support Completions Derender API")
109
110 if isinstance(request, DerenderCompletionStreamRequest):
111 stream_result = await handler.derender_completion_stream_response(request)
112 if isinstance(stream_result, ErrorResponse):
113 return JSONResponse(
114 content=stream_result.model_dump(),
115 status_code=stream_result.error.code,
116 )
117 chunk, stream_state = stream_result
118 response = DerenderCompletionStreamResponse(
119 chunk=chunk, stream_state=stream_state
120 )
121 return JSONResponse(content=response.model_dump())
122
123 result = await handler.derender_completion_response(request)
124 if isinstance(result, ErrorResponse):
125 return JSONResponse(content=result.model_dump(), status_code=result.error.code)
126 return JSONResponse(content=result.model_dump())