File size: 14,431 Bytes
5720799
86b116d
6015c25
86b116d
0e216c6
89a4312
86b116d
 
2cb4727
dc6e56f
86b116d
2665582
857c4c0
73ed159
bed2d0a
fc3fdb8
 
f546abb
b5d5e39
 
 
 
 
758943e
f992e80
 
f546abb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8da6264
f546abb
f992e80
 
 
 
fc3fdb8
 
 
 
 
bed2d0a
2cb4727
0757010
1949ac7
2cb4727
0757010
e7063a6
0757010
dc6e56f
972f028
 
0757010
972f028
 
 
0757010
972f028
0757010
5adc6a4
972f028
 
 
5adc6a4
f546abb
 
 
 
 
 
 
 
 
 
 
 
 
972f028
5adc6a4
972f028
 
0757010
fc3fdb8
 
5adc6a4
972f028
fc3fdb8
 
2cb4727
fc3fdb8
 
 
 
d891499
0757010
 
2cb4727
0757010
 
 
 
fc3fdb8
 
 
 
0757010
 
 
 
 
fc3fdb8
 
 
2cb4727
0757010
fc3fdb8
 
0757010
a3e0ade
5adc6a4
b40bdef
fc3fdb8
 
b40bdef
 
fc3fdb8
 
 
 
b40bdef
 
fc3fdb8
 
 
0757010
fc3fdb8
0757010
fc3fdb8
 
 
 
 
2cb4727
0757010
fc3fdb8
0757010
cbac037
a3e0ade
 
0757010
fc3fdb8
2cb4727
0757010
 
a3e0ade
 
2cb4727
 
 
0757010
 
 
 
 
 
 
 
 
2cb4727
8cfe660
a3e0ade
f546abb
a3e0ade
 
 
 
 
 
 
8cfe660
 
 
a3e0ade
 
 
 
 
dc6e56f
8cfe660
 
 
 
dc6e56f
8cfe660
 
a3e0ade
fc3fdb8
8cfe660
fc3fdb8
a3e0ade
fc3fdb8
8cfe660
fc3fdb8
 
 
 
8cfe660
a3e0ade
 
fc3fdb8
8cfe660
fc3fdb8
 
 
 
dc6e56f
a3e0ade
 
fc3fdb8
 
 
 
a3e0ade
2cb4727
a3e0ade
 
 
 
 
 
 
2cb4727
22e5f83
 
fc3fdb8
 
 
 
 
 
 
 
 
 
 
 
 
 
2cb4727
22e5f83
e0ec429
5adc6a4
fc3fdb8
3c74ffa
0757010
1949ac7
2cb4727
86b116d
2cb4727
bed2d0a
 
aba1e9b
bed2d0a
 
 
f992e80
 
2cb4727
 
 
f992e80
a3e0ade
809cf6d
a3e0ade
 
e2ee43d
cbac037
 
f546abb
f992e80
5adc6a4
cbac037
 
bf25842
f992e80
 
 
 
5adc6a4
f992e80
 
2cb4727
0757010
052fcc8
0757010
 
1949ac7
0757010
2cb4727
0757010
 
2cb4727
 
 
a3e0ade
0757010
bed2d0a
2cb4727
d891499
0757010
972f028
 
0757010
 
fc3fdb8
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
import streamlit as st
import time
import os
import sys
import json
from datetime import datetime
from pathlib import Path
sys.path.append(str(Path(__file__).parent))

# Import modules from src.ui.chat_handler import chat_handler
from utils.config import config
from core.session import session_manager
from core.memory import check_redis_health
from core.errors import translate_error
from core.personality import personality
from src.analytics.user_logger import user_logger
from src.analytics.session_analytics import session_analytics
from src.llm.factory import llm_factory
import logging

# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

st.set_page_config(page_title="CosmicCat AI Assistant", page_icon="🐱", layout="wide")

# Initialize session state properly
if "messages" not in st.session_state:
    st.session_state.messages = []
if "is_processing" not in st.session_state:
    st.session_state.is_processing = False
