Spaces:
Runtime error
Runtime error
| """Main application entry point for the multi-language chat agent.""" | |
| import os | |
| from flask import Flask | |
| from flask_socketio import SocketIO | |
| from flask_session import Session | |
| import redis | |
| from config import config | |
| from chat_agent.models.base import db | |
| from chat_agent.utils.logging_config import setup_logging | |
| from chat_agent.utils.error_handler import set_error_handler, ErrorHandler | |
| from chat_agent.utils.connection_pool import initialize_connection_pools, get_connection_pool_manager | |
| from chat_agent.services.cache_service import initialize_cache_service, get_cache_service | |
| from chat_agent.utils.response_optimization import ResponseMiddleware | |
| # Initialize extensions | |
| socketio = SocketIO() | |
| session = Session() | |
| def create_app(config_name=None): | |
| """Application factory pattern.""" | |
| if config_name is None: | |
| config_name = os.getenv('FLASK_ENV', 'development') | |
| app = Flask(__name__) | |
| app.config.from_object(config[config_name]) | |
| # Setup comprehensive logging | |
| loggers = setup_logging("chat_agent", app.config.get('LOG_LEVEL', 'INFO')) | |
| app.logger = loggers['main'] | |
| # Setup global error handler | |
| error_handler = ErrorHandler(loggers['error']) | |
| set_error_handler(error_handler) | |
| app.logger.info("Chat agent application starting", extra={ | |
| 'config': config_name, | |
| 'debug': app.config.get('DEBUG', False), | |
| 'logging_level': app.config.get('LOG_LEVEL', 'INFO') | |
| }) | |
| # Initialize connection pools for performance optimization | |
| database_url = app.config.get('SQLALCHEMY_DATABASE_URI') | |
| redis_url = app.config.get('REDIS_URL') | |
| connection_pool_manager = initialize_connection_pools(database_url, redis_url) | |
| # Configure SQLAlchemy to use connection pool | |
| if connection_pool_manager: | |
| app.config['SQLALCHEMY_ENGINE_OPTIONS'] = { | |
| 'pool_size': int(os.getenv('DB_POOL_SIZE', '10')), | |
| 'max_overflow': int(os.getenv('DB_MAX_OVERFLOW', '20')), | |
| 'pool_recycle': int(os.getenv('DB_POOL_RECYCLE', '3600')), | |
| 'pool_pre_ping': True, | |
| 'pool_timeout': int(os.getenv('DB_POOL_TIMEOUT', '30')) | |
| } | |
| # Initialize extensions with app | |
| db.init_app(app) | |
| socketio.init_app(app, cors_allowed_origins="*") | |
| # Initialize response optimization middleware | |
| ResponseMiddleware(app) | |
| # Configure Redis for sessions and caching (if available) | |
| redis_client = None | |
| if redis_url and redis_url != 'None': | |
| try: | |
| # Use connection pool manager's Redis client if available | |
| if connection_pool_manager: | |
| redis_client = connection_pool_manager.get_redis_client() | |
| else: | |
| redis_client = redis.from_url(redis_url) | |
| if redis_client: | |
| redis_client.ping() # Test connection | |
| app.config['SESSION_REDIS'] = redis_client | |
| session.init_app(app) | |
| app.logger.info("Redis connection established for sessions and caching") | |
| else: | |
| raise Exception("Redis client not available") | |
| except Exception as e: | |
| app.logger.warning(f"Redis connection failed: {e}. Sessions will use filesystem.") | |
| app.config['SESSION_TYPE'] = 'filesystem' | |
| session.init_app(app) | |
| redis_client = None | |
| else: | |
| app.logger.info("Redis disabled. Using filesystem sessions.") | |
| app.config['SESSION_TYPE'] = 'filesystem' | |
| session.init_app(app) | |
| # Initialize cache service with Redis client | |
| cache_service = initialize_cache_service(redis_client) | |
| app.logger.info(f"Cache service initialized", extra={ | |
| 'redis_enabled': bool(redis_client) | |
| }) | |
| # Register API blueprints | |
| from chat_agent.api import chat_bp, create_limiter, setup_error_handlers, RequestLoggingMiddleware | |
| from chat_agent.api.health import health_bp | |
| from chat_agent.api.performance_routes import performance_bp | |
| app.register_blueprint(chat_bp) | |
| app.register_blueprint(health_bp) | |
| app.register_blueprint(performance_bp) | |
| # Configure rate limiting | |
| limiter = create_limiter(app) | |
| if redis_url and redis_url != 'None': | |
| limiter.storage_uri = redis_url | |
| # If no Redis, limiter will use in-memory storage (with warning) | |
| # Setup error handlers | |
| setup_error_handlers(app) | |
| # Setup request logging middleware | |
| RequestLoggingMiddleware(app) | |
| # Add chat interface route | |
| def chat_interface(): | |
| """Serve the chat interface.""" | |
| from flask import render_template | |
| return render_template('chat.html') | |
| # Initialize real chat agent services | |
| from chat_agent.services.groq_client import GroqClient | |
| from chat_agent.services.language_context import LanguageContextManager | |
| from chat_agent.services.session_manager import SessionManager | |
| from chat_agent.services.chat_history import ChatHistoryManager | |
| from chat_agent.services.chat_agent import ChatAgent | |
| from chat_agent.services.programming_assistance import ProgrammingAssistanceService | |
| # Initialize services | |
| try: | |
| # Initialize Redis client | |
| redis_url = app.config.get('REDIS_URL', 'redis://localhost:6379/0') | |
| redis_client = redis.from_url(redis_url) | |
| # Test Redis connection | |
| redis_client.ping() | |
| print("✅ Redis connection successful") | |
| groq_client = GroqClient() | |
| language_context_manager = LanguageContextManager() | |
| session_manager = SessionManager(redis_client) | |
| chat_history_manager = ChatHistoryManager(redis_client) | |
| programming_assistance_service = ProgrammingAssistanceService() | |
| # Initialize main chat agent | |
| chat_agent = ChatAgent( | |
| groq_client=groq_client, | |
| language_context_manager=language_context_manager, | |
| session_manager=session_manager, | |
| chat_history_manager=chat_history_manager, | |
| programming_assistance_service=programming_assistance_service | |
| ) | |
| print("✅ Chat agent services initialized successfully") | |
| except Exception as e: | |
| print(f"⚠️ Error initializing chat agent services: {e}") | |
| print("🔄 Falling back to demo mode") | |
| chat_agent = None | |
| # Store session mapping for WebSocket connections | |
| websocket_sessions = {} | |
| # Initialize WebSocket handlers for chat interface | |
| def handle_connect(auth=None): | |
| """Handle WebSocket connection for chat interface.""" | |
| from flask_socketio import emit | |
| from flask import request | |
| import uuid | |
| try: | |
| # Create a new session for this connection | |
| user_id = f"user_{request.sid}" # Use socket ID as user ID for demo | |
| if chat_agent and session_manager: | |
| session = session_manager.create_session(user_id, language='python') | |
| websocket_sessions[request.sid] = session.id | |
| emit('connection_status', { | |
| 'status': 'connected', | |
| 'session_id': session.id, | |
| 'language': session.language, | |
| 'message_count': session.message_count, | |
| 'timestamp': datetime.now().isoformat() | |
| }) | |
| print(f"WebSocket connected: session={session.id}, user={user_id}") | |
| else: | |
| # Fallback to demo mode | |
| session_id = str(uuid.uuid4()) | |
| websocket_sessions[request.sid] = session_id | |
| emit('connection_status', { | |
| 'status': 'connected', | |
| 'session_id': session_id, | |
| 'language': 'python', | |
| 'message_count': 0, | |
| 'timestamp': datetime.now().isoformat() | |
| }) | |
| print(f"WebSocket connected (demo mode): session={session_id}, user={user_id}") | |
| except Exception as e: | |
| print(f"Error connecting WebSocket: {e}") | |
| emit('error', {'message': 'Connection failed', 'code': 'CONNECTION_ERROR'}) | |
| def handle_disconnect(reason=None): | |
| """Handle WebSocket disconnection.""" | |
| from flask import request | |
| # Clean up session mapping | |
| if request.sid in websocket_sessions: | |
| session_id = websocket_sessions[request.sid] | |
| del websocket_sessions[request.sid] | |
| print(f"WebSocket disconnected: session={session_id}") | |
| else: | |
| print("WebSocket disconnected") | |
| def handle_message(data): | |
| """Handle chat messages using real chat agent.""" | |
| from flask_socketio import emit | |
| from flask import request | |
| try: | |
| print(f"Received message: {data}") | |
| # Get session ID for this connection | |
| if request.sid not in websocket_sessions: | |
| emit('error', {'message': 'No active session', 'code': 'NO_SESSION'}) | |
| return | |
| session_id = websocket_sessions[request.sid] | |
| content = data.get('content', '').strip() | |
| language = data.get('language', 'python') | |
| if not content: | |
| emit('error', {'message': 'Empty message received', 'code': 'EMPTY_MESSAGE'}) | |
| return | |
| # Process message with real chat agent | |
| emit('response_start', { | |
| 'session_id': session_id, | |
| 'language': language, | |
| 'timestamp': datetime.now().isoformat() | |
| }) | |
| try: | |
| if chat_agent: | |
| # Use the real chat agent to process the message | |
| print(f"🤖 Processing message with chat agent: '{content}' (language: {language})") | |
| result = chat_agent.process_message(session_id, content, language) | |
| # Extract response content from the result dictionary | |
| if isinstance(result, dict) and 'response' in result: | |
| response = result['response'] | |
| print(f"✅ Chat agent response: {response[:100]}..." if len(response) > 100 else f"✅ Chat agent response: {response}") | |
| else: | |
| print(f"✅ Unexpected response format: {type(result)}, value: {result}") | |
| response = str(result) | |
| else: | |
| # Fallback response if chat agent is not available | |
| response = f"I understand you're asking about: '{content}'. I'm currently in demo mode, but I can help you with {language} programming concepts, debugging, and best practices. The full AI-powered assistant will provide more detailed responses." | |
| # Ensure response is a string before processing | |
| if not isinstance(response, str): | |
| response = str(response) | |
| # Send response in chunks to simulate streaming | |
| words = response.split() | |
| chunk_size = 5 | |
| total_chunks = (len(words) + chunk_size - 1) // chunk_size | |
| for i in range(0, len(words), chunk_size): | |
| chunk = ' '.join(words[i:i+chunk_size]) + ' ' | |
| emit('response_chunk', { | |
| 'content': chunk, | |
| 'timestamp': datetime.now().isoformat() | |
| }) | |
| socketio.sleep(0.02) # Small delay for streaming effect | |
| emit('response_complete', { | |
| 'message_id': str(uuid.uuid4()), | |
| 'total_chunks': total_chunks, | |
| 'processing_time': 1.0, | |
| 'timestamp': datetime.now().isoformat() | |
| }) | |
| except Exception as e: | |
| print(f"❌ Error processing message with chat agent: {e}") | |
| # Fallback to demo response if chat agent fails | |
| demo_response = f"I apologize, but I'm having trouble processing your request right now. You asked about: '{content}'. Please try again in a moment, or check that the Groq API key is properly configured." | |
| emit('response_chunk', { | |
| 'content': demo_response, | |
| 'timestamp': datetime.now().isoformat() | |
| }) | |
| emit('response_complete', { | |
| 'message_id': str(uuid.uuid4()), | |
| 'total_chunks': 1, | |
| 'processing_time': 0.1, | |
| 'timestamp': datetime.now().isoformat() | |
| }) | |
| except Exception as e: | |
| print(f"Error handling message: {e}") | |
| emit('error', {'message': 'Failed to process message', 'code': 'PROCESSING_ERROR'}) | |
| def handle_language_switch(data): | |
| """Handle language switching using real chat agent.""" | |
| from flask_socketio import emit | |
| from flask import request | |
| try: | |
| # Get session ID for this connection | |
| if request.sid not in websocket_sessions: | |
| emit('error', {'message': 'No active session', 'code': 'NO_SESSION'}) | |
| return | |
| session_id = websocket_sessions[request.sid] | |
| new_language = data.get('language', 'python') | |
| language_names = { | |
| 'python': 'Python', | |
| 'javascript': 'JavaScript', | |
| 'java': 'Java', | |
| 'cpp': 'C++', | |
| 'csharp': 'C#', | |
| 'go': 'Go', | |
| 'rust': 'Rust', | |
| 'typescript': 'TypeScript' | |
| } | |
| try: | |
| if chat_agent: | |
| # Use real chat agent to switch language | |
| result = chat_agent.switch_language(session_id, new_language) | |
| emit('language_switched', { | |
| 'previous_language': result.get('previous_language', 'python'), | |
| 'new_language': result.get('new_language', new_language), | |
| 'message': result.get('message', f'Language switched to {language_names.get(new_language, new_language)}'), | |
| 'timestamp': datetime.now().isoformat() | |
| }) | |
| print(f"🔄 Language switched to: {new_language} for session {session_id}") | |
| else: | |
| # Fallback for demo mode | |
| emit('language_switched', { | |
| 'previous_language': 'python', | |
| 'new_language': new_language, | |
| 'message': f"Switched to {language_names.get(new_language, new_language)}. I'm now ready to help you with {language_names.get(new_language, new_language)} programming!", | |
| 'timestamp': datetime.now().isoformat() | |
| }) | |
| print(f"🔄 Language switched to: {new_language} (demo mode)") | |
| except Exception as e: | |
| print(f"❌ Error switching language: {e}") | |
| emit('error', {'message': 'Failed to switch language', 'code': 'LANGUAGE_SWITCH_ERROR'}) | |
| except Exception as e: | |
| print(f"Error handling language switch: {e}") | |
| emit('error', {'message': 'Failed to switch language', 'code': 'LANGUAGE_SWITCH_ERROR'}) | |
| # Add error handlers for WebSocket | |
| def default_error_handler(e): | |
| """Handle WebSocket errors.""" | |
| print(f"WebSocket error: {e}") | |
| from flask_socketio import emit | |
| emit('error', {'message': 'Connection error occurred', 'code': 'WEBSOCKET_ERROR'}) | |
| # Import datetime for timestamps | |
| from datetime import datetime | |
| import uuid | |
| return app | |
| if __name__ == '__main__': | |
| app = create_app() | |
| socketio.run(app, debug=True, host='0.0.0.0', port=7860) |