acecalisto3 commited on
Commit
4bc55d3
Β·
verified Β·
1 Parent(s): d7ff1c4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +232 -881
app.py CHANGED
@@ -1,903 +1,254 @@
1
- import copy
2
- import difflib
3
- import hashlib
4
- import pickle
5
- import random
6
- import json
7
- from datetime import datetime
8
- from typing import Dict, Any, List, Callable, Optional
9
-
10
- from marshmallow import Schema, fields, ValidationError, EXCLUDE
11
- from semantic_version import Version
12
  import gradio as gr
13
-
14
- # ---------- Constants & Helpers ----------
15
- from examples import EXAMPLES
16
-
17
- COLOR_SCHEMES = [
18
- ["#FF6B6B", "#4ECDC4", "#45B7D1"], # Ocean Breeze
19
- ["#2A9D8F", "#E9C46A", "#F4A261"], # Desert Sunset
20
- ["#264653", "#2A9D8F", "#E9C46A"], # Forest Dawn
21
- ["#F4A261", "#E76F51", "#2A9D8F"], # Coral Reef
22
- ["#8338EC", "#3A86FF", "#FF006E"], # Neon Nights
23
- ["#06D6A0", "#118AB2", "#073B4C"], # Deep Ocean
24
- ["#FFB4A2", "#E5989B", "#B5838D"], # Rose Garden
25
- ["#22223B", "#4A4E69", "#9A8C98"] # Midnight Purple
26
- ]
27
-
28
- def generate_gradient(colors: List[str]) -> str:
29
- """Generate modern gradient styling with enhanced visual effects"""
30
- return f"""
31
- /* Modern gradient background with enhanced visual effects */
32
- .gradio-container {{
33
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
34
- }}
35
-
36
- .container {{
37
- background: linear-gradient(135deg, {colors[0]}, {colors[1]}, {colors[2]});
38
- background-size: 300% 300%;
39
- animation: gradient-shift 15s ease infinite;
40
- border-radius: 12px;
41
- padding: 24px;
42
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
43
- transition: all 0.3s ease;
44
- margin-bottom: 1rem;
45
- }}
46
-
47
- .chat-message {{
48
- padding: 12px 16px;
49
- border-radius: 8px;
50
- margin: 8px 0;
51
- max-width: 85%;
52
- transition: transform 0.2s ease;
53
- }}
54
-
55
- .user-message {{
56
- background: rgba(255, 255, 255, 0.95);
57
- color: #333;
58
- margin-left: auto;
59
- border-top-right-radius: 2px;
60
- }}
61
-
62
- .assistant-message {{
63
- background: rgba(0, 0, 0, 0.05);
64
- color: #fff;
65
- margin-right: auto;
66
- border-top-left-radius: 2px;
67
- }}
68
-
69
- .system-message {{
70
- background: rgba(0, 0, 0, 0.2);
71
- color: #fff;
72
- margin: 8px auto;
73
- font-style: italic;
74
- text-align: center;
75
- }}
76
-
77
- .notification {{
78
- position: fixed;
79
- top: 20px;
80
- right: 20px;
81
- padding: 12px 24px;
82
- border-radius: 8px;
83
- background: rgba(0, 0, 0, 0.8);
84
- color: white;
85
- z-index: 1000;
86
- animation: slide-in 0.3s ease;
87
- }}
88
-
89
- .typing-indicator {{
90
- display: inline-block;
91
- padding: 8px 16px;
92
- background: rgba(0, 0, 0, 0.1);
93
- border-radius: 16px;
94
- color: rgba(255, 255, 255, 0.8);
95
- font-style: italic;
96
- margin: 8px 0;
97
- }}
98
-
99
- .control-panel {{
100
- background: rgba(255, 255, 255, 0.2);
101
- border-radius: 12px;
102
- padding: 20px;
103
- position: fixed;
104
- right: -250px;
105
- top: 50%;
106
- transform: translateY(-50%);
107
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
108
- z-index: 1000;
109
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
110
- width: 220px;
111
- backdrop-filter: blur(10px);
112
- border: 1px solid rgba(255, 255, 255, 0.1);
113
- pointer-events: auto;
114
- }}
115
-
116
- /* Make the panel accessible via keyboard focus */
117
- .control-panel:focus-within,
118
- .control-panel:hover {{
119
- right: 20px;
120
- outline: none;
121
- }}
122
-
123
- /* Ensure the panel is keyboard navigable */
124
- .control-panel button:focus {{
125
- outline: 2px solid rgba(255, 255, 255, 0.5);
126
- outline-offset: 2px;
127
- }}
128
-
129
- /* Improve button hover states */
130
- .control-panel button:hover {{
131
- background: rgba(255, 255, 255, 0.25);
132
- transform: translateX(5px);
133
- }}
134
-
135
- /* Make the panel accessible via keyboard focus */
136
- .control-panel:hover,
137
- .control-panel:focus-within {{
138
- right: 20px;
139
- outline: none;
140
- background: rgba(255, 255, 255, 0.3);
141
- box-shadow: 0 6px 25px rgba(0, 0, 0, 0.3);
142
- }}
143
-
144
- .control-panel:hover ~ .chat-window {{
145
- transform: translateX(-110px);
146
- }}
147
-
148
- .control-panel::before {{
149
- content: "☰ Settings";
150
- position: fixed;
151
- right: 0;
152
- top: 50%;
153
- transform: translateY(-50%);
154
- background: rgba(255, 255, 255, 0.5);
155
- padding: 15px 35px 15px 25px;
156
- border-radius: 12px 0 0 12px;
157
- font-size: 16px;
158
- font-weight: bold;
159
- color: white;
160
- cursor: pointer;
161
- transition: all 0.3s ease;
162
- box-shadow: -2px 0 15px rgba(0, 0, 0, 0.2);
163
- z-index: 999;
164
- text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
165
- white-space: nowrap;
166
- border: 1px solid rgba(255, 255, 255, 0.3);
167
- border-right: none;
168
- animation: pulse 2s infinite;
169
- }}
170
-
171
- .control-panel:hover::before {{
172
- opacity: 0;
173
- transform: translateY(-50%) translateX(-20px);
174
- }}
175
-
176
- .control-panel:hover ~ .chat-window {{
177
- transform: translateX(-110px);
178
- transition: transform 0.3s ease;
179
- }}
180
-
181
- .control-panel:hover::before {{
182
- opacity: 0;
183
- transform: translateY(-50%) translateX(-20px);
184
- }}
185
-
186
- /* Menu trigger tab with pulsing effect */
187
- .control-panel::before {{
188
- content: "☰ Settings";
189
- position: fixed;
190
- right: 0;
191
- top: 50%;
192
- transform: translateY(-50%);
193
- background: rgba(255, 255, 255, 0.5);
194
- padding: 15px 35px 15px 25px;
195
- border-radius: 12px 0 0 12px;
196
- font-size: 16px;
197
- font-weight: bold;
198
- color: white;
199
- cursor: pointer;
200
- transition: all 0.3s ease;
201
- box-shadow: -2px 0 15px rgba(0, 0, 0, 0.2);
202
- z-index: 999;
203
- text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
204
- white-space: nowrap;
205
- border: 1px solid rgba(255, 255, 255, 0.3);
206
- border-right: none;
207
- animation: pulse 2s infinite;
208
- }}
209
-
210
- .control-panel:hover::before {{
211
- background: rgba(255, 255, 255, 0.6);
212
- padding-right: 45px;
213
- transform: translateY(-50%) translateX(-10px);
214
- }}
215
-
216
- /* Add hover trigger area */
217
- .control-panel::after {{
218
- content: "";
219
- position: fixed;
220
- right: 0;
221
- top: 0;
222
- width: 100px; /* Even wider hover area */
223
- height: 100%;
224
- z-index: 998;
225
- background: linear-gradient(to right, transparent, rgba(255, 255, 255, 0.05));
226
- pointer-events: none; /* Allow clicks to pass through */
227
- }}
228
-
229
- /* Ensure the menu is interactive */
230
- .control-panel:hover {{
231
- pointer-events: auto;
232
- }}
233
-
234
- /* Hide trigger area when menu is open */
235
- .control-panel:hover::after {{
236
- opacity: 0;
237
- width: 0;
238
- }}
239
-
240
- @keyframes pulse {{
241
- 0% {{ box-shadow: -2px 0 15px rgba(0, 0, 0, 0.2); }}
242
- 50% {{ box-shadow: -2px 0 20px rgba(255, 255, 255, 0.3); }}
243
- 100% {{ box-shadow: -2px 0 15px rgba(0, 0, 0, 0.2); }}
244
- }}
245
-
246
- .control-panel:hover::before {{
247
- opacity: 0;
248
- transform: translateY(-50%) translateX(-10px);
249
- pointer-events: none;
250
- background: rgba(255, 255, 255, 0.5);
251
- padding-right: 35px;
252
- }}
253
-
254
- .control-panel:not(:hover)::before {{
255
- opacity: 1;
256
- pointer-events: auto;
257
- }}
258
-
259
- /* Menu trigger tab */
260
- .control-panel::before {{
261
- content: "☰ Settings β†’";
262
- position: fixed;
263
- right: 0;
264
- top: 50%;
265
- transform: translateY(-50%);
266
- background: rgba(255, 255, 255, 0.4);
267
- padding: 15px 25px;
268
- border-radius: 12px 0 0 12px;
269
- font-size: 16px;
270
- font-weight: bold;
271
- color: white;
272
- cursor: pointer;
273
- transition: all 0.3s ease;
274
- box-shadow: -2px 0 15px rgba(0, 0, 0, 0.2);
275
- z-index: 999;
276
- text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
277
- white-space: nowrap;
278
- border: 1px solid rgba(255, 255, 255, 0.2);
279
- border-right: none;
280
- }}
281
-
282
- .control-panel:hover::before {{
283
- background: rgba(255, 255, 255, 0.5);
284
- padding-right: 35px;
285
- opacity: 0;
286
- transform: translateY(-50%) translateX(-10px);
287
- }}
288
-
289
- .control-panel button {{
290
- width: 100%;
291
- margin: 8px 0;
292
- transition: all 0.2s ease;
293
- }}
294
-
295
- .control-panel button:hover {{
296
- transform: translateX(5px);
297
- background: rgba(255, 255, 255, 0.1);
298
- }}
299
-
300
- button {{
301
- transition: all 0.2s ease;
302
- padding: 12px 20px;
303
- background: rgba(255, 255, 255, 0.2);
304
- border: none;
305
- border-radius: 8px;
306
- color: white;
307
- font-size: 14px;
308
- font-weight: 500;
309
- cursor: pointer;
310
- text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
311
- margin: 5px 0;
312
- display: flex;
313
- align-items: center;
314
- justify-content: center;
315
- width: 100%;
316
- }}
317
-
318
- button[aria-label="Show Examples"] {{
319
- background: rgba(255, 255, 255, 0.3);
320
- font-weight: 600;
321
- border: 1px solid rgba(255, 255, 255, 0.3);
322
- }}
323
-
324
- button[aria-label="Show Examples"]:hover {{
325
- background: rgba(255, 255, 255, 0.4);
326
- transform: translateY(-2px);
327
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
328
- }}
329
-
330
- button:hover {{
331
- transform: translateX(5px);
332
- background: rgba(255, 255, 255, 0.3);
333
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
334
- }}
335
-
336
- button:active {{
337
- transform: translateX(2px);
338
- background: rgba(255, 255, 255, 0.25);
339
- }}
340
-
341
- @keyframes gradient-shift {{
342
- 0% {{ background-position: 0% 50%; }}
343
- 50% {{ background-position: 100% 50%; }}
344
- 100% {{ background-position: 0% 50%; }}
345
- }}
346
-
347
- @keyframes slide-in {{
348
- from {{ transform: translateX(100%); opacity: 0; }}
349
- to {{ transform: translateX(0); opacity: 1; }}
350
- }}
351
-
352
- /* Responsive adjustments */
353
- @media (max-width: 768px) {{
354
- .container {{ padding: 16px; }}
355
- .chat-message {{ max-width: 95%; }}
356
- }}
357
- """
358
-
359
- # ---------- Core System ----------
360
- class AppState:
361
- def __init__(self):
362
- self.chat_history = []
363
- self.color_scheme = random.choice(COLOR_SCHEMES)
364
- self.ui_elements = []
365
- self.version = "1.0.0"
366
- self.last_modified = datetime.now()
367
- self.error_log = []
368
- self.notifications = []
369
- self.settings = {
370
- "show_timestamps": True,
371
- "show_version_info": True,
372
- "enable_animations": True,
373
- "theme_name": "Default",
374
- "search_filter": ""
375
- }
376
-
377
- def log_error(self, error_message: str, error_type: str = "general"):
378
- """Log an error with timestamp and type"""
379
- self.error_log.append({
380
- "timestamp": datetime.now(),
381
- "type": error_type,
382
- "message": error_message
383
- })
384
- self.add_notification(f"Error: {error_message}", "error")
385
-
386
- def add_notification(self, message: str, level: str = "info"):
387
- """Add a notification with level (info, warning, error)"""
388
- self.notifications.append({
389
- "timestamp": datetime.now(),
390
- "level": level,
391
- "message": message,
392
- "id": hashlib.md5(f"{message}{datetime.now()}".encode()).hexdigest()[:8]
393
- })
394
-
395
- class StateVersionManager:
396
  def __init__(self):