if "ngrok_url_temp" not in st.session_state:
    st.session_state.ngrok_url_temp = st.session_state.get("ngrok_url", "https://7bcc180dffd1.ngrok-free.app")
if "cosmic_mode" not in st.session_state:
    st.session_state.cosmic_mode = True
if "show_welcome" not in st.session_state:
    st.session_state.show_welcome = True
if "last_processed_message" not in st.session_state:
    st.session_state.last_processed_message = ""
if "session_id" not in st.session_state:
    st.session_state.session_id = f"sess_{int(time.time())}_{abs(hash(str(time.time()))) % 10000}"
if "selected_model_value" not in st.session_state:
    st.session_state.selected_model_value = "auto"

# Start session tracking
try:
    session_analytics.start_session_tracking("default_user", st.session_state.session_id)
except Exception as e:
    logger.warning(f"Analytics session tracking failed: {e}")

# Log page view
session_analytics.track_interaction("default_user", st.session_state.session_id, "page_view", {
    "page": "main_chat"
})

# Sidebar
with st.sidebar:
    st.title("🐱 CosmicCat AI Assistant")
    st.markdown("Your personal AI-powered assistant with a cosmic twist.")
    
    # Model selection
    model_options = {
        "Auto Select": "auto",
        "πŸ¦™ Ollama (Local)": "ollama",
        "πŸ€— HF Endpoint": "huggingface"
    }
    
    selected_model_key = st.selectbox(
        "Select Provider",
        options=list(model_options.keys()),
        index=0
    )
    st.session_state.selected_model_value = model_options[selected_model_key]
    
    # Show which provider will actually be used
    actual_provider = "Unknown"
    if st.session_state.selected_model_value == "auto":
        try:
            from src.services.hf_endpoint_monitor import hf_monitor
            if config.hf_token:
                status = hf_monitor.get_endpoint_status()
                if status["available"]:
                    actual_provider = "πŸ€— HF Endpoint"
                elif config.ollama_host:
                    actual_provider = "πŸ¦™ Ollama"
            elif config.ollama_host:
                actual_provider = "πŸ¦™ Ollama"
        except:
            if config.ollama_host:
                actual_provider = "πŸ¦™ Ollama"
    else:
        actual_provider = "πŸ€— HF Endpoint" if st.session_state.selected_model_value == "huggingface" else "πŸ¦™ Ollama"
    
    st.info(f"**Using Provider:** {actual_provider}")
    
    # Log model selection
    session_analytics.track_interaction("default_user", st.session_state.session_id, "model_selection", {
        "selected_model": st.session_state.selected_model_value,
        "actual_provider": actual_provider
    })
    
    # Cosmic mode toggle
    st.session_state.cosmic_mode = st.checkbox("Enable Cosmic Mode", value=st.session_state.cosmic_mode,
                                              on_change=lambda: session_analytics.track_interaction("default_user", st.session_state.session_id, "cosmic_mode_toggle", {
                                                  "enabled": st.session_state.cosmic_mode
                                              }))
    
    st.divider()
    
    # Configuration
    st.subheader("βš™οΈ Configuration")
    ngrok_url_input = st.text_input(
        "Ollama Server URL",
        value=st.session_state.ngrok_url_temp,
        help="Enter your ngrok URL",
        on_change=lambda: session_analytics.track_interaction("default_user", st.session_state.session_id, "url_update", {
            "url_changed": ngrok_url_input != st.session_state.ngrok_url_temp
        })
    )
    
    if ngrok_url_input != st.session_state.ngrok_url_temp:
        st.session_state.ngrok_url_temp = ngrok_url_input
        st.success("βœ… URL updated!")
        session_analytics.track_interaction("default_user", st.session_state.session_id, "url_updated", {
            "new_url": ngrok_url_input
        })
    
    if st.button("πŸ“‘ Test Connection"):
        start_time = time.time()
        session_analytics.track_interaction("default_user", st.session_state.session_id, "test_connection_click")
        try:
            from core.providers.ollama import OllamaProvider
            ollama_provider = OllamaProvider(st.session_state.selected_model_value)
            is_valid = ollama_provider.validate_model()
            end_time = time.time()
            
            if is_valid:
                st.success("βœ… Connection successful!")
                session_analytics.track_interaction("default_user", st.session_state.session_id, "connection_success", {
                    "response_time": end_time - start_time
                })
                user_logger.log_performance_metric("default_user", "connection_test", end_time - start_time)
            else:
                st.error("❌ Model validation failed")
                session_analytics.track_interaction("default_user", st.session_state.session_id, "connection_failed", {
                    "error": "model_validation_failed"
                })
        except Exception as e:
            end_time = time.time()
            st.error(f"❌ Error: {str(e)[:50]}...")
            session_analytics.track_interaction("default_user", st.session_state.session_id, "connection_error", {
                "error": str(e)[:100],
                "response_time": end_time - start_time
            })
            user_logger.log_error("default_user", "connection_test", str(e))
    
    if st.button("πŸ—‘οΈ Clear History"):
        session_analytics.track_interaction("default_user", st.session_state.session_id, "clear_history_click")
        st.session_state.messages = []
        st.session_state.last_processed_message = ""
        # Also clear backend session
        session_manager.clear_session("default_user")
        st.success("History cleared!")
        session_analytics.track_interaction("default_user", st.session_state.session_id, "history_cleared")
    
    st.divider()
    
    # System Status with enhanced HF monitoring
    with st.expander("πŸ” System Status", expanded=True):
        st.subheader("πŸ“Š Status")
        
        # Ollama Status
        try:
            from services.ollama_monitor import check_ollama_status
            ollama_status = check_ollama_status()
            if ollama_status.get("running"):
                st.success("πŸ¦™ Ollama: Running")
            else:
                st.warning("πŸ¦™ Ollama: Not running")
        except:
            st.info("πŸ¦™ Ollama: Unknown")
        
        # HF Endpoint Status (Enhanced with initialization info)
        try:
            from src.services.hf_endpoint_monitor import hf_monitor
            status_message = hf_monitor.get_human_readable_status()
            
            # Display appropriate status icon
            if "🟒" in status_message:
                st.success(status_message)
            elif "🟑" in status_message:
                st.warning(status_message)
            elif "πŸ”΄" in status_message:
                st.error(status_message)
            elif "❌" in status_message:
                st.error(status_message)
            elif "⏳" in status_message:
                st.info(status_message)
            else:
                st.info(status_message)
            
            # Show initialization progress if applicable
            init_progress = hf_monitor.get_initialization_progress()
            if init_progress:
                st.info(init_progress)
            
            # Add wake-up button if scaled to zero or initializing
            if "scaled to zero" in status_message.lower() or "initializing" in status_message.lower():
                if st.button("⚑ Wake Up HF Endpoint", key="wake_up_hf"):
                    session_analytics.track_interaction("default_user", st.session_state.session_id, "wake_up_hf_click")
                    with st.spinner("Attempting to wake up HF endpoint... This may take 2-4 minutes during initialization..."):
                        start_time = time.time()
                        if hf_monitor.attempt_wake_up():
                            end_time = time.time()
                            st.success("βœ… Wake-up request sent! The endpoint should be initializing now. Try your request again in a moment.")
                            session_analytics.track_interaction("default_user", st.session_state.session_id, "hf_wake_up_success", {
                                "response_time": end_time - start_time
                            })
                            user_logger.log_performance_metric("default_user", "hf_wake_up", end_time - start_time)
                            time.sleep(3)
                            st.experimental_rerun()
                        else:
                            end_time = time.time()
                            st.error("❌ Failed to send wake-up request. Please try again or wait for initialization to complete.")
                            session_analytics.track_interaction("default_user", st.session_state.session_id, "hf_wake_up_failed", {
                                "response_time": end_time - start_time
                            })
                            user_logger.log_error("default_user", "hf_wake_up", "Failed to wake up HF endpoint")
        
        except Exception as e:
            st.info(f"πŸ€— HF Endpoint: Error checking status - {str(e)}")
            session_analytics.track_interaction("default_user", st.session_state.session_id, "hf_status_error", {
                "error": str(e)
            })
            user_logger.log_error("default_user", "hf_status_check", str(e))
        
        # Redis Status
        try:
            if check_redis_health():
                st.success("πŸ’Ύ Redis: Connected")
            else:
                st.error("πŸ’Ύ Redis: Disconnected")
        except:
            st.info("πŸ’Ύ Redis: Unknown")
    
    st.divider()
    
    # Feedback Section
    st.subheader("⭐ Feedback")
    rating = st.radio("How would you rate your experience?", [1, 2, 3, 4, 5], horizontal=True)
    feedback_comment = st.text_area("Additional comments (optional):")
    if st.button("Submit Feedback"):
        user_logger.log_feedback("default_user", rating, feedback_comment)
        session_analytics.track_interaction("default_user", st.session_state.session_id, "feedback_submitted", {
            "rating": rating,
            "has_comment": bool(feedback_comment)
        })
        st.success("Thank you for your feedback! πŸ™")
    
    st.divider()
    
    # Debug Info
    st.subheader("πŸ› Debug Info")
    st.markdown(f"**Environment:** {'HF Space' if config.is_hf_space else 'Local'}")
    st.markdown(f"**Model:** {st.session_state.selected_model_value}")
    st.markdown(f"**Session ID:** {st.session_state.session_id}")

