acsaco commited on
Commit
4f4f61b
verified
1 Parent(s): 35eeb25

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +152 -51
app.py CHANGED
@@ -1,16 +1,24 @@
1
  import os
 
 
 
2
  import torch
3
  import spaces
4
- import gradio as gr
 
 
 
 
5
  from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
6
  from threading import Thread
 
7
 
8
  MODEL_ID = "Qwen/Qwen2.5-Coder-32B-Instruct-AWQ"
9
 
10
- # Cargar tokenizer
11
  tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
12
 
13
- # Cargar modelo cuantizado en AWQ (ocupa ~18-20GB VRAM)
14
  model = AutoModelForCausalLM.from_pretrained(
15
  MODEL_ID,
16
  torch_dtype=torch.float16,
@@ -18,21 +26,27 @@ model = AutoModelForCausalLM.from_pretrained(
18
  trust_remote_code=True
19
  )
20
 
21
- @spaces.GPU(duration=120)
22
- def generate(message, history, system_prompt, temperature, top_p, max_tokens):
23
- messages = []
24
- if system_prompt:
25
- messages.append({"role": "system", "content": system_prompt})
26
 
27
- for user_msg, bot_msg in history:
28
- messages.append({"role": "user", "content": user_msg})
29
- if bot_msg:
30
- messages.append({"role": "assistant", "content": bot_msg})
31
 
32
- messages.append({"role": "user", "content": message})
 
 
 
 
 
 
33
 
 
 
 
34
  text = tokenizer.apply_chat_template(
35
- messages,
36
  tokenize=False,
37
  add_generation_prompt=True
38
  )
@@ -48,58 +62,145 @@ def generate(message, history, system_prompt, temperature, top_p, max_tokens):
48
  generate_kwargs = dict(
49
  model_inputs,
50
  streamer=streamer,
51
- max_new_tokens=int(max_tokens),
52
  do_sample=temperature > 0.0,
53
  temperature=max(temperature, 1e-2) if temperature > 0.0 else None,
54
  top_p=top_p if temperature > 0.0 else None,
55
  repetition_penalty=1.05
56
  )
57
 
58
- # Iniciar generaci贸n en un hilo secundario para streaming fluido
59
  thread = Thread(target=model.generate, kwargs=generate_kwargs)
60
  thread.start()
61
 
62
- partial_text = ""
63
  for new_token in streamer:
64
- partial_text += new_token
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  yield partial_text
66
 
67
- # Interfaz de Gradio
68
- with gr.Blocks(title="Qwen2.5-Coder-32B-Instruct AWQ (ZeroGPU)", theme=gr.themes.Soft()) as demo:
69
  gr.Markdown("# 馃殌 Qwen2.5-Coder-32B-Instruct (AWQ en ZeroGPU)")
70
- gr.Markdown("Servicio de inferencia optimizado para c贸digo y compatible con clientes v铆a API / Gradio.")
71
-
72
- chat_interface = gr.ChatInterface(
73
- fn=generate,
74
  additional_inputs=[
75
- gr.Textbox(
76
- value="Eres un asistente de programaci贸n experto, preciso y conciso.",
77
- label="System Prompt",
78
- lines=2
79
- ),
80
- gr.Slider(
81
- minimum=0.0,
82
- maximum=1.0,
83
- value=0.2,
84
- step=0.05,
85
- label="Temperature"
86
- ),
87
- gr.Slider(
88
- minimum=0.1,
89
- maximum=1.0,
90
- value=0.9,
91
- step=0.05,
92
- label="Top-P"
93
- ),
94
- gr.Slider(
95
- minimum=256,
96
- maximum=4096,
97
- value=2048,
98
- step=256,
99
- label="Max New Tokens"
100
- )
101
  ]
102
  )
103
 
 
 
 
104
  if __name__ == "__main__":
105
- demo.queue().launch()
 
1
  import os
2
+ import time
3
+ import json
4
+ import uuid
5
  import torch
6
  import spaces
7
+ import uvicorn
8
+ from fastapi import FastAPI
9
+ from fastapi.responses import StreamingResponse
10
+ from pydantic import BaseModel
11
+ from typing import List, Optional, Dict
12
  from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
13
  from threading import Thread
14
+ import gradio as gr
15
 
16
  MODEL_ID = "Qwen/Qwen2.5-Coder-32B-Instruct-AWQ"
17
 
18
+ # Cargar Tokenizer
19
  tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
20
 
21
+ # Cargar Modelo en AWQ (18-20 GB VRAM)
22
  model = AutoModelForCausalLM.from_pretrained(
23
  MODEL_ID,
24
  torch_dtype=torch.float16,
 
26
  trust_remote_code=True
27
  )
28
 
29
+ # Inicializar FastAPI
30
+ app = FastAPI(title="Qwen2.5-Coder-32B OpenAI Compatible API")
 
 
 
31
 
32
+ # --- Esquemas Pydantic ---
33
+ class ChatMessage(BaseModel):
34
+ role: str
35
+ content: str
36
 
37
+ class ChatCompletionRequest(BaseModel):
38
+ model: Optional[str] = "Qwen/Qwen2.5-Coder-32B-Instruct-AWQ"
39
+ messages: List[ChatMessage]
40
+ temperature: Optional[float] = 0.2
41
+ top_p: Optional[float] = 0.9
42
+ max_tokens: Optional[int] = 2048
43
+ stream: Optional[bool] = False
44
 
45
+ # Funci贸n central de generaci贸n protegida por ZeroGPU
46
+ @spaces.GPU(duration=120)
47
+ def generate_stream_tokens(messages_dict: List[Dict[str, str]], temperature: float, top_p: float, max_tokens: int):
48
  text = tokenizer.apply_chat_template(
49
+ messages_dict,
50
  tokenize=False,
51
  add_generation_prompt=True
52
  )
 
62
  generate_kwargs = dict(
63
  model_inputs,
64
  streamer=streamer,
65
+ max_new_tokens=max_tokens,
66
  do_sample=temperature > 0.0,
67
  temperature=max(temperature, 1e-2) if temperature > 0.0 else None,
68
  top_p=top_p if temperature > 0.0 else None,
69
  repetition_penalty=1.05
70
  )
71
 
 
72
  thread = Thread(target=model.generate, kwargs=generate_kwargs)
73
  thread.start()
74
 
 
75
  for new_token in streamer:
76
+ yield new_token
77
+
78
+ # --- Endpoints OpenAI (/v1) ---
79
+ @app.get("/v1/models")
80
+ async def list_models():
81
+ return {
82
+ "object": "list",
83
+ "data": [
84
+ {
85
+ "id": MODEL_ID,
86
+ "object": "model",
87
+ "created": int(time.time()),
88
+ "owned_by": "huggingface"
89
+ }
90
+ ]
91
+ }
92
+
93
+ @app.post("/v1/chat/completions")
94
+ async def chat_completions(req: ChatCompletionRequest):
95
+ req_id = f"chatcmpl-{uuid.uuid4().hex[:12]}"
96
+ created_time = int(time.time())
97
+ messages_dict = [{"role": m.role, "content": m.content} for m in req.messages]
98
+
99
+ # Manejo de streaming (SSE)
100
+ if req.stream:
101
+ async def event_generator():
102
+ for token in generate_stream_tokens(
103
+ messages_dict,
104
+ temperature=req.temperature or 0.2,
105
+ top_p=req.top_p or 0.9,
106
+ max_tokens=req.max_tokens or 2048
107
+ ):
108
+ chunk = {
109
+ "id": req_id,
110
+ "object": "chat.completion.chunk",
111
+ "created": created_time,
112
+ "model": req.model,
113
+ "choices": [
114
+ {
115
+ "index": 0,
116
+ "delta": {"content": token},
117
+ "finish_reason": None
118
+ }
119
+ ]
120
+ }
121
+ yield f"data: {json.dumps(chunk)}\n\n"
122
+
123
+ final_chunk = {
124
+ "id": req_id,
125
+ "object": "chat.completion.chunk",
126
+ "created": created_time,
127
+ "model": req.model,
128
+ "choices": [
129
+ {
130
+ "index": 0,
131
+ "delta": {},
132
+ "finish_reason": "stop"
133
+ }
134
+ ]
135
+ }
136
+ yield f"data: {json.dumps(final_chunk)}\n\n"
137
+ yield "data: [DONE]\n\n"
138
+
139
+ return StreamingResponse(event_generator(), media_type="text/event-stream")
140
+
141
+ # Respuesta est谩ndar (sin streaming)
142
+ full_content = ""
143
+ for token in generate_stream_tokens(
144
+ messages_dict,
145
+ temperature=req.temperature or 0.2,
146
+ top_p=req.top_p or 0.9,
147
+ max_tokens=req.max_tokens or 2048
148
+ ):
149
+ full_content += token
150
+
151
+ return {
152
+ "id": req_id,
153
+ "object": "chat.completion",
154
+ "created": created_time,
155
+ "model": req.model,
156
+ "choices": [
157
+ {
158
+ "index": 0,
159
+ "message": {
160
+ "role": "assistant",
161
+ "content": full_content
162
+ },
163
+ "finish_reason": "stop"
164
+ }
165
+ ],
166
+ "usage": {
167
+ "prompt_tokens": -1,
168
+ "completion_tokens": -1,
169
+ "total_tokens": -1
170
+ }
171
+ }
172
+
173
+ # --- Interfaz Gradio ---
174
+ def gradio_generate(message, history, system_prompt, temperature, top_p, max_tokens):
175
+ messages = []
176
+ if system_prompt:
177
+ messages.append({"role": "system", "content": system_prompt})
178
+ for user_msg, bot_msg in history:
179
+ messages.append({"role": "user", "content": user_msg})
180
+ if bot_msg:
181
+ messages.append({"role": "assistant", "content": bot_msg})
182
+ messages.append({"role": "user", "content": message})
183
+
184
+ partial_text = ""
185
+ for token in generate_stream_tokens(messages, temperature, top_p, int(max_tokens)):
186
+ partial_text += token
187
  yield partial_text
188
 
189
+ with gr.Blocks(title="Qwen2.5-Coder-32B API & UI", theme=gr.themes.Soft()) as gradio_app:
 
190
  gr.Markdown("# 馃殌 Qwen2.5-Coder-32B-Instruct (AWQ en ZeroGPU)")
191
+ gr.Markdown("Servicio con interfaz web y endpoints OpenAI (`/v1/chat/completions`).")
192
+ gr.ChatInterface(
193
+ fn=gradio_generate,
 
194
  additional_inputs=[
195
+ gr.Textbox("Eres un asistente de programaci贸n experto.", label="System Prompt"),
196
+ gr.Slider(0.0, 1.0, 0.2, step=0.05, label="Temperature"),
197
+ gr.Slider(0.1, 1.0, 0.9, step=0.05, label="Top-P"),
198
+ gr.Slider(256, 4096, 2048, step=256, label="Max New Tokens")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  ]
200
  )
201
 
202
+ # Montar Gradio en la ra铆z
203
+ app = gr.mount_gradio_app(app, gradio_app, path="/")
204
+
205
  if __name__ == "__main__":
206
+ uvicorn.run(app, host="0.0.0.0", port=7860)