397
- self.versions: Dict[str, Dict] = {}
398
- self.current_state = AppState()
399
- self.history = []
400
- self.max_versions = 50
401
-
402
- def create_version(self, description: str = "") -> Optional[Dict]:
403
- try:
404
- state_hash = hashlib.sha256(pickle.dumps(self.current_state)).hexdigest()[:8]
405
- version_data = {
406
- "state": copy.deepcopy(self.current_state),
407
- "timestamp": datetime.now(),
408
- "description": description,
409
- "hash": state_hash
410
- }
411
 
412
- if len(self.history) >= self.max_versions:
413
- oldest_version = self.history.pop(0)
414
- self.versions.pop(oldest_version["hash"], None)
415
-
416
- self.versions[version_data["hash"]] = version_data
417
- self.history.append(version_data)
418
- return version_data
419
- except Exception as e:
420
- self.current_state.log_error(f"Failed to create version: {str(e)}", "version_creation")
421
- return None
422
-
423
- def restore_version(self, version_hash: str) -> bool:
424
  try:
425
- if version_hash in self.versions:
426
- self.current_state = copy.deepcopy(self.versions[version_hash]["state"])
427
- self.current_state.add_notification(
428
- f"Successfully restored to version {version_hash[:6]}",
429
- "success"
430
- )
431
- return True
432
- self.current_state.add_notification(
433
- f"Version {version_hash[:6]} not found",
434
- "warning"
435
- )
436
- return False
437
  except Exception as e:
