""" Legal RAG Pipeline - Flask REST API This module provides the REST API interface for the Legal RAG Pipeline application. It handles all HTTP requests from the frontend and coordinates interactions with the LegalRagAgent and DatabaseManager components. The API provides endpoints for: - Agent initialization and configuration - Case and client management - Document upload and search - Legal question answering - Chat history management Author: Legal RAG Pipeline Team Version: 1.0.0 """ from flask import Flask, abort, request, jsonify, send_from_directory from flask_cors import CORS import os import json from dotenv import load_dotenv import sys # Add the parent directory to path for imports sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from src.core.agent import LegalRagAgent from src.storage.database_manager import DatabaseManager PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) FRONTEND_BUILD_DIR = os.path.join(PROJECT_ROOT, 'ui', 'build') # Initialize Flask application app = Flask(__name__, static_folder=FRONTEND_BUILD_DIR, static_url_path='') # Enable CORS for all routes to allow frontend access CORS(app, resources={r"/*": {"origins": "*"}}) # Load environment variables from .env file load_dotenv() # Global instances - initialized when agent is created agent: LegalRagAgent = None db_manager: DatabaseManager = None message_count = 0 # Track number of questions asked for advice generation # HEALTH CHECK AND STATUS ENDPOINTS @app.route('/') def index(): """ Root endpoint to verify the API is running. Returns: JSON response with API status """ if os.path.exists(os.path.join(FRONTEND_BUILD_DIR, 'index.html')): return send_from_directory(FRONTEND_BUILD_DIR, 'index.html') return jsonify({ 'status': 'ok', 'message': 'Legal RAG Pipeline API is running', 'version': '1.0.0', 'endpoints': { 'initialize': '/api/initialize_agent', 'cases': '/api/get_cases', 'chat': '/api/ask_question' } }) @app.route('/api/status') def api_status(): return jsonify({ 'status': 'ok', 'message': 'Legal RAG Pipeline API is running', 'version': '1.0.0', }) # AGENT INITIALIZATION AND CONFIGURATION @app.route('/api/initialize_agent', methods=['POST']) def initialize_agent(): """ Initialize the LegalRagAgent with specified model and directories. This endpoint sets up the entire legal RAG system including: - Language model configuration - Database connections - Vector store initialization Request Body: model_name (str): Name of the language model to use Returns: JSON response indicating success/failure of initialization """ data = request.json model_name = data.get('model_name', 'mistral-large-2411') global agent, db_manager # If agent already exists, just update the model if agent: if agent.set_llm(model_name): return jsonify({ 'success': True, 'message': f'Agent re-initialized with model {model_name}' }), 200 else: return jsonify({ 'success': False, 'message': f'Model {model_name} not supported' }), 500 try: # Validate required environment variables db_dir = os.getenv('DB_DIR') vector_store_dir = os.getenv('VECTOR_STORE_DIR') if not db_dir or not vector_store_dir: raise EnvironmentError( "Required environment variables DB_DIR and VECTOR_STORE_DIR are not set. " "Please check your .env file." ) # Create necessary directories if they don't exist for dir_path in [db_dir, vector_store_dir]: os.makedirs(dir_path, exist_ok=True) # Initialize database manager db_manager = DatabaseManager( root_vector_db_dir=vector_store_dir, db_dir=db_dir, ) # Initialize the legal RAG agent agent = LegalRagAgent(db_manager, model_name=model_name) return jsonify({ 'success': True, 'message': f'Legal RAG Agent initialized successfully with {model_name}', 'model': model_name }), 200 except EnvironmentError as e: return jsonify({ 'success': False, 'message': f'Configuration error: {str(e)}' }), 400 except Exception as e: return jsonify({ 'success': False, 'message': f'Error initializing agent: {str(e)}' }), 500 # CASE MANAGEMENT ENDPOINTS @app.route('/api/get_cases', methods=['GET']) def get_cases(): """ Retrieve all cases, optionally filtered by search term. Query Parameters: search_term (str, optional): Filter cases by name containing this term Returns: JSON response with list of cases matching the search criteria """ global agent if not agent: return jsonify({ 'success': False, 'message': 'Agent not initialized. Please initialize the agent first.' }), 400 search_term = request.args.get('search_term', '') try: cases = agent.database_manager.get_cases_by_name(search_term) case_list = [] for case in cases: case_list.append({ 'id': case.id, 'name': case.name, 'jurisdiction_code': case.jurisdiction_code, 'notes': case.notes, 'created_at': case.created_at.isoformat() if case.created_at else None, 'case_type': case.case_type, 'case_status': case.case_status, 'client_id': case.client_id }) return jsonify({ 'success': True, 'cases': case_list, 'total': len(case_list) }), 200 except Exception as e: return jsonify({ 'success': False, 'message': f'Error fetching cases: {str(e)}' }), 500 @app.route('/api/load_case', methods=['POST']) def load_case(): """ Load a specific case for the current session. This endpoint loads a case and initializes the database manager with the case context, enabling case-specific operations like document management and chat history. Request Body: case_name (str): Name of the case to load case_path (str, optional): Path to JSON file containing case data Returns: JSON response with loaded case information """ global agent if not agent: return jsonify({ 'success': False, 'message': 'Agent not initialized. Please initialize the agent first.' }), 400 data = request.json case_path = data.get('case_path', '') case_name = data.get('case_name', '') try: # Load case from JSON file or by name if case_path: case = agent._new_object_from_json("case", case_path) else: case = agent.get_case_by_name_web_safe(case_name) if not case: return jsonify({ 'success': False, 'message': f'Case "{case_name}" not found. Please check the case name or create a new case.' }), 404 # Initialize case context in database manager agent.database_manager.initialize_from_case(case) agent.rag_pipeline.database_manager = agent.database_manager return jsonify({ 'success': True, 'message': f'Case "{case.name}" loaded successfully', 'case': { 'id': case.id, 'name': case.name, 'jurisdiction_code': case.jurisdiction_code, 'notes': case.notes, 'created_at': case.created_at.isoformat() if case.created_at else None, 'case_type': case.case_type, 'case_status': case.case_status, 'client_id': case.client_id } }), 200 except Exception as e: return jsonify({ 'success': False, 'message': f'Error loading case: {str(e)}' }), 500 @app.route('/api/create_case', methods=['POST']) def create_case(): """ Create a new legal case. Request Body: name (str): Case name jurisdiction_code (str): Legal jurisdiction code (e.g., 'oncj', 'bcca') notes (str, optional): Additional case notes case_type (str, optional): Type of case (e.g., 'Civil', 'Criminal') case_status (str, optional): Current status (e.g., 'Open', 'Closed') Returns: JSON response with created case information """ global agent if not agent: return jsonify({ 'success': False, 'message': 'Agent not initialized. Please initialize the agent first.' }), 400 data = request.json # Validate required fields if not data.get('name'): return jsonify({ 'success': False, 'message': 'Case name is required' }), 400 try: case = agent.database_manager.add_case(**data) # Initialize the case context agent.database_manager.initialize_from_case(case) agent.rag_pipeline.database_manager = agent.database_manager return jsonify({ 'success': True, 'message': f'Case "{case.name}" created successfully', 'case': { 'id': case.id, 'name': case.name, 'jurisdiction_code': case.jurisdiction_code, 'notes': case.notes, 'case_type': getattr(case, 'case_type', None), 'case_status': getattr(case, 'case_status', None) } }), 201 except Exception as e: return jsonify({ 'success': False, 'message': f'Error creating case: {str(e)}' }), 500 @app.route('/api/update_case', methods=['PUT']) def update_case(): """ Update an existing case. Request Body: case_id (str): ID of the case to update name (str, optional): Case name jurisdiction_code (str, optional): Legal jurisdiction code notes (str, optional): Additional case notes case_type (str, optional): Type of case case_status (str, optional): Current status Returns: JSON response with updated case information """ global agent if not agent: return jsonify({ 'success': False, 'message': 'Agent not initialized. Please initialize the agent first.' }), 400 data = request.json case_id = data.get('case_id') if not case_id: return jsonify({ 'success': False, 'message': 'Case ID is required for updates' }), 400 try: # Get the existing case case = agent.database_manager.get_case_by_id(case_id) if not case: return jsonify({ 'success': False, 'message': f'Case with ID "{case_id}" not found' }), 404 # Update only the provided fields if 'name' in data: case.name = data['name'] if 'jurisdiction_code' in data: case.jurisdiction_code = data['jurisdiction_code'] if 'notes' in data: case.notes = data['notes'] if 'case_type' in data: case.case_type = data['case_type'] if 'case_status' in data: case.case_status = data['case_status'] # Commit changes and refresh agent.database_manager.session.commit() agent.database_manager.session.refresh(case) # If this is the current case, update the agent's reference if agent.database_manager.case and agent.database_manager.case.id == case.id: agent.database_manager.case = case return jsonify({ 'success': True, 'message': f'Case "{case.name}" updated successfully', 'case': { 'id': case.id, 'name': case.name, 'jurisdiction_code': case.jurisdiction_code, 'notes': case.notes, 'case_type': getattr(case, 'case_type', None), 'case_status': getattr(case, 'case_status', None), 'client_id': case.client_id } }), 200 except Exception as e: return jsonify({ 'success': False, 'message': f'Error updating case: {str(e)}' }), 500 # CLIENT MANAGEMENT ENDPOINTS @app.route('/api/get_clients', methods=['GET']) def get_clients(): """ Retrieve all clients, optionally filtered by search term, or a specific client by ID. Query Parameters: client_id (str, optional): ID of specific client to retrieve search_term (str, optional): Filter clients by name containing this term Returns: JSON response with client details or list of clients """ global agent if not agent: return jsonify({ 'success': False, 'message': 'Agent not initialized. Please initialize the agent first.' }), 400 client_id = request.args.get('client_id', '') search_term = request.args.get('search_term', '') try: # If client_id is provided, return specific client (backward compatibility) if client_id: client = agent.database_manager.get_client_by_id(client_id) if client: client_data = { 'id': client.id, 'name': client.name, 'email': client.email, 'phone': client.phone, 'address': client.address, 'date_of_birth': client.date_of_birth, 'gender': client.gender, 'occupation': client.occupation, 'notes': client.notes } return jsonify({ 'success': True, 'client': client_data }), 200 else: return jsonify({ 'success': False, 'message': f'Client with ID "{client_id}" not found' }), 404 # Otherwise, return all clients filtered by search term else: clients = agent.database_manager.get_clients_by_name(search_term) client_list = [] for client in clients: client_list.append({ 'id': client.id, 'name': client.name, 'email': client.email, 'phone': client.phone, 'address': client.address, 'date_of_birth': client.date_of_birth, 'gender': client.gender, 'occupation': client.occupation, 'notes': client.notes }) return jsonify({ 'success': True, 'clients': client_list }), 200 except Exception as e: return jsonify({ 'success': False, 'message': f'Error fetching clients: {str(e)}' }), 500 @app.route('/api/load_client', methods=['POST']) def load_client(): """ Load a client for the current case session. Request Body: client_name (str): Name of the client to load Returns: JSON response with loaded client information """ global agent if not agent: return jsonify({ 'success': False, 'message': 'Agent not initialized. Please initialize the agent first.' }), 400 if not agent.database_manager.case: return jsonify({ 'success': False, 'message': 'A case must be loaded before loading a client' }), 400 data = request.json client_name = data.get('client_name', '') if not client_name: return jsonify({ 'success': False, 'message': 'Client name is required' }), 400 try: client = agent.get_client_by_name_web_safe(client_name) if not client: return jsonify({ 'success': False, 'message': f'Client "{client_name}" not found. Please create the client first.' }), 404 return jsonify({ 'success': True, 'message': f'Client "{client.name}" loaded successfully', 'client': { 'id': client.id, 'name': client.name, 'email': client.email, 'phone': client.phone, 'address': client.address, 'date_of_birth': client.date_of_birth, 'gender': client.gender, 'occupation': client.occupation, 'notes': client.notes } }), 200 except Exception as e: return jsonify({ 'success': False, 'message': f'Error loading client: {str(e)}' }), 500 @app.route('/api/create_client', methods=['POST']) def create_client(): """ Create a new client and associate with the current case. Request Body: name (str): Client's full name client_details (dict): Personal information including: - email (str): Email address - phone (str): Phone number - address (str): Physical address - date_of_birth (str): Date of birth - gender (str): Gender identity - occupation (str): Occupation notes (str, optional): Additional notes about the client Returns: JSON response with created client information """ global agent if not agent: return jsonify({ 'success': False, 'message': 'Agent not initialized. Please initialize the agent first.' }), 400 if not agent.database_manager.case: return jsonify({ 'success': False, 'message': 'A case must be loaded before creating a client' }), 400 data = request.json # Validate required fields if not data.get('name'): return jsonify({ 'success': False, 'message': 'Client name is required' }), 400 try: client = agent.database_manager.add_case_client( case=agent.database_manager.case, **data ) return jsonify({ 'success': True, 'message': f'Client "{client.name}" created successfully', 'client': { 'id': client.id, 'name': client.name, 'email': client.email, 'phone': client.phone, 'address': client.address, 'date_of_birth': client.date_of_birth, 'gender': client.gender, 'occupation': client.occupation } }), 201 except Exception as e: return jsonify({ 'success': False, 'message': f'Error creating client: {str(e)}' }), 500 @app.route('/api/update_client', methods=['PUT']) def update_client(): """ Update an existing client. Request Body: client_id (str): ID of the client to update name (str, optional): Client's full name email (str, optional): Email address phone (str, optional): Phone number address (str, optional): Physical address date_of_birth (str, optional): Date of birth gender (str, optional): Gender identity occupation (str, optional): Occupation notes (str, optional): Additional notes about the client Returns: JSON response with updated client information """ global agent if not agent: return jsonify({ 'success': False, 'message': 'Agent not initialized. Please initialize the agent first.' }), 400 data = request.json client_id = data.get('client_id') if not client_id: return jsonify({ 'success': False, 'message': 'Client ID is required for updates' }), 400 try: # Get the existing client client = agent.database_manager.get_client_by_id(client_id) if not client: return jsonify({ 'success': False, 'message': f'Client with ID "{client_id}" not found' }), 404 # Update only the provided fields if 'name' in data: client.name = data['name'] if 'notes' in data: client.notes = data['notes'] # Handle client_details structure - update the JSON field properly client_details = client.client_details.copy() if client.client_details else {} # Update client_details from both flat structure and nested structure if 'email' in data: client_details['email'] = data['email'] if 'phone' in data: client_details['phone'] = data['phone'] if 'address' in data: client_details['address'] = data['address'] if 'date_of_birth' in data: client_details['date_of_birth'] = data['date_of_birth'] if 'gender' in data: client_details['gender'] = data['gender'] if 'occupation' in data: client_details['occupation'] = data['occupation'] # Also handle nested client_details structure for backward compatibility nested_details = data.get('client_details', {}) if 'email' in nested_details: client_details['email'] = nested_details['email'] if 'phone' in nested_details: client_details['phone'] = nested_details['phone'] if 'address' in nested_details: client_details['address'] = nested_details['address'] if 'date_of_birth' in nested_details: client_details['date_of_birth'] = nested_details['date_of_birth'] if 'gender' in nested_details: client_details['gender'] = nested_details['gender'] if 'occupation' in nested_details: client_details['occupation'] = nested_details['occupation'] # Update the client_details JSON field client.client_details = client_details # Commit changes and refresh agent.database_manager.session.commit() agent.database_manager.session.refresh(client) # If this is the current client, update the agent's reference if agent.database_manager.client and agent.database_manager.client.id == client.id: agent.database_manager.client = client return jsonify({ 'success': True, 'message': f'Client "{client.name}" updated successfully', 'client': { 'id': client.id, 'name': client.name, 'email': client.email, 'phone': client.phone, 'address': client.address, 'date_of_birth': client.date_of_birth, 'gender': client.gender, 'occupation': client.occupation, 'notes': client.notes } }), 200 except Exception as e: return jsonify({ 'success': False, 'message': f'Error updating client: {str(e)}' }), 500 # LEGAL REFERENCE AND RESEARCH ENDPOINTS @app.route('/api/set_legal_references', methods=['POST']) def set_legal_references(): """ Set up legal references for the current case by searching CanLII. This endpoint automatically searches Canadian legal databases for relevant case law and legislation based on the current case details. The AI evaluates and filters the most relevant legal precedents. Returns: JSON response indicating success/failure of reference setup """ global agent if not agent: return jsonify({ 'success': False, 'message': 'Agent not initialized. Please initialize the agent first.' }), 400 if not agent.database_manager.case: return jsonify({ 'success': False, 'message': 'A case must be loaded before setting legal references' }), 400 try: # Search CanLII and set up legal references agent.set_legal_references() return jsonify({ 'success': True, 'message': 'Legal references have been successfully researched and set for this case' }), 200 except Exception as e: return jsonify({ 'success': False, 'message': f'Error setting legal references: {str(e)}' }), 500 # CHAT AND QUESTION ANSWERING ENDPOINTS @app.route('/api/ask_question', methods=['POST']) def ask_question(): """ Process a legal question and provide an AI-generated response. This endpoint is the core of the legal assistance functionality. It uses the RAG pipeline to provide contextual answers based on: - Uploaded case documents - Legal precedents and references - Case and client context - Conversation history Request Body: question (str): The legal question to ask Returns: JSON response with the AI-generated answer and metadata """ global agent, message_count if not agent: return jsonify({ 'success': False, 'message': 'Agent not initialized. Please initialize the agent first.' }), 400 if not agent.database_manager.case: return jsonify({ 'success': False, 'message': 'A case must be loaded before asking questions' }), 400 data = request.json question = data.get('question', '').strip() if not question: return jsonify({ 'success': False, 'message': 'Question cannot be empty' }), 400 try: # Process the question through the RAG pipeline chat_interaction = agent.execute_actions(question) if not chat_interaction: return jsonify({ 'success': False, 'message': 'Failed to process the question. Please try again.' }), 500 # Increment message count for advice generation tracking message_count += 1 response_data = { 'success': True, 'question': chat_interaction.user_prompt, 'answer': chat_interaction.assistant_response, 'timestamp': chat_interaction.timestamp.isoformat() if chat_interaction.timestamp else None, 'message_count': message_count, } # Generate periodic legal advice (every 5 messages) if message_count % 5 == 0: try: advice_response = agent.generate_passive_legal_information() if advice_response: response_data['new_advice'] = advice_response except Exception as advice_error: # Don't fail the whole request if advice generation fails print(f"Warning: Failed to generate passive legal advice: {advice_error}") return jsonify(response_data), 200 except Exception as e: return jsonify({ 'success': False, 'message': f'Error processing question: {str(e)}' }), 500 @app.route('/api/get_chat_history', methods=['GET']) def get_chat_history(): """ Retrieve the chat history for the current case. Returns: JSON response with list of previous questions and answers """ global agent if not agent: return jsonify({ 'success': False, 'message': 'Agent not initialized. Please initialize the agent first.' }), 400 if not agent.database_manager.case: return jsonify({ 'success': False, 'message': 'A case must be loaded to retrieve chat history' }), 400 try: chat_history = agent.database_manager.get_chat_history() history_list = [] for chat in chat_history: history_list.append({ 'id': chat.id, 'question': chat.user_prompt, 'answer': chat.assistant_response, 'timestamp': chat.timestamp.isoformat() if chat.timestamp else None }) return jsonify({ 'success': True, 'history': history_list, 'total': len(history_list) }), 200 except Exception as e: return jsonify({ 'success': False, 'message': f'Error fetching chat history: {str(e)}' }), 500 # DOCUMENT MANAGEMENT ENDPOINTS @app.route('/api/get_documents', methods=['GET']) def get_documents(): """ Retrieve all documents uploaded for the current case. Returns: JSON response with list of uploaded documents and their metadata """ global agent if not agent: return jsonify({ 'success': False, 'message': 'Agent not initialized. Please initialize the agent first.' }), 400 if not agent.database_manager.case: return jsonify({ 'success': False, 'message': 'A case must be loaded to retrieve documents' }), 400 try: documents = agent.database_manager.get_uploaded_documents() doc_list = [] for doc in documents: doc_list.append({ 'id': doc.id, 'source_name': doc.source_name, 'title': doc.title, 'description': doc.description, 'language': doc.language, 'uploaded_at': doc.uploaded_at.isoformat() if doc.uploaded_at else None }) return jsonify({ 'success': True, 'documents': doc_list, 'total': len(doc_list) }), 200 except Exception as e: return jsonify({ 'success': False, 'message': f'Error fetching documents: {str(e)}' }), 500 @app.route('/api/upload_document', methods=['POST']) def upload_document(): """ Upload documents to the current case for semantic search and analysis. This endpoint accepts both local file paths and web URLs, processes them through OCR if needed, and indexes them in the vector database for intelligent retrieval during legal consultations. Request Body: file_paths (list): List of local file paths to upload urls (list): List of web URLs to scrape and index Returns: JSON response indicating upload success and number of documents processed """ global agent if not agent: return jsonify({ 'success': False, 'message': 'Agent not initialized. Please initialize the agent first.' }), 400 if not agent.database_manager.case: return jsonify({ 'success': False, 'message': 'A case must be loaded before uploading documents' }), 400 data = request.json file_paths = data.get('file_paths', []) urls = data.get('urls', []) if not file_paths and not urls: return jsonify({ 'success': False, 'message': 'Either file paths or URLs must be provided' }), 400 try: # Upload and index documents result = agent.upload_documents(web_urls=urls, doc_files=file_paths) total_uploaded = len(file_paths) + len(urls) return jsonify({ 'success': True, 'message': f'Successfully uploaded and indexed {total_uploaded} documents', 'files_processed': len(file_paths), 'urls_processed': len(urls) }), 200 except Exception as e: return jsonify({ 'success': False, 'message': f'Error uploading documents: {str(e)}' }), 500 @app.route('/api/delete_document', methods=['DELETE']) def delete_document(): """ Delete a document from the current case. This removes the document from both the database and vector store, ensuring it won't appear in future search results. Request Body: document_id (str): ID of the document to delete Returns: JSON response indicating deletion success/failure """ global agent if not agent: return jsonify({ 'success': False, 'message': 'Agent not initialized. Please initialize the agent first.' }), 400 if not agent.database_manager.case: return jsonify({ 'success': False, 'message': 'A case must be loaded to delete documents' }), 400 data = request.json document_id = data.get('document_id', '') if not document_id: return jsonify({ 'success': False, 'message': 'Document ID is required' }), 400 try: success = agent.database_manager.delete_documents([document_id]) if not success: return jsonify({ 'success': False, 'message': f'Document with ID "{document_id}" not found' }), 404 return jsonify({ 'success': True, 'message': 'Document deleted successfully' }), 200 except Exception as e: return jsonify({ 'success': False, 'message': f'Error deleting document: {str(e)}' }), 500 # UTILITY AND SYSTEM ENDPOINTS @app.route('/api/save', methods=['POST']) def save(): """ Save all pending changes to the database and vector store. This endpoint commits all database transactions and saves the current state of the vector store to disk. Returns: JSON response indicating save success/failure """ global agent if not agent: return jsonify({ 'success': False, 'message': 'Agent not initialized. Please initialize the agent first.' }), 400 try: success = agent.database_manager.save_all() if success: return jsonify({ 'success': True, 'message': 'All changes saved successfully' }), 200 else: return jsonify({ 'success': True, 'message': 'No changes to save' }), 200 except Exception as e: return jsonify({ 'success': False, 'message': f'Error saving changes: {str(e)}' }), 500 @app.route('/api/get_general_advice', methods=['GET']) def get_general_advice(): """ Generate general legal advice and helpful resources. This endpoint provides periodic legal guidance and educational resources that may be relevant to legal practice in general. Returns: JSON response with general legal advice and resource links """ global agent try: if agent and agent.database_manager.case: # Generate contextual advice if case is loaded advice_response = agent.generate_passive_legal_information() passive_advice = advice_response.get('advice', 'No specific advice available at this time.') resources = advice_response.get('helpful_resources', []) else: # Provide generic legal guidance passive_advice = ( "Legal processes often involve several key steps: identifying the appropriate " "forms and procedures, completing and submitting required documents, understanding " "deadlines and timelines, and following ethical guidelines for honesty and " "confidentiality. Whether you are filing a case, responding to legal notices, " "or simply completing legal forms, it is important to ensure accuracy and " "completeness. Procedures and requirements can vary significantly by jurisdiction " "and case type, so consulting with qualified legal professionals is always recommended " "for specific legal matters." ) resources = [ "Government of Canada Justice Laws Website: https://laws-lois.justice.gc.ca/", "CanLII - Canadian Legal Information Institute: https://www.canlii.org/", "Legal Aid Offices (varies by province)" ] return jsonify({ 'success': True, 'advice': passive_advice, 'resources': resources, 'disclaimer': 'This information is for educational purposes only and does not constitute legal advice.' }), 200 except Exception as e: return jsonify({ 'success': False, 'message': f'Error generating general advice: {str(e)}' }), 500 @app.route('/') def serve_frontend(path): if path.startswith('api/'): abort(404) requested_path = os.path.join(FRONTEND_BUILD_DIR, path) if os.path.exists(requested_path) and not os.path.isdir(requested_path): return send_from_directory(FRONTEND_BUILD_DIR, path) index_path = os.path.join(FRONTEND_BUILD_DIR, 'index.html') if os.path.exists(index_path): return send_from_directory(FRONTEND_BUILD_DIR, 'index.html') abort(404) # APPLICATION STARTUP if __name__ == '__main__': """ Start the Flask development server. When launched by the LegalNexus desktop app, the Electron main process picks a free port and passes it via LEGALNEXUS_PORT (or FLASK_RUN_PORT). For standalone dev runs we still default to 5000. For production deployment, use a proper WSGI server like Gunicorn. """ port = int( os.environ.get('LEGALNEXUS_PORT') or os.environ.get('FLASK_RUN_PORT') or 5000 ) # In the desktop wrapper we don't want Flask's reloader (it forks a # child the Electron process can't track for clean shutdown). in_desktop = bool(os.environ.get('LEGALNEXUS_PORT')) debug = not in_desktop print("Starting Legal RAG Pipeline API Server...") print(f"API will be available at: http://127.0.0.1:{port}") app.run( debug=debug, host='127.0.0.1' if in_desktop else '0.0.0.0', port=port, use_reloader=False, )