vercelgpt commited on
Commit
b6ac232
Β·
verified Β·
1 Parent(s): b8837f2

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +166 -0
app.py ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Gradio ZeroGPU Space β€” Ollama-style LLM chat using llama-cpp-python.
3
+
4
+ GPU is acquired per request via @spaces.GPU, so the A100 is only held
5
+ while a token is being generated, not for the entire session lifetime.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ import threading
12
+ from typing import Iterator
13
+
14
+ import gradio as gr
15
+ import spaces
16
+ from huggingface_hub import hf_hub_download
17
+ from llama_cpp import Llama
18
+
19
+ # ---------------------------------------------------------------------------
20
+ # Model configuration β€” change these to switch models
21
+ # ---------------------------------------------------------------------------
22
+ MODEL_REPO = os.getenv("MODEL_REPO", "LiquidAI/LFM2.5-230M-GGUF")
23
+ MODEL_FILE = os.getenv("MODEL_FILE", "LFM2.5-230M-F16.gguf")
24
+ CONTEXT_SIZE = int(os.getenv("CONTEXT_SIZE", "4096"))
25
+
26
+ # ---------------------------------------------------------------------------
27
+ # Load model once at startup (CPU map; GPU layers allocated at inference time)
28
+ # ---------------------------------------------------------------------------
29
+ print(f"Downloading {MODEL_FILE} from {MODEL_REPO} …")
30
+ model_path = hf_hub_download(repo_id=MODEL_REPO, filename=MODEL_FILE)
31
+ print("Model downloaded. Initialising llama-cpp …")
32
+
33
+ llm = Llama(
34
+ model_path=model_path,
35
+ n_ctx=CONTEXT_SIZE,
36
+ n_gpu_layers=-1, # offload ALL layers to GPU (set 0 for CPU-only)
37
+ verbose=False,
38
+ )
39
+ print("Model ready.")
40
+
41
+ # ---------------------------------------------------------------------------
42
+ # Inference β€” wrapped with @spaces.GPU so A100 is acquired per call
43
+ # ---------------------------------------------------------------------------
44
+ @spaces.GPU(duration=120)
45
+ def _generate(
46
+ messages: list[dict],
47
+ temperature: float,
48
+ max_new_tokens: int,
49
+ top_p: float,
50
+ ) -> Iterator[str]:
51
+ """Yield partial assistant responses token by token."""
52
+ stream = llm.create_chat_completion(
53
+ messages=messages,
54
+ temperature=temperature,
55
+ max_tokens=max_new_tokens,
56
+ top_p=top_p,
57
+ stream=True,
58
+ )
59
+ for chunk in stream:
60
+ delta = chunk["choices"][0]["delta"]
61
+ token = delta.get("content", "")
62
+ if token:
63
+ yield token
64
+
65
+
66
+ def build_messages(
67
+ history: list[dict],
68
+ system_prompt: str,
69
+ ) -> list[dict]:
70
+ """Convert Gradio history format to llama-cpp messages list."""
71
+ messages: list[dict] = []
72
+ if system_prompt.strip():
73
+ messages.append({"role": "system", "content": system_prompt.strip()})
74
+ for msg in history:
75
+ messages.append({"role": msg["role"], "content": msg["content"]})
76
+ return messages
77
+
78
+
79
+ def chat_fn(
80
+ message: str,
81
+ history: list[dict],
82
+ system_prompt: str,
83
+ temperature: float,
84
+ max_new_tokens: int,
85
+ top_p: float,
86
+ ) -> Iterator[str]:
87
+ """Gradio streaming chat handler."""
88
+ history = history or []
89
+ history.append({"role": "user", "content": message})
90
+ messages = build_messages(history, system_prompt)
91
+
92
+ partial = ""
93
+ for token in _generate(messages, temperature, max_new_tokens, top_p):
94
+ partial += token
95
+ yield partial
96
+
97
+
98
+ # ---------------------------------------------------------------------------
99
+ # UI
100
+ # ---------------------------------------------------------------------------
101
+ DEFAULT_SYSTEM = (
102
+ "You are a helpful, harmless, and honest AI assistant. "
103
+ "Answer concisely and clearly."
104
+ )
105
+
106
+ with gr.Blocks(title="ZeroGPU LLM Chat", theme=gr.themes.Soft()) as demo:
107
+ gr.Markdown(
108
+ f"""
109
+ # πŸ¦™ ZeroGPU LLM Chat
110
+ **Model:** `{MODEL_REPO} / {MODEL_FILE}`
111
+ GPU is allocated on demand (ZeroGPU) β€” first response may take a few seconds while the Space warms up.
112
+ """
113
+ )
114
+
115
+ with gr.Row():
116
+ with gr.Column(scale=3):
117
+ chatbot = gr.ChatInterface(
118
+ fn=chat_fn,
119
+ type="messages",
120
+ additional_inputs_accordion=gr.Accordion(
121
+ label="βš™οΈ Generation settings", open=False
122
+ ),
123
+ additional_inputs=[
124
+ gr.Textbox(
125
+ value=DEFAULT_SYSTEM,
126
+ label="System prompt",
127
+ lines=3,
128
+ placeholder="Enter a system prompt …",
129
+ ),
130
+ gr.Slider(
131
+ minimum=0.0,
132
+ maximum=2.0,
133
+ value=0.7,
134
+ step=0.05,
135
+ label="Temperature",
136
+ info="Higher = more creative, lower = more deterministic",
137
+ ),
138
+ gr.Slider(
139
+ minimum=64,
140
+ maximum=2048,
141
+ value=512,
142
+ step=64,
143
+ label="Max new tokens",
144
+ info="Maximum number of tokens to generate per reply",
145
+ ),
146
+ gr.Slider(
147
+ minimum=0.1,
148
+ maximum=1.0,
149
+ value=0.95,
150
+ step=0.05,
151
+ label="Top-p (nucleus sampling)",
152
+ ),
153
+ ],
154
+ examples=[
155
+ "Explain quantum entanglement in simple terms.",
156
+ "Write a Python function that checks if a string is a palindrome.",
157
+ "What are the pros and cons of renewable energy?",
158
+ "Translate 'Hello, how are you?' into French, German, and Japanese.",
159
+ ],
160
+ cache_examples=False,
161
+ )
162
+
163
+ demo.queue(max_size=10)
164
+
165
+ if __name__ == "__main__":
166
+ demo.launch()