438
- self.current_state.log_error(f"Failed to restore version: {str(e)}", "version_restoration")
439
- return False
440
-
441
- def get_version_list(self) -> List[Dict]:
442
- return [{
443
- "hash": v["hash"],
444
- "label": f"{v['description']} ({v['timestamp'].strftime('%Y-%m-%d %H:%M')})",
445
- "timestamp": v["timestamp"].strftime("%Y-%m-%d %H:%M:%S"),
446
- "description": v["description"]
447
- } for v in self.history]
448
-
449
- # ---------- Chat Application ----------
450
- class EvolvableChatApp:
451
- def __init__(self):
452
- self.state_manager = StateVersionManager()
453
- self.current_gradient = generate_gradient(self.state_manager.current_state.color_scheme)
454
- self.setup_initial_state()
455
- self.typing_indicator_visible = False
456
 
457
- def setup_initial_state(self):
 
458
  try:
459
- initial_state = self.state_manager.current_state
460
- initial_state.ui_elements = [
461
- {"type": "button", "label": "🎨 Change Theme", "action": self.change_theme},
462
- {"type": "button", "label": "πŸ—‘οΈ Clear Chat", "action": self.clear_chat},
463
- {"type": "button", "label": "πŸ“€ Export Chat", "action": self.export_chat},
464
- {"type": "button", "label": "πŸ”„ Reset State", "action": self.reset_state}
465
- ]
466
-
467
- welcome_message = self.format_message(
468
- "system",
469
- "πŸ‘‹ Welcome to the Enhanced Chat Assistant! I'm here to help and learn from our interactions."
470
- )
471
- initial_state.chat_history.append(welcome_message)
472
-
473
- self.state_manager.create_version("Initial state")
474
  except Exception as e:
