fomext commited on
Commit
5ef66a8
·
verified ·
1 Parent(s): 2747d0c

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +201 -0
  2. requirements.txt +5 -0
app.py ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import time
3
+ import uuid
4
+ from typing import Optional
5
+
6
+ import gradio as gr
7
+ import spaces
8
+ import torch
9
+ from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
10
+
11
+ # ---------------------------------------------------------------------------
12
+ # Configuration
13
+ # ---------------------------------------------------------------------------
14
+
15
+ MODEL_ID = "Qwen/Qwen3-Coder-30B-A3B-Instruct-FP8"
16
+ MODEL_ALIAS = "qwen3-coder-30b-a3b-instruct-fp8"
17
+
18
+ print(f"Loading tokenizer for {MODEL_ID} …")
19
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
20
+
21
+ print(f"Loading model {MODEL_ID} …")
22
+ model = AutoModelForCausalLM.from_pretrained(
23
+ MODEL_ID,
24
+ torch_dtype="auto",
25
+ device_map="auto",
26
+ )
27
+ model.eval()
28
+ print("Model ready.")
29
+
30
+
31
+ # ---------------------------------------------------------------------------
32
+ # GPU generation functions — ZeroGPU anchors
33
+ # ---------------------------------------------------------------------------
34
+
35
+
36
+ @spaces.GPU
37
+ def gradio_chat(message: str, history: list) -> str:
38
+ hf_messages = [{"role": "user" if i % 2 == 0 else "assistant", "content": m}
39
+ for i, m in enumerate([msg for pair in history for msg in pair] + [message])]
40
+ prompt = tokenizer.apply_chat_template(
41
+ hf_messages, tokenize=False, add_generation_prompt=True
42
+ # NOTE: Qwen3-Coder is non-thinking only; enable_thinking is not supported.
43
+ )
44
+ inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
45
+ with torch.no_grad():
46
+ output_ids = model.generate(
47
+ **inputs,
48
+ max_new_tokens=512,
49
+ do_sample=True,
50
+ temperature=0.7,
51
+ top_p=0.9,
52
+ pad_token_id=tokenizer.eos_token_id,
53
+ )
54
+ new_ids = output_ids[0][inputs["input_ids"].shape[1]:]
55
+ return tokenizer.decode(new_ids, skip_special_tokens=True)
56
+
57
+
58
+ @spaces.GPU
59
+ def _generate_response(prompt: str, gen_kwargs: dict) -> str:
60
+ inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
61
+ with torch.no_grad():
62
+ output_ids = model.generate(**inputs, **gen_kwargs)
63
+ new_ids = output_ids[0][inputs["input_ids"].shape[1]:]
64
+ return tokenizer.decode(new_ids, skip_special_tokens=True)
65
+
66
+
67
+ # ---------------------------------------------------------------------------
68
+ # API functions
69
+ # ---------------------------------------------------------------------------
70
+
71
+
72
+ def list_models() -> str:
73
+ """Returns a JSON string listing available models."""
74
+ result = {
75
+ "object": "list",
76
+ "data": [{"id": MODEL_ALIAS, "object": "model", "created": int(time.time()), "owned_by": "qwen"}],
77
+ }
78
+ return json.dumps(result)
79
+
80
+
81
+ def chat_completions(
82
+ messages_json: str,
83
+ max_tokens: int = 512,
84
+ temperature: float = 0.7,
85
+ top_p: float = 0.9,
86
+ ) -> str:
87
+ """
88
+ Non-streaming chat completions. Returns an OpenAI-compatible JSON string.
89
+
90
+ messages_json: JSON array of {role, content} objects,
91
+ e.g. '[{"role":"user","content":"Hello"}]'
92
+
93
+ NOTE: Qwen3-Coder-30B-A3B-Instruct is non-thinking only.
94
+ The enable_thinking parameter has been removed accordingly.
95
+ """
96
+ try:
97
+ messages = json.loads(messages_json)
98
+ except json.JSONDecodeError as e:
99
+ return json.dumps({"error": f"Invalid messages_json: {e}"})
100
+
101
+ try:
102
+ hf_messages = [{"role": m["role"], "content": m["content"]} for m in messages]
103
+ prompt = tokenizer.apply_chat_template(
104
+ hf_messages,
105
+ tokenize=False,
106
+ add_generation_prompt=True,
107
+ )
108
+ except Exception as e:
109
+ return json.dumps({"error": f"Prompt build failed: {e}"})
110
+
111
+ gen_kwargs = dict(
112
+ max_new_tokens=max_tokens,
113
+ temperature=temperature,
114
+ top_p=top_p,
115
+ do_sample=True,
116
+ pad_token_id=tokenizer.eos_token_id,
117
+ )
118
+
119
+ try:
120
+ content = _generate_response(prompt, gen_kwargs)
121
+ except Exception as e:
122
+ return json.dumps({"error": f"Generation failed: {e}"})
123
+
124
+ cid = f"chatcmpl-{uuid.uuid4().hex}"
125
+ result = {
126
+ "id": cid,
127
+ "object": "chat.completion",
128
+ "created": int(time.time()),
129
+ "model": MODEL_ALIAS,
130
+ "choices": [{"index": 0, "message": {"role": "assistant", "content": content}, "finish_reason": "stop"}],
131
+ "usage": {"prompt_tokens": -1, "completion_tokens": -1, "total_tokens": -1},
132
+ }
133
+ return json.dumps(result)
134
+
135
+
136
+ def health() -> str:
137
+ """Returns a JSON health-check string."""
138
+ return json.dumps({"status": "ok", "model": MODEL_ID})
139
+
140
+
141
+ # ---------------------------------------------------------------------------
142
+ # Gradio UI + API
143
+ # ---------------------------------------------------------------------------
144
+
145
+ with gr.Blocks(title=f"{MODEL_ALIAS} API") as demo:
146
+ gr.Markdown(f"""
147
+ # {MODEL_ALIAS} — Gradio API
148
+
149
+ Endpoints (via Gradio built-in API):
150
+
151
+ | api_name | Description |
152
+ |----------|-------------|
153
+ | `list_models` | List available models → JSON string |
154
+ | `chat_completions` | Chat completions → JSON string |
155
+ | `health` | Health check → JSON string |
156
+
157
+ Call them at `/gradio_api/call/<api_name>` (POST with `{{"data": [...]}}`)
158
+ or use the Gradio Python client.
159
+
160
+ You can also chat directly below.
161
+ """)
162
+
163
+ gr.ChatInterface(fn=gradio_chat)
164
+
165
+ with gr.Row(visible=False):
166
+ # -- health ------------------------------------------------------
167
+ _health_btn = gr.Button("health")
168
+ _health_out = gr.Textbox()
169
+ _health_btn.click(fn=health, inputs=[], outputs=[_health_out], api_name="health")
170
+
171
+ # -- list_models -------------------------------------------------
172
+ _models_btn = gr.Button("list_models")
173
+ _models_out = gr.Textbox()
174
+ _models_btn.click(fn=list_models, inputs=[], outputs=[_models_out], api_name="list_models")
175
+
176
+ with gr.Row(visible=False):
177
+ # -- chat_completions --------------------------------------------
178
+ _cc_messages = gr.Textbox(label="messages_json")
179
+ _cc_max_tokens = gr.Number(label="max_tokens", value=512)
180
+ _cc_temp = gr.Number(label="temperature", value=0.7)
181
+ _cc_top_p = gr.Number(label="top_p", value=0.9)
182
+ _cc_out = gr.Textbox(label="result")
183
+ _cc_btn = gr.Button("chat_completions")
184
+ _cc_btn.click(
185
+ fn=chat_completions,
186
+ inputs=[_cc_messages, _cc_max_tokens, _cc_temp, _cc_top_p],
187
+ outputs=[_cc_out],
188
+ api_name="chat_completions",
189
+ )
190
+
191
+
192
+ # ---------------------------------------------------------------------------
193
+ # Entry-point
194
+ # ---------------------------------------------------------------------------
195
+
196
+ if __name__ == "__main__":
197
+ demo.queue()
198
+ demo.launch(
199
+ server_name="0.0.0.0",
200
+ server_port=7860,
201
+ )
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ huggingface_hub==0.30
2
+ transformers>=4.51.0
3
+ tokenizers>=0.21.0
4
+ accelerate>=0.34.0
5
+ fastapi>=0.110.0