acsaco commited on
Commit
33174d8
·
verified ·
1 Parent(s): 00ee26b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -136
app.py CHANGED
@@ -1,151 +1,57 @@
1
- import os
2
- import time
3
- import json
4
- import uuid
5
  import gradio as gr
 
6
  from fastapi import FastAPI, Request
7
- from fastapi.responses import StreamingResponse, JSONResponse
8
- from pydantic import BaseModel
9
- from typing import List, Optional
10
 
11
- from vllm.engine.async_llm_engine import AsyncLLMEngine
12
- from vllm.engine.arg_utils import AsyncEngineArgs
13
- from vllm.sampling_params import SamplingParams
14
- from transformers import AutoTokenizer
15
 
16
- # --- Configuración del Modelo ---
17
- MODEL_NAME = os.getenv("MODEL_NAME", "Qwen/Qwen3.8-27B")
18
-
19
- # Argumentos del motor vLLM
20
- engine_args = AsyncEngineArgs(
21
- model=MODEL_NAME,
22
- trust_remote_code=True,
23
- gpu_memory_utilization=0.90,
24
- max_model_len=4096,
25
- tensor_parallel_size=1
26
  )
27
 
28
- # Inicializar motor y tokenizador
29
- engine = AsyncLLMEngine.from_engine_args(engine_args)
30
- tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
31
-
32
- app = FastAPI(title="vLLM Gradio + OpenAI Server")
33
-
34
- # --- Esquemas Pydantic OpenAI ---
35
- class ChatMessage(BaseModel):
36
- role: str
37
- content: str
38
 
39
- class ChatCompletionRequest(BaseModel):
40
- model: Optional[str] = MODEL_NAME
41
- messages: List[ChatMessage]
42
- temperature: Optional[float] = 0.7
43
- top_p: Optional[float] = 0.95
44
- max_tokens: Optional[int] = 2048
45
- stream: Optional[bool] = False
46
-
47
- # --- Endpoints OpenAI (/v1) ---
48
- @app.get("/v1/models")
49
- async def list_models():
50
- return {
51
- "object": "list",
52
- "data": [{"id": MODEL_NAME, "object": "model", "owned_by": "vllm"}]
53
- }
54
-
55
- @app.post("/v1/chat/completions")
56
- async def chat_completions(req: ChatCompletionRequest):
57
- raw_messages = [{"role": m.role, "content": m.content} for m in req.messages]
58
-
59
- # Aplicar plantilla de chat del modelo
60
- prompt = tokenizer.apply_chat_template(raw_messages, tokenize=False, add_generation_prompt=True)
61
 
62
- sampling_params = SamplingParams(
63
- temperature=req.temperature if req.temperature > 0 else 0.0,
64
- top_p=req.top_p,
65
- max_tokens=req.max_tokens or 2048
66
- )
67
 
68
- request_id = f"chatcmpl-{uuid.uuid4().hex}"
 
 
 
69
 
70
- if req.stream:
71
- async def stream_generator():
72
- prev_text = ""
73
- results_generator = engine.generate(prompt, sampling_params, request_id)
74
- async for request_output in results_generator:
75
- text = request_output.outputs[0].text
76
- delta = text[len(prev_text):]
77
- prev_text = text
78
-
79
- chunk_data = {
80
- "id": request_id,
81
- "object": "chat.completion.chunk",
82
- "created": int(time.time()),
83
- "model": MODEL_NAME,
84
- "choices": [{"index": 0, "delta": {"content": delta}, "finish_reason": None}]
85
- }
86
- yield f"data: {json.dumps(chunk_data)}\n\n"
87
- yield "data: [DONE]\n\n"
88
-
89
- return StreamingResponse(stream_generator(), media_type="text/event-stream")
90
- else:
91
- results_generator = engine.generate(prompt, sampling_params, request_id)
92
- final_output = None
93
- async for request_output in results_generator:
94
- final_output = request_output
95
-
96
- response_text = final_output.outputs[0].text if final_output else ""
97
- return {
98
- "id": request_id,
99
- "object": "chat.completion",
100
- "created": int(time.time()),
101
- "model": MODEL_NAME,
102
- "choices": [{
103
- "index": 0,
104
- "message": {"role": "assistant", "content": response_text},
105
- "finish_reason": "stop"
106
- }]
107
- }
108
-
109
- # --- Interfaz de Gradio ---
110
- async def gradio_chat(message, history, system_prompt, temperature, max_tokens):
111
- messages = []
112
- if system_prompt:
113
- messages.append({"role": "system", "content": system_prompt})
114
-
115
- for user_msg, bot_msg in history:
116
- messages.append({"role": "user", "content": user_msg})
117
- messages.append({"role": "assistant", "content": bot_msg})
118
-
119
- messages.append({"role": "user", "content": message})
120
-
121
  prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