475
- self.state_manager.current_state.log_error(
476
- f"Failed to setup initial state: {str(e)}",
477
- "initialization"
478
- )
479
 
480
- def update_state(self, new_chat: Optional[Dict] = None, description: str = "") -> Optional[Dict]:
481
- try:
482
- if new_chat:
483
- if self.state_manager.current_state.settings["show_timestamps"]:
484
- timestamp = datetime.now().strftime("%H:%M:%S")
485
- new_chat["content"] = f"[{timestamp}] {new_chat['content']}"
486
- self.state_manager.current_state.chat_history.append(new_chat)
487
-
488
- self.state_manager.current_state.last_modified = datetime.now()
489
- return self.state_manager.create_version(description)
490
- except Exception as e:
491
- self.state_manager.current_state.log_error(
492
- f"Failed to update state: {str(e)}",
493
- "state_update"
494
- )
495
- return None
496
-
497
- def change_theme(self):
498
- try:
499
- current_scheme = self.state_manager.current_state.color_scheme
500
- available_schemes = [scheme for scheme in COLOR_SCHEMES if scheme != current_scheme]
501
- new_scheme = random.choice(available_schemes)
502
-
503
- theme_names = [
504
- "Ocean Breeze", "Desert Sunset", "Forest Dawn", "Coral Reef",
505
- "Neon Nights", "Deep Ocean", "Rose Garden", "Midnight Purple"
506
- ]
507
- theme_name = next(
508
- (name for i, colors in enumerate(COLOR_SCHEMES)
509
- if colors == new_scheme and i < len(theme_names)),
510
- "Custom"
511
- )
512
-
513
- self.state_manager.current_state.color_scheme = new_scheme
514
- self.state_manager.current_state.settings["theme_name"] = theme_name
515
- self.current_gradient = generate_gradient(new_scheme)
516
-
517
- theme_message = self.format_message(
518
- "system",
519
- f"🎨 Theme changed to: {theme_name}"
520
- )
521
- self.update_state(theme_message, f"Changed theme to {theme_name}")
522
-
523
- return self.get_chat_history_tuples(), f"Current Theme: {theme_name}"
524
- except Exception as e:
525
- error_message = self.format_message(
526
- "system",
527
- f"❌ Failed to change theme: {str(e)}"
528
- )
529
- self.update_state(error_message, "Theme change failed")
530
- return self.get_chat_history_tuples(), "Theme change failed"
531
 
532
- def clear_chat(self):
533
- try:
534
- self.state_manager.current_state.chat_history = []
535
- welcome_message = self.format_message(
536
- "system",
537
- "πŸ’« Chat history cleared"
538
- )
539
- self.update_state(welcome_message, "Cleared chat history")
540
- return self.get_chat_history_tuples()
541
- except Exception as e:
542
- self.state_manager.current_state.log_error(
543
- f"Failed to clear chat: {str(e)}",
544
- "clear_chat"
545
- )
546
- return self.get_chat_history_tuples(), self.create_ui()
547
-
548
- def export_chat(self):
549
- try:
550
- chat_export = {
551
- "timestamp": datetime.now().isoformat(),
552
- "version": self.state_manager.current_state.version,
553
- "messages": self.state_manager.current_state.chat_history
554
- }
555
-
556
- export_message = self.format_message(
557
- "system",
558
- "πŸ“€ Chat history exported successfully"
559
- )
560
- self.update_state(export_message, "Exported chat history")
561
-
562
- return (
563
- json.dumps(chat_export, indent=2),
564
- self.get_chat_history_tuples()
565
- )
566
- except Exception as e:
567
- self.state_manager.current_state.log_error(
568
- f"Failed to export chat: {str(e)}",
569
- "export_chat"
570
- )
571
- return "", self.get_chat_history_tuples()
572
-
573
- def search_messages(self, query: str):
574
- try:
575
- if not query:
576
- return self.get_chat_history_tuples()
577
-
578
- filtered_messages = [
579
- msg for msg in self.state_manager.current_state.chat_history
580
- if query.lower() in msg["content"].lower()
581
- ]
582
-
583
- return [(msg["role"] == "user", msg["content"])
584
- for msg in filtered_messages
585
- if msg["role"] in ["user", "assistant"]]
586
- except Exception as e:
587
- self.state_manager.current_state.log_error(
588
- f"Failed to search messages: {str(e)}",
589
- "search_messages"
590
- )
591
- return self.get_chat_history_tuples()
592
-
593
- def reset_state(self):
594
- try:
595
- self.state_manager.current_state = AppState()
596
- self.setup_initial_state()
597
- self.current_gradient = generate_gradient(
598
- self.state_manager.current_state.color_scheme
599
- )
600
- return self.get_chat_history_tuples(), self.create_ui()
601
- except Exception as e:
602
- self.state_manager.current_state.log_error(
603
- f"Failed to reset state: {str(e)}",
604
- "reset_state"
605
- )
606
- return self.get_chat_history_tuples(), self.create_ui()
607
-
608
- def show_examples(self):
609
- """Display example messages and commands"""
610
  try:
