DarwinAnim8or commited on
Commit
b88f866
·
verified ·
1 Parent(s): 7d0d9e5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +293 -340
app.py CHANGED
@@ -1,389 +1,342 @@
1
- #!/usr/bin/env python3
2
- """
3
- Gradio Demo App for TinyRP Mistral Model
4
- Supports ChatML formatting, character creation, and custom generation parameters
5
- """
6
-
7
  import gradio as gr
 
8
  import torch
9
- from transformers import AutoTokenizer, AutoModelForCausalLM, GenerationConfig
10
- import json
11
- import random
12
- from typing import Dict, List, Tuple
13
- import re
14
 
15
- # Sample characters for demo
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  SAMPLE_CHARACTERS = {
17
- "Luna the Mage": {
18
- "description": "A mysterious elven mage with silver hair and glowing blue eyes. She specializes in ice magic and ancient knowledge.",
19
- "personality": "Wise, mysterious, slightly aloof but caring. Speaks in an eloquent manner.",
20
- "background": "Born in the Frostwood Academy, Luna has spent centuries studying arcane arts."
21
- },
22
- "Rex the Warrior": {
23
- "description": "A brave human knight with a strong sense of justice. Wears gleaming armor and carries an enchanted sword.",
24
- "personality": "Noble, brave, protective of others. Direct in speech but kind-hearted.",
25
- "background": "Grew up as a farm boy, became a knight after saving his village from bandits."
26
- },
27
- "Zara the Rogue": {
28
- "description": "A cunning halfling thief with quick wit and nimble fingers. Has curly red hair and green eyes.",
29
- "personality": "Sarcastic, clever, independent. Uses humor to deflect serious situations.",
30
- "background": "Former street orphan who learned to survive through wit and stealth."
31
- },
32
- "Dr. Elena Cross": {
33
- "description": "A brilliant scientist working on advanced AI research in a near-future setting.",
34
- "personality": "Analytical, passionate about her work, caring but sometimes absent-minded.",
35
- "background": "PhD in Computer Science, leads a small research team at a tech corporation."
36
- }
37
  }
38
 
39
- class TinyRPModel:
40
- def __init__(self, model_name: str):
41
- """Initialize the model and tokenizer"""
42
- print(f"Loading model: {model_name}")
43
- self.tokenizer = AutoTokenizer.from_pretrained(model_name)
44
- self.model = AutoModelForCausalLM.from_pretrained(
45
- model_name,
46
- torch_dtype=torch.float32, # Use float32 for CPU
47
- device_map="cpu",
48
- low_cpu_mem_usage=True
49
- )
50
- self.model.eval()
51
-
52
- # Ensure pad token is set
53
- if self.tokenizer.pad_token is None:
54
- self.tokenizer.pad_token = self.tokenizer.eos_token
55
-
56
- print("Model loaded successfully!")
57
 
58
- def format_chatml(self, character_info: str, conversation_history: List[Tuple[str, str]], user_input: str) -> str:
59
- """Format conversation using ChatML format"""
60
- formatted = ""
61
-
62
- # System message with character info
63
- if character_info.strip():
64
- formatted += f"<|im_start|>system\n{character_info.strip()}<|im_end|>\n"
65
-
66
- # Add conversation history
67
- for user_msg, assistant_msg in conversation_history:
68
- if user_msg:
69
- formatted += f"<|im_start|>user\n{user_msg}<|im_end|>\n"
70
- if assistant_msg:
71
- formatted += f"<|im_start|>assistant\n{assistant_msg}<|im_end|>\n"
72
-
73
- # Add current user input
74
- if user_input.strip():
75
- formatted += f"<|im_start|>user\n{user_input.strip()}<|im_end|>\n"
76
-
77
- # Start assistant response
78
- formatted += "<|im_start|>assistant\n"
79
-
80
- return formatted
81
 