# Main interface
st.title("🐱 CosmicCat AI Assistant")
st.markdown("Ask me anything!")

# Welcome message
if st.session_state.show_welcome:
    with st.chat_message("assistant"):
        greeting = personality.get_greeting(cosmic_mode=st.session_state.cosmic_mode)
        st.markdown(greeting)
    st.session_state.show_welcome = False

# Display existing conversation history
for message in st.session_state.get("messages", []):
    with st.chat_message(message["role"]):
        st.markdown(message["content"])
        if "timestamp" in message:
            provider_info = f" (via {message.get('provider', 'ollama')})" if message["role"] == "assistant" else ""
            st.caption(f"πŸ•’ {message['timestamp']}{provider_info}")

# Chat input with enhanced processing
user_input = st.chat_input("Type your message here...", key="chat_input")

# Process message when received
if user_input and user_input.strip():
    # Handle user message display first
    if not st.session_state.get('is_processing', False):
        chat_handler.process_user_message(user_input, st.session_state.selected_model_value)
    else:
        st.warning("Still processing your previous request...")

# Handle AI response processing (triggered after user message display)
if st.session_state.get('is_processing', False) and st.session_state.get('last_processed_message'):
    chat_handler.process_ai_response(
        st.session_state.last_processed_message, 
        st.session_state.selected_model_value
    )

# About tab
st.divider()
tab1, = st.tabs(["ℹ️ About"])

with tab1:
    st.header("ℹ️ About CosmicCat AI Assistant")
    st.markdown("""
    The CosmicCat AI Assistant is a sophisticated conversational AI with a cosmic theme.
    
    ### 🧠 Core Features
    - **Local AI processing** with Ollama models
    - **Persistent memory** using Redis
    - **Space-themed personality** for fun interactions
    - **HF Endpoint integration** for advanced capabilities
    
    ### πŸš€ Cosmic Mode
    When enabled, the AI responds with space-themed language and metaphors.
    
    ### πŸ› οΈ Technical Architecture
    - **Primary model**: HF Endpoint (advanced processing)
    - **Secondary model**: Ollama (local processing)
    - **Memory system**: Redis-based session management
    """)
    
    # Log about page view
    session_analytics.track_interaction("default_user", st.session_state.session_id, "about_page_view")

# End session tracking when app closes
def on_session_end():
    session_analytics.end_session_tracking("default_user", st.session_state.session_id)

# Register cleanup function
import atexit
atexit.register(on_session_end)