611
- # Format examples with better styling
612
- formatted_examples = EXAMPLES.replace(":", ":\n").replace("\n\n", "\n")
613
- examples_message = self.format_message(
614
- "system",
615
- f"πŸ“ Available Commands and Examples\n\n"
616
- f"Here are some example messages you can try:\n\n"
617
- f"{formatted_examples}\n\n"
618
- f"Try any of these messages or commands to explore the chat assistant's capabilities!"
619
- )
620
- self.update_state(examples_message, "Showed examples")
621
- return self.get_chat_history_tuples(), self.create_ui()
622
- except Exception as e:
623
- self.state_manager.current_state.log_error(
624
- f"Failed to show examples: {str(e)}",
625
- "show_examples"
626
- )
627
- return self.get_chat_history_tuples(), self.create_ui()
628
-
629
- def create_ui(self):
630
- with gr.Blocks(css=self.current_gradient) as interface:
631
- with gr.Row(elem_classes="container"):
632
- with gr.Column(scale=2):
633
- gr.Markdown(f"""
634
- # πŸ€– Enhanced Chat Assistant
635
- <p class="theme-text">Current Theme: {self.state_manager.current_state.settings['theme_name']}</p>
636
- """)
637
- with gr.Column(scale=1):
638
- version_dropdown = gr.Dropdown(
639
- label="Version History",
640
- choices=[v["label"] for v in self.state_manager.get_version_list()],
641
- interactive=True
642
- )
643
-
644
- # Main chat area with full width
645
- with gr.Column(scale=1):
646
- chat_window = gr.Chatbot(
647
- value=self.get_chat_history_tuples(),
648
- height=500, # Increased height
649
- elem_classes="chat-window",
650
- type="messages"
651
- )
652
-
653
- # Message input area
654
- with gr.Row():
655
- message_input = gr.Textbox(
656
- placeholder="Type your message...",
657
- scale=4, # Increased scale
658
- container=False
659
- )
660
- submit_btn = gr.Button("Send", scale=1)
661
-
662
- # Search functionality
663
- with gr.Row():
664
- search_input = gr.Textbox(
665
- placeholder="Search messages...",
666
- scale=4, # Increased scale
667
- container=False
668
- )
669
- search_btn = gr.Button("πŸ” Search", scale=1)
670
-
671
- # Floating control panel
672
- with gr.Group(elem_classes="control-panel"):
673
- gr.Markdown("### Control Panel")
674
-
675
- theme_info = gr.Markdown(
676
- f"Current Theme: {self.state_manager.current_state.settings['theme_name']}",
677
- elem_classes="theme-text"
678
- )
679
-
680
- # Control buttons with proper event handling
681
- examples_btn = gr.Button("πŸ“ Show Examples", elem_classes=["control-button"])
682
- change_theme_btn = gr.Button("🎨 Change Theme")
683
- clear_chat_btn = gr.Button("πŸ—‘οΈ Clear Chat")
684
- export_chat_btn = gr.Button("πŸ“€ Export Chat")
685
- reset_state_btn = gr.Button("πŸ”„ Reset State")
686
-
687
- # Event handlers for examples
688
- examples_btn.click(
689
- self.show_examples,
690
- outputs=[chat_window]
691
- )
692
-
693
- export_text = gr.Textbox(
694
- label="Exported Chat Data",
695
- visible=False
696
- )
697
-
698
- # Event handlers for control buttons
699
- change_theme_btn.click(
700
- self.change_theme,
701
- outputs=[chat_window, theme_info]
702
- )
703
-
704
- clear_chat_btn.click(
705
- self.clear_chat,
706
- outputs=[chat_window]
707
- )
708
-
709
- export_chat_btn.click(
710
- self.export_chat,
711
- outputs=[export_text, chat_window]
712
- )
713
-
714
- reset_state_btn.click(
715
- self.reset_state,
716
- outputs=[chat_window]
717
- )
718
-
719
- # Event handlers for message submission
720
- def handle_message(message):
721
- if not message.strip():
722
- return self.get_chat_history_tuples(), ""
723
-
724
- # Add user message
725
- user_message = self.format_message("user", message)
726
- self.update_state(user_message, "User message")
727
-
728
- # Generate and add AI response
729
- response = self.generate_response(message)
730
- ai_message = self.format_message("assistant", response)
731
- self.update_state(ai_message, "AI response")
732
-
733
- return self.get_chat_history_tuples(), ""
734
-
735
- submit_btn.click(
736
- fn=handle_message,
737
- inputs=[message_input],
738
- outputs=[chat_window, message_input]
739
- )
740
-
741
- search_btn.click(
742
- self.search_messages,
743
- inputs=[search_input],
744
- outputs=[chat_window]
745
- )
746
-
747
- version_dropdown.change(
748
- self.restore_version,
749
- inputs=[version_dropdown],
750
- outputs=[chat_window]
751
- )
752
 
753
- return interface
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
754
 
755
- def format_message(self, role: str, content: str) -> Dict[str, str]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
756
  return {
757
- "role": role,
758
- "content": content,
759
- "timestamp": datetime.now().isoformat()
760
  }
 
 
761
 
