how to run below code?

#!/usr/bin/env python
# coding: utf-8

# --- Import required packages ---
import os
import logging
import re
import json
from fastapi import FastAPI, HTTPException, Depends, Query
from pydantic import BaseModel, Field
from typing import List, Dict, Any, Optional
from dotenv import load_dotenv

# Adding advanced NLP packages for better policy text analysis
import spacy
import nltk
from nltk.tokenize import word_tokenize, sent_tokenize

from langchain_community.utilities.sql_database import SQLDatabase
from langchain_community.document_loaders import PyPDFLoader, Docx2txtLoader
from langchain.text_splitters import RecursiveCharacterTextSplitter
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.chat_models import ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain.tools.retriever.retriever_tool import create_retriever_tool
from langchain_community.agent_toolkits import SQLDatabaseToolkit
from langchain.tools import tool  # Importing tool decorator

from crewai import Agent, Task, Crew, Process

# --- Logging Setup ---
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s"
)
logger = logging.getLogger(__name__)

# --- Load environment variables ---
load_dotenv()

# --- Configuration ---
openai_api_key = os.getenv("OPENAI_API_KEY")
if not openai_api_key:
    raise ValueError("OPENAI_API_KEY is missing. Check your .env file.")

db_user = os.getenv("DB_USER", "")
db_password = os.getenv("DB_PASSWORD", "")
db_host = os.getenv("DB_HOST", "")
db_name = os.getenv("DB_NAME", "classicmodels")

folder_path = os.getenv("DOCS_PATH", "./docs")  # Use env var or default to ./docs

# --- Initialize FastAPI ---
# ** Added FastAPI initialization **
app = FastAPI(
    title="Policy Compliance API",
    description="API for checking policy compliance using NLP and database analysis",
    version="1.0.0"
)

# --- Initialize NLP components ---
nlp = None
try:
    # Initialize NLP components from code 2
    nltk.download('punkt', quiet=True)
    if not spacy.util.is_package('en_core_web_sm'):
        logger.info("Downloading Spacy model...")
        spacy.cli.download('en_core_web_sm')
    nlp = spacy.load("en_core_web_sm")
except Exception as e:
    logger.error(f"Error initializing NLP components: {e}")

# --- Define Pydantic Models ---
# ** Added Pydantic models for API requests/responses **
class ComplianceQuery(BaseModel):
    query_text: str = Field(..., description="Natural language query about policy compliance")
    department: Optional[str] = Field(None, description="Optional department filter")
    
class PolicyViolation(BaseModel):
    employee_id: str
    first_name: str
    last_name: str
    department: str
    violation_type: str
    policy_reference: str
    
class ComplianceResponse(BaseModel):
    query: str
    violations: List[PolicyViolation]
    total_count: int
    relevant_policies: List[str]

class PolicySearchQuery(BaseModel):
    search_text: str = Field(..., description="Text to search for in policy documents")

class PolicySearchResult(BaseModel):
    document_name: str
    content: str
    relevance_score: float

class PolicySearchResponse(BaseModel):
    query: str
    results: List[PolicySearchResult]

# --- Define SQL Query Templates ---
# ** Modified to prevent SQL injection **
SQL_QUERY_TEMPLATES = {
    "violation_count": "SELECT COUNT(*) FROM employees WHERE ?;",
    "salary_violation": "SELECT * FROM employees WHERE salary < ?;",
    "policy_breach": "SELECT employee_name FROM violations WHERE policy_id IN (SELECT id FROM policies WHERE criteria = ?);",
    "detailed_violations": "SELECT employee_id, first_name, last_name FROM employees WHERE ?;",
    "department_summary": "SELECT department, COUNT(*) as violation_count FROM employees WHERE ? GROUP BY department;"
}

# --- Load and Split Documents ---
def load_documents(folder_path: str) -> List:
    """Loads PDF and DOCX files from the specified folder."""
    documents = []
    if not os.path.exists(folder_path):
        logger.warning(f"Folder does not exist: {folder_path}")
        return documents
    for filename in os.listdir(folder_path):
        file_path = os.path.join(folder_path, filename)
        try:
            if filename.endswith('.pdf'):
                documents.extend(PyPDFLoader(file_path).load())
            elif filename.endswith('.docx'):
                documents.extend(Docx2txtLoader(file_path).load())
            else:
                logger.info(f"Unsupported file type: {filename}")
        except Exception as e:
            logger.error(f"Error loading {filename}: {e}")
    return documents

# ** Enhanced error handling for document loading **
logger.info("Loading policy documents...")
documents = load_documents(folder_path)

if not documents:
    logger.warning("No documents loaded! Vector store may be empty.")
    # ** Added fallback for empty document set **
    documents = [{"page_content": "No policy documents found. Please add documents to the docs folder.", "metadata": {"source": "system"}}]

text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(documents)

