Compare commits
17 Commits
3fcce3b464
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a76579785f | |||
| 64e22d34fb | |||
| 3cae5feee7 | |||
| cfe4d8b619 | |||
| 60d2368be9 | |||
| 5b055f2870 | |||
| 96b3a4407d | |||
| cbfdd96412 | |||
| 95a4d0f035 | |||
| 86ba5416c2 | |||
| 981901f491 | |||
| 14821cb487 | |||
| 6d14e65907 | |||
| 34ddbd50dd | |||
| 95d99f7ce9 | |||
| bf2379e39d | |||
| 3bd6213a8d |
@@ -1,6 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
from typing import Optional
|
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.security import APIKeyHeader
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
@@ -27,9 +27,23 @@ import os
|
|||||||
from PyPDF2 import PdfReader
|
from PyPDF2 import PdfReader
|
||||||
from config import QUIZ_TYPES
|
from config import QUIZ_TYPES
|
||||||
from config import Config
|
from config import Config
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
# Load environment variables
|
# Load environment variables
|
||||||
load_dotenv()
|
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")
|
base_path = os.path.join("data", "config_files")
|
||||||
QUESTIONS_PATH = os.path.join(base_path, "questions.json")
|
QUESTIONS_PATH = os.path.join(base_path, "questions.json")
|
||||||
@@ -46,6 +60,40 @@ app = FastAPI(
|
|||||||
version="1.0.0"
|
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
|
# Add CORS middleware
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
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]
|
token = api_key_header.split(' ')[1]
|
||||||
|
print(f"Token : {token}")
|
||||||
if token != API_KEY:
|
if token != API_KEY:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=401,
|
status_code=401,
|
||||||
@@ -288,7 +337,7 @@ async def generate_pdf_endpoint(
|
|||||||
if request.form_id:
|
if request.form_id:
|
||||||
print(f"Fetching form response for form_id: {request.form_id}") # Debugging print
|
print(f"Fetching form response for form_id: {request.form_id}") # Debugging print
|
||||||
try:
|
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}"
|
url = f"{backend_base_url}/v3/api/custom/theme-document/answer/{request.form_id}?x-project={x_api_key}"
|
||||||
result = requests.get(url)
|
result = requests.get(url)
|
||||||
result.raise_for_status() # Ensure we raise an error for bad responses
|
result.raise_for_status() # Ensure we raise an error for bad responses
|
||||||
@@ -411,7 +460,10 @@ async def generate_quiz_endpoint(
|
|||||||
detail=f"Unexpected error during quiz generation: {str(e)}"
|
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")
|
@app.on_event("startup")
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
QUIZ_TYPES = {
|
QUIZ_TYPES = {
|
||||||
1: {
|
1: {
|
||||||
"name": "Single Line Text Inputs",
|
"name": "Single Line Text Inputs",
|
||||||
|
|||||||
@@ -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"
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
387857
|
||||||
@@ -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
|
||||||
@@ -25,3 +25,7 @@ reportlab==4.3.1
|
|||||||
python-docx==1.1.2
|
python-docx==1.1.2
|
||||||
unstructured==0.17.2
|
unstructured==0.17.2
|
||||||
pypdf==5.4.0
|
pypdf==5.4.0
|
||||||
|
gunicorn==23.0.0
|
||||||
|
python-dotenv
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Executable
+226
@@ -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
|
||||||
+41
-1
@@ -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)),
|
("system", chat_prompt(theme, resume,full_history,form_response,generate_theme)),
|
||||||
MessagesPlaceholder(variable_name="messages")
|
MessagesPlaceholder(variable_name="messages")
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
def fetch_conversation_history(conversation_id: str) -> List[Message]:
|
def fetch_conversation_history(conversation_id: str) -> List[Message]:
|
||||||
"""
|
"""
|
||||||
Fetch conversation history from the API using the conversation ID.
|
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 = requests.get(url)
|
||||||
response.raise_for_status() # Raise an error for bad responses
|
response.raise_for_status() # Raise an error for bad responses
|
||||||
data = response.json()["data"] # First JSON parse
|
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
|
# Parse the API response into Message objects
|
||||||
messages = []
|
messages = []
|
||||||
|
|
||||||
|
# Check if data exists and is a list
|
||||||
|
if data and isinstance(data, list):
|
||||||
for item in data:
|
for item in data:
|
||||||
|
|
||||||
|
|
||||||
|
# Check if item is a dictionary
|
||||||
|
if isinstance(item, dict):
|
||||||
role = item.get("role", "unknown")
|
role = item.get("role", "unknown")
|
||||||
content = item.get("content", "")
|
content = item.get("content", "")
|
||||||
timestamp = datetime.now().isoformat() # Use current timestamp if not provided
|
timestamp = datetime.now().isoformat() # Use current timestamp if not provided
|
||||||
messages.append(Message(role=role, content=content))
|
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
|
return messages
|
||||||
|
|
||||||
except requests.RequestException as e:
|
except requests.RequestException as e:
|
||||||
print(f"Error fetching conversation history: {e}")
|
print(f"Error fetching conversation history: {e}")
|
||||||
return []
|
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]:
|
def convert_to_langchain_messages(messages: List[Message]) -> List[HumanMessage | AIMessage]:
|
||||||
@@ -131,6 +169,8 @@ def ai_chat(query: str, conversation_id: str, theme_id: int, resume: str, full_h
|
|||||||
# Fetch conversation history from the API
|
# Fetch conversation history from the API
|
||||||
history = fetch_conversation_history(conversation_id)
|
history = fetch_conversation_history(conversation_id)
|
||||||
|
|
||||||
|
print(history)
|
||||||
|
|
||||||
config = {"configurable": {"thread_id": conversation_id}}
|
config = {"configurable": {"thread_id": conversation_id}}
|
||||||
language = "English"
|
language = "English"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user