762
- def get_chat_history_tuples(self):
763
- try:
764
- formatted_messages = self.ensure_message_format(
765
- self.state_manager.current_state.chat_history
766
- )
767
- return [
768
- (msg["role"] == "user", msg["content"]) # Tuple format for Gradio
769
- for msg in formatted_messages
770
- if msg["role"] in ["user", "assistant", "system"]
771
- ]
772
- except Exception as e:
773
- self.state_manager.current_state.log_error(
774
- f"Failed to format chat history: {str(e)}",
775
- "format_history"
776
- )
777
- return []
778
-
779
- def process_message(self, message: str):
780
- """Process a user message and generate a response"""
781
- try:
782
- if not message.strip():
783
- return self.get_chat_history_tuples(), ""
784
-
785
- # Format and add user message
786
- user_message = self.format_message("user", message)
787
- self.update_state(user_message, "User message")
788
-
789
- # Generate and format AI response
790
- response = self.generate_response(message)
791
- ai_message = self.format_message("assistant", response)
792
- self.update_state(ai_message, "AI response")
793
-
794
- # Return updated chat history and clear input
795
- return self.get_chat_history_tuples(), ""
796
- except Exception as e:
797
- self.state_manager.current_state.log_error(
798
- f"Failed to process message: {str(e)}",
799
- "message_processing"
800
- )
801
- error_message = self.format_message(
802
- "system",
803
- "❌ I apologize, but I encountered an error processing your message. Please try again."
804
- )
805
- self.update_state(error_message, "Error processing message")
806
- return self.get_chat_history_tuples(), ""
807
-
808
- def generate_response(self, message: str):
809
  try:
810
- # Enhanced responses with more variety and context
811
- responses = [
812
- "I've analyzed your message and here's what I think:",
813
- "Based on our conversation history, I suggest:",
814
- "That's interesting! Here's my perspective:",
815
- "I've evolved to understand that:",
816
- "Let me share my thoughts on that:",
817
- "After processing your input, I believe:",
818
- "My evolving knowledge suggests:",
819
- "Here's my enhanced response:"
820
- ]
821
-
822
- # Add contextual information
823
- response = random.choice(responses)
824
- if self.state_manager.current_state.settings["show_version_info"]:
825
- response += f" (v{self.state_manager.current_state.version})"
826
-
827
- # Add a random insight or suggestion
828
- insights = [
829
- "Consider exploring this further.",
830
- "This could lead to interesting developments.",
831
- "I'm learning from our interaction.",
832
- "Your input helps me evolve.",
833
- "This adds to my growing understanding."
834
- ]
835
- response += f" {random.choice(insights)}"
836
-
837
- return response
838
  except Exception as e:
839
- self.state_manager.current_state.log_error(
840
- f"Failed to generate response: {str(e)}",
841
- "response_generation"
842
- )
843
- return "I apologize, but I encountered an error generating a response."
 
 
 
 
 
 
 
 
 
844
 
845
- def ensure_message_format(self, messages: List[Any]) -> List[Dict[str, str]]:
846
- formatted_messages = []
847
- for msg in messages:
848
- if isinstance(msg, dict) and "role" in msg and "content" in msg:
849
- formatted_messages.append(msg)
850
- elif isinstance(msg, str):
851
- if msg.startswith("User: "):
852
- formatted_messages.append({"role": "user", "content": msg[6:]})
853
- elif msg.startswith("AI: "):
854
- formatted_messages.append({"role": "assistant", "content": msg[4:]})
855
- elif msg.startswith("System: "):
856
- formatted_messages.append({"role": "system", "content": msg[8:]})
857
- else:
858
- formatted_messages.append({"role": "system", "content": msg})
859
- elif isinstance(msg, (list, tuple)) and len(msg) == 2:
860
- role = "user" if msg[0] else "assistant"
861
- formatted_messages.append({"role": role, "content": msg[1]})
862
- return formatted_messages
 
 
 
 
 
 
 
 
 
 
 
 
 
 
863
 
864
- def restore_version(self, version_label: str):
865
- try:
866
- version_info = next(
867
- (v for v in self.state_manager.get_version_list()
868
- if v["label"] == version_label),
869
- None
870
- )
871
-
872
- if version_info and self.state_manager.restore_version(version_info["hash"]):
873
- self.current_gradient = generate_gradient(
874
- self.state_manager.current_state.color_scheme
875
- )
876
- return self.get_chat_history_tuples()
877
-
878
- return self.get_chat_history_tuples()
879
- except Exception as e:
880
- self.state_manager.current_state.log_error(
881
- f"Failed to restore version: {str(e)}",
882
- "version_restoration"
883
- )
884
- return self.get_chat_history_tuples()
885
 
886
- # ---------- Main Execution ----------
887
  if __name__ == "__main__":
