Really-Amazing commited on
Commit
650b346
·
verified ·
1 Parent(s): 42844de

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +60 -53
app.py CHANGED
@@ -1,104 +1,111 @@
 
1
  import torch
2
  import gradio as gr
3
- from nanochat.engine import Engine
4
- from nanochat.tokenizer import get_tokenizer
5
  from nanochat.gpt import GPT, GPTConfig
 
6
 
 
7
  MODEL_PATH = "model_000971.pt"
 
 
 
 
8
 
9
- print("Waking up the toddler (NanoChat-ClimbMix-D12)...")
10
- tokenizer = get_tokenizer()
11
 
12
- # SET SPECIAL TOKENS (Aligned with Saint Iberis working space)
13
- # We use .get() or try/except to handle different tokenizer versions
14
- try:
15
- bos_id = tokenizer.encode("<|bos|>")[0]
16
- user_start_id = tokenizer.encode("<|user_start|>")[0]
17
- user_end_id = tokenizer.encode("<|user_end|>")[0]
18
- assistant_start_id = tokenizer.encode("<|assistant_start|>")[0]
19
- assistant_end_id = tokenizer.encode("<|assistant_end|>")[0]
20
- except:
21
- # Fallback to standard tags if the specific Saint Iberis ones aren't in your vocab
22
- bos_id = tokenizer.encode("<|endoftext|>")[0]
23
- user_start_id = tokenizer.encode("<|user|>")[0]
24
- user_end_id = tokenizer.encode("<|end|>")[0]
25
- assistant_start_id = tokenizer.encode("<|assistant|>")[0]
26
- assistant_end_id = tokenizer.encode("<|end|>")[0]
27
 
28
- print("Creating GPT model skeleton (6-head, 2048 seq)...")
 
 
 
 
 
 
 
29
  config = GPTConfig(
30
  vocab_size=32768,
31
  n_layer=12,
32
- n_head=6,
33
- n_kv_head=6,
34
  n_embd=768,
35
- sequence_len=2048,
36
  )
37
 
38
  model = GPT(config)
39
 
 
40
  print("Loading weights...")
41
  state_dict = torch.load(MODEL_PATH, map_location="cpu", weights_only=False)
42
  state_dict = {k.replace("_orig_mod.", ""): v for k, v in state_dict.items()}
43
-
44
  model.load_state_dict(state_dict, strict=False)
45
  model.to("cpu")
46
  model.eval()
47
- print("Model ready!")
48
 
49
- # We use the model directly to avoid 'Engine' type-hinting issues
50
  def chat_fn(message, history):
51
  try:
52
- # 1. Build token list
53
- tokens = [bos_id]
54
  for user_msg, assistant_msg in history:
55
- tokens.extend([user_start_id] + list(tokenizer.encode(user_msg)) + [user_end_id])
56
  if assistant_msg:
57
- tokens.extend([assistant_start_id] + list(tokenizer.encode(assistant_msg)) + [assistant_end_id])
58
 
59
- # Current turn
60
- tokens.extend([user_start_id] + list(tokenizer.encode(message)) + [user_end_id])
61
- tokens.append(assistant_start_id)
62
 
63
- # 2. THE FIX: Convert to Tensor before generating
64
- input_ids = torch.tensor([tokens], dtype=torch.long).to("cpu")
65
 
66
- # 3. Generate
67
- # If your model.generate is a generator (streaming), we'll take the result
 
68
  with torch.no_grad():
69
  output_ids = model.generate(
70
  input_ids,
71
  max_tokens=512,
72
  temperature=0.8,
73
- top_k=50,
74
  )
75
 
76
- # 4. Decode (handling both streaming and blocking outputs)
77
- # If output_ids is a generator, we collect it; if it's a tensor, we decode it.
78
  if isinstance(output_ids, torch.Tensor):
79
- # Take only the newly generated tokens
80
  new_tokens = output_ids[0][input_ids.shape[1]:]
81
- response = tokenizer.decode(new_tokens.tolist()).strip()
82
  else:
83
- # It's a generator (streaming)
84
- full_response = ""
85
  for token in output_ids:
86
- full_response += tokenizer.decode([token])
87
- response = full_response.strip()
 
 
 
88
 
89
- # Clean up stop tags
90
  for tag in ["<|assistant_end|>", "<|end|>", "<|user_start|>"]:
91
- if tag in response:
92
- response = response.split(tag)[0].strip()
93
-
94
- return response or "Toddler is thinking... 😅"
95
 
96
  except Exception as e:
 
 
97
  return f"Toddler tantrum: {str(e)}"
98
 
99
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
 
100
  gr.Markdown("# 🧸 NanoChat-ClimbMix-D12")
101
- gr.ChatInterface(fn=chat_fn, title="Toddler Chat")
 
 
 
 
 
102
 
103
  if __name__ == "__main__":
104
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
+ import os
2
  import torch
3
  import gradio as gr
 
 
4
  from nanochat.gpt import GPT, GPTConfig
5
+ from nanochat.tokenizer import RustBPETokenizer
6
 