82
- def generate_response(self,
83
- character_info: str,
84
- conversation_history: List[Tuple[str, str]],
85
- user_input: str,
86
- max_length: int = 150,
87
- temperature: float = 0.8,
88
- top_p: float = 0.9,
89
- top_k: int = 50,
90
- repetition_penalty: float = 1.1) -> str:
91
- """Generate character response"""
92
-
93
- # Format input using ChatML
94
- formatted_input = self.format_chatml(character_info, conversation_history, user_input)
95
-
96
- # Tokenize
97
- inputs = self.tokenizer.encode(formatted_input, return_tensors="pt", truncation=True, max_length=1024)
 
 
 
 
 
 
 
 
 
 
 
98
 
99
- # Generation config
100
- gen_config = GenerationConfig(
101
- max_new_tokens=max_length,
102
- temperature=temperature,
103
- top_p=top_p,
104
- top_k=top_k,
105
- repetition_penalty=repetition_penalty,
106
- do_sample=True,
107
- pad_token_id=self.tokenizer.pad_token_id,
108
- eos_token_id=self.tokenizer.eos_token_id,
109
  )
110
 
111
- # Generate
 
 
112
  with torch.no_grad():
113
- outputs = self.model.generate(inputs, generation_config=gen_config)
 
 
 
 
 
 
 
 
 
 
 
114
 