888
- try:
889
- print("Starting Enhanced Chat Assistant...")
890
- print("Initializing application state...")
891
- app = EvolvableChatApp()
892
- print("Creating user interface...")
893
- interface = app.create_ui()
894
- print("Launching interface on http://localhost:8000")
895
- interface.launch(
896
- share=False,
897
- # server_name="0.0.0.0",
898
- # server_port=8000,
899
- show_api=False
900
- )
901
- except Exception as e:
902
- print(f"Error starting application: {str(e)}")
903
- print("Please ensure all requirements are installed: pip install -r requirements.txt")
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
+ import os
3
+ import requests
4
+ from git import Repo, GitCommandError
5
+ from pathlib import Path
6
+ from datetime import datetime
7
+ import shutil
8
+ import time
9
+ import json
10
+ import aiohttp
11
+ import asyncio
12
+ from typing import Dict, Optional, Tuple
13
+ import logging
14
+
15
+ # ========== Configuration ==========
16
+ REPO_OWNER = "enricoros"
17
+ REPO_NAME = "big-agi"
18
+ MIAGI_FORK = "https://github.com/Ig0tU/miagi.git"
19
+ WORKSPACE = Path("/tmp/issue_workspace")
20
+ WORKSPACE.mkdir(exist_ok=True)
21
+ HF_SPACE_API = "https://huggingface.co/api/spaces" # Hypothetical HF Space API
22
+
23
+ # Setup logging
24
+ logging.basicConfig(level=logging.INFO)
25
+ logger = logging.getLogger(__name__)
26
+
27
+ # ========== Enhanced Theme ==========
28
+ theme = gr.themes.Default(
29
+ primary_hue="emerald",
30
+ secondary_hue="zinc",
31
+ radius_size="lg",
32
+ ).set(
33
+ button_primary_background_fill="*primary_500",
34
+ button_primary_background_fill_hover="*primary_400",
35
+ button_primary_text_color="white",
36
+ block_label_background_fill="*primary_500",
37
+ block_label_text_color="white",
38
+ body_background_fill="gray_100",
39
+ )
40
+
41
+ # ========== State Management with Spaces Integration ==========
42
+ class EnhancedIssueManager:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  def __init__(self):
44
+ self.issues: Dict[int, dict] = {}
45
+ self.last_fetched: Optional[datetime] = None
46
+ self.current_issue: Optional[int] = None
47
+ self.repo: Optional[Repo] = None
48
+ self.space_status: Dict = {}
49
+ self.progress_tracker = {}
50
+
51
+ async def fetch_issues(self) -> Tuple[bool, str]:
52
+ if self.last_fetched and (datetime.now() - self.last_fetched).seconds < 300:
53
+ return True, "Using cached issues"
 
 
 
 
54
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  try:
56
+ headers = {"Authorization": f"Bearer {os.environ.get('GITHUB_TOKEN', '')}"}
57
+ async with aiohttp.ClientSession() as session:
58
+ async with session.get(f"https://api.github.com/repos/{REPO_OWNER}/{REPO_NAME}/issues",
59
+ headers=headers) as response:
60
+ response.raise_for_status()
61
+ self.issues = {issue['number']: issue for issue in await response.json()}
62
+ self.last_fetched = datetime.now()
63
+ return True, "Successfully fetched issues"
 
 
 
 
64
  except Exception as e:
65
+ logger.error(f"Error fetching issues: {e}")
66
+ return False, f"Failed to fetch issues: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
+ async def check_space_status(self, issue_number: int) -> Dict:
69
+ """Check if issue relates to a Hugging Face Space"""
70
  try:
71
+ headers = {"Authorization": f"Bearer {os.environ.get('HF_TOKEN', '')}"}
72
+ async with aiohttp.ClientSession() as session:
73
+ async with session.get(f"{HF_SPACE_API}/{REPO_OWNER}/{REPO_NAME}",
74
+ headers=headers) as response:
75
+ if response.status == 200:
76
+ space_data = await response.json()
77
+ self.space_status[issue_number] = space_data
78
+ return {"space_found": True, "data": space_data}
79
+ return {"space_found": False}
 
 
 
 
 
 
80
  except Exception as e:
81
+ logger.error(f"Space check failed: {e}")
82
+ return {"space_found": False, "error": str(e)}
 
 
83
 
84
+ issue_manager = EnhancedIssueManager()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
+ # ========== Core Functions with Space Handling ==========
87
+ async def replicate_issue(issue_number: str) -> Tuple[Dict, str, gr.update]:
88
+ try:
89
+ issue_number = int(issue_number)
90
+ success, fetch_message = await issue_manager.fetch_issues()
91
+ if not success:
92
+ return {"error": f"⚠️ {fetch_message}"}, None, None
93
+
94
+ if issue_number not in issue_manager.issues:
95
+ return {"error": f"❌ Issue #{issue_number} not found"}, None, None
96
+
97
+ issue = issue_manager.issues[issue_number]
98
+ repo_path = WORKSPACE / f"miagi-{issue_number}"
99
+ issue_manager.progress_tracker[issue_number] = {"status": "initializing"}
100
+
101
+ # Check Space status
102
+ space_status = await issue_manager.check_space_status(issue_number)
103
+
104
+ # Clone with progress
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  try:
106
+ issue_manager.progress_tracker[issue_number] = {"status": "cloning"}
107
+ repo = Repo.clone_from(MIAGI_FORK, repo_path, branch="main",
108
+ progress=lambda x: issue_manager.progress_tracker[issue_number].update({"progress": x}))
109
+ except GitCommandError:
110
+ repo = Repo(repo_path)
111
+
112
+ branch_name = f"issue-{issue_number}"
113
+ repo.git.checkout('-b', branch_name)
114
+
115
+ # Handle Space replication if applicable
116
+ issues_dir = repo_path / "issues" / str(issue_number)
117
+ issues_dir.mkdir(parents=True, exist_ok=True)
118
+
119
+ replication_details = {
120
+ "timestamp": datetime.utcnow().isoformat(),
121
+ "space_status": space_status
122
+ }
123
+ (issues_dir / "replication.json").write_text(json.dumps(replication_details))
124
+
125
+ issue_manager.repo = repo
126
+ issue_manager.current_issue = issue_number
127
+
128
+ return {
129
+ "success": f"πŸš€ Replicated issue #{issue_number}",
130
+ "space": space_status,
131
+ "progress": issue_manager.progress_tracker[issue_number]
132
+ }, issue["body"], gr.update(selected=1)
133
+
134
+ except Exception as e:
135
+ logger.error(f"Replication error: {e}")
136
+ return {"error": f"β›” Critical error: {str(e)}"}, None, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
+ async def resolve_issue(resolution_notes: str, space_config: Optional[str] = None) -> Tuple[Dict, gr.update]:
139
+ if not issue_manager.current_issue:
140
+ return {"error": "⚠️ No active issue selected"}, None
141
+
142
+ try:
143
+ repo = issue_manager.repo
144
+ issue_number = issue_manager.current_issue
145
+ resolution_dir = repo.working_dir / "issues" / str(issue_number)
146
+
147
+ timestamp = datetime.utcnow().strftime("%Y%m%d-%H%M%S")
148
+ resolution_data = {
149
+ "notes": resolution_notes,
150
+ "space_config": space_config,
151
+ "resolved_at": timestamp
152
+ }
153
+
154
+ (resolution_dir / f"resolution_{timestamp}.json").write_text(json.dumps(resolution_data))
155
+ (resolution_dir / "RESOLVED").touch()
156
+
157
+ return {"success": "πŸ”§ Resolution saved"}, gr.update(visible=True)
158
+ except Exception as e:
159
+ return {"error": f"β›” Resolution failed: {str(e)}"}, None
160
 