7
+ # Configuration
8
  MODEL_PATH = "model_000971.pt"
9
+ # The Dockerfile moves files to this specific cache location
10
+ CACHE_DIR = os.path.expanduser("~/.cache/nanochat/tokenizer/")
11
+ # Fallback to current directory if cache doesn't exist (local testing)
12
+ TOKENIZER_DIR = CACHE_DIR if os.path.exists(CACHE_DIR) else "."
13
 
14
+ print(f"--- Waking up the Toddler ---")
15
+ print(f"Loading tokenizer from: {TOKENIZER_DIR}")
16
 
17
+ # 1. Load Tokenizer & Map Special Tokens
18
+ tokenizer = RustBPETokenizer.from_directory(TOKENIZER_DIR)
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
+ # These must match your training vocab
21
+ tokenizer.bos_token_id = tokenizer.enc.encode_single_token("<|bos|>")
22
+ tokenizer.user_start_id = tokenizer.enc.encode_single_token("<|user_start|>")
23
+ tokenizer.user_end_id = tokenizer.enc.encode_single_token("<|user_end|>")
24
+ tokenizer.assistant_start_id = tokenizer.enc.encode_single_token("<|assistant_start|>")
25
+ tokenizer.assistant_end_id = tokenizer.enc.encode_single_token("<|assistant_end|>")
26
+
27
+ # 2. Build Model Architecture
28
  config = GPTConfig(
29
  vocab_size=32768,
30
  n_layer=12,
31
+ n_head=6,
32
+ n_kv_head=6,
33
  n_embd=768,
34
+ sequence_len=2048,
35
  )
36
 
37
  model = GPT(config)
38
 
39
+ # 3. Load Weights (with _orig_mod. cleaning)
40
  print("Loading weights...")
41
  state_dict = torch.load(MODEL_PATH, map_location="cpu", weights_only=False)
42
  state_dict = {k.replace("_orig_mod.", ""): v for k, v in state_dict.items()}
 
43
  model.load_state_dict(state_dict, strict=False)
44
  model.to("cpu")
45
  model.eval()
46
+ print("Toddler is awake and ready!")
47
 
 
48
  def chat_fn(message, history):
49
  try:
50
+ # Build Chat History
51
+ tokens = [tokenizer.bos_token_id]
52
  for user_msg, assistant_msg in history:
53
+ tokens.extend([tokenizer.user_start_id] + tokenizer.encode(user_msg) + [tokenizer.user_end_id])
54
  if assistant_msg:
55
+ tokens.extend([tokenizer.assistant_start_id] + tokenizer.encode(assistant_msg) + [tokenizer.assistant_end_id])
56
 
57
+ # Add current user prompt
58
+ tokens.extend([tokenizer.user_start_id] + tokenizer.encode(message) + [tokenizer.user_end_id])
59
+ tokens.append(tokenizer.assistant_start_id)
60
 
61
+ input_ids = torch.tensor([tokens], dtype=torch.long)
 
62
 
63
+ # 4. Generate with Streaming Logic
64
+ # Note: In nanochat.gpt, generate is typically an autoregressive loop.
65
+ # If your version returns a generator, we iterate. If a tensor, we slice.
66
  with torch.no_grad():
67
  output_ids = model.generate(
68
  input_ids,
69
  max_tokens=512,
70
  temperature=0.8,
71
+ top_k=40
72
  )
73
 
74
+ # Handle Tensor vs Generator output
 
75
  if isinstance(output_ids, torch.Tensor):
76
+ # Just take the new parts
77
  new_tokens = output_ids[0][input_ids.shape[1]:]
78
+ response = tokenizer.decode(new_tokens.tolist())
79
  else:
80
+ # It's a generator yielding token by token
81
+ response = ""
82
  for token in output_ids:
83
+ decoded = tokenizer.decode([token])
84
+ if "<|assistant_end|>" in decoded:
85
+ break
86
+ response += decoded
87
+ yield response # Yield for streaming UI effect
88
 
89
+ # Final cleanup for non-streaming return
90
  for tag in ["<|assistant_end|>", "<|end|>", "<|user_start|>"]:
91
+ response = response.split(tag)[0]
92
+
93
+ return response.strip()
 
94
 
95
  except Exception as e:
96
+ # Crucial for QA: see the actual error in Space logs
97
+ print(f"ERROR: {e}")
98
  return f"Toddler tantrum: {str(e)}"
99
 
100
+ # 5. Launch UI
101
+ with gr.Blocks(theme=gr.themes.Default(primary_hue="orange")) as demo:
102
  gr.Markdown("# 🧸 NanoChat-ClimbMix-D12")
103
+ gr.Markdown("A custom-trained small language model running on your CPU.")
104
+ gr.ChatInterface(
105
+ fn=chat_fn,
106
+ type="messages", # Updated for latest Gradio versions
107
+ examples=["Hi Toddler!", "How does UPI work?", "Tell me a story."]
108
+ )
109
 
110
  if __name__ == "__main__":
111
  demo.launch(server_name="0.0.0.0", server_port=7860)