Spaces:
Sleeping
Sleeping
| """ | |
| 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 | |
| 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' | |
| } | |
| }) | |
| def api_status(): | |
| return jsonify({ | |
| 'status': 'ok', | |
| 'message': 'Legal RAG Pipeline API is running', | |
| 'version': '1.0.0', | |
| }) | |
| # AGENT INITIALIZATION AND CONFIGURATION | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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, | |
| ) | |