GitBot / app.py
acecalisto3's picture
Update app.py
d7ff1c4 verified
raw
history blame
32.2 kB
import copy
import difflib
import hashlib
import pickle
import random
import json
from datetime import datetime
from typing import Dict, Any, List, Callable, Optional
from marshmallow import Schema, fields, ValidationError, EXCLUDE
from semantic_version import Version
import gradio as gr
# ---------- Constants & Helpers ----------
from examples import EXAMPLES
COLOR_SCHEMES = [
["#FF6B6B", "#4ECDC4", "#45B7D1"], # Ocean Breeze
["#2A9D8F", "#E9C46A", "#F4A261"], # Desert Sunset
["#264653", "#2A9D8F", "#E9C46A"], # Forest Dawn
["#F4A261", "#E76F51", "#2A9D8F"], # Coral Reef
["#8338EC", "#3A86FF", "#FF006E"], # Neon Nights
["#06D6A0", "#118AB2", "#073B4C"], # Deep Ocean
["#FFB4A2", "#E5989B", "#B5838D"], # Rose Garden
["#22223B", "#4A4E69", "#9A8C98"] # Midnight Purple
]
def generate_gradient(colors: List[str]) -> str:
"""Generate modern gradient styling with enhanced visual effects"""
return f"""
/* Modern gradient background with enhanced visual effects */
.gradio-container {{
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}}
.container {{
background: linear-gradient(135deg, {colors[0]}, {colors[1]}, {colors[2]});
background-size: 300% 300%;
animation: gradient-shift 15s ease infinite;
border-radius: 12px;
padding: 24px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
margin-bottom: 1rem;
}}
.chat-message {{
padding: 12px 16px;
border-radius: 8px;
margin: 8px 0;
max-width: 85%;
transition: transform 0.2s ease;
}}
.user-message {{
background: rgba(255, 255, 255, 0.95);
color: #333;
margin-left: auto;
border-top-right-radius: 2px;
}}
.assistant-message {{
background: rgba(0, 0, 0, 0.05);
color: #fff;
margin-right: auto;
border-top-left-radius: 2px;
}}
.system-message {{
background: rgba(0, 0, 0, 0.2);
color: #fff;
margin: 8px auto;
font-style: italic;
text-align: center;
}}
.notification {{
position: fixed;
top: 20px;
right: 20px;
padding: 12px 24px;
border-radius: 8px;
background: rgba(0, 0, 0, 0.8);
color: white;
z-index: 1000;
animation: slide-in 0.3s ease;
}}
.typing-indicator {{
display: inline-block;
padding: 8px 16px;
background: rgba(0, 0, 0, 0.1);
border-radius: 16px;
color: rgba(255, 255, 255, 0.8);
font-style: italic;
margin: 8px 0;
}}
.control-panel {{
background: rgba(255, 255, 255, 0.2);
border-radius: 12px;
padding: 20px;
position: fixed;
right: -250px;
top: 50%;
transform: translateY(-50%);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
z-index: 1000;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
width: 220px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
pointer-events: auto;
}}
/* Make the panel accessible via keyboard focus */
.control-panel:focus-within,
.control-panel:hover {{
right: 20px;
outline: none;
}}
/* Ensure the panel is keyboard navigable */
.control-panel button:focus {{
outline: 2px solid rgba(255, 255, 255, 0.5);
outline-offset: 2px;
}}
/* Improve button hover states */
.control-panel button:hover {{
background: rgba(255, 255, 255, 0.25);
transform: translateX(5px);
}}
/* Make the panel accessible via keyboard focus */
.control-panel:hover,
.control-panel:focus-within {{
right: 20px;
outline: none;
background: rgba(255, 255, 255, 0.3);
box-shadow: 0 6px 25px rgba(0, 0, 0, 0.3);
}}
.control-panel:hover ~ .chat-window {{
transform: translateX(-110px);
}}
.control-panel::before {{
content: "☰ Settings";
position: fixed;
right: 0;
top: 50%;
transform: translateY(-50%);
background: rgba(255, 255, 255, 0.5);
padding: 15px 35px 15px 25px;
border-radius: 12px 0 0 12px;
font-size: 16px;
font-weight: bold;
color: white;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: -2px 0 15px rgba(0, 0, 0, 0.2);
z-index: 999;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
white-space: nowrap;
border: 1px solid rgba(255, 255, 255, 0.3);
border-right: none;
animation: pulse 2s infinite;
}}
.control-panel:hover::before {{
opacity: 0;
transform: translateY(-50%) translateX(-20px);
}}
.control-panel:hover ~ .chat-window {{
transform: translateX(-110px);
transition: transform 0.3s ease;
}}
.control-panel:hover::before {{
opacity: 0;
transform: translateY(-50%) translateX(-20px);
}}
/* Menu trigger tab with pulsing effect */
.control-panel::before {{
content: "☰ Settings";
position: fixed;
right: 0;
top: 50%;
transform: translateY(-50%);
background: rgba(255, 255, 255, 0.5);
padding: 15px 35px 15px 25px;
border-radius: 12px 0 0 12px;
font-size: 16px;
font-weight: bold;
color: white;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: -2px 0 15px rgba(0, 0, 0, 0.2);
z-index: 999;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
white-space: nowrap;
border: 1px solid rgba(255, 255, 255, 0.3);
border-right: none;
animation: pulse 2s infinite;
}}
.control-panel:hover::before {{
background: rgba(255, 255, 255, 0.6);
padding-right: 45px;
transform: translateY(-50%) translateX(-10px);
}}
/* Add hover trigger area */
.control-panel::after {{
content: "";
position: fixed;
right: 0;
top: 0;
width: 100px; /* Even wider hover area */
height: 100%;
z-index: 998;
background: linear-gradient(to right, transparent, rgba(255, 255, 255, 0.05));
pointer-events: none; /* Allow clicks to pass through */
}}
/* Ensure the menu is interactive */
.control-panel:hover {{
pointer-events: auto;
}}
/* Hide trigger area when menu is open */
.control-panel:hover::after {{
opacity: 0;
width: 0;
}}
@keyframes pulse {{
0% {{ box-shadow: -2px 0 15px rgba(0, 0, 0, 0.2); }}
50% {{ box-shadow: -2px 0 20px rgba(255, 255, 255, 0.3); }}
100% {{ box-shadow: -2px 0 15px rgba(0, 0, 0, 0.2); }}
}}
.control-panel:hover::before {{
opacity: 0;
transform: translateY(-50%) translateX(-10px);
pointer-events: none;
background: rgba(255, 255, 255, 0.5);
padding-right: 35px;
}}
.control-panel:not(:hover)::before {{
opacity: 1;
pointer-events: auto;
}}
/* Menu trigger tab */
.control-panel::before {{
content: "☰ Settings →";
position: fixed;
right: 0;
top: 50%;
transform: translateY(-50%);
background: rgba(255, 255, 255, 0.4);
padding: 15px 25px;
border-radius: 12px 0 0 12px;
font-size: 16px;
font-weight: bold;
color: white;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: -2px 0 15px rgba(0, 0, 0, 0.2);
z-index: 999;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
white-space: nowrap;
border: 1px solid rgba(255, 255, 255, 0.2);
border-right: none;
}}
.control-panel:hover::before {{
background: rgba(255, 255, 255, 0.5);
padding-right: 35px;
opacity: 0;
transform: translateY(-50%) translateX(-10px);
}}
.control-panel button {{
width: 100%;
margin: 8px 0;
transition: all 0.2s ease;
}}
.control-panel button:hover {{
transform: translateX(5px);
background: rgba(255, 255, 255, 0.1);
}}
button {{
transition: all 0.2s ease;
padding: 12px 20px;
background: rgba(255, 255, 255, 0.2);
border: none;
border-radius: 8px;
color: white;
font-size: 14px;
font-weight: 500;
cursor: pointer;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
margin: 5px 0;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
}}
button[aria-label="Show Examples"] {{
background: rgba(255, 255, 255, 0.3);
font-weight: 600;
border: 1px solid rgba(255, 255, 255, 0.3);
}}
button[aria-label="Show Examples"]:hover {{
background: rgba(255, 255, 255, 0.4);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}}
button:hover {{
transform: translateX(5px);
background: rgba(255, 255, 255, 0.3);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}}
button:active {{
transform: translateX(2px);
background: rgba(255, 255, 255, 0.25);
}}
@keyframes gradient-shift {{
0% {{ background-position: 0% 50%; }}
50% {{ background-position: 100% 50%; }}
100% {{ background-position: 0% 50%; }}
}}
@keyframes slide-in {{
from {{ transform: translateX(100%); opacity: 0; }}
to {{ transform: translateX(0); opacity: 1; }}
}}
/* Responsive adjustments */
@media (max-width: 768px) {{
.container {{ padding: 16px; }}
.chat-message {{ max-width: 95%; }}
}}
"""
# ---------- Core System ----------
class AppState:
def __init__(self):
self.chat_history = []
self.color_scheme = random.choice(COLOR_SCHEMES)
self.ui_elements = []
self.version = "1.0.0"
self.last_modified = datetime.now()
self.error_log = []
self.notifications = []
self.settings = {
"show_timestamps": True,
"show_version_info": True,
"enable_animations": True,
"theme_name": "Default",
"search_filter": ""
}
def log_error(self, error_message: str, error_type: str = "general"):
"""Log an error with timestamp and type"""
self.error_log.append({
"timestamp": datetime.now(),
"type": error_type,
"message": error_message
})
self.add_notification(f"Error: {error_message}", "error")
def add_notification(self, message: str, level: str = "info"):
"""Add a notification with level (info, warning, error)"""
self.notifications.append({
"timestamp": datetime.now(),
"level": level,
"message": message,
"id": hashlib.md5(f"{message}{datetime.now()}".encode()).hexdigest()[:8]
})
class StateVersionManager:
def __init__(self):
self.versions: Dict[str, Dict] = {}
self.current_state = AppState()
self.history = []
self.max_versions = 50
def create_version(self, description: str = "") -> Optional[Dict]:
try:
state_hash = hashlib.sha256(pickle.dumps(self.current_state)).hexdigest()[:8]
version_data = {
"state": copy.deepcopy(self.current_state),
"timestamp": datetime.now(),
"description": description,
"hash": state_hash
}
if len(self.history) >= self.max_versions:
oldest_version = self.history.pop(0)
self.versions.pop(oldest_version["hash"], None)
self.versions[version_data["hash"]] = version_data
self.history.append(version_data)
return version_data
except Exception as e:
self.current_state.log_error(f"Failed to create version: {str(e)}", "version_creation")
return None
def restore_version(self, version_hash: str) -> bool:
try:
if version_hash in self.versions:
self.current_state = copy.deepcopy(self.versions[version_hash]["state"])
self.current_state.add_notification(
f"Successfully restored to version {version_hash[:6]}",
"success"
)
return True
self.current_state.add_notification(
f"Version {version_hash[:6]} not found",
"warning"
)
return False
except Exception as e:
self.current_state.log_error(f"Failed to restore version: {str(e)}", "version_restoration")
return False
def get_version_list(self) -> List[Dict]:
return [{
"hash": v["hash"],
"label": f"{v['description']} ({v['timestamp'].strftime('%Y-%m-%d %H:%M')})",
"timestamp": v["timestamp"].strftime("%Y-%m-%d %H:%M:%S"),
"description": v["description"]
} for v in self.history]
# ---------- Chat Application ----------
class EvolvableChatApp:
def __init__(self):
self.state_manager = StateVersionManager()
self.current_gradient = generate_gradient(self.state_manager.current_state.color_scheme)
self.setup_initial_state()
self.typing_indicator_visible = False
def setup_initial_state(self):
try:
initial_state = self.state_manager.current_state
initial_state.ui_elements = [
{"type": "button", "label": "🎨 Change Theme", "action": self.change_theme},
{"type": "button", "label": "🗑️ Clear Chat", "action": self.clear_chat},
{"type": "button", "label": "📤 Export Chat", "action": self.export_chat},
{"type": "button", "label": "🔄 Reset State", "action": self.reset_state}
]
welcome_message = self.format_message(
"system",
"👋 Welcome to the Enhanced Chat Assistant! I'm here to help and learn from our interactions."
)
initial_state.chat_history.append(welcome_message)
self.state_manager.create_version("Initial state")
except Exception as e:
self.state_manager.current_state.log_error(
f"Failed to setup initial state: {str(e)}",
"initialization"
)
def update_state(self, new_chat: Optional[Dict] = None, description: str = "") -> Optional[Dict]:
try:
if new_chat:
if self.state_manager.current_state.settings["show_timestamps"]:
timestamp = datetime.now().strftime("%H:%M:%S")
new_chat["content"] = f"[{timestamp}] {new_chat['content']}"
self.state_manager.current_state.chat_history.append(new_chat)
self.state_manager.current_state.last_modified = datetime.now()
return self.state_manager.create_version(description)
except Exception as e:
self.state_manager.current_state.log_error(
f"Failed to update state: {str(e)}",
"state_update"
)
return None
def change_theme(self):
try:
current_scheme = self.state_manager.current_state.color_scheme
available_schemes = [scheme for scheme in COLOR_SCHEMES if scheme != current_scheme]
new_scheme = random.choice(available_schemes)
theme_names = [
"Ocean Breeze", "Desert Sunset", "Forest Dawn", "Coral Reef",
"Neon Nights", "Deep Ocean", "Rose Garden", "Midnight Purple"
]
theme_name = next(
(name for i, colors in enumerate(COLOR_SCHEMES)
if colors == new_scheme and i < len(theme_names)),
"Custom"
)
self.state_manager.current_state.color_scheme = new_scheme
self.state_manager.current_state.settings["theme_name"] = theme_name
self.current_gradient = generate_gradient(new_scheme)
theme_message = self.format_message(
"system",
f"🎨 Theme changed to: {theme_name}"
)
self.update_state(theme_message, f"Changed theme to {theme_name}")
return self.get_chat_history_tuples(), f"Current Theme: {theme_name}"
except Exception as e:
error_message = self.format_message(
"system",
f"❌ Failed to change theme: {str(e)}"
)
self.update_state(error_message, "Theme change failed")
return self.get_chat_history_tuples(), "Theme change failed"
def clear_chat(self):
try:
self.state_manager.current_state.chat_history = []
welcome_message = self.format_message(
"system",
"💫 Chat history cleared"
)
self.update_state(welcome_message, "Cleared chat history")
return self.get_chat_history_tuples()
except Exception as e:
self.state_manager.current_state.log_error(
f"Failed to clear chat: {str(e)}",
"clear_chat"
)
return self.get_chat_history_tuples(), self.create_ui()
def export_chat(self):
try:
chat_export = {
"timestamp": datetime.now().isoformat(),
"version": self.state_manager.current_state.version,
"messages": self.state_manager.current_state.chat_history
}
export_message = self.format_message(
"system",
"📤 Chat history exported successfully"
)
self.update_state(export_message, "Exported chat history")
return (
json.dumps(chat_export, indent=2),
self.get_chat_history_tuples()
)
except Exception as e:
self.state_manager.current_state.log_error(
f"Failed to export chat: {str(e)}",
"export_chat"
)
return "", self.get_chat_history_tuples()
def search_messages(self, query: str):
try:
if not query:
return self.get_chat_history_tuples()
filtered_messages = [
msg for msg in self.state_manager.current_state.chat_history
if query.lower() in msg["content"].lower()
]
return [(msg["role"] == "user", msg["content"])
for msg in filtered_messages
if msg["role"] in ["user", "assistant"]]
except Exception as e:
self.state_manager.current_state.log_error(
f"Failed to search messages: {str(e)}",
"search_messages"
)
return self.get_chat_history_tuples()
def reset_state(self):
try:
self.state_manager.current_state = AppState()
self.setup_initial_state()
self.current_gradient = generate_gradient(
self.state_manager.current_state.color_scheme
)
return self.get_chat_history_tuples(), self.create_ui()
except Exception as e:
self.state_manager.current_state.log_error(
f"Failed to reset state: {str(e)}",
"reset_state"
)
return self.get_chat_history_tuples(), self.create_ui()
def show_examples(self):
"""Display example messages and commands"""
try:
# Format examples with better styling
formatted_examples = EXAMPLES.replace(":", ":\n").replace("\n\n", "\n")
examples_message = self.format_message(
"system",
f"📝 Available Commands and Examples\n\n"
f"Here are some example messages you can try:\n\n"
f"{formatted_examples}\n\n"
f"Try any of these messages or commands to explore the chat assistant's capabilities!"
)
self.update_state(examples_message, "Showed examples")
return self.get_chat_history_tuples(), self.create_ui()
except Exception as e:
self.state_manager.current_state.log_error(
f"Failed to show examples: {str(e)}",
"show_examples"
)
return self.get_chat_history_tuples(), self.create_ui()
def create_ui(self):
with gr.Blocks(css=self.current_gradient) as interface:
with gr.Row(elem_classes="container"):
with gr.Column(scale=2):
gr.Markdown(f"""
# 🤖 Enhanced Chat Assistant
<p class="theme-text">Current Theme: {self.state_manager.current_state.settings['theme_name']}</p>
""")
with gr.Column(scale=1):
version_dropdown = gr.Dropdown(
label="Version History",
choices=[v["label"] for v in self.state_manager.get_version_list()],
interactive=True
)
# Main chat area with full width
with gr.Column(scale=1):
chat_window = gr.Chatbot(
value=self.get_chat_history_tuples(),
height=500, # Increased height
elem_classes="chat-window",
type="messages"
)
# Message input area
with gr.Row():
message_input = gr.Textbox(
placeholder="Type your message...",
scale=4, # Increased scale
container=False
)
submit_btn = gr.Button("Send", scale=1)
# Search functionality
with gr.Row():
search_input = gr.Textbox(
placeholder="Search messages...",
scale=4, # Increased scale
container=False
)
search_btn = gr.Button("🔍 Search", scale=1)
# Floating control panel
with gr.Group(elem_classes="control-panel"):
gr.Markdown("### Control Panel")
theme_info = gr.Markdown(
f"Current Theme: {self.state_manager.current_state.settings['theme_name']}",
elem_classes="theme-text"
)
# Control buttons with proper event handling
examples_btn = gr.Button("📝 Show Examples", elem_classes=["control-button"])
change_theme_btn = gr.Button("🎨 Change Theme")
clear_chat_btn = gr.Button("🗑️ Clear Chat")
export_chat_btn = gr.Button("📤 Export Chat")
reset_state_btn = gr.Button("🔄 Reset State")
# Event handlers for examples
examples_btn.click(
self.show_examples,
outputs=[chat_window]
)
export_text = gr.Textbox(
label="Exported Chat Data",
visible=False
)
# Event handlers for control buttons
change_theme_btn.click(
self.change_theme,
outputs=[chat_window, theme_info]
)
clear_chat_btn.click(
self.clear_chat,
outputs=[chat_window]
)
export_chat_btn.click(
self.export_chat,
outputs=[export_text, chat_window]
)
reset_state_btn.click(
self.reset_state,
outputs=[chat_window]
)
# Event handlers for message submission
def handle_message(message):
if not message.strip():
return self.get_chat_history_tuples(), ""
# Add user message
user_message = self.format_message("user", message)
self.update_state(user_message, "User message")
# Generate and add AI response
response = self.generate_response(message)
ai_message = self.format_message("assistant", response)
self.update_state(ai_message, "AI response")
return self.get_chat_history_tuples(), ""
submit_btn.click(
fn=handle_message,
inputs=[message_input],
outputs=[chat_window, message_input]
)
search_btn.click(
self.search_messages,
inputs=[search_input],
outputs=[chat_window]
)
version_dropdown.change(
self.restore_version,
inputs=[version_dropdown],
outputs=[chat_window]
)
return interface
def format_message(self, role: str, content: str) -> Dict[str, str]:
return {
"role": role,
"content": content,
"timestamp": datetime.now().isoformat()
}
def get_chat_history_tuples(self):
try:
formatted_messages = self.ensure_message_format(
self.state_manager.current_state.chat_history
)
return [
(msg["role"] == "user", msg["content"]) # Tuple format for Gradio
for msg in formatted_messages
if msg["role"] in ["user", "assistant", "system"]
]
except Exception as e:
self.state_manager.current_state.log_error(
f"Failed to format chat history: {str(e)}",
"format_history"
)
return []
def process_message(self, message: str):
"""Process a user message and generate a response"""
try:
if not message.strip():
return self.get_chat_history_tuples(), ""
# Format and add user message
user_message = self.format_message("user", message)
self.update_state(user_message, "User message")
# Generate and format AI response
response = self.generate_response(message)
ai_message = self.format_message("assistant", response)
self.update_state(ai_message, "AI response")
# Return updated chat history and clear input
return self.get_chat_history_tuples(), ""
except Exception as e:
self.state_manager.current_state.log_error(
f"Failed to process message: {str(e)}",
"message_processing"
)
error_message = self.format_message(
"system",
"❌ I apologize, but I encountered an error processing your message. Please try again."
)
self.update_state(error_message, "Error processing message")
return self.get_chat_history_tuples(), ""
def generate_response(self, message: str):
try:
# Enhanced responses with more variety and context
responses = [
"I've analyzed your message and here's what I think:",
"Based on our conversation history, I suggest:",
"That's interesting! Here's my perspective:",
"I've evolved to understand that:",
"Let me share my thoughts on that:",
"After processing your input, I believe:",
"My evolving knowledge suggests:",
"Here's my enhanced response:"
]
# Add contextual information
response = random.choice(responses)
if self.state_manager.current_state.settings["show_version_info"]:
response += f" (v{self.state_manager.current_state.version})"
# Add a random insight or suggestion
insights = [
"Consider exploring this further.",
"This could lead to interesting developments.",
"I'm learning from our interaction.",
"Your input helps me evolve.",
"This adds to my growing understanding."
]
response += f" {random.choice(insights)}"
return response
except Exception as e:
self.state_manager.current_state.log_error(
f"Failed to generate response: {str(e)}",
"response_generation"
)
return "I apologize, but I encountered an error generating a response."
def ensure_message_format(self, messages: List[Any]) -> List[Dict[str, str]]:
formatted_messages = []
for msg in messages:
if isinstance(msg, dict) and "role" in msg and "content" in msg:
formatted_messages.append(msg)
elif isinstance(msg, str):
if msg.startswith("User: "):
formatted_messages.append({"role": "user", "content": msg[6:]})
elif msg.startswith("AI: "):
formatted_messages.append({"role": "assistant", "content": msg[4:]})
elif msg.startswith("System: "):
formatted_messages.append({"role": "system", "content": msg[8:]})
else:
formatted_messages.append({"role": "system", "content": msg})
elif isinstance(msg, (list, tuple)) and len(msg) == 2:
role = "user" if msg[0] else "assistant"
formatted_messages.append({"role": role, "content": msg[1]})
return formatted_messages
def restore_version(self, version_label: str):
try:
version_info = next(
(v for v in self.state_manager.get_version_list()
if v["label"] == version_label),
None
)
if version_info and self.state_manager.restore_version(version_info["hash"]):
self.current_gradient = generate_gradient(
self.state_manager.current_state.color_scheme
)
return self.get_chat_history_tuples()
return self.get_chat_history_tuples()
except Exception as e:
self.state_manager.current_state.log_error(
f"Failed to restore version: {str(e)}",
"version_restoration"
)
return self.get_chat_history_tuples()
# ---------- Main Execution ----------
if __name__ == "__main__":
try:
print("Starting Enhanced Chat Assistant...")
print("Initializing application state...")
app = EvolvableChatApp()
print("Creating user interface...")
interface = app.create_ui()
print("Launching interface on http://localhost:8000")
interface.launch(
share=False,
# server_name="0.0.0.0",
# server_port=8000,
show_api=False
)
except Exception as e:
print(f"Error starting application: {str(e)}")
print("Please ensure all requirements are installed: pip install -r requirements.txt")