MarkChenX commited on
Commit
ccab657
·
verified ·
1 Parent(s): 11d6138

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +55 -227
app.py CHANGED
@@ -1,9 +1,9 @@
1
  from __future__ import annotations
2
 
3
  import os
 
4
  import sys
5
  from pathlib import Path
6
- from typing import Any
7
 
8
  import gradio as gr
9
  import torch
@@ -13,7 +13,9 @@ from huggingface_hub import snapshot_download
13
  MODEL_ID = "MarkChenX/lfm2-quantum-128m-sft-v2"
14
  HF_TOKEN = os.getenv("HF_TOKEN")
15
 
16
- DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
 
 
17
 
18
 
19
  print(f"Downloading custom checkpoint: {MODEL_ID}")
@@ -22,259 +24,85 @@ model_dir = Path(
22
  snapshot_download(
23
  repo_id=MODEL_ID,
24
  token=HF_TOKEN,
 
 
 
 
 
 
25
  )
26
  )
27
 
28
- # The repository contains the custom nanochat package.
29
- sys.path.insert(0, str(model_dir))
30
-
31
  print(f"Model repository: {model_dir}")
32
 
 
33
 
34
  try:
35
  from nanochat.checkpoint_manager import build_model
36
  except ImportError as error:
37
  raise RuntimeError(
38
- "Could not import nanochat.checkpoint_manager from the model "
39
- f"repository at {model_dir}. Ensure the repository contains "
40
- "nanochat/__init__.py and nanochat/checkpoint_manager.py."
41
  ) from error
42
 
43
 
44
- def find_checkpoint_file() -> Path:
45
- candidates = [
46
- model_dir / "model.pt",
47
- model_dir / "model.safetensors",
48
- model_dir / "checkpoint.pt",
49
- model_dir / "checkpoint_849.pt",
50
- ]
51
-
52
- for path in candidates:
53
- if path.exists():
54
- return path
55
 
56
- discovered = list(model_dir.glob("*.pt"))
57
- discovered += list(model_dir.glob("*.pth"))
58
- discovered += list(model_dir.glob("*.safetensors"))
59
-
60
- if not discovered:
61
- raise FileNotFoundError(
62
- f"No checkpoint file found in {model_dir}"
63
  )
64
 
65
- return discovered[0]
66
-
67
-
68
- checkpoint_path = find_checkpoint_file()
69
-
70
- print(f"Checkpoint file: {checkpoint_path}")
71
- print("Building custom nanochat model...")
72
-
73
-
74
- # Adjust these arguments only if your actual build_model signature differs.
75
- model, tokenizer = build_model(
76
- checkpoint_path=str(checkpoint_path),
77
- device=DEVICE,
78
- )
79
-
80
- model.eval()
81
-
82
- print(f"Model loaded on {DEVICE}")
83
-
84
-
85
- def normalize_history(
86
- history: list[Any] | None,
87
- ) -> list[dict[str, str]]:
88
- messages: list[dict[str, str]] = []
89
-
90
- for item in history or []:
91
- if isinstance(item, dict):
92
- role = item.get("role")
93
- content = item.get("content")
94
-
95
- if (
96
- role in {"user", "assistant"}
97
- and isinstance(content, str)
98
- and content.strip()
99
- ):
100
- messages.append(
101
- {
102
- "role": role,
103
- "content": content,
104
- }
105
- )
106
-
107
- elif isinstance(item, (list, tuple)) and len(item) == 2:
108
- user_text, assistant_text = item
109
-
110
- if isinstance(user_text, str) and user_text.strip():
111
- messages.append(
112
- {
113
- "role": "user",
114
- "content": user_text,
115
- }
116
- )
117
-
118
- if (
119
- isinstance(assistant_text, str)
120
- and assistant_text.strip()
121
- ):
122
- messages.append(
123
- {
124
- "role": "assistant",
125
- "content": assistant_text,
126
- }
127
- )
128
-
129
- return messages
130
-
131
-
132
- def build_prompt(
133
- message: str,
134
- history: list[Any],
135
- system_message: str,
136
- ) -> str:
137
- messages: list[dict[str, str]] = []
138
 
139
- if system_message.strip():
140
- messages.append(
141
- {
142
- "role": "system",
143
- "content": system_message.strip(),
144
- }
145
  )
146
 
147
- messages.extend(normalize_history(history))
148
-
149
- messages.append(
150
- {
151
- "role": "user",
152
- "content": message.strip(),
153
- }
154
  )
155
 
156
- parts: list[str] = []
157
-
158
- for item in messages:
159
- role = item["role"]
160
- content = item["content"]
161
- parts.append(f"<|im_start|>{role}\n{content}<|im_end|>\n")
162
-
163
- parts.append("<|im_start|>assistant\n")
164
-
165
- return "".join(parts)
166
-
167
-
168
- def encode_prompt(prompt: str) -> torch.Tensor:
169
- encoded = tokenizer.encode(prompt)
170
-
171
- if isinstance(encoded, torch.Tensor):
172
- input_ids = encoded
173
- else:
174
- input_ids = torch.tensor(encoded, dtype=torch.long)
175
 
176
- if input_ids.ndim == 1:
177
- input_ids = input_ids.unsqueeze(0)
178
 
179
- return input_ids.to(DEVICE)
180
 
 
181
 
