| |
| """ |
| Mindfull AI Avatar Chatbot - Web API |
| Flask web server for serving the Mindfull pipeline via HTTP API |
| """ |
|
|
| import os |
| import json |
| import uuid |
| import threading |
| from datetime import datetime |
| from pathlib import Path |
|
|
| from flask import Flask, request, jsonify, send_file, send_from_directory |
| from flask_cors import CORS |
| import logging |
|
|
| |
| from mindfull_pipeline import MindfullPipeline |
| from mindfull_config import config, logger |
|
|
| |
| app = Flask(__name__) |
| CORS(app, origins=config.CORS_ORIGINS) |
|
|
| |
| pipeline = None |
| active_sessions = {} |
| session_lock = threading.Lock() |
|
|
| def initialize_app(): |
| """Initialize the Flask application""" |
| global pipeline |
| |
| logger.info("Initializing Mindfull Web API...") |
| |
| |
| pipeline = MindfullPipeline() |
| |
| if not pipeline.is_initialized: |
| logger.error("Failed to initialize Mindfull pipeline") |
| return False |
| |
| logger.info("✅ Mindfull Web API initialized successfully") |
| return True |
|
|
| @app.route('/health', methods=['GET']) |
| def health_check(): |
| """Health check endpoint""" |
| if not pipeline or not pipeline.is_initialized: |
| return jsonify({ |
| 'status': 'error', |
| 'message': 'Pipeline not initialized' |
| }), 503 |
| |
| return jsonify({ |
| 'status': 'healthy', |
| 'timestamp': datetime.now().isoformat(), |
| 'components': { |
| 'ollama': pipeline.ollama_available, |
| 'f5tts': pipeline.f5tts_available, |
| 'sadtalker': pipeline.sadtalker_available |
| } |
| }) |
|
|
| @app.route('/session', methods=['POST']) |
| def create_session(): |
| """Create a new chat session""" |
| try: |
| session_id = pipeline.create_session() |
| |
| with session_lock: |
| active_sessions[session_id] = { |
| 'created': datetime.now().isoformat(), |
| 'last_activity': datetime.now().isoformat() |
| } |
| |
| return jsonify({ |
| 'status': 'success', |
| 'session_id': session_id, |
| 'message': 'Session created successfully' |
| }) |
| |
| except Exception as e: |
| logger.error(f"Error creating session: {str(e)}") |
| return jsonify({ |
| 'status': 'error', |
| 'message': 'Failed to create session' |
| }), 500 |
|
|
| @app.route('/session/<session_id>', methods=['GET']) |
| def get_session_info(session_id): |
| """Get session information""" |
| try: |
| if session_id not in active_sessions: |
| return jsonify({ |
| 'status': 'error', |
| 'message': 'Session not found' |
| }), 404 |
| |
| session_info = pipeline.get_session_info() |
| |
| return jsonify({ |
| 'status': 'success', |
| 'session_info': session_info |
| }) |
| |
| except Exception as e: |
| logger.error(f"Error getting session info: {str(e)}") |
| return jsonify({ |
| 'status': 'error', |
| 'message': 'Failed to get session info' |
| }), 500 |
|
|
| @app.route('/chat', methods=['POST']) |
| def chat(): |
| """Main chat endpoint""" |
| try: |
| data = request.get_json() |
| |
| if not data or 'message' not in data: |
| return jsonify({ |
| 'status': 'error', |
| 'message': 'Missing message in request' |
| }), 400 |
| |
| user_message = data['message'].strip() |
| session_id = data.get('session_id') |
| include_video = data.get('include_video', True) |
| avatar_image = data.get('avatar_image') |
| |
| |
| if len(user_message) < config.MIN_TEXT_LENGTH: |
| return jsonify({ |
| 'status': 'error', |
| 'message': 'Message too short' |
| }), 400 |
| |
| if len(user_message) > config.MAX_TEXT_LENGTH: |
| return jsonify({ |
| 'status': 'error', |
| 'message': 'Message too long' |
| }), 400 |
| |
| |
| if session_id and session_id in active_sessions: |
| with session_lock: |
| active_sessions[session_id]['last_activity'] = datetime.now().isoformat() |
| |
| |
| if include_video: |
| result = pipeline.process_complete_interaction(user_message, avatar_image) |
| else: |
| |
| response_text, emotion = pipeline.generate_response(user_message) |
| audio_path = pipeline.generate_audio(response_text) |
| |
| result = { |
| 'session_id': session_id, |
| 'user_input': user_message, |
| 'timestamp': datetime.now().isoformat(), |
| 'response_text': response_text, |
| 'emotion_detected': emotion, |
| 'audio_path': audio_path, |
| 'video_path': None, |
| 'processing_time': 0, |
| 'errors': [] |
| } |
| |
| |
| response_data = { |
| 'status': 'success', |
| 'session_id': result['session_id'], |
| 'response_text': result['response_text'], |
| 'emotion_detected': result['emotion_detected'], |
| 'processing_time': result['processing_time'], |
| 'timestamp': result['timestamp'] |
| } |
| |
| |
| if result['audio_path']: |
| audio_filename = Path(result['audio_path']).name |
| response_data['audio_url'] = f'/audio/{audio_filename}' |
| |
| if result['video_path']: |
| video_filename = Path(result['video_path']).name |
| response_data['video_url'] = f'/video/{video_filename}' |
| |
| if result['errors']: |
| response_data['warnings'] = result['errors'] |
| |
| return jsonify(response_data) |
| |
| except Exception as e: |
| logger.error(f"Error in chat endpoint: {str(e)}") |
| return jsonify({ |
| 'status': 'error', |
| 'message': 'Internal server error' |
| }), 500 |
|
|
| @app.route('/audio/<filename>') |
| def serve_audio(filename): |
| """Serve audio files""" |
| try: |
| return send_from_directory(config.AUDIO_OUTPUT_DIR, filename) |
| except FileNotFoundError: |
| return jsonify({'error': 'Audio file not found'}), 404 |
|
|
| @app.route('/video/<filename>') |
| def serve_video(filename): |
| """Serve video files""" |
| try: |
| return send_from_directory(config.VIDEO_OUTPUT_DIR, filename) |
| except FileNotFoundError: |
| return jsonify({'error': 'Video file not found'}), 404 |
|
|
| @app.route('/models', methods=['GET']) |
| def get_model_status(): |
| """Get status of all models""" |
| try: |
| model_status = config.get_model_status() |
| |
| return jsonify({ |
| 'status': 'success', |
| 'models': model_status, |
| 'pipeline_status': { |
| 'ollama': pipeline.ollama_available if pipeline else False, |
| 'f5tts': pipeline.f5tts_available if pipeline else False, |
| 'sadtalker': pipeline.sadtalker_available if pipeline else False |
| } |
| }) |
| |
| except Exception as e: |
| logger.error(f"Error getting model status: {str(e)}") |
| return jsonify({ |
| 'status': 'error', |
| 'message': 'Failed to get model status' |
| }), 500 |
|
|
| @app.route('/cleanup', methods=['POST']) |
| def cleanup_files(): |
| """Clean up old generated files""" |
| try: |
| data = request.get_json() or {} |
| max_age_hours = data.get('max_age_hours', 24) |
| |
| if pipeline: |
| pipeline.cleanup_old_files(max_age_hours) |
| |
| return jsonify({ |
| 'status': 'success', |
| 'message': f'Cleaned up files older than {max_age_hours} hours' |
| }) |
| |
| except Exception as e: |
| logger.error(f"Error during cleanup: {str(e)}") |
| return jsonify({ |
| 'status': 'error', |
| 'message': 'Cleanup failed' |
| }), 500 |
|
|
| @app.route('/config', methods=['GET']) |
| def get_config(): |
| """Get configuration information""" |
| return jsonify({ |
| 'status': 'success', |
| 'config': { |
| 'max_text_length': config.MAX_TEXT_LENGTH, |
| 'min_text_length': config.MIN_TEXT_LENGTH, |
| 'allowed_languages': config.ALLOWED_LANGUAGES, |
| 'session_timeout': config.SESSION_TIMEOUT, |
| 'available_emotions': list(config.EMOTION_KEYWORDS.keys()), |
| 'default_model': config.DEFAULT_MODEL, |
| 'fallback_model': config.FALLBACK_MODEL |
| } |
| }) |
|
|
| @app.route('/', methods=['GET']) |
| def index(): |
| """Main index page""" |
| return jsonify({ |
| 'service': 'Mindfull AI Avatar Chatbot', |
| 'version': '1.0.0', |
| 'status': 'running', |
| 'endpoints': { |
| 'health': '/health', |
| 'create_session': '/session [POST]', |
| 'chat': '/chat [POST]', |
| 'get_config': '/config [GET]', |
| 'model_status': '/models [GET]', |
| 'cleanup': '/cleanup [POST]' |
| }, |
| 'documentation': 'See API documentation for usage details' |
| }) |
|
|
| @app.errorhandler(404) |
| def not_found(error): |
| return jsonify({'error': 'Endpoint not found'}), 404 |
|
|
| @app.errorhandler(500) |
| def internal_error(error): |
| return jsonify({'error': 'Internal server error'}), 500 |
|
|
| def run_server(host=None, port=None, debug=None): |
| """Run the Flask server""" |
| host = host or config.WEB_HOST |
| port = port or config.WEB_PORT |
| debug = debug or config.WEB_DEBUG |
| |
| logger.info(f"Starting Mindfull Web API server on {host}:{port}") |
| |
| app.run( |
| host=host, |
| port=port, |
| debug=debug, |
| threaded=config.WEB_THREADED |
| ) |
|
|
| if __name__ == '__main__': |
| if initialize_app(): |
| run_server() |
| else: |
| logger.error("Failed to initialize application") |
| exit(1) |
|
|