from flask import Flask, render_template, request, jsonify, redirect, url_for import os import sys from dotenv import load_dotenv # Load environment variables load_dotenv() # Add src directory to path for imports sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src')) from recommendation_engine import RecommendationEngine from recommendation_explainer import RecommendationExplainer from use_case_helper import UseCaseHelper app = Flask(__name__) app.secret_key = os.environ.get('SECRET_KEY', 'dev-secret-key-change-in-production') # Initialize recommendation engine and explainer recommendation_engine = RecommendationEngine() recommendation_explainer = RecommendationExplainer() use_case_helper = UseCaseHelper() # Initialize RAG chatbot (lazy initialization on first request) rag_chatbot = None chatbot_initialized = False chatbot_error = None def get_chatbot(): """Get or initialize the RAG chatbot""" global rag_chatbot, chatbot_initialized, chatbot_error if chatbot_initialized: return rag_chatbot try: print("🤖 Initializing RAG chatbot...") from rag import RAGChatbot rag_chatbot = RAGChatbot() rag_chatbot.initialize() chatbot_initialized = True print("✅ RAG chatbot initialized successfully") return rag_chatbot except Exception as e: chatbot_error = str(e) chatbot_initialized = True # Mark as initialized to avoid retrying print(f"❌ Failed to initialize chatbot: {e}") return None # Custom Jinja filter for simple markdown rendering import re @app.template_filter('simple_markdown') def simple_markdown(text): """Convert simple markdown to HTML (bold and bullets only).""" if not text: return text # Convert **text** to text text = re.sub(r'\*\*([^*]+)\*\*', r'\1', text) # Convert bullet points (- or *) to HTML list items lines = text.split('\n') in_list = False result = [] for line in lines: stripped = line.strip() if stripped.startswith('- ') or stripped.startswith('* '): if not in_list: result.append('') in_list = False if stripped: # Only add non-empty lines result.append(f'

{line}

') if in_list: result.append('') return ''.join(result) @app.route('/') def landing(): """Landing page with PickLLM introduction and CTA""" return render_template('landing.html') @app.route('/questionnaire') def questionnaire(): """Questionnaire page for user requirements""" return render_template('questionnaire.html') @app.route('/submit-questionnaire', methods=['POST']) def submit_questionnaire(): """Process questionnaire responses and generate recommendations""" data = request.get_json() if request.is_json else request.form.to_dict() # Get top 3 recommendations based on user's use case use_case = data.get('use_case') visual_ai_type = data.get('visual_ai_type') model_type = data.get('model_type') priority = data.get('priority') # 'lower_cost' or 'better_performance' recommendations = recommendation_engine.get_top_recommendations( use_case=use_case, visual_ai_type=visual_ai_type, model_type=model_type, priority=priority, top_n=3 ) # Generate AI explanation for the recommendations try: explanation = recommendation_explainer.generate_explanation( use_case=use_case, recommendations=recommendations, visual_ai_type=visual_ai_type, model_type=model_type ) print(f"✅ Generated explanation: {len(explanation) if explanation else 0} characters") except Exception as e: print(f"❌ Error generating explanation: {e}") explanation = "" # Render results page with recommendations return render_template('results.html', recommendations=recommendations, use_case=use_case, visual_ai_type=visual_ai_type, explanation=explanation, user_data=data) @app.route('/results') def results(): """Display LLM recommendations (direct access fallback)""" return render_template('results.html', recommendations=[]) @app.route('/api/health') def health_check(): """Health check endpoint""" return jsonify({"status": "healthy", "service": "PickLLM"}) @app.route('/api/chat', methods=['POST']) def chat(): """Chatbot endpoint for answering user questions""" try: data = request.get_json() query = data.get('message', '').strip() if not query: return jsonify({"error": "Message is required"}), 400 # Get chatbot and generate response chatbot = get_chatbot() if chatbot is None: return jsonify({ "response": "Sorry, the chatbot is currently unavailable. Please try again later or contact support.", "status": "success" }) response = chatbot.chat(query) return jsonify({ "response": response, "status": "success" }) except Exception as e: print(f"❌ Error in chat endpoint: {e}") import traceback traceback.print_exc() return jsonify({ "response": "Sorry, I encountered an error processing your question. Please try again.", "status": "success" }) @app.route('/api/suggest-use-case', methods=['POST']) def suggest_use_case(): """Endpoint to suggest use case category based on user description""" try: data = request.get_json() description = data.get('description', '').strip() if not description: return jsonify({"error": "Description is required"}), 400 # Get use case suggestion from AI suggested_category = use_case_helper.use_case_classification(description) if not suggested_category: return jsonify({ "error": "Failed to classify use case. Please try again.", "status": "error" }), 500 # Map category to display label category_labels = { 'conversational_knowledge': 'Conversational & Knowledge Agents', 'productivity_information': 'Productivity & Information Handling', 'creative_content': 'Creative & Content Generation', 'technical_developer': 'Technical & Developer Tools', 'advanced_automation': 'Advanced Automation', 'visual_ai': 'Visual AI' } return jsonify({ "category": suggested_category, "label": category_labels.get(suggested_category, suggested_category), "status": "success" }) except Exception as e: print(f"❌ Error in suggest-use-case endpoint: {e}") import traceback traceback.print_exc() return jsonify({ "error": "An error occurred while processing your request.", "status": "error" }), 500 @app.route('/api/feedback', methods=['POST']) def save_feedback(): """Endpoint to save user feedback on recommendations""" try: import json from datetime import datetime feedback = request.get_json() if not feedback: return jsonify({"error": "No feedback data provided"}), 400 feedback_type = feedback.get('type') # 'up' or 'down' if feedback_type not in ['up', 'down']: return jsonify({"error": "Invalid feedback type"}), 400 # Create filename with timestamp timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') folder = 'thumbs_up' if feedback_type == 'up' else 'thumbs_down' filename = f"feedback_{timestamp}.json" filepath = os.path.join('outputs', folder, filename) # Ensure directory exists os.makedirs(os.path.dirname(filepath), exist_ok=True) # Save feedback to file with open(filepath, 'w') as f: json.dump(feedback, f, indent=2) print(f"✅ Saved {feedback_type} feedback to {filepath}") return jsonify({ "status": "success", "message": "Feedback saved successfully", "filename": filename }) except Exception as e: print(f"❌ Error saving feedback: {e}") import traceback traceback.print_exc() return jsonify({ "error": "An error occurred while saving feedback.", "status": "error" }), 500 if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=5555)