182
- def decode_tokens(token_ids: torch.Tensor) -> str:
183
- ids = token_ids.detach().cpu().tolist()
184
-
185
- if ids and isinstance(ids[0], list):
186
- ids = ids[0]
187
-
188
- return tokenizer.decode(ids)
189
-
190
-
191
- def respond(
192
- message: str,
193
- history: list[Any],
194
- system_message: str,
195
- max_tokens: int,
196
- temperature: float,
197
- top_p: float,
198
- ) -> str:
199
- if not message or not message.strip():
200
- return "Please enter a message."
201
-
202
- try:
203
- prompt = build_prompt(
204
- message=message,
205
- history=history,
206
- system_message=system_message,
207
- )
208
-
209
- input_ids = encode_prompt(prompt)
210
- input_length = input_ids.shape[-1]
211
-
212
- generation_kwargs = {
213
- "input_ids": input_ids,
214
- "max_new_tokens": int(max_tokens),
215
- "temperature": float(temperature),
216
- "top_p": float(top_p),
217
- }
218
-
219
- with torch.inference_mode():
220
- output = model.generate(**generation_kwargs)
221
-
222
- generated_ids = output[:, input_length:]
223
- response = decode_tokens(generated_ids).strip()
224
-
225
- return response or "The model generated an empty response."
226
-
227
- except Exception as error:
228
- return (
229
- "Generation failed: "
230
- f"{type(error).__name__}: {error}"
231
- )
232
 
233
 
234
- with gr.Blocks(title="LFM2 Quantum Chat") as demo:
235
- gr.Markdown(
236
- """
237
- # LFM2 Quantum 128M
238
 
239
- Custom Nanochat LFM2-style hybrid quantum model.
240
- """
241
- )
242
 
243
- gr.ChatInterface(
244
- fn=respond,
245
- additional_inputs=[
246
- gr.Textbox(
247
- value="You are a helpful and friendly assistant.",
248
- label="System message",
249
- lines=3,
250
- ),
251
- gr.Slider(
252
- minimum=1,
253
- maximum=1024,
254
- value=256,
255
- step=1,
256
- label="Max new tokens",
257
- ),
258
- gr.Slider(
259
- minimum=0.0,
260
- maximum=2.0,
261
- value=0.3,
262
- step=0.1,
263
- label="Temperature",
264
- ),
265
- gr.Slider(
266
- minimum=0.1,
267
- maximum=1.0,
268
- value=0.95,
269
- step=0.05,
270
- label="Top-p",
271
- ),
272
- ],
273
- )
274
 
 
275
 
276
- if __name__ == "__main__":
277
- demo.queue().launch(
278
- share=False,
279
- ssr_mode=False,
280
- )
 
1
  from __future__ import annotations
2
 
3
  import os
4
+ import re
5
  import sys
6
  from pathlib import Path
 
7
 
8
  import gradio as gr
9
  import torch
 
13
  MODEL_ID = "MarkChenX/lfm2-quantum-128m-sft-v2"
14
  HF_TOKEN = os.getenv("HF_TOKEN")
15
 
16
+ DEVICE = torch.device(
17
+ "cuda" if torch.cuda.is_available() else "cpu"
18
+ )
19
 
20
 
21
  print(f"Downloading custom checkpoint: {MODEL_ID}")
 
24
  snapshot_download(
25
  repo_id=MODEL_ID,
26
  token=HF_TOKEN,
27
+ # Do not download optimizer files into the Space.
28
+ ignore_patterns=[
29
+ "optim_*.pt",
30
+ "optimizer*.pt",
31
+ "*.bin",
32
+ ],
33
  )
34
  )
35
 
 
 
 
36
  print(f"Model repository: {model_dir}")
37
 
38
+ sys.path.insert(0, str(model_dir))
39
 
40
  try:
41
  from nanochat.checkpoint_manager import build_model
42
  except ImportError as error:
43
  raise RuntimeError(
44
+ "Could not import nanochat.checkpoint_manager. "
45
+ "Confirm that the model repository contains the nanochat directory."
 
46
  ) from error
47
 
48
 
49
+ def find_latest_checkpoint(
50
+ repository_dir: Path,
51
+ ) -> tuple[Path, int]:
52
+ checkpoint_candidates: list[tuple[int, Path]] = []
 
 
 
 
 
 
 
53
 
54
+ for checkpoint in repository_dir.glob("model_*.pt"):
55
+ match = re.fullmatch(
56
+ r"model_(\d+)\.pt",
57
+ checkpoint.name,
 
 
 
58
  )
59
 
60
+ if match:
61
+ step = int(match.group(1))
62
+ checkpoint_candidates.append((step, checkpoint))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
+ if not checkpoint_candidates:
65
+ raise FileNotFoundError(
66
+ f"No model_XXXXXX.pt checkpoint found in {repository_dir}"
 
 
 
67
  )
68
 
69
+ checkpoint_candidates.sort(
70
+ key=lambda item: item[0],
71
+ reverse=True,
 
 
 
 
72
  )
73
 
74
+ latest_step, latest_path = checkpoint_candidates[0]
75
+ return latest_path, latest_step
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
 
 
77
 
78
+ checkpoint_path, checkpoint_step = find_latest_checkpoint(model_dir)
79
 
80
+ expected_meta_path = model_dir / f"meta_{checkpoint_step:06d}.json"
81
 
82
+ if not expected_meta_path.exists():
83
+ raise FileNotFoundError(
84
+ "The checkpoint metadata file is missing. Expected: "
85
+ f"{expected_meta_path}"
86
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
88
 
89
+ print(f"Checkpoint file: {checkpoint_path}")
90
+ print(f"Checkpoint step: {checkpoint_step}")
91
+ print(f"Metadata file: {expected_meta_path}")
92
+ print("Building custom nanochat model...")
93
 
 
 
 
94
 
95
+ model, tokenizer, metadata = build_model(
96
+ str(model_dir),
97
+ checkpoint_step,
98
+ DEVICE,
99
+ "eval",
100
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
+ model.eval()
103
 
104
+ print(f"Model loaded successfully on {DEVICE}")
105
+ print(
106
+ "Model config:",
107
+ metadata.get("model_config", "not present"),
108
+ )