philip11 commited on
Commit
9a8eea6
Β·
verified Β·
1 Parent(s): 2fea3b9

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +807 -0
app.py ADDED
@@ -0,0 +1,807 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import numpy as np
3
+ import uuid
4
+ import json
5
+ import os
6
+ import time
7
+ from datetime import datetime
8
+ from huggingface_hub import InferenceClient
9
+ from sentence_transformers import SentenceTransformer
10
+ from sklearn.metrics.pairwise import cosine_similarity
11
+ from transformers import AutoTokenizer, AutoModelForCausalLM
12
+ from openai import OpenAI
13
+
14
+ HF_TOKEN = os.environ.get("HF_TOKEN", "")
15
+
16
+ # Create necessary directories if they don't exist
17
+ os.makedirs("data/sessions", exist_ok=True)
18
+ os.makedirs("data/documents", exist_ok=True)
19
+ os.makedirs("data/embeddings", exist_ok=True)
20
+
21
+ # Configure page settings
22
+ st.set_page_config(
23
+ page_title="Matrix AI Chat with RAG",
24
+ page_icon="πŸ•ΆοΈ",
25
+ layout="wide",
26
+ initial_sidebar_state="expanded"
27
+ )
28
+
29
+ # Matrix-style CSS
30
+ def load_css():
31
+ matrix_css = """
32
+ <style>
33
+ @import url('https://fonts.googleapis.com/css2?family=Courier+New:wght@400;700&display=swap');
34
+
35
+ /* Global Matrix styling */
36
+ .stApp {
37
+ background-color: #000000 !important;
38
+ color: #00ff00 !important;
39
+ font-family: 'Courier New', monospace !important;
40
+ }
41
+
42
+ /* Main content area */
43
+ .main .block-container {
44
+ background-color: #000000 !important;
45
+ color: #00ff00 !important;
46
+ }
47
+
48
+ /* Sidebar */
49
+ .css-1d391kg {
50
+ background-color: #000000 !important;
51
+ border-right: 2px solid #00ff00 !important;
52
+ }
53
+
54
+ /* Chat messages */
55
+ .stChatMessage {
56
+ background-color: #001100 !important;
57
+ border: 1px solid #00ff00 !important;
58
+ border-radius: 5px !important;
59
+ padding: 15px !important;
60
+ margin: 10px 0 !important;
61
+ color: #00ff00 !important;
62
+ font-family: 'Courier New', monospace !important;
63
+ box-shadow: 0 0 10px rgba(0, 255, 0, 0.3) !important;
64
+ }
65
+
66
+ /* Input containers */
67
+ .stTextInput > div > div > input,
68
+ .stTextArea > div > div > textarea {
69
+ background-color: #000000 !important;
70
+ color: #00ff00 !important;
71
+ border: 1px solid #00ff00 !important;
72
+ font-family: 'Courier New', monospace !important;
73
+ }
74
+
75
+ /* Selectbox */
76
+ .stSelectbox > div > div > div {
77
+ background-color: #000000 !important;
78
+ color: #00ff00 !important;
79
+ border: 1px solid #00ff00 !important;
80
+ font-family: 'Courier New', monospace !important;
81
+ }
82
+
83
+ /* Buttons */
84
+ .stButton > button {
85
+ background-color: #000000 !important;
86
+ color: #00ff00 !important;
87
+ border: 1px solid #00ff00 !important;
88
+ font-family: 'Courier New', monospace !important;
89
+ font-weight: bold !important;
90
+ transition: all 0.3s ease !important;
91
+ }
92
+
93
+ .stButton > button:hover {
94
+ background-color: #00ff00 !important;
95
+ color: #000000 !important;
96
+ box-shadow: 0 0 15px rgba(0, 255, 0, 0.7) !important;
97
+ }
98
+
99
+ /* Headers */
100
+ h1, h2, h3, h4, h5, h6 {
101
+ color: #00ff00 !important;
102
+ font-family: 'Courier New', monospace !important;
103
+ text-shadow: 0 0 10px rgba(0, 255, 0, 0.8) !important;
104
+ }
105
+
106
+ /* Main header */
107
+ .main-header {
108
+ text-align: center;
109
+ color: #00ff00 !important;
110
+ margin-bottom: 2rem;
111
+ font-size: 3rem !important;
112
+ text-shadow: 0 0 20px rgba(0, 255, 0, 1) !important;
113
+ animation: matrix-glow 2s ease-in-out infinite alternate;
114
+ }
115
+
116
+ @keyframes matrix-glow {
117
+ from { text-shadow: 0 0 20px rgba(0, 255, 0, 0.8); }
118
+ to { text-shadow: 0 0 30px rgba(0, 255, 0, 1), 0 0 40px rgba(0, 255, 0, 0.8); }
119
+ }
120
+
121
+ /* Status indicators */
122
+ .status-success {
123
+ color: #00ff00 !important;
124
+ font-weight: bold !important;
125
+ text-shadow: 0 0 5px rgba(0, 255, 0, 0.8) !important;
126
+ }
127
+
128
+ .status-error {
129
+ color: #ff0000 !important;
130
+ font-weight: bold !important;
131
+ text-shadow: 0 0 5px rgba(255, 0, 0, 0.8) !important;
132
+ }
133
+
134
+ /* Chat input */
135
+ .stChatInputContainer {
136
+ background-color: #000000 !important;
137
+ border-top: 1px solid #00ff00 !important;
138
+ }
139
+
140
+ /* Expander */
141
+ .streamlit-expanderHeader {
142
+ background-color: #000000 !important;
143
+ color: #00ff00 !important;
144
+ border: 1px solid #00ff00 !important;
145
+ }
146
+
147
+ /* Info boxes */
148
+ .stInfo {
149
+ background-color: #001100 !important;
150
+ color: #00ff00 !important;
151
+ border: 1px solid #00ff00 !important;
152
+ }
153
+
154
+ /* Warning boxes */
155
+ .stWarning {
156
+ background-color: #110100 !important;
157
+ color: #ffff00 !important;
158
+ border: 1px solid #ffff00 !important;
159
+ }
160
+
161
+ /* Error boxes */
162
+ .stError {
163
+ background-color: #110000 !important;
164
+ color: #ff0000 !important;
165
+ border: 1px solid #ff0000 !important;
166
+ }
167
+
168
+ /* Success boxes */
169
+ .stSuccess {
170
+ background-color: #001100 !important;
171
+ color: #00ff00 !important;
172
+ border: 1px solid #00ff00 !important;
173
+ }
174
+
175
+ /* Spinner */
176
+ .stSpinner {
177
+ color: #00ff00 !important;
178
+ }
179
+
180
+ /* Caption */
181
+ .caption {
182
+ color: #00aa00 !important;
183
+ font-family: 'Courier New', monospace !important;
184
+ text-align: center;
185
+ font-style: italic;
186
+ }
187
+
188
+ /* Matrix rain effect */
189
+ .matrix-bg::before {
190
+ content: "";
191
+ position: fixed;
192
+ top: 0;
193
+ left: 0;
194
+ width: 100%;
195
+ height: 100%;
196
+ background: repeating-linear-gradient(
197
+ 90deg,
198
+ transparent,
199
+ transparent 98px,
200
+ rgba(0, 255, 0, 0.03) 100px
201
+ );
202
+ pointer-events: none;
203
+ z-index: -1;
204
+ }
205
+
206
+ /* Model selection highlight */
207
+ .model-selector {
208
+ border: 2px solid #00ff00 !important;
209
+ border-radius: 5px !important;
210
+ padding: 10px !important;
211
+ background-color: #001100 !important;
212
+ margin: 10px 0 !important;
213
+ }
214
+
215
+ /* Scrollbar */
216
+ ::-webkit-scrollbar {
217
+ width: 12px;
218
+ }
219
+
220
+ ::-webkit-scrollbar-track {
221
+ background: #000000;
222
+ }
223
+
224
+ ::-webkit-scrollbar-thumb {
225
+ background: #00ff00;
226
+ border-radius: 6px;
227
+ }
228
+
229
+ ::-webkit-scrollbar-thumb:hover {
230
+ background: #00aa00;
231
+ }
232
+ </style>
233
+ """
234
+ st.markdown(matrix_css, unsafe_allow_html=True)
235
+
236
+ # Model configurations
237
+ MODEL_CONFIGS = {
238
+ "DeepSeek-R1": {
239
+ "provider": "together",
240
+ "model_name": "deepseek-ai/DeepSeek-R1-0528",
241
+ "type": "api"
242
+ },
243
+ "Llama-3.2-3B": {
244
+ "provider": "huggingface",
245
+ "model_name": "meta-llama/Llama-3.2-3B",
246
+ "type": "local"
247
+ },
248
+ "Qwen2.5-VL-7B-Instruct": {
249
+ "provider": "hyperbolic",
250
+ "model_name": "Qwen/Qwen2.5-VL-7B-Instruct",
251
+ "type": "api"
252
+ }
253
+ }
254
+
255
+ # Initialize clients based on selected model
256
+ @st.cache_resource
257
+ def get_model_client(model_name):
258
+ try:
259
+ if not HF_TOKEN:
260
+ st.error("❌ Hugging Face token is required!")
261
+ return None, None
262
+
263
+ config = MODEL_CONFIGS[model_name]
264
+
265
+ if config["type"] == "api":
266
+ if config["provider"] == "together":
267
+ client = InferenceClient(
268
+ provider="together",
269
+ api_key=HF_TOKEN,
270
+ )
271
+ return client, config
272
+ elif config["provider"] == "hyperbolic":
273
+ client = OpenAI(
274
+ base_url="https://router.huggingface.co/hyperbolic/v1",
275
+ api_key=HF_TOKEN,
276
+ )
277
+ return client, config
278
+ elif config["type"] == "local":
279
+ # For local models, we'll load tokenizer and model
280
+ tokenizer = AutoTokenizer.from_pretrained(config["model_name"])
281
+ model = AutoModelForCausalLM.from_pretrained(config["model_name"])
282
+ return (tokenizer, model), config
283
+
284
+ return None, None
285
+ except Exception as e:
286
+ st.error(f"❌ Error initializing {model_name} client: {e}")
287
+ return None, None
288
+
289
+ # Initialize session management
290
+ def get_session_id():
291
+ if "session_id" not in st.session_state:
292
+ st.session_state.session_id = str(uuid.uuid4())
293
+ save_session_metadata(st.session_state.session_id)
294
+ return st.session_state.session_id
295
+
296
+ # Save session metadata
297
+ def save_session_metadata(session_id):
298
+ try:
299
+ session_file = f"data/sessions/{session_id}_metadata.json"
300
+ metadata = {
301
+ "session_id": session_id,
302
+ "created_at": datetime.now().isoformat(),
303
+ "last_updated": datetime.now().isoformat()
304
+ }
305
+ with open(session_file, "w") as f:
306
+ json.dump(metadata, f, indent=2)
307
+ except Exception as e:
308
+ st.warning(f"Could not save session metadata: {e}")
309
+
310
+ # Update session timestamp
311
+ def update_session_timestamp(session_id):
312
+ try:
313
+ session_file = f"data/sessions/{session_id}_metadata.json"
314
+ if os.path.exists(session_file):
315
+ with open(session_file, "r") as f:
316
+ metadata = json.load(f)
317
+ metadata["last_updated"] = datetime.now().isoformat()
318
+ with open(session_file, "w") as f:
319
+ json.dump(metadata, f, indent=2)
320
+ except Exception as e:
321
+ st.warning(f"Could not update session timestamp: {e}")
322
+
323
+ # Save chat history
324
+ def save_chat_history(prompt, response, embedding=None, context=""):
325
+ try:
326
+ session_id = get_session_id()
327
+ history_file = f"data/sessions/{session_id}_history.json"
328
+
329
+ # Load existing history or create new
330
+ if os.path.exists(history_file):
331
+ with open(history_file, "r") as f:
332
+ history = json.load(f)
333
+ else:
334
+ history = []
335
+
336
+ # Get message order
337
+ message_order = len(history) + 1
338
+
339
+ # Create history entry
340
+ entry = {
341
+ "message_id": message_order,
342
+ "prompt": prompt,
343
+ "response": response,
344
+ "context": context,
345
+ "timestamp": datetime.now().isoformat()
346
+ }
347
+
348
+ # Save embedding if available
349
+ if embedding is not None:
350
+ embedding_file = f"data/embeddings/{session_id}_{message_order}.npy"
351
+ np.save(embedding_file, np.array(embedding))
352
+ entry["embedding_path"] = embedding_file
353
+
354
+ # Append and save history
355
+ history.append(entry)
356
+ with open(history_file, "w") as f:
357
+ json.dump(history, f, indent=2)
358
+
359
+ # Update session timestamp
360
+ update_session_timestamp(session_id)
361
+ except Exception as e:
362
+ st.warning(f"Could not save chat history: {e}")
363
+
364
+ # Add a document to the RAG system
365
+ def add_document(title, content, embedding=None):
366
+ try:
367
+ # Generate document ID
368
+ doc_id = str(uuid.uuid4())
369
+
370
+ # Save document
371
+ document_file = f"data/documents/{doc_id}.json"
372
+ document = {
373
+ "id": doc_id,
374
+ "title": title,
375
+ "content": content,
376
+ "created_at": datetime.now().isoformat()
377
+ }
378
+
379
+ with open(document_file, "w") as f:
380
+ json.dump(document, f, indent=2)
381
+
382
+ # Save embedding if available
383
+ if embedding is not None:
384
+ embedding_file = f"data/embeddings/doc_{doc_id}.npy"
385
+ np.save(embedding_file, np.array(embedding))
386
+
387
+ # Save embedding reference
388
+ document["embedding_path"] = embedding_file
389
+ with open(document_file, "w") as f:
390
+ json.dump(document, f, indent=2)
391
+
392
+ return doc_id
393
+ except Exception as e:
394
+ st.error(f"Error adding document: {e}")
395
+ return None
396
+
397
+ # Function to get embedding vector
398
+ @st.cache_resource
399
+ def load_embedding_model():
400
+ try:
401
+ model = SentenceTransformer('all-MiniLM-L6-v2')
402
+ return model
403
+ except Exception as e:
404
+ st.error(f"Error loading embedding model: {e}")
405
+ return None
406
+
407
+ def get_embedding(text):
408
+ model = load_embedding_model()
409
+ if model:
410
+ try:
411
+ return model.encode(text)
412
+ except Exception as e:
413
+ st.warning(f"Embedding error: {e}")
414
+ return None
415
+
416
+ # Generate conversation context
417
+ def generate_context(user_query, max_turns=3):
418
+ try:
419
+ session_id = get_session_id()
420
+ history_file = f"data/sessions/{session_id}_history.json"
421
+
422
+ if not os.path.exists(history_file):
423
+ return ""
424
+
425
+ with open(history_file, "r") as f:
426
+ history = json.load(f)
427
+
428
+ # Get last N conversation turns
429
+ recent_history = history[-max_turns:] if len(history) >= max_turns else history
430
+
431
+ # Create context string
432
+ context = ""
433
+ for entry in recent_history:
434
+ context += f"User: {entry['prompt']}\nAssistant: {entry['response']}\n\n"
435
+
436
+ return context.strip()
437
+ except Exception as e:
438
+ st.warning(f"Error generating context: {e}")
439
+ return ""
440
+
441
+ # Fetch stored embeddings
442
+ def fetch_embeddings():
443
+ try:
444
+ session_id = get_session_id()
445
+ history_file = f"data/sessions/{session_id}_history.json"
446
+
447
+ if not os.path.exists(history_file):
448
+ return [], np.array([])
449
+
450
+ with open(history_file, "r") as f:
451
+ history = json.load(f)
452
+
453
+ prompts, responses, embeddings, contexts = [], [], [], []
454
+
455
+ for entry in history:
456
+ if "embedding_path" in entry and os.path.exists(entry["embedding_path"]):
457
+ try:
458
+ embedding = np.load(entry["embedding_path"])
459
+ embeddings.append(embedding)
460
+ prompts.append(entry["prompt"])
461
+ responses.append(entry["response"])
462
+ contexts.append(entry.get("context", ""))
463
+ except Exception:
464
+ continue # Skip corrupted embeddings
465
+
466
+ return list(zip(prompts, responses, contexts)), np.array(embeddings) if embeddings else np.array([])
467
+ except Exception as e:
468
+ st.warning(f"Error fetching embeddings: {e}")
469
+ return [], np.array([])
470
+
471
+ # Search for similar documents in the RAG system
472
+ def search_rag_documents(query_embedding, top_k=3, threshold=0.7):
473
+ try:
474
+ if not os.path.exists("data/documents"):
475
+ return []
476
+
477
+ results = []
478
+ document_files = [f for f in os.listdir("data/documents") if f.endswith(".json")]
479
+
480
+ for doc_file in document_files:
481
+ try:
482
+ with open(f"data/documents/{doc_file}", "r") as f:
483
+ document = json.load(f)
484
+
485
+ # Check if embedding exists
486
+ if "embedding_path" in document and os.path.exists(document["embedding_path"]):
487
+ doc_embedding = np.load(document["embedding_path"])
488
+
489
+ # Calculate similarity
490
+ similarity = cosine_similarity([query_embedding], [doc_embedding])[0][0]
491
+
492
+ # Add if above threshold
493
+ if similarity >= threshold:
494
+ results.append((
495
+ document["id"],
496
+ document["title"],
497
+ document["content"],
498
+ similarity
499
+ ))
500
+ except Exception:
501
+ continue # Skip corrupted documents
502
+
503
+ # Sort by similarity score (descending)
504
+ results.sort(key=lambda x: x[3], reverse=True)
505
+ return results[:top_k]
506
+ except Exception as e:
507
+ st.warning(f"Error searching RAG documents: {e}")
508
+ return []
509
+
510
+ # Similarity Search Function
511
+ def find_similar_response(user_query, user_embedding, threshold=0.85):
512
+ try:
513
+ # First check for similar responses in conversation history
514
+ data, embeddings = fetch_embeddings()
515
+
516
+ if embeddings.size > 0:
517
+ similarities = cosine_similarity([user_embedding], embeddings)[0]
518
+ best_match_index = np.argmax(similarities)
519
+
520
+ if similarities[best_match_index] >= threshold:
521
+ matched_prompt, matched_response, matched_context = data[best_match_index]
522
+ return matched_response, ""
523
+
524
+ # If no match in history, search RAG documents
525
+ rag_results = search_rag_documents(user_embedding)
526
+ if rag_results:
527
+ context_docs = "\n\n".join([
528
+ f"**{title}**\n{content}"
529
+ for _, title, content, _ in rag_results
530
+ ])
531
+ return None, context_docs
532
+
533
+ return None, ""
534
+ except Exception as e:
535
+ st.warning(f"Error in similarity search: {e}")
536
+ return None, ""
537
+
538
+ # Generate response using selected model
539
+ def generate_response(prompt, system_prompt="", rag_context="", selected_model="DeepSeek-R1"):
540
+ client, config = get_model_client(selected_model)
541
+
542
+ if not client:
543
+ return f"❌ {selected_model} client not available. Please check your configuration."
544
+
545
+ try:
546
+ # Construct user content
547
+ user_content = prompt
548
+ if rag_context:
549
+ user_content = f"Context information:\n{rag_context}\n\nQuestion: {prompt}"
550
+
551
+ if config["type"] == "api":
552
+ # Handle API-based models
553
+ messages = []
554
+
555
+ if system_prompt:
556
+ messages.append({
557
+ "role": "system",
558
+ "content": system_prompt
559
+ })
560
+
561
+ messages.append({
562
+ "role": "user",
563
+ "content": user_content
564
+ })
565
+
566
+ # Generate response based on provider
567
+ if config["provider"] == "together":
568
+ completion = client.chat.completions.create(
569
+ model=config["model_name"],
570
+ messages=messages,
571
+ max_tokens=1000,
572
+ temperature=0.7,
573
+ top_p=0.9,
574
+ )
575
+ return completion.choices[0].message.content
576
+
577
+ elif config["provider"] == "hyperbolic":
578
+ completion = client.chat.completions.create(
579
+ model=config["model_name"],
580
+ messages=messages,
581
+ max_tokens=1000,
582
+ temperature=0.7,
583
+ )
584
+ return completion.choices[0].message.content
585
+
586
+ elif config["type"] == "local":
587
+ # Handle local models
588
+ tokenizer, model = client
589
+
590
+ # Prepare input
591
+ full_prompt = f"{system_prompt}\n\nUser: {user_content}\nAssistant:"
592
+ inputs = tokenizer(full_prompt, return_tensors="pt")
593
+
594
+ # Generate response
595
+ with torch.no_grad():
596
+ outputs = model.generate(
597
+ inputs.input_ids,
598
+ max_length=inputs.input_ids.shape[1] + 500,
599
+ temperature=0.7,
600
+ do_sample=True,
601
+ pad_token_id=tokenizer.eos_token_id
602
+ )
603
+
604
+ # Decode response
605
+ response = tokenizer.decode(outputs[0], skip_special_tokens=True)
606
+ # Extract only the assistant's response
607
+ response = response.split("Assistant:")[-1].strip()
608
+ return response
609
+
610
+ except Exception as e:
611
+ st.error(f"Error generating response: {e}")
612
+ return f"I apologize, but I encountered an error while processing your request: {str(e)}"
613
+
614
+ # Main UI
615
+ def main():
616
+ # Load CSS
617
+ load_css()
618
+
619
+ # Matrix background div
620
+ st.markdown('<div class="matrix-bg"></div>', unsafe_allow_html=True)
621
+
622
+ st.markdown('<h1 class="main-header">πŸ•ΆοΈ MATRIX AI CHAT</h1>', unsafe_allow_html=True)
623
+ st.markdown('<p class="caption">ENTER THE MATRIX: Advanced AI with Retrieval-Augmented Generation</p>', unsafe_allow_html=True)
624
+
625
+ # Get session ID
626
+ session_id = get_session_id()
627
+
628
+ # Sidebar Configuration
629
+ with st.sidebar:
630
+ st.markdown("## βš™οΈ MATRIX CONTROL PANEL")
631
+
632
+ # Model Selection
633
+ st.markdown('<div class="model-selector">', unsafe_allow_html=True)
634
+ st.markdown("### πŸ€– AI MODEL SELECTION")
635
+ selected_model = st.selectbox(
636
+ "Choose your AI:",
637
+ options=list(MODEL_CONFIGS.keys()),
638
+ index=0,
639
+ help="Select the AI model to power your conversations"
640
+ )
641
+ st.markdown('</div>', unsafe_allow_html=True)
642
+
643
+ # Token status
644
+ if HF_TOKEN:
645
+ st.markdown('<p class="status-success">βœ… HUGGING FACE TOKEN: CONNECTED</p>', unsafe_allow_html=True)
646
+
647
+ # Test connection
648
+ client, config = get_model_client(selected_model)
649
+ if client:
650
+ st.markdown(f'<p class="status-success">βœ… {selected_model}: READY</p>', unsafe_allow_html=True)
651
+ else:
652
+ st.markdown(f'<p class="status-error">❌ {selected_model}: CONNECTION FAILED</p>', unsafe_allow_html=True)
653
+ else:
654
+ st.markdown('<p class="status-error">❌ NO HUGGING FACE TOKEN FOUND</p>', unsafe_allow_html=True)
655
+ st.info("Please set your HF_TOKEN environment variable to enter the Matrix.")
656
+
657
+ st.divider()
658
+
659
+ # System prompt configuration
660
+ system_prompt = st.text_area(
661
+ "SYSTEM PROMPT",
662
+ value=f"You are {selected_model}, an advanced AI assistant operating within the Matrix. Provide accurate, detailed, and helpful responses. If given context information, use it to enhance your answers. Embrace the digital realm.",
663
+ height=120,
664
+ help="Define how the AI should behave in the Matrix"
665
+ )
666
+
667
+ st.divider()
668
+
669
+ # Session controls
670
+ st.markdown("### πŸ”„ SESSION CONTROLS")
671
+ col1, col2 = st.columns(2)
672
+
673
+ with col1:
674
+ if st.button("NEW JACK IN", use_container_width=True):
675
+ # Reset session
676
+ for key in ["session_id", "message_log"]:
677
+ if key in st.session_state:
678
+ del st.session_state[key]
679
+ st.rerun()
680
+
681
+ with col2:
682
+ if st.button("PURGE ALL", use_container_width=True):
683
+ # Clear all data (with confirmation)
684
+ if st.session_state.get("confirm_clear", False):
685
+ try:
686
+ import shutil
687
+ if os.path.exists("data"):
688
+ shutil.rmtree("data")
689
+ os.makedirs("data/sessions", exist_ok=True)
690
+ os.makedirs("data/documents", exist_ok=True)
691
+ os.makedirs("data/embeddings", exist_ok=True)
692
+ st.success("Matrix data purged!")
693
+ st.session_state.confirm_clear = False
694
+ st.rerun()
695
+ except Exception as e:
696
+ st.error(f"Error purging Matrix: {e}")
697
+ else:
698
+ st.session_state.confirm_clear = True
699
+ st.warning("Click again to confirm Matrix purge")
700
+
701
+ st.divider()
702
+
703
+ # RAG Document Upload
704
+ st.markdown("### πŸ“š KNOWLEDGE MATRIX")
705
+
706
+ with st.expander("UPLOAD DATA"):
707
+ doc_title = st.text_input("DATA TITLE", placeholder="Enter data identifier...")
708
+ doc_content = st.text_area(
709
+ "DATA CONTENT",
710
+ placeholder="Upload your knowledge to the Matrix...",
711
+ height=200
712
+ )
713
+
714
+ if st.button("πŸ“ INJECT DATA", use_container_width=True):
715
+ if doc_title and doc_content:
716
+ with st.spinner("Integrating into Matrix..."):
717
+ doc_embedding = get_embedding(doc_content)
718
+ doc_id = add_document(doc_title, doc_content, doc_embedding)
719
+ if doc_id:
720
+ st.success(f"βœ… Data '{doc_title}' integrated into Matrix!")
721
+ else:
722
+ st.error("❌ Failed to integrate data")
723
+ else:
724
+ st.warning("Please provide both title and content")
725
+
726
+ # Display document count
727
+ try:
728
+ doc_count = len([f for f in os.listdir("data/documents") if f.endswith(".json")])
729
+ st.info(f"πŸ“„ {doc_count} data nodes in Matrix")
730
+ except:
731
+ st.info("πŸ“„ 0 data nodes in Matrix")
732
+
733
+ st.divider()
734
+ st.markdown(f"**SESSION ID:** `{session_id[:8]}...`")
735
+
736
+ # Initialize chat history
737
+ if "message_log" not in st.session_state:
738
+ st.session_state.message_log = [{
739
+ "role": "assistant",
740
+ "content": f"πŸ•ΆοΈ Welcome to the Matrix. I am {selected_model}, your guide through the digital realm. The red pill or the blue pill - what will you choose to explore today?"
741
+ }]
742
+
743
+ # Display chat history
744
+ for message in st.session_state.message_log:
745
+ with st.chat_message(message["role"]):
746
+ st.markdown(message["content"])
747
+
748
+ # Chat input
749
+ user_query = st.chat_input("Enter your query into the Matrix...")
750
+
751
+ # Process user query
752
+ if user_query and HF_TOKEN:
753
+ # Add user message to chat
754
+ st.session_state.message_log.append({"role": "user", "content": user_query})
755
+
756
+ # Display user message
757
+ with st.chat_message("user"):
758
+ st.markdown(user_query)
759
+
760
+ # Generate response
761
+ with st.chat_message("assistant"):
762
+ with st.spinner(f"🧠 {selected_model} is processing in the Matrix..."):
763
+ # Get embedding for similarity search
764
+ user_embedding = get_embedding(user_query)
765
+
766
+ # Check for similar responses or RAG context
767
+ cached_response = None
768
+ rag_context = ""
769
+
770
+ if user_embedding is not None:
771
+ cached_response, rag_context = find_similar_response(user_query, user_embedding)
772
+
773
+ if cached_response:
774
+ # Use cached response
775
+ st.info("πŸ” Found similar data in Matrix")
776
+ response_text = cached_response
777
+ else:
778
+ # Generate new response
779
+ response_text = generate_response(user_query, system_prompt, rag_context, selected_model)
780
+
781
+ # Display response with Matrix-style streaming effect
782
+ response_placeholder = st.empty()
783
+ displayed_response = ""
784
+
785
+ # Simulate Matrix-style streaming
786
+ for char in response_text:
787
+ displayed_response += char
788
+ response_placeholder.markdown(displayed_response + "β–ˆ")
789
+ time.sleep(0.02) # Slightly slower for Matrix effect
790
+
791
+ # Final response
792
+ response_placeholder.markdown(response_text)
793
+
794
+ # Add response to chat history
795
+ st.session_state.message_log.append({"role": "assistant", "content": response_text})
796
+
797
+ # Save to persistent storage
798
+ save_chat_history(user_query, response_text, user_embedding, generate_context(user_query))
799
+
800
+ # Rerun to update UI
801
+ st.rerun()
802
+
803
+ elif user_query and not HF_TOKEN:
804
+ st.error("❌ Please set your Hugging Face token to enter the Matrix.")
805
+
806
+ if __name__ == "__main__":
807
+ main()