import os import logging from logging.handlers import RotatingFileHandler # Set up logging to console console_handler = logging.StreamHandler() console_formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') console_handler.setFormatter(console_formatter) console_handler.setLevel(logging.DEBUG) logging.getLogger().addHandler(console_handler) logging.getLogger().setLevel(logging.DEBUG) from flask import Flask, request, jsonify from flask_jwt_extended import JWTManager, create_access_token, jwt_required, get_jwt_identity from flask_cors import CORS from dotenv import load_dotenv import openai import yaml import pandas as pd import router_logic load_dotenv() app = Flask(__name__) CORS(app) # Load tool data once when the app starts csv_path = os.path.join(os.path.dirname(__file__), 'ai_tools.csv') print(f"[STARTUP DEBUG] Attempting to load tools from: {csv_path}") logging.getLogger().info(f"[STARTUP DEBUG] Attempting to load tools from: {csv_path}") TOOL_DATA_DF = router_logic.load_tool_data(csv_path) tool_count = len(TOOL_DATA_DF) print(f"[STARTUP DEBUG] Loaded {tool_count} tools from ai_tools.csv.") logging.getLogger().info(f"[STARTUP DEBUG] Loaded {tool_count} tools from ai_tools.csv.") print(f"[STARTUP DEBUG] DataFrame columns: {TOOL_DATA_DF.columns.tolist()}") logging.getLogger().info(f"[STARTUP DEBUG] DataFrame columns: {TOOL_DATA_DF.columns.tolist()}") print(f"[STARTUP DEBUG] Unique MediaType values: {TOOL_DATA_DF['MediaType'].unique()}") logging.getLogger().info(f"[STARTUP DEBUG] Unique MediaType values: {TOOL_DATA_DF['MediaType'].unique()}") if tool_count == 0: print("[STARTUP DEBUG] No tools loaded from ai_tools.csv! File may be missing or unreadable.") logging.getLogger().warning("[STARTUP DEBUG] No tools loaded from ai_tools.csv! File may be missing or unreadable.") app.config['JWT_SECRET_KEY'] = os.environ.get('JWT_SECRET', 'supersecret') jwt = JWTManager(app) openai.api_key = os.environ.get('OPENAI_API_KEY') # In-memory user store (replace with DB for production) USERS = { 'demo': { 'password': 'demo', 'prompts': [] } } @app.route("/") def index(): return "Welcome to the Promptified Backend API" @app.route('/api/login', methods=['POST']) def login(): data = request.json username = data.get('username') password = data.get('password') if not username or not password: return jsonify({'error': 'Missing username or password'}), 400 user = USERS.get(username) if not user or user['password'] != password: return jsonify({'error': 'Invalid credentials'}), 401 token = create_access_token(identity=username) return jsonify({'token': token}) @app.route('/api/improve-prompt', methods=['POST']) @jwt_required() def improve_prompt(): data = request.json prompt = data.get('prompt') mode = data.get('mode', 'mode') verbosity = data.get('verbosity', 'concise') if not prompt: return jsonify({'error': 'Missing prompt'}), 400 # Load system prompts from YAML config config_path = os.path.join(os.path.dirname(__file__), 'system_prompts.yaml') try: with open(config_path, 'r', encoding='utf-8') as f: system_prompts = yaml.safe_load(f) app.logger.info(f"[PROMPT IMPROVEMENT] Loaded system prompts: {system_prompts}") except Exception as e: app.logger.error(f"[PROMPT IMPROVEMENT] Error loading system prompts: {str(e)}") system_prompts = {} # Default system prompt (fallback) default_system_prompt = ( "You are an expert at refining prompts for creative AI (image, video, music). " "Given a user prompt and selected mode (e.g., photography, digital art), rewrite the prompt for maximum clarity and creative potential. " "Also, provide a breakdown of what you changed and what could be added for improvement (e.g., camera settings, lighting, subject details)." ) # Use mode-specific system prompt if available mode_key = mode.lower() system_prompt = system_prompts.get(mode_key, default_system_prompt) app.logger.info(f"[PROMPT IMPROVEMENT] Using mode: {mode_key}") app.logger.info(f"[PROMPT IMPROVEMENT] System prompt: {system_prompt}") user_content = f"Prompt: {prompt}\nMode: {mode}\nVerbosity: {verbosity}" try: response = openai.chat.completions.create( model="gpt-4.1", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_content} ], max_tokens=400 ) content = response.choices[0].message.content parts = content.split("Improvement Notes:") improved_prompt = parts[0].replace("Improved Prompt:", "").strip() notes = parts[1].strip() if len(parts) > 1 else "" return jsonify({'improvedPrompt': improved_prompt, 'notes': notes}) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/api/save-prompt', methods=['POST']) @jwt_required() def save_prompt(): username = get_jwt_identity() data = request.json prompt = data.get('prompt') if not prompt: return jsonify({'error': 'Missing prompt'}), 400 USERS[username]['prompts'].append(prompt) return jsonify({'success': True}) @app.route('/api/get-prompts', methods=['GET']) @jwt_required() def get_prompts(): username = get_jwt_identity() prompts = USERS[username]['prompts'] return jsonify({'prompts': prompts}) import logging from logging.handlers import RotatingFileHandler # Set up logging to app.log log_handler = RotatingFileHandler('/tmp/app.log', maxBytes=1024000, backupCount=3) log_formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') log_handler.setFormatter(log_formatter) log_handler.setLevel(logging.DEBUG) app.logger.addHandler(log_handler) app.logger.setLevel(logging.DEBUG) @app.route('/api/route', methods=['POST']) #@wt_required() # Remove this decorator if you want the endpoint public def handle_routing_request(): if TOOL_DATA_DF is None: app.logger.error("Tool data not loaded on server") return jsonify({"error": "Tool data not loaded on server"}), 500 import traceback data = request.get_json() app.logger.info(f"[ROUTER DEBUG] Incoming data: {data}") if not data or 'prompt' not in data or 'media_type' not in data: app.logger.warning("[ROUTER DEBUG] Missing prompt or media_type in request body") return jsonify({"error": "Missing 'prompt' or 'media_type' in request body"}), 400 prompt = data.get('prompt') media_type = data.get('media_type') criteria = data.get('criteria', None) # e.g., {"Cost": "Free", "IsOpenSource": True} user_id = data.get('user_id', None) try: app.logger.info(f"[ROUTER DEBUG] prompt={prompt}, media_type={media_type}, criteria={criteria}, user_id={user_id}") selected_tool_details, debug_info = router_logic.universal_router_agent( prompt=prompt, tool_data_df=TOOL_DATA_DF, user_criteria=criteria, media_type=media_type, user_id=user_id, debug=True ) app.logger.info(f"[ROUTER DEBUG] Filtered tools: {debug_info.get('filtered_tools')}") app.logger.info(f"[ROUTER DEBUG] Selected tool details: {selected_tool_details}") if selected_tool_details: return jsonify({"selected_tool": selected_tool_details}), 200 else: app.logger.warning("[ROUTER DEBUG] Could not determine a suitable tool") return jsonify({"error": "Could not determine a suitable tool", "debug": debug_info}), 404 except Exception as e: tb_str = traceback.format_exc() app.logger.error(f"[ROUTER ERROR] Exception during routing: {e}\n{tb_str}") # Return the error message in the response for debugging (remove in production) return jsonify({"error": "Internal server error during routing", "details": str(e), "traceback": tb_str}), 500 if __name__ == "__main__": app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 7860)), debug=True)