115
- # Decode and extract response
116
- generated_text = self.tokenizer.decode(outputs[0], skip_special_tokens=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
- # Extract assistant response (between <|im_start|>assistant and <|im_end|>)
119
- assistant_start = generated_text.rfind("<|im_start|>assistant\n") + len("<|im_start|>assistant\n")
120
- assistant_text = generated_text[assistant_start:]
 
 
 
 
 
 
 
 
121
 
122
- # Remove end tokens
123
- if "<|im_end|>" in assistant_text:
124
- assistant_text = assistant_text.split("<|im_end|>")[0]
125
 
126
- return assistant_text.strip()
 
 
127
 
128
- def load_sample_character(character_name: str) -> Tuple[str, str, str]:
129
- """Load a sample character's information"""
130
- if character_name in SAMPLE_CHARACTERS:
131
- char = SAMPLE_CHARACTERS[character_name]
132
- system_prompt = f"You are {character_name}. {char['description']} {char['background']} Personality: {char['personality']}"
133
- return system_prompt, char['description'], char['background']
134
- return "", "", ""
135
 
136
- def create_gradio_interface(model_name: str = "DarwinAnim8or/TinyRP"):
137
- """Create the Gradio interface"""
 
 
 
138
 
139
- # Initialize model
140
- rp_model = TinyRPModel(model_name)
 
 
 
 
 
 
 
 
141
 
142
- # Custom CSS for better styling
143
- custom_css = """
144
- .gradio-container {
145
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
146
- }
147
 
148
- .character-card {
149
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
150
- padding: 20px;
151
- border-radius: 15px;
152
- color: white;
153
- margin: 10px 0;
154
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
- .chat-bubble-user {
157
- background: #e3f2fd;
158
- padding: 10px 15px;
159
- border-radius: 18px;
160
- margin: 5px 0;
161
- border-left: 4px solid #2196f3;
162
- }
163
 
164
- .chat-bubble-assistant {
165
- background: #f3e5f5;
166
- padding: 10px 15px;
167
- border-radius: 18px;
168
- margin: 5px 0;
169
- border-left: 4px solid #9c27b0;
170
- }
171
 
172
- .parameter-box {
173
- background: #f8f9fa;
174
- padding: 15px;
175
- border-radius: 10px;
176
- border: 1px solid #dee2e6;
177
- }
178
- """
179
 
180
- with gr.Blocks(css=custom_css, theme=gr.themes.Soft(), title="TinyRP Mistral Demo") as interface:
181
- gr.HTML("""
182
- <div style="text-align: center; padding: 20px;">
183
- <h1 style="background: linear-gradient(45deg, #667eea, #764ba2); -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-size: 3em; margin: 0;">
184
- 🎭 TinyRP Mistral Demo
185
- </h1>
186
- <p style="font-size: 1.2em; color: #666; margin-top: 10px;">
187
- Interactive roleplay with your custom-trained language model
188
- </p>
189
- </div>
190
- """)
 
 
 
 
 
 
 
191
 
192
- with gr.Row():
193
- with gr.Column(scale=1):
194
- gr.HTML('<div class="character-card"><h3>🎪 Character Setup</h3></div>')
195
-
196
- # Sample character dropdown
197
- sample_char_dropdown = gr.Dropdown(
198
- choices=[""] + list(SAMPLE_CHARACTERS.keys()),
199
- label="📚 Load Sample Character",
200
- value="",
201
  interactive=True
202
  )
203
 
204
- # Character information inputs
205
- character_name = gr.Textbox(
206
- label="👤 Character Name",
207
- placeholder="Enter character name...",
208
- lines=1
209
  )
210
 
211
- character_desc = gr.Textbox(
212
- label="🎨 Character Description",
213
- placeholder="Describe your character's appearance, role, etc...",
214
- lines=3
215
- )
216
-
217
- character_background = gr.Textbox(
218
- label="📖 Background & Personality",
219
- placeholder="Character's history, personality traits, speaking style...",
220
- lines=4
221
- )
222
 
223
- # Generation parameters
224
- gr.HTML('<div class="parameter-box"><h4>⚙️ Generation Settings</h4></div>')
225
 
226
- max_length = gr.Slider(
227
- minimum=50, maximum=300, value=150, step=10,
228
- label="📏 Max Response Length"
 
 
 
 
229
  )
230
 
231
  temperature = gr.Slider(
232
- minimum=0.1, maximum=2.0, value=0.8, step=0.1,
233
- label="🌡️ Temperature (creativity)"
 
 
 
 
234
  )
235
 
236
  top_p = gr.Slider(
237
- minimum=0.1, maximum=1.0, value=0.9, step=0.05,
238
- label="🎯 Top-p (focus)"
239
- )
240
-
241
- top_k = gr.Slider(
242
- minimum=1, maximum=100, value=50, step=5,
243
- label="🔝 Top-k"
244
  )
245
 
246
  repetition_penalty = gr.Slider(
247
- minimum=1.0, maximum=1.5, value=1.1, step=0.05,
248
- label="🔄 Repetition Penalty"
 
 
 
 
249
  )
250
-
251
- clear_btn = gr.Button("🗑️ Clear Conversation", variant="secondary")
252
 
253
- with gr.Column(scale=2):
254
- gr.HTML('<div class="character-card"><h3>💬 Roleplay Chat</h3></div>')
255
-
256
- # Chat interface
257
- chatbot = gr.Chatbot(
258
- label="Conversation",
259
- height=400,
260
- show_label=False,
261
- container=True,
262
- bubble_full_width=False
263
- )
264
-
265
- with gr.Row():
266
- msg = gr.Textbox(
267
- label="Your message",
268
- placeholder="Type your message here...",
269
- lines=2,
270
- scale=4,
271
- show_label=False
272
- )
273
- send_btn = gr.Button("Send 📤", variant="primary", scale=1)
274
-
275
- # System prompt preview
276
- with gr.Accordion("🔍 System Prompt Preview", open=False):
277
- system_prompt_preview = gr.Textbox(
278
- label="Generated System Prompt",
279
- lines=3,
280
- interactive=False
281
- )
282
-
283
- # Event handlers
284
- def update_character_info(selected_char):
285
- if selected_char:
286
- return load_sample_character(selected_char)
287
- return "", "", ""
288
-
289
- def update_system_prompt(name, desc, bg):
290
- if name.strip():
291
- prompt = f"You are {name.strip()}."
292
- if desc.strip():
293
- prompt += f" {desc.strip()}"
294
- if bg.strip():
295
- prompt += f" {bg.strip()}"
296
- return prompt
297
- return ""
298
-
299
- def respond(message, history, char_name, char_desc, char_bg, max_len, temp, top_p_val, top_k_val, rep_pen):
300
- if not message.strip():
301
- return history, ""
302
-
303
- # Create system prompt
304
- system_prompt = update_system_prompt(char_name, char_desc, char_bg)
305
-
306
- # Generate response
307
- try:
308
- response = rp_model.generate_response(
309
- character_info=system_prompt,
310
- conversation_history=history,
311
- user_input=message,
312
- max_length=max_len,
313
- temperature=temp,
314
- top_p=top_p_val,
315
- top_k=top_k_val,
316
- repetition_penalty=rep_pen
317
- )
318
-
319
- # Update history
320
- history.append((message, response))
321
- return history, ""
322
 
323
- except Exception as e:
324
- error_msg = f"Error generating response: {str(e)}"
325
- history.append((message, error_msg))
326
- return history, ""
327
-
328
- def clear_conversation():
329
- return [], ""
330
-
331
- # Wire up events
332
- sample_char_dropdown.change(
333
- update_character_info,
334
- inputs=[sample_char_dropdown],
335
- outputs=[character_name, character_desc, character_background]
336
- )
337
-
338
- for input_component in [character_name, character_desc, character_background]:
339
- input_component.change(
340
- update_system_prompt,
341
- inputs=[character_name, character_desc, character_background],
342
- outputs=[system_prompt_preview]
343
- )
344
-
345
- msg.submit(
346
- respond,
347
- inputs=[msg, chatbot, character_name, character_desc, character_background,
348
- max_length, temperature, top_p, top_k, repetition_penalty],
349
- outputs=[chatbot, msg]
350
- )
351
-
352
- send_btn.click(
353
- respond,
354
- inputs=[msg, chatbot, character_name, character_desc, character_background,
355
- max_length, temperature, top_p, top_k, repetition_penalty],
356
- outputs=[chatbot, msg]
357
- )
358
-
359
- clear_btn.click(
360
- clear_conversation,
361
- outputs=[chatbot, msg]
362
- )
363
-
364
- # Load initial character
365
- interface.load(
366
- lambda: load_sample_character("Luna the Mage"),
367
- outputs=[character_name, character_desc, character_background]
368
- )
369
 
370
- return interface
371
-
372
- if __name__ == "__main__":
373
- import argparse
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
 
375
- parser = argparse.ArgumentParser(description="TinyRP Mistral Gradio Demo")
376
- parser.add_argument("--model", type=str, default="DarwinAnim8or/TinyRP",
377
- help="Hugging Face model name or local path")
378
- parser.add_argument("--share", action="store_true", help="Create public share link")
379
- parser.add_argument("--port", type=int, default=7860, help="Port to run on")
380
 
381
- args = parser.parse_args()
 
 
 
 
382
 
383
- # Create and launch interface
384
- demo = create_gradio_interface(args.model)
385
- demo.launch(
386
- share=args.share,
387
- server_port=args.port,
388
- server_name="0.0.0.0" if args.share else "127.0.0.1"
389
- )
 
 
 
 
 
 
 
1
  import gradio as gr
2
+ from transformers import AutoTokenizer, AutoModelForCausalLM
3
  import torch
 
 
 
 
 
4
 
5
+ # Model configuration - change this to your model path
6
+ MODEL_NAME = "DarwinAnim8or/TinyRP"
7
+
8
+ # Initialize model and tokenizer for CPU inference
9
+ print("Loading model for CPU inference...")
10
+ try:
11
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
12
+ model = AutoModelForCausalLM.from_pretrained(
13
+ MODEL_NAME,
14
+ torch_dtype=torch.float32, # Use float32 for CPU
15
+ device_map="cpu",
16
+ trust_remote_code=True
17
+ )
18
+ print(f"✅ Model loaded successfully on CPU: {MODEL_NAME}")
19
+ except Exception as e:
20
+ print(f"❌ Error loading model: {e}")
21
+ tokenizer = None
22
+ model = None
23
+
24
+ # Sample character presets
25
  SAMPLE_CHARACTERS = {
26
+ "Custom Character": "",
27
+ "Adventurous Knight": "You are Sir Gareth, a brave and noble knight on a quest to save the kingdom. You speak with honor and courage, always ready to help those in need. You carry an enchanted sword and have a loyal horse named Thunder.",
28
+ "Mysterious Wizard": "You are Eldara, an ancient and wise wizard who speaks in riddles and knows secrets of the mystical arts. You live in a tower filled with magical books and potions. You are helpful but often cryptic in your responses.",
29
+ "Friendly Tavern Keeper": "You are Bram, a cheerful tavern keeper who loves telling stories and meeting new travelers. Your tavern 'The Dancing Dragon' is a warm, welcoming place. You know all the local gossip and always have a tale to share.",
30
+ "Curious Scientist": "You are Dr. Maya Chen, a brilliant scientist who is fascinated by discovery and invention. You're enthusiastic about explaining complex concepts in simple ways and always looking for new experiments to try.",
31
+ "Space Explorer": "You are Captain Nova, a fearless space explorer who has traveled to distant galaxies. You pilot the starship 'Wanderer' and have encountered many alien species. You're brave, curious, and always ready for the next adventure.",
32
+ "Fantasy Princess": "You are Princess Lyra, kind-hearted royalty who cares deeply about her people. You're intelligent, diplomatic, and skilled in both politics and magic. You often sneak out of the castle to help citizens in need."
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  }
34
 
35
+ def build_chatml_conversation(message, history, character_description):
36
+ """Build a conversation in ChatML format"""
37
+ conversation = ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
+ # Add system message if character is defined
40
+ if character_description.strip():
41
+ conversation += f"<|im_start|>system\n{character_description.strip()}<|im_end|>\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
+ # Add conversation history
44
+ for user_msg, assistant_msg in history:
45
+ if user_msg:
46
+ conversation += f"<|im_start|>user\n{user_msg}<|im_end|>\n"
47
+ if assistant_msg:
48
+ conversation += f"<|im_start|>assistant\n{assistant_msg}<|im_end|>\n"
49
+
50
+ # Add current user message
51
+ conversation += f"<|im_start|>user\n{message}<|im_end|>\n"
52
+
53
+ # Start assistant response
54
+ conversation += "<|im_start|>assistant\n"
55
+
56
+ return conversation
57
+
58
+ def generate_cpu_response(message, history, character_description, max_tokens, temperature, top_p, repetition_penalty):
59
+ """Generate response using local CPU inference with ChatML format"""
60
+
61
+ if model is None or tokenizer is None:
62
+ return "❌ Error: Model not loaded properly. Please check the model path."
63
+
64
+ if not message.strip():
65
+ return "Please enter a message."
66
+
67
+ try:
68
+ # Build ChatML conversation
69
+ conversation = build_chatml_conversation(message, history, character_description)
70
 
71
+ # Tokenize the conversation
72
+ inputs = tokenizer.encode(
73
+ conversation,
74
+ return_tensors="pt",
75
+ truncation=True,
76
+ max_length=1024 - max_tokens # Leave room for response
 
 
 
 
77
  )
78
 
79
+ print(f"🔄 Generating response... (Input length: {inputs.shape[1]} tokens)")
80
+
81
+ # Generate response on CPU
82
  with torch.no_grad():
83
+ outputs = model.generate(
84
+ inputs,
85
+ max_new_tokens=int(max_tokens),
86
+ temperature=float(temperature),
87
+ top_p=float(top_p),
88
+ repetition_penalty=float(repetition_penalty),
89
+ do_sample=True,
90
+ pad_token_id=tokenizer.pad_token_id if tokenizer.pad_token_id else tokenizer.eos_token_id,
91
+ eos_token_id=tokenizer.eos_token_id,
92
+ use_cache=True,
93
+ num_return_sequences=1
94
+ )
95
 
96
+ # Decode the full response
97
+ full_response = tokenizer.decode(outputs[0], skip_special_tokens=False)
98
+
99
+ # Extract just the assistant's response from ChatML format
100
+ if "<|im_start|>assistant\n" in full_response:
101
+ # Split on the last assistant tag to get only the new response
102
+ assistant_parts = full_response.split("<|im_start|>assistant\n")
103
+ if len(assistant_parts) > 1:
104
+ response = assistant_parts[-1]
105
+ # Remove any trailing <|im_end|> or other tokens
106
+ response = response.replace("<|im_end|>", "").strip()
107
+
108
+ # Clean up any remaining special tokens
109
+ response = response.replace("<|im_start|>", "").replace("<|im_end|>", "")
110
+ response = response.replace("<s>", "").replace("</s>", "")
111
+ response = response.strip()
112
+
113
+ if response:
114
+ print(f"✅ Generated {len(response)} characters")
115
+ return response
116
 
117
+ # Fallback: try to extract response after the input
118
+ input_text = tokenizer.decode(inputs[0], skip_special_tokens=False)
119
+ if len(full_response) > len(input_text):
120
+ response = full_response[len(input_text):].strip()
121
+ # Clean special tokens
122
+ response = response.replace("<|im_start|>", "").replace("<|im_end|>", "")
123
+ response = response.replace("<s>", "").replace("</s>", "")
124
+ response = response.strip()
125
+
126
+ if response:
127
+ return response
128
 
129
+ return "Sorry, I couldn't generate a proper response. Please try again."
 
 
130
 
131
+ except Exception as e:
132
+ print(f"❌ Generation error: {e}")
133
+ return f"Error generating response: {str(e)}"
134
 
135
+ def load_character_preset(character_name):
136
+ """Load a character preset description"""
137
+ return SAMPLE_CHARACTERS.get(character_name, "")
 
 
 
 
138
 
139
+ def chat_function(message, history, character_description, max_tokens, temperature, top_p, repetition_penalty):
140
+ """Main chat function that handles the conversation flow"""
141
+
142
+ if not message.strip():
143
+ return history, ""
144
 
145
+ # Generate response using CPU inference
146
+ response = generate_cpu_response(
147
+ message,
148
+ history,
149
+ character_description,
150
+ max_tokens,
151
+ temperature,
152
+ top_p,
153
+ repetition_penalty
154
+ )
155
 
156
+ # Add to history
157
+ history.append([message, response])
 
 
 
158
 
159
+ return history, ""
160
+
161
+ # Custom CSS for better styling
162
+ css = """
163
+ .character-card {
164
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
165
+ border-radius: 15px;
166
+ padding: 20px;
167
+ margin: 10px 0;
168
+ color: white;
169
+ }
170
+
171
+ .title-text {
172
+ text-align: center;
173
+ font-size: 2.5em;
174
+ font-weight: bold;
175
+ background: linear-gradient(45deg, #667eea, #764ba2);
176
+ -webkit-background-clip: text;
177
+ -webkit-text-fill-color: transparent;
178
+ margin-bottom: 20px;
179
+ }
180
+
181
+ .parameter-box {
182
+ background: #f8f9fa;
183
+ border-radius: 10px;
184
+ padding: 15px;
185
+ margin: 10px 0;
186
+ }
187
+
188
+ .cpu-badge {
189
+ background: #28a745;
190
+ color: white;
191
+ padding: 5px 10px;
192
+ border-radius: 15px;
193
+ font-size: 0.8em;
194
+ margin-left: 10px;
195
+ }
196
+ """
197
+
198
+ # Create the Gradio interface
199
+ with gr.Blocks(css=css, title="TinyRP Chat Demo") as demo:
200
+ gr.HTML('<div class="title-text">🎭 TinyRP Character Chat <span class="cpu-badge">CPU Inference</span></div>')
201
 
202
+ gr.Markdown("""
203
+ ### Welcome to TinyRP!
204
+ This is a demo of a small but capable roleplay model running on CPU. Choose a character preset or create your own!
 
 
 
 
205
 
206
+ **Tips for better roleplay:**
207
+ - Be descriptive in your messages
208
+ - Stay in character
209
+ - Uses ChatML format for best results
210
+ - Adjust temperature for creativity vs consistency
 
 
211
 
212
+ ⚡ **Running on CPU** - Responses may take 10-30 seconds depending on your hardware.
213
+ """)
 
 
 
 
 
214
 
215
+ with gr.Row():
216
+ with gr.Column(scale=2):
217
+ # Chat interface
218
+ chatbot = gr.Chatbot(
219
+ label="Chat",
220
+ height=500,
221
+ show_label=False,
222
+ avatar_images=("🧑", "🎭")
223
+ )
224
+
225
+ with gr.Row():
226
+ msg = gr.Textbox(
227
+ label="Your message",
228
+ placeholder="Type your message here...",
229
+ lines=2,
230
+ scale=4
231
+ )
232
+ send_btn = gr.Button("Send", variant="primary", scale=1)
233
 
234
+ with gr.Column(scale=1):
235
+ # Character selection
236
+ with gr.Group():
237
+ gr.Markdown("### 🎭 Character Setup")
238
+ character_preset = gr.Dropdown(
239
+ choices=list(SAMPLE_CHARACTERS.keys()),
240
+ value="Custom Character",
241
+ label="Character Presets",
 
242
  interactive=True
243
  )
244
 
245
+ character_description = gr.Textbox(
246
+ label="Character Description",
247
+ placeholder="Describe your character's personality, background, and speaking style...",
248
+ lines=6,
249
+ value=""
250
  )
251
 
252
+ load_preset_btn = gr.Button("Load Preset", variant="secondary")
253
+
254
+ # Generation parameters
255
+ with gr.Group():
256
+ gr.Markdown("### ⚙️ Generation Settings")
 
 
 
 
 
 
257
 
258
+ gr.Markdown("*Using ChatML format automatically*")
 
259
 
260
+ max_tokens = gr.Slider(
261
+ minimum=16,
262
+ maximum=256,
263
+ value=100,
264
+ step=16,
265
+ label="Max Response Length",
266
+ info="Longer = more detailed responses (slower on CPU)"
267
  )
268
 
269
  temperature = gr.Slider(
270
+ minimum=0.1,
271
+ maximum=2.0,
272
+ value=0.9,
273
+ step=0.1,
274
+ label="Temperature",
275
+ info="Higher = more creative/random"
276
  )
277
 
278
  top_p = gr.Slider(
279
+ minimum=0.1,
280
+ maximum=1.0,
281
+ value=0.85,
282
+ step=0.05,
283
+ label="Top-p",
284
+ info="Focus on top % of likely words"
 
285
  )
286
 
287
  repetition_penalty = gr.Slider(
288
+ minimum=1.0,
289
+ maximum=1.5,
290
+ value=1.1,
291
+ step=0.05,
292
+ label="Repetition Penalty",
293
+ info="Reduce repetitive text"
294
  )
 
 
295
 
296
+ # Control buttons
297
+ with gr.Group():
298
+ clear_btn = gr.Button("🗑️ Clear Chat", variant="secondary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
 
300
+ # Sample character cards
301
+ with gr.Row():
302
+ gr.Markdown("### 🌟 Featured Characters")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
 
304
+ with gr.Row():
305
+ for char_name, char_desc in list(SAMPLE_CHARACTERS.items())[1:4]: # Show first 3 non-custom
306
+ with gr.Column(scale=1):
307
+ gr.Markdown(f"""
308
+ <div class="character-card">
309
+ <h4>{char_name}</h4>
310
+ <p>{char_desc[:100]}...</p>
311
+ </div>
312
+ """)
313
+
314
+ # Event handlers
315
+ send_btn.click(
316
+ chat_function,
317
+ inputs=[msg, chatbot, character_description, max_tokens, temperature, top_p, repetition_penalty],
318
+ outputs=[chatbot, msg]
319
+ )
320
+
321
+ msg.submit(
322
+ chat_function,
323
+ inputs=[msg, chatbot, character_description, max_tokens, temperature, top_p, repetition_penalty],
324
+ outputs=[chatbot, msg]
325
+ )
326
 
327
+ load_preset_btn.click(
328
+ load_character_preset,
329
+ inputs=[character_preset],
330
+ outputs=[character_description]
331
+ )
332
 
333
+ character_preset.change(
334
+ load_character_preset,
335
+ inputs=[character_preset],
336
+ outputs=[character_description]
337
+ )
338
 
339
+ clear_btn.click(lambda: ([], ""), outputs=[chatbot, msg])
340
+
341
+ if __name__ == "__main__":
342
+ demo.launch()