taellinglin commited on
Commit
0be3d69
·
verified ·
1 Parent(s): 9ebe9ef

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +1 -7
  2. main.py +231 -0
  3. requirements.txt +4 -0
README.md CHANGED
@@ -1,12 +1,6 @@
1
  ---
2
  title: SannyChatMini
3
- emoji: 👁
4
- colorFrom: green
5
- colorTo: indigo
6
  sdk: gradio
7
  sdk_version: 5.27.0
8
- app_file: app.py
9
- pinned: false
10
  ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
  title: SannyChatMini
3
+ app_file: main.py
 
 
4
  sdk: gradio
5
  sdk_version: 5.27.0
 
 
6
  ---
 
 
main.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import gradio as gr
4
+ from datetime import datetime
5
+ from threading import Lock
6
+ from transformers import pipeline, AutoTokenizer, AutoModelForCausalLM
7
+ import torch
8
+ # ========== Auto-create folders ==========
9
+ os.makedirs("chat_history", exist_ok=True)
10
+ os.makedirs("system", exist_ok=True)
11
+
12
+ # ========== Load System Context ==========
13
+ context_path = "system/context.txt"
14
+ if not os.path.exists(context_path):
15
+ raise FileNotFoundError(f"Missing system context file at {context_path}!")
16
+
17
+ with open(context_path, "r", encoding="utf-8") as f:
18
+ loaded_context = f.read()
19
+
20
+ # ========== Simple Chatbot Logic ==========
21
+ lock = Lock()
22
+
23
+ # Provide the folder path, not the file path
24
+ model_folder = "model/Mistral-7B-Instruct-v0.3"
25
+
26
+ # Load the model and tokenizer
27
+ model = AutoModelForCausalLM.from_pretrained(model_folder, torch_dtype=torch.bfloat16)
28
+ tokenizer = AutoTokenizer.from_pretrained(model_folder)
29
+
30
+ # Set pad_token to eos_token if pad_token is not available
31
+ if tokenizer.pad_token is None:
32
+ tokenizer.pad_token = tokenizer.eos_token
33
+
34
+ # Initialize the pipeline for text generation
35
+ generator = pipeline("text-generation", model=model, tokenizer=tokenizer)
36
+
37
+
38
+ # ========== Helper Functions ==========
39
+
40
+ def sanitize_username(username):
41
+ return ''.join(c for c in username if c.isalnum() or c in ('_', '-')).strip()
42
+
43
+ def user_folder(username):
44
+ return os.path.join("chat_history", username)
45
+
46
+ def load_latest_history(username):
47
+ folder = user_folder(username)
48
+ if not os.path.exists(folder):
49
+ os.makedirs(folder, exist_ok=True)
50
+ return []
51
+ files = sorted(os.listdir(folder), reverse=True)
52
+ if not files:
53
+ return []
54
+ latest_file = os.path.join(folder, files[0])
55
+ with open(latest_file, "r", encoding="utf-8") as f:
56
+ lines = f.readlines()
57
+ history = []
58
+ for line in lines:
59
+ if ": " in line:
60
+ user, msg = line.split(": ", 1)
61
+ history.append((user.strip(), msg.strip()))
62
+ return history
63
+
64
+ def save_history(username, history):
65
+ folder = user_folder(username)
66
+ os.makedirs(folder, exist_ok=True)
67
+ filepath = os.path.join(folder, "history.txt")
68
+
69
+ with open(filepath, "a", encoding="utf-8") as f:
70
+ # Only write the last two new entries (user + Sanny Lin)
71
+ for user, msg in history[-2:]:
72
+ f.write(f"{user}: {msg}\n")
73
+
74
+ def format_chat(history):
75
+ formatted = ""
76
+ for user, msg in history:
77
+ if user == "Sanny Lin":
78
+ formatted += f"""
79
+ <div style='text-align: left; margin: 5px;'>
80
+ <span class='sanny-message' style='background-color: #e74c3c; color: white; padding: 10px 15px; border-radius: 20px; display: inline-block; max-width: 70%; word-wrap: break-word;'>
81
+ {msg}
82
+ </span>
83
+ </div>
84
+ """
85
+ else:
86
+ formatted += f"""
87
+ <div style='text-align: right; margin: 5px;'>
88
+ <span style='background-color: #3498db; color: white; padding: 10px 15px; border-radius: 20px; display: inline-block; max-width: 70%; word-wrap: break-word;'>
89
+ {msg}
90
+ </span>
91
+ </div>
92
+ """
93
+ return formatted
94
+
95
+
96
+ def generate_reply(username, user_message, history):
97
+ with lock:
98
+ if not user_message.strip():
99
+ return history
100
+
101
+ # Retrieve the last 30 messages, including history from the user
102
+ history = history[-30:] # Limit to the last 30 messages
103
+
104
+ messages = []
105
+
106
+ # Start with the system context
107
+ if not history:
108
+ messages.append({"role": "system", "content": loaded_context})
109
+
110
+ # Add the last 30 messages to the conversation history
111
+ for user, msg in history:
112
+ role = "user" if user == username else "assistant"
113
+ messages.append({"role": role, "content": msg})
114
+
115
+ # Add the user message at the end
116
+ messages.append({"role": "user", "content": user_message})
117
+
118
+ # Append the personalized prompt "You are chatting with {{ username }} now:" at the end of the context
119
+ user_prompt = f"You are chatting with {username} now. Reply to this message:"
120
+ messages.append({"role": "system", "content": user_prompt})
121
+
122
+ # Extract the content part of each message for encoding
123
+ text_messages = [message["content"] for message in messages]
124
+
125
+ # Tokenize using only the content part
126
+ prompt = tokenizer.batch_encode_plus(text_messages, return_tensors="pt", padding=True, truncation=False)
127
+
128
+ # Generate the assistant's reply without the user message being included at the start
129
+ generated_output = generator(user_message,
130
+ max_length=32768,
131
+ max_new_tokens=512,# Set max length for truncation
132
+ num_return_sequences=1,
133
+ do_sample=True,
134
+ temperature=0.5,
135
+ top_p=0.5,
136
+ top_k=0,
137
+ typical_p=1,
138
+ repetition_penalty=1) # Disable sampling for more creative and deterministic responses
139
+
140
+ response = generated_output[0]["generated_text"]
141
+
142
+ # Clean the response to remove any prefix from the last user message
143
+ if response.startswith(user_message):
144
+ response = response[len(user_message):].strip()
145
+
146
+ # Smart truncation to cut off at 4096 characters without cutting in the middle of a word
147
+ max_length = 4096
148
+ if len(response) > max_length:
149
+ # Find the last space before the cutoff point
150
+ truncated_response = response[:max_length]
151
+ last_space_idx = truncated_response.rfind(" ")
152
+
153
+ if last_space_idx != -1:
154
+ response = truncated_response[:last_space_idx]
155
+ else:
156
+ response = truncated_response
157
+
158
+ # Add the user message and assistant's response to history
159
+ history.append((username, user_message))
160
+ history.append(("Sanny Lin", response))
161
+
162
+ save_history(username, history)
163
+
164
+ return format_chat(history)
165
+
166
+
167
+
168
+
169
+
170
+ # ========== Gradio Interface ==========
171
+
172
+ with gr.Blocks(theme=gr.themes.Monochrome(), css="""
173
+ @font-face {
174
+ font-family: "DaemonFont";
175
+ src: url('static/daemon.otf') format('opentype');
176
+ }
177
+ body { background-color: #121212 !important; }
178
+ .gradio-container { background-color: #121212 !important; }
179
+ textarea { background-color: #1e1e1e !important; color: white; }
180
+ input { background-color: #1e1e1e !important; color: white; }
181
+ #chat_display { overflow-y: auto; height: calc(100vh - 200px); }
182
+ .sanny-message {
183
+ font-family: "DaemonFont", sans-serif;
184
+ }
185
+ """) as demo:
186
+
187
+ chat_display = gr.HTML(value="", elem_id="chat_display", show_label=False)
188
+
189
+ with gr.Row():
190
+ username_box = gr.Textbox(label="Username", placeholder="Enter username...", interactive=True, scale=2)
191
+ user_input = gr.Textbox(placeholder="Type your message...", lines=2, show_label=False, scale=8)
192
+ send_button = gr.Button("Send", scale=1)
193
+
194
+ username_state = gr.State("")
195
+ history_state = gr.State([])
196
+
197
+ def user_send(user_message, username, history, username_input):
198
+ if not username_input.strip():
199
+ return "<div style='color: red;'>Please enter a valid username first.</div>", history, username
200
+
201
+ username_input = sanitize_username(username_input)
202
+ if not username:
203
+ username = username_input
204
+
205
+ history = history or load_latest_history(username)
206
+
207
+ return generate_reply(username, user_message, history), history, username
208
+
209
+ send_button.click(
210
+ fn=user_send,
211
+ inputs=[user_input, username_state, history_state, username_box],
212
+ outputs=[chat_display, history_state, username_state]
213
+ )
214
+
215
+ send_button.click(lambda: "", None, user_input) # Clear input after send
216
+
217
+ demo.load(None, None, None, js="""
218
+ () => {
219
+ const textbox = document.querySelector('textarea');
220
+ const sendButton = document.querySelector('button');
221
+
222
+ textbox.addEventListener('keydown', function(e) {
223
+ if (e.key === 'Enter' && !e.shiftKey) {
224
+ e.preventDefault();
225
+ sendButton.click();
226
+ }
227
+ });
228
+ }
229
+ """)
230
+
231
+ demo.launch(share=False)
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio==3.28.0
2
+ torch==2.1.0+cpu # Or use 'torch==2.1.0+cu118' for CUDA-enabled GPU support
3
+ transformers==4.30.0
4
+ safetensors==0.3.0