acsaco commited on
Commit
3dc5acc
verified
1 Parent(s): 5c2d8fb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +97 -46
app.py CHANGED
@@ -1,100 +1,151 @@
1
  import os
2
- import json
3
  import time
4
- from typing import List, Optional
 
5
  import gradio as gr
6
- from fastapi import FastAPI, Request, HTTPException
7
  from fastapi.responses import StreamingResponse, JSONResponse
8
  from pydantic import BaseModel
9
- from huggingface_hub import InferenceClient
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
- # Configuraci贸n del modelo
12
- MODEL_ID = "Qwen/Qwen3.8-27B" # Cambiar al modelo deseado
13
- HF_TOKEN = os.getenv("HF_TOKEN")
14
 
15
- client = InferenceClient(model=MODEL_ID, token=HF_TOKEN)
16
- app = FastAPI(title="OpenAI Compatible HF Space API")
17
 
18
- # --- Esquemas Pydantic para OpenAI API ---
19
  class ChatMessage(BaseModel):
20
  role: str
21
  content: str
22
 
23
  class ChatCompletionRequest(BaseModel):
24
- model: Optional[str] = MODEL_ID
25
  messages: List[ChatMessage]
26
  temperature: Optional[float] = 0.7
 
27
  max_tokens: Optional[int] = 2048
28
  stream: Optional[bool] = False
29
 
30
- # --- Endpoints OpenAI ---
31
  @app.get("/v1/models")
32
  async def list_models():
33
  return {
34
  "object": "list",
35
- "data": [{"id": MODEL_ID, "object": "model", "owned_by": "huggingface"}]
36
  }
37
 
38
  @app.post("/v1/chat/completions")
39
  async def chat_completions(req: ChatCompletionRequest):
40
- formatted_messages = [{"role": m.role, "content": m.content} for m in req.messages]
 
 
 
 
 
 
 
 
 
 
 
41
 
42
  if req.stream:
43
  async def stream_generator():
44
- stream = client.chat_completion(
45
- messages=formatted_messages,
46
- temperature=req.temperature,
47
- max_tokens=req.max_tokens,
48
- stream=True
49
- )
50
- for chunk in stream:
51
- delta = chunk.choices[0].delta.content or ""
52
- payload = {
53
- "id": f"chatcmpl-{int(time.time())}",
54
  "object": "chat.completion.chunk",
55
  "created": int(time.time()),
56
- "model": MODEL_ID,
57
  "choices": [{"index": 0, "delta": {"content": delta}, "finish_reason": None}]
58
  }
59
- yield f"data: {json.dumps(payload)}\n\n"
60
  yield "data: [DONE]\n\n"
61
 
62
  return StreamingResponse(stream_generator(), media_type="text/event-stream")
63
  else:
64
- response = client.chat_completion(
65
- messages=formatted_messages,
66
- temperature=req.temperature,
67
- max_tokens=req.max_tokens,
68
- stream=False
69
- )
70
  return {
71
- "id": f"chatcmpl-{int(time.time())}",
72
  "object": "chat.completion",
73
  "created": int(time.time()),
74
- "model": MODEL_ID,
75
  "choices": [{
76
  "index": 0,
77
- "message": {"role": "assistant", "content": response.choices[0].message.content},
78
  "finish_reason": "stop"
79
  }]
80
  }
81
 
82
- # --- Interfaz Web Gradio ---
83
- def gradio_chat(message, history):
84
  messages = []
 
 
 
85
  for user_msg, bot_msg in history:
86
  messages.append({"role": "user", "content": user_msg})
87
  messages.append({"role": "assistant", "content": bot_msg})
 
88
  messages.append({"role": "user", "content": message})
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
- response = ""
91
- for chunk in client.chat_completion(messages=messages, stream=True, max_tokens=2048):
92
- response += chunk.choices[0].delta.content or ""
93
- yield response
 
 
 
 
 
 
 
 
94
 
95
- with gr.Blocks(title="AI Chat & API Hub") as demo:
96
- gr.Markdown(f"# 馃 Interfaz de Chat & API\n**Modelo Activo:** `{MODEL_ID}`\n\nCon茅ctate desde Cursor/OpenCode usando:\n`https:huggingface.co/spaces/acsaco.hf.space/v1`")
97
- gr.ChatInterface(fn=gradio_chat)
98
 
99
- # Montar Gradio dentro de FastAPI
100
- app = gr.mount_gradio_app(app, demo, path="/")
 
 
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)