122
- sampling_params = SamplingParams(
123
- temperature=temperature if temperature > 0 else 0.0,
124
- max_tokens=max_tokens
125
- )
126
 
127
- request_id = f"gradio-{uuid.uuid4().hex}"
128
- results_generator = engine.generate(prompt, sampling_params, request_id)
129
 
130
- async for request_output in results_generator:
131
- yield request_output.outputs[0].text
132
-
133
- with gr.Blocks(title="vLLM + OpenAI Compatible Server") as demo:
134
- gr.Markdown(f"# 🚀 vLLM LLM Engine\n**Modelo cargado:** `{MODEL_NAME}`\n\n**Base URL para Cursor / OpenCode:** `https://huggingface.co/spaces/acsaco.hf.space/v1`")
135
-
136
- with gr.Accordion("Configuración Avanzada", open=False):
137
- system_prompt = gr.Textbox(label="System Prompt", value="Eres un asistente experto en programación.")
138
- temperature = gr.Slider(minimum=0.0, maximum=1.0, value=0.7, step=0.05, label="Temperature")
139
- max_tokens = gr.Slider(minimum=128, maximum=4096, value=2048, step=128, label="Max Tokens")
140
-
141
- gr.ChatInterface(
142
- fn=gradio_chat,
143
- additional_inputs=[system_prompt, temperature, max_tokens]
144
- )
145
 
146
- # Montar interfaz web Gradio dentro del servidor FastAPI
147
- app = gr.mount_gradio_app(app, demo, path="/")
148
 
149
- if __name__ == "__main__":
150
- import uvicorn
151
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
1
+ import spaces
 
 
 
2
  import gradio as gr
3
+ import torch
4
  from fastapi import FastAPI, Request
5
+ from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
6
+ from threading import Thread
 
7
 
8
+ MODEL_ID = "Qwen/Qwen3.8-27B" # Modelos >14B suelen exceder la memoria dinámica de ZeroGPU
 
 
 
9
 
10
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
11
+ model = AutoModelForCausalLM.from_pretrained(
12
+ MODEL_ID,
13
+ torch_dtype=torch.bfloat16,
14
+ device_map="auto"
 
 
 
 
 
15
  )
16
 
17
+ app = FastAPI()
 
 
 
 
 
 
 
 
 
18
 
19
+ @spaces.GPU(duration=120)
20
+ def generate_response(prompt: str, max_tokens: int = 2048):
21
+ inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
22
+ streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
23
+ generation_kwargs = dict(inputs, streamer=streamer, max_new_tokens=max_tokens)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
+ thread = Thread(target=model.generate, kwargs=generation_kwargs)
26
+ thread.start()
 
 
 
27
 
28
+ output_text = ""
29
+ for new_text in streamer:
30
+ output_text += new_text
31
+ return output_text
32
 
33
+ @app.post("/v1/chat/completions")
34
+ async def chat_completions(request: Request):
35
+ data = await request.json()
36
+ messages = data.get("messages", [])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
 
 
 
 
38
 
39
+ response_text = generate_response(prompt)
 
40
 
41
+ return {
42
+ "id": "chatcmpl-zerogpu",
43
+ "object": "chat.completion",
44
+ "choices": [{
45
+ "index": 0,
46
+ "message": {
47
+ "role": "assistant",
48
+ "content": response_text
49
+ },
50
+ "finish_reason": "stop"
51
+ }]
52
+ }
 
 
 
53
 
54
+ with gr.Blocks() as demo:
55
+ gr.Markdown("# Qwen ZeroGPU Endpoint")
56
 
57
+ app = gr.mount_gradio_app(app, demo, path="/")