logger.info("Creating embeddings and vector store...")
embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key)
vectorstore = Chroma.from_documents(splits, embedding=embeddings)
retriever = vectorstore.as_retriever()

policy_retriever_tool = create_retriever_tool(
    retriever,
    "policy_document_retriever",
    "Searches and returns policy document information."
)

# --- Database Setup ---
# ** Enhanced database connection with better error handling **
logger.info(f"Connecting to database {db_name}@{db_host}")
db = None
db_tools = []
try:
    connection_string = f"mysql+pymysql://{db_user}:{db_password}@{db_host}/{db_name}"
    db = SQLDatabase.from_uri(connection_string)
    llm = ChatOpenAI(model="gpt-4o-mini", temperature=0, openai_api_key=openai_api_key)
    db_toolkit = SQLDatabaseToolkit(db=db, llm=llm)
    db_tools = db_toolkit.get_tools()
    logger.info("Database connection successful")
except Exception as e:
    logger.error(f"Database connection failed: {e}")
    # ** Added more specific error handling **
    if "Access denied" in str(e):
        logger.error("Database credentials are incorrect")
    elif "Unknown database" in str(e):
        logger.error(f"Database '{db_name}' does not exist")
    elif "Can't connect" in str(e):
        logger.error(f"Cannot connect to host '{db_host}'")

# --- Helper function for enhanced criteria mapping ---
def extract_criteria_from_text(text: str) -> Dict[str, Any]:
    """
    Enhanced criteria extraction using regex, heuristics, and NLP.
    Combines pattern matching with NLP capabilities.
    
    ** Completed implementation with SQL injection protection **
    """
    criteria = {}

    # Basic regex patterns
    hire_date_match = re.search(r"employees hired (before|after) (\d{4})", text, re.IGNORECASE)
    if hire_date_match:
        operator = "<" if hire_date_match.group(1).lower() == "before" else ">"
        year = hire_date_match.group(2)
        criteria["hire_date"] = {"operator": operator, "value": f"{year}-01-01"}

    dept_match = re.search(r"department[s]? (is|are|in) ([\w\s,]+)", text, re.IGNORECASE)
    if dept_match:
        depts = [d.strip() for d in dept_match.group(2).split(',')]
        criteria["department"] = {"operator": "IN", "values": depts}
    
    # Enhanced NLP-based extraction
    if nlp:
        doc = nlp(text)
        
        # Extract salary information
        salary_pattern = re.search(r"salary (below|under|less than|above|over|more than) (\d+[k]?)", text, re.IGNORECASE)
        if salary_pattern:
            comparison = salary_pattern.group(1).lower()
            amount = salary_pattern.group(2)
            
            # Convert k to thousands
            if amount.endswith('k'):
                amount = int(amount[:-1]) * 1000
            else:
                amount = int(amount)
                
            operator = "<" if comparison in ["below", "under", "less than"] else ">"
            criteria["salary"] = {"operator": operator, "value": amount}
            
        # Extract date ranges
        for ent in doc.ents:
            if ent.label_ == "DATE":
                date_text = ent.text
                if "since" in text[:ent.start].lower() or "after" in text[:ent.start].lower():
                    criteria["date_range"] = {"operator": ">", "value": date_text}
                elif "before" in text[:ent.start].lower() or "until" in text[:ent.start].lower():
                    criteria["date_range"] = {"operator": "<", "value": date_text}
    
    return criteria

# ** Added function to safely build SQL queries **
def build_safe_sql_query(template: str, criteria: Dict[str, Any]) -> tuple:
    """
    Builds a safe SQL query using parameterized queries to prevent SQL injection.
    Returns the query string and parameters tuple.
    """
    params = []
    conditions = []
    
    for key, value in criteria.items():
        if key == "hire_date":
            conditions.append(f"hire_date {value['operator']} %s")
            params.append(value['value'])
        elif key == "department":
            placeholders = ", ".join(["%s"] * len(value['values']))
            conditions.append(f"department IN ({placeholders})")
            params.extend(value['values'])
        elif key == "salary":
            conditions.append(f"salary {value['operator']} %s")
            params.append(value['value'])
        elif key == "date_range":
            conditions.append(f"event_date {value['operator']} %s")
            params.append(value['value'])
    
    where_clause = " AND ".join(conditions) if conditions else "1=1"
    query = template.replace("?", where_clause)
    
    return query, tuple(params)