161
+ async def submit_resolution() -> Dict:
162
+ if not issue_manager.current_issue:
163
+ return {"error": "⚠️ No active issue selected"}
164
+
165
+ try:
166
+ repo = issue_manager.repo
167
+ issue_number = issue_manager.current_issue
168
+
169
+ repo.git.add(all=True)
170
+ commit_message = f"Resolve #{issue_number}: {issue_manager.issues[issue_number]['title']}"
171
+ repo.index.commit(commit_message)
172
+
173
+ origin = repo.remote(name='origin')
174
+ branch_name = f"issue-{issue_number}"
175
+ origin.push(refspec=f"{branch_name}:{branch_name}")
176
+
177
+ # Space-specific submission if applicable
178
+ if issue_manager.space_status.get(issue_number, {}).get("space_found"):
179
+ await self._update_space_config(issue_number)
180
+
181
+ pr_url = f"https://github.com/Ig0tU/miagi/compare/{branch_name}?expand=1"
182
  return {
183
+ "success": f"πŸš€ Resolution submitted!\n\n[Review PR]({pr_url})",
184
+ "pr_url": pr_url
 
185
  }
186
+ except Exception as e:
187
+ return {"error": f"β›” Submission failed: {str(e)}"}
188
 
189
+ async def _update_space_config(self, issue_number: int):
190
+ """Update Hugging Face Space configuration"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  try:
192
+ headers = {"Authorization": f"Bearer {os.environ.get('HF_TOKEN', '')}"}
193
+ config = {"issue_number": issue_number, "status": "resolved"}
194
+ async with aiohttp.ClientSession() as session:
195
+ await session.post(
196
+ f"{HF_SPACE_API}/{REPO_OWNER}/{REPO_NAME}/config",
197
+ json=config,
198
+ headers=headers
199
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  except Exception as e:
201
+ logger.error(f"Space config update failed: {e}")
202
+
203
+ # ========== Enhanced UI ==========
204
+ def create_enhanced_ui():
205
+ with gr.Blocks(theme=theme, css="footer {visibility: hidden}") as app:
206
+ gr.Markdown("# πŸš€ Enhanced Repository Manager\n### With Hugging Face Spaces Integration")
207
+
208
+ with gr.Tabs() as tabs:
209
+ with gr.Tab("πŸ” Investigate"):
210
+ with gr.Row():
211
+ issue_input = gr.Number(label="Issue Number")
212
+ replicate_btn = gr.Button("⚑ Replicate", variant="primary")
213
+ status_output = gr.JSON(label="Replication Status")
214
+ progress_bar = gr.Progress()
215
 
216
+ with gr.Tab("πŸ”§ Resolve"):
217
+ with gr.Row():
218
+ with gr.Column():
219
+ issue_desc = gr.Markdown()
220
+ resolution_input = gr.Textbox(lines=8, label="Resolution Notes")
221
+ space_config = gr.Textbox(lines=2, label="Space Config (if applicable)")
222
+ resolve_btn = gr.Button("πŸ’Ύ Save", variant="primary")
223
+ with gr.Column():
224
+ file_explorer = gr.FileExplorer()
225
+
226
+ with gr.Tab("πŸš€ Deploy"):
227
+ deploy_btn = gr.Button("πŸ”₯ Submit", variant="primary")
228
+ pr_output = gr.Markdown()
229
+ space_status = gr.JSON(label="Space Status")
230
+
231
+ # Event handlers with async support
232
+ replicate_btn.click(
233
+ fn=replicate_issue,
234
+ inputs=issue_input,
235
+ outputs=[status_output, issue_desc, tabs]
236
+ )
237
+
238
+ resolve_btn.click(
239
+ fn=resolve_issue,
240
+ inputs=[resolution_input, space_config],
241
+ outputs=[status_output, deploy_btn]
242
+ )
243
+
244
+ deploy_btn.click(
245
+ fn=submit_resolution,
246
+ outputs=[status_output, pr_output, space_status]
247
+ )
248
 
249
+ return app
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
 
251
+ # ========== Execution ==========
252
  if __name__ == "__main__":
253
+ app = create_enhanced_ui()
254
+ app.launch(server_port=7860, share=True)