serotoninboi commited on
Commit
d60f0ff
·
verified ·
1 Parent(s): 10ae8c5

Upload folder using huggingface_hub

Browse files
Files changed (4) hide show
  1. .gitignore +8 -0
  2. README.md +40 -7
  3. app.py +297 -0
  4. requirements.txt +7 -0
.gitignore ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.pyc
3
+ .DS_Store
4
+ *.safetensors
5
+ *.bin
6
+ *.pt
7
+ *.pth
8
+ .huggingface/
README.md CHANGED
@@ -1,13 +1,46 @@
1
  ---
2
- title: Codecraft
3
- emoji: 👀
4
- colorFrom: yellow
5
- colorTo: yellow
6
  sdk: gradio
7
- sdk_version: 6.20.0
8
- python_version: '3.13'
9
  app_file: app.py
10
  pinned: false
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: CodeCraft - Qwen2.5-Coder-7B
3
+ emoji: 💻
4
+ colorFrom: indigo
5
+ colorTo: purple
6
  sdk: gradio
7
+ sdk_version: 5.23.3
 
8
  app_file: app.py
9
  pinned: false
10
+ python_version: "3.12"
11
  ---
12
 
13
+ # CodeCraft - AI Coding Assistant
14
+
15
+ Powered by **Qwen2.5-Coder-7B-Instruct** running on Hugging Face ZeroGPU.
16
+
17
+ Chat with a state-of-the-art coding assistant. Supports code generation, debugging,
18
+ refactoring, explanation, and general programming help across all major languages.
19
+
20
+ ## Features
21
+
22
+ - 💬 **Chat interface** with syntax-highlighted code blocks
23
+ - ⚙️ **Adjustable parameters**: temperature, top-p, max tokens, system prompt
24
+ - 📡 **Built-in API endpoint** at `/api` for programmatic use
25
+ - 🎨 **Syntax-highlighted output** via Gradio Markdown + code blocks
26
+
27
+ ## API Usage
28
+
29
+ Every Gradio Space exposes a Rest API at `/api`. For this Space:
30
+
31
+ ```python
32
+ import requests
33
+
34
+ response = requests.post(
35
+ "https://<your-space>.hf.space/gradio_api/call/generate",
36
+ json={
37
+ "data": ["write a fibonacci function in rust", "You are a helpful coding assistant.", 0.3, 0.9, 2048]
38
+ }
39
+ )
40
+ print(response.json())
41
+ ```
42
+
43
+ ## Model
44
+
45
+ [Qwen/Qwen2.5-Coder-7B-Instruct](https://huggingface.co/Qwen/Qwen2.5-Coder-7B-Instruct) —
46
+ 7B parameter code-specific LLM with 128K context, instruction-tuned for chat and coding tasks.
app.py ADDED
@@ -0,0 +1,297 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import spaces
2
+ import gradio as gr
3
+ import torch
4
+ from transformers import (
5
+ AutoModelForCausalLM,
6
+ AutoTokenizer,
7
+ BitsAndBytesConfig,
8
+ TextIteratorStreamer,
9
+ )
10
+ from threading import Thread
11
+ from typing import Optional, Generator
12
+
13
+ # ---------------------------------------------------------------------------
14
+ # Module-scope model loading — ZeroGPU manages GPU offload transparently
15
+ # ---------------------------------------------------------------------------
16
+ MODEL_ID = "Qwen/Qwen2.5-Coder-7B-Instruct"
17
+
18
+ quant_config = BitsAndBytesConfig(
19
+ load_in_4bit=True,
20
+ bnb_4bit_quant_type="nf4",
21
+ bnb_4bit_use_double_quant=True,
22
+ bnb_4bit_compute_dtype=torch.bfloat16,
23
+ )
24
+
25
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
26
+ model = AutoModelForCausalLM.from_pretrained(
27
+ MODEL_ID,
28
+ quantization_config=quant_config,
29
+ device_map="auto",
30
+ torch_dtype=torch.bfloat16,
31
+ trust_remote_code=True,
32
+ )
33
+ model.eval()
34
+
35
+ DEFAULT_SYSTEM = "You are an expert coding assistant. Write clean, efficient, well-documented code."
36
+
37
+
38
+ # ---------------------------------------------------------------------------
39
+ # ZeroGPU-decorated generation
40
+ # ---------------------------------------------------------------------------
41
+ @spaces.GPU(duration=120)
42
+ def generate(
43
+ messages: list[dict],
44
+ temperature: float,
45
+ top_p: float,
46
+ max_new_tokens: int,
47
+ ) -> str:
48
+ """Run model inference inside a ZeroGPU worker process.
49
+
50
+ Args are pickled across the process boundary.
51
+ Returns CPU text — safe for unpickling in the main process.
52
+ """
53
+ inputs = tokenizer.apply_chat_template(
54
+ messages,
55
+ tokenize=True,
56
+ add_generation_prompt=True,
57
+ return_tensors="pt",
58
+ ).to(model.device)
59
+
60
+ with torch.inference_mode():
61
+ outputs = model.generate(
62
+ inputs,
63
+ max_new_tokens=max_new_tokens,
64
+ temperature=temperature,
65
+ top_p=top_p,
66
+ do_sample=temperature > 0.0,
67
+ pad_token_id=tokenizer.eos_token_id,
68
+ )
69
+
70
+ generated = outputs[0][inputs.shape[1]:]
71
+ return tokenizer.decode(generated, skip_special_tokens=True)
72
+
73
+
74
+ # ---------------------------------------------------------------------------
75
+ # Streaming variant — yields tokens as they're generated
76
+ # ---------------------------------------------------------------------------
77
+ @spaces.GPU(duration=120)
78
+ def generate_stream(
79
+ messages: list[dict],
80
+ temperature: float,
81
+ top_p: float,
82
+ max_new_tokens: int,
83
+ ) -> Generator[str, None, None]:
84
+ """Stream tokens from the model one-by-one."""
85
+ inputs = tokenizer.apply_chat_template(
86
+ messages,
87
+ tokenize=True,
88
+ add_generation_prompt=True,
89
+ return_tensors="pt",
90
+ ).to(model.device)
91
+
92
+ streamer = TextIteratorStreamer(
93
+ tokenizer,
94
+ skip_prompt=True,
95
+ skip_special_tokens=True,
96
+ )
97
+
98
+ generation_kwargs = dict(
99
+ inputs=inputs,
100
+ max_new_tokens=max_new_tokens,
101
+ temperature=temperature,
102
+ top_p=top_p,
103
+ do_sample=temperature > 0.0,
104
+ pad_token_id=tokenizer.eos_token_id,
105
+ streamer=streamer,
106
+ )
107
+
108
+ thread = Thread(target=model.generate, kwargs=generation_kwargs)
109
+ thread.start()
110
+
111
+ for token in streamer:
112
+ yield token
113
+
114
+
115
+ # ---------------------------------------------------------------------------
116
+ # Non-streaming wrapper (for API endpoint)
117
+ # ---------------------------------------------------------------------------
118
+ def predict(
119
+ message: str,
120
+ history: list,
121
+ system_prompt: str,
122
+ temperature: float,
123
+ top_p: float,
124
+ max_tokens: int,
125
+ ):
126
+ """Chat function — called both from UI and the auto-generated Gradio API."""
127
+ messages = [{"role": "system", "content": system_prompt}]
128
+ for user_msg, asst_msg in history:
129
+ messages.append({"role": "user", "content": user_msg})
130
+ if asst_msg:
131
+ messages.append({"role": "assistant", "content": asst_msg})
132
+ messages.append({"role": "user", "content": message})
133
+
134
+ output = generate(messages, temperature, top_p, max_tokens)
135
+ return output
136
+
137
+
138
+ # ---------------------------------------------------------------------------
139
+ # Streaming chat handler
140
+ # ---------------------------------------------------------------------------
141
+ def chat_fn(
142
+ message: str,
143
+ history: list,
144
+ system_prompt: str,
145
+ temperature: float,
146
+ top_p: float,
147
+ max_tokens: int,
148
+ ):
149
+ """Generator that yields partial (message, history) tuples for streaming UI."""
150
+ messages = [{"role": "system", "content": system_prompt}]
151
+ for user_msg, asst_msg in history:
152
+ messages.append({"role": "user", "content": user_msg})
153
+ if asst_msg:
154
+ messages.append({"role": "assistant", "content": asst_msg})
155
+ messages.append({"role": "user", "content": message})
156
+
157
+ partial = ""
158
+ for token in generate_stream(messages, temperature, top_p, max_tokens):
159
+ partial += token
160
+ yield partial
161
+
162
+
163
+ # ---------------------------------------------------------------------------
164
+ # Lang / theme helper
165
+ # ---------------------------------------------------------------------------
166
+ LANGUAGES = ["python", "javascript", "typescript", "rust", "go", "java", "cpp",
167
+ "csharp", "ruby", "php", "sql", "bash", "html", "css", "json", "yaml"]
168
+
169
+
170
+ def build_examples():
171
+ return [
172
+ ["Write a Python function that checks if a string is a palindrome."],
173
+ ["Create a Rust function that reads a CSV file and returns the row count."],
174
+ ["Explain the difference between an interface and a type in TypeScript."],
175
+ ["Write a Go HTTP server that serves static files on port 8080."],
176
+ ]
177
+
178
+
179
+ # ---------------------------------------------------------------------------
180
+ # Gradio UI
181
+ # ---------------------------------------------------------------------------
182
+ def create_ui():
183
+ with gr.Blocks(
184
+ title="CodeCraft - AI Coding Assistant",
185
+ theme=gr.themes.Soft(
186
+ primary_hue="indigo",
187
+ neutral_hue="slate",
188
+ ),
189
+ fill_width=True,
190
+ ) as demo:
191
+ gr.Markdown(
192
+ "# 💻 CodeCraft — AI Coding Assistant\n"
193
+ "Powered by **Qwen2.5-Coder-7B-Instruct** · ZeroGPU"
194
+ )
195
+
196
+ chatbot = gr.Chatbot(
197
+ label="Conversation",
198
+ placeholder="Ask me anything about code...",
199
+ render_markdown=True,
200
+ show_copy_button=True,
201
+ height=500,
202
+ )
203
+
204
+ with gr.Row():
205
+ msg = gr.Textbox(
206
+ label="Your message",
207
+ placeholder="Write a Python async function that downloads a URL...",
208
+ scale=8,
209
+ container=False,
210
+ )
211
+ submit_btn = gr.Button("Send", variant="primary", scale=1, min_width=80)
212
+ clear_btn = gr.Button("Clear", scale=1, min_width=80)
213
+
214
+ with gr.Accordion("⚙️ Settings", open=False):
215
+ with gr.Row():
216
+ system_prompt = gr.Textbox(
217
+ label="System Prompt",
218
+ value=DEFAULT_SYSTEM,
219
+ lines=2,
220
+ scale=3,
221
+ )
222
+ with gr.Column(scale=1):
223
+ temperature = gr.Slider(
224
+ label="Temperature", minimum=0.0, maximum=1.5,
225
+ value=0.3, step=0.05,
226
+ )
227
+ with gr.Row():
228
+ top_p = gr.Slider(
229
+ label="Top-P", minimum=0.6, maximum=1.0,
230
+ value=0.9, step=0.05,
231
+ )
232
+ max_tokens = gr.Slider(
233
+ label="Max Tokens", minimum=128, maximum=4096,
234
+ value=2048, step=128,
235
+ )
236
+
237
+ gr.Examples(
238
+ examples=build_examples(),
239
+ inputs=[msg],
240
+ label="Try these prompts",
241
+ )
242
+
243
+ # -- State: chat history --
244
+ history_state = gr.State([])
245
+
246
+ # -- Event wiring --
247
+ def respond(message, history, system, temp, top_p_val, max_tok):
248
+ if not message.strip():
249
+ return "", history, history
250
+ history = history + [(message, None)]
251
+ yield "", history, []
252
+ for partial in chat_fn(message, history[:-1], system, temp, top_p_val, max_tok):
253
+ history[-1] = (message, partial)
254
+ yield "", history, []
255
+ yield "", history, [message]
256
+
257
+ # Wire submit via message box
258
+ msg.submit(
259
+ respond,
260
+ inputs=[msg, history_state, system_prompt, temperature, top_p, max_tokens],
261
+ outputs=[msg, chatbot, history_state],
262
+ concurrency_limit=8,
263
+ )
264
+ submit_btn.click(
265
+ respond,
266
+ inputs=[msg, history_state, system_prompt, temperature, top_p, max_tokens],
267
+ outputs=[msg, chatbot, history_state],
268
+ concurrency_limit=8,
269
+ )
270
+
271
+ # Clear conversation
272
+ def clear_conversation():
273
+ return [], "", []
274
+
275
+ clear_btn.click(
276
+ clear_conversation,
277
+ outputs=[history_state, chatbot, msg],
278
+ concurrency_limit=8,
279
+ )
280
+
281
+ # -- API endpoint exposure (auto by Gradio, but re-binding as top-level fn) --
282
+ gr.Markdown(
283
+ """
284
+ ### 📡 API
285
+
286
+ This Space exposes a REST API at `/gradio_api/call/predict`.
287
+ See the [Gradio docs](https://www.gradio.app/guides/sharing-your-app#api) for usage.
288
+ """
289
+ )
290
+
291
+ return demo
292
+
293
+
294
+ if __name__ == "__main__":
295
+ demo = create_ui()
296
+ demo.queue(default_concurrency_limit=8)
297
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ torch>=2.8.0
2
+ transformers>=4.50.0
3
+ accelerate>=1.5.0
4
+ bitsandbytes>=0.45.0
5
+ gradio>=5.23.0
6
+ sentencepiece>=0.2.0
7
+ protobuf>=5.28.0