Compare commits

...

17 Commits

Author SHA1 Message Date
EC2 Default User a76579785f latest fix 2025-06-18 16:44:10 +00:00
owusu 64e22d34fb Merge pull request 'fix server issues' (#7) from dev into main
Reviewed-on: #7
2025-06-18 16:30:42 +00:00
OwusuBlessing 3cae5feee7 fix server issues 2025-06-18 17:27:50 +01:00
owusu cfe4d8b619 Merge pull request 'dev' (#6) from dev into main
Reviewed-on: #6
2025-06-18 16:18:32 +00:00
OwusuBlessing 60d2368be9 added 2025-06-18 17:15:19 +01:00
OwusuBlessing 5b055f2870 added please 2025-06-18 17:09:36 +01:00
owusu 96b3a4407d Merge pull request 'dev' (#5) from dev into main
Reviewed-on: #5
2025-06-18 15:44:41 +00:00
OwusuBlessing cbfdd96412 fix chat issues 2025-06-18 16:39:29 +01:00
OwusuBlessing 95a4d0f035 aded 2025-06-18 16:00:38 +01:00
owusu 86ba5416c2 Merge pull request 'updated start sh' (#4) from dev into main
Reviewed-on: #4
2025-06-11 21:09:41 +00:00
OwusuBlessing 981901f491 updated start sh 2025-06-11 22:07:25 +01:00
owusu 14821cb487 Merge pull request 'added gunicorn' (#3) from dev into main
Reviewed-on: #3
2025-06-11 20:56:51 +00:00
OwusuBlessing 6d14e65907 added gunicorn 2025-06-11 21:53:57 +01:00
owusu 34ddbd50dd Merge pull request 'updated eployment script' (#2) from dev into main
Reviewed-on: #2
2025-06-11 20:29:14 +00:00
OwusuBlessing 95d99f7ce9 updated eployment script 2025-06-11 21:26:34 +01:00
owusu bf2379e39d Merge pull request 'prod deploy version' (#1) from dev into main
Reviewed-on: #1
2025-06-11 16:58:47 +00:00
OwusuBlessing 3bd6213a8d prod deploy version 2025-06-11 17:40:17 +01:00
9 changed files with 393 additions and 228 deletions
+58 -6
View File
@@ -1,6 +1,6 @@
import os
from typing import Optional
from fastapi import FastAPI, HTTPException, Security, Depends
from fastapi import FastAPI, HTTPException, Security, Depends, Request
from fastapi.security import APIKeyHeader
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
@@ -27,9 +27,23 @@ import os
from PyPDF2 import PdfReader
from config import QUIZ_TYPES
from config import Config
import logging
import time
# Load environment variables
load_dotenv()
API_KEY = os.getenv("API_KEY_ACCESS")
from config import Config
API_KEY = Config.API_KEY_ACCESS
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# Also configure uvicorn logger
uvicorn_logger = logging.getLogger("uvicorn.access")
uvicorn_logger.setLevel(logging.INFO)
base_path = os.path.join("data", "config_files")
QUESTIONS_PATH = os.path.join(base_path, "questions.json")
@@ -46,6 +60,40 @@ app = FastAPI(
version="1.0.0"
)
# Add request logging middleware
@app.middleware("http")
async def log_requests(request: Request, call_next):
start_time = time.time()
# Log incoming request (using both logger and print for visibility)
log_msg = f"🔥 INCOMING REQUEST: {request.method} {request.url}"
logger.info(log_msg)
print(log_msg)
# Get request body for POST requests
if request.method == "POST":
body = await request.body()
body_msg = f"🔥 Request Body: {body.decode('utf-8') if body else 'Empty'}"
logger.info(body_msg)
print(body_msg)
# Re-create request with body for downstream processing
async def receive():
return {"type": "http.request", "body": body}
request._receive = receive
response = await call_next(request)
# Log response
process_time = time.time() - start_time
response_msg = f"🔥 RESPONSE: {response.status_code} - Time: {process_time:.4f}s"
logger.info(response_msg)
print(response_msg)
return response
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
@@ -68,6 +116,7 @@ async def get_api_key(api_key_header: str = Security(api_key_header)) -> str:
)
token = api_key_header.split(' ')[1]
print(f"Token : {token}")
if token != API_KEY:
raise HTTPException(
status_code=401,
@@ -288,7 +337,7 @@ async def generate_pdf_endpoint(
if request.form_id:
print(f"Fetching form response for form_id: {request.form_id}") # Debugging print
try:
x_api_key = os.getenv("BACKEND_XAPI_KEY")
x_api_key = Config.BACKEND_XAPI_KEY
url = f"{backend_base_url}/v3/api/custom/theme-document/answer/{request.form_id}?x-project={x_api_key}"
result = requests.get(url)
result.raise_for_status() # Ensure we raise an error for bad responses
@@ -410,10 +459,13 @@ async def generate_quiz_endpoint(
status_code=500,
detail=f"Unexpected error during quiz generation: {str(e)}"
)
@app.get("/health")
async def health_check():
"""Health check endpoint to verify the service is running."""
return {"status": "healthy", "timestamp": datetime.now().isoformat()}
@app.on_event("startup")
async def startup_event():
"""Initialize required components on startup"""
+3
View File
@@ -1,4 +1,7 @@
import os
from dotenv import load_dotenv
load_dotenv()
QUIZ_TYPES = {
1: {
"name": "Single Line Text Inputs",
-216
View File
@@ -1,216 +0,0 @@
#!/bin/bash
# Configuration
SERVER_USER="your_username"
SERVER_IP="your_server_ip"
APP_DIR="/path/to/your/app"
PORT=5042
PYTHON_VERSION="3.11"
WORKERS=4
THREADS=2
TIMEOUT=120
MAX_REQUESTS=1000
MAX_REQUESTS_JITTER=50
DEBUG_MODE=false # Set to true for verbose output
# Colors for output
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Logging function
log() {
local level=$1
local message=$2
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
case $level in
"INFO")
echo -e "${BLUE}[$timestamp] INFO: $message${NC}"
;;
"SUCCESS")
echo -e "${GREEN}[$timestamp] SUCCESS: $message${NC}"
;;
"WARNING")
echo -e "${YELLOW}[$timestamp] WARNING: $message${NC}"
;;
"ERROR")
echo -e "${RED}[$timestamp] ERROR: $message${NC}"
;;
esac
}
# Debug logging function
debug_log() {
if [ "$DEBUG_MODE" = true ]; then
echo -e "${YELLOW}[DEBUG] $1${NC}"
fi
}
# Error handling
set -e
trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG
trap 'if [ $? -ne 0 ]; then log "ERROR" "Command failed: $last_command"; exit 1; fi' EXIT
log "INFO" "Starting deployment process..."
# Check if required packages are installed
log "INFO" "Checking required packages..."
if ! command -v ssh &> /dev/null; then
log "ERROR" "SSH is not installed. Please install it first."
exit 1
fi
# Create requirements.txt if it doesn't exist
if [ ! -f "requirements.txt" ]; then
log "INFO" "Creating requirements.txt..."
echo "fastapi==0.109.2
uvicorn==0.27.1
gunicorn==21.2.0
python-dotenv==1.0.1
langchain-openai==0.0.5
requests==2.31.0
PyPDF2==3.0.1" > requirements.txt
debug_log "Created requirements.txt with specific versions"
fi
# Create gunicorn config file
log "INFO" "Creating Gunicorn configuration..."
cat > gunicorn_config.py << EOL
import multiprocessing
import os
# Server socket
bind = "0.0.0.0:${PORT}"
backlog = 2048
# Worker processes
workers = ${WORKERS}
worker_class = "uvicorn.workers.UvicornWorker"
worker_connections = 1000
timeout = ${TIMEOUT}
keepalive = 2
# Process naming
proc_name = "firefighter"
pythonpath = "."
# Logging
accesslog = "logs/access.log"
errorlog = "logs/error.log"
loglevel = "info"
# Server mechanics
daemon = False
pidfile = "gunicorn.pid"
umask = 0
user = None
group = None
tmp_upload_dir = None
# SSL
keyfile = None
certfile = None
# Server hooks
def on_starting(server):
pass
def on_reload(server):
pass
def on_exit(server):
pass
# Worker lifecycle
max_requests = ${MAX_REQUESTS}
max_requests_jitter = ${MAX_REQUESTS_JITTER}
graceful_timeout = 30
preload_app = True
# Debug
reload = False
reload_engine = "auto"
spew = False
# Server mechanics
check_config = False
preload_app = True
EOL
debug_log "Created gunicorn_config.py with specified settings"
# Copy files to server
log "INFO" "Copying files to server..."
debug_log "Using SCP to copy files to $SERVER_USER@$SERVER_IP:$APP_DIR/"
scp -r ./* $SERVER_USER@$SERVER_IP:$APP_DIR/
# SSH into server and set up environment
log "INFO" "Setting up environment on server..."
ssh $SERVER_USER@$SERVER_IP << EOF
set -e
cd $APP_DIR
# Install Python 3.11 if not present
if ! command -v python${PYTHON_VERSION} &> /dev/null; then
echo "Installing Python ${PYTHON_VERSION}..."
sudo apt-get update
sudo apt-get install -y software-properties-common
sudo add-apt-repository -y ppa:deadsnakes/ppa
sudo apt-get update
sudo apt-get install -y python${PYTHON_VERSION} python${PYTHON_VERSION}-venv python${PYTHON_VERSION}-dev
fi
# Create virtual environment if it doesn't exist
if [ ! -d "venv" ]; then
python${PYTHON_VERSION} -m venv venv
fi
# Activate virtual environment and install dependencies
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
# Create systemd service file
sudo tee /etc/systemd/system/firefighter.service << 'EOL'
[Unit]
Description=Fire Fighter Interview API
After=network.target
[Service]
User=$SERVER_USER
WorkingDirectory=$APP_DIR
Environment="PATH=$APP_DIR/venv/bin"
Environment="PYTHONPATH=$APP_DIR"
ExecStart=$APP_DIR/venv/bin/gunicorn -c gunicorn_config.py app:app
Restart=always
RestartSec=5
StartLimitInterval=0
[Install]
WantedBy=multi-user.target
EOL
# Create log directory
mkdir -p logs
# Reload systemd and start service
sudo systemctl daemon-reload
sudo systemctl enable firefighter
sudo systemctl restart firefighter
# Check service status
sudo systemctl status firefighter
EOF
log "SUCCESS" "Deployment completed!"
log "INFO" "Your application should now be running at http://$SERVER_IP:$PORT"
log "INFO" "Check logs at $APP_DIR/logs/"
# Print helpful commands
echo -e "\n${BLUE}Useful commands:${NC}"
echo "View service status: sudo systemctl status firefighter"
echo "View logs: sudo journalctl -u firefighter -f"
echo "Restart service: sudo systemctl restart firefighter"
echo "Stop service: sudo systemctl stop firefighter"
+1
View File
@@ -0,0 +1 @@
387857
+45
View File
@@ -0,0 +1,45 @@
import multiprocessing
import os
# Server socket
bind = "0.0.0.0:5042"
backlog = 2048
# Worker processes
workers = 4
worker_class = "uvicorn.workers.UvicornWorker"
worker_connections = 1000
timeout = 120
keepalive = 2
# Process naming
proc_name = "firefighter"
pythonpath = "."
# Logging
accesslog = "logs/access.log"
errorlog = "logs/error.log"
loglevel = "info"
# Server mechanics
daemon = False
pidfile = "gunicorn.pid"
umask = 0
user = None
group = None
tmp_upload_dir = None
# Worker lifecycle
max_requests = 1000
max_requests_jitter = 50
graceful_timeout = 30
preload_app = True
# Debug
reload = False
reload_engine = "auto"
spew = False
# Server mechanics
check_config = False
preload_app = True
+4
View File
@@ -25,3 +25,7 @@ reportlab==4.3.1
python-docx==1.1.2
unstructured==0.17.2
pypdf==5.4.0
gunicorn==23.0.0
python-dotenv
+226
View File
@@ -0,0 +1,226 @@
#!/bin/bash
# Configuration
REPO_URL="http://owusu:890eccfcea010beb94a0adba246aaf9258330b70@23.29.118.76:3000/owusu/ds-fire-fighter.git"
APP_DIR="/home/ec2-user/ds-fire-fighter"
BRANCH="main"
PYTHON_VERSION="3.11"
WORKERS=4
THREADS=2
TIMEOUT=120
MAX_REQUESTS=1000
MAX_REQUESTS_JITTER=50
DEBUG_MODE=true
# Colors for output
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Logging function
log() {
local level=$1
local message=$2
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
case $level in
"INFO")
echo -e "${BLUE}[$timestamp] INFO: $message${NC}"
;;
"SUCCESS")
echo -e "${GREEN}[$timestamp] SUCCESS: $message${NC}"
;;
"WARNING")
echo -e "${YELLOW}[$timestamp] WARNING: $message${NC}"
;;
"ERROR")
echo -e "${RED}[$timestamp] ERROR: $message${NC}"
;;
esac
}
# Debug logging function
debug_log() {
if [ "$DEBUG_MODE" = true ]; then
echo -e "${YELLOW}[DEBUG] $1${NC}"
fi
}
# Error handling
set -e
trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG
trap 'if [ $? -ne 0 ]; then log "ERROR" "Command failed: $last_command"; exit 1; fi' EXIT
# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Function to install Python 3.11
install_python() {
log "INFO" "Installing Python ${PYTHON_VERSION}..."
sudo yum update -y
sudo yum groupinstall -y "Development Tools"
sudo yum install -y openssl-devel bzip2-devel libffi-devel xz-devel
# Install Python 3.11 from source
cd /tmp
wget https://www.python.org/ftp/python/3.11.0/Python-3.11.0.tgz
tar xzf Python-3.11.0.tgz
cd Python-3.11.0
./configure --enable-optimizations
make -j $(nproc)
sudo make altinstall
# Create symlink for python3.11
sudo ln -sf /usr/local/bin/python3.11 /usr/bin/python3.11
sudo ln -sf /usr/local/bin/pip3.11 /usr/bin/pip3.11
}
# Function to setup git repository
setup_repo() {
if [ ! -d "$APP_DIR" ]; then
log "INFO" "Cloning repository..."
git clone $REPO_URL $APP_DIR
else
log "INFO" "Updating repository..."
cd $APP_DIR
git fetch origin
git reset --hard origin/$BRANCH
git clean -fd
fi
}
# Function to setup virtual environment
setup_venv() {
log "INFO" "Setting up virtual environment..."
if [ ! -d "$APP_DIR/venv" ]; then
python3.11 -m venv $APP_DIR/venv
fi
source $APP_DIR/venv/bin/activate
pip install --upgrade pip
pip install -r $APP_DIR/requirements.txt
}
# Function to create gunicorn config
create_gunicorn_config() {
log "INFO" "Creating Gunicorn configuration..."
cat > $APP_DIR/gunicorn_config.py << EOL
import multiprocessing
import os
# Server socket
bind = "0.0.0.0:5042"
backlog = 2048
# Worker processes
workers = ${WORKERS}
worker_class = "uvicorn.workers.UvicornWorker"
worker_connections = 1000
timeout = ${TIMEOUT}
keepalive = 2
# Process naming
proc_name = "firefighter"
pythonpath = "."
# Logging
accesslog = "logs/access.log"
errorlog = "logs/error.log"
loglevel = "info"
# Server mechanics
daemon = False
pidfile = "gunicorn.pid"
umask = 0
user = None
group = None
tmp_upload_dir = None
# Worker lifecycle
max_requests = ${MAX_REQUESTS}
max_requests_jitter = ${MAX_REQUESTS_JITTER}
graceful_timeout = 30
preload_app = True
# Debug
reload = False
reload_engine = "auto"
spew = False
# Server mechanics
check_config = False
preload_app = True
EOL
}
# Function to setup systemd service
setup_service() {
log "INFO" "Setting up systemd service..."
sudo tee /etc/systemd/system/firefighter.service << EOL
[Unit]
Description=Fire Fighter Interview API
After=network.target
[Service]
User=ec2-user
WorkingDirectory=$APP_DIR
Environment="PATH=$APP_DIR/venv/bin"
Environment="PYTHONPATH=$APP_DIR"
ExecStart=$APP_DIR/start.sh
Restart=always
RestartSec=5
StartLimitInterval=0
[Install]
WantedBy=multi-user.target
EOL
sudo systemctl daemon-reload
sudo systemctl enable firefighter
}
# Main deployment process
main() {
log "INFO" "Starting deployment process..."
# Check and install Python if needed
if ! command_exists python3.11; then
install_python
fi
# Setup repository
setup_repo
# Create logs directory
mkdir -p $APP_DIR/logs
# Setup virtual environment and install dependencies
setup_venv
# Create gunicorn config
create_gunicorn_config
# Setup and start service
setup_service
sudo systemctl restart firefighter
log "SUCCESS" "Deployment completed!"
log "INFO" "Your application should now be running at http://localhost:5042"
# Print helpful commands
echo -e "\n${BLUE}Useful commands:${NC}"
echo "View service status: sudo systemctl status firefighter"
echo "View logs: sudo journalctl -u firefighter -f"
echo "View application logs: tail -f $APP_DIR/logs/access.log"
echo "View error logs: tail -f $APP_DIR/logs/error.log"
echo "Restart service: sudo systemctl restart firefighter"
echo "Stop service: sudo systemctl stop firefighter"
echo "Start service: sudo systemctl start firefighter"
echo "Deploy new version: ./server_deploy.sh"
}
# Run main function
main
+46 -6
View File
@@ -62,6 +62,8 @@ def setup_prompt_template(theme: int, resume: str,full_history=None,form_respons
("system", chat_prompt(theme, resume,full_history,form_response,generate_theme)),
MessagesPlaceholder(variable_name="messages")
])
def fetch_conversation_history(conversation_id: str) -> List[Message]:
"""
Fetch conversation history from the API using the conversation ID.
@@ -74,18 +76,54 @@ def fetch_conversation_history(conversation_id: str) -> List[Message]:
response = requests.get(url)
response.raise_for_status() # Raise an error for bad responses
data = response.json()["data"] # First JSON parse
data = json.loads(json.loads(data))
if isinstance(data, str):
print("Data is a string, parsing as JSON...")
data = json.loads(data)
# Parse the API response into Message objects
messages = []
for item in data:
role = item.get("role", "unknown")
content = item.get("content", "")
timestamp = datetime.now().isoformat() # Use current timestamp if not provided
messages.append(Message(role=role, content=content))
# Check if data exists and is a list
if data and isinstance(data, list):
for item in data:
# Check if item is a dictionary
if isinstance(item, dict):
role = item.get("role", "unknown")
content = item.get("content", "")
timestamp = datetime.now().isoformat() # Use current timestamp if not provided
messages.append(Message(role=role, content=content))
elif isinstance(item, str):
# If item is a string, it might be JSON that needs parsing
try:
parsed_item = json.loads(item)
if isinstance(parsed_item, dict):
role = parsed_item.get("role", "unknown")
content = parsed_item.get("content", "")
messages.append(Message(role=role, content=content))
else:
print(f"Parsed item is not a dict: {parsed_item}")
except json.JSONDecodeError as json_err:
print(f"Failed to parse JSON string: {item}, error: {json_err}")
else:
print(f"Unexpected item type: {type(item)} for item: {item}")
else:
print(f"No data or data is not a list. Data: {data}")
return messages
except requests.RequestException as e:
print(f"Error fetching conversation history: {e}")
return []
except KeyError as e:
print(f"Expected key not found in response: {e}")
return []
except Exception as e:
print(f"Unexpected error: {e}")
return []
def convert_to_langchain_messages(messages: List[Message]) -> List[HumanMessage | AIMessage]:
@@ -130,6 +168,8 @@ def ai_chat(query: str, conversation_id: str, theme_id: int, resume: str, full_h
# Fetch conversation history from the API
history = fetch_conversation_history(conversation_id)
print(history)
config = {"configurable": {"thread_id": conversation_id}}
language = "English"
Executable
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
# Create logs directory if it doesn't exist
mkdir -p logs
# Activate virtual environment
source venv/bin/activate
# Start Gunicorn with the configuration
gunicorn -c gunicorn_config.py app:app