scratch_chat / app.py
WebashalarForML's picture
Update app.py
840ea23 verified
"""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
@app.route('/')
@app.route('/chat')
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
@socketio.on('connect')
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'})
@socketio.on('disconnect')
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")
@socketio.on('message')
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'})
@socketio.on('language_switch')
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
@socketio.on_error_default
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)