# --- CrewAI Implementation ---
# ** Added CrewAI implementation **
def setup_crew():
    """
    Sets up the CrewAI agents and tasks for policy analysis.
    """
    # Define the language model
    llm = ChatOpenAI(model="gpt-4o-mini", temperature=0, openai_api_key=openai_api_key)
    
    # Create agents
    policy_analyst = Agent(
        role="Policy Analyst",
        goal="Analyze policy documents and extract key compliance requirements",
        backstory="You are an expert in corporate policy analysis with years of experience in compliance",
        verbose=True,
        llm=llm,
        tools=[policy_retriever_tool]
    )
    
    data_analyst = Agent(
        role="Data Analyst",
        goal="Query databases to find compliance violations based on policy requirements",
        backstory="You are a skilled data analyst who specializes in finding patterns in employee data",
        verbose=True,
        llm=llm,
        tools=db_tools
    )
    
    compliance_officer = Agent(
        role="Compliance Officer",
        goal="Evaluate policy violations and provide recommendations",
        backstory="You are a senior compliance officer responsible for ensuring company-wide policy adherence",
        verbose=True,
        llm=llm
    )
    
    # Create tasks
    policy_analysis_task = Task(
        description="Analyze policy documents to extract compliance requirements",
        agent=policy_analyst,
        expected_output="A list of key compliance requirements from policy documents"
    )
    
    data_analysis_task = Task(
        description="Query the database to find potential policy violations",
        agent=data_analyst,
        expected_output="A list of potential policy violations with employee details"
    )
    
    compliance_evaluation_task = Task(
        description="Evaluate violations and provide recommendations for compliance improvement",
        agent=compliance_officer,
        expected_output="A comprehensive compliance report with recommendations"
    )
    
    # Create the crew
    crew = Crew(
        agents=[policy_analyst, data_analyst, compliance_officer],
        tasks=[policy_analysis_task, data_analysis_task, compliance_evaluation_task],
        verbose=2,
        process=Process.sequential
    )
    
    return crew

# --- API Endpoints ---
# ** Added FastAPI endpoints **
@app.get("/")
async def root():
    """Root endpoint that returns API information."""
    return {
        "message": "Policy Compliance API",
        "version": "1.0.0",
        "status": "operational",
        "endpoints": ["/compliance/check", "/policy/search"]
    }

@app.post("/compliance/check", response_model=ComplianceResponse)
async def check_compliance(query: ComplianceQuery):
    """
    Checks policy compliance based on natural language query.
    Uses CrewAI to analyze policies and find violations.
    """
    if not db:
        raise HTTPException(status_code=503, detail="Database connection not available")
    
    try:
        # Extract criteria from query
        criteria = extract_criteria_from_text(query.query_text)
        
        # Add department filter if provided
        if query.department:
            criteria["department"] = {"operator": "=", "values": [query.department]}
        
        # Build safe SQL query
        template = SQL_QUERY_TEMPLATES["detailed_violations"]
        sql_query, params = build_safe_sql_query(template, criteria)
        
        # Execute query using database tools
        # This is a simplified version - in production, you'd use the CrewAI setup
        # crew = setup_crew()
        # result = crew.kickoff(inputs={"query": query.query_text, "criteria": criteria})
        
        # For demonstration, we'll mock the response
        violations = [
            PolicyViolation(
                employee_id="E001",
                first_name="John",
                last_name="Doe",
                department="Sales",
                violation_type="Salary Policy",
                policy_reference="Section 3.2 of Compensation Policy"
            ),
            PolicyViolation(
                employee_id="E002",
                first_name="Jane",
                last_name="Smith",
                department="Marketing",
                violation_type="Work Hours",
                policy_reference="Section 2.1 of Employee Handbook"
            )
        ]
        
        # Get relevant policies
        policy_docs = retriever.get_relevant_documents(query.query_text)
        relevant_policies = [doc.metadata.get("source", "Unknown") for doc in policy_docs[:3]]
        
        return ComplianceResponse(
            query=query.query_text,
            violations=violations,
            total_count=len(violations),
            relevant_policies=relevant_policies
        )
        
    except Exception as e:
        logger.error(f"Error processing compliance check: {e}")
        raise HTTPException(status_code=500, detail=f"Error processing request: {str(e)}")

@app.post("/policy/search", response_model=PolicySearchResponse)
async def search_policy(query: PolicySearchQuery):
    """
    Searches policy documents for relevant information.
    """
    try:
        # Get relevant documents
        docs = retriever.get_relevant_documents(query.search_text)
        
        results = []
        for i, doc in enumerate(docs[:5]):  # Return top 5 results
            results.append(
                PolicySearchResult(
                    document_name=doc.metadata.get("source", "Unknown"),
                    content=doc.page_content[:500] + "..." if len(doc.page_content) > 500 else doc.page_content,
                    relevance_score=1.0 - (i * 0.1)  # Mock relevance score
                )
            )
        
        return PolicySearchResponse(
            query=query.search_text,
            results=results
        )
        
    except Exception as e:
        logger.error(f"Error searching policy documents: {e}")
        raise HTTPException(status_code=500, detail=f"Error processing request: {str(e)}")

# --- Main execution ---
if __name__ == "__main__":
    import uvicorn
    
    # Setup CrewAI if needed
    if db:
        logger.info("Setting up CrewAI...")
        crew = setup_crew()
    
    # Run the FastAPI app
    logger.info("Starting API server...")
    uvicorn.run(app, host="0.0.0.0", port=8000)