misukisu commited on
Commit
040e231
·
verified ·
1 Parent(s): 96ed33c

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +266 -0
  2. requirements.txt +5 -0
app.py ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ from functools import lru_cache
4
+
5
+ import gradio as gr
6
+ from huggingface_hub import hf_hub_download
7
+ from llama_cpp import Llama
8
+ from transformers import AutoTokenizer
9
+
10
+ MODEL_REPO = os.getenv("MODEL_REPO", "HuggingFaceTB/SmolLM2-1.7B-Instruct-GGUF")
11
+ MODEL_FILE = os.getenv("MODEL_FILE", "smollm2-1.7b-instruct-q4_k_m.gguf")
12
+ TOKENIZER_REPO = os.getenv("TOKENIZER_REPO", "HuggingFaceTB/SmolLM2-1.7B-Instruct")
13
+ MAX_TOTAL_TOKENS = int(os.getenv("MAX_TOTAL_TOKENS", "2048"))
14
+ DEFAULT_MAX_NEW_TOKENS = int(os.getenv("DEFAULT_MAX_NEW_TOKENS", "220"))
15
+ DEFAULT_TEMPERATURE = float(os.getenv("DEFAULT_TEMPERATURE", "0.55"))
16
+ DEFAULT_TOP_P = float(os.getenv("DEFAULT_TOP_P", "0.9"))
17
+ DEFAULT_TOP_K = int(os.getenv("DEFAULT_TOP_K", "40"))
18
+ DEFAULT_REPEAT_PENALTY = float(os.getenv("DEFAULT_REPEAT_PENALTY", "1.08"))
19
+ DEFAULT_SYSTEM = os.getenv(
20
+ "DEFAULT_SYSTEM",
21
+ "You are concise, sharp, and helpful. Follow the system instruction carefully, but do not become robotic or overly cautious. Answer directly.",
22
+ )
23
+
24
+ CSS = """
25
+ :root {
26
+ --bg-0: #07111f;
27
+ --bg-1: #0d1b2f;
28
+ --bg-2: #10233c;
29
+ --text: #ebf3ff;
30
+ --muted: #a9bdd9;
31
+ --accent: #7cc7ff;
32
+ --accent-2: #9b8cff;
33
+ --card: rgba(255,255,255,0.06);
34
+ --border: rgba(255,255,255,0.11);
35
+ --shadow: 0 18px 60px rgba(0,0,0,0.35);
36
+ }
37
+ body, .gradio-container {
38
+ background:
39
+ radial-gradient(70rem 40rem at 10% -10%, rgba(124,199,255,0.18), transparent 55%),
40
+ radial-gradient(50rem 35rem at 100% 0%, rgba(155,140,255,0.14), transparent 40%),
41
+ linear-gradient(180deg, var(--bg-0), var(--bg-1) 50%, #091320);
42
+ color: var(--text);
43
+ }
44
+ .top-wrap {
45
+ max-width: 1100px;
46
+ margin: 0 auto;
47
+ }
48
+ .hero {
49
+ border: 1px solid var(--border);
50
+ background: linear-gradient(180deg, rgba(255,255,255,0.08), rgba(255,255,255,0.045));
51
+ backdrop-filter: blur(12px);
52
+ border-radius: 26px;
53
+ padding: 28px 28px 22px 28px;
54
+ box-shadow: var(--shadow);
55
+ margin-bottom: 18px;
56
+ }
57
+ .hero h1 {
58
+ font-size: 34px;
59
+ line-height: 1.05;
60
+ margin: 0;
61
+ letter-spacing: -0.03em;
62
+ }
63
+ .hero p {
64
+ color: var(--muted);
65
+ margin: 12px 0 0 0;
66
+ font-size: 15px;
67
+ }
68
+ .card {
69
+ border: 1px solid var(--border) !important;
70
+ background: linear-gradient(180deg, rgba(255,255,255,0.07), rgba(255,255,255,0.045)) !important;
71
+ backdrop-filter: blur(12px);
72
+ border-radius: 24px !important;
73
+ box-shadow: var(--shadow);
74
+ }
75
+ .pill {
76
+ display: inline-flex;
77
+ gap: 8px;
78
+ align-items: center;
79
+ padding: 9px 12px;
80
+ border-radius: 999px;
81
+ border: 1px solid var(--border);
82
+ background: rgba(255,255,255,0.04);
83
+ color: var(--muted);
84
+ font-size: 12px;
85
+ margin-right: 8px;
86
+ }
87
+ #run-btn {
88
+ background: linear-gradient(135deg, var(--accent), var(--accent-2)) !important;
89
+ color: #08111d !important;
90
+ border: 0 !important;
91
+ font-weight: 700 !important;
92
+ min-height: 52px !important;
93
+ border-radius: 18px !important;
94
+ }
95
+ #stop-btn {
96
+ min-height: 52px !important;
97
+ border-radius: 18px !important;
98
+ }
99
+ .output-shell {
100
+ min-height: 420px;
101
+ }
102
+ .output-shell textarea, .output-shell .wrap {
103
+ font-size: 15px !important;
104
+ line-height: 1.6 !important;
105
+ }
106
+ .footer-note {
107
+ color: var(--muted);
108
+ font-size: 12px;
109
+ text-align: center;
110
+ margin-top: 8px;
111
+ }
112
+ """
113
+
114
+ @lru_cache(maxsize=1)
115
+ def get_tokenizer():
116
+ return AutoTokenizer.from_pretrained(TOKENIZER_REPO)
117
+
118
+ @lru_cache(maxsize=1)
119
+ def get_model():
120
+ model_path = hf_hub_download(repo_id=MODEL_REPO, filename=MODEL_FILE)
121
+ cpu_count = os.cpu_count() or 2
122
+ threads = max(1, min(cpu_count, 8))
123
+ return Llama(
124
+ model_path=model_path,
125
+ n_ctx=MAX_TOTAL_TOKENS,
126
+ n_threads=threads,
127
+ n_threads_batch=threads,
128
+ n_batch=256,
129
+ n_ubatch=256,
130
+ use_mmap=True,
131
+ use_mlock=False,
132
+ flash_attn=False,
133
+ logits_all=False,
134
+ verbose=False,
135
+ seed=42,
136
+ )
137
+
138
+
139
+ def build_prompt(system_prompt: str, user_prompt: str) -> str:
140
+ tokenizer = get_tokenizer()
141
+ messages = [
142
+ {"role": "system", "content": system_prompt.strip()},
143
+ {"role": "user", "content": user_prompt.strip()},
144
+ ]
145
+ return tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
146
+
147
+
148
+ def format_metrics(start_time: float, first_token_time: float | None, output_text: str) -> str:
149
+ total = max(time.time() - start_time, 1e-6)
150
+ first = None if first_token_time is None else first_token_time - start_time
151
+ chars = len(output_text)
152
+ cps = chars / total
153
+ rows = [
154
+ ["Total time", f"{total:.2f}s"],
155
+ ["Time to first token", "-" if first is None else f"{first:.2f}s"],
156
+ ["Characters", str(chars)],
157
+ ["Chars/sec", f"{cps:.1f}"],
158
+ ]
159
+ table = "<table style='width:100%; border-collapse:collapse;'>"
160
+ for k, v in rows:
161
+ table += f"<tr><td style='padding:8px 10px; color:#a9bdd9; border-bottom:1px solid rgba(255,255,255,0.08);'>{k}</td><td style='padding:8px 10px; text-align:right; border-bottom:1px solid rgba(255,255,255,0.08);'>{v}</td></tr>"
162
+ table += "</table>"
163
+ return table
164
+
165
+
166
+ def generate(system_prompt, user_prompt, max_new_tokens, temperature, top_p, top_k, repeat_penalty):
167
+ if not user_prompt or not user_prompt.strip():
168
+ raise gr.Error("Kirjoita ensin prompti.")
169
+
170
+ model = get_model()
171
+ prompt = build_prompt(system_prompt, user_prompt)
172
+ max_new_tokens = int(max(32, min(max_new_tokens, 512)))
173
+ start_time = time.time()
174
+ first_token_time = None
175
+ text = ""
176
+
177
+ stream = model(
178
+ prompt,
179
+ max_tokens=max_new_tokens,
180
+ temperature=temperature,
181
+ top_p=top_p,
182
+ top_k=top_k,
183
+ repeat_penalty=repeat_penalty,
184
+ stop=["<|im_end|>", "<|endoftext|>"],
185
+ stream=True,
186
+ )
187
+
188
+ yield "", "", gr.update(interactive=False), gr.update(interactive=True)
189
+
190
+ for chunk in stream:
191
+ token = chunk["choices"][0]["text"]
192
+ if token:
193
+ if first_token_time is None:
194
+ first_token_time = time.time()
195
+ text += token
196
+ metrics = format_metrics(start_time, first_token_time, text)
197
+ yield text.strip(), metrics, gr.update(interactive=False), gr.update(interactive=True)
198
+
199
+ metrics = format_metrics(start_time, first_token_time, text)
200
+ yield text.strip(), metrics, gr.update(interactive=True), gr.update(interactive=False)
201
+
202
+
203
+ def clear_all():
204
+ return "", "", ""
205
+
206
+
207
+ with gr.Blocks(css=CSS, theme=gr.themes.Base(), fill_width=True) as demo:
208
+ with gr.Column(elem_classes=["top-wrap"]):
209
+ gr.HTML(
210
+ """
211
+ <div class='hero'>
212
+ <div class='pill'>SmolLM2 1.7B Instruct</div>
213
+ <div class='pill'>Q4_K_M GGUF</div>
214
+ <div class='pill'>CPU Basic friendly</div>
215
+ <h1>Fast local-feeling text generation.</h1>
216
+ <p>Minimal UI, fast start, streamed output, and a setup tuned for Hugging Face Spaces CPU Basic.</p>
217
+ </div>
218
+ """
219
+ )
220
+
221
+ with gr.Row(equal_height=True):
222
+ with gr.Column(scale=11):
223
+ with gr.Group(elem_classes=["card"]):
224
+ system_box = gr.Textbox(
225
+ label="System instruction",
226
+ value=DEFAULT_SYSTEM,
227
+ lines=4,
228
+ max_lines=8,
229
+ container=True,
230
+ )
231
+ prompt_box = gr.Textbox(
232
+ label="Prompt",
233
+ placeholder="Write your request here...",
234
+ lines=10,
235
+ max_lines=16,
236
+ container=True,
237
+ )
238
+ with gr.Row():
239
+ run_btn = gr.Button("Generate", elem_id="run-btn")
240
+ stop_btn = gr.Button("Stop", elem_id="stop-btn")
241
+ clear_btn = gr.Button("Clear")
242
+ with gr.Accordion("Tuning", open=False):
243
+ max_new_tokens = gr.Slider(64, 512, value=DEFAULT_MAX_NEW_TOKENS, step=8, label="Max new tokens")
244
+ temperature = gr.Slider(0.0, 1.4, value=DEFAULT_TEMPERATURE, step=0.05, label="Temperature")
245
+ top_p = gr.Slider(0.1, 1.0, value=DEFAULT_TOP_P, step=0.05, label="Top-p")
246
+ top_k = gr.Slider(1, 100, value=DEFAULT_TOP_K, step=1, label="Top-k")
247
+ repeat_penalty = gr.Slider(1.0, 1.3, value=DEFAULT_REPEAT_PENALTY, step=0.01, label="Repeat penalty")
248
+
249
+ with gr.Column(scale=10):
250
+ with gr.Group(elem_classes=["card", "output-shell"]):
251
+ output_box = gr.Textbox(label="Output", lines=22, max_lines=22, show_copy_button=True)
252
+ metrics_box = gr.HTML()
253
+
254
+ gr.HTML("<div class='footer-note'>For best CPU latency, keep max new tokens moderate and system prompts short.</div>")
255
+
256
+ generation = run_btn.click(
257
+ fn=generate,
258
+ inputs=[system_box, prompt_box, max_new_tokens, temperature, top_p, top_k, repeat_penalty],
259
+ outputs=[output_box, metrics_box, run_btn, stop_btn],
260
+ show_progress="hidden",
261
+ )
262
+ stop_btn.click(fn=None, cancels=[generation])
263
+ clear_btn.click(fn=clear_all, outputs=[prompt_box, output_box, metrics_box], show_progress="hidden")
264
+
265
+ if __name__ == "__main__":
266
+ demo.queue(max_size=8, default_concurrency_limit=1).launch()
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ gradio>=5.23.3
2
+ huggingface_hub>=0.30.0
3
+ llama-cpp-python>=0.3.7
4
+ transformers>=4.51.0
5
+ sentencepiece>=0.2.0